summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/tex
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
committerKarl Berry <karl@freefriends.org>2021-02-25 19:22:25 +0000
commitad547a6b5986815fda458221149728d9d9ab1d87 (patch)
tree16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/web2c/luatexdir/tex
parent947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff)
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex')
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/align.c1299
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/align.h54
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/arithmetic.c815
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/arithmetic.h80
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/backend.c124
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/backend.h54
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/buildpage.c1164
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/buildpage.h74
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/commands.c936
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/commands.h359
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/conditional.c568
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/conditional.h78
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/directions.c196
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/directions.h168
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/dumpdata.c539
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/dumpdata.h114
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/equivalents.c1220
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/equivalents.h916
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/errors.c1055
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/errors.h116
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/expand.c967
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/expand.h102
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/extensions.c1344
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/extensions.h147
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/filename.c383
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/filename.h33
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/inputstack.c960
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/inputstack.h344
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/linebreak.c2537
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/linebreak.h98
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mainbody.c780
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mainbody.h246
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/maincontrol.c4245
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/maincontrol.h217
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mathcodes.c360
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mathcodes.h67
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/memoryword.c29
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/memoryword.h128
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mlist.c4830
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mlist.h41
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/nesting.c429
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/nesting.h70
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/packaging.c2145
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/packaging.h156
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/postlinebreak.c603
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/postlinebreak.h50
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/primitive.c785
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/primitive.h89
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/printing.c1350
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/printing.h133
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/scanning.c2751
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/scanning.h109
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/stringpool.c375
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/stringpool.h142
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texdeffont.c247
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texdeffont.h30
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texfileio.c1509
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texfileio.h145
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texmath.c2727
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texmath.h306
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texnodes.c4840
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texnodes.h1188
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/textcodes.c530
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/textcodes.h54
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/textoken.c3920
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/textoken.h189
66 files changed, 52659 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/align.c b/Build/source/texk/web2c/luatexdir/tex/align.c
new file mode 100644
index 00000000000..bb721856fac
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/align.c
@@ -0,0 +1,1299 @@
+/*
+
+Copyright 2009-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+void fin_align(void);
+void init_row(void);
+void init_col(void);
+
+#define noDEBUG
+
+/*tex
+
+ 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.
+
+ \starttyping
+ \tabskip 2pt plus 3pt
+ \halign to 300pt{u1#v1&
+ \hskip 50pt \tabskip 1pt plus 1fil u2#v2&
+ \hskip 50pt u3#v3\cr
+ \hskip 25pt a1&\omit a2&\vrule\cr
+ \hskip 25pt \noalign\{\vskip 3pt}
+ \hskip 25pt b1\span b2\cr
+ \hskip 25pt \omit&c2\span\omit\cr}
+ \stoptyping
+
+ Here's what happens:
+
+ \startitemize
+
+ \startitem
+ 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.
+ \stopitem
+
+ \startitem
+ The preamble is scanned next. Macros in the preamble are not
+ expanded, 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:
+
+ \starttabulate
+ \NC \type{\glue 2pt plus 3pt} \NC the tabskip preceding column 1 \NC \NR
+ \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 1 \NC \NR
+ \NC \type{\glue 2pt plus 3pt} \NC the tabskip between columns 1 and 2 \NC \NR
+ \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 2 \NC \NR
+ \NC \type{\glue 1pt plus 1fil} \NC the tabskip between columns 2 and 3 \NC \NR
+ \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 3 \NC \NR
+ \NC \type{\glue 1pt plus 1fil} \NC the tabskip following column 3 \NC \NR
+ \stoptabulate
+
+ These ``alignrecord'' entries have the same size as an |unset_node|,
+ since they will later be converted into such nodes. These alignrecord
+ nodes have no |depth| field; this is split into |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. Furthermore, They have a |span_ptr| instead of a
+ |node_attr| field, and these |span_ptr| fields are initially set to
+ the value |end_span|, for reasons explained below.
+ \stopitem
+
+ \startitem
+ \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.
+ \stopitem
+
+ \startitem
+ 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.
+ \stopitem
+
+ \startitem
+ 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.
+ \stopitem
+
+ \startitem
+ The first row has now ended; it is made into a single unset box
+ comprising the following seven items:
+
+ \starttyping
+ \glue 2pt plus 3pt
+ \unsetbox for 1 column: u1a1v1
+ \glue 2pt plus 3pt
+ \unsetbox for 1 column: a2
+ \glue 1pt plus 1fil
+ \unsetbox for 1 column: u3\vrule v3
+ \glue 1pt plus 1fil
+ \stoptyping
+
+ 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.
+ \stopitem
+
+ \startitem
+ 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|.
+ \stopitem
+
+ \startitem
+ The next row produces an unset box that looks like this:
+
+ \starttyping
+ \glue 2pt plus 3pt
+ \unsetbox for 2 columns: u1b1v1u2b2v2
+ \glue 1pt plus 1fil
+ \unsetbox for 1 column: {(empty)}
+ \glue 1pt plus 1fil
+ \stoptyping
+
+ 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
+ |span_ptr| field of the alignrecord for column~1 now points to the
+ new span node, and the |span_ptr| of the span node points to
+ |end_span|.
+ \stopitem
+
+ \startitem
+
+ The final row produces the unset box
+
+ \starttyping
+ \glue 2pt plus 3pt\cr
+ \unsetbox for 1 column: {(empty)}
+ \glue 2pt plus 3pt\cr
+ \unsetbox for 2 columns: u2c2v2
+ \glue 1pt plus 1fil
+ \stoptyping
+
+ A new span node is attached to the alignrecord for column 2.
+ \stopitem
+
+ \startitem
+ 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.
+ \stopitem
+
+ \stopitemize
+
+ 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.
+
+*/
+
+/*tex The pointer to \<u_j> token list: */
+
+#define u_part(A) vlink((A)+depth_offset)
+
+/*tex The pointer to \<v_j> token list */
+
+#define v_part(A) vinfo((A)+depth_offset)
+
+/*tex A column spanning list */
+
+#define span_ptr(A) vinfo((A)+1)
+
+/*tex Info to remember during template */
+
+#define extra_info(A) vinfo((A)+list_offset)
+
+/*tex
+
+ 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, and alike
+ |cur_pre_head| and |cur_pre_tail| for pre-adjust lists.
+
+ The current values of these nine quantities appear in global variables; when
+ they have to be pushed down, they are stored in 6-word nodes, and |align_ptr|
+ points to the topmost such node.
+
+*/
+
+/*tex This could be in |texnodes.h| but it's documented here. */
+
+/*tex The current preamble list: */
+
+#define preamble vlink(align_head)
+
+/*tex The current position in the preamble list: */
+
+pointer cur_align = null;
+
+/*tex The start of the currently spanned columns in the preamble list: */
+
+pointer cur_span = null;
+
+/*tex A place to copy when extending a periodic preamble: */
+
+pointer cur_loop = null;
+
+/*tex The most recently pushed-down alignment stack node: */
+
+pointer align_ptr = null;
+
+/*tex Adjustment list pointers: */
+
+pointer cur_head = null, cur_tail = null;
+
+/*tex Pre-adjustment list pointers: */
+
+pointer cur_pre_head = null, cur_pre_tail = null;
+
+/*tex
+
+ The |align_state| and |preamble| variables are initialized elsewhere.
+
+ Alignment stack maintenance is handled by a pair of trivial routines called
+ |push_alignment| and |pop_alignment|.
+
+ (HH:) It makes not much sense to add support for an \.{attr} keyword to
+ \.{\\halign} and \.{\\valign} because then we need to decide if we tag rows
+ or cells or both or come up with \.{cellattr} and \.{rowattr} and such. But
+ then it even makes sense to have explicit commands (in addition to the
+ seperator) to tags individual cells. Too muss hassle for now and the
+ advantages are not that large.
+
+*/
+
+static void push_alignment(void)
+{
+ /*tex The new alignment stack node: */
+ pointer p;
+ p = new_node(align_stack_node, 0);
+ vinfo(p + 1) = align_ptr;
+ vlink(p + 1) = cur_align;
+ vinfo(p + 2) = preamble;
+ vlink(p + 2) = cur_span;
+ vinfo(p + 3) = cur_loop;
+ vlink(p + 3) = align_state;
+ vinfo(p + 4) = cur_head;
+ vlink(p + 4) = cur_tail;
+ vinfo(p + 5) = cur_pre_head;
+ vlink(p + 5) = cur_pre_tail;
+ align_ptr = p;
+ cur_head = new_node(temp_node, 0);
+ cur_pre_head = new_node(temp_node, 0);
+}
+
+static void pop_alignment(void)
+{
+ /*tex The top alignment stack node: */
+ pointer p;
+ flush_node(cur_head);
+ flush_node(cur_pre_head);
+ p = align_ptr;
+ cur_pre_tail = vlink(p + 5);
+ cur_pre_head = vinfo(p + 5);
+ cur_tail = vlink(p + 4);
+ cur_head = vinfo(p + 4);
+ align_state = vlink(p + 3);
+ cur_loop = vinfo(p + 3);
+ cur_span = vlink(p + 2);
+ preamble = vinfo(p + 2);
+ cur_align = vlink(p + 1);
+ align_ptr = vinfo(p + 1);
+ flush_node(p);
+}
+
+/*tex
+
+ \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|.
+
+ 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).
+
+*/
+
+static void get_preamble_token(void)
+{
+ RESTART:
+ get_token();
+ while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) {
+ /*tex This token will be expanded once. */
+ get_token();
+ if (cur_cmd > max_command_cmd) {
+ expand();
+ get_token();
+ }
+ }
+ if (cur_cmd == endv_cmd)
+ fatal_error("(interwoven alignment preambles are not allowed)");
+ if ((cur_cmd == assign_glue_cmd)
+ && (cur_chr == glue_base + tab_skip_code)) {
+ scan_optional_equals();
+ scan_glue(glue_val_level);
+ if (global_defs_par > 0)
+ geq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val);
+ else
+ eq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val);
+ goto RESTART;
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void init_align(void)
+{
+ /*tex |warning_index| value for error messages */
+ pointer save_cs_ptr;
+ /*tex for short-term temporary use */
+ pointer p, r;
+ /*tex \.{\\halign} or \.{\\valign}, usually */
+ save_cs_ptr = cur_cs;
+ push_alignment();
+ /*tex enter a new alignment level */
+ align_state = -1000000;
+ /*tex
+
+ When \.{\\halign} is used as a displayed formula, there should be no
+ other pieces of mlists present.
+
+ */
+ if ((cur_list.mode_field == mmode) && ((cur_list.tail_field != cur_list.head_field) || (incompleat_noad_par != null))) {
+ const char *hlp[] = {
+ "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.",
+ NULL
+ };
+ tex_error("Improper \\halign inside $$'s", hlp);
+ flush_math();
+ }
+ /*tex Enter a new semantic level. */
+ push_nest();
+ /*tex
+
+ 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.
+ */
+ if (cur_list.mode_field == mmode) {
+ cur_list.mode_field = -vmode;
+ prev_depth_par = nest[nest_ptr - 2].prev_depth_field;
+ } else if (cur_list.mode_field > 0) {
+ cur_list.mode_field = -(cur_list.mode_field);
+ }
+ scan_spec(align_group);
+ /*tex Scan the preamble. */
+ preamble = null;
+ cur_align = align_head;
+ cur_loop = null;
+ scanner_status = aligning;
+ warning_index = save_cs_ptr;
+ align_state = -1000000;
+ /*tex At this point, |cur_cmd=left_brace|. */
+ while (true) {
+ /*tex Append the current tabskip glue to the preamble list. */
+ r = new_param_glue(tab_skip_code);
+ vlink(cur_align) = r;
+ cur_align = vlink(cur_align);
+ if (cur_cmd == car_ret_cmd) {
+ /*tex \.{\\cr} ends the preamble. */
+ break;
+ }
+ /*tex
+
+ Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| and
+ then scan the template \<u_j>, putting the resulting token list in
+ |hold_token_head|. Spaces are eliminated from the beginning of a
+ template.
+
+ */
+ p = hold_token_head;
+ token_link(p) = null;
+ while (1) {
+ get_preamble_token();
+ if (cur_cmd == mac_param_cmd)
+ break;
+ if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd)
+ && (align_state == -1000000)) {
+ if ((p == hold_token_head) && (cur_loop == null) && (cur_cmd == tab_mark_cmd)) {
+ cur_loop = cur_align;
+ } else {
+ const char *hlp[] = {
+ "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.",
+ NULL
+ };
+ back_input();
+ tex_error("Missing # inserted in alignment preamble", hlp);
+ break;
+ }
+ } else if ((cur_cmd != spacer_cmd) || (p != hold_token_head)) {
+ r = get_avail();
+ token_link(p) = r;
+ p = token_link(p);
+ token_info(p) = cur_tok;
+ }
+ }
+ r = new_node(align_record_node, 0);
+ vlink(cur_align) = r;
+ /*tex A new align record: */
+ cur_align = vlink(cur_align);
+ span_ptr(cur_align) = end_span;
+ width(cur_align) = null_flag;
+ u_part(cur_align) = token_link(hold_token_head);
+ /*tex
+
+ Scan the template \<v_j>, putting the resulting token list in
+ |hold_token_head|.
+
+ */
+ p = hold_token_head;
+ token_link(p) = null;
+ while (1) {
+ CONTINUE:
+ get_preamble_token();
+ if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) && (align_state == -1000000))
+ break;
+ if (cur_cmd == mac_param_cmd) {
+ const char *hlp[] = {
+ "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.",
+ NULL
+ };
+ tex_error("Only one # is allowed per tab", hlp);
+ goto CONTINUE;
+ }
+ r = get_avail();
+ token_link(p) = r;
+ p = token_link(p);
+ token_info(p) = cur_tok;
+ }
+ r = get_avail();
+ token_link(p) = r;
+ p = token_link(p);
+ /*tex Put \.{\\endtemplate} at the end: */
+ token_info(p) = end_template_token;
+ v_part(cur_align) = token_link(hold_token_head);
+ }
+ scanner_status = normal;
+ new_save_level(align_group);
+ if (every_cr_par != null)
+ begin_token_list(every_cr_par, every_cr_text);
+ /*tex Look for \.{\\noalign} or \.{\\omit}. */
+ align_peek();
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void align_peek(void)
+{
+ RESTART:
+ align_state = 1000000;
+ do {
+ get_x_or_protected();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_cmd == no_align_cmd) {
+ scan_left_brace();
+ new_save_level(no_align_group);
+ if (cur_list.mode_field == -vmode)
+ normal_paragraph();
+ } else if (cur_cmd == right_brace_cmd) {
+ fin_align();
+ } else if ((cur_cmd == car_ret_cmd) && (cur_chr == cr_cr_code)) {
+ /*tex Ignore \.{\\crcr}. */
+ goto RESTART;
+ } else {
+ /*tex Start a new row. */
+ init_row();
+ /*tex Start a new column and replace what we peeked at. */
+ init_col();
+ }
+}
+
+
+/*tex
+
+ 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.
+
+*/
+
+static void init_span(pointer p)
+{
+ push_nest();
+ if (cur_list.mode_field == -hmode) {
+ space_factor_par = 1000;
+ } else {
+ prev_depth_par = ignore_depth;
+ normal_paragraph();
+ }
+ cur_span = p;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void init_row(void)
+{
+ push_nest();
+ cur_list.mode_field = (-hmode - vmode) - cur_list.mode_field;
+ if (cur_list.mode_field == -hmode)
+ space_factor_par = 0;
+ else
+ prev_depth_par = 0;
+ tail_append(new_glue(preamble));
+ subtype(cur_list.tail_field) = tab_skip_code + 1;
+ cur_align = vlink(preamble);
+ cur_tail = cur_head;
+ cur_pre_tail = cur_pre_head;
+ init_span(cur_align);
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void init_col(void)
+{
+ extra_info(cur_align) = cur_cmd;
+ if (cur_cmd == omit_cmd)
+ align_state = 0;
+ else {
+ back_input();
+ begin_token_list(u_part(cur_align), u_template);
+ }
+ /*tex now |align_state=1000000| */
+}
+
+
+/*tex
+
+ 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.
+
+*/
+
+void insert_vj_template(void)
+{
+ if ((scanner_status == aligning) || (cur_align == null))
+ fatal_error("(interwoven alignment preambles are not allowed)");
+ cur_cmd = extra_info(cur_align);
+ extra_info(cur_align) = cur_chr;
+ if (cur_cmd == omit_cmd)
+ begin_token_list(omit_template, v_template);
+ else
+ begin_token_list(v_part(cur_align), v_template);
+ align_state = 1000000;
+}
+
+/*tex Determine the stretch order */
+
+#define determine_stretch_order() do { \
+ if (total_stretch[filll]!= 0) o = filll; \
+ else if (total_stretch[fill] != 0) o = fill; \
+ else if (total_stretch[fil] != 0) o = fil; \
+ else if (total_stretch[sfi] != 0) o = sfi; \
+ else o=normal; \
+} while (0)
+
+/*tex Determine the shrink order */
+
+#define determine_shrink_order() do { \
+ if (total_shrink[filll] != 0) o = filll; \
+ else if (total_shrink[fill] != 0) o = fill; \
+ else if (total_shrink[fil] != 0) o = fil; \
+ else if (total_shrink[sfi] != 0) o = sfi; \
+ else o=normal; \
+} while (0)
+
+/*tex
+
+ 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.
+
+*/
+
+boolean fin_col(void)
+{
+ /*tex the alignrecord after the current one */
+ pointer p;
+ /*tex temporary pointers for list manipulation */
+ pointer q, r;
+ /*tex a new span node */
+ pointer s;
+ /*tex a new unset box */
+ pointer u;
+ /*tex natural width */
+ scaled w;
+ /*tex order of infinity */
+ unsigned char o;
+ /*tex span counter */
+ halfword n;
+ if (cur_align == null)
+ confusion("endv");
+ q = vlink(cur_align);
+ if (q == null)
+ confusion("endv");
+ if (align_state < 500000)
+ fatal_error("(interwoven alignment preambles are not allowed)");
+ p = vlink(q);
+ /*tex 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) {
+ /*tex Lengthen the preamble periodically: */
+ r = new_node(align_record_node, 0);
+ vlink(q) = r;
+ /*tex A new align record: */
+ p = vlink(q);
+ span_ptr(p) = end_span;
+ width(p) = null_flag;
+ cur_loop = vlink(cur_loop);
+ /*tex Copy the templates from node |cur_loop| into node |p|. */
+ q = hold_token_head;
+ r = u_part(cur_loop);
+ while (r != null) {
+ s = get_avail();
+ token_link(q) = s;
+ q = token_link(q);
+ token_info(q) = token_info(r);
+ r = token_link(r);
+ }
+ token_link(q) = null;
+ u_part(p) = token_link(hold_token_head);
+ q = hold_token_head;
+ r = v_part(cur_loop);
+ while (r != null) {
+ s = get_avail();
+ token_link(q) = s;
+ q = token_link(q);
+ token_info(q) = token_info(r);
+ r = token_link(r);
+ }
+ token_link(q) = null;
+ v_part(p) = token_link(hold_token_head);
+ cur_loop = vlink(cur_loop);
+ r = new_glue(cur_loop);
+ vlink(p) = r;
+ } else {
+ const char *hlp[] = {
+ "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.",
+ NULL
+ };
+ extra_info(cur_align) = cr_code;
+ tex_error("Extra alignment tab has been changed to \\cr", hlp);
+ }
+ }
+ if (extra_info(cur_align) != span_code) {
+ unsave();
+ new_save_level(align_group);
+ /*tex Package an unset box for the current column and record its width. */
+ if (cur_list.mode_field == -hmode) {
+ adjust_tail = cur_tail;
+ pre_adjust_tail = cur_pre_tail;
+ u = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, additional, align_set_group, -1, 0, 0);
+ w = width(u);
+ cur_tail = adjust_tail;
+ adjust_tail = null;
+ cur_pre_tail = pre_adjust_tail;
+ pre_adjust_tail = null;
+ } else {
+ u = filtered_vpackage(vlink(cur_list.head_field), 0, additional, 0, align_set_group, -1, 0, 0);
+ w = height(u);
+ }
+ /*tex This represents a span count of 1: */
+ n = min_quarterword;
+ if (cur_span != cur_align) {
+ /*tex Update width entry for spanned columns. */
+ q = cur_span;
+ do {
+ incr(n);
+ q = vlink(vlink(q));
+ } while (q != cur_align);
+ if (n > max_quarterword) {
+ /*tex This can happen, but won't. */
+ confusion("too many spans");
+ }
+ q = cur_span;
+ while (span_span(span_ptr(q)) < n) {
+ q = span_ptr(q);
+ }
+ if (span_span(span_ptr(q)) > n) {
+ s = new_span_node(span_ptr(q), n, w);
+ span_ptr(q) = s;
+ } else if (width(span_ptr(q)) < w) {
+ width(span_ptr(q)) = w;
+ }
+ } else if (w > width(cur_align)) {
+ width(cur_align) = w;
+ }
+ type(u) = unset_node;
+ span_count(u) = (quarterword) n;
+ determine_stretch_order();
+ glue_order(u) = o;
+ glue_stretch(u) = total_stretch[o];
+ determine_shrink_order();
+ glue_sign(u) = o;
+ glue_shrink(u) = total_shrink[o];
+ pop_nest();
+ vlink(cur_list.tail_field) = u;
+ cur_list.tail_field = u;
+ /*tex Copy the tabskip glue between columns. */
+ tail_append(new_glue(vlink(cur_align)));
+ subtype(cur_list.tail_field) = tab_skip_code + 1;
+ if (extra_info(cur_align) >= cr_code) {
+ return true;
+ }
+ init_span(p);
+ }
+ align_state = 1000000;
+ do {
+ get_x_or_protected();
+ } while (cur_cmd == spacer_cmd);
+ cur_align = p;
+ init_col();
+ return false;
+}
+
+/*tex
+
+ A span node is a 3-word record containing |width|, |span_span|, and
+ |span_ptr| fields. The |span_span| field indicates the number of spanned
+ columns; the |span_ptr| 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 |span_span| 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 |span_ptr| field of the alignrecord for that column.
+ The code has to make sure that there is room for |span_ptr| in both the
+ alignrecord and the span nodes, which is why |span_ptr| replaces |node_attr|.
+
+ The |new_span_node| function is defined in |texnodes.c|.
+
+*/
+
+/*tex This is normally |alink|: */
+
+#ifndef span_span
+# define span_span(A) vlink((A)+1)
+#endif
+
+/*tex
+
+ 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.
+
+*/
+
+void fin_row(void)
+{
+ /*tex The new unset box: */
+ pointer p;
+ if (cur_list.mode_field == -hmode) {
+ p = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0,
+ additional, fin_row_group, -1, 0, 0);
+ pop_nest();
+ if (cur_pre_head != cur_pre_tail)
+ append_list(cur_pre_head, cur_pre_tail);
+ append_to_vlist(p,lua_key_index(alignment));
+ if (cur_head != cur_tail)
+ append_list(cur_head, cur_tail);
+ } else {
+ p = filtered_vpackage(vlink(cur_list.head_field),
+ 0, additional, max_depth_par, fin_row_group, -1, 0, 0);
+ pop_nest();
+ vlink(cur_list.tail_field) = p;
+ cur_list.tail_field = p;
+ space_factor_par = 1000;
+ }
+ type(p) = unset_node;
+ glue_stretch(p) = 0;
+ if (every_cr_par != null)
+ begin_token_list(every_cr_par, every_cr_text);
+ align_peek();
+ /*tex Note that |glue_shrink(p)=0| since |glue_shrink==shift_amount|. */
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void fin_align(void)
+{
+ /*tex registers for the list operations */
+ pointer p, q, r, s, u, rr;
+ /*tex width of column */
+ scaled t, w;
+ /*tex shift offset for unset boxes */
+ scaled o;
+ /*tex matching span amount */
+ halfword n;
+ /*tex temporary storage for |overfull_rule| */
+ scaled rule_save;
+ /*tex temporary storage for |prev_depth| */
+ halfword pd;
+ /*tex temporary storage for |new_glue| */
+ halfword ng;
+ /*tex The |align_group| was for individual entries: */
+ if (cur_group != align_group)
+ confusion("align1");
+ unsave();
+ /*tex The |align_group| was for the whole alignment: */
+ if (cur_group != align_group)
+ confusion("align0");
+ unsave();
+ if (nest[nest_ptr - 1].mode_field == mmode) {
+ o = display_indent_par;
+ } else {
+ o = 0;
+ }
+ /*tex
+
+ Go through the preamble list, determining the column widths and
+ changing the alignrecords to dummy unset boxes.
+
+ 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\L
+ i\L j}\biggl( w_{ij}-\sum_{i\L 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.
+
+ */
+ q = vlink(preamble);
+ do {
+ flush_list(u_part(q));
+ flush_list(v_part(q));
+ p = vlink(vlink(q));
+ if (width(q) == null_flag) {
+ /*tex Nullify |width(q)| and the tabskip glue following this column. */
+ width(q) = 0;
+ r = vlink(q);
+ reset_glue_to_zero(r);
+ }
+ if (span_ptr(q) != end_span) {
+ /*tex
+
+ Merge the widths in the span nodes of |q| with those of |p|,
+ destroying the span nodes of |q|.
+
+ Merging of two span-node lists is a typical exercise in the
+ manipulation of linearly linked data structures. The essential
+ invariant in the following |repeat| 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|.
+
+ */
+ t = width(q) + width(vlink(q));
+ r = span_ptr(q);
+ s = end_span;
+ span_ptr(s) = p;
+ n = min_quarterword + 1;
+ do {
+ width(r) = width(r) - t;
+ u = span_ptr(r);
+ while (span_span(r) > n) {
+ s = span_ptr(s);
+ n = span_span(span_ptr(s)) + 1;
+ }
+ if (span_span(r) < n) {
+ span_ptr(r) = span_ptr(s);
+ span_ptr(s) = r;
+ decr(span_span(r));
+ s = r;
+ } else {
+ if (width(r) > width(span_ptr(s)))
+ width(span_ptr(s)) = width(r);
+ flush_node(r);
+ }
+ r = u;
+ } while (r != end_span);
+ }
+ 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);
+ /*tex
+
+ Package the preamble list, to determine the actual tabskip glue amounts,
+ and let |p| point to this prototype box.
+
+ 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.
+
+ */
+ decr(save_ptr);
+ pack_begin_line = -cur_list.ml_field;
+ if (cur_list.mode_field == -vmode) {
+ rule_save = overfull_rule_par;
+ /*tex Prevent the rule from being packaged. */
+ overfull_rule_par = 0;
+ p = hpack(preamble, saved_value(0), saved_level(0), -1);
+ overfull_rule_par = rule_save;
+ } else {
+ q = vlink(preamble);
+ do {
+ height(q) = width(q);
+ width(q) = 0;
+ q = vlink(vlink(q));
+ } while (q != null);
+ p = filtered_vpackage(preamble,
+ saved_value(0), saved_level(0), max_depth_par, preamble_group, -1, 0, 0);
+ q = vlink(preamble);
+ do {
+ width(q) = height(q);
+ height(q) = 0;
+ q = vlink(vlink(q));
+ } while (q != null);
+ }
+ pack_begin_line = 0;
+ /*tex Set the glue in all the unset boxes of the current list. */
+ q = vlink(cur_list.head_field);
+ s = cur_list.head_field;
+ while (q != null) {
+ if (!is_char_node(q)) {
+ if (type(q) == unset_node) {
+ /*tex
+
+ We set the unset box |q| and the unset boxes in it. The unset
+ box |q| represents a row that contains one or more unset
+ boxes, depending on how soon \.{\\cr} occurred in that row.
+
+ */
+ if (cur_list.mode_field == -vmode) {
+ type(q) = hlist_node;
+ subtype(q) = align_row_list;
+ width(q) = width(p);
+ } else {
+ type(q) = vlist_node;
+ subtype(q) = align_row_list;
+ 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 = vlink(list_ptr(q));
+ if (type(r) != unset_node) {
+ formatted_error("alignment","bad box");
+ }
+ s = vlink(list_ptr(p));
+ do {
+ /*tex
+
+ We set the glue in node |r| and change it from an unset
+ node. 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.
+
+ */
+ n = span_count(r);
+ t = width(s);
+ w = t;
+ u = hold_head;
+ while (n > min_quarterword) {
+ decr(n);
+ /*tex
+
+ Append tabskip glue and an empty box to list |u|, and
+ update |s| and |t| as the prototype nodes are passed.
+
+ */
+ s = vlink(s);
+ ng = new_glue(s);
+ vlink(u) = ng;
+ u = vlink(u);
+ subtype(u) = tab_skip_code + 1;
+ t = t + width(s);
+ if (glue_sign(p) == stretching) {
+ if (stretch_order(s) == glue_order(p))
+ t = t + round(float_cast(glue_set(p)) * float_cast(stretch(s)));
+ } else if (glue_sign(p) == shrinking) {
+ if (shrink_order(s) == glue_order(p))
+ t = t - round(float_cast(glue_set(p)) * float_cast(shrink(s)));
+ }
+ s = vlink(s);
+ rr = new_null_box();
+ vlink(u) = rr;
+ u = vlink(u);
+ t = t + width(s);
+ subtype(u) = align_cell_list;
+ if (cur_list.mode_field == -vmode) {
+ width(u) = width(s);
+ } else {
+ type(u) = vlist_node;
+ height(u) = width(s);
+ }
+ }
+ if (cur_list.mode_field == -vmode) {
+ /*tex
+
+ Make the unset node |r| into an |hlist_node| of width
+ |w|, setting the glue as if the width were |t|.
+
+ */
+ height(r) = height(q);
+ depth(r) = depth(q);
+ if (t == width(r)) {
+ glue_sign(r) = normal;
+ glue_order(r) = normal;
+ set_glue_ratio_zero(glue_set(r));
+ } else if (t > width(r)) {
+ glue_sign(r) = stretching;
+ if (glue_stretch(r) == 0)
+ set_glue_ratio_zero(glue_set(r));
+ else
+ glue_set(r) = unfloat((double) (t - width(r)) / glue_stretch(r));
+ } else {
+ glue_order(r) = glue_sign(r);
+ glue_sign(r) = shrinking;
+ if (glue_shrink(r) == 0)
+ set_glue_ratio_zero(glue_set(r));
+ else if ((glue_order(r) == normal) && (width(r) - t > glue_shrink(r)))
+ set_glue_ratio_one(glue_set(r));
+ else
+ glue_set(r) = unfloat((double) (width(r) - t) / glue_shrink(r));
+ }
+ width(r) = w;
+ type(r) = hlist_node;
+ subtype(r) = align_cell_list;
+
+ } else {
+ /*tex
+
+ Make the unset node |r| into a |vlist_node| of height
+ |w|, setting the glue as if the height were |t|.
+
+ */
+ width(r) = width(q);
+ if (t == height(r)) {
+ glue_sign(r) = normal;
+ glue_order(r) = normal;
+ set_glue_ratio_zero(glue_set(r));
+ } else if (t > height(r)) {
+ glue_sign(r) = stretching;
+ if (glue_stretch(r) == 0)
+ set_glue_ratio_zero(glue_set(r));
+ else
+ glue_set(r) = unfloat((t - height(r)) / glue_stretch(r));
+ } else {
+ glue_order(r) = glue_sign(r);
+ glue_sign(r) = shrinking;
+ if (glue_shrink(r) == 0)
+ set_glue_ratio_zero(glue_set(r));
+ else if ((glue_order(r) == normal) && (height(r) - t > glue_shrink(r)))
+ set_glue_ratio_one(glue_set(r));
+ else
+ glue_set(r) = unfloat((height(r) - t) / glue_shrink(r));
+ }
+ height(r) = w;
+ type(r) = vlist_node;
+ subtype(r) = align_cell_list;
+ }
+ shift_amount(r) = 0;
+ if (u != hold_head) {
+ /*tex Append blank boxes to account for spanned nodes. */
+ vlink(u) = vlink(r);
+ vlink(r) = vlink(hold_head);
+ r = u;
+ }
+
+ r = vlink(vlink(r));
+ s = vlink(vlink(s));
+ } while (r != null);
+
+ } else if (type(q) == rule_node) {
+ /*tex
+
+ Make the running dimensions in rule |q| extend to the
+ boundaries of the alignment.
+
+ */
+ 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 = vlink(q);
+ vlink(q) = null;
+ q = hpack(q, 0, additional, -1);
+ shift_amount(q) = o;
+ subtype(q) = align_cell_list;
+ vlink(q) = r;
+ vlink(s) = q;
+ }
+ }
+ }
+ s = q;
+ q = vlink(q);
+ }
+ flush_node_list(p);
+ pop_alignment();
+ /*tex
+
+ We now have a completed alignment, in the list that starts at
+ |cur_list.head_field| and ends at |cur_list.tail_field|. 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.)
+
+ */
+ pd = prev_depth_par;
+ p = vlink(cur_list.head_field);
+ q = cur_list.tail_field;
+ pop_nest();
+ if (cur_list.mode_field == mmode) {
+ finish_display_alignment(p, q, pd);
+ } else {
+ prev_depth_par = pd;
+ vlink(cur_list.tail_field) = p;
+ if (p != null)
+ cur_list.tail_field = q;
+ if (cur_list.mode_field == vmode) {
+ if (!output_active)
+ lua_node_filter_s(buildpage_filter_callback,lua_key_index(alignment));
+ build_page();
+ }
+ }
+}
+
+/*tex
+
+ The token list |omit_template| just referred to is a constant token list that
+ contains the special control sequence \.{\\endtemplate} only.
+
+*/
+
+void initialize_alignments(void)
+{
+ token_info(omit_template) = end_template_token;
+ span_span(end_span) = max_quarterword + 1;
+ span_ptr(end_span) = null;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/align.h b/Build/source/texk/web2c/luatexdir/tex/align.h
new file mode 100644
index 00000000000..a93539f0dc5
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/align.h
@@ -0,0 +1,54 @@
+/* align.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef ALIGN_H
+# define ALIGN_H 1
+
+/* 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.
+*/
+
+# define tab_mark_cmd_code 1114113 /* {|biggest_char+2|} */
+# define span_code 1114114 /* {|biggest_char+3|} */
+# define cr_code (span_code+1) /* distinct from |span_code| and from any character */
+# define cr_cr_code (cr_code+1) /* this distinguishes \.{\\crcr} from \.{\\cr} */
+
+
+extern void init_align(void);
+extern void initialize_alignments(void);
+
+extern boolean fin_col(void);
+extern void fin_row(void);
+
+extern void align_peek(void);
+extern void insert_vj_template(void);
+
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/arithmetic.c b/Build/source/texk/web2c/luatexdir/tex/arithmetic.c
new file mode 100644
index 00000000000..2782c253400
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/arithmetic.c
@@ -0,0 +1,815 @@
+/*
+
+arithmetic.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+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 |div| with a possibly negative
+numerator. These are harmless; see |div| 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 |mod| as
+well as for |div|.
+
+Here is a routine that calculates half of an integer, using an unambiguous
+convention with respect to signed odd numbers.
+
+*/
+
+int half(int x)
+{
+ if (odd(x))
+ return ((x + 1) / 2);
+ else
+ return (x / 2);
+}
+
+/*tex
+
+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.
+
+*/
+
+scaled round_decimals(int k)
+{
+ int a = 0;
+ while (k-- > 0) {
+ a = (a + dig[k] * two) / 10;
+ }
+ return ((a + 1) / 2);
+}
+
+/*tex
+
+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\L10\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.
+
+The next one prints a scaled real, rounded to five digits.
+
+*/
+
+void print_scaled(scaled s)
+{
+ /*tex The amount of allowable inaccuracy: */
+ scaled delta;
+ char buffer[20];
+ int i = 0;
+ if (s < 0) {
+ /*tex Print the sign, if negative. */
+ print_char('-');
+ negate(s);
+ }
+ /*tex Print the integer part. */
+ print_int(s / unity);
+ buffer[i++] = '.';
+ s = 10 * (s % unity) + 5;
+ delta = 10;
+ do {
+ if (delta > unity) {
+ /*tex Round the last digit. */
+ s = s + 0100000 - 50000;
+ }
+ buffer[i++] = '0' + (s / unity);
+ s = 10 * (s % unity);
+ delta = delta * 10;
+ } while (s > delta);
+ buffer[i++] = '\0';
+ tprint(buffer);
+}
+
+/*tex
+
+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 then
+@t\\{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, |tex_remainder|, holds the
+remainder after a division.
+
+*/
+
+/*tex Has arithmetic overflow occurred recently? */
+
+boolean arith_error;
+
+/*tex The amount subtracted to get an exact division. */
+
+scaled tex_remainder;
+
+/*tex
+
+ 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.
+
+*/
+
+scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer)
+{
+ if (n == 0)
+ return y;
+ if (n < 0) {
+ negate(x);
+ negate(n);
+ }
+ if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) {
+ return (n * x + y);
+ } else {
+ arith_error = true;
+ return 0;
+ }
+}
+
+/*tex
+
+We also need to divide scaled dimensions by integers.
+
+*/
+
+scaled x_over_n(scaled x, int n)
+{
+ /*tex Should |tex_remainder| be negated? */
+ boolean negative = false;
+ if (n == 0) {
+ arith_error = true;
+ tex_remainder = x;
+ return 0;
+ } else {
+ if (n < 0) {
+ negate(x);
+ negate(n);
+ negative = true;
+ }
+ if (x >= 0) {
+ tex_remainder = x % n;
+ if (negative)
+ negate(tex_remainder);
+ return (x / n);
+ } else {
+ tex_remainder = -((-x) % n);
+ if (negative)
+ negate(tex_remainder);
+ return (-((-x) / n));
+ }
+ }
+}
+
+/*tex
+
+Then comes the multiplication of a scaled number by a fraction |n/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.
+
+*/
+
+scaled xn_over_d(scaled x, int n, int d)
+{
+ nonnegative_integer t, u, v, xx, dd;
+ boolean positive = true;
+ if (x < 0) {
+ negate(x);
+ positive = false;
+ }
+ xx = (nonnegative_integer) x;
+ dd = (nonnegative_integer) d;
+ t = ((xx % 0100000) * (nonnegative_integer) n);
+ u = ((xx / 0100000) * (nonnegative_integer) n + (t / 0100000));
+ v = (u % dd) * 0100000 + (t % 0100000);
+ if (u / dd >= 0100000)
+ arith_error = true;
+ else
+ u = 0100000 * (u / dd) + (v / dd);
+ if (positive) {
+ tex_remainder = (int) (v % dd);
+ return (scaled) u;
+ } else {
+ /*tex The casts are for ms cl. */
+ tex_remainder = -(int) (v % dd);
+ return -(scaled) (u);
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+halfword badness(scaled t, scaled s)
+{
+ /*tex Approximation to $\alpha t/s$, where $\alpha^3\approx 100\cdot2^{18}$ */
+ int r;
+ if (t == 0) {
+ return 0;
+ } else if (s <= 0) {
+ return inf_bad;
+ } else {
+ /*tex $297^3=99.94\times2^{18}$ */
+ if (t <= 7230584) {
+ r = (t * 297) / s;
+ } else if (s >= 1663497) {
+ r = t / (s / 297);
+ } else {
+ r = t;
+ }
+ if (r > 1290) {
+ /*tex $1290^3<2^{31}<1291^3$ */
+ return inf_bad;
+ } else {
+ /*tex This is $r^3/2^{18}$, rounded to the nearest integer. */
+ return ((r * r * r + 0400000) / 01000000);
+ }
+ }
+}
+
+/*tex
+
+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 |integer| (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@>
+
+*/
+
+/*
+
+This section is (almost) straight from MetaPost. I (Taco) had to change the types
+(use |integer| instead of |fraction|), but that should not have any influence on
+the actual calculations (the original comments refer to quantities like
+|fraction_four| ($2^{30}$), and that is the same as the numeric representation of
+|max_dimen|).
+
+I've copied the low-level variables and routines that are needed, but only those
+(e.g. |m_log|), not the accompanying ones like |m_exp|. Most of the following
+low-level numeric routines are only needed within the calculation of |norm_rand|.
+I've been forced to rename |make_fraction| to |make_frac| because TeX already has
+a routine by that name with a wholly different function (it creates a
+|fraction_noad| for math typesetting)
+
+And now let's complete our collection of numeric utility routines by considering
+random number generation. \MP{} generates 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 |fraction_one-1|,
+inclusive.
+
+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.
+
+*/
+
+/*tex The last 55 random values generated: */
+
+static int randoms[55];
+
+/*tex The number of unused |randoms|: */
+
+static int j_random;
+
+/*tex The default random seed: */
+
+scaled random_seed;
+
+/*tex A small bit of \METAPOST\ is needed. */
+
+#define fraction_half 01000000000 /* $2^{27} $, represents 0.50000000 */
+#define fraction_one 02000000000 /* $2^{28} $, represents 1.00000000 */
+#define fraction_four 010000000000 /* $2^{30} $, represents 4.00000000 */
+#define el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */
+
+/*tex
+
+The |make_frac| routine produces the |fraction| equivalent of |p/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_frac(t,t)=fraction| is valid; and it's also possible to use the subroutine
+``backwards,'' using the relation |make_frac(t,fraction)=t| between scaled types.
+
+If the result would have magnitude $2^{31}$ or more, |make_frac| sets
+|arith_error:=true|. Most of \MP'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)div 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.
+
+This operation is part of \MP's ``inner loop''; indeed, it will consume nearly
+10\%! of the running time (exclusive of input and output) if the code below is
+left unchanged. A machine-dependent recoding will therefore make \MP\ run faster.
+The present implementation is highly portable, but slow; it avoids multiplication
+and division except in the initial stage. System wizards should be careful to
+replace it with a routine that is guaranteed to produce identical results in all
+cases. @^system dependencies@>
+
+As noted below, a few more routines should also be replaced by machine-dependent
+code, for efficiency. But when a procedure is not part of the ``inner loop,''
+such changes aren't advisable; simplicity and robustness are preferable to
+trickery, unless the cost is too high.
+
+*/
+
+static int make_frac(int p, int q)
+{
+ /*tex The fraction bits, with a leading 1 bit: */
+ int f;
+ /*tex The integer part of $\vert p/q\vert$: */
+ int n;
+ /*tex Disables certain compiler optimizations: */
+ register int be_careful;
+ /*tex Should the result be negated? */
+ boolean negative = false;
+ if (p < 0) {
+ negate(p);
+ negative = true;
+ }
+ if (q <= 0) {
+ 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) * fraction_one;
+ /*tex_remainder
+
+ Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$. The |repeat| 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.
+
+ */
+ f = 1;
+ do {
+ be_careful = p - q;
+ p = be_careful + p;
+ if (p >= 0)
+ f = f + f + 1;
+ else {
+ f += f;
+ p = p + q;
+ }
+ } while (f < fraction_one);
+ be_careful = p - q;
+ if (be_careful + p >= 0)
+ incr(f);
+
+ if (negative)
+ return (-(f + n));
+ else
+ return (f + n);
+ }
+}
+
+static int take_frac(int q, int f)
+{
+ /*tex The fraction so far: */
+ int p;
+ /*tex Additional multiple of $q$: */
+ int n;
+ /*tex Disables certain compiler optimizations. */
+ register int be_careful;
+ /*tex Should the result be negated? */
+ boolean negative = false;
+ /*tex Reduce to the case that |f>=0| and |q>0|. */
+ if (f < 0) {
+ negate(f);
+ negative = true;
+ }
+ if (q < 0) {
+ negate(q);
+ negative = !negative;
+ }
+ if (f < fraction_one) {
+ n = 0;
+ } else {
+ n = f / fraction_one;
+ f = f % fraction_one;
+ if (q <= el_gordo / n) {
+ n = n * q;
+ } else {
+ arith_error = true;
+ n = el_gordo;
+ }
+ }
+ f = f + fraction_one;
+ /*tex
+
+ Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$. 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}$.
+
+ Here |p| becomes $2^{27}$; the invariants hold now with $k=28$:
+
+ */
+ p = fraction_half;
+ if (q < fraction_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);
+ }
+ 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);
+}
+
+/*tex
+
+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.
+
+*/
+
+/*tex The powers of two: */
+
+static int two_to_the[31];
+
+/*tex Special logarithms: */
+
+static int spec_log[29];
+
+void initialize_arithmetic(void)
+{
+ int k;
+ 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;
+}
+
+static int m_log(int x)
+{
+ /*tex Auxiliary registers: */
+ int y, z;
+ /*tex Iteration counter: */
+ int k;
+ if (x <= 0) {
+ /*tex Handle non-positive logarithm. */
+ print_err("Logarithm of ");
+ print_scaled(x);
+ tprint(" 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;
+ } else {
+ /*tex $14\times2^{27}\ln2\approx1302456956.421063$ */
+ y = 1302456956 + 4 - 100;
+ /*tex $2^{16}\times .421063\approx 27595$ */
+ z = 27595 + 6553600;
+ while (x < fraction_four) {
+ x += x;
+ /*tex $2^{27}\ln2\approx 93032639.74436163$ */
+ y = y - 93032639;
+ /*tex $2^{16}\times.74436163\approx 48782$ */
+ z = z - 48782;
+ }
+
+ y = y + (z / unity);
+ k = 2;
+ while (x > fraction_four + 4) {
+ /*tex
+ Increase |k| until |x| can be multiplied by a factor of $2^{-k}$,
+ and adjust $y$ accordingly. Here $z=\lceil x/2^k\rceil$.
+ */
+ z = ((x - 1) / two_to_the[k]) + 1;
+ while (x < fraction_four + z) {
+ z = halfp(z + 1);
+ k = k + 1;
+ }
+ y = y + spec_log[k];
+ x = x - z;
+ }
+ return (y / 8);
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+static int ab_vs_cd(int a, int b, int c, int d)
+{
+ int q, r;
+ /*tex Reduce to the case that |a,c>=0| and |b,d>0|. */
+ if (a < 0) {
+ negate(a);
+ negate(b);
+ }
+ if (c < 0) {
+ negate(c);
+ negate(d);
+ }
+ if (d <= 0) {
+ if (b >= 0)
+ return (((a == 0 || b == 0) && (c == 0 || d == 0)) ? 0 : 1);
+ if (d == 0)
+ return (a == 0 ? 0 : -1);
+ q = a;
+ a = c;
+ c = q;
+ q = -b;
+ b = -d;
+ d = q;
+ } else if (b <= 0) {
+ if (b < 0 && a > 0)
+ return -1;
+ return (c == 0 ? 0 : -1);
+ }
+ while (1) {
+ q = a / d;
+ r = c / b;
+ if (q != r)
+ return (q > r ? 1 : -1);
+ q = a % d;
+ r = c % b;
+ if (r == 0)
+ return (q == 0 ? 0 : 1);
+ if (q == 0)
+ return -1;
+ a = b;
+ b = q;
+ c = d;
+ d = r;
+ /*tex Now |a>d>0| and |c>b>0|. */
+ }
+}
+
+/*tex
+
+To consume a random integer, the program below will say `|next_random|' and then
+it will fetch |randoms[j_random]|.
+
+*/
+
+#define next_random() do { \
+ if (j_random==0) \
+ new_randoms(); \
+ else \
+ decr(j_random); \
+} while (0)
+
+static void new_randoms(void)
+{
+ /*tex The index into |randoms|. */
+ int k;
+ /*tex The accumulator. */
+ int x;
+ for (k = 0; k <= 23; k++) {
+ x = randoms[k] - randoms[k + 31];
+ if (x < 0)
+ x = x + fraction_one;
+ randoms[k] = x;
+ }
+ for (k = 24; k <= 54; k++) {
+ x = randoms[k] - randoms[k - 24];
+ if (x < 0)
+ x = x + fraction_one;
+ randoms[k] = x;
+ }
+ j_random = 54;
+}
+
+/*tex
+
+To initialize the |randoms| table, we call the following routine.
+
+*/
+
+void init_randoms(int seed)
+{
+ /*tex Three more or less random integers. */
+ int j, jj, k;
+ /*tex The index into |randoms|. */
+ int i;
+ j = abs(seed);
+ while (j >= fraction_one)
+ j = halfp(j);
+ k = 1;
+ for (i = 0; i <= 54; i++) {
+ jj = k;
+ k = j - k;
+ j = jj;
+ if (k < 0)
+ k = k + fraction_one;
+ randoms[(i * 21) % 55] = j;
+ }
+ /*tex We ``warm up'' the array. */
+ new_randoms();
+ new_randoms();
+ new_randoms();
+}
+
+/*tex
+
+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 |take_frac| 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.
+
+*/
+
+int unif_rand(int x)
+{
+ int y;
+ next_random();
+ y = take_frac(abs(x), randoms[j_random]);
+ if (y == abs(x))
+ return 0;
+ else if (x > 0)
+ return y;
+ else
+ return -y;
+}
+
+/*tex
+
+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\/}.
+
+*/
+
+int norm_rand(void)
+{
+ /*tex What the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$. */
+ int x, u, l;
+ do {
+ do {
+ next_random();
+ x = take_frac(112429, randoms[j_random] - fraction_half);
+ /*tex Which is $2^{16}\sqrt{8/e}\approx 112428.82793$. */
+ next_random();
+ u = randoms[j_random];
+ } while (abs(x) >= u);
+ x = make_frac(x, u);
+ /*tex More fuzzyness: $2^{24}\cdot12\ln2\approx139548959.6165$. */
+ l = 139548960 - m_log(u);
+ } while (ab_vs_cd(1024, l, x, x) < 0);
+ return x;
+}
+
+/*tex
+
+This function could also be expressed as a macro, but it is a useful breakpoint
+for debugging.
+
+*/
+
+int fix_int(int val, int min, int max)
+{
+ return (val < min ? min : (val > max ? max : val));
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/arithmetic.h b/Build/source/texk/web2c/luatexdir/tex/arithmetic.h
new file mode 100644
index 00000000000..e5638990e0c
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/arithmetic.h
@@ -0,0 +1,80 @@
+/* arithmetic.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef ARITHMETIC_H
+# define ARITHMETIC_H
+
+/* |incr| and |decr| are in texmfmp.h */
+
+ /* #define incr(A) (A)++ *//* increase a variable by unity */
+ /* #define decr(A) (A)-- *//* decrease a variable by unity */
+
+# define negate(A) (A)=-(A) /* change the sign of a variable */
+
+# undef half
+extern int half(int x);
+
+/*
+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.
+*/
+
+# define unity 0200000 /* $2^{16}$, represents 1.00000 */
+# define two 0400000 /* $2^{17}$, represents 2.00000 */
+
+typedef unsigned int nonnegative_integer; /* $0\L x<2^{31}$ */
+
+extern scaled round_decimals(int k);
+extern void print_scaled(scaled s);
+
+extern boolean arith_error;
+extern scaled tex_remainder;
+
+extern scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer);
+
+# define nx_plus_y(A,B,C) mult_and_add((A),(B),(C),07777777777)
+# define mult_integers(A,B) mult_and_add((A),(B),0,017777777777)
+
+extern scaled x_over_n(scaled x, int n);
+extern scaled xn_over_d(scaled x, int n, int d);
+
+# define inf_bad 10000 /* infinitely bad value */
+
+extern halfword badness(scaled t, scaled s);
+
+# define set_glue_ratio_zero(A) (A)=0.0 /* store the representation of zero ratio */
+# define set_glue_ratio_one(A) (A)=1.0 /* store the representation of unit ratio */
+# define float_cast(A) (float)(A) /* convert from |glue_ratio| to type |real| */
+# define unfloat(A) (glue_ratio)(A) /* convert from |real| to type |glue_ratio| */
+# define float_constant(A) (float)A /* convert |integer| constant to |real| */
+# define float_round round
+
+extern void initialize_arithmetic(void);
+
+extern scaled random_seed; /* the default random seed */
+
+extern void init_randoms(int seed);
+extern int unif_rand(int x);
+extern int norm_rand(void);
+
+extern int fix_int(int val, int min, int max);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/backend.c b/Build/source/texk/web2c/luatexdir/tex/backend.c
new file mode 100644
index 00000000000..3115a88a7e3
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/backend.c
@@ -0,0 +1,124 @@
+/* to fill */
+
+#include "ptexlib.h"
+
+scaled max_v = 0; /* maximum height-plus-depth of pages shipped so far */
+scaled max_h = 0; /* maximum width of pages shipped so far */
+boolean doing_leaders = false; /* are we inside a leader box? */
+int cur_s = -1; /* current depth of output box nesting, initially $-1$ */
+
+static backend_struct *backend = NULL;
+backend_function *backend_out, *backend_out_whatsit, *backend_out_control;
+
+static void missing_backend_function(PDF pdf, halfword p)
+{
+ const char *n = get_node_name(type(p), subtype(p));
+ if (type(p) == whatsit_node)
+ formatted_error("pdf backend","no output function for whatsit %s",n);
+ else
+ formatted_error("pdf backend","no output function for node %s",n);
+}
+
+static void init_none_backend_functions(void)
+{
+ backend_struct *p = &backend[OMODE_NONE];
+ p->name = strdup("NONE");
+}
+
+static void init_pdf_backend_functions(void)
+{
+ backend_struct *p = &backend[OMODE_PDF];
+ p->name = strdup("PDF");
+ p->node_fu[rule_node] = &pdf_place_rule;
+ p->node_fu[glyph_node] = &pdf_place_glyph;
+ p->whatsit_fu[special_node] = &pdf_special;
+ p->whatsit_fu[pdf_literal_node] = &pdf_out_literal;
+ p->whatsit_fu[pdf_refobj_node] = &pdf_ref_obj;
+ p->whatsit_fu[pdf_annot_node] = &do_annot;
+ p->whatsit_fu[pdf_start_link_node] = &do_link;
+ p->whatsit_fu[pdf_end_link_node] = &end_link;
+ p->whatsit_fu[pdf_dest_node] = &do_dest;
+ p->whatsit_fu[pdf_thread_node] = &do_thread;
+ p->whatsit_fu[pdf_end_thread_node] = &end_thread;
+ p->whatsit_fu[late_lua_node] = &late_lua;
+ p->whatsit_fu[pdf_colorstack_node] = &pdf_out_colorstack;
+ p->whatsit_fu[pdf_setmatrix_node] = &pdf_out_setmatrix;
+ p->whatsit_fu[pdf_save_node] = &pdf_out_save;
+ p->whatsit_fu[pdf_restore_node] = &pdf_out_restore;
+ p->whatsit_fu[pdf_link_state_node] = &pdf_out_link_state;
+ p->control_fu[backend_control_push_list] = &pdf_push_list;
+ p->control_fu[backend_control_pop_list] = &pdf_pop_list;
+ p->control_fu[backend_control_begin_page] = &pdf_begin_page;
+ p->control_fu[backend_control_end_page] = &pdf_end_page;
+ p->control_fu[backend_control_open_file] = &pdf_open_file;
+ p->control_fu[backend_control_write_header] = &pdf_write_header;
+ p->control_fu[backend_control_finish_file] = &pdf_finish_file;
+ p->control_fu[backend_control_set_reference_point] = &pdf_set_reference_point;
+}
+
+static void init_dvi_backend_functions(void)
+{
+ backend_struct *p = &backend[OMODE_DVI];
+ p->name = strdup("DVI");
+ p->node_fu[rule_node] = &dvi_place_rule;
+ p->node_fu[glyph_node] = &dvi_place_glyph;
+ p->whatsit_fu[special_node] = &dvi_special;
+ p->whatsit_fu[late_lua_node] = &late_lua;
+ p->control_fu[backend_control_push_list] = &dvi_push_list;
+ p->control_fu[backend_control_pop_list] = &dvi_pop_list;
+ p->control_fu[backend_control_begin_page] = &dvi_begin_page;
+ p->control_fu[backend_control_end_page] = &dvi_end_page;
+ p->control_fu[backend_control_open_file] = &dvi_open_file;
+ p->control_fu[backend_control_write_header] = &dvi_write_header;
+ p->control_fu[backend_control_finish_file] = &dvi_finish_file;
+ p->control_fu[backend_control_set_reference_point] = &dvi_set_reference_point;
+}
+
+
+void init_backend_functionpointers(output_mode o_mode)
+{
+ int i, j;
+ if (backend == NULL) {
+ backend = xmalloc((MAX_OMODE + 1) * sizeof(backend_struct));
+ for (i = 0; i <= MAX_OMODE; i++) {
+ backend[i].node_fu = xmalloc((MAX_NODE_TYPE + 1) * sizeof(backend_function));
+ backend[i].whatsit_fu = xmalloc((MAX_WHATSIT_TYPE + 1) * sizeof(backend_function));
+ backend[i].control_fu = xmalloc((MAX_CONTROL_TYPE + 1) * sizeof(backend_function));
+ for (j = 0; j < MAX_NODE_TYPE + 1; j++)
+ backend[i].node_fu[j] = &missing_backend_function;
+ for (j = 0; j < MAX_WHATSIT_TYPE + 1; j++)
+ backend[i].whatsit_fu[j] = &missing_backend_function;
+ for (j = 0; j < MAX_CONTROL_TYPE + 1; j++)
+ backend[i].control_fu[j] = &missing_backend_function;
+ }
+ init_none_backend_functions();
+ init_dvi_backend_functions();
+ init_pdf_backend_functions();
+ }
+ backend_out = backend[o_mode].node_fu;
+ backend_out_whatsit = backend[o_mode].whatsit_fu;
+ backend_out_control = backend[o_mode].control_fu;
+}
+
+output_mode get_o_mode(void)
+{
+ output_mode o_mode;
+ if (output_mode_par > 0) {
+ o_mode = OMODE_PDF;
+ } else
+ o_mode = OMODE_DVI;
+ return o_mode;
+}
+
+void fix_o_mode(void)
+{
+ output_mode o_mode = get_o_mode();
+ if (output_mode_used == OMODE_NONE) {
+ output_mode_used = o_mode;
+ /*tex Used by synctex, we need to use output_mode_used there: */
+ static_pdf->o_mode = output_mode_used;
+ } else if (output_mode_used != o_mode) {
+ normal_error("pdf backend", "\\outputmode can only be changed before anything is written to the output");
+ }
+ init_backend_functionpointers(output_mode_used);
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/backend.h b/Build/source/texk/web2c/luatexdir/tex/backend.h
new file mode 100644
index 00000000000..5cfbd06fb90
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/backend.h
@@ -0,0 +1,54 @@
+/* to fill */
+
+#ifndef BACKEND_H
+# define BACKEND_H
+
+#include "ptexlib.h"
+
+extern scaled max_v;
+extern scaled max_h;
+extern boolean doing_leaders;
+extern int cur_s;
+
+# define MAX_CONTROL_TYPE 7
+
+typedef enum {
+ backend_control_push_list = 0,
+ backend_control_pop_list,
+ backend_control_begin_page,
+ backend_control_end_page,
+ backend_control_open_file,
+ backend_control_write_header,
+ backend_control_finish_file,
+ backend_control_set_reference_point
+} backend_control_types ;
+
+typedef void (*backend_function) (); /* variadic arguments */
+
+typedef struct {
+ char *name; /* name of the backend */
+ backend_function *node_fu; /* array of node output functions */
+ backend_function *whatsit_fu; /* array of whatsit output functions */
+ backend_function *control_fu; /* array of whatsit output functions */
+} backend_struct;
+
+/* extern pos_info_structure pos_info; */
+
+extern backend_function *backend_out;
+extern backend_function *backend_out_whatsit;
+extern backend_function *backend_out_control;
+
+/* get_o_mode translates from output_mode to output_mode_used */
+/* fix_o_mode freezes output_mode as soon as anything goes through the backend */
+
+/*
+ extern void check_o_mode(PDF pdf, const char *s, int o_mode, boolean errorflag);
+ extern void ensure_output_file_open(PDF pdf, const char *ext);
+*/
+
+extern void fix_o_mode(void);
+extern output_mode get_o_mode(void);
+
+extern void init_backend_functionpointers(output_mode o_mode);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/buildpage.c b/Build/source/texk/web2c/luatexdir/tex/buildpage.c
new file mode 100644
index 00000000000..81e69dc3b2a
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/buildpage.c
@@ -0,0 +1,1164 @@
+/*
+
+Copyright 2009-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+
+#define mode mode_par
+#define head head_par
+#define tail tail_par
+
+/*tex
+
+ 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 |vlink(page_head)|, and it
+ ends at |page_tail|. We have |page_head=page_tail| if this list is empty.
+
+ 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|.
+
+*/
+
+/*tex the final node on the current page */
+
+halfword page_tail;
+
+/*tex what is on the current page so far? */
+
+int page_contents;
+
+/*tex maximum box depth on page being built */
+
+scaled page_max_depth;
+
+/*tex break here to get the best page known so far */
+
+halfword best_page_break;
+
+/*tex the score for this currently best page */
+
+int least_page_cost;
+
+/*tex its |page_goal| */
+
+scaled best_size;
+
+/*tex
+
+ 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 |t$_1$=vlink(page_ins_head)|;
+ node $r_j$ is followed by |t$_{j+1}$=vlink(t$_j$)|; and if there are
+ |n| items we have |$_{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)=65535|, 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_node|, then |height(r)| contains the total of the
+ height-plus-depth dimensions of the box and all its inserts seen so far.
+ |type(r)=split_up_node|, 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.
+
+ 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 |vlink(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.
+
+*/
+
+void initialize_buildpage(void)
+{
+ subtype(page_ins_head) = 65535;
+ type(page_ins_head) = split_up_node;
+ vlink(page_ins_head) = page_ins_head;
+
+ type(page_head) = glue_node;
+ subtype(page_head) = normal;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex height and glue of the current page */
+
+scaled page_so_far[8];
+
+/*tex used to implement \.{\\lastskip} */
+
+halfword last_glue;
+
+/*tex used to implement \.{\\lastpenalty} */
+
+int last_penalty;
+
+/*tex used to implement \.{\\lastkern} */
+
+scaled last_kern;
+
+/*tex used to implement \.{\\lastnodetype} */
+
+int last_node_type;
+
+/*tex sum of the penalties for held-over insertions */
+
+int insert_penalties;
+
+#define print_plus(A,B) do { \
+ if (page_so_far[(A)]!=0) { \
+ tprint(" plus "); \
+ print_scaled(page_so_far[(A)]); \
+ tprint((B)); \
+ } \
+} while (0)
+
+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) {
+ tprint(" minus ");
+ print_scaled(page_shrink);
+ }
+}
+
+/*tex
+
+ Here is a procedure that is called when the |page_contents| is changing from
+ |empty| to |inserts_only| or |box_there|.
+
+*/
+
+#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
+#define set_page_so_far_zero(A) page_so_far[(A)]=0
+
+void freeze_page_specs(int s)
+{
+ page_contents = s;
+ page_goal = vsize_par;
+ page_max_depth = max_depth_par;
+ page_depth = 0;
+ do_all_six(set_page_so_far_zero);
+ least_page_cost = awful_bad;
+ if (tracing_pages_par > 0) {
+ begin_diagnostic();
+ tprint_nl("%% goal height=");
+ print_scaled(page_goal);
+ tprint(", max depth=");
+ print_scaled(page_max_depth);
+ end_diagnostic(false);
+ }
+}
+
+/*tex
+
+ The global variable |output_active| is true during the time the user's output
+ routine is driving \TeX.
+
+*/
+
+/*tex Are we in the midst of an output routine? */
+
+boolean output_active;
+
+/*tex
+
+ 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.)
+
+*/
+
+void start_new_page(void)
+{
+ page_contents = empty;
+ page_tail = page_head;
+ vlink(page_head) = null;
+ last_glue = max_halfword;
+ last_penalty = 0;
+ last_kern = 0;
+ last_node_type = -1;
+ page_depth = 0;
+ page_max_depth = 0;
+}
+
+/*tex
+
+ At certain times box \.{\\outputbox} 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.
+
+*/
+
+static void box_error(int n)
+{
+ error();
+ begin_diagnostic();
+ tprint_nl("The following box has been deleted:");
+ show_box(box(n));
+ end_diagnostic(true);
+ flush_node_list(box(n));
+ box(n) = null;
+}
+
+/*tex
+
+ The following procedure guarantees that a given box register does not contain
+ an \.{\\hbox}.
+
+*/
+
+static void ensure_vbox(int n)
+{
+ halfword p = box(n);
+ if (p != null && type(p) == hlist_node) {
+ print_err("Insertions can only be added to a vbox");
+ 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
+
+ \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.
+
+*/
+
+/*tex Append contributions to the current page. */
+
+void build_page(void)
+{
+ /*tex the node being appended */
+ halfword p;
+ /*tex nodes being examined */
+ halfword q, r;
+ /*tex badness and cost of current page */
+ int b, c;
+ /*tex penalty to be added to the badness */
+ int pi = 0;
+ /*tex insertion box number */
+ int n;
+ /*tex sizes used for insertion calculations */
+ scaled delta, h, w;
+ int id, sk, i;
+ if ((vlink(contrib_head) == null) || output_active)
+ return;
+ do {
+ CONTINUE:
+ p = vlink(contrib_head);
+ /*tex Update the values of |last_glue|, |last_penalty|, and |last_kern|. */
+ if (last_glue != max_halfword) {
+ flush_node(last_glue);
+ last_glue = max_halfword;
+ }
+ last_penalty = 0;
+ last_kern = 0;
+ last_node_type = type(p) + 1;
+ if (type(p) == glue_node) {
+ last_glue = new_glue(p);
+ } else if (type(p) == penalty_node) {
+ last_penalty = penalty(p);
+ } else if (type(p) == kern_node) {
+ last_kern = width(p);
+ }
+ /*tex
+
+ 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 users output routine if there is one.
+
+ 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 the various labels have a well-understood meaning.
+
+ 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.
+
+ 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.
+
+ */
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ case rule_node:
+ if (page_contents < box_there) {
+ /*tex
+
+ Initialize the current page, insert the \.{\\topskip}
+ glue ahead of |p|, and |goto continue|.
+
+ */
+ if (page_contents == empty)
+ freeze_page_specs(box_there);
+ else
+ page_contents = box_there;
+ q = new_skip_param(top_skip_code);
+ if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) {
+ if (width(q) > depth(p))
+ width(q) = width(q) - depth(p);
+ else
+ width(q) = 0;
+ } else {
+ if (width(q) > height(p))
+ width(q) = width(q) - height(p);
+ else
+ width(q) = 0;
+ }
+ couple_nodes(q, p);
+ couple_nodes(contrib_head, q);
+ goto CONTINUE;
+ } else {
+ /*tex
+
+ Prepare to move a box or rule node to the current page,
+ then |goto contribute|.
+
+ */
+ if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) {
+ page_total = page_total + page_depth + depth(p);
+ page_depth = height(p);
+ } else {
+ page_total = page_total + page_depth + height(p);
+ page_depth = depth(p);
+ }
+ goto CONTRIBUTE;
+
+ }
+ break;
+ case boundary_node:
+ case whatsit_node:
+ goto CONTRIBUTE;
+ break;
+ 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 (vlink(p) == null)
+ goto EXIT;
+ else if (type(vlink(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;
+ break;
+ case ins_node:
+ /*tex 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;
+ i = 1 ;
+ while (n >= subtype(vlink(r))) {
+ r = vlink(r);
+ i = i + 1 ;
+ }
+ if (subtype(r) != n) {
+ /*tex
+
+ Create a page insertion node with |subtype(r)=qi(n)|, and
+ include the glue correction for box |n| in the current
+ page state.
+
+ 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.
+
+ */
+ id = callback_defined(build_page_insert_callback);
+ if (id != 0) {
+ run_callback(id, "dd->d",n,i,&sk);
+ } else {
+ sk = n;
+ }
+ q = new_node(inserting_node, n);
+ try_couple_nodes(q, vlink(r));
+ couple_nodes(r, q);
+ r = q;
+ 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(sk);
+ if (count(n) == 1000)
+ h = height(r);
+ else
+ h = x_over_n(height(r), 1000) * count(n);
+ page_goal = page_goal - h - width(q);
+ if (stretch_order(q) > 1)
+ page_so_far[1 + stretch_order(q)] = page_so_far[1 + stretch_order(q)] + stretch(q);
+ else
+ 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 \\skip");
+ 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();
+ }
+
+ }
+ if (type(r) == split_up_node) {
+ insert_penalties = insert_penalties + float_cost(p);
+ } else {
+ last_ins_ptr(r) = p;
+ delta = page_goal - page_total - page_depth + page_shrink;
+ /*tex This much room is left if we shrink the maximum. */
+ if (count(n) == 1000) {
+ h = height(p);
+ } else {
+ /*tex This much room is needed. */
+ h = x_over_n(height(p), 1000) * count(n);
+ }
+ if (((h <= 0) || (h <= delta))
+ && (height(p) + height(r) <= dimen(n))) {
+ page_goal = page_goal - h;
+ height(r) = height(r) + height(p);
+ } else {
+ /*tex
+
+ Find the best way to split the insertion, and change
+ |type(r)| to |split_up_node|.
+
+ 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.
+
+ */
+ 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;
+ if (tracing_pages_par > 0) {
+ /*tex Display the insertion split cost. */
+ begin_diagnostic();
+ tprint_nl("% split");
+ print_int(n);
+ tprint(" to ");
+ print_scaled(w);
+ print_char(',');
+ print_scaled(best_height_plus_depth);
+ tprint(" 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);
+ }
+ 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_node;
+ 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);
+ }
+ }
+ goto CONTRIBUTE;
+
+ break;
+ default:
+ formatted_error("pagebuilder","invalid node of type %d in vertical mode", type(p));
+ break;
+ }
+ /*tex
+
+ Check if node |p| is a new champion breakpoint; then if it is time
+ for a page break, prepare for output, and either fire up the users
+ output routine and |return| or ship out the page and |goto done|.
+
+ */
+ if (pi < inf_penalty) {
+ /*tex
+
+ Compute the badness, |b|, of the current page, using |awful_bad|
+ if the box is too full.
+
+ */
+ 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);
+ }
+ 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;
+ if (tracing_pages_par > 0) {
+ /*tex Display the page break cost. */
+ begin_diagnostic();
+ tprint_nl("%");
+ tprint(" t=");
+ print_totals();
+ tprint(" g=");
+ print_scaled(page_goal);
+ tprint(" b=");
+ if (b == awful_bad)
+ print_char('*');
+ else
+ print_int(b);
+ tprint(" p=");
+ print_int(pi);
+ tprint(" c=");
+ if (c == awful_bad)
+ print_char('*');
+ else
+ print_int(c);
+ if (c <= least_page_cost)
+ print_char('#');
+ end_diagnostic(false);
+ }
+ if (c <= least_page_cost) {
+ best_page_break = p;
+ best_size = page_goal;
+ least_page_cost = c;
+ r = vlink(page_ins_head);
+ while (r != page_ins_head) {
+ best_ins_ptr(r) = last_ins_ptr(r);
+ r = vlink(r);
+ }
+ }
+ if ((c == awful_bad) || (pi <= eject_penalty)) {
+ /*tex Output the current page at the best place. */
+ fire_up(p);
+ if (output_active) {
+ /*tex User's output routine will act. */
+ goto EXIT;
+ }
+ /*tex The page has been shipped out by default output routine. */
+ goto DONE;
+ }
+ }
+ if ((type(p) < glue_node) || (type(p) > kern_node))
+ goto CONTRIBUTE;
+ UPDATE_HEIGHTS:
+ /*tex
+
+ Go here to record glue in the |active_height| table. Update the
+ current page measurements with respect to the glue or kern specified
+ by node~|p|.
+
+ */
+ if (type(p) != kern_node) {
+ if (stretch_order(p) > 1)
+ page_so_far[1 + stretch_order(p)] = page_so_far[1 + stretch_order(p)] + stretch(p);
+ else
+ page_so_far[2 + stretch_order(p)] = page_so_far[2 + stretch_order(p)] + stretch(p);
+ page_shrink = page_shrink + shrink(p);
+ if ((shrink_order(p) != normal) && (shrink(p) != 0)) {
+ print_err("Infinite glue shrinkage found on current page");
+ 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();
+ reset_glue_to_zero(p);
+ shrink_order(p) = normal;
+ }
+ }
+ page_total = page_total + page_depth + width(p);
+ page_depth = 0;
+ CONTRIBUTE:
+ /*tex
+
+ Go here to link a node into the current page. 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;
+ }
+ /*tex Link node |p| into the current page and |goto done|. */
+ couple_nodes(page_tail, p);
+ page_tail = p;
+ try_couple_nodes(contrib_head,vlink(p));
+ vlink(p) = null;
+ goto DONE;
+ DONE1:
+ /*tex Recycle node |p|. */
+ try_couple_nodes(contrib_head,vlink(p));
+ vlink(p) = null;
+ if (saving_vdiscards_par > 0) {
+ if (page_disc == null) {
+ page_disc = p;
+ } else {
+ couple_nodes(tail_page_disc, p);
+ }
+ tail_page_disc = p;
+ } else {
+ flush_node_list(p);
+ }
+ DONE:
+ ;
+ } while (vlink(contrib_head) != null);
+ /*tex Make the contribution list empty by setting its tail to |contrib_head|. */
+ contrib_tail = contrib_head;
+ EXIT:
+ ;
+}
+
+/*tex
+
+ 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 \.{\\outputbox},
+ 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(x)<>null| implies |first_mark(x)<>null|;
+ it also knows that |bot_mark(x)=null| implies
+ |top_mark(x)=first_mark(x)=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.
+
+*/
+
+void fire_up(halfword c)
+{
+ /*tex nodes being examined and/or changed */
+ halfword p, q, r, s;
+ /*tex predecessor of |p| */
+ halfword prev_p;
+ /*tex insertion box number */
+ int n;
+ /*tex should the present insertion be held over? */
+ boolean wait;
+ /*tex saved value of |vbadness| */
+ int save_vbadness;
+ /*tex saved value of |vfuzz| */
+ scaled save_vfuzz;
+ /*tex saved value of |split_top_skip| */
+ halfword save_split_top_skip;
+ /*tex for looping through the marks */
+ halfword i;
+ /*tex 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);
+ }
+
+ for (i = 0; i <= biggest_used_mark; i++) {
+ if (bot_mark(i) != null) {
+ if (top_mark(i) != null)
+ delete_token_ref(top_mark(i));
+ set_top_mark(i, bot_mark(i));
+ add_token_ref(top_mark(i));
+ delete_first_mark(i);
+ }
+ }
+ /*tex
+
+ Put the optimal current page into box |output_box|, update |first_mark|
+ and |bot_mark|, append insertions to their boxes, and put the remaining
+ nodes back on the contribution list.
+
+ 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.
+
+ */
+ if (c == best_page_break) {
+ /*tex |c| not yet linked in */
+ best_page_break = null;
+ }
+ /*tex Ensure that box |output_box| is empty before output. */
+ if (box(output_box_par) != null) {
+ print_err("\\box");
+ print_int(output_box_par);
+ tprint(" is not void");
+ help2(
+ "You shouldn't use \\box\\outputbox except in \\output routines.",
+ "Proceed, and I'll discard its present contents."
+ );
+ box_error(output_box_par);
+ }
+ /*tex This will count the number of insertions held over. */
+ insert_penalties = 0;
+ save_split_top_skip = split_top_skip_par;
+ if (holding_inserts_par <= 0) {
+ /*tex
+
+ Prepare all the boxes involved in insertions to act as queues. 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.
+
+ */
+ r = vlink(page_ins_head);
+ while (r != page_ins_head) {
+ if (best_ins_ptr(r) != null) {
+ n = subtype(r);
+ ensure_vbox(n);
+ if (box(n) == null)
+ box(n) = new_null_box();
+ p = box(n) + list_offset;
+ while (vlink(p) != null)
+ p = vlink(p);
+ last_ins_ptr(r) = p;
+ }
+ r = vlink(r);
+ }
+
+ }
+ q = hold_head;
+ vlink(q) = null;
+ prev_p = page_head;
+ p = vlink(prev_p);
+ while (p != best_page_break) {
+ if (type(p) == ins_node) {
+ if (holding_inserts_par <= 0) {
+ /*tex
+
+ 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.
+
+ 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_node|', the remainder after splitting and pruning
+ (if any) will be carried over to the next page.
+
+ */
+ r = vlink(page_ins_head);
+ while (subtype(r) != subtype(p))
+ r = vlink(r);
+ if (best_ins_ptr(r) == null) {
+ wait = true;
+ } else {
+ wait = false;
+ s = last_ins_ptr(r);
+ vlink(s) = ins_ptr(p);
+ if (best_ins_ptr(r) == p) {
+ halfword t;
+ /*tex
+
+ 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.
+
+ */
+ if (type(r) == split_up_node) {
+ if ((broken_ins(r) == p) && (broken_ptr(r) != null)) {
+ while (vlink(s) != broken_ptr(r))
+ s = vlink(s);
+ vlink(s) = null;
+ split_top_skip_par = split_top_ptr(p);
+ ins_ptr(p) =
+ prune_page_top(broken_ptr(r), false);
+ if (ins_ptr(p) != null) {
+ t = vpack(ins_ptr(p), 0, additional, -1);
+ height(p) = height(t) + depth(t);
+ list_ptr(t) = null;
+ flush_node(t);
+ wait = true;
+ }
+ }
+ }
+ best_ins_ptr(r) = null;
+ n = subtype(r);
+ t = list_ptr(box(n));
+ list_ptr(box(n)) = null;
+ flush_node(box(n));
+ box(n) = vpack(t, 0, additional, body_direction_par);
+
+ } else {
+ while (vlink(s) != null)
+ s = vlink(s);
+ last_ins_ptr(r) = s;
+ }
+ }
+ /*tex
+
+ Either append the insertion node |p| after node |q|, and
+ remove it from the current page, or delete |node(p)|.
+
+ */
+ try_couple_nodes(prev_p, vlink(p));
+ vlink(p) = null;
+ if (wait) {
+ couple_nodes(q, p);
+ q = p;
+ incr(insert_penalties);
+ } else {
+ ins_ptr(p) = null;
+ flush_node(p);
+ }
+ p = prev_p;
+
+ }
+ } else if (type(p) == mark_node) {
+ /*tex Update the values of |first_mark| and |bot_mark|. */
+ if (first_mark(mark_class(p)) == null) {
+ set_first_mark(mark_class(p), mark_ptr(p));
+ add_token_ref(first_mark(mark_class(p)));
+ }
+ if (bot_mark(mark_class(p)) != null)
+ delete_token_ref(bot_mark(mark_class(p)));
+ set_bot_mark(mark_class(p), mark_ptr(p));
+ add_token_ref(bot_mark(mark_class(p)));
+
+ }
+ prev_p = p;
+ p = vlink(prev_p);
+ }
+ split_top_skip_par = save_split_top_skip;
+ /*tex
+
+ Break the current page at node |p|, put it in box~|output_box|, and put
+ the remaining nodes on the contribution list.
+
+ When the following code is executed, the current page runs from node
+ |vlink(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
+ |vlink(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.
+
+ */
+ if (p != null) {
+ if (vlink(contrib_head) == null) {
+ contrib_tail = page_tail;
+ }
+ couple_nodes(page_tail,vlink(contrib_head));
+ couple_nodes(contrib_head, p);
+ vlink(prev_p) = null;
+ }
+ save_vbadness = vbadness_par;
+ vbadness_par = inf_bad;
+ save_vfuzz = vfuzz_par;
+ /*tex Inhibit error messages. */
+ vfuzz_par = max_dimen;
+ box(output_box_par) = filtered_vpackage(vlink(page_head),
+ best_size, exactly, page_max_depth, output_group, body_direction_par, 0, 0);
+ vbadness_par = save_vbadness;
+ vfuzz_par = save_vfuzz;
+ if (last_glue != max_halfword)
+ flush_node(last_glue);
+ /*tex Start a new current page. This sets |last_glue:=max_halfword|. */
+ start_new_page();
+ if (q != hold_head) {
+ vlink(page_head) = vlink(hold_head);
+ page_tail = q;
+ }
+ /*tex Delete the page-insertion nodes. */
+ r = vlink(page_ins_head);
+ while (r != page_ins_head) {
+ /*tex Todo: couple. */
+ q = vlink(r);
+ flush_node(r);
+ r = q;
+ }
+ vlink(page_ins_head) = page_ins_head;
+ for (i = 0; i <= biggest_used_mark; i++) {
+ if ((top_mark(i) != null) && (first_mark(i) == null)) {
+ set_first_mark(i, top_mark(i));
+ add_token_ref(top_mark(i));
+ }
+ }
+ if (output_routine_par != null) {
+ if (dead_cycles >= max_dead_cycles_par) {
+ /*tex Explain that too many dead cycles have occurred in a row. */
+ print_err("Output loop---");
+ print_int(dead_cycles);
+ tprint(" consecutive dead cycles");
+ help3(
+ "I've concluded that your \\output is awry; it never does a",
+ "\\shipout, so I'm shipping \\box\\outputbox out myself. Next time",
+ "increase \\maxdeadcycles if you want me to be more patient!"
+ );
+ error();
+ } else {
+ /*tex Fire up the users output routine and |return|. */
+ output_active = true;
+ incr(dead_cycles);
+ push_nest();
+ mode = -vmode;
+ prev_depth_par = ignore_depth;
+ mode_line_par = -line;
+ begin_token_list(output_routine_par, output_text);
+ new_save_level(output_group);
+ normal_paragraph();
+ scan_left_brace();
+ return;
+ }
+ }
+ /*tex
+
+ Perform the default output routine. The list of heldover insertions,
+ running from |vlink(page_head)| to |page_tail|, must be moved to the
+ contribution list when the user has specified no output routine.
+
+ */
+ if (vlink(page_head) != null) {
+ if (vlink(contrib_head) == null) {
+ contrib_tail = page_tail;
+ } else {
+ vlink(page_tail) = vlink(contrib_head);
+ }
+ vlink(contrib_head) = vlink(page_head);
+ vlink(page_head) = null;
+ page_tail = page_head;
+ }
+ flush_node_list(page_disc);
+ page_disc = null;
+ ship_out(static_pdf, box(output_box_par), SHIPPING_PAGE);
+ box(output_box_par) = null;
+}
+
+/*tex
+
+ When the user's output routine finishes, it has constructed a vlist in
+ internal vertical mode, and \TeX\ will do the following:
+
+*/
+
+void resume_after_output(void)
+{
+ if ((iloc != null) || ((token_type != output_text) && (token_type != backed_up))) {
+ /*tex Recover from an unbalanced output routine */
+ print_err("Unbalanced output routine");
+ help2(
+ "Your sneaky output routine has problematic {'s and/or }'s.",
+ "I can't handle that very well; good luck."
+ );
+ error();
+ /*tex Loops forever if reading from a file, since |null=min_halfword<=0|. */
+ do {
+ get_token();
+ } while (iloc != null);
+ }
+ /*tex Conserve stack space in case more outputs are triggered. */
+ end_token_list();
+ end_graf(bottom_level);
+ unsave();
+ output_active = false;
+ insert_penalties = 0;
+ /*tex Ensure that box |output_box| is empty after output. */
+ if (box(output_box_par) != null) {
+ print_err("Output routine didn't use all of \\box");
+ print_int(output_box_par);
+ help3(
+ "Your \\output commands should empty \\box\\outputbox,",
+ "e.g., by saying `\\shipout\\box\\outputbox'.",
+ "Proceed; I'll discard its present contents."
+ );
+ box_error(output_box_par);
+ }
+ if (tail != head) {
+ /*tex Current list goes after heldover insertions. */
+ try_couple_nodes(page_tail, vlink(head));
+ page_tail = tail;
+ }
+ if (vlink(page_head) != null) {
+ /* Both go before heldover contributions. */
+ if (vlink(contrib_head) == null)
+ contrib_tail = page_tail;
+ try_couple_nodes(page_tail, vlink(contrib_head));
+ try_couple_nodes(contrib_head, vlink(page_head));
+ vlink(page_head) = null;
+ page_tail = page_head;
+ }
+ flush_node_list(page_disc);
+ page_disc = null;
+ pop_nest();
+ normal_page_filter(after_output);
+ build_page();
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/buildpage.h b/Build/source/texk/web2c/luatexdir/tex/buildpage.h
new file mode 100644
index 00000000000..9c013514efa
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/buildpage.h
@@ -0,0 +1,74 @@
+/* buildpage.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef BUILDPAGE_H
+# define BUILDPAGE_H
+
+/* empty = 0 */
+# define inserts_only 1 /* |page_contents| when an insert node has been contributed, but no boxes */
+# define box_there 2 /* |page_contents| when a box or rule has been contributed */
+
+extern halfword page_tail; /* the final node on the current page */
+extern int page_contents; /* what is on the current page so far? */
+extern scaled page_max_depth; /* maximum box depth on page being built */
+extern halfword best_page_break; /* break here to get the best page known so far */
+extern int least_page_cost; /* the score for this currently best page */
+extern scaled best_size; /* its |page_goal| */
+
+/*
+The data structure definitions here use the fact that the |@!height| field
+appears in the fourth word of a box node.
+@^data structure assumptions@>
+*/
+
+# define broken_ptr(A) vlink((A)+2) /* an insertion for this class will break here if anywhere */
+# define broken_ins(A) vinfo((A)+2) /* this insertion might break at |broken_ptr| */
+# define last_ins_ptr(A) vlink((A)+3) /* the most recent insertion for this |subtype| */
+# define best_ins_ptr(A) vinfo((A)+3) /* the optimum most recent insertion */
+
+extern void initialize_buildpage(void);
+
+# define page_goal page_so_far[0] /* desired height of information on page being built */
+# define page_total page_so_far[1] /* height of the current page */
+# define page_shrink page_so_far[6] /* shrinkability of the current page */
+# define page_depth page_so_far[7] /* depth of the current page */
+
+extern scaled page_so_far[8]; /* height and glue of the current page */
+extern halfword last_glue; /* used to implement \.{\\lastskip */
+extern int last_penalty; /* used to implement \.{\\lastpenalty} */
+extern scaled last_kern; /* used to implement \.{\\lastkern} */
+extern int last_node_type; /* used to implement \.{\\lastnodetype} */
+extern int insert_penalties; /* sum of the penalties for held-over insertions */
+
+extern void print_totals(void);
+extern void freeze_page_specs(int s);
+
+extern boolean output_active;
+
+extern void start_new_page(void);
+
+# define contrib_tail nest[0].tail_field
+ /* tail of the contribution list */
+
+extern void build_page(void); /* append contributions to the current page */
+extern void fire_up(halfword c);
+extern void resume_after_output(void);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.c b/Build/source/texk/web2c/luatexdir/tex/commands.c
new file mode 100644
index 00000000000..261f23f4a83
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/commands.c
@@ -0,0 +1,936 @@
+/*
+
+commands.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+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.
+
+*/
+
+void initialize_commands(void)
+{
+
+ primitive_tex("lineskip", assign_glue_cmd, glue_base + line_skip_code, glue_base);
+ primitive_tex("baselineskip", assign_glue_cmd, glue_base + baseline_skip_code, glue_base);
+ primitive_tex("parskip", assign_glue_cmd, glue_base + par_skip_code, glue_base);
+ primitive_tex("abovedisplayskip", assign_glue_cmd, glue_base + above_display_skip_code, glue_base);
+ primitive_tex("belowdisplayskip", assign_glue_cmd, glue_base + below_display_skip_code, glue_base);
+ primitive_tex("abovedisplayshortskip", assign_glue_cmd, glue_base + above_display_short_skip_code, glue_base);
+ primitive_tex("belowdisplayshortskip", assign_glue_cmd, glue_base + below_display_short_skip_code, glue_base);
+ primitive_tex("leftskip", assign_glue_cmd, glue_base + left_skip_code, glue_base);
+ primitive_tex("rightskip", assign_glue_cmd, glue_base + right_skip_code, glue_base);
+ primitive_tex("topskip", assign_glue_cmd, glue_base + top_skip_code, glue_base);
+ primitive_tex("splittopskip", assign_glue_cmd, glue_base + split_top_skip_code, glue_base);
+ primitive_tex("tabskip", assign_glue_cmd, glue_base + tab_skip_code, glue_base);
+ primitive_tex("spaceskip", assign_glue_cmd, glue_base + space_skip_code, glue_base);
+ primitive_tex("xspaceskip", assign_glue_cmd, glue_base + xspace_skip_code, glue_base);
+ primitive_tex("parfillskip", assign_glue_cmd, glue_base + par_fill_skip_code, glue_base);
+ primitive_tex("thinmuskip", assign_mu_glue_cmd, glue_base + thin_mu_skip_code, glue_base + thin_mu_skip_code);
+ primitive_tex("medmuskip", assign_mu_glue_cmd, glue_base + med_mu_skip_code, glue_base + thin_mu_skip_code);
+ primitive_tex("thickmuskip", assign_mu_glue_cmd, glue_base + thick_mu_skip_code, glue_base + thin_mu_skip_code);
+ primitive_luatex("mathsurroundskip", assign_glue_cmd, glue_base + math_skip_code, glue_base);
+ primitive_luatex("mathsurroundmode", assign_int_cmd, int_base + math_skip_mode_code, int_base);
+ primitive_luatex("mathscriptboxmode", assign_int_cmd, int_base + math_script_box_mode_code, int_base);
+ primitive_luatex("mathscriptcharmode", assign_int_cmd, int_base + math_script_char_mode_code, int_base);
+ primitive_luatex("mathrulethicknessmode", assign_int_cmd, int_base + math_rule_thickness_mode_code, int_base);
+ primitive_luatex("mathflattenmode", assign_int_cmd, int_base + math_flatten_mode_code, int_base);
+ primitive_tex("output", assign_toks_cmd, output_routine_loc, local_base);
+ primitive_tex("everypar", assign_toks_cmd, every_par_loc, local_base);
+ primitive_tex("everymath", assign_toks_cmd, every_math_loc, local_base);
+ primitive_tex("everydisplay", assign_toks_cmd, every_display_loc, local_base);
+ primitive_tex("everyhbox", assign_toks_cmd, every_hbox_loc, local_base);
+ primitive_tex("everyvbox", assign_toks_cmd, every_vbox_loc, local_base);
+ primitive_tex("everyjob", assign_toks_cmd, every_job_loc, local_base);
+ primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base);
+ primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base);
+
+ /*tex The integer parameter names must be entered into the hash table */
+
+ primitive_tex("pretolerance", assign_int_cmd, int_base + pretolerance_code, int_base);
+ primitive_tex("tolerance", assign_int_cmd, int_base + tolerance_code, int_base);
+ primitive_tex("linepenalty", assign_int_cmd, int_base + line_penalty_code, int_base);
+ primitive_tex("hyphenpenalty", assign_int_cmd, int_base + hyphen_penalty_code, int_base);
+ primitive_tex("exhyphenpenalty", assign_int_cmd, int_base + ex_hyphen_penalty_code, int_base);
+ primitive_tex("clubpenalty", assign_int_cmd, int_base + club_penalty_code, int_base);
+ primitive_tex("widowpenalty", assign_int_cmd, int_base + widow_penalty_code, int_base);
+ primitive_tex("displaywidowpenalty", assign_int_cmd, int_base + display_widow_penalty_code, int_base);
+ primitive_tex("brokenpenalty", assign_int_cmd, int_base + broken_penalty_code, int_base);
+ primitive_tex("binoppenalty", assign_int_cmd, int_base + bin_op_penalty_code, int_base);
+ primitive_luatex("prerelpenalty", assign_int_cmd, int_base + pre_rel_penalty_code, int_base);
+ primitive_luatex("prebinoppenalty", assign_int_cmd, int_base + pre_bin_op_penalty_code, int_base);
+ primitive_tex("relpenalty", assign_int_cmd, int_base + rel_penalty_code, int_base);
+ primitive_tex("predisplaypenalty", assign_int_cmd, int_base + pre_display_penalty_code, int_base);
+ primitive_tex("postdisplaypenalty", assign_int_cmd, int_base + post_display_penalty_code, int_base);
+ primitive_luatex("mathpenaltiesmode", assign_int_cmd, int_base + math_penalties_mode_code, int_base);
+ primitive_luatex("mathdelimitersmode", assign_int_cmd, int_base + math_delimiters_mode_code, int_base);
+ primitive_tex("interlinepenalty", assign_int_cmd, int_base + inter_line_penalty_code, int_base);
+ primitive_tex("doublehyphendemerits", assign_int_cmd, int_base + double_hyphen_demerits_code, int_base);
+ primitive_tex("finalhyphendemerits", assign_int_cmd, int_base + final_hyphen_demerits_code, int_base);
+ primitive_tex("adjdemerits", assign_int_cmd, int_base + adj_demerits_code, int_base);
+ primitive_tex("mag", assign_int_cmd, int_base + mag_code, int_base);
+ primitive_tex("delimiterfactor", assign_int_cmd, int_base + delimiter_factor_code, int_base);
+ primitive_tex("looseness", assign_int_cmd, int_base + looseness_code, int_base);
+ primitive_tex("time", assign_int_cmd, int_base + time_code, int_base);
+ primitive_tex("day", assign_int_cmd, int_base + day_code, int_base);
+ primitive_tex("month", assign_int_cmd, int_base + month_code, int_base);
+ primitive_tex("year", assign_int_cmd, int_base + year_code, int_base);
+ primitive_tex("showboxbreadth", assign_int_cmd, int_base + show_box_breadth_code, int_base);
+ primitive_tex("showboxdepth", assign_int_cmd, int_base + show_box_depth_code, int_base);
+ primitive_tex("hbadness", assign_int_cmd, int_base + hbadness_code, int_base);
+ primitive_tex("vbadness", assign_int_cmd, int_base + vbadness_code, int_base);
+ primitive_tex("pausing", assign_int_cmd, int_base + pausing_code, int_base);
+ primitive_tex("tracingonline", assign_int_cmd, int_base + tracing_online_code, int_base);
+ primitive_tex("tracingmacros", assign_int_cmd, int_base + tracing_macros_code, int_base);
+ primitive_tex("tracingstats", assign_int_cmd, int_base + tracing_stats_code, int_base);
+ primitive_tex("tracingparagraphs", assign_int_cmd, int_base + tracing_paragraphs_code, int_base);
+ primitive_tex("tracingpages", assign_int_cmd, int_base + tracing_pages_code, int_base);
+ primitive_tex("tracingoutput", assign_int_cmd, int_base + tracing_output_code, int_base);
+ primitive_tex("tracinglostchars", assign_int_cmd, int_base + tracing_lost_chars_code, int_base);
+ primitive_tex("tracingcommands", assign_int_cmd, int_base + tracing_commands_code, int_base);
+ primitive_tex("tracingrestores", assign_int_cmd, int_base + tracing_restores_code, int_base);
+ primitive_tex("uchyph", assign_int_cmd, int_base + uc_hyph_code, int_base);
+ primitive_tex("outputpenalty", assign_int_cmd, int_base + output_penalty_code, int_base);
+ primitive_tex("maxdeadcycles", assign_int_cmd, int_base + max_dead_cycles_code, int_base);
+ primitive_tex("hangafter", assign_int_cmd, int_base + hang_after_code, int_base);
+ primitive_tex("floatingpenalty", assign_int_cmd, int_base + floating_penalty_code, int_base);
+ primitive_tex("globaldefs", assign_int_cmd, int_base + global_defs_code, int_base);
+ primitive_tex("fam", assign_int_cmd, int_base + cur_fam_code, int_base);
+ primitive_tex("escapechar", assign_int_cmd, int_base + escape_char_code, int_base);
+ primitive_tex("defaulthyphenchar", assign_int_cmd, int_base + default_hyphen_char_code, int_base);
+ primitive_tex("defaultskewchar", assign_int_cmd, int_base + default_skew_char_code, int_base);
+ primitive_tex("endlinechar", assign_int_cmd, int_base + end_line_char_code, int_base);
+ primitive_tex("newlinechar", assign_int_cmd, int_base + new_line_char_code, int_base);
+ primitive_tex("language", assign_int_cmd, int_base + language_code, int_base);
+ primitive_tex("setlanguage", assign_int_cmd, int_base + cur_lang_code, int_base);
+ primitive_tex("firstvalidlanguage", assign_int_cmd, int_base + first_valid_language_code, int_base);
+ primitive_tex("exhyphenchar", assign_int_cmd, int_base + ex_hyphen_char_code, int_base);
+ primitive_tex("lefthyphenmin", assign_int_cmd, int_base + left_hyphen_min_code, int_base);
+ primitive_tex("righthyphenmin", assign_int_cmd, int_base + right_hyphen_min_code, int_base);
+ primitive_tex("holdinginserts", assign_int_cmd, int_base + holding_inserts_code, int_base);
+ primitive_tex("errorcontextlines", assign_int_cmd, int_base + error_context_lines_code, int_base);
+ primitive_luatex("nokerns", assign_int_cmd, int_base + disable_kern_code, int_base);
+ primitive_luatex("noligs", assign_int_cmd, int_base + disable_lig_code, int_base);
+ primitive_luatex("nospaces", assign_int_cmd, int_base + disable_space_code, int_base);
+ primitive_luatex("catcodetable", assign_int_cmd, int_base + cat_code_table_code, int_base);
+ primitive_luatex("outputbox", assign_int_cmd, int_base + output_box_code, int_base);
+ primitive_luatex("outputmode", assign_int_cmd, int_base + output_mode_code, int_base);
+ primitive_luatex("adjustspacing", assign_int_cmd, int_base + adjust_spacing_code, int_base);
+ primitive_luatex("protrudechars", assign_int_cmd, int_base + protrude_chars_code, int_base);
+ primitive_luatex("tracingfonts", assign_int_cmd, int_base + tracing_fonts_code, int_base);
+ primitive_luatex("draftmode", assign_int_cmd, int_base + draft_mode_code, int_base);
+ primitive_tex("parindent", assign_dimen_cmd, dimen_base + par_indent_code, dimen_base);
+ primitive_tex("mathsurround", assign_dimen_cmd, dimen_base + math_surround_code, dimen_base);
+ primitive_tex("lineskiplimit", assign_dimen_cmd, dimen_base + line_skip_limit_code, dimen_base);
+ primitive_tex("hsize", assign_dimen_cmd, dimen_base + hsize_code, dimen_base);
+ primitive_tex("vsize", assign_dimen_cmd, dimen_base + vsize_code, dimen_base);
+ primitive_tex("maxdepth", assign_dimen_cmd, dimen_base + max_depth_code, dimen_base);
+ primitive_tex("splitmaxdepth", assign_dimen_cmd, dimen_base + split_max_depth_code, dimen_base);
+ primitive_tex("boxmaxdepth", assign_dimen_cmd, dimen_base + box_max_depth_code, dimen_base);
+ primitive_tex("hfuzz", assign_dimen_cmd, dimen_base + hfuzz_code, dimen_base);
+ primitive_tex("vfuzz", assign_dimen_cmd, dimen_base + vfuzz_code, dimen_base);
+ primitive_tex("delimitershortfall", assign_dimen_cmd, dimen_base + delimiter_shortfall_code, dimen_base);
+ primitive_tex("nulldelimiterspace", assign_dimen_cmd, dimen_base + null_delimiter_space_code, dimen_base);
+ primitive_tex("scriptspace", assign_dimen_cmd, dimen_base + script_space_code, dimen_base);
+ primitive_tex("predisplaysize", assign_dimen_cmd, dimen_base + pre_display_size_code, dimen_base);
+ primitive_tex("displaywidth", assign_dimen_cmd, dimen_base + display_width_code, dimen_base);
+ primitive_tex("displayindent", assign_dimen_cmd, dimen_base + display_indent_code, dimen_base);
+ primitive_tex("overfullrule", assign_dimen_cmd, dimen_base + overfull_rule_code, dimen_base);
+ primitive_tex("hangindent", assign_dimen_cmd, dimen_base + hang_indent_code, dimen_base);
+ primitive_tex("hoffset", assign_dimen_cmd, dimen_base + h_offset_code, dimen_base);
+ primitive_tex("voffset", assign_dimen_cmd, dimen_base + v_offset_code, dimen_base);
+ primitive_tex("emergencystretch", assign_dimen_cmd, dimen_base + emergency_stretch_code, dimen_base);
+ primitive_luatex("pagewidth", assign_dimen_cmd, dimen_base + page_width_code, dimen_base);
+ primitive_luatex("pageheight", assign_dimen_cmd, dimen_base + page_height_code, dimen_base);
+ primitive_luatex("pxdimen", assign_dimen_cmd, dimen_base + px_dimen_code, dimen_base);
+ primitive_luatex("predisplaygapfactor", assign_int_cmd, int_base + math_pre_display_gap_factor_code, int_base);
+ primitive_luatex("hyphenpenaltymode", assign_int_cmd, int_base + hyphen_penalty_mode_code, int_base);
+ primitive_luatex("automatichyphenpenalty", assign_int_cmd, int_base + automatic_hyphen_penalty_code, int_base);
+ primitive_luatex("explicithyphenpenalty", assign_int_cmd, int_base + explicit_hyphen_penalty_code, int_base);
+ primitive_luatex("automatichyphenmode", assign_int_cmd, int_base + automatic_hyphen_mode_code, int_base);
+ primitive_luatex("compoundhyphenmode", assign_int_cmd, int_base + compound_hyphen_mode_code, int_base);
+ primitive_luatex("breakafterdirmode", assign_int_cmd, int_base + break_after_dir_mode_code, int_base);
+ primitive_luatex("exceptionpenalty", assign_int_cmd, int_base + exception_penalty_code, int_base);
+ primitive_luatex("fixupboxesmode", assign_int_cmd, int_base + fixup_boxes_code, int_base);
+ primitive_luatex("glyphdimensionsmode", assign_int_cmd, int_base + glyph_dimensions_code, int_base);
+
+ /*tex
+
+ 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:
+
+ */
+
+ primitive_tex(" ", ex_space_cmd, 0, 0);
+ primitive_tex("/", ital_corr_cmd, 0, 0);
+ primitive_tex("accent", accent_cmd, 0, 0);
+ primitive_tex("advance", advance_cmd, 0, 0);
+ primitive_tex("afterassignment", after_assignment_cmd, 0, 0);
+ primitive_tex("aftergroup", after_group_cmd, 0, 0);
+ primitive_tex("begingroup", begin_group_cmd, 0, 0);
+ primitive_tex("char", char_num_cmd, 0, 0);
+ primitive_tex("csname", cs_name_cmd, 0, 0);
+ primitive_luatex("lastnamedcs", cs_name_cmd, 1, 0);
+ primitive_luatex("begincsname", cs_name_cmd, 2, 0);
+ primitive_tex("delimiter", delim_num_cmd, 0, 0);
+ primitive_luatex("Udelimiter", delim_num_cmd, 1, 0);
+ primitive_tex("divide", divide_cmd, 0, 0);
+ primitive_tex("endcsname", end_cs_name_cmd, 0, 0);
+ primitive_tex("endgroup", end_group_cmd, 0, 0);
+ cs_text(frozen_end_group) = maketexstring("endgroup");
+ eqtb[frozen_end_group] = eqtb[cur_val];
+ primitive_tex("expandafter", expand_after_cmd, 0, 0);
+ primitive_tex("font", def_font_cmd, 0, 0);
+ primitive_luatex("letterspacefont", letterspace_font_cmd, 0, 0);
+ primitive_luatex("expandglyphsinfont", normal_cmd, expand_font_code, 0);
+ primitive_luatex("copyfont", copy_font_cmd, 0, 0);
+ primitive_luatex("setfontid", set_font_id_cmd, 0, 0);
+ primitive_tex("fontdimen", assign_font_dimen_cmd, 0, 0);
+ primitive_tex("halign", halign_cmd, 0, 0);
+ primitive_tex("hrule", hrule_cmd, 0, 0);
+ primitive_luatex("nohrule", no_hrule_cmd, 0, 0);
+ primitive_tex("ignorespaces", ignore_spaces_cmd, 0, 0);
+ primitive_tex("insert", insert_cmd, 0, 0);
+ primitive_luatex("leftghost", char_ghost_cmd, 0, 0);
+ primitive_tex("mark", mark_cmd, 0, 0);
+ primitive_tex("mathaccent", math_accent_cmd, 0, 0);
+ primitive_luatex("Umathaccent", math_accent_cmd, 1, 0);
+ primitive_tex("mathchar", math_char_num_cmd, 0, 0);
+ primitive_luatex("Umathchar", math_char_num_cmd, 1, 0);
+ primitive_luatex("Umathcharnum", math_char_num_cmd, 2, 0);
+ primitive_tex("mathchoice", math_choice_cmd, 0, 0);
+ primitive_luatex("Ustack", math_choice_cmd, 1, 0);
+ primitive_tex("multiply", multiply_cmd, 0, 0);
+ primitive_tex("noalign", no_align_cmd, 0, 0);
+ primitive_tex("noboundary", boundary_cmd, 0, 0);
+ primitive_tex("boundary", boundary_cmd, 1, 0);
+ primitive_tex("protrusionboundary", boundary_cmd, 2, 0);
+ primitive_tex("wordboundary", boundary_cmd, 3, 0);
+ primitive_tex("noexpand", no_expand_cmd, 0, 0);
+ primitive_luatex("primitive", no_expand_cmd, 1, 0);
+ primitive_tex("nonscript", non_script_cmd, 0, 0);
+ primitive_tex("omit", omit_cmd, 0, 0);
+ primitive_tex("parshape", set_tex_shape_cmd, par_shape_loc, par_shape_loc);
+ primitive_tex("penalty", break_penalty_cmd, 0, 0);
+ primitive_tex("prevgraf", set_prev_graf_cmd, 0, 0);
+ primitive_tex("radical", radical_cmd, 0, 0);
+ primitive_luatex("Uradical", radical_cmd, 1, 0);
+ primitive_luatex("Uroot", radical_cmd, 2, 0);
+ primitive_luatex("Uunderdelimiter", radical_cmd, 3, 0);
+ primitive_luatex("Uoverdelimiter", radical_cmd, 4, 0);
+ primitive_luatex("Udelimiterunder", radical_cmd, 5, 0);
+ primitive_luatex("Udelimiterover", radical_cmd, 6, 0);
+ primitive_luatex("Uhextensible", radical_cmd, 7, 0);
+ primitive_tex("read", read_to_cs_cmd, 0, 0);
+ primitive_tex("relax", relax_cmd, too_big_char, too_big_char);
+ cs_text(frozen_relax) = maketexstring("relax");
+ eqtb[frozen_relax] = eqtb[cur_val];
+ primitive_luatex("rightghost", char_ghost_cmd, 1, 0);
+ primitive_tex("setbox", set_box_cmd, 0, 0);
+ primitive_tex("the", the_cmd, 0, 0);
+ primitive_luatex("toksapp", combine_toks_cmd, 0, 0);
+ primitive_luatex("etoksapp", combine_toks_cmd, 1, 0);
+ primitive_luatex("tokspre", combine_toks_cmd, 2, 0);
+ primitive_luatex("etokspre", combine_toks_cmd, 3, 0);
+ primitive_luatex("gtoksapp", combine_toks_cmd, 4, 0);
+ primitive_luatex("xtoksapp", combine_toks_cmd, 5, 0);
+ primitive_luatex("gtokspre", combine_toks_cmd, 6, 0);
+ primitive_luatex("xtokspre", combine_toks_cmd, 7, 0);
+ primitive_tex("toks", toks_register_cmd, 0, 0);
+ primitive_tex("vadjust", vadjust_cmd, 0, 0);
+ primitive_tex("valign", valign_cmd, 0, 0);
+ primitive_tex("vcenter", vcenter_cmd, 0, 0);
+ primitive_tex("vrule", vrule_cmd, 0, 0);
+ primitive_luatex("novrule", no_vrule_cmd, 0, 0);
+ primitive_luatex("luafunctioncall", lua_function_call_cmd, 0, 0);
+ primitive_luatex("luabytecodecall", lua_bytecode_call_cmd, 0, 0);
+ primitive_luatex("luadef", def_lua_call_cmd, 0, 0);
+ primitive_tex("par", par_end_cmd, too_big_char, too_big_char);
+ par_loc = cur_val;
+ par_token = cs_token_flag + par_loc;
+
+ /*tex
+ The processing of \.{\\input} involves the |start_input| subroutine,
+ which will be declared later; the processing of \.{\\endinput} is trivial.
+ */
+
+ primitive_tex("input", input_cmd, 0, 0);
+ primitive_tex("endinput", input_cmd, 1, 0);
+ primitive_tex("topmark", top_bot_mark_cmd, top_mark_code, 0);
+ primitive_tex("firstmark", top_bot_mark_cmd, first_mark_code, 0);
+ primitive_tex("botmark", top_bot_mark_cmd, bot_mark_code, 0);
+ primitive_tex("splitfirstmark", top_bot_mark_cmd, split_first_mark_code, 0);
+ primitive_tex("splitbotmark", top_bot_mark_cmd, split_bot_mark_code, 0);
+ primitive_luatex("clearmarks", mark_cmd, clear_marks_code, 0);
+ primitive_etex("marks", mark_cmd, marks_code, 0);
+ primitive_etex("topmarks", top_bot_mark_cmd, top_mark_code + marks_code, 0);
+ primitive_etex("firstmarks", top_bot_mark_cmd, first_mark_code + marks_code, 0);
+ primitive_etex("botmarks", top_bot_mark_cmd, bot_mark_code + marks_code, 0);
+ primitive_etex("splitfirstmarks", top_bot_mark_cmd, split_first_mark_code + marks_code, 0);
+ primitive_etex("splitbotmarks", top_bot_mark_cmd, split_bot_mark_code + marks_code, 0);
+
+ /*
+ The hash table is initialized with `\.{\\count}', `\.{\\attribute}',
+ `\.{\\dimen}', `\.{\\skip}', and `\.{\\muskip}' all having |register|
+ as their command code; they are distinguished by the |chr_code|, which
+ is either |int_val|, |attr_val|, |dimen_val|, |glue_val|, or |mu_val|.
+ */
+
+ primitive_tex("count", register_cmd, int_val_level, 0);
+ primitive_luatex("attribute", register_cmd, attr_val_level, 0);
+ primitive_tex("dimen", register_cmd, dimen_val_level, 0);
+ primitive_tex("skip", register_cmd, glue_val_level, 0);
+ primitive_tex("muskip", register_cmd, mu_val_level, 0);
+
+ primitive_tex("spacefactor", set_aux_cmd, hmode, 0);
+ primitive_tex("prevdepth", set_aux_cmd, vmode, 0);
+ primitive_tex("deadcycles", set_page_int_cmd, 0, 0);
+ primitive_tex("insertpenalties", set_page_int_cmd, 1, 0);
+ primitive_tex("wd", set_box_dimen_cmd, width_offset, 0);
+ primitive_tex("ht", set_box_dimen_cmd, height_offset, 0);
+ primitive_tex("dp", set_box_dimen_cmd, depth_offset, 0);
+ primitive_tex("lastpenalty", last_item_cmd, lastpenalty_code, 0);
+ primitive_tex("lastkern", last_item_cmd, lastkern_code, 0);
+ primitive_tex("lastskip", last_item_cmd, lastskip_code, 0);
+ primitive_tex("inputlineno", last_item_cmd, input_line_no_code, 0);
+ primitive_tex("badness", last_item_cmd, badness_code, 0);
+ primitive_luatex("luatexversion", last_item_cmd, luatex_version_code, 0);
+ primitive_luatex("lastsavedboxresourceindex", last_item_cmd, last_saved_box_resource_index_code, 0);
+ primitive_luatex("lastsavedimageresourceindex", last_item_cmd, last_saved_image_resource_index_code, 0);
+ primitive_luatex("lastsavedimageresourcepages", last_item_cmd, last_saved_image_resource_pages_code, 0);
+ primitive_luatex("lastxpos", last_item_cmd, last_x_pos_code, 0);
+ primitive_luatex("lastypos", last_item_cmd, last_y_pos_code, 0);
+ primitive_luatex("randomseed", last_item_cmd, random_seed_code, 0);
+
+ primitive_tex("number", convert_cmd, number_code, 0);
+ primitive_tex("romannumeral", convert_cmd, roman_numeral_code, 0);
+ primitive_tex("string", convert_cmd, string_code, 0);
+ primitive_luatex("csstring", convert_cmd, cs_string_code, 0);
+ primitive_tex("meaning", convert_cmd, meaning_code, 0);
+ primitive_etex("eTeXVersion", convert_cmd, etex_code, 0);
+ primitive_tex("fontname", convert_cmd, font_name_code, 0);
+ primitive_luatex("fontid", convert_cmd, font_id_code, 0);
+ primitive_luatex("luatexrevision", convert_cmd, luatex_revision_code, 0);
+ primitive_luatex("luatexbanner", convert_cmd, luatex_banner_code, 0);
+ primitive_luatex("leftmarginkern", convert_cmd, left_margin_kern_code, 0);
+ primitive_luatex("rightmarginkern", convert_cmd, right_margin_kern_code, 0);
+ primitive_luatex("uniformdeviate", convert_cmd, uniform_deviate_code, 0);
+ primitive_luatex("normaldeviate", convert_cmd, normal_deviate_code, 0);
+ primitive_core("directlua", convert_cmd, lua_code, 0);
+ primitive_luatex("luafunction", convert_cmd, lua_function_code, 0);
+ primitive_luatex("luabytecode", convert_cmd, lua_bytecode_code, 0);
+ primitive_luatex("luaescapestring", convert_cmd, lua_escape_string_code, 0);
+ primitive_luatex("mathstyle", convert_cmd, math_style_code, 0);
+ primitive_luatex("expanded", convert_cmd, expanded_code, 0);
+ primitive_luatex("immediateassignment", convert_cmd, immediate_assignment_code, 0);
+ primitive_luatex("immediateassigned", convert_cmd, immediate_assigned_code, 0);
+ primitive_tex("jobname", convert_cmd, job_name_code, 0);
+ primitive_luatex("formatname", convert_cmd, format_name_code, 0);
+ primitive_luatex("Uchar", convert_cmd, uchar_code, 0);
+
+ primitive_luatex("Umathcharclass", convert_cmd, math_char_class_code, 0);
+ primitive_luatex("Umathcharfam", convert_cmd, math_char_fam_code, 0);
+ primitive_luatex("Umathcharslot", convert_cmd, math_char_slot_code, 0);
+
+ primitive_tex("if", if_test_cmd, if_char_code, 0);
+ primitive_tex("ifcat", if_test_cmd, if_cat_code, 0);
+ primitive_tex("ifnum", if_test_cmd, if_int_code, 0);
+ primitive_tex("ifdim", if_test_cmd, if_dim_code, 0);
+ primitive_tex("ifodd", if_test_cmd, if_odd_code, 0);
+ primitive_tex("ifvmode", if_test_cmd, if_vmode_code, 0);
+ primitive_tex("ifhmode", if_test_cmd, if_hmode_code, 0);
+ primitive_tex("ifmmode", if_test_cmd, if_mmode_code, 0);
+ primitive_tex("ifinner", if_test_cmd, if_inner_code, 0);
+ primitive_tex("ifvoid", if_test_cmd, if_void_code, 0);
+ primitive_tex("ifhbox", if_test_cmd, if_hbox_code, 0);
+ primitive_tex("ifvbox", if_test_cmd, if_vbox_code, 0);
+ primitive_tex("ifx", if_test_cmd, if_x_code, 0);
+ primitive_tex("ifeof", if_test_cmd, if_eof_code, 0);
+ primitive_tex("iftrue", if_test_cmd, if_true_code, 0);
+ primitive_tex("iffalse", if_test_cmd, if_false_code, 0);
+ primitive_tex("ifcase", if_test_cmd, if_case_code, 0);
+ primitive_luatex("ifprimitive", if_test_cmd, if_primitive_code, 0);
+ primitive_luatex("ifcondition", if_test_cmd, if_condition_code, 0);
+ primitive_tex("fi", fi_or_else_cmd, fi_code, 0);
+ cs_text(frozen_fi) = maketexstring("fi");
+ eqtb[frozen_fi] = eqtb[cur_val];
+ primitive_tex("or", fi_or_else_cmd, or_code, 0);
+ primitive_tex("else", fi_or_else_cmd, else_code, 0);
+
+ /*
+ \TeX\ always knows at least one font, namely the null font. It has no
+ characters, and its seven parameters are all equal to zero.
+ */
+
+ primitive_tex("nullfont", set_font_cmd, null_font, 0);
+ cs_text(frozen_null_font) = maketexstring("nullfont");
+ eqtb[frozen_null_font] = eqtb[cur_val];
+
+ primitive_tex("span", tab_mark_cmd, span_code, tab_mark_cmd_code);
+ primitive_luatex("aligntab", tab_mark_cmd, tab_mark_cmd_code, tab_mark_cmd_code);
+ primitive_luatex("alignmark", mac_param_cmd, tab_mark_cmd_code, tab_mark_cmd_code);
+ primitive_tex("cr", car_ret_cmd, cr_code, cr_code);
+ cs_text(frozen_cr) = maketexstring("cr");
+ eqtb[frozen_cr] = eqtb[cur_val];
+ primitive_tex("crcr", car_ret_cmd, cr_cr_code, cr_code);
+ cs_text(frozen_end_template) = maketexstring("endtemplate");
+ cs_text(frozen_endv) = maketexstring("endtemplate");
+ set_eq_type(frozen_endv, endv_cmd);
+ set_equiv(frozen_endv, null_list);
+ set_eq_level(frozen_endv, level_one);
+ eqtb[frozen_end_template] = eqtb[frozen_endv];
+ set_eq_type(frozen_end_template, end_template_cmd);
+
+ primitive_tex("pagegoal", set_page_dimen_cmd, 0, 0);
+ primitive_tex("pagetotal", set_page_dimen_cmd, 1, 0);
+ primitive_tex("pagestretch", set_page_dimen_cmd, 2, 0);
+ primitive_tex("pagefilstretch", set_page_dimen_cmd, 3, 0);
+ primitive_tex("pagefillstretch", set_page_dimen_cmd, 4, 0);
+ primitive_tex("pagefilllstretch", set_page_dimen_cmd, 5, 0);
+ primitive_tex("pageshrink", set_page_dimen_cmd, 6, 0);
+ primitive_tex("pagedepth", set_page_dimen_cmd, 7, 0);
+
+ /*
+ Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the
+ endgame, since both of them have `|stop|' as their command code.
+ */
+
+ primitive_tex("end", stop_cmd, 0, 0);
+ primitive_tex("dump", stop_cmd, 1, 0);
+
+ primitive_tex("hskip", hskip_cmd, skip_code, 0);
+ primitive_tex("hfil", hskip_cmd, fil_code, 0);
+ primitive_tex("hfill", hskip_cmd, fill_code, 0);
+ primitive_tex("hss", hskip_cmd, ss_code, 0);
+ primitive_tex("hfilneg", hskip_cmd, fil_neg_code, 0);
+ primitive_tex("vskip", vskip_cmd, skip_code, 0);
+ primitive_tex("vfil", vskip_cmd, fil_code, 0);
+ primitive_tex("vfill", vskip_cmd, fill_code, 0);
+ primitive_tex("vss", vskip_cmd, ss_code, 0);
+ primitive_tex("vfilneg", vskip_cmd, fil_neg_code, 0);
+ primitive_tex("mskip", mskip_cmd, mskip_code, 0);
+ primitive_tex("kern", kern_cmd, explicit_kern, 0);
+ primitive_tex("mkern", mkern_cmd, mu_glue, 0);
+ primitive_tex("moveleft", hmove_cmd, 1, 0);
+ primitive_tex("moveright", hmove_cmd, 0, 0);
+ primitive_tex("raise", vmove_cmd, 1, 0);
+ primitive_tex("lower", vmove_cmd, 0, 0);
+ primitive_tex("box", make_box_cmd, box_code, 0);
+ primitive_tex("copy", make_box_cmd, copy_code, 0);
+ primitive_tex("lastbox", make_box_cmd, last_box_code, 0);
+ primitive_tex("vsplit", make_box_cmd, vsplit_code, 0);
+ primitive_tex("tpack", make_box_cmd, tpack_code, 0);
+ primitive_tex("vpack", make_box_cmd, vpack_code, 0);
+ primitive_tex("hpack", make_box_cmd, hpack_code, 0);
+ primitive_tex("vtop", make_box_cmd, vtop_code, 0);
+ primitive_tex("vbox", make_box_cmd, vtop_code + vmode, 0);
+ primitive_tex("hbox", make_box_cmd, vtop_code + hmode, 0);
+ primitive_tex("shipout", leader_ship_cmd, a_leaders - 2, 0); /* |ship_out_flag=leader_flag-2| */
+ primitive_tex("leaders", leader_ship_cmd, a_leaders, 0);
+ primitive_tex("cleaders", leader_ship_cmd, c_leaders, 0);
+ primitive_tex("xleaders", leader_ship_cmd, x_leaders, 0);
+ primitive_luatex("gleaders", leader_ship_cmd, g_leaders, 0);
+ primitive_luatex("boxdir", assign_box_dir_cmd, 0, 0);
+ primitive_luatex("boxdirection", assign_box_direction_cmd, 0, 0);
+ primitive_tex("indent", start_par_cmd, 1, 0);
+ primitive_tex("noindent", start_par_cmd, 0, 0);
+ primitive_luatex("quitvmode", start_par_cmd, 2, 0);
+ primitive_tex("unpenalty", remove_item_cmd, penalty_node, 0);
+ primitive_tex("unkern", remove_item_cmd, kern_node, 0);
+ primitive_tex("unskip", remove_item_cmd, glue_node, 0);
+ primitive_tex("unhbox", un_hbox_cmd, box_code, 0);
+ primitive_tex("unhcopy", un_hbox_cmd, copy_code, 0);
+ primitive_tex("unvbox", un_vbox_cmd, box_code, 0);
+ primitive_tex("unvcopy", un_vbox_cmd, copy_code, 0);
+ primitive_tex("-", discretionary_cmd, explicit_disc, 0); /* good old tex */
+ primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0);
+ primitive_luatex("explicitdiscretionary", discretionary_cmd, explicit_disc, 0);
+ primitive_luatex("automaticdiscretionary", discretionary_cmd, automatic_disc, 0);
+ primitive_luatex("localleftbox", assign_local_box_cmd, 0, 0);
+ primitive_luatex("localrightbox", assign_local_box_cmd, 1, 0);
+
+ primitive_luatex("Ustartmath", math_shift_cs_cmd, text_style, 0);
+ primitive_luatex("Ustopmath", math_shift_cs_cmd, cramped_text_style, 0);
+ primitive_luatex("Ustartdisplaymath", math_shift_cs_cmd, display_style, 0);
+ primitive_luatex("Ustopdisplaymath", math_shift_cs_cmd, cramped_display_style, 0);
+ primitive_tex("eqno", eq_no_cmd, 0, 0);
+ primitive_tex("leqno", eq_no_cmd, 1, 0);
+ primitive_tex("mathord", math_comp_cmd, ord_noad_type, 0);
+ primitive_tex("mathop", math_comp_cmd, op_noad_type_normal, 0);
+ primitive_tex("mathbin", math_comp_cmd, bin_noad_type, 0);
+ primitive_tex("mathrel", math_comp_cmd, rel_noad_type, 0);
+ primitive_tex("mathopen", math_comp_cmd, open_noad_type, 0);
+ primitive_tex("mathclose", math_comp_cmd, close_noad_type, 0);
+ primitive_tex("mathpunct", math_comp_cmd, punct_noad_type, 0);
+ primitive_tex("mathinner", math_comp_cmd, inner_noad_type, 0);
+ primitive_tex("underline", math_comp_cmd, under_noad_type, 0);
+ primitive_tex("overline", math_comp_cmd, over_noad_type, 0);
+ primitive_tex("displaylimits", limit_switch_cmd, op_noad_type_normal, 0);
+ primitive_tex("limits", limit_switch_cmd, op_noad_type_limits, 0);
+ primitive_tex("nolimits", limit_switch_cmd, op_noad_type_no_limits, 0);
+ primitive_tex("displaystyle", math_style_cmd, display_style, 0);
+ primitive_tex("textstyle", math_style_cmd, text_style, 0);
+ primitive_tex("scriptstyle", math_style_cmd, script_style, 0);
+ primitive_tex("scriptscriptstyle", math_style_cmd, script_script_style, 0);
+ primitive_luatex("crampeddisplaystyle", math_style_cmd, cramped_display_style, 0);
+ primitive_luatex("crampedtextstyle", math_style_cmd, cramped_text_style, 0);
+ primitive_luatex("crampedscriptstyle", math_style_cmd, cramped_script_style, 0);
+ primitive_luatex("crampedscriptscriptstyle", math_style_cmd, cramped_script_script_style, 0);
+ primitive_luatex("Usuperscript", super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd);
+ primitive_luatex("Usubscript", super_sub_script_cmd, sub_mark_cmd, sup_mark_cmd);
+ primitive_luatex("Unosuperscript", no_super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd);
+ primitive_luatex("Unosubscript", no_super_sub_script_cmd, sub_mark_cmd, sup_mark_cmd);
+ primitive_tex("above", above_cmd, above_code, 0);
+ primitive_tex("over", above_cmd, over_code, 0);
+ primitive_tex("atop", above_cmd, atop_code, 0);
+ primitive_luatex("Uskewed", above_cmd, skewed_code, 0);
+ primitive_tex("abovewithdelims", above_cmd, delimited_code + above_code, 0);
+ primitive_tex("overwithdelims", above_cmd, delimited_code + over_code, 0);
+ primitive_tex("atopwithdelims", above_cmd, delimited_code + atop_code, 0);
+ primitive_luatex("Uskewedwithdelims", above_cmd, delimited_code + skewed_code, 0);
+ primitive_tex("left", left_right_cmd, left_noad_side, 0);
+ primitive_tex("right", left_right_cmd, right_noad_side, 0);
+ primitive_tex("middle", left_right_cmd, middle_noad_side, 0);
+ primitive_tex("Uleft", left_right_cmd, 10+left_noad_side, 0);
+ primitive_tex("Uright", left_right_cmd, 10+right_noad_side, 0);
+ primitive_tex("Umiddle", left_right_cmd, 10+middle_noad_side, 0);
+ primitive_luatex("Uvextensible", left_right_cmd, 10+no_noad_side, 0);
+ cs_text(frozen_right) = maketexstring("right");
+ eqtb[frozen_right] = eqtb[cur_val];
+
+ primitive_tex("long", prefix_cmd, 1, 0);
+ primitive_tex("outer", prefix_cmd, 2, 0);
+ primitive_tex("global", prefix_cmd, 4, 0);
+ primitive_tex("def", def_cmd, 0, 0);
+ primitive_tex("gdef", def_cmd, 1, 0);
+ primitive_tex("edef", def_cmd, 2, 0);
+ primitive_tex("xdef", def_cmd, 3, 0);
+ primitive_tex("glet", let_cmd, 0, 0);
+ primitive_tex("let", let_cmd, 1, 0);
+ primitive_tex("futurelet", let_cmd, 2, 0);
+ primitive_luatex("letcharcode", let_cmd, 3, 0);
+ primitive_tex("chardef", shorthand_def_cmd, char_def_code, 0);
+ primitive_tex("mathchardef", shorthand_def_cmd, math_char_def_code, 0);
+ primitive_luatex("Umathchardef", shorthand_def_cmd, xmath_char_def_code, 0);
+ primitive_luatex("Umathcharnumdef", shorthand_def_cmd, umath_char_def_code, 0);
+ primitive_tex("countdef", shorthand_def_cmd, count_def_code, 0);
+ primitive_luatex("attributedef", shorthand_def_cmd, attribute_def_code, 0);
+ primitive_tex("dimendef", shorthand_def_cmd, dimen_def_code, 0);
+ primitive_tex("skipdef", shorthand_def_cmd, skip_def_code, 0);
+ primitive_tex("muskipdef", shorthand_def_cmd, mu_skip_def_code, 0);
+ primitive_tex("toksdef", shorthand_def_cmd, toks_def_code, 0);
+ primitive_tex("catcode", def_char_code_cmd, cat_code_base, cat_code_base);
+ primitive_tex("mathcode", def_char_code_cmd, math_code_base, cat_code_base);
+ primitive_tex("lccode", def_char_code_cmd, lc_code_base, cat_code_base);
+ primitive_tex("uccode", def_char_code_cmd, uc_code_base, cat_code_base);
+ primitive_tex("sfcode", def_char_code_cmd, sf_code_base, cat_code_base);
+ primitive_tex("delcode", def_del_code_cmd, del_code_base, del_code_base);
+ primitive_tex("textfont", def_family_cmd, text_size, 0);
+ primitive_tex("scriptfont", def_family_cmd, script_size, 0);
+ primitive_tex("scriptscriptfont", def_family_cmd, script_script_size, 0);
+ primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0);
+ primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0);
+
+ primitive_luatex("Umathoperatorsize", set_math_param_cmd, math_param_operator_size, 0);
+ primitive_luatex("Umathoverbarkern", set_math_param_cmd, math_param_overbar_kern, 0);
+ primitive_luatex("Umathoverbarrule", set_math_param_cmd, math_param_overbar_rule, 0);
+ primitive_luatex("Umathoverbarvgap", set_math_param_cmd, math_param_overbar_vgap, 0);
+ primitive_luatex("Umathunderbarkern", set_math_param_cmd, math_param_underbar_kern, 0);
+ primitive_luatex("Umathunderbarrule", set_math_param_cmd, math_param_underbar_rule, 0);
+ primitive_luatex("Umathunderbarvgap", set_math_param_cmd, math_param_underbar_vgap, 0);
+ primitive_luatex("Umathradicalkern", set_math_param_cmd, math_param_radical_kern, 0);
+ primitive_luatex("Umathradicalrule", set_math_param_cmd, math_param_radical_rule, 0);
+ primitive_luatex("Umathradicalvgap", set_math_param_cmd, math_param_radical_vgap, 0);
+ primitive_luatex("Umathradicaldegreebefore", set_math_param_cmd, math_param_radical_degree_before, 0);
+ primitive_luatex("Umathradicaldegreeafter", set_math_param_cmd, math_param_radical_degree_after, 0);
+ primitive_luatex("Umathradicaldegreeraise", set_math_param_cmd, math_param_radical_degree_raise, 0);
+ primitive_luatex("Umathstackvgap", set_math_param_cmd, math_param_stack_vgap, 0);
+ primitive_luatex("Umathstacknumup", set_math_param_cmd, math_param_stack_num_up, 0);
+ primitive_luatex("Umathstackdenomdown", set_math_param_cmd, math_param_stack_denom_down, 0);
+ primitive_luatex("Umathfractionrule", set_math_param_cmd, math_param_fraction_rule, 0);
+ primitive_luatex("Umathfractionnumvgap", set_math_param_cmd, math_param_fraction_num_vgap, 0);
+ primitive_luatex("Umathfractionnumup", set_math_param_cmd, math_param_fraction_num_up, 0);
+ primitive_luatex("Umathfractiondenomvgap", set_math_param_cmd, math_param_fraction_denom_vgap, 0);
+ primitive_luatex("Umathfractiondenomdown", set_math_param_cmd, math_param_fraction_denom_down, 0);
+ primitive_luatex("Umathfractiondelsize", set_math_param_cmd, math_param_fraction_del_size, 0);
+ primitive_luatex("Umathskewedfractionvgap", set_math_param_cmd, math_param_skewed_fraction_vgap, 0);
+ primitive_luatex("Umathskewedfractionhgap", set_math_param_cmd, math_param_skewed_fraction_hgap, 0);
+ primitive_luatex("Umathlimitabovevgap", set_math_param_cmd, math_param_limit_above_vgap, 0);
+ primitive_luatex("Umathlimitabovebgap", set_math_param_cmd, math_param_limit_above_bgap, 0);
+ primitive_luatex("Umathlimitabovekern", set_math_param_cmd, math_param_limit_above_kern, 0);
+ primitive_luatex("Umathlimitbelowvgap", set_math_param_cmd, math_param_limit_below_vgap, 0);
+ primitive_luatex("Umathlimitbelowbgap", set_math_param_cmd, math_param_limit_below_bgap, 0);
+ primitive_luatex("Umathlimitbelowkern", set_math_param_cmd, math_param_limit_below_kern, 0);
+ primitive_luatex("Umathnolimitsubfactor", set_math_param_cmd, math_param_nolimit_sub_factor, 0); /* bonus */
+ primitive_luatex("Umathnolimitsupfactor", set_math_param_cmd, math_param_nolimit_sup_factor, 0); /* bonus */
+ primitive_luatex("Umathunderdelimitervgap", set_math_param_cmd, math_param_under_delimiter_vgap, 0);
+ primitive_luatex("Umathunderdelimiterbgap", set_math_param_cmd, math_param_under_delimiter_bgap, 0);
+ primitive_luatex("Umathoverdelimitervgap", set_math_param_cmd, math_param_over_delimiter_vgap, 0);
+ primitive_luatex("Umathoverdelimiterbgap", set_math_param_cmd, math_param_over_delimiter_bgap, 0);
+ primitive_luatex("Umathsubshiftdrop", set_math_param_cmd, math_param_sub_shift_drop, 0);
+ primitive_luatex("Umathsupshiftdrop", set_math_param_cmd, math_param_sup_shift_drop, 0);
+ primitive_luatex("Umathsubshiftdown", set_math_param_cmd, math_param_sub_shift_down, 0);
+ primitive_luatex("Umathsubsupshiftdown", set_math_param_cmd, math_param_sub_sup_shift_down, 0);
+ primitive_luatex("Umathsubtopmax", set_math_param_cmd, math_param_sub_top_max, 0);
+ primitive_luatex("Umathsupshiftup", set_math_param_cmd, math_param_sup_shift_up, 0);
+ primitive_luatex("Umathsupbottommin", set_math_param_cmd, math_param_sup_bottom_min, 0);
+ primitive_luatex("Umathsupsubbottommax", set_math_param_cmd, math_param_sup_sub_bottom_max, 0);
+ primitive_luatex("Umathsubsupvgap", set_math_param_cmd, math_param_subsup_vgap, 0);
+ primitive_luatex("Umathspaceafterscript", set_math_param_cmd, math_param_space_after_script, 0);
+ primitive_luatex("Umathconnectoroverlapmin", set_math_param_cmd, math_param_connector_overlap_min, 0);
+ primitive_luatex("Umathordordspacing", set_math_param_cmd, math_param_ord_ord_spacing, 0);
+ primitive_luatex("Umathordopspacing", set_math_param_cmd, math_param_ord_op_spacing, 0);
+ primitive_luatex("Umathordbinspacing", set_math_param_cmd, math_param_ord_bin_spacing, 0);
+ primitive_luatex("Umathordrelspacing", set_math_param_cmd, math_param_ord_rel_spacing, 0);
+ primitive_luatex("Umathordopenspacing", set_math_param_cmd, math_param_ord_open_spacing, 0);
+ primitive_luatex("Umathordclosespacing", set_math_param_cmd, math_param_ord_close_spacing, 0);
+ primitive_luatex("Umathordpunctspacing", set_math_param_cmd, math_param_ord_punct_spacing, 0);
+ primitive_luatex("Umathordinnerspacing", set_math_param_cmd, math_param_ord_inner_spacing, 0);
+ primitive_luatex("Umathopordspacing", set_math_param_cmd, math_param_op_ord_spacing, 0);
+ primitive_luatex("Umathopopspacing", set_math_param_cmd, math_param_op_op_spacing, 0);
+ primitive_luatex("Umathopbinspacing", set_math_param_cmd, math_param_op_bin_spacing, 0);
+ primitive_luatex("Umathoprelspacing", set_math_param_cmd, math_param_op_rel_spacing, 0);
+ primitive_luatex("Umathopopenspacing", set_math_param_cmd, math_param_op_open_spacing, 0);
+ primitive_luatex("Umathopclosespacing", set_math_param_cmd, math_param_op_close_spacing, 0);
+ primitive_luatex("Umathoppunctspacing", set_math_param_cmd, math_param_op_punct_spacing, 0);
+ primitive_luatex("Umathopinnerspacing", set_math_param_cmd, math_param_op_inner_spacing, 0);
+ primitive_luatex("Umathbinordspacing", set_math_param_cmd, math_param_bin_ord_spacing, 0);
+ primitive_luatex("Umathbinopspacing", set_math_param_cmd, math_param_bin_op_spacing, 0);
+ primitive_luatex("Umathbinbinspacing", set_math_param_cmd, math_param_bin_bin_spacing, 0);
+ primitive_luatex("Umathbinrelspacing", set_math_param_cmd, math_param_bin_rel_spacing, 0);
+ primitive_luatex("Umathbinopenspacing", set_math_param_cmd, math_param_bin_open_spacing, 0);
+ primitive_luatex("Umathbinclosespacing", set_math_param_cmd, math_param_bin_close_spacing, 0);
+ primitive_luatex("Umathbinpunctspacing", set_math_param_cmd, math_param_bin_punct_spacing, 0);
+ primitive_luatex("Umathbininnerspacing", set_math_param_cmd, math_param_bin_inner_spacing, 0);
+ primitive_luatex("Umathrelordspacing", set_math_param_cmd, math_param_rel_ord_spacing, 0);
+ primitive_luatex("Umathrelopspacing", set_math_param_cmd, math_param_rel_op_spacing, 0);
+ primitive_luatex("Umathrelbinspacing", set_math_param_cmd, math_param_rel_bin_spacing, 0);
+ primitive_luatex("Umathrelrelspacing", set_math_param_cmd, math_param_rel_rel_spacing, 0);
+ primitive_luatex("Umathrelopenspacing", set_math_param_cmd, math_param_rel_open_spacing, 0);
+ primitive_luatex("Umathrelclosespacing", set_math_param_cmd, math_param_rel_close_spacing, 0);
+ primitive_luatex("Umathrelpunctspacing", set_math_param_cmd, math_param_rel_punct_spacing, 0);
+ primitive_luatex("Umathrelinnerspacing", set_math_param_cmd, math_param_rel_inner_spacing, 0);
+ primitive_luatex("Umathopenordspacing", set_math_param_cmd, math_param_open_ord_spacing, 0);
+ primitive_luatex("Umathopenopspacing", set_math_param_cmd, math_param_open_op_spacing, 0);
+ primitive_luatex("Umathopenbinspacing", set_math_param_cmd, math_param_open_bin_spacing, 0);
+ primitive_luatex("Umathopenrelspacing", set_math_param_cmd, math_param_open_rel_spacing, 0);
+ primitive_luatex("Umathopenopenspacing", set_math_param_cmd, math_param_open_open_spacing, 0);
+ primitive_luatex("Umathopenclosespacing", set_math_param_cmd, math_param_open_close_spacing, 0);
+ primitive_luatex("Umathopenpunctspacing", set_math_param_cmd, math_param_open_punct_spacing, 0);
+ primitive_luatex("Umathopeninnerspacing", set_math_param_cmd, math_param_open_inner_spacing, 0);
+ primitive_luatex("Umathcloseordspacing", set_math_param_cmd, math_param_close_ord_spacing, 0);
+ primitive_luatex("Umathcloseopspacing", set_math_param_cmd, math_param_close_op_spacing, 0);
+ primitive_luatex("Umathclosebinspacing", set_math_param_cmd, math_param_close_bin_spacing, 0);
+ primitive_luatex("Umathcloserelspacing", set_math_param_cmd, math_param_close_rel_spacing, 0);
+ primitive_luatex("Umathcloseopenspacing", set_math_param_cmd, math_param_close_open_spacing, 0);
+ primitive_luatex("Umathcloseclosespacing", set_math_param_cmd, math_param_close_close_spacing, 0);
+ primitive_luatex("Umathclosepunctspacing", set_math_param_cmd, math_param_close_punct_spacing, 0);
+ primitive_luatex("Umathcloseinnerspacing", set_math_param_cmd, math_param_close_inner_spacing, 0);
+ primitive_luatex("Umathpunctordspacing", set_math_param_cmd, math_param_punct_ord_spacing, 0);
+ primitive_luatex("Umathpunctopspacing", set_math_param_cmd, math_param_punct_op_spacing, 0);
+ primitive_luatex("Umathpunctbinspacing", set_math_param_cmd, math_param_punct_bin_spacing, 0);
+ primitive_luatex("Umathpunctrelspacing", set_math_param_cmd, math_param_punct_rel_spacing, 0);
+ primitive_luatex("Umathpunctopenspacing", set_math_param_cmd, math_param_punct_open_spacing, 0);
+ primitive_luatex("Umathpunctclosespacing", set_math_param_cmd, math_param_punct_close_spacing, 0);
+ primitive_luatex("Umathpunctpunctspacing", set_math_param_cmd, math_param_punct_punct_spacing, 0);
+ primitive_luatex("Umathpunctinnerspacing", set_math_param_cmd, math_param_punct_inner_spacing, 0);
+ primitive_luatex("Umathinnerordspacing", set_math_param_cmd, math_param_inner_ord_spacing, 0);
+ primitive_luatex("Umathinneropspacing", set_math_param_cmd, math_param_inner_op_spacing, 0);
+ primitive_luatex("Umathinnerbinspacing", set_math_param_cmd, math_param_inner_bin_spacing, 0);
+ primitive_luatex("Umathinnerrelspacing", set_math_param_cmd, math_param_inner_rel_spacing, 0);
+ primitive_luatex("Umathinneropenspacing", set_math_param_cmd, math_param_inner_open_spacing, 0);
+ primitive_luatex("Umathinnerclosespacing", set_math_param_cmd, math_param_inner_close_spacing, 0);
+ primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, math_param_inner_punct_spacing, 0);
+ primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, math_param_inner_inner_spacing, 0);
+
+ primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base, math_code_base);
+ primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base, del_code_base);
+ primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 1, math_code_base);
+ primitive_luatex("Udelcodenum", extdef_del_code_cmd, del_code_base + 1, del_code_base);
+ primitive_tex("hyphenation", hyph_data_cmd, 0, 0);
+ primitive_tex("patterns", hyph_data_cmd, 1, 0);
+ primitive_luatex("prehyphenchar", hyph_data_cmd, 2, 0);
+ primitive_luatex("posthyphenchar", hyph_data_cmd, 3, 0);
+ primitive_luatex("preexhyphenchar", hyph_data_cmd, 4, 0);
+ primitive_luatex("postexhyphenchar", hyph_data_cmd, 5, 0);
+ primitive_luatex("hyphenationmin", hyph_data_cmd, 6, 0);
+ primitive_luatex("hjcode", hyph_data_cmd, 7, 0);
+ primitive_tex("hyphenchar", assign_font_int_cmd, 0, 0);
+ primitive_tex("skewchar", assign_font_int_cmd, 1, 0);
+ primitive_luatex("lpcode", assign_font_int_cmd, lp_code_base, 0);
+ primitive_luatex("rpcode", assign_font_int_cmd, rp_code_base, 0);
+ primitive_luatex("efcode", assign_font_int_cmd, ef_code_base, 0);
+ primitive_luatex("tagcode", assign_font_int_cmd, tag_code, 0);
+ primitive_luatex("ignoreligaturesinfont", assign_font_int_cmd, no_lig_code, 0);
+ primitive_tex("batchmode", set_interaction_cmd, batch_mode, 0);
+ primitive_tex("nonstopmode", set_interaction_cmd, nonstop_mode, 0);
+ primitive_tex("scrollmode", set_interaction_cmd, scroll_mode, 0);
+ primitive_tex("errorstopmode", set_interaction_cmd, error_stop_mode, 0);
+ primitive_tex("openin", in_stream_cmd, 1, 0);
+ primitive_tex("closein", in_stream_cmd, 0, 0);
+ primitive_tex("message", message_cmd, 0, 0);
+ primitive_tex("errmessage", message_cmd, 1, 0);
+ primitive_tex("lowercase", case_shift_cmd, lc_code_base, lc_code_base);
+ primitive_tex("uppercase", case_shift_cmd, uc_code_base, lc_code_base);
+ primitive_tex("show", xray_cmd, show_code, 0);
+ primitive_tex("showbox", xray_cmd, show_box_code, 0);
+ primitive_tex("showthe", xray_cmd, show_the_code, 0);
+ primitive_tex("showlists", xray_cmd, show_lists, 0);
+
+ primitive_tex("openout", extension_cmd, open_code, 0);
+ primitive_tex("write", extension_cmd, write_code, 0);
+ write_loc = cur_val;
+ primitive_tex("closeout", extension_cmd, close_code, 0);
+ primitive_luatex("endlocalcontrol", extension_cmd, end_local_code, 0);
+ primitive_tex("special", extension_cmd, special_code, 0);
+ cs_text(frozen_special) = maketexstring("special");
+ eqtb[frozen_special] = eqtb[cur_val];
+ primitive_tex("immediate", extension_cmd, immediate_code, 0);
+ primitive_luatex("localinterlinepenalty", assign_int_cmd, int_base + local_inter_line_penalty_code, int_base);
+ primitive_luatex("localbrokenpenalty", assign_int_cmd, int_base + local_broken_penalty_code, int_base);
+ primitive_luatex("pagedir", assign_dir_cmd, int_base + page_direction_code, dir_base);
+ primitive_luatex("bodydir", assign_dir_cmd, int_base + body_direction_code, dir_base);
+ primitive_luatex("pardir", assign_dir_cmd, int_base + par_direction_code, dir_base);
+ primitive_luatex("textdir", assign_dir_cmd, int_base + text_direction_code, dir_base);
+ primitive_luatex("mathdir", assign_dir_cmd, int_base + math_direction_code, dir_base);
+ primitive_luatex("linedir", assign_dir_cmd, int_base + line_direction_code, dir_base);
+ primitive_luatex("pageleftoffset", assign_dimen_cmd, dimen_base + page_left_offset_code, dimen_base);
+ primitive_luatex("pagetopoffset", assign_dimen_cmd, dimen_base + page_top_offset_code, dimen_base);
+ primitive_luatex("pagerightoffset", assign_dimen_cmd, dimen_base + page_right_offset_code, dimen_base);
+ primitive_luatex("pagebottomoffset", assign_dimen_cmd, dimen_base + page_bottom_offset_code, dimen_base);
+ primitive_luatex("saveboxresource", extension_cmd, save_box_resource_code, 0);
+ primitive_luatex("useboxresource", extension_cmd, use_box_resource_code, 0);
+ primitive_luatex("saveimageresource", extension_cmd, save_image_resource_code, 0);
+ primitive_luatex("useimageresource", extension_cmd, use_image_resource_code, 0);
+ primitive_luatex("savepos", normal_cmd, save_pos_code, 0);
+ primitive_luatex("savecatcodetable", normal_cmd, save_cat_code_table_code, 0);
+ primitive_luatex("initcatcodetable", normal_cmd, init_cat_code_table_code, 0);
+ primitive_luatex("setrandomseed", normal_cmd, set_random_seed_code, 0);
+ primitive_luatex("latelua", normal_cmd, late_lua_code, 0);
+ primitive_luatex("lateluafunction", normal_cmd, late_lua_call_code, 0);
+ primitive_luatex("insertht", convert_cmd, insert_ht_code, 0);
+ primitive_luatex("dviextension", extension_cmd, dvi_extension_code, 0);
+ primitive_luatex("dvifeedback", feedback_cmd, dvi_feedback_code, 0);
+ primitive_luatex("dvivariable", variable_cmd, dvi_variable_code, 0);
+ primitive_luatex("pdfextension", extension_cmd, pdf_extension_code, 0);
+ primitive_luatex("pdffeedback", feedback_cmd, pdf_feedback_code, 0);
+ primitive_luatex("pdfvariable", variable_cmd, pdf_variable_code, 0);
+ primitive_luatex("mathoption", option_cmd, math_option_code, 0);
+
+ primitive_luatex("luacopyinputnodes", assign_int_cmd, int_base + copy_lua_input_nodes_code, int_base);
+
+ primitive_luatex("pagedirection", assign_direction_cmd, int_base + page_direction_code, dir_base);
+ primitive_luatex("bodydirection", assign_direction_cmd, int_base + body_direction_code, dir_base);
+ primitive_luatex("pardirection", assign_direction_cmd, int_base + par_direction_code, dir_base);
+ primitive_luatex("textdirection", assign_direction_cmd, int_base + text_direction_code, dir_base);
+ primitive_luatex("mathdirection", assign_direction_cmd, int_base + math_direction_code, dir_base);
+ primitive_luatex("linedirection", assign_direction_cmd, int_base + line_direction_code, dir_base);
+
+ /*
+ some of the internal integer parameters are not associated with actual
+ primitives at all.
+ */
+
+ primitive_no("nolocalwhatsits", assign_int_cmd, int_base + no_local_whatsits_code, int_base);
+ primitive_no("nolocaldirs", assign_int_cmd, int_base + no_local_dirs_code, int_base);
+
+}
+
+void initialize_etex_commands(void)
+{
+ primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0);
+ primitive_etex("eTeXversion", last_item_cmd, eTeX_version_code, 0);
+ primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, 0);
+ primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0);
+
+ /*tex
+
+ First we implement the additional \eTeX\ parameters in the table of
+ equivalents.
+ */
+
+ primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base);
+ primitive_etex("tracingassigns", assign_int_cmd, int_base + tracing_assigns_code, int_base);
+ primitive_etex("tracinggroups", assign_int_cmd, int_base + tracing_groups_code, int_base);
+ primitive_etex("tracingifs", assign_int_cmd, int_base + tracing_ifs_code, int_base);
+ primitive_etex("tracingscantokens", assign_int_cmd, int_base + tracing_scan_tokens_code, int_base);
+ primitive_etex("tracingnesting", assign_int_cmd, int_base + tracing_nesting_code, int_base);
+ primitive_etex("predisplaydirection", assign_int_cmd, int_base + pre_display_direction_code, int_base);
+ primitive_etex("lastlinefit", assign_int_cmd, int_base + last_line_fit_code, int_base);
+ primitive_etex("savingvdiscards", assign_int_cmd, int_base + saving_vdiscards_code, int_base);
+ primitive_etex("savinghyphcodes", assign_int_cmd, int_base + saving_hyph_codes_code, int_base);
+ primitive_luatex("suppressfontnotfounderror", assign_int_cmd, int_base + suppress_fontnotfound_error_code, int_base);
+ primitive_luatex("suppresslongerror", assign_int_cmd, int_base + suppress_long_error_code, int_base);
+ primitive_luatex("suppressprimitiveerror", assign_int_cmd, int_base + suppress_primitive_error_code, int_base);
+ primitive_luatex("suppressmathparerror", assign_int_cmd, int_base + suppress_mathpar_error_code, int_base);
+ primitive_luatex("suppressifcsnameerror", assign_int_cmd, int_base + suppress_ifcsname_error_code, int_base);
+ primitive_luatex("suppressoutererror", assign_int_cmd, int_base + suppress_outer_error_code, int_base);
+ primitive_luatex("matheqnogapstep", assign_int_cmd, int_base + math_eqno_gap_step_code, int_base);
+ primitive_luatex("mathdisplayskipmode", assign_int_cmd, int_base + math_display_skip_mode_code, int_base);
+ primitive_luatex("mathscriptsmode", assign_int_cmd, int_base + math_scripts_mode_code, int_base);
+ primitive_luatex("mathnolimitsmode", assign_int_cmd, int_base + math_nolimits_mode_code, int_base);
+ primitive_luatex("mathitalicsmode", assign_int_cmd, int_base + math_italics_mode_code, int_base);
+ primitive_luatex("mathrulesmode", assign_int_cmd, int_base + math_rules_mode_code, int_base);
+ primitive_luatex("mathrulesfam", assign_int_cmd, int_base + math_rules_fam_code, int_base);
+ primitive_luatex("synctex", assign_int_cmd, int_base + synctex_code, int_base);
+
+ primitive_etex("currentgrouplevel", last_item_cmd, current_group_level_code, 0);
+ primitive_etex("currentgrouptype", last_item_cmd, current_group_type_code, 0);
+
+ primitive_etex("currentiflevel", last_item_cmd, current_if_level_code, 0);
+ primitive_etex("currentiftype", last_item_cmd, current_if_type_code, 0);
+ primitive_etex("currentifbranch", last_item_cmd, current_if_branch_code, 0);
+ primitive_etex("fontcharwd", last_item_cmd, font_char_wd_code, 0);
+ primitive_etex("fontcharht", last_item_cmd, font_char_ht_code, 0);
+ primitive_etex("fontchardp", last_item_cmd, font_char_dp_code, 0);
+ primitive_etex("fontcharic", last_item_cmd, font_char_ic_code, 0);
+
+ primitive_etex("parshapelength", last_item_cmd, par_shape_length_code, 0);
+ primitive_etex("parshapeindent", last_item_cmd, par_shape_indent_code, 0);
+ primitive_etex("parshapedimen", last_item_cmd, par_shape_dimen_code, 0);
+
+ primitive_luatex("shapemode", assign_int_cmd, int_base + shape_mode_code, int_base);
+ primitive_luatex("hyphenationbounds", assign_int_cmd, int_base + hyphenation_bounds_code, int_base);
+
+ primitive_etex("showgroups", xray_cmd, show_groups, 0);
+
+ /*tex
+
+ The \.{\\showtokens} command displays a token list.
+
+ */
+
+ primitive_etex("showtokens", xray_cmd, show_tokens, 0);
+
+ /*tex
+
+ 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.
+
+ */
+
+ primitive_etex("unexpanded", the_cmd, 1, 0);
+ primitive_etex("detokenize", the_cmd, show_tokens, 0);
+
+ /*tex
+
+ The \.{\\showifs} command displays all currently active conditionals.
+
+ */
+
+ primitive_etex("showifs", xray_cmd, show_ifs, 0);
+
+ /*tex
+
+ The \.{\\interactionmode} primitive allows to query and set the interaction mode.
+
+ */
+
+ primitive_etex("interactionmode", set_page_int_cmd, 2, 0);
+
+ /*tex
+
+ The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive.
+
+ */
+
+ primitive_etex("scantokens", input_cmd, 2, 0);
+ primitive_luatex("scantextokens", input_cmd, 3, 0);
+
+ primitive_etex("readline", read_to_cs_cmd, 1, 0);
+
+ primitive_etex("unless", expand_after_cmd, 1, 0);
+ primitive_etex("ifdefined", if_test_cmd, if_def_code, 0);
+ primitive_etex("ifcsname", if_test_cmd, if_cs_code, 0);
+ primitive_etex("iffontchar", if_test_cmd, if_font_char_code, 0);
+ primitive_luatex("ifincsname", if_test_cmd, if_in_csname_code, 0);
+ primitive_luatex("ifabsnum", if_test_cmd, if_abs_num_code, 0);
+ primitive_luatex("ifabsdim", if_test_cmd, if_abs_dim_code, 0);
+
+ /*tex
+
+ 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}.
+ */
+
+ primitive_etex("protected", prefix_cmd, 8, 0);
+
+ /*tex
+
+ Here are the additional \eTeX\ primitives for expressions.
+
+ */
+
+ primitive_etex("numexpr", last_item_cmd, eTeX_expr - int_val_level + int_val_level, 0);
+ primitive_etex("dimexpr", last_item_cmd, eTeX_expr - int_val_level + dimen_val_level, 0);
+ primitive_etex("glueexpr", last_item_cmd, eTeX_expr - int_val_level + glue_val_level, 0);
+ primitive_etex("muexpr", last_item_cmd, eTeX_expr - int_val_level + mu_val_level, 0);
+
+ primitive_etex("gluestretchorder", last_item_cmd, glue_stretch_order_code, 0);
+ primitive_etex("glueshrinkorder", last_item_cmd, glue_shrink_order_code, 0);
+ primitive_etex("gluestretch", last_item_cmd, glue_stretch_code, 0);
+ primitive_etex("glueshrink", last_item_cmd, glue_shrink_code, 0);
+
+ primitive_luatex("eTeXgluestretchorder", last_item_cmd, eTeX_glue_stretch_order_code, 0);
+ primitive_luatex("eTeXglueshrinkorder", last_item_cmd, eTeX_glue_shrink_order_code, 0);
+
+ primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0);
+ primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0);
+
+ /*tex
+
+ 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|.
+
+ */
+
+ primitive_etex("pagediscards", un_vbox_cmd, last_box_code, 0);
+ primitive_etex("splitdiscards", un_vbox_cmd, vsplit_code, 0);
+
+ /*tex
+
+ The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties},
+ and \.{\\displaywidowpenalties} commands allow to define arrays of
+ penalty values to be used instead of the corresponding single values.
+
+ */
+
+ primitive_etex("interlinepenalties", set_etex_shape_cmd, inter_line_penalties_loc, etex_pen_base);
+ primitive_etex("clubpenalties", set_etex_shape_cmd, club_penalties_loc, etex_pen_base);
+ primitive_etex("widowpenalties", set_etex_shape_cmd, widow_penalties_loc, etex_pen_base);
+ primitive_etex("displaywidowpenalties", set_etex_shape_cmd, display_widow_penalties_loc, etex_pen_base);
+
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.h b/Build/source/texk/web2c/luatexdir/tex/commands.h
new file mode 100644
index 00000000000..7d7a7056564
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/commands.h
@@ -0,0 +1,359 @@
+/* commands.h
+
+ Copyright 2008 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef COMMANDS_H
+# define COMMANDS_H
+
+/*
+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.
+
+@ 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}'.
+
+
+@ 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}'.
+
+There is no matching primitive to go with |assign_attr|, but even if
+there was no \.{\\attributedef}, a reserved number would still be
+needed because there is an implied correspondence between the
+|assign_xxx| commands and |xxx_val| expression values. That would
+break down otherwise.
+
+@ 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.
+
+
+*/
+
+typedef enum {
+ relax_cmd = 0, /* do nothing ( \.{\\relax} ) */
+# define escape_cmd relax_cmd /* escape delimiter (called \.\\ in {\sl The \TeX book\/}) */
+ left_brace_cmd, /* beginning of a group ( \.\{ ) */
+ right_brace_cmd, /* ending of a group ( \.\} ) */
+ math_shift_cmd, /* mathematics shift character ( \.\$ ) */
+ tab_mark_cmd, /* alignment delimiter ( \.\&, \.{\\span} ) */
+ car_ret_cmd, /* end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} ) */
+# define out_param_cmd car_ret_cmd /* output a macro parameter */
+ mac_param_cmd, /* macro parameter symbol ( \.\# ) */
+ sup_mark_cmd, /* superscript ( \.{\char'136} ) */
+ sub_mark_cmd, /* subscript ( \.{\char'137} ) */
+ endv_cmd, /* end of \<v_j> list in alignment template */
+# define ignore_cmd endv_cmd /* characters to ignore ( \.{\^\^@@} ) */
+ spacer_cmd, /* characters equivalent to blank space ( \.{\ } ) */
+ letter_cmd, /* characters regarded as letters ( \.{A..Z}, \.{a..z} ) */
+ other_char_cmd, /* none of the special character types */
+ par_end_cmd, /* end of paragraph ( \.{\\par} ) */
+# define active_char_cmd par_end_cmd /* characters that invoke macros ( \.{\char`\~} ) */
+# define match_cmd par_end_cmd /* match a macro parameter */
+ stop_cmd, /* end of job ( \.{\\end}, \.{\\dump} ) */
+# define comment_cmd stop_cmd /* characters that introduce comments ( \.\% ) */
+# define end_match_cmd stop_cmd /* end of parameters to macro */
+ delim_num_cmd, /* specify delimiter numerically ( \.{\\delimiter} ) */
+# define invalid_char_cmd delim_num_cmd /* characters that shouldn't appear ( \.{\^\^?} ) */
+# define max_char_code_cmd delim_num_cmd /* largest catcode for individual characters */
+ char_num_cmd, /* character specified numerically ( \.{\\char} ) */
+ math_char_num_cmd, /* explicit math code ( \.{\\mathchar} ) */
+ mark_cmd, /* mark definition ( \.{\\mark} ) */
+ node_cmd,
+ xray_cmd, /* peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~) */
+ make_box_cmd, /* make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~) */
+ hmove_cmd, /* horizontal motion ( \.{\\moveleft}, \.{\\moveright} ) */
+ vmove_cmd, /* vertical motion ( \.{\\raise}, \.{\\lower} ) */
+ un_hbox_cmd, /* unglue a box ( \.{\\unhbox}, \.{\\unhcopy} ) */
+ un_vbox_cmd, /* unglue a box ( \.{\\unvbox}, \.{\\unvcopy} or \.{\\pagediscards}, \.{\\splitdiscards} ) */
+ remove_item_cmd, /* nullify last item ( \.{\\unpenalty}, \.{\\unkern}, \.{\\unskip} ) */
+ hskip_cmd, /* horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~) */
+ vskip_cmd, /* vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~) */
+ mskip_cmd, /* math glue ( \.{\\mskip} ) */
+ kern_cmd, /* fixed space ( \.{\\kern}) */
+ mkern_cmd, /* math kern ( \.{\\mkern} ) */
+ leader_ship_cmd, /* use a box ( \.{\\shipout}, \.{\\leaders}, etc.~) */
+ halign_cmd, /* horizontal table alignment ( \.{\\halign} ) */
+ valign_cmd, /* vertical table alignment ( \.{\\valign} ) */
+ no_align_cmd, /* temporary escape from alignment ( \.{\\noalign} ) */
+ vrule_cmd, /* vertical rule ( \.{\\vrule} ) */
+ hrule_cmd, /* horizontal rule ( \.{\\hrule} ) */
+ no_vrule_cmd, /* no rule, just reserve space ( \.{\\novrule} )*/
+ no_hrule_cmd, /* no rule, just reserve space ( \.{\\nohrule} )*/
+ insert_cmd, /* vlist inserted in box ( \.{\\insert} ) */
+ vadjust_cmd, /* vlist inserted in enclosing paragraph ( \.{\\vadjust} ) */
+ ignore_spaces_cmd, /* gobble |spacer| tokens ( \.{\\ignorespaces} ) */
+ after_assignment_cmd, /* save till assignment is done ( \.{\\afterassignment} ) */
+ after_group_cmd, /* save till group is done ( \.{\\aftergroup} ) */
+ break_penalty_cmd, /* additional badness ( \.{\\penalty} ) */
+ start_par_cmd, /* begin paragraph ( \.{\\indent}, \.{\\noindent} ) */
+ ital_corr_cmd, /* italic correction ( \.{\\/} ) */
+ accent_cmd, /* attach accent in text ( \.{\\accent} ) */
+ math_accent_cmd, /* attach accent in math ( \.{\\mathaccent} ) */
+ discretionary_cmd, /* discretionary texts ( \.{\\-}, \.{\\discretionary} ) */
+ eq_no_cmd, /* equation number ( \.{\\eqno}, \.{\\leqno} ) */
+ left_right_cmd, /* variable delimiter ( \.{\\left}, \.{\\right} or \.{\\middle} ) */
+ math_comp_cmd, /* component of formula ( \.{\\mathbin}, etc.~) */
+ limit_switch_cmd, /* diddle limit conventions ( \.{\\displaylimits}, etc.~) */
+ above_cmd, /* generalized fraction ( \.{\\above}, \.{\\atop}, etc.~) */
+ math_style_cmd, /* style specification ( \.{\\displaystyle}, etc.~) */
+ math_choice_cmd, /* choice specification ( \.{\\mathchoice} ) */
+ non_script_cmd, /* conditional math glue ( \.{\\nonscript} ) */
+ vcenter_cmd, /* vertically center a vbox ( \.{\\vcenter} ) */
+ case_shift_cmd, /* force specific case ( \.{\\lowercase}, \.{\\uppercase}~) */
+ message_cmd, /* send to user ( \.{\\message}, \.{\\errmessage} ) */
+ normal_cmd, /* general extensions to \TeX\ that don't fit into a category */
+ extension_cmd, /* extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~) */
+ option_cmd,
+ lua_function_call_cmd,
+ lua_bytecode_call_cmd,
+ lua_call_cmd,
+ in_stream_cmd, /* files for reading ( \.{\\openin}, \.{\\closein} ) */
+ begin_group_cmd, /* begin local grouping ( \.{\\begingroup} ) */
+ end_group_cmd, /* end local grouping ( \.{\\endgroup} ) */
+ omit_cmd, /* omit alignment template ( \.{\\omit} ) */
+ ex_space_cmd, /* explicit space ( \.{\\\ } ) */
+ boundary_cmd, /* insert boundry node with value ( \.{\\*boundary} ) */
+ radical_cmd, /* square root and similar signs ( \.{\\radical} ) */
+ super_sub_script_cmd, /* explicit super- or subscript */
+ no_super_sub_script_cmd, /* explicit no super- or subscript */
+ math_shift_cs_cmd, /* start- and endmath */
+ end_cs_name_cmd, /* end control sequence ( \.{\\endcsname} ) */
+ char_ghost_cmd, /* \.{\\leftghost}, \.{\\rightghost} character for kerning */
+ assign_local_box_cmd, /* box for guillemets \.{\\localleftbox} or \.{\\localrightbox} */
+ char_given_cmd, /* character code defined by \.{\\chardef} */
+# define min_internal_cmd char_given_cmd /* the smallest code that can follow \.{\\the} */
+ math_given_cmd, /* math code defined by \.{\\mathchardef} */
+ xmath_given_cmd, /* math code defined by \.{\\Umathchardef} or \.{\\Umathcharnumdef} */
+ last_item_cmd, /* most recent item ( \.{\\lastpenalty}, \.{\\lastkern}, \.{\\lastskip} ) */
+# define max_non_prefixed_command_cmd last_item_cmd /* largest command code that can't be \.{\\global} */
+ toks_register_cmd, /* token list register ( \.{\\toks} ) */
+ assign_toks_cmd, /* special token list ( \.{\\output}, \.{\\everypar}, etc.~) */
+ assign_int_cmd, /* user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~) */
+ assign_attr_cmd, /* user-defined attributes */
+ assign_dimen_cmd, /* user-defined length ( \.{\\hsize}, etc.~) */
+ assign_glue_cmd, /* user-defined glue ( \.{\\baselineskip}, etc.~) */
+ assign_mu_glue_cmd, /* user-defined muglue ( \.{\\thinmuskip}, etc.~) */
+ assign_font_dimen_cmd, /* user-defined font dimension ( \.{\\fontdimen} ) */
+ assign_font_int_cmd, /* user-defined font integer ( \.{\\hyphenchar}, \.{\\skewchar} ) */
+ assign_hang_indent_cmd,
+ set_aux_cmd, /* specify state info ( \.{\\spacefactor}, \.{\\prevdepth} ) */
+ set_prev_graf_cmd, /* specify state info ( \.{\\prevgraf} ) */
+ set_page_dimen_cmd, /* specify state info ( \.{\\pagegoal}, etc.~) */
+ set_page_int_cmd, /* specify state info ( \.{\\deadcycles}, \.{\\insertpenalties} ) */
+ set_box_dimen_cmd, /* change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} ) */
+ set_tex_shape_cmd, /* specify fancy paragraph shape ( \.{\\parshape} ) */
+ set_etex_shape_cmd, /* specify etex extended list ( \.{\\interlinepenalties}, etc.~) */
+ def_char_code_cmd, /* define a character code ( \.{\\catcode}, etc.~) */
+ def_del_code_cmd, /* define a delimiter code ( \.{\\delcode}) */
+ extdef_math_code_cmd, /* define an extended character code ( \.{\\Umathcode}, etc.~) */
+ extdef_del_code_cmd, /* define an extended delimiter code ( \.{\\Udelcode}, etc.~) */
+ def_family_cmd, /* declare math fonts ( \.{\\textfont}, etc.~) */
+ set_math_param_cmd, /* set math parameters ( \.{\\mathquad}, etc.~) */
+ set_font_cmd, /* set current font ( font identifiers ) */
+ def_font_cmd, /* define a font file ( \.{\\font} ) */
+ register_cmd, /* internal register ( \.{\\count}, \.{\\dimen}, etc.~) */
+ assign_box_direction_cmd, /* (\.{\\boxdirection}) */
+ assign_box_dir_cmd, /* (\.{\\boxdir}) */
+ assign_direction_cmd, /* (\.{\\pagedirection}, \.{\\textdirection}) */
+ assign_dir_cmd, /* (\.{\\pagedir}, \.{\\textdir}) */
+ combine_toks_cmd,
+# define max_internal_cmd assign_dir_cmd /* the largest code that can follow \.{\\the} */
+ advance_cmd, /* advance a register or parameter ( \.{\\advance} ) */
+ multiply_cmd, /* multiply a register or parameter ( \.{\\multiply} ) */
+ divide_cmd, /* divide a register or parameter ( \.{\\divide} ) */
+ prefix_cmd, /* qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} ) */
+ let_cmd, /* assign a command code ( \.{\\let}, \.{\\futurelet} ) */
+ shorthand_def_cmd, /* code definition ( \.{\\chardef}, \.{\\countdef}, etc.~) */
+ def_lua_call_cmd,
+ read_to_cs_cmd, /* read into a control sequence ( \.{\\read} ) */
+ def_cmd, /* macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} ) */
+ set_box_cmd, /* set a box ( \.{\\setbox} ) */
+ hyph_data_cmd, /* hyphenation data ( \.{\\hyphenation}, \.{\\patterns} ) */
+ set_interaction_cmd, /* define level of interaction ( \.{\\batchmode}, etc.~) */
+ letterspace_font_cmd, /* letterspace a font ( \.{\\letterspacefont} ) */
+ expand_font_cmd, /* expand glyphs ( \.{\\expandglyphsinfont} ) */
+ copy_font_cmd, /* create a new font instance ( \.{\\copyfont} ) */
+ set_font_id_cmd,
+ undefined_cs_cmd, /* initial state of most |eq_type| fields */
+ expand_after_cmd, /* special expansion ( \.{\\expandafter} ) */
+ no_expand_cmd, /* special nonexpansion ( \.{\\noexpand} ) */
+ input_cmd, /* input a source file ( \.{\\input}, \.{\\endinput} or \.{\\scantokens} or \.{\\scantextokens} ) */
+ lua_expandable_call_cmd,
+ lua_local_call_cmd,
+ if_test_cmd, /* conditional text ( \.{\\if}, \.{\\ifcase}, etc.~) */
+ fi_or_else_cmd, /* delimiters for conditionals ( \.{\\else}, etc.~) */
+ cs_name_cmd, /* make a control sequence from tokens ( \.{\\csname} ) */
+ convert_cmd, /* convert to text ( \.{\\number}, \.{\\string}, etc.~) */
+ variable_cmd,
+ feedback_cmd,
+ the_cmd, /* expand an internal quantity ( \.{\\the} or \.{\\unexpanded}, \.{\\detokenize} ) */
+ top_bot_mark_cmd, /* inserted mark ( \.{\\topmark}, etc.~) */
+ call_cmd, /* non-long, non-outer control sequence */
+ long_call_cmd, /* long, non-outer control sequence */
+ outer_call_cmd, /* non-long, outer control sequence */
+ long_outer_call_cmd, /* long, outer control sequence */
+ end_template_cmd, /* end of an alignment template */
+ dont_expand_cmd, /* the following token was marked by \.{\\noexpand} */
+ glue_ref_cmd, /* the equivalent points to a glue specification */
+ shape_ref_cmd, /* the equivalent points to a parshape specification */
+ box_ref_cmd, /* the equivalent points to a box node, or is |null| */
+ data_cmd, /* the equivalent is simply a halfword number */
+} tex_command_code;
+
+# define max_command_cmd set_font_id_cmd /* the largest command code seen at |big_switch| */
+# define last_cmd data_cmd
+# define max_non_prefixed_command last_item_cmd
+
+typedef enum {
+ above_code = 0,
+ over_code = 1,
+ atop_code = 2,
+ skewed_code = 3,
+ delimited_code = 4,
+} fraction_codes;
+
+typedef enum {
+ number_code = 0, /* command code for \.{\\number} */
+ lua_code, /* command code for \.{\\directlua} */
+ lua_function_code, /* command code for \.{\\luafunction} */
+ lua_bytecode_code, /* command code for \.{\\luabytecode} */
+ expanded_code, /* command code for \.{\\expanded} */
+ immediate_assignment_code, /* command code for \.{\\immediateassignment} */
+ immediate_assigned_code, /* command code for \.{\\assigned} */
+ math_style_code, /* command code for \.{\\mathstyle} */
+ string_code, /* command code for \.{\\string} */
+ cs_string_code, /* command code for \.{\\csstring} */
+ roman_numeral_code, /* command code for \.{\\romannumeral} */
+ meaning_code, /* command code for \.{\\meaning} */
+ uchar_code, /* command code for \.{\\Uchar} */
+ lua_escape_string_code, /* command code for \.{\\luaescapestring} */
+ font_id_code, /* command code for \.{\\fontid} */
+ font_name_code, /* command code for \.{\\fontname} */
+ left_margin_kern_code, /* command code for \.{\\leftmarginkern} */
+ right_margin_kern_code, /* command code for \.{\\rightmarginkern} */
+ uniform_deviate_code, /* command code for \.{\\uniformdeviate} */
+ normal_deviate_code, /* command code for \.{\\normaldeviate} */
+ math_char_class_code,
+ math_char_fam_code,
+ math_char_slot_code,
+ insert_ht_code, /* command code for \.{\\insertht} */
+ job_name_code, /* command code for \.{\\jobname} */
+ format_name_code, /* command code for \.{\\AlephVersion} */
+ luatex_banner_code, /* command code for \.{\\luatexbanner}: */
+ luatex_revision_code, /* command code for \.{\\luatexrevision} */
+ etex_code, /* command code for \.{\\eTeXVersion} */
+ eTeX_revision_code, /* command code for \.{\\eTeXrevision} */
+ font_identifier_code, /* command code for \.{tex.fontidentifier} (virtual) */
+ /* backend */
+ dvi_feedback_code,
+ pdf_feedback_code,
+ dvi_variable_code,
+ pdf_variable_code,
+} convert_codes;
+
+typedef enum {
+ lastpenalty_code = 0, /* code for \.{\\lastpenalty} */
+ lastattr_code, /* not used */
+ lastkern_code, /* code for \.{\\lastkern} */
+ lastskip_code, /* code for \.{\\lastskip} */
+ last_node_type_code, /* code for \.{\\lastnodetype} */
+ input_line_no_code, /* code for \.{\\inputlineno} */
+ badness_code, /* code for \.{\\badness} */
+ last_saved_box_resource_index_code, /* code for \.{\\lastsavedboxresourceindex} */
+ last_saved_image_resource_index_code, /* code for \.{\\lastsavedimageresourceindex} */
+ last_saved_image_resource_pages_code, /* code for \.{\\lastsavedimageresourcepages} */
+ last_x_pos_code, /* code for \.{\\lastxpos} */
+ last_y_pos_code, /* code for \.{\\lastypos} */
+ random_seed_code, /* code for \.{\\randomseed} */
+ luatex_version_code, /* code for \.{\\luatexversion} */
+ eTeX_minor_version_code, /* code for \.{\\eTeXminorversion} */
+ eTeX_version_code, /* code for \.{\\eTeXversion} */
+# define eTeX_int eTeX_version_code /* first of \eTeX\ codes for integers */
+ current_group_level_code, /* code for \.{\\currentgrouplevel} */
+ current_group_type_code, /* code for \.{\\currentgrouptype} */
+ current_if_level_code, /* code for \.{\\currentiflevel} */
+ current_if_type_code, /* code for \.{\\currentiftype} */
+ current_if_branch_code, /* code for \.{\\currentifbranch} */
+ glue_stretch_order_code, /* code for \.{\\gluestretchorder} */
+ glue_shrink_order_code, /* code for \.{\\glueshrinkorder} */
+ eTeX_glue_stretch_order_code, /* code for \.{\\eTeXgluestretchorder} */
+ eTeX_glue_shrink_order_code, /* code for \.{\\eTeXglueshrinkorder} */
+ font_char_wd_code, /* code for \.{\\fontcharwd} */
+# define eTeX_dim font_char_wd_code /* first of \eTeX\ codes for dimensions */
+ font_char_ht_code, /* code for \.{\\fontcharht} */
+ font_char_dp_code, /* code for \.{\\fontchardp} */
+ font_char_ic_code, /* code for \.{\\fontcharic} */
+ par_shape_length_code, /* code for \.{\\parshapelength} */
+ par_shape_indent_code, /* code for \.{\\parshapeindent} */
+ par_shape_dimen_code, /* code for \.{\\parshapedimen} */
+ glue_stretch_code, /* code for \.{\\gluestretch} */
+ glue_shrink_code, /* code for \.{\\glueshrink} */
+ mu_to_glue_code, /* code for \.{\\mutoglue} */
+# define eTeX_glue mu_to_glue_code /* first of \eTeX\ codes for glue */
+ glue_to_mu_code, /* code for \.{\\gluetomu} */
+# define eTeX_mu glue_to_mu_code /* first of \eTeX\ codes for muglue */
+ numexpr_code, /* code for \.{\\numexpr} */
+# define eTeX_expr numexpr_code /* first of \eTeX\ codes for expressions */
+ attrexpr_code, /* not used */
+ dimexpr_code, /* code for \.{\\dimexpr} */
+ glueexpr_code, /* code for \.{\\glueexpr} */
+ muexpr_code, /* code for \.{\\muexpr} */
+} last_item_codes;
+
+
+typedef enum {
+ save_cat_code_table_code=0,
+ init_cat_code_table_code,
+ set_random_seed_code,
+ save_pos_code,
+ late_lua_code,
+ late_lua_call_code,
+ expand_font_code,
+} normal_codes;
+
+typedef enum {
+ lp_code_base = 2,
+ rp_code_base = 3,
+ ef_code_base = 4,
+ tag_code = 5,
+ no_lig_code = 6,
+ gp_code_base = 7
+} font_codes ;
+
+# define immediate_code 4 /* command modifier for \.{\\immediate} */
+
+extern void initialize_commands(void);
+extern void initialize_etex_commands(void);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.c b/Build/source/texk/web2c/luatexdir/tex/conditional.c
new file mode 100644
index 00000000000..948db580340
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/conditional.c
@@ -0,0 +1,568 @@
+/*
+
+conditional.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+@* We consider now the way \TeX\ handles various kinds of \.{\\if} commands.
+
+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|.
+
+In |cond_ptr| we keep track of the top of the condition stack while |if_limit|
+holds the upper bound on |fi_or_else| codes. The type of conditional being worked
+on is stored in cur_if and |if_line| keeps track of the line where that
+conditional began. When we skip conditional text, |skip_line| keeps track of the
+line number where skipping began, for use in error messages.
+
+*/
+
+halfword cond_ptr;
+int if_limit, cur_if, if_line, skip_line;
+
+/*tex
+
+Here is a procedure that ignores text until coming to an \.{\\or}, \.{\\else}, or
+\.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$ nesting. After it has acted,
+|cur_chr| will indicate the token that was found, but |cur_tok| will not be set
+(because this makes the procedure run faster).
+
+With |l| we keep track of the level of $\.{\\if}\ldots\.{\\fi}$ nesting and
+|scanner_status| let us return to the entry status.
+
+*/
+
+void pass_text(void)
+{
+ int l = 0;
+ int save_scanner_status = scanner_status;
+ scanner_status = skipping;
+ skip_line = line;
+ while (1) {
+ get_next();
+ if (cur_cmd == fi_or_else_cmd) {
+ if (l == 0)
+ break;
+ if (cur_chr == fi_code)
+ decr(l);
+ } else if (cur_cmd == if_test_cmd) {
+ incr(l);
+ }
+ }
+ scanner_status = save_scanner_status;
+ if (tracing_ifs_par > 0)
+ show_cur_cmd_chr();
+}
+
+/*tex
+
+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'.
+
+*/
+
+void push_condition_stack(void)
+{
+ halfword p = new_node(if_node, 0);
+ vlink(p) = cond_ptr;
+ if_limit_type(p) = (quarterword) if_limit;
+ if_limit_subtype(p) = (quarterword) cur_if;
+ if_line_field(p) = if_line;
+ cond_ptr = p;
+ cur_if = cur_chr;
+ if_limit = if_code;
+ if_line = line;
+}
+
+void pop_condition_stack(void)
+{
+ halfword p;
+ if (if_stack[in_open] == cond_ptr) {
+ /*tex Conditionals are possibly not properly nested with files. */
+ if_warning();
+ }
+ p = cond_ptr;
+ if_line = if_line_field(p);
+ cur_if = if_limit_subtype(p);
+ if_limit = if_limit_type(p);
+ cond_ptr = vlink(p);
+ flush_node(p);
+}
+
+/*tex
+
+Here's a procedure that changes the |if_limit| code corresponding to a given
+value of |cond_ptr|.
+
+*/
+
+void change_if_limit(int l, halfword p)
+{
+ if (p == cond_ptr) {
+ if_limit = l;
+ } else {
+ halfword q = cond_ptr;
+ while (1) {
+ if (q == null)
+ confusion("if");
+ if (vlink(q) == p) {
+ if_limit_type(q) = (quarterword) l;
+ return;
+ }
+ q = vlink(q);
+ }
+ }
+}
+
+/*tex
+
+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).
+
+*/
+
+static halfword last_tested_cs ;
+
+static boolean test_for_cs(void)
+{
+ /*tex Is the condition true? */
+ boolean b = false;
+ /*tex To be tested against the second operand: */
+ int m, s;
+ /*tex For traversing token lists in \.{\\ifx} tests: */
+ halfword q;
+ halfword n = get_avail();
+ /*tex Head of the list of characters: */
+ halfword p = n;
+ is_in_csname += 1;
+ while (1) {
+ get_x_token();
+ if (cur_cs != 0)
+ break;
+ store_new_token(cur_tok);
+ }
+ if (cur_cmd != end_cs_name_cmd) {
+ last_tested_cs = null_cs;
+ if (suppress_ifcsname_error_par) {
+ do {
+ get_x_token();
+ } while (cur_cmd != end_cs_name_cmd);
+ flush_list(n);
+ is_in_csname -= 1;
+ return b;
+ } else {
+ complain_missing_csname();
+ }
+ }
+ /*tex Look up the characters of list |n| in the hash table, and set |cur_cs|. */
+ m = first;
+ p = token_link(n);
+ while (p != null) {
+ if (m >= max_buf_stack) {
+ max_buf_stack = m + 4;
+ if (max_buf_stack >= buf_size)
+ check_buffer_overflow(max_buf_stack);
+ }
+ s = token_chr(token_info(p));
+ if (s <= 0x7F) {
+ buffer[m++] = (packed_ASCII_code) s;
+ } else if (s <= 0x7FF) {
+ buffer[m++] = (packed_ASCII_code) (0xC0 + s / 0x40);
+ buffer[m++] = (packed_ASCII_code) (0x80 + s % 0x40);
+ } else if (s <= 0xFFFF) {
+ buffer[m++] = (packed_ASCII_code) (0xE0 + s / 0x1000);
+ buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) / 0x40);
+ buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) % 0x40);
+ } else {
+ buffer[m++] = (packed_ASCII_code) (0xF0 + s / 0x40000);
+ buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x40000) / 0x1000);
+ buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) / 0x40);
+ buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) % 0x40);
+ }
+ p = token_link(p);
+ }
+ if (m > first) {
+ /*tex |no_new_control_sequence| is |true| */
+ cur_cs = id_lookup(first, m - first);
+ } else if (m == first) {
+ /*tex the list is empty */
+ cur_cs = null_cs;
+ }
+ b = (eq_type(cur_cs) != undefined_cs_cmd);
+ flush_list(n);
+ last_cs_name = cur_cs;
+ is_in_csname -= 1;
+ return b;
+}
+
+/*tex
+
+An active character will be treated as category 13 following \.{\\if\\noexpand}
+or following \.{\\ifcat\\noexpand}.
+
+*/
+
+#define get_x_token_or_active_char() do { \
+ get_x_token(); \
+ if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \
+ if (is_active_cs(cs_text(cur_cs))) { \
+ cur_cmd=active_char_cmd; \
+ cur_chr=active_cs_value(cs_text(cur_tok-cs_token_flag)); \
+ } \
+ } \
+} while (0)
+
+/*tex
+
+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@>
+
+*/
+
+void conditional(void)
+{
+ /*tex Is the condition true? */
+ boolean b = false;
+ /*tex The relation to be evaluated: */
+ int r;
+ /*tex To be tested against the second operand: */
+ int m, n;
+ /*tex For traversing token lists in \.{\\ifx} tests: */
+ halfword p, q;
+ /*tex The |scanner_status| upon entry: */
+ int save_scanner_status;
+ /*tex The |cond_ptr| corresponding to this conditional: */
+ halfword save_cond_ptr;
+ /*tex The type of this conditional: */
+ int this_if;
+ /*tex Was this \.{\\if} preceded by \.{\\unless}? */
+ boolean is_unless;
+ if ((tracing_ifs_par > 0) && (tracing_commands_par <= 1)) {
+ show_cur_cmd_chr();
+ }
+ push_condition_stack();
+ save_cond_ptr = cond_ptr;
+ is_unless = (cur_chr >= unless_code);
+ this_if = cur_chr % unless_code;
+ /*tex Either process \.{\\ifcase} or set |b| to the value of a boolean condition. */
+ switch (this_if) {
+ case if_char_code:
+ case if_cat_code:
+ /*tex Test if two characters match. */
+ get_x_token_or_active_char();
+ if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
+ /*tex It's not a character. */
+ m = relax_cmd;
+ n = too_big_char;
+ } else {
+ m = cur_cmd;
+ n = cur_chr;
+ }
+ get_x_token_or_active_char();
+ if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
+ cur_cmd = relax_cmd;
+ cur_chr = too_big_char;
+ }
+ if (this_if == if_char_code)
+ b = (n == cur_chr);
+ else
+ b = (m == cur_cmd);
+ break;
+ case if_int_code:
+ case if_dim_code:
+ case if_abs_dim_code:
+ case if_abs_num_code:
+ /*tex
+ Test the relation between integers or dimensions. Here we use the fact
+ that |<|, |=|, and |>| are consecutive ASCII codes.
+ */
+ if (this_if == if_int_code || this_if == if_abs_num_code)
+ scan_int();
+ else
+ scan_normal_dimen();
+ n = cur_val;
+ if ((n < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
+ negate(n);
+ /*tex Get the next non-blank non-call... */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ r = cur_tok - other_token;
+ if ((r < '<') || (r > '>')) {
+ print_err("Missing = inserted for ");
+ print_cmd_chr(if_test_cmd, this_if);
+ help1("I was expecting to see `<', `=', or `>'. Didn't.");
+ back_error();
+ r = '=';
+ }
+ if (this_if == if_int_code || this_if == if_abs_num_code)
+ scan_int();
+ else
+ scan_normal_dimen();
+ if ((cur_val < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
+ negate(cur_val);
+ switch (r) {
+ case '<':
+ b = (n < cur_val);
+ break;
+ case '=':
+ b = (n == cur_val);
+ break;
+ case '>':
+ b = (n > cur_val);
+ break;
+ default:
+ /*tex This can't happen. */
+ b = false;
+ break;
+ }
+ break;
+ case if_odd_code:
+ /*tex Test if an integer is odd. */
+ scan_int();
+ b = odd(cur_val);
+ break;
+ case if_vmode_code:
+ b = (abs(cur_list.mode_field) == vmode);
+ break;
+ case if_hmode_code:
+ b = (abs(cur_list.mode_field) == hmode);
+ break;
+ case if_mmode_code:
+ b = (abs(cur_list.mode_field) == mmode);
+ break;
+ case if_inner_code:
+ b = (cur_list.mode_field < 0);
+ break;
+ case if_void_code:
+ scan_register_num();
+ p = box(cur_val);
+ b = (p == null);
+ break;
+ case if_hbox_code:
+ scan_register_num();
+ p = box(cur_val);
+ b = (p != null) && (type(p) == hlist_node);
+ break;
+ case if_vbox_code:
+ scan_register_num();
+ p = box(cur_val);
+ b = (p != null) && (type(p) == vlist_node);
+ break;
+ case if_x_code:
+ /*tex
+ Test if two tokens match. 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.
+ */
+ 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_cmd) {
+ b = (cur_chr == q);
+ } else {
+ /*tex
+ Test if two macro texts match. 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}}$$
+ */
+ p = token_link(cur_chr);
+ /*tex Omit reference counts. */
+ q = token_link(equiv(n));
+ if (p == q) {
+ b = true;
+ } else {
+ while ((p != null) && (q != null)) {
+ if (token_info(p) != token_info(q)) {
+ p = null;
+ break;
+ } else {
+ p = token_link(p);
+ q = token_link(q);
+ }
+ }
+ b = ((p == null) && (q == null));
+ }
+ }
+ scanner_status = save_scanner_status;
+ 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;
+ case if_case_code:
+ /*tex Select the appropriate case and |return| or |goto common_ending|. */
+ scan_int();
+ /*tex |n| is the number of cases to pass. */
+ n = cur_val;
+ if (tracing_commands_par > 1) {
+ begin_diagnostic();
+ tprint("{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_condition_stack();
+ }
+ }
+ change_if_limit(or_code, save_cond_ptr);
+ /*tex Wait for \.{\\or}, \.{\\else}, or \.{\\fi}. */
+ return;
+ break;
+ case if_primitive_code:
+ save_scanner_status = scanner_status;
+ scanner_status = normal;
+ get_next();
+ scanner_status = save_scanner_status;
+ m = prim_lookup(cs_text(cur_cs));
+ b = ((cur_cmd != undefined_cs_cmd) &&
+ (m != undefined_primitive) &&
+ (cur_cmd == get_prim_eq_type(m)) &&
+ (cur_chr == get_prim_equiv(m)));
+ break;
+ case if_def_code:
+ /*tex
+ 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.
+ */
+ save_scanner_status = scanner_status;
+ scanner_status = normal;
+ get_next();
+ b = (cur_cmd != undefined_cs_cmd);
+ scanner_status = save_scanner_status;
+ break;
+ case if_cs_code:
+ b = test_for_cs();
+ break;
+ case if_in_csname_code:
+ b = is_in_csname;
+ break;
+ case if_font_char_code:
+ /*tex
+ The conditional \.{\\iffontchar} tests the existence of a
+ character in a font.
+ */
+ scan_font_ident();
+ n = cur_val;
+ scan_char_num();
+ b = char_exists(n, cur_val);
+ break;
+ default:
+ /*tex there are no other cases, but we need to please |-Wall|. */
+ b = false;
+ }
+ if (is_unless)
+ b = !b;
+ if (tracing_commands_par > 1) {
+ /*tex Display the value of |b|. */
+ begin_diagnostic();
+ if (b)
+ tprint("{true}");
+ else
+ tprint("{false}");
+ end_diagnostic(false);
+ }
+ if (b) {
+ change_if_limit(else_code, save_cond_ptr);
+ /*tex Wait for \.{\\else} or \.{\\fi}. */
+ return;
+ }
+ /*tex
+ Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|. 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.
+ */
+ while (1) {
+ pass_text();
+ if (cond_ptr == save_cond_ptr) {
+ if (cur_chr != or_code)
+ goto COMMON_ENDING;
+ print_err("Extra \\or");
+ help1(
+ "I'm ignoring this; it doesn't match any \\if."
+ );
+ error();
+ } else if (cur_chr == fi_code) {
+ pop_condition_stack();
+ }
+ }
+ COMMON_ENDING:
+ if (cur_chr == fi_code) {
+ pop_condition_stack();
+ } else {
+ /*tex Wait for \.{\\fi}. */
+ if_limit = fi_code;
+ }
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.h b/Build/source/texk/web2c/luatexdir/tex/conditional.h
new file mode 100644
index 00000000000..483496eece2
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/conditional.h
@@ -0,0 +1,78 @@
+/* conditional.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef CONDITIONAL_H
+# define CONDITIONAL_H
+
+# define unless_code 32 /* amount added for `\.{\\unless}' prefix */
+
+typedef enum {
+ if_char_code = 0, /* \.{\\if} */
+ if_cat_code = 1, /* \.{\\ifcat} */
+ if_int_code = 2, /* \.{\\ifnum} */
+ if_dim_code = 3, /* \.{\\ifdim} */
+ if_odd_code = 4, /* \.{\\ifodd} */
+ if_vmode_code = 5, /* \.{\\ifvmode} */
+ if_hmode_code = 6, /* \.{\\ifhmode} */
+ if_mmode_code = 7, /* \.{\\ifmmode} */
+ if_inner_code = 8, /* \.{\\ifinner} */
+ if_void_code = 9, /* \.{\\ifvoid} */
+ if_hbox_code = 10, /* \.{\\ifhbox} */
+ if_vbox_code = 11, /* \.{\\ifvbox} */
+ if_x_code = 12, /* \.{\\ifx} */
+ if_eof_code = 13, /* \.{\\ifeof} */
+ if_true_code = 14, /* \.{\\iftrue} */
+ if_false_code = 15, /* \.{\\iffalse} */
+ if_case_code = 16, /* \.{\\ifcase} */
+ if_def_code = 17, /* \.{\\ifdefined} */
+ if_cs_code = 18, /* \.{\\ifcsname} */
+ if_font_char_code = 19, /* \.{\\iffontchar} */
+ if_in_csname_code = 20, /* \.{\\ifincsname} */
+ if_primitive_code = 21, /* \.{\\ifprimitive} */
+ if_abs_num_code = 22, /* \.{\\ifabsnum} */
+ if_abs_dim_code = 23, /* \.{\\ifabsdim} */
+ if_condition_code = 24, /* \.{\\ifcondition} */
+} if_type_codes;
+
+# define if_limit_subtype(A) subtype((A)+1)
+# define if_limit_type(A) type((A)+1)
+# define if_line_field(A) vlink((A)+1)
+
+typedef enum {
+ if_code = 1, /* \.{\\if...} */
+ fi_code = 2, /* \.{\\fi} */
+ else_code = 3, /* \.{\\else} */
+ or_code = 4, /* \.{\\or} */
+} else_type_codes;
+
+extern halfword cond_ptr; /* top of the condition stack */
+extern int if_limit; /* upper bound on |fi_or_else| codes */
+extern int cur_if; /* type of conditional being worked on */
+extern int if_line; /* line where that conditional began */
+extern int skip_line; /* skipping began here */
+
+extern void pass_text(void);
+extern void push_condition_stack(void);
+extern void pop_condition_stack(void);
+extern void change_if_limit(int l, halfword p);
+
+extern void conditional(void);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/directions.c b/Build/source/texk/web2c/luatexdir/tex/directions.c
new file mode 100644
index 00000000000..59b2f7dcd85
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/directions.c
@@ -0,0 +1,196 @@
+/*
+
+directions.w
+
+Copyright 2009-2014 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+void scan_direction(void)
+{
+ int save_cur_cmd = cur_cmd;
+ int save_cur_chr = cur_chr;
+ get_x_token();
+ if (cur_cmd == assign_dir_cmd) {
+ cur_val = eqtb[cur_chr].cint;
+ goto EXIT;
+ } else {
+ back_input();
+ }
+ if (scan_keyword("tlt")) {
+ cur_val = dir_TLT;
+ } else if (scan_keyword("trt")) {
+ cur_val = dir_TRT;
+ } else if (scan_keyword("ltl")) {
+ cur_val = dir_LTL;
+ } else if (scan_keyword("rtt")) {
+ cur_val = dir_RTT;
+ } else {
+ tex_error("Bad direction", NULL);
+ cur_val = 0;
+ }
+ get_x_token();
+ if (cur_cmd != spacer_cmd)
+ back_input();
+ EXIT:
+ cur_cmd = save_cur_cmd;
+ cur_chr = save_cur_chr;
+}
+
+/*tex
+
+ The next two are used by |postlinebreak.c|:
+
+*/
+
+halfword do_push_dir_node(halfword p, halfword a)
+{
+ halfword n = copy_node(a);
+ vlink(n) = p;
+ return n;
+}
+
+halfword do_pop_dir_node(halfword p)
+{
+ halfword n = vlink(p);
+ flush_node(p);
+ return n;
+}
+
+halfword dir_ptr;
+halfword text_dir_ptr;
+
+void initialize_directions(void)
+{
+ /*tex There is no need to do anything here at the moment. */
+}
+
+halfword new_dir(int s)
+{
+ halfword p = new_node(dir_node, 0);
+ dir_dir(p) = s;
+ dir_level(p) = cur_level;
+ return p;
+}
+
+const char *dir_strings_par[4] = { [0] =
+ "TLT","TRT","LTL","RTT",
+};
+
+const char *dir_strings_text_normal[4] = { [0] =
+ "+TLT","+TRT","+LTL","+RTT",
+};
+
+const char *dir_strings_text_cancel[4] = { [0] =
+ "-TLT","-TRT","-LTL","-RTT",
+};
+
+void print_dir_par(int d)
+{
+ tprint(dir_strings_par[d]);
+}
+
+void print_dir_text(halfword d)
+{
+ if (subtype(d) == cancel_dir) {
+ tprint(dir_strings_text_cancel[dir_dir(d)]);
+ } else {
+ tprint(dir_strings_text_normal[dir_dir(d)]);
+ }
+}
+
+scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph)
+{
+ scaled wd = 0;
+ if (isglyph) {
+ if (textdir_parallel(curdir, pdir) == textglyphdir_orthogonal(pdir)) {
+ wd = glyph_width(p);
+ if (ex_glyph(p) != 0) {
+ /* wd = round_xn_over_d(wd, 1000 + ex_glyph(p)/1000, 1000); */
+ wd = ext_xn_over_d(wd, 1000000+ex_glyph(p), 1000000);
+
+ }
+ } else {
+ wd = glyph_depth(p) + glyph_height(p);
+ }
+ } else {
+ if (textdir_parallel(pdir, curdir))
+ wd = width(p);
+ else
+ wd = depth(p) + height(p);
+ }
+ return wd;
+}
+
+scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph)
+{
+ scaled_whd whd = { 0, 0, 0 };
+ whd.wd = pack_width(curdir, pdir, p, isglyph);
+ if (isglyph) {
+ if (is_rotated(curdir)) {
+ if (textdir_parallel(curdir, pdir))
+ whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2;
+ else
+ whd.ht = whd.dp = glyph_width(p) / 2;
+ } else if (is_rotated(pdir)) {
+ if (textdir_parallel(curdir, pdir))
+ whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2;
+ else
+ whd.ht = glyph_width(p);
+ } else {
+ if (glyphdir_eq(curdir, pdir)) {
+ whd.ht = glyph_height(p);
+ whd.dp = glyph_depth(p);
+ } else if (glyphdir_opposite(curdir, pdir)) {
+ whd.ht = glyph_depth(p);
+ whd.dp = glyph_height(p);
+ } else
+ whd.ht = glyph_width(p);
+ }
+ } else {
+ if (is_rotated(curdir)) {
+ if (textdir_parallel(curdir, pdir))
+ whd.ht = whd.dp = (height(p) + depth(p)) / 2;
+ else
+ whd.ht = whd.dp = width(p) / 2;
+ } else if (pardir_eq(curdir, pdir)) {
+ whd.ht = height(p);
+ whd.dp = depth(p);
+ } else if (pardir_opposite(curdir, pdir)) {
+ whd.ht = depth(p);
+ whd.dp = height(p);
+ } else
+ whd.ht = width(p);
+ }
+ return whd;
+}
+
+void update_text_dir_ptr(int val)
+{
+ if (dir_level(text_dir_ptr) == cur_level) {
+ /* update */
+ dir_dir(text_dir_ptr) = val;
+ } else {
+ /* addition */
+ halfword text_dir_tmp = new_dir(val);
+ vlink(text_dir_tmp) = text_dir_ptr;
+ text_dir_ptr = text_dir_tmp;
+ }
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/directions.h b/Build/source/texk/web2c/luatexdir/tex/directions.h
new file mode 100644
index 00000000000..92d6cc18f12
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/directions.h
@@ -0,0 +1,168 @@
+/* directions.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef DIRECTIONS_H
+# define DIRECTIONS_H
+
+/*
+#define dir_swap 4
+*/
+
+#define dir_min_value 0
+#define dir_max_value 3
+
+#define check_dir_value(d) \
+ if ((d < dir_min_value) || (d > dir_max_value)) \
+ d = dir_min_value;
+
+#define is_mirrored(a) 0
+
+#define is_rotated(a) (a == dir_RTT)
+
+/*
+
+ # define textdir_parallel(a,b) ( \
+ ((a == dir_TLT || a == dir_TRT) && (b == dir_TLT || b == dir_TRT)) || \
+ ((a == dir_LTL || a == dir_RTT) && (b == dir_LTL || b == dir_RTT)) \
+ )
+
+ # define pardir_parallel(a,b) ( \
+ ((a == dir_TLT || a == dir_TRT) && (b == dir_TLT || b == dir_TRT)) || \
+ ((a == dir_LTL || a == dir_RTT) && (b == dir_LTL || b == dir_RTT)) \
+ )
+
+ # define pardir_eq(a,b) ( \
+ ((a == dir_TLT || a == dir_TRT) && (b == dir_TLT || b == dir_TRT)) || \
+ (a == dir_LTL && b == dir_LTL) || \
+ (a == dir_RTT && b == dir_RTT) \
+ )
+
+ # define textdir_eq(a,b) ( \
+ (a == dir_TLT && b == dir_TLT) || \
+ (a == dir_TRT && b == dir_TRT) || \
+ (a == dir_LTL && (b == dir_LTL || b == dir_RTT)) || \
+ (a == dir_RTT && (b == dir_LTL || b == dir_RTT))
+
+ # define partextdir_eq(a,b) ( \
+ ((a == dir_TLT || a == dir_TRT) && (b == dir_LTL || b == dir_RTT)) || \
+ (a == dir_LTL && b == dir_TLT) || \
+ (a == dir_RTT && b == dir_TRT) \
+ )
+
+ # define dir_TLT_or_TRT(a) (a == dir_TLT || a == dir_TRT)
+ # define dir_LTL_or_RTT(a) (a == dir_LTL || a == dir_RTT)
+
+*/
+
+/* TLT TRT LTL RTT */
+
+# define dir_TLT_or_TRT(a) (a < 2)
+# define dir_LTL_or_RTT(a) (a > 1)
+
+# define textdir_parallel(a,b) ( \
+ (dir_TLT_or_TRT(a) && dir_TLT_or_TRT(b)) || \
+ (dir_LTL_or_RTT(a) && dir_LTL_or_RTT(b)) \
+)
+
+# define pardir_parallel(a,b) ( \
+ (dir_TLT_or_TRT(a) && dir_TLT_or_TRT(b)) || \
+ (dir_LTL_or_RTT(a) && dir_LTL_or_RTT(b)) \
+)
+
+# define pardir_opposite(a,b) ( \
+ (a == dir_LTL && b == dir_RTT) || \
+ (a == dir_RTT && b == dir_LTL) \
+)
+
+# define textdir_opposite(a,b) ( \
+ (a == dir_TLT && b == dir_TRT) || \
+ (a == dir_TRT && b == dir_TLT) \
+)
+
+# define glyphdir_opposite(a,b) 0
+
+# define pardir_eq(a,b) ( \
+ (dir_TLT_or_TRT(a) && dir_TLT_or_TRT(b)) || \
+ (a == dir_LTL && b == dir_LTL) || \
+ (a == dir_RTT && b == dir_RTT) \
+)
+
+# define textdir_eq(a,b) ( \
+ (a == dir_TLT && b == dir_TLT) || \
+ (a == dir_TRT && b == dir_TRT) || \
+ (a == dir_LTL && dir_LTL_or_RTT(b)) || \
+ (a == dir_RTT && dir_LTL_or_RTT(b)) \
+)
+
+# define glyphdir_eq(a,b) ( \
+ (a != dir_LTL && b != dir_LTL) || \
+ (a == dir_LTL && b == dir_LTL) \
+)
+
+# define partextdir_eq(a,b) ( \
+ (dir_TLT_or_TRT(a) && dir_LTL_or_RTT(b)) || \
+ (a == dir_LTL && b == dir_TLT) || \
+ (a == dir_RTT && b == dir_TRT) \
+)
+
+# define textglyphdir_orthogonal(a) (a != dir_RTT)
+
+# define textdir_is_L(a) (a == dir_TLT)
+
+# define push_dir(p,a) { \
+ halfword dir_tmp=new_dir((a)); \
+ vlink(dir_tmp)=p; \
+ p=dir_tmp; \
+}
+
+# define push_dir_node(p,a) { \
+ halfword dir_tmp=copy_node((a)); \
+ vlink(dir_tmp)=p; \
+ p=dir_tmp; \
+}
+
+# define pop_dir_node(p) { \
+ halfword dir_tmp=p; \
+ p=vlink(dir_tmp); \
+ flush_node(dir_tmp); \
+}
+
+extern halfword dir_ptr;
+
+extern halfword text_dir_ptr;
+
+extern void initialize_directions(void);
+extern halfword new_dir(int s);
+
+extern const char *string_dir(int d);
+extern void print_dir_par(int d);
+extern void print_dir_text(halfword d);
+
+extern void scan_direction(void);
+
+extern halfword do_push_dir_node(halfword p, halfword a);
+extern halfword do_pop_dir_node(halfword p);
+
+scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph);
+scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph);
+
+void update_text_dir_ptr(int val);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.c b/Build/source/texk/web2c/luatexdir/tex/dumpdata.c
new file mode 100644
index 00000000000..e7d53050b29
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.c
@@ -0,0 +1,539 @@
+/*
+
+dumpdata.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+ We use a magic number to register the version of the format. Normally this
+ number only increments when we add a new primitive of change command codes.
+ We start with 907 which is the sum of the values of the bytes of \quote
+ {don knuth}.
+
+*/
+
+#define FORMAT_ID (907+54)
+#if ((FORMAT_ID>=0) && (FORMAT_ID<=256))
+#error Wrong value for FORMAT_ID.
+#endif
+
+/*tex
+
+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. |FORMAT_ID| is a new field of type int suitable for the identification of
+a format: values between 0 and 256 (included) can not be used because in the
+previous format they are used for the length of the name of the engine.
+
+*/
+
+str_number format_ident;
+str_number format_name;
+
+
+/*tex
+
+Format files consist of |memory_word| items, and we use the following macros to
+dump words of different types:
+
+*/
+
+FILE *fmt_file;
+
+void store_fmt_file(void)
+{
+ int j, k, l, x;
+ halfword p;
+ char *format_engine;
+ int callback_id;
+ char *fmtname = NULL;
+ /*tex
+ If dumping is not allowed, abort. 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 (save_ptr != 0) {
+ print_err("You can't dump inside a group");
+ help1("`{...\\dump}' is a no-no.");
+ succumb();
+ }
+ /*tex
+ Create the |format_ident|, open the format file, and inform the user that
+ dumping has begun.
+ */
+ callback_id = callback_defined(pre_dump_callback);
+ if (callback_id > 0) {
+ (void) run_callback(callback_id, "->");
+ }
+ selector = new_string;
+ tprint(" (format=");
+ print(job_name);
+ print_char(' ');
+ print_int(year_par);
+ print_char('.');
+ print_int(month_par);
+ print_char('.');
+ print_int(day_par);
+ print_char(')');
+ str_room(2);
+ format_ident = make_string();
+ print(job_name);
+ format_name = make_string();
+ if (interaction == batch_mode) {
+ selector = log_only;
+ } else {
+ selector = term_and_log;
+ }
+ fmtname = pack_job_name(format_extension);
+ while (!zopen_w_output(&fmt_file, fmtname, FOPEN_WBIN_MODE)) {
+ fmtname = prompt_file_name("format file name", format_extension);
+ }
+ tprint_nl("Beginning to dump on file ");
+ tprint(fmtname);
+ free(fmtname);
+ tprint_nl("");
+ print(format_ident);
+ /*tex
+ Dump constants for consistency check. The next few sections of the
+ program should make it clear how we use the dump/undump macros. First
+ comes Web2C \TeX's magic constant: "W2TX"
+ */
+ dump_int(0x57325458);
+ dump_int(FORMAT_ID);
+ /*tex
+ We align |engine_name| to 4 bytes with one or more trailing |NUL|.
+ */
+ x = (int) strlen(engine_name);
+ format_engine = xmalloc((unsigned) (x + 4));
+ strcpy(format_engine, engine_name);
+ for (k = x; k <= x + 3; k++)
+ format_engine[k] = 0;
+ x = x + 4 - (x % 4);
+ dump_int(x);
+ dump_things(format_engine[0], x);
+ xfree(format_engine);
+ dump_int(0x57325458);
+ dump_int(max_halfword);
+ dump_int(hash_high);
+ dump_int(eqtb_size);
+ dump_int(hash_prime);
+ /*tex Dump the string pool. */
+ k = dump_string_pool();
+ print_ln();
+ print_int(k);
+ tprint(" strings using ");
+ print_int((longinteger) pool_size);
+ tprint(" bytes");
+ /*tex
+ Dump the dynamic memory. 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_node_mem();
+ dump_int(temp_token_head);
+ dump_int(hold_token_head);
+ dump_int(omit_template);
+ dump_int(null_list);
+ dump_int(backup_head);
+ dump_int(garbage);
+ x = (int) fix_mem_min;
+ dump_int(x);
+ x = (int) fix_mem_max;
+ dump_int(x);
+ x = (int) fix_mem_end;
+ dump_int(x);
+ dump_int(avail);
+ dyn_used = (int) fix_mem_end + 1;
+ dump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1);
+ x = x + (int) (fix_mem_end + 1 - fix_mem_min);
+ p = avail;
+ while (p != null) {
+ decr(dyn_used);
+ p = token_link(p);
+ }
+ dump_int(dyn_used);
+ print_ln();
+ print_int(x);
+ tprint(" memory locations dumped; current usage is ");
+ print_int(var_used);
+ print_char('&');
+ print_int(dyn_used);
+ /*tex
+ Dump regions 1 to 4 of |eqtb|, the table of equivalents. 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)$.
+ */
+ k = null_cs;
+ 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;
+ /*tex |j=int_base-1| */
+ goto DONE1;
+ 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);
+ dump_things(eqtb[k], l - k);
+ k = j + 1;
+ dump_int(k - l);
+ } while (k != int_base);
+ /*tex Dump regions 5 and 6 of |eqtb|. */
+ do {
+ j = k;
+ while (j < eqtb_size) {
+ if (eqtb[j].cint == eqtb[j + 1].cint)
+ goto FOUND2;
+ incr(j);
+ }
+ l = eqtb_size + 1;
+ /*tex |j=eqtb_size| */
+ goto DONE2;
+ FOUND2:
+ incr(j);
+ l = j;
+ while (j < eqtb_size) {
+ if (eqtb[j].cint != eqtb[j + 1].cint)
+ goto DONE2;
+ incr(j);
+ }
+ DONE2:
+ dump_int(l - k);
+ dump_things(eqtb[k], l - k);
+ k = j + 1;
+ dump_int(k - l);
+ } while (k <= eqtb_size);
+ if (hash_high > 0) {
+ /*tex Dump the |hash_extra| part: */
+ dump_things(eqtb[eqtb_size + 1], hash_high);
+ }
+ dump_int(par_loc);
+ dump_int(write_loc);
+ dump_math_codes();
+ dump_text_codes();
+ /*tex
+ Dump the hash table, 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_primitives();
+ dump_int(hash_used);
+ cs_count = frozen_control_sequence - 1 - hash_used + hash_high;
+ for (p = hash_base; p <= hash_used; p++) {
+ if (cs_text(p) != 0) {
+ dump_int(p);
+ dump_hh(hash[p]);
+ incr(cs_count);
+ }
+ }
+ dump_things(hash[hash_used + 1],undefined_control_sequence - 1 - hash_used);
+ if (hash_high > 0) {
+ dump_things(hash[eqtb_size + 1], hash_high);
+ }
+ dump_int(cs_count);
+ print_ln();
+ print_int(cs_count);
+ tprint(" multiletter control sequences");
+ /*tex Dump the font information. */
+ dump_int(max_font_id());
+ for (k = 0; k <= max_font_id(); k++) {
+ /*tex Dump the array info for internal font number |k|. */
+ dump_font(k);
+ tprint_nl("\\font");
+ print_esc(font_id_text(k));
+ print_char('=');
+ tprint_file_name((unsigned char *) font_name(k),
+ (unsigned char *) font_area(k), NULL);
+ if (font_size(k) != font_dsize(k)) {
+ tprint(" at ");
+ print_scaled(font_size(k));
+ tprint("pt");
+ }
+ }
+ print_ln();
+ print_int(max_font_id());
+ tprint(" preloaded font");
+ if (max_font_id() != 1)
+ print_char('s');
+ dump_math_data();
+ /*tex Dump the hyphenation tables. */
+ dump_language_data();
+ /*tex Dump a couple more things and the closing check word. */
+ dump_int(interaction);
+ dump_int(format_ident);
+ dump_int(format_name);
+ dump_int(69069);
+ /*tex
+ We have already printed a lot of statistics, so we set |tracing_stats:=0|
+ to prevent them from appearing again.
+ */
+ tracing_stats_par = 0;
+ /*tex Dump the \LUA\ bytecodes. */
+ dump_luac_registers();
+ /*tex Close the format file. */
+ zwclose(fmt_file);
+}
+
+/*tex
+
+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.
+
+*/
+
+#define too_small(A) do { \
+ wake_up_terminal(); \
+ wterm_cr(); \
+ fprintf(term_out,"---! Must increase the %s",(A)); \
+ goto BAD_FMT; \
+} while (0)
+
+/*tex
+
+ 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|.
+
+*/
+
+#define undump(A,B,C) do { \
+ undump_int(x); \
+ if (x<(A) || x>(B)) \
+ goto BAD_FMT; \
+ else \
+ (C) = x; \
+} while (0)
+
+#define format_debug(A,B) do { \
+ if (debug_format_file) { \
+ fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \
+ } \
+} while (0)
+
+#define undump_size(A,B,C,D) do { \
+ undump_int(x); \
+ if (x<(A)) \
+ goto BAD_FMT; \
+ if (x>(B)) \
+ too_small(C); \
+ else \
+ format_debug (C,x); \
+ (D) = x; \
+} while (0)
+
+boolean load_fmt_file(const char *fmtname)
+{
+ int j, k, x;
+ halfword p;
+ char *format_engine;
+ /*tex Undump constants for consistency check .*/
+ if (ini_version) {
+ libcfree(hash);
+ libcfree(eqtb);
+ libcfree(fixmem);
+ libcfree(varmem);
+ }
+ undump_int(x);
+ format_debug("format magic number", x);
+ if (x != 0x57325458) {
+ /*tex it's not a format file. */
+ goto BAD_FMT;
+ }
+ undump_int(x);
+ format_debug("format id", x);
+ if (x != FORMAT_ID) {
+ /*tex We have a |FORMAT_ID| mismatch. */
+ goto BAD_FMT;
+ }
+ undump_int(x);
+ format_debug("engine name size", x);
+ if ((x < 0) || (x > 256)) {
+ /*tex The format file is corrupt. */
+ goto BAD_FMT;
+ }
+ format_engine = xmalloc((unsigned) x);
+ undump_things(format_engine[0], x);
+ format_engine[x - 1] = 0;
+ if (strcmp(engine_name, format_engine)) {
+ wake_up_terminal();
+ wterm_cr();
+ fprintf(term_out, "---! %s was written by %s", fmtname, format_engine);
+ xfree(format_engine);
+ goto BAD_FMT;
+ }
+ xfree(format_engine);
+ undump_int(x);
+ format_debug("string pool checksum", x);
+ if (x != 0x57325458) {
+ wake_up_terminal();
+ wterm_cr();
+ fprintf(term_out, "---! %s was written by a different version",
+ fmtname);
+ goto BAD_FMT;
+ }
+ undump_int(x);
+ if (x != max_halfword)
+ goto BAD_FMT;
+ undump_int(hash_high);
+ if ((hash_high < 0) || (hash_high > sup_hash_extra))
+ goto BAD_FMT;
+ if (hash_extra < hash_high)
+ hash_extra = hash_high;
+ eqtb_top = eqtb_size + hash_extra;
+ if (hash_extra == 0)
+ hash_top = undefined_control_sequence;
+ else
+ hash_top = eqtb_top;
+ hash = xmallocarray(two_halves, (unsigned) (1 + hash_top));
+ memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1));
+ eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1));
+ set_eq_type(undefined_control_sequence, undefined_cs_cmd);
+ set_equiv(undefined_control_sequence, null);
+ set_eq_level(undefined_control_sequence, level_zero);
+ for (x = eqtb_size + 1; x <= eqtb_top; x++)
+ eqtb[x] = eqtb[undefined_control_sequence];
+ undump_int(x);
+ if (x != eqtb_size)
+ goto BAD_FMT;
+ undump_int(x);
+ if (x != hash_prime)
+ goto BAD_FMT;
+ /*tex Undump the string pool */
+ str_ptr = undump_string_pool();
+ /*tex Undump the dynamic memory */
+ undump_node_mem();
+ undump_int(temp_token_head);
+ undump_int(hold_token_head);
+ undump_int(omit_template);
+ undump_int(null_list);
+ undump_int(backup_head);
+ undump_int(garbage);
+ undump_int(fix_mem_min);
+ undump_int(fix_mem_max);
+ fixmem = xmallocarray(smemory_word, fix_mem_max + 1);
+ memset(voidcast(fixmem), 0, (fix_mem_max + 1) * sizeof(smemory_word));
+ undump_int(fix_mem_end);
+ undump_int(avail);
+ undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1);
+ undump_int(dyn_used);
+ /*tex Undump regions 1 to 6 of the table of equivalents |eqtb|. */
+ k = null_cs;
+ do {
+ undump_int(x);
+ if ((x < 1) || (k + x > eqtb_size + 1))
+ goto BAD_FMT;
+ undump_things(eqtb[k], x);
+ 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);
+ if (hash_high > 0) {
+ /*tex undump |hash_extra| part */
+ undump_things(eqtb[eqtb_size + 1], hash_high);
+ }
+ undump(hash_base, hash_top, par_loc);
+ par_token = cs_token_flag + par_loc;
+ undump(hash_base, hash_top, write_loc);
+ undump_math_codes();
+ undump_text_codes();
+ /*tex Undump the hash table */
+ undump_primitives();
+ 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);
+ undump_things(hash[hash_used + 1], undefined_control_sequence - 1 - hash_used);
+ if (debug_format_file)
+ print_csnames(hash_base, undefined_control_sequence - 1);
+ if (hash_high > 0) {
+ undump_things(hash[eqtb_size + 1], hash_high);
+ if (debug_format_file)
+ print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1));
+ }
+ undump_int(cs_count);
+ /*tex Undump the font information */
+ undump_int(x);
+ set_max_font_id(x);
+ for (k = 0; k <= max_font_id(); k++) {
+ /*tex Undump the array info for internal font number |k| */
+ undump_font(k);
+ }
+ undump_math_data();
+ /*tex Undump the hyphenation tables */
+ undump_language_data();
+ /*tex Undump a couple more things and the closing check word */
+ undump(batch_mode, error_stop_mode, interaction);
+ if (interactionoption != unspecified_mode)
+ interaction = interactionoption;
+ undump(0, str_ptr, format_ident);
+ undump(0, str_ptr, format_name);
+ undump_int(x);
+ if (x != 69069)
+ goto BAD_FMT;
+ /*tex Undump the lua bytecodes. */
+ undump_luac_registers();
+ prev_depth_par = ignore_depth;
+ return true;
+ BAD_FMT:
+ wake_up_terminal();
+ wterm_cr();
+ fprintf(term_out, "(Fatal format file error; I'm stymied)");
+ return false;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.h b/Build/source/texk/web2c/luatexdir/tex/dumpdata.h
new file mode 100644
index 00000000000..933dcfa1f0d
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.h
@@ -0,0 +1,114 @@
+/* dumpdata.h
+
+ Copyright 2009, 2012 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef DUMPDATA_H
+# define DUMPDATA_H
+
+extern str_number format_ident;
+extern str_number format_name; /* principal file name */
+extern FILE *fmt_file; /* for input or output of format information */
+
+extern void store_fmt_file(void);
+extern boolean load_fmt_file(const char *);
+
+/* (Un)dumping. These are called from the change file. */
+# define dump_things(base, len) \
+ do_zdump ((char *) &(base), sizeof (base), (int) (len), DUMP_FILE)
+# define undump_things(base, len) \
+ do_zundump ((char *) &(base), sizeof (base), (int) (len), DUMP_FILE)
+
+extern void do_zdump(char *, int, int, FILE *);
+extern void do_zundump(char *, int, int, FILE *);
+
+/* Like do_undump, but check each value against LOW and HIGH. The
+ slowdown isn't significant, and this improves the chances of
+ detecting incompatible format files. In fact, Knuth himself noted
+ this problem with Web2c some years ago, so it seems worth fixing. We
+ can't make this a subroutine because then we lose the type of BASE. */
+# define undump_checked_things(low, high, base, len) \
+ do { \
+ unsigned i; \
+ undump_things (base, len); \
+ for (i = 0; i < (len); i++) { \
+ if ((&(base))[i] < (low) || (&(base))[i] > (high)) { \
+ FATAL5 ("Item %u (=%ld) of .fmt array at %lx <%ld or >%ld", \
+ i, (unsigned long) (&(base))[i], (unsigned long) &(base), \
+ (unsigned long) low, (unsigned long) high); \
+ } \
+ } \
+ } while (0)
+
+/* Like undump_checked_things, but only check the upper value. We use
+ this when the base type is unsigned, and thus all the values will be
+ greater than zero by definition. */
+# define undump_upper_check_things(high, base, len) \
+ do { \
+ unsigned i; \
+ undump_things (base, len); \
+ for (i = 0; i < (len); i++) { \
+ if ((&(base))[i] > (high)) { \
+ FATAL4 ("Item %u (=%ld) of .fmt array at %lx >%ld", \
+ i, (unsigned long) (&(base))[i], (unsigned long) &(base), \
+ (unsigned long) high); \
+ } \
+ } \
+ } while (0)
+
+/* Use the above for all the other dumping and undumping. */
+# define generic_dump(x) dump_things (x, 1)
+# define generic_undump(x) undump_things (x, 1)
+
+# define dump_wd generic_dump
+# define dump_hh generic_dump
+# define dump_qqqq generic_dump
+# define undump_wd generic_undump
+# define undump_hh generic_undump
+# define undump_qqqq generic_undump
+
+/* `dump_int' is called with constant integers, so we put them into a
+ variable first. */
+# define dump_int(x) \
+ do \
+ { \
+ int x_val = (x); \
+ generic_dump (x_val); \
+ } \
+ while (0)
+
+/* web2c/regfix puts variables in the format file loading into
+ registers. Some compilers aren't willing to take addresses of such
+ variables. So we must kludge. */
+# if defined(REGFIX) || defined(WIN32)
+# define undump_int(x) \
+ do \
+ { \
+ int x_val; \
+ generic_undump (x_val); \
+ x = x_val; \
+ } \
+ while (0)
+# else
+# define undump_int generic_undump
+# endif /* not (REGFIX || WIN32) */
+
+
+
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.c b/Build/source/texk/web2c/luatexdir/tex/equivalents.c
new file mode 100644
index 00000000000..3d260d5c5ee
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.c
@@ -0,0 +1,1220 @@
+/*
+
+equivalents.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+halfword last_cs_name = null_cs;
+
+/*tex |eqtb[p]| has just been restored or retained. */
+
+static void diagnostic_trace(halfword p, const char *s)
+{
+ begin_diagnostic();
+ print_char('{');
+ tprint(s);
+ print_char(' ');
+ show_eqtb(p);
+ print_char('}');
+ end_diagnostic(false);
+}
+
+void show_eqtb_meaning(halfword n);
+
+/*tex
+
+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|:
+
+1) |eqtb[null_cs]| holds the current equivalent of the zero-length control
+sequence.
+
+2) |eqtb[hash_base..(glue_base-1)]| holds the current equivalents of single- and
+multiletter control sequences.
+
+3) |eqtb[glue_base..(local_base-1)]| holds the current equivalents of glue
+parameters like the current baselineskip.
+
+4) |eqtb[local_base..(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.
+
+5) |eqtb[int_base..(dimen_base-1)]| holds the current equivalents of fullword
+integer parameters like the current hyphenation penalty.
+
+6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents of fullword
+dimension parameters like the current hsize or amount of hanging indentation.
+
+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.
+
+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|.
+
+*/
+
+memory_word *eqtb;
+halfword eqtb_top;
+quarterword xeq_level[(eqtb_size + 1)];
+
+void initialize_equivalents(void)
+{
+ int k;
+ for (k = int_base; k <= eqtb_size; k++)
+ xeq_level[k] = level_one;
+}
+
+/*tex
+
+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 |save_record|. 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_value|, and it is interpreted in one of four
+ways:
+
+1) If |save_type(p)=restore_old_value|, then |save_value(p)| is a location in
+|eqtb| whose current value should be destroyed at the end of the current group
+and replaced by |save_word(p-1)| (|save_type(p-1)==saved_eqtb|). Furthermore if
+|save_value(p)>=int_base|, then |save_level(p)| should replace the corresponding
+entry in |xeq_level| (if |save_value(p)<int_base|, then the level is part of
+|save_word(p-1)|).
+
+2) If |save_type(p)=restore_zero|, then |save_value(p)| is a location in |eqtb|
+whose current value should be destroyed at the end of the current group, when it
+should be replaced by the current value of |eqtb[undefined_control_sequence]|.
+
+3) If |save_type(p)=insert_token|, then |save_value(p)| is a token that should be
+inserted into \TeX's input when the current group ends.
+
+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_value(p)| points to the level
+boundary word at the bottom of the entries for that group. Furthermore,
+|save_value(p-1)| contains the source line number at which the current level of
+grouping was entered, this field has itself a type: |save_type(p-1)==saved_line|.
+
+Besides this `official' use, various subroutines push temporary variables on the
+save stack when it is handy to do so. These all have an explicit |save_type|, and
+they are:
+
+|saved_adjust| signifies an adjustment is beging scanned,
+|saved_insert| an insertion is being scanned,
+|saved_disc| the \.{\\discretionary} sublist we are working on right now,
+|saved_boxtype| whether a \.{\\localbox} is \.{\\left} or \.{\\right},
+|saved_textdir| a text direction to be restored,
+|saved_eqno| diffentiates between \.{\\eqno} and \.{\\leqno},
+|saved_choices| the \.{\\mathchoices} sublist we are working on right now,
+|saved_math| and interrupted math list,
+|saved_boxcontext| the box context value,
+|saved_boxspec| the box \.{to} or \.{spread} specification,
+|saved_boxdir| the box \.{dir} specification,
+|saved_boxattr| the box \.{attr} specification,
+|saved_boxpack| the box \.{pack} specification.
+
+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|.
+
+*/
+
+save_record *save_stack;
+int save_ptr; /* first unused entry on the save stack */
+int max_save_stack; /* maximum usage of save stack */
+quarterword cur_level = level_one; /* current nesting level for groups */
+group_code cur_group = bottom_level; /* current group type */
+int cur_boundary; /* where the current level begins */
+
+/*tex
+
+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|.
+
+The following macro is used to test if there is room for up to eight 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.
+
+*/
+
+#define check_full_save_stack() do { \
+ if (save_ptr>max_save_stack) { \
+ max_save_stack=save_ptr; \
+ if (max_save_stack>save_size-8) \
+ overflow("save size",(unsigned)save_size); \
+ } \
+} while (0)
+
+/*tex
+
+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 six 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}' is being treated, the 100pt dimension is stored on
+|save_stack| just before |new_save_level| is called.
+
+*/
+
+void new_save_level(group_code c)
+{ /*tex We begin a new level of grouping. */
+ check_full_save_stack();
+ set_saved_record(0, saved_line, 0, line);
+ incr(save_ptr);
+ save_type(save_ptr) = level_boundary;
+ save_level(save_ptr) = cur_group;
+ save_value(save_ptr) = cur_boundary;
+ if (cur_level == max_quarterword) {
+ overflow("grouping levels", max_quarterword - min_quarterword);
+ }
+ /*tex We quit if |(cur_level+1)| is too big to be stored in |eqtb|. */
+ cur_boundary = save_ptr;
+ cur_group = c;
+ if (tracing_groups_par > 0)
+ group_trace(false);
+ incr(cur_level);
+ incr(save_ptr);
+}
+
+static const char *save_stack_type(int v)
+{
+ const char *s = "";
+ switch (save_type(v)) {
+ case restore_old_value: s = "restore_old_value"; break;
+ case restore_zero: s = "restore_zero"; break;
+ case insert_token: s = "insert_token"; break;
+ case level_boundary: s = "level_boundary"; break;
+ case saved_line: s = "saved_line"; break;
+ case saved_adjust: s = "saved_adjust"; break;
+ case saved_insert: s = "saved_insert"; break;
+ case saved_disc: s = "saved_disc"; break;
+ case saved_boxtype: s = "saved_boxtype"; break;
+ case saved_textdir: s = "saved_textdir"; break;
+ case saved_eqno: s = "saved_eqno"; break;
+ case saved_choices: s = "saved_choices"; break;
+ case saved_math: s = "saved_math"; break;
+ case saved_boxcontext: s = "saved_boxcontext"; break;
+ case saved_boxspec: s = "saved_boxspec"; break;
+ case saved_boxdir: s = "saved_boxdir"; break;
+ case saved_boxattr: s = "saved_boxattr"; break;
+ case saved_boxpack: s = "saved_boxpack"; break;
+ case saved_attrlist: s = "saved_attrlist"; break;
+ case saved_eqtb: s = "saved_eqtb"; break;
+ default: break;
+ }
+ return s;
+}
+
+void print_save_stack(void)
+{
+ int i;
+ begin_diagnostic();
+ selector = term_and_log;
+ print_ln();
+ for (i = (save_ptr - 1); i >= 0; i--) {
+ tprint("save_stack[");
+ if (i < 100)
+ print_char(' ');
+ if (i < 10)
+ print_char(' ');
+ print_int(i);
+ tprint("]: ");
+ tprint(save_stack_type(i));
+ switch (save_type(i)) {
+ case restore_old_value:
+ tprint(", ");
+ show_eqtb_meaning(save_value(i));
+ tprint("=");
+ if (save_value(i) >= int_base) {
+ print_int(save_word(i - 1).cint);
+ } else {
+ print_int(eq_type_field(save_word(i - 1)));
+ print_char(',');
+ print_int(equiv_field(save_word(i - 1)));
+ }
+ i--;
+ break;
+ case restore_zero:
+ tprint(", ");
+ show_eqtb_meaning(save_value(i));
+ break;
+ case insert_token:
+ tprint(", ");
+ {
+ halfword p = get_avail();
+ set_token_info(p, save_value(i));
+ show_token_list(p, null, 1);
+ free_avail(p);
+ }
+ break;
+ case level_boundary:
+ tprint(", old group=");
+ print_int(save_level(i));
+ tprint(", boundary = ");
+ print_int(save_value(i));
+ tprint(", line = ");
+ print_int(save_value(i - 1));
+ i--;
+ break;
+ case saved_adjust:
+ tprint(", ");
+ /*tex vadjust vs vadjust pre */
+ print_int(save_level(i));
+ break;
+ case saved_insert:
+ tprint(", ");
+ /*tex insert number */
+ print_int(save_value(i));
+ break;
+ case saved_boxtype:
+ /*tex \.{\\localleftbox} vs \.{\\localrightbox} */
+ tprint(", ");
+ print_int(save_value(i));
+ break;
+ case saved_eqno:
+ /*tex \.{\\eqno} vs \.{\\leqno} */
+ tprint(", ");
+ print_int(save_value(i));
+ break;
+ case saved_disc:
+ case saved_choices:
+ tprint(", ");
+ print_int(save_value(i));
+ break;
+ case saved_math:
+ tprint(", listptr=");
+ print_int(save_value(i));
+ break;
+ case saved_boxcontext:
+ tprint(", ");
+ print_int(save_value(i));
+ break;
+ case saved_boxspec:
+ tprint(", spec=");
+ print_int(save_level(i));
+ tprint(", dimen=");
+ print_int(save_value(i));
+ break;
+ case saved_textdir:
+ case saved_boxdir:
+ tprint(", ");
+ print_dir_text(save_value(i));
+ break;
+ case saved_boxattr:
+ case saved_boxpack:
+ case saved_attrlist:
+ tprint(", ");
+ print_int(save_value(i));
+ break;
+ case saved_line:
+ case saved_eqtb:
+ break;
+ default:
+ break;
+ }
+ print_ln();
+ }
+ end_diagnostic(true);
+}
+
+/*tex
+
+The \.{\\showgroups} command displays all currently active grouping levels.
+
+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.
+
+*/
+
+void show_save_groups(void)
+{
+ int p = nest_ptr;
+ int m;
+ save_pointer v = save_ptr;
+ quarterword l = cur_level;
+ group_code c = cur_group;
+ int a = 1; /* to keep track of alignments */
+ int i;
+ quarterword j;
+ const char *s = NULL;
+ save_ptr = cur_boundary;
+ decr(cur_level);
+ tprint_nl("");
+ print_ln();
+ while (1) {
+ tprint_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);
+ tprint(" (");
+ switch (cur_group) {
+ case simple_group:
+ incr(p);
+ goto FOUND2;
+ break;
+ 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)
+ tprint("align entry");
+ else
+ tprint_esc("cr");
+ if (p >= a)
+ p = p - a;
+ a = 0;
+ goto FOUND;
+ }
+ break;
+ case no_align_group:
+ incr(p);
+ a = -1;
+ tprint_esc("noalign");
+ goto FOUND2;
+ break;
+ case output_group:
+ tprint_esc("output");
+ goto FOUND;
+ break;
+ case math_group:
+ goto FOUND2;
+ break;
+ case disc_group:
+ tprint_esc("discretionary");
+ for (i = 1; i < 3; i++) {
+ if (i <= saved_value(-2)) {
+ tprint("{}");
+ }
+ }
+ goto FOUND2;
+ break;
+ case math_choice_group:
+ tprint_esc("mathchoice");
+ for (i = 1; i < 4; i++) {
+ /*tex A different offset because |-2==saved_textdir|: */
+ if (i <= saved_value(-3)) {
+ tprint("{}");
+ }
+ }
+ goto FOUND2;
+ break;
+ case insert_group:
+ if (saved_type(-1) == saved_adjust) {
+ tprint_esc("vadjust");
+ if (saved_level(-1) != 0)
+ tprint(" pre");
+ } else {
+ tprint_esc("insert");
+ print_int(saved_value(-1));
+ }
+ goto FOUND2;
+ break;
+ case vcenter_group:
+ s = "vcenter";
+ goto FOUND1;
+ break;
+ case semi_simple_group:
+ incr(p);
+ tprint_esc("begingroup");
+ goto FOUND;
+ break;
+ case math_shift_group:
+ if (m == mmode) {
+ print_char('$');
+ } else if (nest[p].mode_field == mmode) {
+ print_cmd_chr(eq_no_cmd, saved_value(-2));
+ goto FOUND;
+ }
+ print_char('$');
+ goto FOUND;
+ break;
+ case math_left_group:
+ if (subtype(nest[p + 1].eTeX_aux_field) == left_noad_side)
+ tprint_esc("left");
+ else
+ tprint_esc("middle");
+ goto FOUND;
+ break;
+ default:
+ confusion("showgroups");
+ break;
+ }
+ /*tex Show the box context */
+ i = saved_value(-5);
+ if (i != 0) {
+ if (i < box_flag) {
+ if (abs(nest[p].mode_field) == vmode)
+ j = hmove_cmd;
+ else
+ j = vmove_cmd;
+ if (i > 0)
+ print_cmd_chr(j, 0);
+ else
+ print_cmd_chr(j, 1);
+ print_scaled(abs(i));
+ tprint("pt");
+ } else if (i < ship_out_flag) {
+ if (i >= global_box_flag) {
+ tprint_esc("global");
+ i = i - (global_box_flag - box_flag);
+ }
+ tprint_esc("setbox");
+ print_int(i - box_flag);
+ print_char('=');
+ } else {
+ print_cmd_chr(leader_ship_cmd, i - (leader_flag - a_leaders));
+ }
+ }
+ FOUND1:
+ tprint_esc(s);
+ /*tex Show the box packaging info. The offsets may vary. */
+ {
+ int ii = -1;
+ while (saved_type(ii) != saved_boxspec)
+ ii--;
+ if (saved_value(ii) != 0) {
+ print_char(' ');
+ if (saved_level(ii) == exactly)
+ tprint("to");
+ else
+ tprint("spread");
+ print_scaled(saved_value(ii));
+ tprint("pt");
+ }
+ }
+ FOUND2:
+ print_char('{');
+ FOUND:
+ print_char(')');
+ decr(cur_level);
+ cur_group = save_level(save_ptr);
+ save_ptr = save_value(save_ptr);
+ }
+ DONE:
+ save_ptr = v;
+ cur_level = l;
+ cur_group = c;
+}
+
+/*tex
+
+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@>
+
+We don't need to destroy when an assignment has the same node:
+
+*/
+
+void eq_destroy(memory_word w)
+{
+ halfword q;
+ switch (eq_type_field(w)) {
+ case call_cmd:
+ case long_call_cmd:
+ case outer_call_cmd:
+ case long_outer_call_cmd:
+ delete_token_ref(equiv_field(w));
+ break;
+ case glue_ref_cmd:
+ flush_node(equiv_field(w));
+ break;
+ case shape_ref_cmd:
+ q = equiv_field(w);
+ if (q != null) {
+ /*tex
+ We need to free a \.{\\parshape} block. Such a block is
+ |2n+1| words long, where |n=vinfo(q)|. It happens in the
+ flush function.
+ */
+ flush_node(q);
+ }
+ break;
+ case box_ref_cmd:
+ flush_node_list(equiv_field(w));
+ break;
+ default:
+ break;
+ }
+}
+
+/*tex
+
+To save a value of |eqtb[p]| that was established at level |l|, we can use the
+following subroutine.
+
+*/
+void eq_save(halfword p, quarterword l)
+{
+ check_full_save_stack();
+ if (l == level_zero) {
+ save_type(save_ptr) = restore_zero;
+ } else {
+ save_word(save_ptr) = eqtb[p];
+ save_type(save_ptr) = saved_eqtb;
+ incr(save_ptr);
+ save_type(save_ptr) = restore_old_value;
+ }
+ save_level(save_ptr) = l;
+ save_value(save_ptr) = p;
+ incr(save_ptr);
+}
+
+/*tex
+
+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.
+
+*/
+
+void eq_define(halfword p, quarterword t, halfword e)
+{
+ boolean trace = tracing_assigns_par > 0;
+ if ((eq_type(p) == t) && (equiv(p) == e)) {
+ if (trace)
+ diagnostic_trace(p, "reassigning");
+ eq_destroy(eqtb[p]);
+ return;
+ }
+ if (trace)
+ diagnostic_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));
+ set_eq_level(p, cur_level);
+ set_eq_type(p, t);
+ set_equiv(p, e);
+ if (trace)
+ diagnostic_trace(p, "into");
+}
+
+/*tex
+
+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.
+
+*/
+
+void eq_word_define(halfword p, int w)
+{
+ boolean trace = tracing_assigns_par > 0;
+ if (eqtb[p].cint == w) {
+ if (trace)
+ diagnostic_trace(p, "reassigning");
+ return;
+ }
+ if (trace)
+ diagnostic_trace(p, "changing");
+ if (xeq_level[p] != cur_level) {
+ eq_save(p, xeq_level[p]);
+ xeq_level[p] = cur_level;
+ }
+ eqtb[p].cint = w;
+ if (trace)
+ diagnostic_trace(p, "into");
+}
+
+/*tex
+
+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|.
+
+*/
+
+void geq_define(halfword p, quarterword t, halfword e)
+{
+ boolean trace = tracing_assigns_par > 0;
+ if (trace)
+ diagnostic_trace(p, "globally changing");
+ eq_destroy(eqtb[p]);
+ set_eq_level(p, level_one);
+ set_eq_type(p, t);
+ set_equiv(p, e);
+ if (trace)
+ diagnostic_trace(p, "into");
+}
+
+void geq_word_define(halfword p, int w)
+{
+ boolean trace = tracing_assigns_par > 0;
+ if (trace)
+ diagnostic_trace(p, "globally changing");
+ eqtb[p].cint = w;
+ xeq_level[p] = level_one;
+ if (trace)
+ diagnostic_trace(p, "into");
+}
+
+/*tex
+
+Subroutine |save_for_after| puts a token on the stack for save-keeping.
+
+*/
+
+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_value(save_ptr) = t;
+ incr(save_ptr);
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+void unsave(void)
+{
+ halfword p;
+ quarterword l = level_one;
+ /*tex Variable |a| registers if we already have processed an \.{\\aftergroup}. */
+ boolean a = false;
+ unsave_math_codes(cur_level);
+ unsave_cat_codes(cat_code_table_par, cur_level);
+ unsave_text_codes(cur_level);
+ unsave_math_data(cur_level);
+ if (cur_level > level_one) {
+ boolean trace = tracing_restores_par > 0;
+ decr(cur_level);
+ /*tex Clear off top level from |save_stack|. */
+ while (true) {
+ decr(save_ptr);
+ if (save_type(save_ptr) == level_boundary)
+ break;
+ p = save_value(save_ptr);
+ if (save_type(save_ptr) == insert_token) {
+ reinsert_token(a, p);
+ /*tex always |true| as we are always in \ETEX\ now. */
+ a = true;
+ } else {
+ if (save_type(save_ptr) == restore_old_value) {
+ l = save_level(save_ptr);
+ decr(save_ptr);
+ } else {
+ save_word(save_ptr) = eqtb[undefined_control_sequence];
+ }
+ /*tex
+ Store |save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]|
+ holds a global value A global definition, which sets the
+ level to |level_one|, 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.
+ */
+ if (p < int_base || p > eqtb_size) {
+ if (eq_level(p) == level_one) {
+ /*tex Destroy the saved value: */
+ eq_destroy(save_word(save_ptr));
+ if (trace)
+ diagnostic_trace(p, "retaining");
+ } else {
+ /*tex Destroy the current value: */
+ eq_destroy(eqtb[p]);
+ /*tex Restore the saved value: */
+ eqtb[p] = save_word(save_ptr);
+ if (trace)
+ diagnostic_trace(p, "restoring");
+ }
+ } else if (xeq_level[p] != level_one) {
+ eqtb[p] = save_word(save_ptr);
+ xeq_level[p] = l;
+ if (trace)
+ diagnostic_trace(p, "restoring");
+ } else {
+ if (trace)
+ diagnostic_trace(p, "retaining");
+ }
+ }
+ }
+
+ if (tracing_groups_par > 0) {
+ group_trace(true);
+ }
+ if (grp_stack[in_open] == cur_boundary) {
+ /*tex Groups are possibly not properly nested with files. */
+ group_warning();
+ }
+ cur_group = save_level(save_ptr);
+ cur_boundary = save_value(save_ptr);
+ decr(save_ptr);
+ } else {
+ /*tex |unsave| is not used when |cur_group=bottom_level| */
+ confusion("curlevel");
+ }
+ attr_list_cache = cache_disabled;
+}
+
+/*tex
+
+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.
+
+The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| for
+magnification. If nonzero, this magnification should be used henceforth. We might
+drop magnifaction at some point.
+
+*/
+
+int mag_set = 0;
+
+void prepare_mag(void)
+{
+ if ((mag_set > 0) && (mag_par != mag_set)) {
+ print_err("Incompatible magnification (");
+ print_int(mag_par);
+ tprint(");");
+ tprint_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_par <= 0) || (mag_par > 32768)) {
+ print_err("Illegal magnification has been changed to 1000");
+ help1(
+ "The magnification ratio must be between 1 and 32768."
+ );
+ int_error(mag_par);
+ geq_word_define(int_base + mag_code, 1000);
+ }
+ if ((mag_set == 0) && (mag_par != mag_set)) {
+ if (mag_par != 1000)
+ one_true_inch = xn_over_d(one_hundred_inch, 10, mag_par);
+ else
+ one_true_inch = one_inch;
+ }
+ mag_set = mag_par;
+}
+
+/*tex
+
+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.
+
+In general, |cur_cmd| is the current command as set by |get_next|, while
+|cur_chr| is the operand of the current command. The control sequence found here
+is registsred in |cur_cs| and is zero if none found. The |cur_tok| variable
+contains the packed representative of |cur_cmd| and |cur_chr| and like the other
+ones is global.
+
+*/
+
+int cur_cmd;
+halfword cur_chr;
+halfword cur_cs;
+halfword cur_tok;
+
+/*tex
+
+Here is a procedure that displays the current command. The variable |n| holds the
+level of \.{\\if...\\fi} nesting and |l| the line where \.{\\if} started.
+
+*/
+
+void show_cur_cmd_chr(void)
+{
+ int n, l;
+ halfword p;
+ begin_diagnostic();
+ tprint_nl("{");
+ if (mode_par != shown_mode) {
+ print_mode(mode_par);
+ tprint(": ");
+ shown_mode = mode_par;
+ }
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ if (tracing_ifs_par > 0) {
+ if (cur_cmd >= if_test_cmd) {
+ if (cur_cmd <= fi_or_else_cmd) {
+ tprint(": ");
+ if (cur_cmd == fi_or_else_cmd) {
+ print_cmd_chr(if_test_cmd, cur_if);
+ print_char(' ');
+ n = 0;
+ l = if_line;
+ } else {
+ n = 1;
+ l = line;
+ }
+ p = cond_ptr;
+ while (p != null) {
+ incr(n);
+ p = vlink(p);
+ }
+ tprint("(level ");
+ print_int(n);
+ print_char(')');
+ print_if_line(l);
+ }
+ }
+ }
+ print_char('}');
+ end_diagnostic(false);
+}
+
+/*tex
+
+Here is a procedure that displays the contents of |eqtb[n]| symbolically.
+
+*/
+
+void show_eqtb(halfword n)
+{
+ if (n < null_cs) {
+ /*tex
+ This can't happen in a pure \TEX\ run, but careless usage of tokens
+ at the \LUA\ end can make you end up here.
+ */
+ tprint("? bad token, case 1: ");
+ print_int(n);
+ } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) {
+ /*tex
+ This routine show the current meaning of |eqtb| entry |n| in region 1 or 2.
+ Similar routines for the other regions will appear below.
+ */
+ sprint_cs(n);
+ print_char('=');
+ print_cmd_chr(eq_type(n), equiv(n));
+ if (eq_type(n) >= call_cmd) {
+ print_char(':');
+ show_token_list(token_link(equiv(n)), null, 32);
+ }
+ } else if (n < local_base) {
+ /*tex
+ Here we show equivalent |n| in region 3. All glue parameters and registers
+ are initially `\.{0pt plus0pt minus0pt}'.
+ */
+ if (n < skip_base) {
+ if (n < glue_base + thin_mu_skip_code)
+ print_cmd_chr(assign_glue_cmd, n);
+ else
+ print_cmd_chr(assign_mu_glue_cmd, n);
+ 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) {
+ tprint_esc("skip");
+ print_int(n - skip_base);
+ print_char('=');
+ print_spec(equiv(n), "pt");
+ } else {
+ tprint_esc("muskip");
+ print_int(n - mu_skip_base);
+ print_char('=');
+ print_spec(equiv(n), "mu");
+ }
+
+ } else if (n < int_base) {
+ /*tex
+ We're now at equivalent |n| in region 4. First 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.
+ */
+ if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) {
+ if (n == par_shape_loc)
+ print_cmd_chr(set_tex_shape_cmd, n);
+ else
+ print_cmd_chr(set_etex_shape_cmd, 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)
+ tprint_esc("ETC.");
+ } else {
+ print_int(vinfo(par_shape_par_ptr + 1));
+ }
+ } else if (n < toks_base) {
+ print_cmd_chr(assign_toks_cmd, n);
+ print_char('=');
+ if (equiv(n) != null)
+ show_token_list(token_link(equiv(n)), null, 32);
+ } else if (n < box_base) {
+ tprint_esc("toks");
+ print_int(n - toks_base);
+ print_char('=');
+ if (equiv(n) != null)
+ show_token_list(token_link(equiv(n)), null, 32);
+ } else if (n < cur_font_loc) {
+ tprint_esc("box");
+ print_int(n - box_base);
+ print_char('=');
+ if (equiv(n) == null) {
+ tprint("void");
+ } else {
+ depth_threshold = 0;
+ breadth_max = 1;
+ show_node_list(equiv(n));
+ }
+ } else if (n == cur_font_loc) {
+ /*tex
+ Let's show the font identifier in |eqtb[n]|, that's
+ |font_id_text(equiv(n))|
+ */
+ tprint("current font");
+ print_char('=');
+ print_esc(hash[font_id_base + equiv(n)].rh);
+ }
+ } else if (n < dimen_base) {
+ /*tex Show equivalent |n| in region 5: */
+ if (n < dir_base) {
+ print_cmd_chr(assign_int_cmd, n);
+ print_char('=');
+ print_int(eqtb[n].cint);
+ } else if (n < count_base) {
+ print_cmd_chr(assign_dir_cmd, n);
+ print_char(' ');
+ print_dir_par(eqtb[n].cint);
+ } else if (n < attribute_base) {
+ tprint_esc("count");
+ print_int(n - count_base);
+ print_char('=');
+ print_int(eqtb[n].cint);
+ } else if (n < del_code_base) {
+ tprint_esc("attribute");
+ print_int(n - attribute_base);
+ print_char('=');
+ print_int(eqtb[n].cint);
+ }
+ } else if (n <= eqtb_size) {
+ /*tex Show equivalent |n| in region 6: */
+ if (n < scaled_base) {
+ print_cmd_chr(assign_dimen_cmd, n);
+ } else {
+ tprint_esc("dimen");
+ print_int(n - scaled_base);
+ }
+ print_char('=');
+ print_scaled(eqtb[n].cint);
+ tprint("pt");
+ } else {
+ /*tex This can't happen unless you messed up at the \LUA\ end. */
+ tprint("? bad token, case 2: ");
+ print_int(n);
+ }
+}
+
+void show_eqtb_meaning(halfword n)
+{
+ if (n < null_cs) {
+ /*tex This can't happen. */
+ print_char('?');
+ } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) {
+ /*tex
+ 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.
+ */
+ sprint_cs(n);
+ } else if (n < local_base) {
+ /*
+ Show equivalent |n| in region 3. All glue parameters and registers
+ are initially `\.{0pt plus0pt minus0pt}'.
+ */
+ if (n < skip_base) {
+ if (n < glue_base + thin_mu_skip_code)
+ print_cmd_chr(assign_glue_cmd, n);
+ else
+ print_cmd_chr(assign_mu_glue_cmd, n);
+ } else if (n < mu_skip_base) {
+ tprint_esc("skip");
+ print_int(n - skip_base);
+ } else {
+ tprint_esc("muskip");
+ print_int(n - mu_skip_base);
+ }
+ } else if (n < int_base) {
+ /*tex
+ Show equivalent |n| in region 4. 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.
+ */
+ if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) {
+ if (n == par_shape_loc)
+ print_cmd_chr(set_tex_shape_cmd, n);
+ else
+ print_cmd_chr(set_etex_shape_cmd, n);
+ } else if (n < toks_base) {
+ print_cmd_chr(assign_toks_cmd, n);
+ } else if (n < box_base) {
+ tprint_esc("toks");
+ print_int(n - toks_base);
+ } else if (n < cur_font_loc) {
+ tprint_esc("box");
+ print_int(n - box_base);
+ } else if (n == cur_font_loc) {
+ /*tex Show the font identifier in |eqtb[n]|. */
+ tprint("current font");
+ }
+ } else if (n < dimen_base) {
+ /*tex Show equivalent |n| in region 5. */
+ if (n < dir_base) {
+ print_cmd_chr(assign_int_cmd, n);
+ } else if (n < count_base) {
+ print_cmd_chr(assign_dir_cmd, n);
+ } else if (n < attribute_base) {
+ tprint_esc("count");
+ print_int(n - count_base);
+ } else if (n < del_code_base) {
+ tprint_esc("attribute");
+ print_int(n - attribute_base);
+ }
+ } else if (n <= eqtb_size) {
+ /*tex Show equivalent |n| in region 6. */
+ if (n < scaled_base) {
+ print_cmd_chr(assign_dimen_cmd, n);
+ } else {
+ tprint_esc("dimen");
+ print_int(n - scaled_base);
+ }
+ } else {
+ /*tex This can't happen either. */
+ print_char('?');
+ }
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.h b/Build/source/texk/web2c/luatexdir/tex/equivalents.h
new file mode 100644
index 00000000000..83117c64fa1
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.h
@@ -0,0 +1,916 @@
+/* equivalents.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef EQUIVALENTS_H
+# define EQUIVALENTS_H
+
+/*
+
+Like the preceding parameters, the following quantities can be changed at compile
+time to extend or reduce \TeX's capacity. But if they are changed, it is
+necessary to rerun the initialization program \.{INITEX} @.INITEX@> to generate
+new tables for the production \TeX\ program. One can't simply make helter-skelter
+changes to the following constants, since certain rather complex initialization
+numbers are computed from them. They are defined here using \.{WEB} macros,
+instead of being put into \PASCAL's |const| list, in order to emphasize this
+distinction.
+
+*/
+
+# define font_base 0 /* smallest internal font number; must not be less than |min_quarterword| */
+# define biggest_reg 65535 /* the largest allowed register number; must be |< max_quarterword| */
+# define number_regs 65536 /* |biggest_reg+1| */
+# define number_attrs 65536 /* total numbeer of attributes */
+# define biggest_char 1114111 /* 0x10FFFF, the largest allowed character number; must be |< max_halfword| */
+# define too_big_char (biggest_char+1) /* 1114112, |biggest_char+1| */
+# define special_char (biggest_char+2) /* 1114113, |biggest_char+2| */
+# define number_chars (biggest_char+3) /* 1114112, |biggest_char+1| */
+# define number_fonts (5535-font_base+1)
+# define biggest_lang 32767
+# define too_big_lang 32768
+# define text_size 0 /* size code for the largest size in a family */
+# define script_size 1 /* size code for the medium size in a family */
+# define script_script_size 2 /* size code for the smallest size in a family */
+
+/*
+
+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.
+
+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 a single entry for the `null csname' of length zero.
+In luatex, the active characters and and single-letter control sequence names are
+part of the next region.
+
+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).
+
+@ Region 3 of |eqtb| contains the |number_regs| \.{\\skip} registers, as well as
+the glue parameters defined here. It is important that the ``muskip'' parameters
+have larger numbers than the others.
+
+@ 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.
+
+@ 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..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.
+
+@ 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@>
+
+@ The final region of |eqtb| contains the dimension parameters defined here, and
+the |number_regs| \.{\\dimen} registers.
+
+*/
+
+# define null_cs 1 /* equivalent of \.{\\csname\\endcsname} */
+# define hash_base (null_cs+1) /* beginning of region 2, for the hash table */
+# define frozen_control_sequence (hash_base+hash_size) /* for error recovery */
+# define frozen_protection (frozen_control_sequence) /* inaccessible but definable */
+# define frozen_cr (frozen_control_sequence+1) /* permanent `\.{\\cr}' */
+# define frozen_end_group (frozen_control_sequence+2) /* permanent `\.{\\endgroup}' */
+# define frozen_right (frozen_control_sequence+3) /* permanent `\.{\\right}' */
+# define frozen_fi (frozen_control_sequence+4) /* permanent `\.{\\fi}' */
+# define frozen_end_template (frozen_control_sequence+5) /* permanent `\.{\\endtemplate}' */
+# define frozen_endv (frozen_control_sequence+6) /* second permanent `\.{\\endtemplate}' */
+# define frozen_relax (frozen_control_sequence+7) /* permanent `\.{\\relax}' */
+# define end_write (frozen_control_sequence+8) /* permanent `\.{\\endwrite}' */
+# define frozen_dont_expand (frozen_control_sequence+9 ) /* permanent `\.{\\notexpanded:}' */
+# define frozen_primitive (frozen_control_sequence+11 ) /* permanent `\.{\\primitive}' */
+# define frozen_special (frozen_control_sequence+12 ) /* permanent `\.{\\special}' */
+# define frozen_null_font (frozen_control_sequence+13 ) /* permanent `\.{\\nullfont}' */
+# define font_id_base (frozen_null_font-font_base ) /* begins table of |number_fonts| permanent font identifiers */
+# define undefined_control_sequence (frozen_null_font+number_fonts)
+# define glue_base (undefined_control_sequence+1) /* beginning of region 3 */
+
+# define line_skip_code 0 /* interline glue if |baseline_skip| is infeasible */
+# define baseline_skip_code 1 /* desired glue between baselines */
+# define par_skip_code 2 /* extra glue just above a paragraph */
+# define above_display_skip_code 3 /* extra glue just above displayed math */
+# define below_display_skip_code 4 /* extra glue just below displayed math */
+# define above_display_short_skip_code 5 /* glue above displayed math following short lines */
+# define below_display_short_skip_code 6 /* glue below displayed math following short lines */
+# define left_skip_code 7 /* glue at left of justified lines */
+# define right_skip_code 8 /* glue at right of justified lines */
+# define top_skip_code 9 /* glue at top of main pages */
+# define split_top_skip_code 10 /* glue at top of split pages */
+# define tab_skip_code 11 /* glue between aligned entries */
+# define space_skip_code 12 /* glue between words (if not |zero_glue|) */
+# define xspace_skip_code 13 /* glue after sentences (if not |zero_glue|) */
+# define par_fill_skip_code 14 /* glue on last line of paragraph */
+# define math_skip_code 15
+# define thin_mu_skip_code 16 /* thin space in math formula */
+# define med_mu_skip_code 17 /* medium space in math formula */
+# define thick_mu_skip_code 18 /* thick space in math formula */
+# define glue_pars 19 /* total number of glue parameters */
+
+# define skip_base (glue_base+glue_pars) /* table of |number_regs| ``skip'' registers */
+# define mu_skip_base (skip_base+number_regs) /* table of |number_regs| ``muskip'' registers */
+# define local_base (mu_skip_base+number_regs) /* beginning of region 4 */
+
+# define par_shape_loc (local_base) /* specifies paragraph shape */
+# define output_routine_loc (local_base+1) /* points to token list for \.{\\output} */
+# define every_par_loc (local_base+2) /* points to token list for \.{\\everypar} */
+# define every_math_loc (local_base+3) /* points to token list for \.{\\everymath} */
+# define every_display_loc (local_base+4) /* points to token list for \.{\\everydisplay} */
+# define every_hbox_loc (local_base+5) /* points to token list for \.{\\everyhbox} */
+# define every_vbox_loc (local_base+6) /* points to token list for \.{\\everyvbox} */
+# define every_job_loc (local_base+7) /* points to token list for \.{\\everyjob} */
+# define every_cr_loc (local_base+8) /* points to token list for \.{\\everycr} */
+# define err_help_loc (local_base+9) /* points to token list for \.{\\errhelp} */
+# define every_eof_loc (local_base+10) /* points to token list for \.{\\everyeof} */
+
+# define backend_toks_base (local_base+11)
+# define backend_toks_last (local_base+20)
+
+# define toks_base (local_base+21) /* table of |number_regs| token list registers */
+
+# define etex_pen_base (toks_base+number_regs) /* start of table of \eTeX's penalties */
+# define inter_line_penalties_loc (etex_pen_base) /* additional penalties between lines */
+# define club_penalties_loc (etex_pen_base+1) /* penalties for creating club lines */
+# define widow_penalties_loc (etex_pen_base+2) /* penalties for creating widow lines */
+# define display_widow_penalties_loc (etex_pen_base+3) /* ditto, just before a display */
+# define etex_pens (etex_pen_base+4) /* end of table of \eTeX's penalties */
+# define local_left_box_base (etex_pens)
+# define local_right_box_base (local_left_box_base+1)
+# define box_base (local_right_box_base+1) /* table of |number_regs| box registers */
+# define cur_font_loc (box_base+number_regs) /* internal font number outside math mode */
+# define internal_math_param_base (cur_font_loc+1 ) /* current math parameter data index */
+# define cat_code_base (internal_math_param_base+1) /* current category code data index */
+# define lc_code_base (cat_code_base+1) /* table of |number_chars| lowercase mappings */
+# define uc_code_base (lc_code_base+1) /* table of |number_chars| uppercase mappings */
+# define sf_code_base (uc_code_base+1) /* table of |number_chars| spacefactor mappings */
+# define math_code_base (sf_code_base+1) /* table of |number_chars| math mode mappings */
+# define int_base (math_code_base+1) /* beginning of region 5 */
+
+# define pretolerance_code 0 /* badness tolerance before hyphenation */
+# define tolerance_code 1 /* badness tolerance after hyphenation */
+# define line_penalty_code 2 /* added to the badness of every line */
+# define hyphen_penalty_code 3 /* penalty for break after discretionary hyphen */
+# define ex_hyphen_penalty_code 4 /* penalty for break after explicit hyphen */
+# define club_penalty_code 5 /* penalty for creating a club line */
+# define widow_penalty_code 6 /* penalty for creating a widow line */
+# define display_widow_penalty_code 7 /* ditto, just before a display */
+# define broken_penalty_code 8 /* penalty for breaking a page at a broken line */
+# define bin_op_penalty_code 9 /* penalty for breaking after a binary operation */
+# define rel_penalty_code 10 /* penalty for breaking after a relation */
+# define pre_display_penalty_code 11 /* penalty for breaking just before a displayed formula */
+# define post_display_penalty_code 12 /* penalty for breaking just after a displayed formula */
+# define inter_line_penalty_code 13 /* additional penalty between lines */
+# define double_hyphen_demerits_code 14 /* demerits for double hyphen break */
+# define final_hyphen_demerits_code 15 /* demerits for final hyphen break */
+# define adj_demerits_code 16 /* demerits for adjacent incompatible lines */
+# define mag_code 17 /* magnification ratio */
+# define delimiter_factor_code 18 /* ratio for variable-size delimiters */
+# define looseness_code 19 /* change in number of lines for a paragraph */
+# define time_code 20 /* current time of day */
+# define day_code 21 /* current day of the month */
+# define month_code 22 /* current month of the year */
+# define year_code 23 /* current year of our Lord */
+# define show_box_breadth_code 24 /* nodes per level in |show_box| */
+# define show_box_depth_code 25 /* maximum level in |show_box| */
+# define hbadness_code 26 /* hboxes exceeding this badness will be shown by |hpack| */
+# define vbadness_code 27 /* vboxes exceeding this badness will be shown by |vpack| */
+# define pausing_code 28 /* pause after each line is read from a file */
+# define tracing_online_code 29 /* show diagnostic output on terminal */
+# define tracing_macros_code 30 /* show macros as they are being expanded */
+# define tracing_stats_code 31 /* show memory usage if \TeX\ knows it */
+# define tracing_paragraphs_code 32 /* show line-break calculations */
+# define tracing_pages_code 33 /* show page-break calculations */
+# define tracing_output_code 34 /* show boxes when they are shipped out */
+# define tracing_lost_chars_code 35 /* show characters that aren't in the font */
+# define tracing_commands_code 36 /* show command codes at |big_switch| */
+# define tracing_restores_code 37 /* show equivalents when they are restored */
+# define uc_hyph_code 38 /* hyphenate words beginning with a capital letter */
+# define output_penalty_code 39 /* penalty found at current page break */
+# define max_dead_cycles_code 40 /* bound on consecutive dead cycles of output */
+# define hang_after_code 41 /* hanging indentation changes after this many lines */
+# define floating_penalty_code 42 /* penalty for insertions heldover after a split */
+# define global_defs_code 43 /* override \.{\\global} specifications */
+# define cur_fam_code 44 /* current family */
+# define escape_char_code 45 /* escape character for token output */
+# define default_hyphen_char_code 46 /* value of \.{\\hyphenchar} when a font is loaded */
+# define default_skew_char_code 47 /* value of \.{\\skewchar} when a font is loaded */
+# define end_line_char_code 48 /* character placed at the right end of the buffer */
+# define new_line_char_code 49 /* character that prints as |print_ln| */
+# define language_code 50 /* current hyphenation table */
+# define left_hyphen_min_code 51 /* minimum left hyphenation fragment size */
+# define right_hyphen_min_code 52 /* minimum right hyphenation fragment size */
+# define holding_inserts_code 53 /* do not remove insertion nodes from \.{\\box255} */
+# define error_context_lines_code 54 /* maximum intermediate line pairs shown */
+# define local_inter_line_penalty_code 55 /* local \.{\\interlinepenalty} */
+# define local_broken_penalty_code 56 /* local \.{\\brokenpenalty} */
+# define no_local_whatsits_code 57 /* counts local whatsits */
+# define no_local_dirs_code 58
+
+# define disable_lig_code 60
+# define disable_kern_code 61
+# define disable_space_code 62
+# define cat_code_table_code 63
+# define output_box_code 64
+# define cur_lang_code 65 /* current language id */
+# define ex_hyphen_char_code 66
+# define hyphenation_min_code 67 /* minimum word length */
+# define adjust_spacing_code 68 /* level of spacing adjusting */
+# define protrude_chars_code 69 /* protrude chars at left/right edge of paragraphs */
+# define output_mode_code 70 /* switch on PDF output if positive */
+# define draft_mode_code 71
+# define tracing_fonts_code 72
+# define tracing_assigns_code 73 /* show assignments */
+# define tracing_groups_code 74 /* show save/restore groups */
+# define tracing_ifs_code 75 /* show conditionals */
+# define tracing_scan_tokens_code 76 /* show pseudo file open and close */
+# define tracing_nesting_code 77 /* show incomplete groups and ifs within files */
+# define pre_display_direction_code 78 /* text direction preceding a display */
+# define last_line_fit_code 79 /* adjustment for last line of paragraph */
+# define saving_vdiscards_code 80 /* save items discarded from vlists */
+# define saving_hyph_codes_code 81 /* save hyphenation codes for languages */
+# define suppress_fontnotfound_error_code 82 /* suppress errors for missing fonts */
+# define suppress_long_error_code 83 /* suppress errors for missing fonts */
+# define suppress_ifcsname_error_code 84 /* suppress errors for failed \.{\\ifcsname} */
+# define suppress_outer_error_code 85 /* suppress errors for \.{\\outer} */
+# define suppress_mathpar_error_code 86 /* suppress errors for \.{\\par}} in math */
+# define math_eqno_gap_step_code 87 /* factor/1000 used for distance between eq and eqno */
+# define math_display_skip_mode_code 88
+# define math_scripts_mode_code 89
+# define math_nolimits_mode_code 90
+# define math_rules_mode_code 91
+# define math_rules_fam_code 92
+# define math_italics_mode_code 93
+# define synctex_code 94 /* is synctex file generation enabled ? */
+# define shape_mode_code 95
+# define first_valid_language_code 96
+# define hyphenation_bounds_code 97
+# define math_skip_mode_code 98
+# define math_pre_display_gap_factor_code 99
+# define hyphen_penalty_mode_code 100
+# define automatic_hyphen_penalty_code 101
+# define explicit_hyphen_penalty_code 102
+# define automatic_hyphen_mode_code 103
+# define compound_hyphen_mode_code 104
+# define break_after_dir_mode_code 105
+# define exception_penalty_code 106
+
+# define pre_bin_op_penalty_code 107
+# define pre_rel_penalty_code 108
+# define math_penalties_mode_code 109
+# define math_delimiters_mode_code 110
+# define math_script_box_mode_code 111
+# define math_script_char_mode_code 112
+# define math_rule_thickness_mode_code 113
+# define math_flatten_mode_code 114
+
+# define copy_lua_input_nodes_code 115
+# define suppress_primitive_error_code 116
+# define fixup_boxes_code 117
+# define glyph_dimensions_code 118
+
+# define math_option_code 119
+
+# define mathoption_int_base_code (math_option_code+1) /* one reserve */
+# define mathoption_int_last_code (mathoption_int_base_code+8)
+
+# define backend_int_base_code (mathoption_int_last_code+1)
+# define backend_int_last_code (backend_int_base_code+32) /* we need some 25 but take some slack */
+
+# define tex_int_pars (backend_int_last_code+1) /* total number of integer parameters */
+
+# define mathoption_int_base (int_base+mathoption_int_base_code)
+# define mathoption_int_last (int_base+mathoption_int_last_code)
+
+# define backend_int_base (int_base+backend_int_base_code)
+# define backend_int_last (int_base+backend_int_last_code)
+
+# define page_direction_code (tex_int_pars)
+# define body_direction_code (tex_int_pars+1)
+# define par_direction_code (tex_int_pars+2)
+# define text_direction_code (tex_int_pars+3)
+# define math_direction_code (tex_int_pars+4)
+# define line_direction_code (tex_int_pars+5) /* gets remapped so is no real register */
+
+# define int_pars (tex_int_pars+5) /* total number of integer parameters */
+
+# define dir_base (int_base+tex_int_pars)
+# define count_base (int_base+int_pars) /* |number_regs| user \.{\\count} registers */
+# define attribute_base (count_base+number_regs) /* |number_attrs| user \.{\\attribute} registers */
+# define del_code_base (attribute_base+number_attrs) /* |number_chars| delimiter code mappings */
+# define dimen_base (del_code_base+1) /* beginning of region 6 */
+
+# define par_indent_code 0 /* indentation of paragraphs */
+# define math_surround_code 1 /* space around math in text */
+# define line_skip_limit_code 2 /* threshold for |line_skip| instead of |baseline_skip| */
+# define hsize_code 3 /* line width in horizontal mode */
+# define vsize_code 4 /* page height in vertical mode */
+# define max_depth_code 5 /* maximum depth of boxes on main pages */
+# define split_max_depth_code 6 /* maximum depth of boxes on split pages */
+# define box_max_depth_code 7 /* maximum depth of explicit vboxes */
+# define hfuzz_code 8 /* tolerance for overfull hbox messages */
+# define vfuzz_code 9 /* tolerance for overfull vbox messages */
+# define delimiter_shortfall_code 10 /* maximum amount uncovered by variable delimiters */
+# define null_delimiter_space_code 11 /* blank space in null delimiters */
+# define script_space_code 12 /* extra space after subscript or superscript */
+# define pre_display_size_code 13 /* length of text preceding a display */
+# define display_width_code 14 /* length of line for displayed equation */
+# define display_indent_code 15 /* indentation of line for displayed equation */
+# define overfull_rule_code 16 /* width of rule that identifies overfull hboxes */
+# define hang_indent_code 17 /* amount of hanging indentation */
+# define h_offset_code 18 /* amount of horizontal offset when shipping pages out */
+# define v_offset_code 19 /* amount of vertical offset when shipping pages out */
+# define emergency_stretch_code 20 /* reduces badnesses on final pass of line-breaking */
+# define page_left_offset_code 21
+# define page_top_offset_code 22
+# define page_right_offset_code 23
+# define page_bottom_offset_code 24
+# define px_dimen_code 25
+# define page_width_code 26 /* page width of the output */
+# define page_height_code 27 /* page height of the output */
+
+# define backend_dimen_base (dimen_base+28)
+# define backend_dimen_last (dimen_base+37)
+
+# define dimen_pars (38) /* total number of dimension parameters */
+
+# define scaled_base (dimen_base+dimen_pars) /* table of |number_regs| user-defined \.{\\dimen} registers */
+# define eqtb_size (scaled_base+biggest_reg) /* largest subscript of |eqtb| */
+
+extern memory_word *eqtb;
+
+extern halfword eqtb_top; /* maximum of the |eqtb| */
+
+extern quarterword xeq_level[(eqtb_size + 1)];
+extern void initialize_equivalents(void);
+
+# define eq_level_field(A) (A).hh.b1
+# define eq_type_field(A) (A).hh.b0
+# define equiv_field(A) (A).hh.rh
+
+# define eq_level(A) eq_level_field(eqtb[(A)]) /* level of definition */
+# define eq_type(A) eq_type_field(eqtb[(A)]) /* command code for equivalent */
+# define equiv(A) equiv_field(eqtb[(A)]) /* equivalent value */
+
+# define set_eq_level(A,B) eq_level((A)) = (B)
+# define set_eq_type(A,B) eq_type((A)) = (B)
+# define set_equiv(A,B) equiv((A)) = (B)
+
+typedef struct save_record_ {
+ quarterword type_;
+ quarterword level_;
+ memory_word word_;
+} save_record;
+
+extern save_record *save_stack;
+extern int save_ptr; /* first unused entry on |save_stack| */
+extern int max_save_stack; /* maximum usage of save stack */
+extern quarterword cur_level; /* current nesting level for groups */
+extern group_code cur_group; /* current group type */
+extern int cur_boundary; /* where the current level begins */
+
+# define save_type(A) save_stack[(A)].type_ /* classifies a |save_stack| entry */
+# define save_level(A) save_stack[(A)].level_ /* saved level for regions 5 and 6, or group code */
+# define save_value(A) save_stack[(A)].word_.cint /* |eqtb| location or token or |save_stack| location */
+# define save_word(A) save_stack[(A)].word_ /* |eqtb| entry */
+
+/*
+
+We use the notation |saved(k)| to stand for an item that appears in location
+|save_ptr+k| of the save stack.
+
+*/
+
+# define saved_type(A) save_stack[save_ptr+(A)].type_
+# define saved_level(A) save_stack[save_ptr+(A)].level_
+# define saved_value(A) save_stack[save_ptr+(A)].word_.cint
+
+# define set_saved_record(A,B,C,D) do { \
+ saved_type(A) = (quarterword)(B); \
+ saved_level(A) = (quarterword)(C); \
+ saved_value(A) = (D); \
+ } while (0)
+
+# define restore_old_value 0 /* |save_type| when a value should be restored later */
+# define restore_zero 1 /* |save_type| when an undefined entry should be restored */
+# define insert_token 2 /* |save_type| when a token is being saved for later use */
+# define level_boundary 3 /* |save_type| corresponding to beginning of group */
+# define saved_line 4
+# define saved_adjust 5
+# define saved_insert 6
+# define saved_disc 7
+# define saved_boxtype 8
+# define saved_textdir 9
+# define saved_eqno 10
+# define saved_choices 11
+# define saved_math 12
+# define saved_boxcontext 13
+# define saved_boxspec 14
+# define saved_boxdir 15
+# define saved_boxattr 16
+# define saved_boxpack 17
+# define saved_attrlist 18
+# define saved_eqtb 19
+
+extern void print_save_stack(void);
+
+# define int_par(A) eqtb[int_base+(A)].cint
+# define dimen_par(A) eqtb[dimen_base+(A)].cint
+# define loc_par(A) equiv(local_base+(A))
+# define glue_par(A) equiv(glue_base+(A))
+
+typedef enum {
+ c_mathoption_old_code = 0, /* this one is stable */
+ /*
+ c_mathoption_umathcode_meaning_code,
+ */
+} math_option_codes ;
+
+# define mathoption_int_par(A) eqtb[mathoption_int_base+(A)].cint
+
+/* if nonzero, this magnification should be used henceforth */
+
+extern int mag_set;
+extern void prepare_mag(void);
+
+/*
+
+@ 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}.
+
+*/
+
+typedef enum {
+ bottom_level = 0, /* group code for the outside world */
+ simple_group, /* group code for local structure only */
+ hbox_group, /* code for `\.{\\hbox}\grp' */
+ adjusted_hbox_group, /* code for `\.{\\hbox}\grp' in vertical mode */
+ vbox_group, /* code for `\.{\\vbox}\grp' */
+ vtop_group, /* code for `\.{\\vtop}\grp' */
+ align_group, /* code for `\.{\\halign}\grp', `\.{\\valign}\grp' */
+ no_align_group, /* code for `\.{\\noalign}\grp' */
+ output_group, /* code for output routine */
+ math_group, /* code for, e.g., `\.{\char'136}\grp' */
+ disc_group, /* code for `\.{\\discretionary}\grp\grp\grp' */
+ insert_group, /* code for `\.{\\insert}\grp', `\.{\\vadjust}\grp' */
+ vcenter_group, /* code for `\.{\\vcenter}\grp' */
+ math_choice_group, /* code for `\.{\\mathchoice}\grp\grp\grp\grp' */
+ semi_simple_group, /* code for `\.{\\begingroup...\\endgroup}' */
+ math_shift_group, /* code for `\.{\$...\$}' */
+ math_left_group, /* code for `\.{\\left...\\right}' */
+ local_box_group, /* code for `\.{\\localleftbox...\\localrightbox}' */
+ split_off_group, /* box code for the top part of a \.{\\vsplit} */
+ split_keep_group, /* box code for the bottom part of a \.{\\vsplit} */
+ preamble_group, /* box code for the preamble processing in an alignment */
+ align_set_group, /* box code for the final item pass in an alignment */
+ fin_row_group /* box code for a provisory line in an alignment */
+} tex_group_codes;
+
+typedef enum {
+ new_graf_par_code = 0,
+ local_box_par_code,
+ hmode_par_par_code,
+ penalty_par_code,
+ math_par_code,
+} tex_par_codes ;
+
+typedef enum {
+ display_style = 0, /* |subtype| for \.{\\displaystyle} */
+ cramped_display_style, /* |subtype| for \.{\\crampeddisplaystyle} */
+ text_style, /* |subtype| for \.{\\textstyle} */
+ cramped_text_style, /* |subtype| for \.{\\crampedtextstyle} */
+ script_style, /* |subtype| for \.{\\scriptstyle} */
+ cramped_script_style, /* |subtype| for \.{\\crampedscriptstyle} */
+ script_script_style, /* |subtype| for \.{\\scriptscriptstyle} */
+ cramped_script_script_style, /* |subtype| for \.{\\crampedscriptscriptstyle} */
+} math_style_subtypes;
+
+typedef enum { /* this could move to directions.h */
+ dir_TLT = 0,
+ dir_TRT,
+ dir_LTL,
+ dir_RTT,
+} dir_codes;
+
+# define max_group_code local_box_group /* which is wrong, but is what the web says */
+
+extern int cur_cmd; /* current command set by |get_next| */
+extern halfword cur_chr; /* operand of current command */
+extern halfword cur_cs; /* control sequence found here, zero if none found */
+extern halfword cur_tok; /* packed representative of |cur_cmd| and |cur_chr| */
+
+extern void show_cur_cmd_chr(void);
+
+extern void new_save_level(group_code c); /* begin a new level of grouping */
+extern void eq_destroy(memory_word w); /* gets ready to forget |w| */
+extern void eq_save(halfword p, quarterword l); /* saves |eqtb[p]| */
+extern void eq_define(halfword p, quarterword t, halfword e); /* new data for |eqtb| */
+extern void eq_word_define(halfword p, int w);
+extern void geq_define(halfword p, quarterword t, halfword e); /* global |eq_define| */
+extern void geq_word_define(halfword p, int w); /* global |eq_word_define| */
+extern void save_for_after(halfword t);
+extern void unsave(void); /* pops the top level off the save stack */
+extern void show_save_groups(void);
+
+# define level_zero 0 /* level for undefined quantities */
+# define level_one 1 /* outermost level for defined quantities */
+
+extern void show_eqtb(halfword n);
+
+/* \csname \ifcsname */
+
+extern halfword last_cs_name;
+
+/*
+ The *_par macros expand to the variables that are (in most cases) also accessible
+ at the users end. Most are registers but some are in the (stack) lists.
+
+ More *_par will move here : there is no real need for thse macros but because there
+ were already a bunch and because they were defined all over the place we moved them
+ here.
+
+*/
+
+#define space_skip_par glue_par(space_skip_code)
+#define xspace_skip_par glue_par(xspace_skip_code)
+#define math_skip_par glue_par(math_skip_code)
+#define math_skip_mode int_par(math_skip_mode_code)
+
+#define pre_display_size_par dimen_par(pre_display_size_code)
+#define display_width_par dimen_par(display_width_code)
+#define display_indent_par dimen_par(display_indent_code)
+#define math_surround_par dimen_par(math_surround_code)
+
+#define display_skip_mode_par int_par(math_display_skip_mode_code)
+#define math_eqno_gap_step_par int_par(math_eqno_gap_step_code)
+
+#define body_direction_par int_par(body_direction_code)
+#define page_direction_par int_par(page_direction_code)
+#define par_direction_par int_par(par_direction_code)
+#define text_direction_par int_par(text_direction_code)
+#define math_direction_par int_par(math_direction_code)
+
+#define shape_mode_par int_par(shape_mode_code)
+#define hyphenation_bounds_par int_par(hyphenation_bounds_code)
+#define first_valid_language_par int_par(first_valid_language_code)
+
+#define hsize_par dimen_par(hsize_code)
+#define vsize_par dimen_par(vsize_code)
+#define hfuzz_par dimen_par(hfuzz_code)
+#define vfuzz_par dimen_par(vfuzz_code)
+#define hbadness_par int_par(hbadness_code)
+#define vbadness_par int_par(vbadness_code)
+
+#define baseline_skip_par glue_par(baseline_skip_code)
+#define par_indent_par dimen_par(par_indent_code)
+#define hang_indent_par dimen_par(hang_indent_code)
+#define hang_after_par int_par(hang_after_code)
+#define left_skip_par glue_par(left_skip_code)
+#define right_skip_par glue_par(right_skip_code)
+
+#define emergency_stretch_par dimen_par(emergency_stretch_code)
+#define pretolerance_par int_par(pretolerance_code)
+#define tolerance_par int_par(tolerance_code)
+#define looseness_par int_par(looseness_code)
+#define adjust_spacing_par int_par(adjust_spacing_code)
+#define adj_demerits_par int_par(adj_demerits_code)
+#define protrude_chars_par int_par(protrude_chars_code)
+#define line_penalty_par int_par(line_penalty_code)
+#define last_line_fit_par int_par(last_line_fit_code)
+#define double_hyphen_demerits_par int_par(double_hyphen_demerits_code)
+#define final_hyphen_demerits_par int_par(final_hyphen_demerits_code)
+#define inter_line_penalty_par int_par(inter_line_penalty_code)
+#define club_penalty_par int_par(club_penalty_code)
+#define broken_penalty_par int_par(broken_penalty_code)
+#define display_widow_penalty_par int_par(display_widow_penalty_code)
+#define widow_penalty_par int_par(widow_penalty_code)
+#define line_skip_limit_par dimen_par(line_skip_limit_code)
+
+#define delimiter_shortfall_par dimen_par(delimiter_shortfall_code)
+#define null_delimiter_space_par dimen_par(null_delimiter_space_code)
+#define script_space_par dimen_par(script_space_code)
+#define max_depth_par dimen_par(max_depth_code)
+#define box_max_depth_par dimen_par(box_max_depth_code)
+#define split_max_depth_par dimen_par(split_max_depth_code)
+#define overfull_rule_par dimen_par(overfull_rule_code)
+#define box_max_depth_par dimen_par(box_max_depth_code)
+#define top_skip_par glue_par(top_skip_code)
+#define split_top_skip_par glue_par(split_top_skip_code)
+
+#define cur_fam_par int_par(cur_fam_code)
+#define pre_display_direction_par int_par(pre_display_direction_code)
+#define pre_display_penalty_par int_par(pre_display_penalty_code)
+#define post_display_penalty_par int_par(post_display_penalty_code)
+
+#define cur_fam_par_in_range ((cur_fam_par>=0)&&(cur_fam_par<256))
+
+#define local_inter_line_penalty_par int_par(local_inter_line_penalty_code)
+#define local_broken_penalty_par int_par(local_broken_penalty_code)
+#define local_left_box_par equiv(local_left_box_base)
+#define local_right_box_par equiv(local_right_box_base)
+
+#define end_line_char_par int_par(end_line_char_code)
+#define new_line_char_par int_par(new_line_char_code)
+#define escape_char_par int_par(escape_char_code)
+
+#define end_line_char_inactive ((end_line_char_par < 0) || (end_line_char_par > 127))
+
+#define delimiter_factor_par int_par(delimiter_factor_code)
+#define bin_op_penalty_par int_par(bin_op_penalty_code)
+#define rel_penalty_par int_par(rel_penalty_code)
+#define pre_bin_op_penalty_par int_par(pre_bin_op_penalty_code)
+#define pre_rel_penalty_par int_par(pre_rel_penalty_code)
+#define math_penalties_mode_par int_par(math_penalties_mode_code)
+#define math_delimiters_mode_par int_par(math_delimiters_mode_code)
+#define math_script_box_mode_par int_par(math_script_box_mode_code)
+#define math_script_char_mode_par int_par(math_script_char_mode_code)
+#define math_rule_thickness_mode_par int_par(math_rule_thickness_mode_code)
+#define math_flatten_mode_par int_par(math_flatten_mode_code)
+#define null_delimiter_space_par dimen_par(null_delimiter_space_code)
+#define disable_lig_par int_par(disable_lig_code)
+#define disable_kern_par int_par(disable_kern_code)
+#define disable_space_par int_par(disable_space_code)
+#define math_scripts_mode_par int_par(math_scripts_mode_code)
+#define math_nolimits_mode_par int_par(math_nolimits_mode_code)
+#define math_italics_mode_par int_par(math_italics_mode_code)
+#define math_rules_mode_par int_par(math_rules_mode_code)
+#define math_rules_fam_par int_par(math_rules_fam_code)
+
+#define thin_mu_skip_par glue_par(thin_mu_skip_code)
+#define med_mu_skip_par glue_par(med_mu_skip_code)
+#define thick_mu_skip_par glue_par(thick_mu_skip_code)
+
+#define every_math_par equiv(every_math_loc)
+#define every_display_par equiv(every_display_loc)
+#define every_cr_par equiv(every_cr_loc)
+#define every_hbox_par equiv(every_hbox_loc)
+#define every_vbox_par equiv(every_vbox_loc)
+#define every_eof_par equiv(every_eof_loc)
+#define every_par_par equiv(every_par_loc)
+#define err_help_par equiv(err_help_loc)
+
+#define no_local_whatsits_par int_par(no_local_whatsits_code)
+#define no_local_dirs_par int_par(no_local_dirs_code)
+
+#define prev_depth_par cur_list.prev_depth_field
+#define prev_graf_par cur_list.pg_field
+#define tail_par cur_list.tail_field
+#define head_par cur_list.head_field
+#define mode_par cur_list.mode_field
+#define dirs_par cur_list.dirs_field
+#define space_factor_par cur_list.space_factor_field
+#define incompleat_noad_par cur_list.incompleat_noad_field
+#define mode_line_par cur_list.ml_field
+#define aux_par cur_list.eTeX_aux_field
+#define delim_par aux_par
+
+#define par_shape_par_ptr equiv(par_shape_loc)
+#define inter_line_penalties_par_ptr equiv(inter_line_penalties_loc)
+#define club_penalties_par_ptr equiv(club_penalties_loc)
+#define widow_penalties_par_ptr equiv(widow_penalties_loc)
+#define display_widow_penalties_par_ptr equiv(display_widow_penalties_loc)
+
+#define page_width_par dimen_par(page_width_code)
+#define page_height_par dimen_par(page_height_code)
+#define h_offset_par dimen_par(h_offset_code)
+#define v_offset_par dimen_par(v_offset_code)
+#define page_left_offset_par dimen_par(page_left_offset_code)
+#define page_top_offset_par dimen_par(page_top_offset_code)
+#define page_right_offset_par dimen_par(page_right_offset_code)
+#define page_bottom_offset_par dimen_par(page_bottom_offset_code)
+#define px_dimen_par dimen_par(px_dimen_code)
+
+#define max_dead_cycles_par int_par(max_dead_cycles_code)
+#define output_box_par int_par(output_box_code)
+#define holding_inserts_par int_par(holding_inserts_code)
+#define output_routine_par equiv(output_routine_loc)
+#define floating_penalty_par int_par(floating_penalty_code)
+
+#define mag_par int_par(mag_code)
+
+#define global_defs_par int_par(global_defs_code)
+#define cat_code_table_par int_par(cat_code_table_code)
+#define saving_vdiscards_par int_par(saving_vdiscards_code)
+
+#define tracing_output_par int_par(tracing_output_code)
+#define tracing_stats_par int_par(tracing_stats_code)
+#define tracing_online_par int_par(tracing_online_code)
+#define tracing_paragraphs_par int_par(tracing_paragraphs_code)
+#define tracing_nesting_par int_par(tracing_nesting_code)
+#define tracing_lost_chars_par int_par(tracing_lost_chars_code)
+#define tracing_scan_tokens_par int_par(tracing_scan_tokens_code)
+#define tracing_ifs_par int_par(tracing_ifs_code)
+#define tracing_commands_par int_par(tracing_commands_code)
+#define tracing_macros_par int_par(tracing_macros_code)
+#define tracing_assigns_par int_par(tracing_assigns_code)
+#define tracing_fonts_par int_par(tracing_fonts_code)
+#define tracing_pages_par int_par(tracing_pages_code)
+#define tracing_restores_par int_par(tracing_restores_code)
+#define tracing_groups_par int_par(tracing_groups_code)
+
+#define show_box_depth_par int_par(show_box_depth_code)
+#define show_box_breadth_par int_par(show_box_breadth_code)
+
+#define pausing_par int_par(pausing_code)
+
+#define suppress_outer_error_par int_par(suppress_outer_error_code)
+#define suppress_long_error_par int_par(suppress_long_error_code)
+#define suppress_mathpar_error_par int_par(suppress_mathpar_error_code)
+#define suppress_fontnotfound_error_par int_par(suppress_fontnotfound_error_code)
+#define suppress_ifcsname_error_par int_par(suppress_ifcsname_error_code)
+#define suppress_primitive_error_par int_par(suppress_primitive_error_code)
+#define error_context_lines_par int_par(error_context_lines_code)
+#define copy_lua_input_nodes_par int_par(copy_lua_input_nodes_code)
+
+#define math_old_par mathoption_int_par(c_mathoption_old_code)
+
+/*
+#define math_umathcode_meaning_par mathoption_int_par(c_mathoption_umathcode_meaning_code)
+*/
+
+#define math_pre_display_gap_factor_par int_par(math_pre_display_gap_factor_code)
+
+#define time_par int_par(time_code)
+#define day_par int_par(day_code)
+#define month_par int_par(month_code)
+#define year_par int_par(year_code)
+
+#define output_mode_par int_par(output_mode_code)
+#define draft_mode_par int_par(draft_mode_code)
+#define synctex_par int_par(synctex_code)
+
+#define language_par int_par(language_code)
+#define uc_hyph_par int_par(uc_hyph_code)
+#define left_hyphen_min_par int_par(left_hyphen_min_code)
+#define right_hyphen_min_par int_par(right_hyphen_min_code)
+#define ex_hyphen_char_par int_par(ex_hyphen_char_code)
+#define hyphen_penalty_par int_par(hyphen_penalty_code)
+#define ex_hyphen_penalty_par int_par(ex_hyphen_penalty_code)
+#define default_hyphen_char_par int_par(default_hyphen_char_code)
+#define default_skew_char_par int_par(default_skew_char_code)
+#define saving_hyph_codes_par int_par(saving_hyph_codes_code)
+
+#define hyphen_penalty_mode_par int_par(hyphen_penalty_mode_code)
+#define automatic_hyphen_penalty_par int_par(automatic_hyphen_penalty_code)
+#define explicit_hyphen_penalty_par int_par(explicit_hyphen_penalty_code)
+#define automatic_hyphen_mode_par int_par(automatic_hyphen_mode_code)
+#define compound_hyphen_mode_par int_par(compound_hyphen_mode_code)
+#define break_after_dir_mode_par int_par(break_after_dir_mode_code)
+#define exception_penalty_par int_par(exception_penalty_code)
+
+#define cur_lang_par int_par(cur_lang_code)
+#define cur_font_par equiv(cur_font_loc)
+
+#define fixup_boxes_par int_par(fixup_boxes_code)
+#define glyph_dimensions_par int_par(glyph_dimensions_code)
+
+/* */
+
+#define math_use_current_family_code 7
+
+/*
+ #define box(A) equiv(box_base+(A))
+ #define box(A) eqtb[box_base+(A)].hh.rh
+*/
+
+#define attribute(A) equiv(attribute_base+(A))
+#define box(A) equiv(box_base+(A))
+#define count(A) equiv(count_base+(A))
+#define dimen(A) equiv(scaled_base+(A))
+#define mu_skip(A) equiv(mu_skip_base+(A))
+#define skip(A) equiv(skip_base+(A))
+#define toks(A) equiv(toks_base+(A))
+
+#define get_tex_attribute_register(j) attribute(j)
+#define get_tex_box_register(j) box(j)
+#define get_tex_count_register(j) count(j)
+#define get_tex_dimen_register(j) dimen(j)
+#define get_tex_mu_skip_register(j) mu_skip(j)
+#define get_tex_skip_register(j) skip(j)
+
+#define font_id_text(A) cs_text(font_id_base+(A))
+
+#define end_template_token (cs_token_flag+frozen_end_template)
+#define end_write_token (cs_token_flag+end_write)
+
+/* for now here */
+
+#define xspace_skip_subtype (xspace_skip_code + 1)
+#define space_skip_subtype (space_skip_code + 1)
+
+/*
+
+hyphen_penalty_mode_par automatic_disc (-) explicit_disc (\-)
+---------------------------------------------------------------------------------
+0 (default) ex_hyphen_penalty_par ex_hyphen_penalty_par
+1 hyphen_penalty_par hyphen_penalty_par
+2 ex_hyphen_penalty_par hyphen_penalty_par
+3 hyphen_penalty_par ex_hyphen_penalty_par
+4 automatic_hyphen_penalty_par explicit_disc_penalty_par
+5 ex_hyphen_penalty_par explicit_disc_penalty_par
+6 hyphen_penalty_par explicit_disc_penalty_par
+7 automatic_hyphen_penalty_par ex_hyphen_penalty_par
+8 automatic_hyphen_penalty_par hyphen_penalty_par
+*/
+
+#define set_automatic_disc_penalty(n) \
+ switch (hyphen_penalty_mode_par) { \
+ case 0: \
+ case 2: \
+ case 5: \
+ /* we take ex_hyphen_penalty */ \
+ disc_penalty(n) = ex_hyphen_penalty_par; \
+ break; \
+ case 1: \
+ case 3: \
+ case 6: \
+ /* we take hyphen_penalty */ \
+ disc_penalty(n) = hyphen_penalty_par; \
+ break; \
+ case 4: \
+ case 7: \
+ case 8: \
+ /* we take automatic_hyphen_penalty */ \
+ disc_penalty(n) = automatic_hyphen_penalty_par; \
+ break; \
+ default: \
+ /* what we've done since the beginning */ \
+ disc_penalty(n) = ex_hyphen_penalty_par; \
+ break; \
+ }
+
+#define set_explicit_disc_penalty(n) \
+ switch (hyphen_penalty_mode_par) { \
+ case 0: \
+ case 3: \
+ case 7: \
+ /* we take ex_hyphen_penalty */ \
+ disc_penalty(n) = ex_hyphen_penalty_par; \
+ break; \
+ case 1: \
+ case 2: \
+ case 8: \
+ /* we take hyphen_penalty */ \
+ disc_penalty(n) = hyphen_penalty_par; \
+ break; \
+ case 4: \
+ case 5: \
+ case 6: \
+ /* we take automatic_hyphen_penalty */ \
+ disc_penalty(n) = explicit_hyphen_penalty_par; \
+ break; \
+ default: \
+ /* what we've done since the beginning */ \
+ disc_penalty(n) = ex_hyphen_penalty_par; \
+ break; \
+ }
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.c b/Build/source/texk/web2c/luatexdir/tex/errors.c
new file mode 100644
index 00000000000..ce80df99e7e
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/errors.c
@@ -0,0 +1,1055 @@
+/*
+
+errors.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+#define edit_var "TEXEDIT"
+
+/*tex
+
+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@>
+
+*/
+
+/*tex The current level of interaction: */
+
+int interaction;
+
+/*tex Set from the command line: */
+
+int interactionoption;
+
+char *last_error = NULL;
+char *last_lua_error = NULL;
+char *last_warning_tag = NULL;
+char *last_warning_str = NULL;
+char *last_error_context = NULL;
+
+int err_old_setting = 0 ;
+int in_error = 0 ;
+
+void set_last_error_context(void)
+{
+ str_number str;
+ int sel = selector;
+ int saved_new_line_char;
+ int saved_new_string_line;
+ selector = new_string;
+ saved_new_line_char = new_line_char_par;
+ saved_new_string_line = new_string_line;
+ new_line_char_par = 10;
+ new_string_line = 10;
+ show_context();
+ xfree(last_error_context);
+ str = make_string();
+ last_error_context = makecstring(str);
+ flush_str(str);
+ selector = sel;
+ new_line_char_par = saved_new_line_char;
+ new_string_line = saved_new_string_line;
+ return;
+}
+
+void flush_err(void)
+{
+ str_number s_error;
+ char *s = NULL;
+ int callback_id ;
+ if (in_error) {
+ selector = err_old_setting;
+ str_room(1);
+ s_error = make_string();
+ s = makecstring(s_error);
+ flush_str(s_error);
+ if (interaction == error_stop_mode) {
+ wake_up_terminal();
+ }
+ xfree(last_error);
+ last_error = (string) xmalloc((unsigned) (strlen(s) + 1));
+ strcpy(last_error,s);
+ callback_id = callback_defined(show_error_message_callback);
+ if (callback_id > 0) {
+ run_callback(callback_id, "->");
+ } else {
+ tprint(s);
+ }
+ in_error = 0 ;
+ }
+}
+
+void print_err(const char *s)
+{
+ int callback_id = callback_defined(show_error_message_callback);
+ if (interaction == error_stop_mode) {
+ wake_up_terminal();
+ }
+ if (callback_id > 0) {
+ err_old_setting = selector;
+ selector = new_string;
+ in_error = 1 ;
+ }
+ if (filelineerrorstylep) {
+ print_file_line();
+ } else {
+ tprint_nl("! ");
+ }
+ tprint(s);
+ if (callback_id <= 0) {
+ xfree(last_error);
+ last_error = (string) xmalloc((unsigned) (strlen(s) + 1));
+ strcpy(last_error,s);
+ }
+}
+
+/*tex
+
+\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
+
+|no_print| (when |interaction=batch_mode| and |log_file| not yet open);
+
+|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
+
+|log_only| (when |interaction=batch_mode| and |log_file| is open);
+
+|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
+
+*/
+
+void fixup_selector(boolean logopened)
+{
+ if (interaction == batch_mode)
+ selector = no_print;
+ else
+ selector = term_only;
+ if (logopened)
+ selector = selector + 2;
+}
+
+/*tex
+
+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.
+
+*/
+
+/*tex Is it safe for |error| to call |get_token|? */
+
+boolean deletions_allowed;
+
+/*tex Is it safe to do a \.{\\setbox} assignment? */
+
+boolean set_box_allowed;
+/*tex Has the source input been clean so far? */
+
+int history;
+
+/*tex The number of scrolled errors since the last paragraph ended. */
+
+int error_count;
+
+/*tex Should \TeX\ pause for instructions? */
+
+int interrupt;
+
+/*tex Should interrupts be observed? */
+
+boolean OK_to_interrupt;
+
+/*tex
+
+The value of |history| is initially |fatal_error_stop|, but it will be changed to
+|spotless| if \TeX\ survives the initialization process.
+
+*/
+
+void initialize_errors(void)
+{
+ if (interactionoption == unspecified_mode)
+ interaction = error_stop_mode;
+ else
+ interaction = interactionoption;
+ deletions_allowed = true;
+ set_box_allowed = true;
+ OK_to_interrupt = true;
+}
+
+/*tex
+
+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.
+
+Individual lines of help are recorded in the array |help_line|.
+
+*/
+
+const char *help_line[7];
+
+/*tex
+ Should the |err_help| list be shown?
+*/
+
+boolean use_err_help;
+
+/*tex
+
+The |jump_out| procedure just cuts across all active procedure levels and exits
+the program. It is used when there is no recovery from a particular error. The
+exit code can be overloaded.
+
+*/
+
+int defaultexitcode = 0;
+
+__attribute__ ((noreturn))
+void do_final_end(void)
+{
+ update_terminal();
+ ready_already = 0;
+ lua_close(Luas);
+ if ((history != spotless) && (history != warning_issued))
+ uexit(1);
+ else
+ uexit(defaultexitcode);
+}
+
+__attribute__ ((noreturn))
+void jump_out(void)
+{
+ close_files_and_terminate();
+ do_final_end();
+}
+
+/*tex
+
+Here is the function that calls the editor, if one is defined. This is loosely
+based on a similar function in kpathsea, but the calling convention is quite
+different.
+
+*/
+
+static const_string edit_value = EDITOR;
+
+#if defined(WIN32)
+
+static int Isspace (char c)
+{
+ return (c == ' ' || c == '\t');
+}
+
+#endif /* WIN32 */
+
+__attribute__ ((noreturn))
+static void luatex_calledit (int baseptr, int linenumber)
+{
+ char *temp, *command;
+ char *fullcmd = NULL; /* avoid compiler warning */
+ char c;
+ int sdone, ddone, i;
+ char *filename = makecstring(input_stack[base_ptr].name_field);
+ int fnlength = strlen(filename);
+#ifdef WIN32
+ char *fp, *ffp, *env, editorname[256], buffer[256];
+ int cnt = 0;
+ int dontchange = 0;
+#endif
+ sdone = ddone = 0;
+ /*tex
+ Close any open input files, since we're going to kill the job.
+ */
+ close_files_and_terminate();
+ /*tex
+ Replace the default with the value of the appropriate environment
+ variable or config file value, if it's set.
+ */
+ temp = kpse_var_value (edit_var);
+ if (temp != NULL)
+ edit_value = temp;
+ /*tex
+ Construct the command string. The `11' is the maximum length an
+ integer might be.
+ */
+ command = xmalloc (strlen (edit_value) + fnlength + 11);
+ /*tex
+ So we can construct it as we go.
+ */
+ temp = command;
+#ifdef WIN32
+ fp = editorname;
+ if ((isalpha(*edit_value) && *(edit_value + 1) == ':' && IS_DIR_SEP (*(edit_value + 2)))
+ || (*edit_value == '"' && isalpha(*(edit_value + 1))
+ && *(edit_value + 2) == ':' && IS_DIR_SEP (*(edit_value + 3)))) {
+ dontchange = 1;
+ }
+#endif
+ while ((c = *edit_value++) != 0) {
+ if (c == '%') {
+ switch (c = *edit_value++) {
+ case 'd':
+ if (ddone)
+ FATAL1 ("call_edit: `%%d' appears twice in editor command: `%s'", edit_value);
+ sprintf (temp, "%ld", (long int)linenumber);
+ while (*temp != '\0')
+ temp++;
+ ddone = 1;
+ break;
+ case 's':
+ if (sdone)
+ FATAL1 ("call_edit: `%%s' appears twice in editor command: `%s'", edit_value);
+ for (i =0; i < fnlength; i++)
+ *temp++ = filename[i];
+ sdone = 1;
+ break;
+ case '\0':
+ *temp++ = '%';
+ /*tex
+ Back up to the null to force termination.
+ */
+ edit_value--;
+ break;
+ default:
+ *temp++ = '%';
+ *temp++ = c;
+ break;
+ }
+ } else {
+#ifdef WIN32
+ if (dontchange) {
+ *temp++ = c;
+ } else if(Isspace(c) && cnt == 0) {
+ cnt++;
+ temp = command;
+ *temp++ = c;
+ *fp = '\0';
+ } else if(!Isspace(c) && cnt == 0) {
+ *fp++ = c;
+ } else {
+ *temp++ = c;
+ }
+#else
+ *temp++ = c;
+#endif
+ }
+ }
+ *temp = 0;
+#ifdef WIN32
+ if (dontchange == 0) {
+ if(editorname[0] == '.' || editorname[0] == '/' || editorname[0] == '\\') {
+ fprintf(stderr, "%s is not allowed to execute.\n", editorname);
+ do_final_end();
+ }
+ env = (char *)getenv("PATH");
+ if(SearchPath(env, editorname, ".exe", 256, buffer, &ffp)==0) {
+ if(SearchPath(env, editorname, ".bat", 256, buffer, &ffp)==0) {
+ fprintf(stderr, "I cannot find %s in the PATH.\n", editorname);
+ do_final_end();
+ }
+ }
+ fullcmd = (char *)xmalloc(strlen(buffer)+strlen(command)+5);
+ strcpy(fullcmd, "\"");
+ strcat(fullcmd, buffer);
+ strcat(fullcmd, "\"");
+ strcat(fullcmd, command);
+ }
+#else
+ fullcmd = command;
+#endif
+ /*tex Execute the command. */
+ if (system (fullcmd) != 0) {
+ fprintf (stderr, "! Trouble executing `%s'.\n", command);
+ }
+ /*tex Quit, since we found an error. */
+ do_final_end ();
+}
+
+/*tex
+
+ This completes the job of error reporting.
+
+*/
+
+void error(void)
+{
+ /*tex What the user types :*/
+ ASCII_code c;
+ int callback_id;
+ /*tex Used to save global variables when deleting tokens: */
+ int s1, s2, s3, s4;
+ int i;
+ flush_err();
+ if (history < error_message_issued)
+ history = error_message_issued;
+ callback_id = callback_defined(show_error_hook_callback);
+ if (callback_id > 0) {
+ set_last_error_context();
+ run_callback(callback_id, "->");
+ } else {
+ print_char('.');
+ show_context();
+ }
+ if (haltonerrorp) {
+ history = fatal_error_stop;
+ jump_out();
+ }
+ if (interaction == error_stop_mode) {
+ /*tex Get user's advice and |return|. */
+ while (1) {
+ CONTINUE:
+ /*tex
+ Original reports:
+
+ https://tex.stackexchange.com/questions/551313/
+ https://tug.org/pipermail/tex-live/2020-June/045876.html
+
+ This will probably be fixed by DEK in the 2021 tuneup in a different
+ way (so we'll have to remove or alter this change), but the interaction
+ sequence in the reports above causes a segmentation fault in web2c -
+ writing to the closed \write15 stream because we wrongly decrement
+ selector from 16 to 15 in term_input, due to the lack of this check in
+ recursive error() call.
+ */
+ if (interaction !=error_stop_mode)
+ return;
+ clear_for_error_prompt();
+ prompt_input("? ");
+ if (last == first)
+ return;
+ c = buffer[first];
+ if (c >= 'a')
+ c = c + 'A' - 'a';
+ /*tex
+ 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.
+ */
+ 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) {
+ /*tex
+ Delete |c-"0"| tokens and |goto continue|. We allow
+ deletion of up to 99 tokens at a time.
+ */
+ 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) {
+ /*tex One-level recursive call of |error| is possible. */
+ get_token();
+ 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 CONTINUE;
+ }
+ break;
+ case 'E':
+ if (base_ptr > 0) {
+ int callback_id = callback_defined(call_edit_callback);
+ if (callback_id>0) {
+ (void)run_callback(callback_id, "Sd->", makecstring(input_stack[base_ptr].name_field), line);
+ /*tex This should not be reached. */
+ jump_out();
+ } else {
+ tprint_nl("You want to edit file ");
+ print(input_stack[base_ptr].name_field);
+ tprint(" at line ");
+ print_int(line);
+ interaction = scroll_mode;
+ if (kpse_init) {
+ luatex_calledit(base_ptr, line);
+ } else {
+ /*tex This should not be reached. */
+ tprint_nl("There is no valid callback defined.");
+ jump_out();
+ }
+ }
+ }
+ break;
+ case 'H':
+ /*tex Print the help information and |goto continue| */
+ if (use_err_help) {
+ give_err_help();
+ } else {
+ if (help_line[0] == NULL) {
+ help2(
+ "Sorry, I don't know how to help in this situation.",
+ "Maybe you should try asking a human?"
+ );
+ }
+ i = 0;
+ while (help_line[i] != NULL)
+ tprint_nl(help_line[i++]);
+ 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 CONTINUE;
+ }
+ break;
+ case 'I':
+ /*tex
+
+ Introduce new material from the terminal and |return|. When
+ the following code is executed, |buffer[(first+1)..(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.
+
+ We enter a new syntactic level for terminal input:
+
+ */
+ begin_file_reading();
+ /*tex
+ Now |state=mid_line|, so an initial blank space will count as
+ a blank.
+ */
+ if (last > first + 1) {
+ iloc = first + 1;
+ buffer[first] = ' ';
+ } else {
+ prompt_input("insert>");
+ iloc = first;
+ }
+ first = last;
+ /*tex No |end_line_char| ends this line. */
+ ilimit = last - 1;
+ return;
+ break;
+ case 'Q':
+ case 'R':
+ case 'S':
+ /*tex
+
+ Change the interaction level and |return|. 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|.
+
+ */
+ error_count = 0;
+ interaction = batch_mode + c - 'Q';
+ tprint("OK, entering ");
+ switch (c) {
+ case 'Q':
+ tprint_esc("batchmode");
+ decr(selector);
+ break;
+ case 'R':
+ tprint_esc("nonstopmode");
+ break;
+ case 'S':
+ tprint_esc("scrollmode");
+ break;
+ }
+ tprint("...");
+ print_ln();
+ update_terminal();
+ return;
+ break;
+ case 'X':
+ interaction = scroll_mode;
+ jump_out();
+ break;
+ default:
+ break;
+ }
+ if (!use_err_help) {
+ /* Print the menu of available options */
+ tprint("Type <return> to proceed, S to scroll future error messages,");
+ tprint_nl("R to run without stopping, Q to run quietly,");
+ tprint_nl("I to insert something, ");
+ if (base_ptr > 0)
+ tprint("E to edit your file,");
+ if (deletions_allowed)
+ tprint_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
+ tprint_nl("H for help, X to quit.");
+ }
+ use_err_help = false;
+ }
+
+ }
+ incr(error_count);
+ if (error_count == 100) {
+ tprint_nl("(That makes 100 errors; please try again.)");
+ history = fatal_error_stop;
+ jump_out();
+ }
+ /*tex Put help message on the transcript file. */
+ if (interaction > batch_mode) {
+ /*tex Avoid terminal output: */
+ decr(selector);
+ }
+ if (use_err_help) {
+ print_ln();
+ give_err_help();
+ } else {
+ int i1 = 0;
+ while (help_line[i1] != NULL)
+ tprint_nl(help_line[i1++]);
+ }
+ print_ln();
+ if (interaction > batch_mode) {
+ /*tex Re-enable terminal output: */
+ incr(selector);
+ }
+ print_ln();
+}
+
+/*tex
+
+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:
+
+*/
+
+void int_error(int n)
+{
+ tprint(" (");
+ print_int(n);
+ print_char(')');
+ error();
+}
+
+/*tex
+
+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.
+
+*/
+
+void normalize_selector(void)
+{
+ if (log_opened_global)
+ selector = term_and_log;
+ else
+ selector = term_only;
+ if (job_name == 0)
+ open_log_file();
+ if (interaction == batch_mode)
+ decr(selector);
+}
+
+/*tex
+
+The following procedure prints \TeX's last words before dying.
+
+*/
+
+void succumb(void)
+{
+ if (interaction == error_stop_mode) {
+ /*tex No more interaction: */
+ interaction = scroll_mode;
+ }
+ if (log_opened_global) {
+ error();
+ }
+ history = fatal_error_stop;
+ /*tex Irrecoverable error: */
+ jump_out();
+}
+
+/*tex
+
+This prints |s|, and that's it.
+
+*/
+
+void fatal_error(const char *s)
+{
+ normalize_selector();
+ print_err("Emergency stop");
+ help1(s);
+ succumb();
+}
+
+/*tex
+
+Here is the most dreaded error message. We stop due to finiteness.
+
+*/
+
+void overflow(const char *s, unsigned int n)
+{
+ normalize_selector();
+ print_err("TeX capacity exceeded, sorry [");
+ tprint(s);
+ print_char('=');
+ print_int((int) n);
+ print_char(']');
+ if (varmem == NULL) {
+ print_err("Sorry, I ran out of memory.");
+ print_ln();
+ exit(EXIT_FAILURE);
+ }
+ help2(
+ "If you really absolutely need more capacity,",
+ "you can ask a wizard to enlarge me."
+ );
+ succumb();
+}
+
+/*tex
+
+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@>
+
+*/
+
+void confusion(const char *s)
+{ /* consistency check violated; |s| tells where */
+ normalize_selector();
+ if (history < error_message_issued) {
+ print_err("This can't happen (");
+ tprint(s);
+ print_char(')');
+ help1(
+ "I'm broken. Please show this to someone who can fix"
+ );
+ } else {
+ print_err("I can't go on meeting you like this");
+ 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();
+}
+
+/*tex
+
+Users occasionally want to interrupt \TeX\ while it's running. If the 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 debugger.
+@^system dependencies@> @^debugging@>
+
+*/
+
+void check_interrupt(void)
+{
+ if (interrupt != 0)
+ pause_for_instructions();
+}
+
+/*tex
+
+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.
+
+*/
+
+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");
+ help3(
+ "You rang?",
+ "Try to insert some instructions for me (e.g.,`I\\showlists'),",
+ "unless you just want to quit by typing `X'."
+ );
+ deletions_allowed = false;
+ error();
+ deletions_allowed = true;
+ interrupt = 0;
+ }
+}
+
+void tex_error(const char *msg, const char **hlp)
+{
+ print_err(msg);
+ if (hlp != NULL) {
+ int i;
+ for (i = 0; (hlp[i] != NULL && i <= 5); i++) {
+ help_line[i] = hlp[i];
+ }
+ help_line[i] = NULL;
+ } else {
+ help_line[0] = NULL;
+ }
+ error();
+}
+
+/*tex
+
+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.
+
+*/
+
+void back_error(void)
+{
+ OK_to_interrupt = false;
+ back_input();
+ OK_to_interrupt = true;
+ error();
+}
+
+/*tex
+
+ Back up one inserted token and call |error|.
+*/
+
+void ins_error(void)
+{
+ OK_to_interrupt = false;
+ back_input();
+ token_type = inserted;
+ OK_to_interrupt = true;
+ error();
+}
+
+/*tex
+
+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.
+If |tracing_lost_chars_par| (i.e. \.{\\tracinglostchar}) is greater than 2,
+it's considered as an error.
+
+*/
+
+void char_warning(internal_font_number f, int c)
+{
+ /*tex saved value of |tracing_online| */
+ int old_setting;
+ /* index to current digit; we assume that $0\L n<16^{22}$ */
+ int k;
+ if (tracing_lost_chars_par > 0) {
+ old_setting = tracing_online_par;
+ if (tracing_lost_chars_par > 1)
+ tracing_online_par = 1;
+ begin_diagnostic();
+ tprint_nl("Missing character: There is no ");
+ print(c);
+ tprint(" (U+");
+ k = 0;
+ if (c < 16)
+ print_char('0');
+ if (c < 256)
+ print_char('0');
+ if (c < 4096)
+ print_char('0');
+ do {
+ dig[k] = c % 16;
+ c = c / 16;
+ incr(k);
+ } while (c != 0);
+ print_the_digs((eight_bits) k);
+ tprint(") in font ");
+ print_font_name(f);
+ print_char('!');
+ end_diagnostic(false);
+ tracing_online_par = old_setting;
+ }
+ if (tracing_lost_chars_par > 2) {
+ error();
+ }
+}
+
+void wrapup_backend(void) {
+ ensure_output_state(static_pdf, ST_OMODE_FIX);
+ if (output_mode_used == OMODE_NONE) {
+ print_err(" ==> Fatal error occurred, no FMT file produced!");
+ } else {
+ backend_out_control[backend_control_finish_file](static_pdf,history == fatal_error_stop);
+ }
+}
+
+void normal_error(const char *t, const char *p)
+{
+ normalize_selector();
+ if (interaction == error_stop_mode) {
+ wake_up_terminal();
+ }
+ if (filelineerrorstylep) {
+ print_file_line();
+ } else {
+ tprint_nl("! ");
+ }
+ tprint("error: ");
+ if (cur_file_name) {
+ tprint(" (file ");
+ tprint(cur_file_name);
+ tprint(")");
+ }
+ if (t != NULL) {
+ tprint(" (");
+ tprint(t);
+ tprint(")");
+ }
+ tprint(": ");
+ if (p != NULL)
+ tprint(p);
+ history = fatal_error_stop;
+ wrapup_backend();
+ exit(EXIT_FAILURE);
+}
+
+void normal_warning(const char *t, const char *p)
+{
+ int report_id ;
+ if (strcmp(t,"lua") == 0) {
+ int saved_new_line_char;
+ saved_new_line_char = new_line_char_par;
+ new_line_char_par = 10;
+ report_id = callback_defined(show_lua_error_hook_callback);
+ if (report_id == 0) {
+ if (p != NULL)
+ tprint(p);
+ help2(
+ "The lua interpreter ran into a problem, so the",
+ "remainder of this lua chunk will be ignored."
+ );
+ } else {
+ (void) run_callback(report_id, "->");
+ }
+ error();
+ new_line_char_par = saved_new_line_char;
+ } else {
+ report_id = callback_defined(show_warning_message_callback);
+ if (report_id > 0) {
+ /*tex Free the last ones, */
+ xfree(last_warning_str);
+ xfree(last_warning_tag);
+ last_warning_str = (string) xmalloc(strlen(p) + 1);
+ last_warning_tag = (string) xmalloc(strlen(t) + 1);
+ strcpy(last_warning_str,p);
+ strcpy(last_warning_tag,t);
+ run_callback(report_id, "->");
+ } else {
+ print_ln();
+ tprint("warning ");
+ if (cur_file_name) {
+ tprint(" (file ");
+ tprint(cur_file_name);
+ tprint(")");
+ }
+ if (t != NULL) {
+ tprint(" (");
+ tprint(t);
+ tprint(")");
+ }
+ tprint(": ");
+ if (p != NULL)
+ tprint(p);
+ print_ln();
+ }
+ if (history == spotless)
+ history = warning_issued;
+ }
+}
+
+static char print_buf[PRINTF_BUF_SIZE];
+
+__attribute__ ((format(printf, 2,3)))
+void formatted_error(const char *t, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
+ normal_error(t,print_buf);
+ va_end(args);
+}
+
+__attribute__ ((format(printf, 2,3)))
+void formatted_warning(const char *t, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args);
+ normal_warning(t,print_buf);
+ va_end(args);
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.h b/Build/source/texk/web2c/luatexdir/tex/errors.h
new file mode 100644
index 00000000000..39fdf6b46e2
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/errors.h
@@ -0,0 +1,116 @@
+/* errors.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef ERRORS_H
+# define ERRORS_H
+
+/*
+The global variable |interaction| has four settings, representing increasing
+amounts of user interaction:
+*/
+
+typedef enum {
+ batch_mode = 0, /* omits all stops and omits terminal output */
+ nonstop_mode = 1, /* omits all stops */
+ scroll_mode = 2, /* omits error stops */
+ error_stop_mode = 3, /* stops at every opportunity to interact */
+ unspecified_mode = 4, /* extra value for command-line switch */
+} interaction_settings;
+
+extern int interaction; /* current level of interaction */
+extern int interactionoption; /* set from command line */
+
+extern int defaultexitcode; /* the exit code can be overloaded */
+
+extern void initialize_errors(void);
+
+extern char *last_error;
+extern char *last_lua_error;
+extern char *last_warning_tag;
+extern char *last_warning_str;
+extern char *last_error_context;
+
+extern void set_last_error_context(void);
+
+extern void print_err(const char *s);
+
+extern void fixup_selector(boolean log_opened);
+
+extern boolean deletions_allowed;
+extern boolean set_box_allowed;
+extern int history;
+extern int error_count;
+extern int interrupt;
+extern boolean OK_to_interrupt;
+
+typedef enum {
+ spotless = 0, /* |history| value when nothing has been amiss yet */
+ warning_issued = 1, /* |history| value when |begin_diagnostic| has been called */
+ error_message_issued = 2, /* |history| value when |error| has been called */
+ fatal_error_stop = 3, /* |history| value when termination was premature */
+} error_states;
+
+extern const char *help_line[7]; /* helps for the next |error| */
+extern boolean use_err_help; /* should the |err_help| list be shown? */
+
+/* these macros are just temporary, until everything is in C */
+
+# define hlp1(A) help_line[0]=A
+# define hlp2(A,B) help_line[1]=A; hlp1(B)
+# define hlp3(A,B,C) help_line[2]=A; hlp2(B,C)
+# define hlp4(A,B,C,D) help_line[3]=A; hlp3(B,C,D)
+# define hlp5(A,B,C,D,E) help_line[4]=A; hlp4(B,C,D,E)
+# define hlp6(A,B,C,D,E,F) help_line[5]=A; hlp5(B,C,D,E,F)
+
+# define help0() help_line[0]=NULL /* sometimes there might be no help */
+# define help1(A) do { help_line[1]=NULL; hlp1(A); } while (0)
+# define help2(A,B) do { help_line[2]=NULL; hlp2(B,A); } while (0)
+# define help3(A,B,C) do { help_line[3]=NULL; hlp3(C,B,A); } while (0)
+# define help4(A,B,C,D) do { help_line[4]=NULL; hlp4(D,C,B,A); } while (0)
+# define help5(A,B,C,D,E) do { help_line[5]=NULL; hlp5(E,D,C,B,A); } while (0)
+# define help6(A,B,C,D,E,F) do { help_line[6]=NULL; hlp6(F,E,D,C,B,A); } while (0)
+
+extern void do_final_end(void);
+extern void jump_out(void);
+extern void error(void);
+extern void int_error(int n);
+extern void normalize_selector(void);
+extern void succumb(void);
+extern void fatal_error(const char *s);
+extern void overflow(const char *s, unsigned int n);
+extern void confusion(const char *s);
+extern void check_interrupt(void);
+extern void pause_for_instructions(void);
+
+extern void tex_error(const char *msg, const char **hlp);
+extern void normal_error(const char *t, const char *p);
+extern void normal_warning(const char *t, const char *p);
+extern void formatted_error(const char *t, const char *fmt, ...);
+extern void formatted_warning(const char *t, const char *fmt, ...);
+
+extern void back_error(void);
+extern void ins_error(void);
+extern void flush_err(void);
+
+extern void char_warning(internal_font_number f, int c);
+
+extern void wrapup_backend(void);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.c b/Build/source/texk/web2c/luatexdir/tex/expand.c
new file mode 100644
index 00000000000..e85c8f3bdb6
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/expand.c
@@ -0,0 +1,967 @@
+/*
+
+Copyright 2009-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+ 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|.
+
+ Sometimes, recursive calls to the following |expand| routine may cause
+ exhaustion of the run-time calling stack, resulting in forced execution stops
+ by the operating system. To diminish the chance of this happening, a counter
+ is used to keep track of the recursion depth, in conjunction with a constant
+ called |expand_depth|.
+
+ Note that this does not catch all possible infinite recursion loops, just the
+ ones that exhaust the application calling stack. The actual maximum value of
+ |expand_depth| is outside of our control, but the initial setting of |100|
+ should be enough to prevent problems.
+
+*/
+
+static int expand_depth_count = 0;
+
+/*tex
+
+ 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.
+
+*/
+
+int is_in_csname = 0;
+
+void expand(void)
+{
+ /*tex token that is being ``expanded after'' */
+ halfword t;
+ /*tex for list manipulation */
+ halfword p;
+ /*tex for a local token list pointer */
+ halfword cur_ptr;
+ /*tex to save the global quantity |cur_val| */
+ int cv_backup;
+ /*tex to save |cur_val_level|, etc. */
+ int cvl_backup, radix_backup, co_backup;
+ /*tex to save |link(backup_head)| */
+ halfword backup_backup;
+ /*tex temporary storage of |scanner_status| */
+ int save_scanner_status;
+ incr(expand_depth_count);
+ if (expand_depth_count >= expand_depth)
+ overflow("expansion depth", (unsigned) expand_depth);
+ cv_backup = cur_val;
+ cvl_backup = cur_val_level;
+ radix_backup = radix;
+ co_backup = cur_order;
+ backup_backup = token_link(backup_head);
+ RESWITCH:
+ if (cur_cmd < call_cmd) {
+ /*tex Expand a nonmacro. */
+ if (tracing_commands_par > 1)
+ show_cur_cmd_chr();
+ switch (cur_cmd) {
+ case top_bot_mark_cmd:
+ /*tex Insert the appropriate mark text into the scanner. */
+ t = cur_chr % marks_code;
+ if (cur_chr >= marks_code)
+ scan_mark_num();
+ else
+ cur_val = 0;
+ switch (t) {
+ case first_mark_code:
+ cur_ptr = first_mark(cur_val);
+ break;
+ case bot_mark_code:
+ cur_ptr = bot_mark(cur_val);
+ break;
+ case split_first_mark_code:
+ cur_ptr = split_first_mark(cur_val);
+ break;
+ case split_bot_mark_code:
+ cur_ptr = split_bot_mark(cur_val);
+ break;
+ default:
+ cur_ptr = top_mark(cur_val);
+ break;
+ }
+ if (cur_ptr != null)
+ begin_token_list(cur_ptr, mark_text);
+ break;
+ case expand_after_cmd:
+ if (cur_chr == 0) {
+ /*tex
+
+ Expand the token after the next token. It takes only a
+ little shuffling to do what \TeX\ calls
+ \.{\\expandafter}.
+
+ */
+ get_token();
+ t = cur_tok;
+ get_token();
+ if (cur_cmd > max_command_cmd)
+ expand();
+ else
+ back_input();
+ cur_tok = t;
+ back_input();
+ } else {
+ /*tex
+
+ Negate a boolean conditional and |goto reswitch|. The
+ result of a boolean condition is reversed when the
+ conditional is preceded by \.{\\unless}.
+
+ */
+ get_token();
+ if ((cur_cmd == if_test_cmd) && (cur_chr != if_case_code)) {
+ cur_chr = cur_chr + unless_code;
+ goto RESWITCH;
+ }
+ print_err("You can't use `\\unless' before `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ print_char('\'');
+ help1("Continue, and I'll forget that it ever happened.");
+ back_error();
+ }
+ break;
+ case no_expand_cmd:
+ if (cur_chr == 0) {
+ /*tex
+
+ Suppress expansion of the next token. 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.
+
+ */
+ save_scanner_status = scanner_status;
+ scanner_status = normal;
+ get_token();
+ scanner_status = save_scanner_status;
+ t = cur_tok;
+ back_input();
+ /*tex Now |start| and |loc| point to the backed-up token |t|. */
+ if (t >= cs_token_flag) {
+ p = get_avail();
+ set_token_info(p, cs_token_flag + frozen_dont_expand);
+ set_token_link(p, iloc);
+ istart = p;
+ iloc = p;
+ }
+
+ } else {
+ /*tex
+
+ The \.{\\primitive} handling. If the primitive meaning of
+ the next token is an expandable command, it suffices to
+ replace the current token with the primitive one and
+ restart |expand|.
+
+ Otherwise, the token we just read has to be pushed back,
+ as well as a token matching the internal form of
+ \.{\\primitive}, that is sneaked in as an alternate form
+ of |ignore_spaces|.
+
+ An implementation problem surfaces: There really is no
+ |cur_cs| attached to the inserted primitive command, so
+ it is safer to set |cur_cs| to zero. |cur_tok| has a
+ similar problem. And for the non-expanded branch, simply
+ pushing back a token that matches the correct internal
+ command does not work, because that approach would not
+ survive roundtripping to a temporary file or even a token
+ list.
+
+ It would be smart to create |frozen_| versions of all the
+ primitives. Then, this problem would not happen, at the
+ expense of a few hundred extra control sequences.
+
+ */
+ save_scanner_status = scanner_status;
+ scanner_status = normal;
+ get_token();
+ scanner_status = save_scanner_status;
+ cur_cs = prim_lookup(cs_text(cur_cs));
+ if (cur_cs != undefined_primitive) {
+ t = get_prim_eq_type(cur_cs);
+ if (t > max_command_cmd) {
+ cur_cmd = t;
+ cur_chr = get_prim_equiv(cur_cs);
+ cur_tok = token_val(cur_cmd, cur_chr);
+ cur_cs = 0;
+ goto RESWITCH;
+ } else {
+ back_input();
+ /*tex Now |loc| and |start| point to a one-item list. */
+ p = get_avail();
+ set_token_info(p, cs_token_flag + frozen_primitive);
+ set_token_link(p, iloc);
+ iloc = p;
+ istart = p;
+ }
+ } else if (suppress_primitive_error_par == 0) {
+ print_err("Missing primitive name");
+ help2(
+ "The control sequence marked <to be read again> does not",
+ "represent any known primitive."
+ );
+ back_error();
+ }
+ }
+ break;
+ case cs_name_cmd:
+ /*tex Manufacture a control sequence name. */
+ if (cur_chr == 0) {
+ manufacture_csname(0);
+ } else if (cur_chr == 1) {
+ inject_last_tested_cs();
+ } else {
+ manufacture_csname(1);
+ }
+ break;
+ case convert_cmd:
+ conv_toks();
+ break;
+ case the_cmd:
+ ins_the_toks();
+ break;
+ case if_test_cmd:
+ /*tex An experiment. */
+ if (cur_chr == if_condition_code) {
+ break;
+ }
+ /*tex End of experiment. */
+ conditional();
+ break;
+ case fi_or_else_cmd:
+ /*tex
+
+ Terminate the current conditional and skip to \.{\\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.
+
+ */
+ if (tracing_ifs_par > 0)
+ if (tracing_commands_par <= 1)
+ show_cur_cmd_chr();
+ if (cur_chr > if_limit) {
+ if (if_limit == if_code) {
+ /*tex Condition is not yet evaluated. */
+ insert_relax();
+ } else {
+ print_err("Extra ");
+ print_cmd_chr(fi_or_else_cmd, cur_chr);
+ help1("I'm ignoring this; it doesn't match any \\if.");
+ error();
+ }
+ } else {
+ while (cur_chr != fi_code) {
+ /*tex Skip to \.{\\fi}. */
+ pass_text();
+ }
+ pop_condition_stack();
+ }
+ break;
+ case input_cmd:
+ /*tex Initiate or terminate input from a file */
+ if (cur_chr == 1)
+ force_eof = true;
+ else if (cur_chr == 2)
+ pseudo_start();
+ else if (cur_chr == 3) {
+ pseudo_start();
+ iname = 19;
+ } else if (name_in_progress)
+ insert_relax();
+ else
+ start_input();
+ break;
+ case lua_expandable_call_cmd:
+ if (cur_chr <= 0) {
+ normal_error("luacall", "invalid number");
+ } else {
+ str_number u = save_cur_string();
+ luacstrings = 0;
+ luafunctioncall(cur_chr);
+ restore_cur_string(u);
+ if (luacstrings > 0)
+ lua_string_start();
+ }
+ break;
+ case lua_local_call_cmd:
+ if (cur_chr <= 0) {
+ normal_error("luacall", "invalid number");
+ } else {
+ str_number u = save_cur_string();
+ luacstrings = 0;
+ lua_rawgeti(Luas, LUA_REGISTRYINDEX, cur_chr);
+ lua_call(Luas,0,0);
+ restore_cur_string(u);
+ if (luacstrings > 0)
+ lua_string_start();
+ }
+ break;
+ case variable_cmd:
+ do_variable();
+ break;
+ case feedback_cmd:
+ do_feedback();
+ break;
+ default:
+ /*tex Complain about an undefined macro */
+ print_err("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();
+ break;
+ }
+ } else if (cur_cmd < end_template_cmd) {
+ macro_call();
+ } else {
+ /*tex
+
+ Insert a token containing |frozen_endv|. 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.)
+
+ */
+ cur_tok = cs_token_flag + frozen_endv;
+ back_input();
+ }
+ cur_val = cv_backup;
+ cur_val_level = cvl_backup;
+ radix = radix_backup;
+ cur_order = co_backup;
+ set_token_link(backup_head, backup_backup);
+ decr(expand_depth_count);
+}
+
+void complain_missing_csname(void)
+{
+ print_err("Missing \\endcsname inserted");
+ help2(
+ "The control sequence marked <to be read again> should",
+ "not appear between \\csname and \\endcsname."
+ );
+ back_error();
+}
+
+void manufacture_csname(boolean use)
+{
+ halfword p, q, r;
+ lstring *ss;
+ r = get_avail();
+ p = r;
+ is_in_csname += 1;
+ do {
+ get_x_token();
+ if (cur_cs == 0)
+ store_new_token(cur_tok);
+ } while (cur_cs == 0);
+ if (cur_cmd != end_cs_name_cmd) {
+ /*tex Complain about missing \.{\\endcsname}. */
+ complain_missing_csname();
+ }
+ /*tex Look up the characters of list |r| in the hash table, and set |cur_cs|. */
+ ss = tokenlist_to_lstring(r, true);
+ is_in_csname -= 1;
+ if (use) {
+ if (ss->l > 0) {
+ cur_cs = string_lookup((char *) ss->s, ss->l);
+ } else {
+ cur_cs = null_cs;
+ }
+ last_cs_name = cur_cs ;
+ free_lstring(ss);
+ flush_list(r);
+ if (cur_cs == null_cs) {
+ /*tex skip */
+ } else if (eq_type(cur_cs) == undefined_cs_cmd) {
+ /*tex skip */
+ } else {
+ cur_tok = cur_cs + cs_token_flag;
+ back_input();
+ }
+ } else {
+ if (ss->l > 0) {
+ no_new_control_sequence = false;
+ cur_cs = string_lookup((char *) ss->s, ss->l);
+ no_new_control_sequence = true;
+ } else {
+ /*tex the list is empty */
+ cur_cs = null_cs;
+ }
+ last_cs_name = cur_cs ;
+ free_lstring(ss);
+ flush_list(r);
+ if (eq_type(cur_cs) == undefined_cs_cmd) {
+ /*tex The |save_stack| might change! */
+ eq_define(cur_cs, relax_cmd, too_big_char);
+ };
+ /*tex The control sequence will now match \.{\\relax} */
+ cur_tok = cur_cs + cs_token_flag;
+ back_input();
+ }
+}
+
+void inject_last_tested_cs(void)
+{
+ if (last_cs_name != null_cs) {
+ cur_cs = last_cs_name;
+ cur_tok = last_cs_name + cs_token_flag;
+ back_input();
+ }
+}
+
+/*tex
+
+ Sometimes the expansion looks too far ahead, so we want to insert a harmless
+ \.{\\relax} into the user's input.
+
+*/
+
+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;
+}
+
+
+/*tex
+
+ 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.
+
+*/
+
+void get_x_token(void)
+{
+ /*tex This code sets |cur_cmd|, |cur_chr|, |cur_tok|, and expands macros. */
+ RESTART:
+ get_next();
+ if (cur_cmd <= max_command_cmd)
+ goto DONE;
+ if (cur_cmd >= call_cmd) {
+ if (cur_cmd < end_template_cmd) {
+ macro_call();
+ } else {
+ cur_cs = frozen_endv;
+ cur_cmd = endv_cmd;
+ /*tex Now |cur_chr=null_list|. */
+ goto DONE;
+ }
+ } else {
+ expand();
+ }
+ goto RESTART;
+ DONE:
+ if (cur_cs == 0)
+ cur_tok = token_val(cur_cmd, cur_chr);
+ else
+ cur_tok = cs_token_flag + cur_cs;
+}
+
+/*tex
+
+ The |get_x_token| procedure is equivalent to two consecutive procedure calls:
+ |get_next; x_token|. It's |get_x_token| without the initial |get_next|.
+
+*/
+
+void x_token(void)
+{
+ while (cur_cmd > max_command_cmd) {
+ expand();
+ get_next();
+ }
+ if (cur_cs == 0)
+ cur_tok = token_val(cur_cmd, cur_chr);
+ else
+ cur_tok = cs_token_flag + cur_cs;
+}
+
+/*tex
+
+ 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 five global
+ arrays of pointers; we refer to the individual entries of these arrays by
+ symbolic macros |top_mark|, etc. The value of |top_mark(x)|, etc. is either
+ |null| or a pointer to the reference count of a token list.
+
+ The variable |biggest_used_mark| is an aid to try and keep the code somehwat
+ efficient without too much extra work: it registers the highest mark class
+ ever instantiated by the user, so the loops in |fire_up| and |vsplit| do not
+ have to traverse the full range |0..biggest_mark|.
+
+*/
+
+halfword top_marks_array[(biggest_mark + 1)];
+halfword first_marks_array[(biggest_mark + 1)];
+halfword bot_marks_array[(biggest_mark + 1)];
+halfword split_first_marks_array[(biggest_mark + 1)];
+halfword split_bot_marks_array[(biggest_mark + 1)];
+halfword biggest_used_mark;
+
+void initialize_marks(void)
+{
+ int i;
+ biggest_used_mark = 0;
+ for (i = 0; i <= biggest_mark; i++) {
+ top_mark(i) = null;
+ first_mark(i) = null;
+ bot_mark(i) = null;
+ split_first_mark(i) = null;
+ split_bot_mark(i) = null;
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex Governs the acceptance of \.{\\par}: */
+
+int long_state;
+
+/*tex
+
+ 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.)
+
+*/
+
+/*tex The arguments supplied to a macro: */
+
+halfword pstack[9];
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex This invokes a user-defined control sequence. */
+
+void macro_call(void)
+{
+ /*tex current node in the macro's token list */
+ halfword r;
+ /*tex current node in parameter token list being built */
+ halfword p = null;
+ /*tex new node being put into the token list */
+ halfword q;
+ /*tex backup pointer for parameter matching */
+ halfword s;
+ /*tex cycle pointer for backup recovery */
+ halfword t;
+ /*tex auxiliary pointers for backup recovery */
+ halfword u, v;
+ /*tex one step before the last |right_brace| token */
+ halfword rbrace_ptr = null;
+ /*tex the number of parameters scanned */
+ int n = 0;
+ /*tex unmatched left braces in current parameter */
+ halfword unbalance;
+ /*tex the number of tokens or groups (usually) */
+ halfword m = 0;
+ /*tex start of the token list */
+ halfword ref_count;
+ /*tex |scanner_status| upon entry */
+ int save_scanner_status = scanner_status;
+ /*tex |warning_index| upon entry */
+ halfword save_warning_index = warning_index;
+ /*tex character used in parameter */
+ int match_chr = 0;
+ warning_index = cur_cs;
+ ref_count = cur_chr;
+ r = token_link(ref_count);
+ if (tracing_macros_par > 0) {
+ /*tex Show the text of the macro being expanded. */
+ begin_diagnostic();
+ print_input_level();
+ // print_ln();
+ print_cs(warning_index);
+ token_show(ref_count);
+ end_diagnostic(false);
+ }
+ if (token_info(r) == protected_token)
+ r = token_link(r);
+ if (token_info(r) != end_match_token) {
+ /*tex
+
+ Scan the parameters and make |link(r)| point to the macro body; but
+ |return| if an illegal \.{\\par} is detected.
+
+ 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.
+
+ */
+ scanner_status = matching;
+ unbalance = 0;
+ long_state = eq_type(cur_cs);
+ if (long_state >= outer_call_cmd)
+ long_state = long_state - 2;
+ do {
+ set_token_link(temp_token_head, null);
+ if ((token_info(r) >= end_match_token)
+ || (token_info(r) < match_token)) {
+ s = null;
+ } else {
+ match_chr = token_info(r) - match_token;
+ s = token_link(r);
+ r = s;
+ p = temp_token_head;
+ m = 0;
+ }
+ /*tex
+
+ Scan a parameter until its delimiter string has been found; or,
+ if |s=null|, simply scan the delimiter string.
+
+ 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.
+
+ */
+ CONTINUE:
+ /*tex Set |cur_tok| to the next token of input: */
+ get_token();
+ if (cur_tok == token_info(r)) {
+ /*tex
+
+ Advance |r|; |goto found| if the parameter delimiter has been
+ fully matched, otherwise |goto continue|.
+
+ 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.
+
+ */
+ r = token_link(r);
+ if ((token_info(r) >= match_token)
+ && (token_info(r) <= end_match_token)) {
+ if (cur_tok < left_brace_limit)
+ decr(align_state);
+ goto FOUND;
+ } else {
+ goto CONTINUE;
+ }
+
+ }
+ /*tex
+
+ Contribute the recently matched tokens to the current parameter,
+ and |goto continue| if a partial match is still in effect; but
+ abort if |s=null|.
+
+ 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.
+
+ */
+ if (s != r) {
+ if (s == null) {
+ /*tex Report an improper use of the macro and abort. */
+ print_err("Use of ");
+ sprint_cs(warning_index);
+ tprint(" 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 EXIT;
+
+ } else {
+ t = s;
+ do {
+ store_new_token(token_info(t));
+ incr(m);
+ u = token_link(t);
+ v = s;
+ while (1) {
+ if (u == r) {
+ if (cur_tok != token_info(v)) {
+ goto DONE;
+ } else {
+ r = token_link(v);
+ goto CONTINUE;
+ }
+ }
+ if (token_info(u) != token_info(v))
+ goto DONE;
+ u = token_link(u);
+ v = token_link(v);
+ }
+ DONE:
+ t = token_link(t);
+ } while (t != r);
+ r = s;
+ /*tex At this point, no tokens are recently matched. */
+ }
+ }
+ if (cur_tok == par_token)
+ if (long_state != long_call_cmd)
+ if (!suppress_long_error_par) {
+ goto RUNAWAY;
+ }
+ if (cur_tok < right_brace_limit) {
+ if (cur_tok < left_brace_limit) {
+ /*tex Contribute an entire group to the current parameter. */
+ unbalance = 1;
+ while (1) {
+ fast_store_new_token(cur_tok);
+ get_token();
+ if (cur_tok == par_token) {
+ if (long_state != long_call_cmd) {
+ if (!suppress_long_error_par) {
+ goto RUNAWAY;
+
+ }
+ }
+ }
+ if (cur_tok < right_brace_limit) {
+ if (cur_tok < left_brace_limit) {
+ incr(unbalance);
+ } else {
+ decr(unbalance);
+ if (unbalance == 0)
+ break;
+ }
+ }
+ }
+ rbrace_ptr = p;
+ store_new_token(cur_tok);
+ } else {
+ /*tex Report an extra right brace and |goto continue|. */
+ back_input();
+ print_err("Argument of ");
+ sprint_cs(warning_index);
+ tprint(" 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_cmd;
+ cur_tok = par_token;
+ ins_error();
+ goto CONTINUE;
+ /*tex A white lie; the \.{\\par} won't always trigger a runaway. */
+ }
+ } else {
+ /*tex
+
+ Store the current token, but |goto continue| if it is a blank
+ space that would become an undelimited parameter.
+
+ */
+ if (cur_tok == space_token && token_info(r) <= end_match_token && token_info(r) >= match_token)
+ goto CONTINUE;
+ store_new_token(cur_tok);
+ }
+ incr(m);
+ if (token_info(r) > end_match_token)
+ goto CONTINUE;
+ if (token_info(r) < match_token)
+ goto CONTINUE;
+ FOUND:
+ if (s != null) {
+ /*
+
+ Tidy up the parameter just scanned, and tuck it away. 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.
+
+ */
+ if ((m == 1) && (token_info(p) < right_brace_limit)
+ && (p != temp_token_head)) {
+ set_token_link(rbrace_ptr, null);
+ free_avail(p);
+ p = token_link(temp_token_head);
+ pstack[n] = token_link(p);
+ free_avail(p);
+ } else {
+ pstack[n] = token_link(temp_token_head);
+ }
+ incr(n);
+ if (tracing_macros_par > 0) {
+ begin_diagnostic();
+ print_input_level();
+ // print_nl(match_chr);
+ print(match_chr);
+ print_int(n);
+ tprint("<-");
+ show_token_list(pstack[n - 1], null, 1000);
+ end_diagnostic(false);
+ }
+ }
+ /*tex
+
+ Now |info(r)| is a token whose command code is either |match| or
+ |end_match|.
+
+ */
+ } while (token_info(r) != end_match_token);
+ }
+ /*tex
+
+ Feed the macro body and its parameters to the scanner 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.
+
+ */
+ while ((istate == token_list) && (iloc == null) && (token_type != v_template)) {
+ /*tex Conserve stack space. */
+ end_token_list();
+ }
+ begin_token_list(ref_count, macro);
+ iname = warning_index;
+ iloc = token_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", (unsigned) param_size);
+ }
+ for (m = 0; m <= n - 1; m++)
+ param_stack[param_ptr + m] = pstack[m];
+ param_ptr = param_ptr + n;
+ }
+ goto EXIT;
+ RUNAWAY:
+ /*tex
+
+ Report a runaway argument and abort. If |long_state=outer_call|, a
+ runaway argument has already been reported.
+
+ */
+ if (long_state == call_cmd) {
+ runaway();
+ print_err("Paragraph ended before ");
+ sprint_cs(warning_index);
+ tprint(" 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] = token_link(temp_token_head);
+ align_state = align_state - unbalance;
+ for (m = 0; m <= n; m++)
+ flush_list(pstack[m]);
+ EXIT:
+ scanner_status = save_scanner_status;
+ warning_index = save_warning_index;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.h b/Build/source/texk/web2c/luatexdir/tex/expand.h
new file mode 100644
index 00000000000..18c0514e317
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/expand.h
@@ -0,0 +1,102 @@
+/* expand.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef EXPAND_H
+# define EXPAND_H
+
+extern boolean is_in_csname;
+extern void expand(void);
+extern void complain_missing_csname(void);
+extern void manufacture_csname(boolean use);
+extern void inject_last_tested_cs(void);
+extern void insert_relax(void);
+extern void get_x_token(void);
+extern void x_token(void);
+
+# define top_mark_code 0 /* the mark in effect at the previous page break */
+# define first_mark_code 1 /* the first mark between |top_mark| and |bot_mark| */
+# define bot_mark_code 2 /* the mark in effect at the current page break */
+# define split_first_mark_code 3
+ /* the first mark found by \.{\\vsplit} */
+# define split_bot_mark_code 4 /* the last mark found by \.{\\vsplit} */
+# define marks_code 5
+
+# define top_mark(A) top_marks_array[(A)]
+# define first_mark(A) first_marks_array[(A)]
+# define bot_mark(A) bot_marks_array[(A)]
+# define split_first_mark(A) split_first_marks_array[(A)]
+# define split_bot_mark(A) split_bot_marks_array[(A)]
+
+# define set_top_mark(A,B) top_mark(A)=(B)
+# define set_first_mark(A,B) first_mark(A)=(B)
+# define set_bot_mark(A,B) bot_mark(A)=(B)
+# define set_split_first_mark(A,B) split_first_mark(A)=(B)
+# define set_split_bot_mark(A,B) split_bot_mark(A)=(B)
+
+# define delete_top_mark(A) do { \
+ if (top_mark(A)!=null) \
+ delete_token_ref(top_mark(A)); \
+ top_mark(A)=null; \
+ } while (0)
+
+# define delete_bot_mark(A) do { \
+ if (bot_mark(A)!=null) \
+ delete_token_ref(bot_mark(A)); \
+ bot_mark(A)=null; \
+ } while (0)
+
+# define delete_first_mark(A) do { \
+ if (first_mark(A)!=null) \
+ delete_token_ref(first_mark(A)); \
+ first_mark(A)=null; \
+ } while (0)
+
+# define delete_split_first_mark(A) do { \
+ if (split_first_mark(A)!=null) \
+ delete_token_ref(split_first_mark(A)); \
+ split_first_mark(A)=null; \
+ } while (0)
+
+# define delete_split_bot_mark(A) do { \
+ if (split_bot_mark(A)!=null) \
+ delete_token_ref(split_bot_mark(A)); \
+ split_bot_mark(A)=null; \
+} while (0)
+
+# define clear_marks_code 1
+
+# define biggest_mark 65535
+
+extern halfword top_marks_array[(biggest_mark + 1)];
+extern halfword first_marks_array[(biggest_mark + 1)];
+extern halfword bot_marks_array[(biggest_mark + 1)];
+extern halfword split_first_marks_array[(biggest_mark + 1)];
+extern halfword split_bot_marks_array[(biggest_mark + 1)];
+extern halfword biggest_used_mark;
+
+
+extern void initialize_marks(void);
+
+extern int long_state;
+extern halfword pstack[9];
+extern void macro_call(void);
+
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.c b/Build/source/texk/web2c/luatexdir/tex/extensions.c
new file mode 100644
index 00000000000..19d4e444173
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/extensions.c
@@ -0,0 +1,1344 @@
+/*
+
+Copyright 2009-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation;
+either version 2 of the License, or (at your option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+#define mode mode_par
+#define tail tail_par
+#define head head_par
+#define dir_save dirs_par
+
+/*tex
+
+ 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}', and
+ `\.{\\special}' 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.
+
+ 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}, and \.{\\special}. 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.
+
+ 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.
+
+ 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|.
+
+*/
+
+alpha_file write_file[last_file_selector+1];
+halfword write_file_mode[last_file_selector+1];
+halfword write_file_translation[last_file_selector+1];
+boolean write_open[last_file_selector+1];
+
+scaled neg_wd;
+scaled pos_wd;
+scaled neg_ht;
+
+/*tex
+
+ The variable |write_loc| just introduced is used to provide an appropriate
+ error message in case of ``runaway'' write texts.
+
+*/
+
+/*tex The |eqtb| address of \.{\\write}: */
+
+halfword write_loc;
+
+/*tex
+
+ When an |extension| command occurs in |main_control|, in any mode, the
+ |do_extension| routine is called.
+
+*/
+
+int last_saved_image_index ;
+int last_saved_image_pages ;
+int last_saved_box_index ;
+scaledpos last_position = { 0, 0 };
+
+static void do_extension_dvi(int immediate)
+{
+ if (scan_keyword("literal")) {
+ new_whatsit(special_node);
+ write_stream(tail) = null;
+ scan_toks(false, true);
+ write_tokens(tail) = def_ref;
+ } else {
+ tex_error("unexpected use of \\dviextension",null);
+ }
+}
+
+static void do_extension_pdf(int immediate)
+{
+ int i;
+ if (scan_keyword("literal")) {
+ new_whatsit(pdf_literal_node);
+ if (scan_keyword("direct"))
+ set_pdf_literal_mode(tail, direct_always);
+ else if (scan_keyword("page"))
+ set_pdf_literal_mode(tail, direct_page);
+ else if (scan_keyword("text"))
+ set_pdf_literal_mode(tail, direct_text);
+ else if (scan_keyword("raw"))
+ set_pdf_literal_mode(tail, direct_raw);
+ else if (scan_keyword("origin"))
+ set_pdf_literal_mode(tail, set_origin);
+ else
+ set_pdf_literal_mode(tail, set_origin);
+ scan_toks(false, true);
+ set_pdf_literal_type(tail, normal);
+ set_pdf_literal_data(tail, def_ref);
+ } else if (scan_keyword("dest")) {
+ scan_pdfdest(static_pdf);
+ } else if (scan_keyword("annot")) {
+ scan_annot(static_pdf);
+ } else if (scan_keyword("save")) {
+ new_whatsit(pdf_save_node);
+ } else if (scan_keyword("restore")) {
+ new_whatsit(pdf_restore_node);
+ } else if (scan_keyword("linkstate")) {
+ new_whatsit(pdf_link_state_node);
+ scan_int();
+ pdf_link_state(tail) = cur_val;
+ } else if (scan_keyword("setmatrix")) {
+ new_whatsit(pdf_setmatrix_node);
+ scan_toks(false, true);
+ set_pdf_setmatrix_data(tail, def_ref);
+ } else if (scan_keyword("obj")) {
+ scan_obj(static_pdf);
+ if (immediate) {
+ if (obj_data_ptr(static_pdf, pdf_last_obj) == 0) {
+ /*tex This object has not been initialized yet. */
+ normal_error("pdf backend","\\pdfextension obj 'reserveobjnum' cannot be used with \\immediate");
+ }
+ pdf_write_obj(static_pdf, pdf_last_obj);
+ }
+ } else if (scan_keyword("refobj")) {
+ scan_refobj(static_pdf);
+ } else if (scan_keyword("colorstack")) {
+ scan_int();
+ if (cur_val >= colorstackused()) {
+ print_err("Unknown color stack number ");
+ print_int(cur_val);
+ help3(
+ "Allocate and initialize a color stack with \\pdfextension colorstackinit.",
+ "I'll use default color stack 0 here.",
+ "Proceed, with fingers crossed."
+ );
+ error();
+ cur_val = 0;
+ }
+ if (cur_val < 0) {
+ print_err("Invalid negative color stack number");
+ help2(
+ "I'll use default color stack 0 here.",
+ "Proceed, with fingers crossed."
+ );
+ error();
+ cur_val = 0;
+ }
+ 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;
+ if (i >= 0) {
+ new_whatsit(pdf_colorstack_node);
+ set_pdf_colorstack_stack(tail, cur_val);
+ set_pdf_colorstack_cmd(tail, i);
+ set_pdf_colorstack_data(tail, null);
+ if (i <= colorstack_data) {
+ scan_toks(false, true);
+ set_pdf_colorstack_data(tail, def_ref);
+ }
+ } else {
+ print_err("Color stack action is missing");
+ help3(
+ "The expected actions for \\pdfextension colorstack:",
+ " set, push, pop, current",
+ "I'll ignore the color stack command."
+ );
+ error();
+ }
+ } else if (scan_keyword("startlink")) {
+ scan_startlink(static_pdf);
+ } else if (scan_keyword("endlink")) {
+ if (abs(mode) == vmode)
+ normal_error("pdf backend", "\\pdfextension endlink cannot be used in vertical mode");
+ new_whatsit(pdf_end_link_node);
+ } else if (scan_keyword("startthread")) {
+ new_annot_whatsit(pdf_start_thread_node);
+ scan_thread_id();
+ } else if (scan_keyword("endthread")) {
+ new_whatsit(pdf_end_thread_node);
+ } else if (scan_keyword("thread")) {
+ new_annot_whatsit(pdf_thread_node);
+ scan_thread_id();
+ } else if (scan_keyword("outline")) {
+ scan_pdfoutline(static_pdf);
+ } else if (scan_keyword("glyphtounicode")) {
+ glyph_to_unicode();
+ } else if (scan_keyword("catalog")) {
+ scan_pdfcatalog(static_pdf);
+ } else if (scan_keyword("fontattr")) {
+ /*tex
+
+ The font attributes are simply initialized to zero now, this is
+ easier to deal with from C than an empty \TeX{} string, and surely
+ nobody will want to set font attr to a string containing a single
+ zero, as that would be nonsensical in the PDF output.
+
+ */
+ scan_font_ident();
+ i = cur_val;
+ if (i == null_font)
+ normal_error("pdf backend", "invalid font identifier");
+ scan_toks(false, true);
+ set_pdf_font_attr(i, tokens_to_string(def_ref));
+ if (str_length(pdf_font_attr(i)) == 0) {
+ /*tex From |tokens_to_string|. */
+ flush_str((str_ptr - 1));
+ set_pdf_font_attr(i, 0);
+ }
+ } else if (scan_keyword("mapfile")) {
+ scan_toks(false, true);
+ pdfmapfile(def_ref);
+ delete_token_ref(def_ref);
+ } else if (scan_keyword("mapline")) {
+ scan_toks(false, true);
+ pdfmapline(def_ref);
+ delete_token_ref(def_ref);
+ } else if (scan_keyword("includechars")) {
+ pdf_include_chars(static_pdf);
+ } else if (scan_keyword("info")) {
+ scan_toks(false, true);
+ pdf_info_toks = concat_tokens(pdf_info_toks, def_ref);
+ } else if (scan_keyword("names")) {
+ scan_toks(false, true);
+ pdf_names_toks = concat_tokens(pdf_names_toks, def_ref);
+ } else if (scan_keyword("trailer")) {
+ scan_toks(false, true);
+ pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref);
+ } else {
+ tex_error("unexpected use of \\pdfextension",null);
+ }
+}
+
+static void do_resource_dvi(int immediate, int code)
+{
+ /*tex Nothing is done here. */
+}
+
+static void do_resource_pdf(int immediate, int code)
+{
+ switch (code) {
+ case use_box_resource_code:
+ scan_pdfrefxform(static_pdf);
+ break;
+ case use_image_resource_code:
+ scan_pdfrefximage(static_pdf);
+ break;
+ case save_box_resource_code:
+ scan_pdfxform(static_pdf);
+ if (immediate) {
+ pdf_cur_form = last_saved_box_index;
+ ship_out(static_pdf, obj_xform_box(static_pdf, last_saved_box_index), SHIPPING_FORM);
+ }
+ break;
+ case save_image_resource_code:
+ scan_pdfximage(static_pdf);
+ if (immediate) {
+ pdf_write_image(static_pdf, last_saved_image_index);
+ }
+ break;
+ }
+}
+
+/*tex
+
+ Ad immediate:
+
+ 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}.
+
+ 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}'.
+
+*/
+
+/*tex
+
+ The extensions are backend related. The next subroutine uses |cur_chr| to
+ decide what sort of whatsit is involved, and also inserts a |write_stream|
+ number.
+
+*/
+
+static void new_write_whatsit(int w, int check)
+{
+ new_whatsit(cur_chr);
+ if (check) {
+ /*tex So we check with open and close. */
+ scan_limited_int(last_file_selector,NULL);
+ } else {
+ /*tex But we're tolerant with the rest. */
+ scan_int();
+ if (cur_val < 0)
+ cur_val = term_only;
+ else if (cur_val > last_file_selector) {
+ cur_val = term_and_log;
+ }
+ }
+ write_stream(tail) = cur_val;
+}
+
+void do_extension(int immediate)
+{
+ /*tex An all-purpose pointer. */
+ halfword p;
+ if (cur_cmd == extension_cmd) {
+ /*tex These have their own range starting at 0. */
+ switch (cur_chr) {
+ case open_code:
+ p = tail;
+ new_write_whatsit(open_node_size,1);
+ scan_optional_equals();
+ /* scan_file_name(); */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ back_input();
+ if (cur_cmd != left_brace_cmd) {
+ scan_file_name();
+ } else {
+ scan_file_name_toks();
+ }
+ /* */
+ open_name(tail) = cur_name;
+ open_area(tail) = cur_area;
+ open_ext(tail) = cur_ext;
+ if (immediate) {
+ wrapup_leader(tail);
+ flush_node_list(tail);
+ tail = p;
+ vlink(p) = null;
+ }
+ break;
+ case write_code:
+ /*tex
+
+ 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.
+
+ */
+ p = tail;
+ new_write_whatsit(write_node_size,0);
+ cur_cs = write_stream(tail);
+ scan_toks(false, false);
+ write_tokens(tail) = def_ref;
+ if (immediate) {
+ wrapup_leader(tail);
+ flush_node_list(tail);
+ tail = p;
+ vlink(p) = null;
+ }
+ break;
+ case close_code:
+ p = tail;
+ new_write_whatsit(close_node_size,1);
+ write_tokens(tail) = null;
+ if (immediate) {
+ wrapup_leader(tail);
+ flush_node_list(tail);
+ tail = p;
+ vlink(p) = null;
+ }
+ break;
+ case special_code:
+ /*tex
+
+ When `\.{\\special\{...\}}' appears, we expand the macros in
+ the token list as in \.{\\xdef} and \.{\\mark}.
+
+ */
+ new_whatsit(special_node);
+ write_stream(tail) = null;
+ p = scan_toks(false, true);
+ write_tokens(tail) = def_ref;
+ break;
+ case immediate_code:
+ get_x_token();
+ do_extension(1);
+ break;
+ case end_local_code:
+ if (tracing_nesting_par > 2) {
+ local_control_message("leaving token scanner");
+ }
+ end_local_control();
+ break;
+ case use_box_resource_code:
+ case use_image_resource_code:
+ case save_box_resource_code:
+ case save_image_resource_code:
+ switch (get_o_mode()) {
+ case OMODE_DVI:
+ do_resource_dvi(immediate,cur_chr);
+ break;
+ case OMODE_PDF:
+ do_resource_pdf(immediate,cur_chr);
+ break;
+ default:
+ break;
+ }
+ break;
+ /*tex Backend extensions have their own range starting at 32. */
+ case dvi_extension_code:
+ if (get_o_mode() == OMODE_DVI)
+ do_extension_dvi(immediate);
+ break;
+ case pdf_extension_code:
+ if (get_o_mode() == OMODE_PDF)
+ do_extension_pdf(immediate);
+ break;
+ /*tex Done. */
+ default:
+ if (immediate) {
+ back_input();
+ } else {
+ confusion("invalid extension");
+ }
+ break;
+ }
+ } else {
+ /*tex No extension command, quite certainly following |\immediate|. */
+ back_input();
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void new_whatsit(int s)
+{
+ halfword p = new_node(whatsit_node, s);
+ couple_nodes(tail, p);
+ tail = p;
+}
+
+/*tex
+
+ 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 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.
+
+*/
+
+void expand_macros_in_tokenlist(halfword p)
+{
+ int old_mode;
+ pointer q = get_avail();
+ pointer r = get_avail();
+ token_info(q) = right_brace_token + '}';
+ token_link(q) = r;
+ token_info(r) = end_write_token;
+ begin_token_list(q, inserted);
+ begin_token_list(write_tokens(p), write_text);
+ q = get_avail();
+ token_info(q) = left_brace_token + '{';
+ begin_token_list(q, inserted);
+ /*tex
+
+ Now we're ready to scan `\.\{$\langle\,$token list$\,\rangle$\.{\}
+ \\endwrite}'.
+
+ */
+ old_mode = mode;
+ mode = 0;
+ /*tex
+
+ Disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip},
+ \.{\\prevgraf}.
+
+ */
+ cur_cs = write_loc;
+ /*tex Expand macros, etc. */
+ q = scan_toks(false, true);
+ get_token();
+ if (cur_tok != end_write_token) {
+ /*tex Recover from an unbalanced write command */
+ const char *hlp[] = {
+ "On this page there's a \\write with fewer real {'s than }'s.",
+ "I can't handle that very well; good luck.",
+ NULL
+ };
+ tex_error("Unbalanced write command", hlp);
+ do {
+ get_token();
+ } while (cur_tok != end_write_token);
+ }
+ mode = old_mode;
+ /*tex Conserve stack space. */
+ end_token_list();
+}
+
+void write_out(halfword p)
+{
+ /*tex holds print |selector| */
+ int old_setting;
+ /*tex write stream number */
+ int j;
+ /*tex line to be written, as a Lua string */
+ lstring *ss = NULL;
+ lstring *s = NULL;
+ int callback_id;
+ int lua_retval;
+ expand_macros_in_tokenlist(p);
+ old_setting = selector;
+ j = write_stream(p);
+ if (file_can_be_written(j)) {
+ selector = j;
+ } else if ((j == term_only) && (selector == term_and_log)) {
+ /*tex write to the terminal if file isn't open */
+ selector = log_only;
+ tprint_nl("");
+ } else {
+ tprint_nl("");
+ }
+ s = tokenlist_to_lstring(def_ref, false);
+ if (selector < no_print) {
+ /*tex selector is a file */
+ callback_id = callback_defined(process_output_buffer_callback);
+ if (callback_id > 0) {
+ /*tex fix up the output buffer using callbacks */
+ lua_retval = run_callback(callback_id, "L->L", s, &ss);
+ if ((lua_retval == true) && (ss != NULL)) {
+ free_lstring(s);
+ s = ss;
+ }
+ }
+ }
+ lprint(s);
+ free_lstring(s);
+ print_ln();
+ flush_list(def_ref);
+ selector = old_setting;
+}
+
+void finalize_write_files(void) {
+ int k;
+ for (k = 0; k <= last_file_selector; k++) {
+ if (write_open[k]) {
+ lua_a_close_out(write_file[k]);
+ }
+ }
+}
+
+void initialize_write_files(void) {
+ int k;
+ for (k = 0; k <= last_file_selector; k++) {
+ write_open[k] = false;
+ }
+}
+
+void close_write_file(int id) {
+ if (write_open[id]) {
+ lua_a_close_out(write_file[id]);
+ write_open[id] = false;
+ }
+}
+
+boolean open_write_file(int id, char *fn) {
+ if (lua_a_open_out(&(write_file[id]), fn, (id + 1))) {
+ write_open[id] = true;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*tex
+
+ To implement primitives as \.{\\pdfextension info}, \.{\\pdfextension
+ catalog} or \.{\\pdfextension names} we need to concatenate tokens lists.
+
+*/
+
+halfword concat_tokens(halfword q, halfword r)
+{
+ /*tex concat |q| and |r| and returns the result tokens list */
+ halfword p;
+ if (q == null)
+ return r;
+ p = q;
+ while (token_link(p) != null)
+ p = token_link(p);
+ set_token_link(p, token_link(r));
+ free_avail(r);
+ return q;
+}
+
+/*tex
+
+ 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.
+
+ In order to handle \.{\\everyeof} we need an array |eof_seen| of boolean
+ variables.
+
+*/
+
+boolean *eof_seen;
+
+/*tex
+
+ The |print_group| procedure prints the current level of grouping and the name
+ corresponding to |cur_group|.
+
+*/
+
+void print_group(boolean e)
+{
+ switch (cur_group) {
+ case bottom_level:
+ tprint("bottom level");
+ return;
+ break;
+ case simple_group:
+ case semi_simple_group:
+ if (cur_group == semi_simple_group)
+ tprint("semi ");
+ tprint("simple");
+ break;;
+ case hbox_group:
+ case adjusted_hbox_group:
+ if (cur_group == adjusted_hbox_group)
+ tprint("adjusted ");
+ tprint("hbox");
+ break;
+ case vbox_group:
+ tprint("vbox");
+ break;
+ case vtop_group:
+ tprint("vtop");
+ break;
+ case align_group:
+ case no_align_group:
+ if (cur_group == no_align_group)
+ tprint("no ");
+ tprint("align");
+ break;
+ case output_group:
+ tprint("output");
+ break;
+ case disc_group:
+ tprint("disc");
+ break;
+ case insert_group:
+ tprint("insert");
+ break;
+ case vcenter_group:
+ tprint("vcenter");
+ break;
+ case math_group:
+ case math_choice_group:
+ case math_shift_group:
+ case math_left_group:
+ tprint("math");
+ if (cur_group == math_choice_group)
+ tprint(" choice");
+ else if (cur_group == math_shift_group)
+ tprint(" shift");
+ else if (cur_group == math_left_group)
+ tprint(" left");
+ break;
+ }
+ tprint(" group (level ");
+ print_int(cur_level);
+ print_char(')');
+ if (saved_value(-1) != 0) {
+ /*tex |saved_line| */
+ if (e)
+ tprint(" entered at line ");
+ else
+ tprint(" at line ");
+ print_int(saved_value(-1));
+ }
+}
+
+/*tex
+
+ The |group_trace| procedure is called when a new level of grouping begins
+ (|e=false|) or ends (|e=true|) with |saved_value(-1)| containing the line
+ number.
+
+*/
+
+void group_trace(boolean e)
+{
+ begin_diagnostic();
+ print_char('{');
+ if (e)
+ tprint("leaving ");
+ else
+ tprint("entering ");
+ print_group(e);
+ print_char('}');
+ end_diagnostic(false);
+}
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex initial |cur_boundary| */
+
+save_pointer *grp_stack;
+
+/*tex initial |cond_ptr| */
+
+halfword *if_stack;
+
+/*tex
+
+ 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|.
+
+*/
+
+void group_warning(void)
+{
+ /*tex do we need a warning? */
+ boolean w = false;
+ /*tex index into |grp_stack| */
+ int i = in_open;
+ base_ptr = input_ptr;
+ /*tex store current state */
+ input_stack[base_ptr] = cur_input;
+ while ((grp_stack[i] == cur_boundary) && (i > 0)) {
+ /*tex
+
+ Set variable |w| to indicate if this case should be reported. This
+ code scans the input stack in order to determine the type of the
+ current input file.
+
+ */
+ if (tracing_nesting_par > 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;
+ }
+ grp_stack[i] = save_value(save_ptr);
+ decr(i);
+ }
+ if (w) {
+ tprint_nl("Warning: end of ");
+ print_group(true);
+ tprint(" of a different file");
+ print_ln();
+ if (tracing_nesting_par > 1)
+ show_context();
+ if (history == spotless)
+ history = warning_issued;
+ }
+}
+
+/*tex
+
+ 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).
+
+*/
+
+void if_warning(void)
+{
+ /*tex Do we need a warning? */
+ boolean w = false;
+ int i = in_open;
+ base_ptr = input_ptr;
+ /*tex Store current state. */
+ input_stack[base_ptr] = cur_input;
+ while (if_stack[i] == cond_ptr) {
+ /*tex Set variable |w| to. */
+ if (tracing_nesting_par > 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;
+ }
+ if_stack[i] = vlink(cond_ptr);
+ decr(i);
+ }
+ if (w) {
+ tprint_nl("Warning: end of ");
+ print_cmd_chr(if_test_cmd, cur_if);
+ print_if_line(if_line);
+ tprint(" of a different file");
+ print_ln();
+ if (tracing_nesting_par > 1)
+ show_context();
+ if (history == spotless)
+ history = warning_issued;
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void file_warning(void)
+{
+ /*tex saved value of |save_ptr| or |cond_ptr| */
+ halfword p = save_ptr;
+ /*tex saved value of |cur_level| or |if_limit| */
+ int l = cur_level;
+ /*tex saved value of |cur_group| or |cur_if| */
+ int c = cur_group;
+ /*tex saved value of |if_line| */
+ int i;
+ save_ptr = cur_boundary;
+ while (grp_stack[in_open] != save_ptr) {
+ decr(cur_level);
+ tprint_nl("Warning: end of file when ");
+ print_group(true);
+ tprint(" is incomplete");
+ cur_group = save_level(save_ptr);
+ save_ptr = save_value(save_ptr);
+ }
+ save_ptr = p;
+ cur_level = (quarterword) l;
+ /*tex Restore old values. */
+ cur_group = (group_code) c;
+ p = cond_ptr;
+ l = if_limit;
+ c = cur_if;
+ i = if_line;
+ while (if_stack[in_open] != cond_ptr) {
+ tprint_nl("Warning: end of file when ");
+ print_cmd_chr(if_test_cmd, cur_if);
+ if (if_limit == fi_code)
+ tprint_esc("else");
+ print_if_line(if_line);
+ tprint(" is incomplete");
+ if_line = if_line_field(cond_ptr);
+ cur_if = if_limit_subtype(cond_ptr);
+ if_limit = if_limit_type(cond_ptr);
+ cond_ptr = vlink(cond_ptr);
+ }
+ /*tex restore old values */
+ cond_ptr = p;
+ if_limit = l;
+ cur_if = c;
+ if_line = i;
+ print_ln();
+ if (tracing_nesting_par > 1)
+ show_context();
+ if (history == spotless)
+ history = warning_issued;
+}
+
+/*tex The |par_fill_skip| glue node of the new paragraph. */
+
+halfword last_line_fill;
+
+/*tex
+
+ The lua interface needs some extra functions. The functions themselves are
+ quite boring, but they are handy because otherwise this internal stuff has to
+ be accessed from C directly, where lots of the defines are not available.
+
+*/
+
+#define get_tex_dimen_register(j) dimen(j)
+#define get_tex_skip_register(j) skip(j)
+#define get_tex_mu_skip_register(j) mu_skip(j)
+#define get_tex_count_register(j) count(j)
+#define get_tex_attribute_register(j) attribute(j)
+#define get_tex_box_register(j) box(j)
+
+/*tex These can now be macros (todo). */
+
+int get_tex_extension_count_register(int i)
+{
+ return (int) int_par(backend_int_base-int_base+i);
+}
+
+void set_tex_extension_count_register(int i, int d)
+{
+ int_par(backend_int_base-int_base+i) = d;
+}
+
+int get_tex_extension_dimen_register(int i)
+{
+ return (int) dimen_par(backend_dimen_base-dimen_base+i);
+}
+
+void set_tex_extension_dimen_register(int i, int d)
+{
+ dimen_par(backend_dimen_base-dimen_base+i) = d;
+}
+
+int get_tex_extension_toks_register(int i)
+{
+ return equiv(backend_toks_base+i);
+}
+
+int set_tex_dimen_register(int j, scaled v)
+{
+ int a;
+ if (global_defs_par > 0)
+ a = 4;
+ else
+ a = 0;
+ word_define(j + scaled_base, v);
+ return 0;
+}
+
+int set_tex_skip_register(int j, halfword v)
+{
+ int a;
+ if (global_defs_par > 0)
+ a = 4;
+ else
+ a = 0;
+ if (type(v) != glue_spec_node)
+ return 1;
+ word_define(j + skip_base, v);
+ return 0;
+}
+
+int set_tex_mu_skip_register(int j, halfword v)
+{
+ int a;
+ if (global_defs_par > 0)
+ a = 4;
+ else
+ a = 0;
+ if (type(v) != glue_spec_node)
+ return 1;
+ word_define(j + mu_skip_base, v);
+ return 0;
+}
+
+int set_tex_count_register(int j, scaled v)
+{
+ int a;
+ if (global_defs_par > 0)
+ a = 4;
+ else
+ a = 0;
+ word_define(j + count_base, v);
+ return 0;
+}
+
+int set_tex_box_register(int j, scaled v)
+{
+ int a;
+ if (global_defs_par > 0)
+ a = 4;
+ else
+ a = 0;
+ define(j + box_base, box_ref_cmd, v);
+ return 0;
+}
+
+int set_tex_attribute_register(int j, scaled v)
+{
+ int a;
+ if (global_defs_par > 0)
+ a = 4;
+ else
+ a = 0;
+ if (j > max_used_attr)
+ max_used_attr = j;
+ attr_list_cache = cache_disabled;
+ word_define(j + attribute_base, v);
+ return 0;
+}
+
+int get_tex_toks_register(int j)
+{
+ str_number s = get_nullstr();
+ if (toks(j) != null) {
+ s = tokens_to_string(toks(j));
+ }
+ return s;
+}
+
+int set_tex_toks_register(int j, lstring s)
+{
+ int a;
+ halfword ref = get_avail();
+ (void) str_toks(s);
+ set_token_ref_count(ref, 0);
+ set_token_link(ref, token_link(temp_token_head));
+ if (global_defs_par > 0)
+ a = 4;
+ else
+ a = 0;
+ define(j + toks_base, call_cmd, ref);
+ return 0;
+}
+
+int scan_tex_toks_register(int j, int c, lstring s)
+{
+ int a;
+ halfword ref = get_avail();
+ (void) str_scan_toks(c,s);
+ set_token_ref_count(ref, 0);
+ set_token_link(ref, token_link(temp_token_head));
+ if (global_defs_par > 0)
+ a = 4;
+ else
+ a = 0;
+ define(j + toks_base, call_cmd, ref);
+ return 0;
+}
+
+scaled get_tex_box_width(int j)
+{
+ halfword q = box(j);
+ if (q != null)
+ return width(q);
+ return 0;
+}
+
+int set_tex_box_width(int j, scaled v)
+{
+ halfword q = box(j);
+ if (q == null)
+ return 1;
+ width(q) = v;
+ return 0;
+}
+
+scaled get_tex_box_height(int j)
+{
+ halfword q = box(j);
+ if (q != null)
+ return height(q);
+ return 0;
+}
+
+int set_tex_box_height(int j, scaled v)
+{
+ halfword q = box(j);
+ if (q == null)
+ return 1;
+ height(q) = v;
+ return 0;
+}
+
+scaled get_tex_box_depth(int j)
+{
+ halfword q = box(j);
+ if (q != null)
+ return depth(q);
+ return 0;
+}
+
+int set_tex_box_depth(int j, scaled v)
+{
+ halfword q = box(j);
+ if (q == null)
+ return 1;
+ depth(q) = v;
+ return 0;
+}
+
+/*tex
+
+ This section is devoted to the {\sl Synchronize \TeX nology} - or simply {\sl
+ Sync\TeX} - used to synchronize between input and output. This section
+ explains how synchronization basics are implemented. Before we enter into
+ more technical details, let us recall in a few words what is synchronization.
+
+ \TeX\ typesetting system clearly separates the input and the output material,
+ and synchronization will provide a new link between both that can help text
+ editors and viewers to work together. More precisely, forwards
+ synchronization is the ability, given a location in the input source file, to
+ find what is the corresponding place in the output. Backwards synchronization
+ just performs the opposite: given a location in the output, retrieve the
+ corresponding material in the input source file.
+
+ For better code management and maintainance, we adopt a naming convention.
+ Throughout this program, code related to the {\sl Synchronize \TeX nology} is
+ tagged with the ``{\sl synctex}'' key word. Any code extract where {\sl
+ Sync\TeX} plays its part, either explicitly or implicitly, (should) contain
+ the string ``{\sl synctex}''. This naming convention also holds for external
+ files. Moreover, all the code related to {\sl Sync\TeX} is gathered in this
+ section, except the definitions.
+
+ Enabling synchronization should be performed from the command line,
+ |synctexoption| is used for that purpose. This global integer variable is
+ declared here but it is not used here. This is just a placeholder where the
+ command line controller will put the {\sl Sync\TeX} related options, and the
+ {\sl Sync\TeX} controller will read them.
+
+*/
+
+int synctexoption;
+
+/*tex
+
+ A convenient primitive is provided: \.{\\synctex=1} in the input source file
+ enables synchronization whereas \.{\\synctex=0} disables it. Its memory
+ address is |synctex_code|. It is initialized by the {\sl Sync\TeX} controller
+ to the command-line option if given. The controller may filter some reserved
+ bits.
+
+ In order to give the {\sl Sync\TeX} controller read and write access to the
+ contents of the \.{\\synctex} primitive, we declare |synctexoffset|, such
+ that |mem[synctexoffset]| and \.{\\synctex} correspond to the same memory
+ storage. |synctexoffset| is initialized to the correct value when quite
+ everything is initialized.
+
+*/
+
+/*tex Holds the true value of |synctex_code|: */
+
+int synctexoffset;
+
+/*tex
+
+ Synchronization is achieved with the help of an auxiliary file named `\.{{\sl
+ jobname}.synctex}' ({\sl jobname} is the contents of the \.{\\jobname}
+ macro), where a {\sl Sync\TeX} controller implemented in the external
+ |synctex.c| file will store geometrical information. This {\sl Sync\TeX}
+ controller will take care of every technical details concerning the {\sl
+ Sync\TeX} file, we will only focus on the messages the controller will
+ receive from the \TeX\ program.
+
+ The most accurate synchronization information should allow to map any
+ character of the input source file to the corresponding location in the
+ output, if relevant. Ideally, the synchronization information of the input
+ material consists of the file name, the line and column numbers of every
+ character. The synchronization information in the output is simply the page
+ number and either point coordinates, or box dimensions and position. The
+ problem is that the mapping between these informations is only known at ship
+ out time, which means that we must keep track of the input synchronization
+ information until the pages ship out.
+
+ As \TeX\ only knows about file names and line numbers, but forgets the column
+ numbers, we only consider a restricted input synchronization information
+ called {\sl Sync\TeX\ information}. It consists of a unique file name
+ identifier, the {\sl Sync\TeX\ file tag}, and the line number.
+
+ Keeping track of such information, should be different whether characters or
+ nodes are involved. Actually, only certain nodes are involved in {\sl
+ Sync\TeX}, we call them {\sl synchronized nodes}. Synchronized nodes store
+ the {\sl Sync\TeX} information in their last two words: the first one
+ contains a {\sl Sync\TeX\ file tag} uniquely identifying the input file, and
+ the second one contains the current line number, as returned by the
+ \.{\\inputlineno} primitive. The |synctex_field_size| macro contains the
+ necessary size to store the {\sl Sync\TeX} information in a node.
+
+ When declaring the size of a new node, it is recommanded to use the following
+ convention: if the node is synchronized, use a definition similar to
+ |my_synchronized_node_size|={\sl xxx}+|synctex_field_size|. Moreover, one
+ should expect that the {\sl Sync\TeX} information is always stored in the
+ last two words of a synchronized node.
+
+ By default, every node with a sufficiently big size is initialized at
+ creation time in the |get_node| routine with the current {\sl Sync\TeX}
+ information, whether or not the node is synchronized. One purpose is to set
+ this information very early in order to minimize code dependencies, including
+ forthcoming extensions. Another purpose is to avoid the assumption that every
+ node type has a dedicated getter, where initialization should take place.
+ Actually, it appears that some nodes are created using directly the
+ |get_node| routine and not the dedicated constructor. And finally,
+ initializing the node at only one place is less error prone.
+
+ Instead of storing the input file name, it is better to store just an
+ identifier. Each time \TeX\ opens a new file, it notifies the {\sl Sync\TeX}
+ controller with a |synctex_start_input| message. This controller will create
+ a new {\sl Sync\TeX} file tag and will update the current input state record
+ accordingly. If the input comes from the terminal or a pseudo file, the
+ |synctex_tag| is set to 0. It results in automatically disabling
+ synchronization for material input from the terminal or pseudo files.
+
+ Synchronized nodes are boxes, math, kern and glue nodes. Other nodes should
+ be synchronized too, in particular math noads. \TeX\ assumes that math, kern
+ and glue nodes have the same size, this is why both are synchronized. {\sl In
+ fine}, only horizontal lists are really used in {\sl Sync\TeX}, but all box
+ nodes are considered the same with respect to synchronization, because a box
+ node type is allowed to change at execution time.
+
+ {\em Nota Bene:} The {\sl Sync\TeX} code is very close to the memory model.
+ It is not connected to any other part of the code, except for memory
+ management. It is possible to neutralize the {\sl Sync\TeX} code rather
+ simply. The first step is to define a null |synctex_field_size|. The second
+ step is to comment out the code in ``Initialize bigger nodes...'' and every
+ ``Copy ... {\sl Sync\TeX} information''. The last step will be to comment out
+ the |synctex_tag_field| related code in the definition of |synctex_tag| and
+ the various ``Prepare ... {\sl Sync\TeX} information''. Then all the
+ remaining code should be just harmless. The resulting program would behave
+ exactly the same as if absolutely no {\sl Sync\TeX} related code was there,
+ including memory management. Of course, all this assumes that {\sl Sync\TeX}
+ is turned off from the command line.
+
+ Here are extra variables for Web2c. (This numbering of the system-dependent
+ section allows easy integration of Web2c and e-\TeX, etc.)
+
+*/
+
+/*tex where the filename to switch to starts */
+
+pool_pointer edit_name_start;
+
+/*tex what line to start editing at */
+
+int edit_name_length, edit_line;
+
+/*tex whether |more_name| returns false for space */
+
+boolean stop_at_space;
+
+/*tex
+
+ The |edit_name_start| will be set to point into |str_pool| somewhere after
+ its beginning if \TeX\ is supposed to switch to an editor on exit.
+
+*/
+
+int shellenabledp;
+int restrictedshell;
+char *output_comment;
+
+/*tex
+
+ Are we printing extra info as we read the format file?
+
+*/
+
+boolean debug_format_file;
+
+void wrapup_leader(halfword p)
+{
+ /*tex Do some work that has been queued up for \.{\\write}. */
+ if (!doing_leaders) {
+ int j = write_stream(p);
+ if (subtype(p) == write_node) {
+ write_out(p);
+ } else if (subtype(p) == close_node) {
+ close_write_file(j);
+ } else if (valid_write_file(j)) {
+ char *fn;
+ close_write_file(j);
+ cur_name = open_name(p);
+ cur_area = open_area(p);
+ cur_ext = open_ext(p);
+ if (cur_ext == get_nullstr())
+ cur_ext = maketexstring(".tex");
+ fn = pack_file_name(cur_name, cur_area, cur_ext);
+ while (! open_write_file(j,fn)) {
+ fn = prompt_file_name("output file name", ".tex");
+ }
+ }
+ }
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.h b/Build/source/texk/web2c/luatexdir/tex/extensions.h
new file mode 100644
index 00000000000..5e22ddf26c3
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/extensions.h
@@ -0,0 +1,147 @@
+/* extensions.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef EXTENSIONS_H
+# define EXTENSIONS_H
+
+extern alpha_file write_file[last_file_selector+1];
+extern halfword write_file_mode[last_file_selector+1];
+extern halfword write_file_translation[last_file_selector+1];
+extern boolean write_open[last_file_selector+1];
+
+# define valid_write_file(n) ((n>=0) && (n<=last_file_selector))
+# define file_can_be_written(n) (valid_write_file(n) && write_open[n])
+
+extern void expand_macros_in_tokenlist(halfword p);
+extern void write_out(halfword p);
+extern void finalize_write_files(void);
+extern void initialize_write_files(void);
+extern void close_write_file(int id);
+extern boolean open_write_file(int id, char *fn);
+
+extern scaled neg_wd;
+extern scaled pos_wd;
+extern scaled neg_ht;
+
+extern halfword write_loc;
+
+extern void do_extension(int immediate);
+
+/*
+User defined whatsits can be inserted into node lists to pass data
+along from one lua call to another without interference from the
+typesetting engine itself. Each has an id, a type, and a value. The
+type of the value depends on the |user_node_type| field.
+*/
+
+extern void new_whatsit(int s);
+
+extern halfword concat_tokens(halfword q, halfword r);
+
+/*
+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|.
+*/
+
+extern boolean *eof_seen; /* has eof been seen? */
+extern void print_group(boolean e);
+extern void group_trace(boolean e);
+extern save_pointer *grp_stack; /* initial |cur_boundary| */
+extern halfword *if_stack; /* initial |cond_ptr| */
+extern void group_warning(void);
+extern void if_warning(void);
+extern void file_warning(void);
+
+extern halfword last_line_fill; /* the |par_fill_skip| glue node of the new paragraph */
+
+# define get_tex_dimen_register(j) dimen(j)
+# define get_tex_skip_register(j) skip(j)
+# define get_tex_mu_skip_register(j) mu_skip(j)
+# define get_tex_count_register(j) count(j)
+# define get_tex_attribute_register(j) attribute(j)
+# define get_tex_box_register(j) box(j)
+
+extern int get_tex_extension_count_register(int i);
+extern void set_tex_extension_count_register(int i, int d);
+extern int get_tex_extension_dimen_register(int i);
+extern void set_tex_extension_dimen_register(int i, int d);
+extern int get_tex_extension_toks_register (int i);
+
+extern int set_tex_dimen_register(int j, scaled v);
+extern int set_tex_skip_register(int j, halfword v);
+extern int set_tex_mu_skip_register(int j, halfword v);
+extern int set_tex_count_register(int j, scaled v);
+extern int set_tex_box_register(int j, scaled v);
+extern int set_tex_attribute_register(int j, scaled v);
+extern int get_tex_toks_register(int l);
+extern int set_tex_toks_register(int j, lstring s);
+extern int scan_tex_toks_register(int j, int c, lstring s);
+extern scaled get_tex_box_width(int j);
+extern int set_tex_box_width(int j, scaled v);
+extern scaled get_tex_box_height(int j);
+extern int set_tex_box_height(int j, scaled v);
+extern scaled get_tex_box_depth(int j);
+extern int set_tex_box_depth(int j, scaled v);
+
+/* Synctex variables */
+
+extern int synctexoption;
+extern int synctexoffset;
+
+/* Here are extra variables for Web2c. */
+
+extern pool_pointer edit_name_start;
+extern int edit_name_length, edit_line;
+extern boolean stop_at_space;
+extern int shellenabledp;
+extern int restrictedshell;
+extern char *output_comment;
+extern boolean debug_format_file;
+
+extern int last_saved_box_index ;
+extern int last_saved_image_index ;
+extern int last_saved_image_pages ;
+extern scaledpos last_position ;
+
+typedef enum {
+ /* traditional extensions */
+ open_code = 0,
+ write_code,
+ close_code,
+ reserved_extension_code, // 3: we moved special below immediate //
+ reserved_immediate_code, // 4: same number as main codes, expected value //
+ /* backend specific implementations */
+ special_code,
+ save_box_resource_code,
+ use_box_resource_code,
+ save_image_resource_code,
+ use_image_resource_code,
+ end_local_code,
+ /* backend */
+ dvi_extension_code,
+ pdf_extension_code,
+} extension_codes ;
+
+extern void wrapup_leader(halfword p);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.c b/Build/source/texk/web2c/luatexdir/tex/filename.c
new file mode 100644
index 00000000000..1e06e877ae8
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/filename.c
@@ -0,0 +1,383 @@
+/*
+
+filename.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+
+/*tex
+
+ 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|.
+
+
+ Here now is the first of the system-dependent routines for file name
+ scanning. @^system dependencies@>
+
+*/
+
+static void begin_name(void)
+{
+ area_delimiter = 0;
+ ext_delimiter = 0;
+ quoted_filename = false;
+}
+
+/*tex
+
+ 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@>
+
+*/
+
+static boolean more_name(ASCII_code c)
+{
+ if (c == ' ' && stop_at_space && (!quoted_filename)) {
+ return false;
+ } else if (c == '"') {
+ quoted_filename = !quoted_filename;
+ return true;
+ } else {
+ str_room(1);
+ append_char(c);
+ if (IS_DIR_SEP(c)) {
+ area_delimiter = (pool_pointer) cur_length;
+ ext_delimiter = 0;
+ } else if (c == '.')
+ ext_delimiter = (pool_pointer) cur_length;
+ return true;
+ }
+}
+
+/*tex
+
+ The third. @^system dependencies@>
+
+*/
+
+static void end_name(void)
+{
+ unsigned char *s;
+ if (str_ptr + 3 > (max_strings + STRING_OFFSET))
+ overflow(
+ "number of strings",
+ (unsigned) (max_strings - init_str_ptr + STRING_OFFSET)
+ );
+ /*tex At this point, the full string lives in |cur_string|. */
+ if (area_delimiter == 0) {
+ cur_area = get_nullstr();
+ } else {
+ s = (unsigned char *) xstrdup((char *) (cur_string + area_delimiter));
+ cur_string[area_delimiter] = '\0';
+ cur_length = (unsigned) strlen((char *) cur_string);
+ cur_area = make_string();
+ xfree(cur_string);
+ cur_length = (unsigned) strlen((char *) s);
+ cur_string = s;
+ }
+ if (ext_delimiter == 0) {
+ cur_name = make_string();
+ cur_ext = get_nullstr();
+ } else {
+ int l = (ext_delimiter - area_delimiter - 1);
+ s = (unsigned char *) xstrdup((char *) (cur_string + l));
+ cur_string[l] = '\0';
+ cur_length = (unsigned) strlen((char *) cur_string);
+ cur_name = make_string();
+ xfree(cur_string);
+ cur_length = (unsigned) strlen((char *) s);
+ cur_string = s;
+ cur_ext = make_string();
+ }
+}
+
+/*tex
+
+ Now let's consider the ``driver'' routines by which \TeX\ deals with file
+ names in a system-independent manner. First comes a procedure that looks for a
+ file name in the input by calling |get_x_token| for the information.
+
+*/
+
+void scan_file_name(void)
+{
+ str_number u = 0;
+ name_in_progress = true;
+ begin_name();
+ /*tex Get the next non-blank non-call token: */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+ while (true) {
+ if ((cur_cmd > other_char_cmd) || (cur_chr > biggest_char)) { /* not a character */
+ back_input();
+ break;
+ }
+ /*tex
+ 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 == ' ') && (istate != token_list) && (iloc > ilimit)
+ && !quoted_filename)
+ break;
+ if (cur_chr > 127) {
+ unsigned char *bytes;
+ unsigned char *thebytes;
+ thebytes = uni2str((unsigned) cur_chr);
+ bytes = thebytes;
+ while (*bytes) {
+ if (!more_name(*bytes))
+ break;
+ bytes++;
+ }
+ xfree(thebytes);
+ } else {
+ if (!more_name(cur_chr))
+ break;
+ }
+ u = save_cur_string();
+ get_x_token();
+ restore_cur_string(u);
+ }
+ end_name();
+ name_in_progress = false;
+}
+
+/*
+ This function constructs a the three file name strings from a token list.
+*/
+
+void scan_file_name_toks(void)
+{
+ char *a, *n, *e, *s = NULL;
+ int i, l = 0;
+ int save_scanner_status = scanner_status;
+ halfword save_def_ref = def_ref;
+ (void) scan_toks(false, true);
+ s = tokenlist_to_cstring(def_ref, true, &l);
+ a = n = s;
+ e = NULL;
+ for (i = 0; i < l; i++) {
+ if (IS_DIR_SEP(s[i])) {
+ n = s + i + 1;
+ e = NULL;
+ } else if (s[i] == '.') {
+ e = s + i;
+ }
+ }
+ if (n != s) {
+ /*tex explicit area */
+ cur_area = maketexlstring(a, (size_t) (n - a));
+ } else {
+ cur_area = get_nullstr();
+ }
+ if (e != NULL) {
+ /*tex explicit extension */
+ cur_name = maketexlstring(n, (size_t) (e - n));
+ cur_ext = maketexstring(e);
+ } else {
+ cur_name = maketexstring(n);
+ cur_ext = get_nullstr();
+ }
+ flush_list(def_ref);
+ scanner_status = save_scanner_status;
+ def_ref = save_def_ref;
+ xfree(s);
+}
+
+/*tex
+
+ 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|; |s = ".log"|, |".dvi"|, or |format_extension|
+*/
+
+char *pack_job_name(const char *s)
+{
+ cur_area = get_nullstr();
+ cur_ext = maketexstring(s);
+ cur_name = job_name;
+ return pack_file_name(cur_name, cur_area, cur_ext);
+}
+
+/*tex
+
+ If some trouble arises when \TeX\ tries to open a file, the following routine
+ calls upon the user to supply another file name. Parameter~|s| is used in the
+ error message to identify the type of file; parameter~|e| is the default
+ extension if none is given. Upon exit from the routine, variables |cur_name|,
+ |cur_area|, and |cur_ext| are ready for another attempt at file opening.
+
+*/
+
+char *prompt_file_name(const char *s, const char *e)
+{
+ int k; /* index into |buffer| */
+ str_number saved_cur_name; /* to catch empty terminal input */
+ int callback_id ;
+ char prompt[256];
+ char *ar, *na, *ex;
+ saved_cur_name = cur_name;
+ if (interaction == scroll_mode) {
+ wake_up_terminal();
+ }
+ ar = makecstring(cur_area);
+ na = makecstring(cur_name);
+ ex = makecstring(cur_ext);
+ if (strcmp(s, "input file name") == 0) {
+ snprintf(prompt, 255, "I can't find file `%s%s%s'.", ar, na, ex);
+ } else {
+ snprintf(prompt, 255, "I can't write on file `%s%s%s'.", ar, na, ex);
+ }
+ free(ar);
+ free(na);
+ free(ex);
+ print_err(prompt);
+ callback_id = callback_defined(show_error_hook_callback);
+ if (callback_id > 0) {
+ flush_err();
+ run_callback(callback_id, "->");
+ } else {
+ if ((strcmp(e, ".tex") == 0) || (strcmp(e, "") == 0))
+ show_context();
+ if (strcmp(s, "input file name") == 0)
+ tprint_nl(promptfilenamehelpmsg ")");
+ }
+ tprint_nl("Please type another ");
+ tprint(s);
+ if (interaction < scroll_mode)
+ fatal_error("*** (job aborted, file error in nonstop mode)");
+ clear_terminal();
+ prompt_input(": ");
+ begin_name();
+ k = first;
+ while ((buffer[k] == ' ') && (k < last))
+ k++;
+ while (true) {
+ if (k == last)
+ break;
+ if (!more_name(buffer[k]))
+ break;
+ k++;
+ }
+ end_name();
+ if (cur_ext == get_nullstr())
+ cur_ext = maketexstring(e);
+ if (str_length(cur_name) == 0)
+ cur_name = saved_cur_name;
+ return pack_file_name(cur_name, cur_area, cur_ext);
+}
+
+void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e)
+{
+ boolean must_quote; /* whether to quote the filename */
+ unsigned char *j; /* index into string */
+ must_quote = false;
+ if (a != NULL) {
+ j = a;
+ while ((!must_quote) && (*j)) {
+ must_quote = (*j == ' ');
+ j++;
+ }
+ }
+ if (n != NULL) {
+ j = n;
+ while ((!must_quote) && (*j)) {
+ must_quote = (*j == ' ');
+ j++;
+ }
+ }
+ if (e != NULL) {
+ j = e;
+ while ((!must_quote) && (*j)) {
+ must_quote = (*j == ' ');
+ j++;
+ }
+ }
+ /*tex
+ Alternative is to assume that any filename that has to be quoted has at
+ least one quoted component...if we pick this, a number of insertions of
+ |print_file_name| should go away.
+ */
+ if (must_quote)
+ print_char('"');
+ if (a != NULL) {
+ for (j = a; *j; j++)
+ if (*j != '"')
+ print_char(*j);
+ }
+ if (n != NULL) {
+ for (j = n; *j; j++)
+ if (*j != '"')
+ print_char(*j);
+ }
+ if (e != NULL) {
+ for (j = e; *j; j++)
+ if (*j != '"')
+ print_char(*j);
+ }
+ if (must_quote)
+ print_char('"');
+}
+
+void print_file_name(str_number n, str_number a, str_number e)
+{
+ char *nam, *are, *ext;
+ nam = makecstring(n);
+ are = makecstring(a);
+ ext = makecstring(e);
+ tprint_file_name(
+ (unsigned char *) nam,
+ (unsigned char *) are,
+ (unsigned char *) ext
+ );
+ free(nam);
+ free(are);
+ free(ext);
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.h b/Build/source/texk/web2c/luatexdir/tex/filename.h
new file mode 100644
index 00000000000..cb3d060fd83
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/filename.h
@@ -0,0 +1,33 @@
+/* filename.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef FILENAME_H
+# define FILENAME_H
+
+extern void scan_file_name(void);
+extern void scan_file_name_toks(void);
+extern char *pack_job_name(const char *s);
+extern char *prompt_file_name(const char *s, const char *e);
+extern void tprint_file_name(unsigned char *n, unsigned char *a,
+ unsigned char *e);
+extern void print_file_name(str_number, str_number, str_number);
+
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/inputstack.c b/Build/source/texk/web2c/luatexdir/tex/inputstack.c
new file mode 100644
index 00000000000..6b0e1a055eb
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.c
@@ -0,0 +1,960 @@
+/*
+
+inputstack.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+in_state_record *input_stack = NULL;
+
+/*tex First unused location of |input_stack|: */
+
+int input_ptr = 0;
+
+/*tex Largest value of |input_ptr| when pushing: */
+
+int max_in_stack = 0;
+
+/*tex The `top' input state: */
+
+in_state_record cur_input;
+
+/*tex The number of lines in the buffer, less one: */
+
+int in_open = 0;
+
+/*tex The number of open text files: */
+
+int open_parens = 0;
+
+alpha_file *input_file = NULL;
+
+/*tex The current line number in the current source file: */
+
+int line = 0;
+
+int *line_stack = NULL;
+
+str_number *source_filename_stack = NULL;
+
+char **full_source_filename_stack = NULL;
+
+/*tex Can a subfile end now? */
+
+int scanner_status = 0;
+
+/*tex Identifier relevant to non-|normal| scanner status: */
+
+pointer warning_index = null;
+
+/*tex Reference count of token list being defined: */
+
+pointer def_ref = null;
+
+/*tex
+
+Here is a procedure that uses |scanner_status| to print a warning message when a
+subfile has ended, and at certain other crucial times:
+
+*/
+
+void runaway(void)
+{
+ /*tex The head of the runaway list: */
+ pointer p = null;
+ if (scanner_status > skipping) {
+ switch (scanner_status) {
+ case defining:
+ tprint_nl("Runaway definition");
+ p = def_ref;
+ break;
+ case matching:
+ tprint_nl("Runaway argument");
+ p = temp_token_head;
+ break;
+ case aligning:
+ tprint_nl("Runaway preamble");
+ p = hold_token_head;
+ break;
+ case absorbing:
+ tprint_nl("Runaway text");
+ p = def_ref;
+ break;
+ default:
+ /*tex There are no other cases. */
+ break;
+ }
+ print_char('?');
+ print_ln();
+ show_token_list(token_link(p), null, error_line - 10);
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+/*tex Token list pointers for parameters: */
+
+pointer *param_stack = NULL;
+
+/*tex First unused entry in |param_stack|: */
+
+int param_ptr = 0;
+
+/*tex Largest value of |param_ptr|, will be |<=param_size+9|: */
+
+int max_param_stack = 0;
+
+/*tex
+
+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|.
+
+*/
+
+/*tex The group level with respect to current alignment: */
+
+int align_state = 0;
+
+/*tex
+
+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.
+
+*/
+
+/*tex The shallowest level shown by |show_context|: */
+
+int base_ptr = 0;
+
+/*tex
+
+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.
+
+*/
+
+static void print_token_list_type(int t)
+{
+ switch (t) {
+ case parameter:
+ tprint_nl("<argument> ");
+ break;
+ case u_template:
+ case v_template:
+ tprint_nl("<template> ");
+ break;
+ case backed_up:
+ if (iloc == null)
+ tprint_nl("<recently read> ");
+ else
+ tprint_nl("<to be read again> ");
+ break;
+ case inserted:
+ tprint_nl("<inserted text> ");
+ break;
+ case macro:
+ print_ln();
+ print_cs(iname);
+ break;
+ case output_text:
+ tprint_nl("<output> ");
+ break;
+ case every_par_text:
+ tprint_nl("<everypar> ");
+ break;
+ case every_math_text:
+ tprint_nl("<everymath> ");
+ break;
+ case every_display_text:
+ tprint_nl("<everydisplay> ");
+ break;
+ case every_hbox_text:
+ tprint_nl("<everyhbox> ");
+ break;
+ case every_vbox_text:
+ tprint_nl("<everyvbox> ");
+ break;
+ case every_job_text:
+ tprint_nl("<everyjob> ");
+ break;
+ case every_cr_text:
+ tprint_nl("<everycr> ");
+ break;
+ case mark_text:
+ tprint_nl("<mark> ");
+ break;
+ case every_eof_text:
+ tprint_nl("<everyeof> ");
+ break;
+ case write_text:
+ tprint_nl("<write> ");
+ break;
+ case local_text:
+ tprint_nl("<local> ");
+ break;
+ default:
+ tprint_nl("?");
+ /*tex This should never happen. */
+ break;
+ }
+}
+
+/*tex
+
+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 mod 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..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)..k-1]|,}$$ where subscripts of |trick_buf| are
+circular modulo |error_line|. The second line consists of |n|~spaces followed by
+|trick_buf[k..(k+m-1)]|, unless |n+m>error_line|; in the latter case, further
+cropping is done. This is easier to program than to explain.
+
+The following code sets up the print routines so that they will gather the
+desired information.
+
+*/
+
+void set_trick_count(void)
+{
+ first_count = tally;
+ trick_count = tally + 1 + error_line - half_error_line;
+ if (trick_count < error_line)
+ trick_count = error_line;
+}
+
+#define begin_pseudoprint() do { \
+ l=tally; \
+ tally=0; \
+ selector=pseudo; \
+ trick_count=1000000; \
+ } while (0)
+
+#define PSEUDO_PRINT_THE_LINE() do { \
+ begin_pseudoprint(); \
+ if (buffer[ilimit]==end_line_char_par) { \
+ j=ilimit; \
+ } else { \
+ /*tex Determine the effective end of the line. */ \
+ j=ilimit+1; \
+ } \
+ if (j>0) { \
+ for (i=istart;i<=j-1;i++) { \
+ if (i==iloc) \
+ set_trick_count(); \
+ print_char(buffer[i]); \
+ } \
+ } \
+} while (0)
+
+/*tex
+
+We don't care too much if we stay a bit too much below the max error_line even if
+we have more room on the line. If length is really an issue then any length is.
+After all one can set the length larger.
+
+*/
+
+#define print_valid_utf8(q) do { \
+ c = (int)trick_buf[q % error_line]; \
+ if (c < 128) { \
+ print_char(c); \
+ } else if (c < 194) { \
+ /* invalid */ \
+ } else if (c < 224) { \
+ print_char(c); \
+ print_char(trick_buf[(q+1) % error_line]); \
+ } else if (c < 240) { \
+ print_char(c); \
+ print_char(trick_buf[(q+1) % error_line]); \
+ print_char(trick_buf[(q+2) % error_line]); \
+ } else if (c < 245) { \
+ print_char(c); \
+ print_char(trick_buf[(q+1) % error_line]); \
+ print_char(trick_buf[(q+2) % error_line]); \
+ print_char(trick_buf[(q+3) % error_line]); \
+ } else { \
+ /*tex Invalid character! */ \
+ } \
+} while (0)
+
+/*tex
+
+This one prints where the scanner is.
+
+*/
+
+void show_context(void)
+{
+ /*tex Saved |selector| setting: */
+ int old_setting;
+ /*tex Number of contexts shown so far, less one: */
+ int nn = -1;
+ /*tex Have we reached the final context to be shown? */
+ boolean bottom_line = false;
+ /*tex Index into |buffer|: */
+ int i;
+ /*tex End of current line in |buffer|: */
+ int j;
+ /*tex Length of descriptive information on line 1: */
+ int l;
+ /*tex Context information gathered for line 2: */
+ int m;
+ /*tex Length of line 1: */
+ int n;
+ /*tex Starting or ending place in |trick_buf|: */
+ int p;
+ /*tex Temporary index: */
+ int q;
+ /*tex Used in sanitizer: */
+ int c;
+ base_ptr = input_ptr;
+ input_stack[base_ptr] = cur_input;
+ /*tex Store the current state. */
+ while (true) {
+ /*tex Enter into the context. */
+ cur_input = input_stack[base_ptr];
+ if (istate != token_list) {
+ if ((iname > 21) || (base_ptr == 0))
+ bottom_line = true;
+ }
+ if ((base_ptr == input_ptr) || bottom_line || (nn < error_context_lines_par)) {
+ /*tex Display the current context. */
+ if ((base_ptr == input_ptr) || (istate != token_list) || (token_type != backed_up) || (iloc != null)) {
+ /*tex
+ We omit backed-up token lists that have already been read.
+ Get ready to count characters.
+ */
+ tally = 0;
+ old_setting = selector;
+ if (istate != token_list) {
+ /*tex
+ Print the location of the current line. 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.
+ */
+ if (iname <= 17) {
+ if (terminal_input) {
+ if (base_ptr == 0)
+ tprint_nl("<*>");
+ else
+ tprint_nl("<insert> ");
+ } else {
+ tprint_nl("<read ");
+ if (iname == 17)
+ print_char('*');
+ else
+ print_int(iname - 1);
+ print_char('>');
+ };
+ } else {
+ tprint_nl("l.");
+ if (iindex == in_open) {
+ print_int(line);
+ } else {
+ /*tex Input from a pseudo file. */
+ print_int(line_stack[iindex + 1]);
+ }
+ }
+ print_char(' ');
+ PSEUDO_PRINT_THE_LINE();
+ } else {
+ print_token_list_type(token_type);
+
+ begin_pseudoprint();
+ if (token_type < macro) {
+ show_token_list(istart, iloc, 100000);
+ } else {
+ /*tex Avoid reference count. */
+ show_token_list(token_link(istart), iloc, 100000);
+ }
+ }
+ /*tex Stop pseudoprinting. */
+ selector = old_setting;
+ /*tex Print two lines using the tricky pseudoprinted information. */
+ if (trick_count == 1000000) {
+ set_trick_count();
+ }
+ /*tex The |set_trick_count| must be performed. */
+ if (tally < trick_count) {
+ m = tally - first_count;
+ } else {
+ /* The context on line 2: */
+ m = trick_count - first_count;
+ }
+ if (l + first_count <= half_error_line) {
+ p = 0;
+ n = l + first_count;
+ } else {
+ tprint("...");
+ p = l + first_count - half_error_line + 3;
+ n = half_error_line;
+ }
+ for (q = p; q <= first_count - 1; q++) {
+ print_valid_utf8(q);
+ }
+ print_ln();
+ /*tex Print |n| spaces to begin line 2. */
+ for (q = 1; q <= n; q++) {
+ print_char(' ');
+ }
+ 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_valid_utf8(q);
+ if (m + n > error_line)
+ tprint("...");
+ incr(nn);
+ }
+ } else if (nn == error_context_lines_par) {
+ tprint_nl("...");
+ incr(nn);
+ /*tex Omitted if |error_context_lines_par<0|. */
+ }
+ if (bottom_line)
+ break;
+ decr(base_ptr);
+ }
+ /*tex Restore the original state. */
+ cur_input = input_stack[input_ptr];
+}
+
+/*tex
+
+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).
+
+Enter a new input level, save the old:
+
+*/
+
+# define pop_input() \
+ cur_input=input_stack[--input_ptr]
+
+# define push_input() \
+ if (input_ptr > max_in_stack) { \
+ max_in_stack = input_ptr; \
+ if (input_ptr == stack_size) \
+ overflow("input stack size", (unsigned) stack_size); \
+ } \
+ input_stack[input_ptr] = cur_input; \
+ nofilter = false; \
+ incr(input_ptr);
+
+/*tex
+
+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|.
+
+*/
+
+void begin_token_list(halfword p, quarterword t)
+{
+ push_input();
+ istate = token_list;
+ istart = p;
+ token_type = (unsigned char) t;
+ if (t >= macro) {
+ /*tex The token list starts with a reference count. */
+ add_token_ref(p);
+ if (t == macro) {
+ param_start = param_ptr;
+ } else {
+ iloc = token_link(p);
+ if (tracing_macros_par > 1) {
+ begin_diagnostic();
+ // tprint_nl("");
+ print_input_level();
+ if (t == mark_text)
+ tprint_esc("mark");
+ else if (t == write_text)
+ tprint_esc("write");
+ else
+ print_cmd_chr(assign_toks_cmd, t - output_text + output_routine_loc);
+ tprint("->");
+ token_show(p);
+ end_diagnostic(false);
+ }
+ }
+ } else {
+ iloc = p;
+ }
+}
+
+/*tex
+
+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@>
+
+*/
+
+void end_token_list(void)
+{
+ /*tex Leave a token-list input level: */
+ if (token_type >= backed_up) {
+ /*tex The token list to be deleted: */
+ if (token_type <= inserted) {
+ flush_list(istart);
+ } else {
+ /*tex Update the reference count: */
+ delete_token_ref(istart);
+ if (token_type == macro) {
+ /*tex 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)");
+ }
+ pop_input();
+ check_interrupt();
+}
+
+/*tex
+
+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@>
+
+*/
+
+/* undoes one token of input */
+
+void back_input(void)
+{
+ /*tex A token list of length one: */
+ halfword p;
+ while ((istate == token_list) && (iloc == null) && (token_type != v_template)) {
+ /*tex Conserve stack space. */
+ end_token_list();
+ }
+ p = get_avail();
+ set_token_info(p, cur_tok);
+ if (cur_tok < right_brace_limit) {
+ if (cur_tok < left_brace_limit)
+ decr(align_state);
+ else
+ incr(align_state);
+ }
+ push_input();
+ /*tex This is |back_list(p)|, without procedure overhead: */
+ istate = token_list;
+ istart = p;
+ token_type = backed_up;
+ iloc = p;
+}
+
+/*tex
+
+Insert token |p| into \TeX's input
+
+*/
+
+void reinsert_token(boolean a, halfword pp)
+{
+ halfword t;
+ t = cur_tok;
+ cur_tok = pp;
+ if (a) {
+ halfword p;
+ p = get_avail();
+ set_token_info(p, cur_tok);
+ set_token_link(p, iloc);
+ iloc = p;
+ istart = p;
+ if (cur_tok < right_brace_limit) {
+ if (cur_tok < left_brace_limit)
+ decr(align_state);
+ else
+ incr(align_state);
+ }
+ } else {
+ back_input();
+ }
+ cur_tok = t;
+}
+
+/*tex
+
+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@>
+
+*/
+
+void begin_file_reading(void)
+{
+ if (in_open == max_in_open)
+ overflow("text input levels", (unsigned) max_in_open);
+ if (first == buf_size)
+ check_buffer_overflow(first);
+ incr(in_open);
+ push_input();
+ iindex = (unsigned short)(in_open);
+ source_filename_stack[iindex] = 0;
+ full_source_filename_stack[iindex] = NULL;
+ eof_seen[iindex] = false;
+ grp_stack[iindex] = cur_boundary;
+ if_stack[iindex] = cond_ptr;
+ line_stack[iindex] = line;
+ istart = first;
+ istate = mid_line;
+ iname = 0;
+ /*tex Variable |terminal_input| is now |true|. */
+ line_catcode_table = DEFAULT_CAT_TABLE;
+ line_partial = false;
+ /*tex Prepare terminal input \SYNCTEX\ information. */
+ synctex_tag = 0;
+}
+
+/*tex
+
+Conversely, the variables must be downdated when such a level of input is
+finished:
+
+*/
+
+void end_file_reading(void)
+{
+ first = istart;
+ line = line_stack[iindex];
+ if ((iname >= 18) && (iname <= 20)) {
+ pseudo_close();
+ } else if (iname == 21) {
+ luacstring_close(iindex);
+ } else if (iname > 17) {
+ /*tex Forget it. */
+ lua_a_close_in(cur_file, 0);
+ source_filename_stack[iindex] = 0;
+ if (full_source_filename_stack[iindex] != NULL) {
+ free(full_source_filename_stack[iindex]);
+ full_source_filename_stack[iindex] = NULL;
+ }
+ }
+ pop_input();
+ decr(in_open);
+}
+
+/*tex
+
+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.
+
+*/
+void clear_for_error_prompt(void)
+{
+ while ((istate != token_list) && terminal_input && (input_ptr > 0) && (iloc > ilimit)) {
+ end_file_reading();
+ }
+ print_ln();
+ clear_terminal();
+}
+
+/*tex
+
+To get \TeX's whole input mechanism going, we perform the following actions.
+
+*/
+
+void initialize_inputstack(void)
+{
+ input_ptr = 0;
+ max_in_stack = 0;
+ source_filename_stack[0] = 0;
+
+ full_source_filename_stack[0] = NULL;
+ 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;
+ istate = new_line;
+ istart = 1;
+ iindex = 0;
+ line = 0;
+ iname = 0;
+ nofilter = false;
+ force_eof = false;
+ luacstrings = 0;
+ line_catcode_table = DEFAULT_CAT_TABLE;
+ line_partial = false;
+ align_state = 1000000;
+ if (!init_terminal()) {
+ /*tex |goto final_end|; */
+ exit(EXIT_FAILURE);
+ }
+ /* |init_terminal| has set |loc| and |last| */
+ ilimit = last;
+ first = last + 1;
+}
+
+/*tex
+
+The global variable |pseudo_files| is used to maintain a stack of pseudo files.
+The |pseudo_lines| field of each pseudo file points to a linked list of variable
+size nodes representing lines not yet processed: the |subtype| field contains the
+size of this node, all the following words contain ASCII codes.
+
+If this is really critical code (which it isn't) then we can consider a c stack
+and store a pointer to a line in the line node instead which saves splitting here
+and reconstructing later.
+
+*/
+
+halfword pseudo_files;
+
+static halfword string_to_pseudo(str_number str, int nl)
+{
+ halfword i, r, q = null;
+ unsigned l, len;
+ four_quarters w;
+ int sz;
+ halfword h = new_node(pseudo_file_node, 0);
+ unsigned char *s = str_string(str);
+ len = (unsigned) str_length(str);
+ l = 0;
+ while (l < len) {
+ /*tex start of current line */
+ unsigned m = l;
+ while ((l < len) && (s[l] != nl))
+ l++;
+ sz = (int) (l - m + 7) / 4;
+ if (sz == 1)
+ sz = 2;
+ r = new_node(pseudo_line_node, sz);
+ i = r;
+ while (--sz > 1) {
+ w.b0 = s[m++];
+ w.b1 = s[m++];
+ w.b2 = s[m++];
+ w.b3 = s[m++];
+ varmem[++i].qqqq = w;
+ }
+ w.b0 = (quarterword) (l > m ? s[m++] : ' ');
+ w.b1 = (quarterword) (l > m ? s[m++] : ' ');
+ w.b2 = (quarterword) (l > m ? s[m++] : ' ');
+ w.b3 = (quarterword) (l > m ? s[m] : ' ');
+ varmem[++i].qqqq = w;
+ if (q == null) {
+ pseudo_lines(h) = r;
+ } else {
+ /*tex There is no |prev| node here so no need to couple_nodes! */
+ vlink(q) = r ;
+ }
+ q = r ;
+ if (s[l] == nl)
+ l++;
+ }
+ return h;
+}
+
+/*tex
+
+The |pseudo_start| procedure initiates reading from a pseudo file.
+
+*/
+
+void pseudo_from_string(void)
+{
+ /*tex The string to be converted into a pseudo file: */
+ str_number s;
+ /*tex A helper for list construction: */
+ halfword p;
+ s = make_string();
+ /*tex Convert string |s| into a new pseudo file */
+ p = string_to_pseudo(s, new_line_char_par);
+ vlink(p) = pseudo_files;
+ pseudo_files = p;
+ flush_str(s);
+ /*tex
+ Initiate input from new pseudo file. It sets up |cur_file| and a new level
+ of input
+ */
+ begin_file_reading();
+ line = 0;
+ ilimit = istart;
+ /*tex force line read */
+ iloc = ilimit + 1;
+ if (tracing_scan_tokens_par > 0) {
+ if (term_offset > max_print_line - 3)
+ print_ln();
+ else if ((term_offset > 0) || (file_offset > 0))
+ print_char(' ');
+ iname = 20;
+ tprint("( ");
+ incr(open_parens);
+ update_terminal();
+ } else {
+ iname = 18;
+ }
+ /*tex Prepare pseudo file \SYNCTEX\ information. */
+ synctex_tag = 0;
+}
+
+void pseudo_start(void)
+{
+ int old_setting;
+ scan_general_text();
+ old_setting = selector;
+ selector = new_string;
+ token_show(temp_token_head);
+ selector = old_setting;
+ flush_list(token_link(temp_token_head));
+ str_room(1);
+ pseudo_from_string();
+}
+
+void lua_string_start(void)
+{
+ /*tex Set up |cur_file| and a new level of input: */
+ begin_file_reading();
+ line = 0;
+ ilimit = istart;
+ /*tex Force line read: */
+ iloc = ilimit + 1;
+ iname = 21;
+ luacstring_start(iindex);
+}
+
+/*tex
+
+Here we read a line from the current pseudo file into |buffer|.
+It inputs the next line or returns |false|.
+
+*/
+
+boolean pseudo_input(void)
+{
+ /*tex The current line from pseudo file: */
+ halfword p;
+ /*tex The size of node |p|: */
+ int sz;
+ /*tex Four ASCII codes: */
+ four_quarters w;
+ /*tex The loop index: */
+ halfword r;
+ /*tex cf.\ Matthew 19\thinspace:\thinspace30 */
+ last = first;
+ p = pseudo_lines(pseudo_files);
+ if (p == null) {
+ return false;
+ } else {
+ pseudo_lines(pseudo_files) = vlink(p);
+ sz = subtype(p);
+ if (4 * sz - 3 >= buf_size - last)
+ check_buffer_overflow(last + 4 * sz);
+ last = first;
+ for (r = p + 1; r <= p + sz - 1; r++) {
+ w = varmem[r].qqqq;
+ buffer[last] = (packed_ASCII_code) w.b0;
+ buffer[last + 1] = (packed_ASCII_code) w.b1;
+ buffer[last + 2] = (packed_ASCII_code) w.b2;
+ buffer[last + 3] = (packed_ASCII_code) w.b3;
+ last += 4;
+ }
+ if (last >= max_buf_stack)
+ max_buf_stack = last + 1;
+ while ((last > first) && (buffer[last - 1] == ' '))
+ decr(last);
+ flush_node(p);
+ }
+ return true;
+}
+
+/*tex
+
+When we are done with a pseudo file we `close' it.
+
+*/
+
+void pseudo_close(void)
+{
+ halfword p;
+ p = vlink(pseudo_files);
+ flush_node(pseudo_files);
+ pseudo_files = p;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/inputstack.h b/Build/source/texk/web2c/luatexdir/tex/inputstack.h
new file mode 100644
index 00000000000..0c7669ff3c8
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.h
@@ -0,0 +1,344 @@
+/* inputstack.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef INPUTSTACK_H
+# define INPUTSTACK_H 1
+
+/*
+ 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|.
+*/
+
+typedef struct in_state_record {
+ halfword start_field;
+ halfword loc_field;
+ halfword limit_field;
+ halfword name_field;
+ int synctex_tag_field; /* stack the tag of the current file */
+ unsigned short state_field:16;
+ unsigned short index_field:16;
+ signed short cattable_field:16; /* category table used by the current line (see textoken.c) */
+ boolean partial_field:8; /* is the current line partial? (see textoken.c) */
+ boolean nofilter_field:8; /* used by token filtering */
+} in_state_record;
+
+extern in_state_record *input_stack;
+extern int input_ptr;
+extern int max_in_stack;
+extern in_state_record cur_input; /* the ``top'' input state */
+
+# define iloc cur_input.loc_field /* location of first unread character in |buffer| */
+# define istate cur_input.state_field /* current scanner state */
+# define iindex cur_input.index_field /* reference for buffer information */
+# define istart cur_input.start_field /* starting position in |buffer| */
+# define ilimit cur_input.limit_field /* end of current line in |buffer| */
+# define iname cur_input.name_field /* name of the current file */
+# define nofilter cur_input.nofilter_field /* is token filtering explicitly disallowed? */
+# define synctex_tag cur_input.synctex_tag_field /* tag of the current file */
+# define line_catcode_table cur_input.cattable_field
+# define line_partial cur_input.partial_field
+
+/*
+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<=20| indicates that we are reading a pseudo file
+created by the \.{\\scantokens} or \.{\\scantextokens} 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|.
+*/
+
+# define max_char_code 15 /* largest catcode for individual characters */
+
+typedef enum {
+ mid_line = 1, /* |state| code when scanning a line of characters */
+ skip_blanks = 2 + max_char_code, /* |state| code when ignoring blanks */
+ new_line = 3 + max_char_code + max_char_code, /* |state| code at start of line */
+} state_codes;
+
+/*
+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..max_in_open] of integer|\unskip'
+by analogy with |line_stack|.
+@^system dependencies@>
+*/
+
+# define terminal_input (iname==0) /* are we reading from the terminal? */
+# define cur_file input_file[iindex] /* the current |alpha_file| variable */
+
+extern int in_open;
+extern int open_parens;
+extern alpha_file *input_file;
+extern int line;
+extern int *line_stack;
+extern str_number *source_filename_stack;
+extern char **full_source_filename_stack;
+
+/*
+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.
+*/
+
+typedef enum {
+ skipping = 1, /* |scanner_status| when passing conditional text */
+ defining = 2, /* |scanner_status| when reading a macro definition */
+ matching = 3, /* |scanner_status| when reading macro arguments */
+ aligning = 4, /* |scanner_status| when reading an alignment preamble */
+ absorbing = 5, /* |scanner_status| when reading a balanced text */
+} scanner_states;
+
+extern int scanner_status;
+extern pointer warning_index;
+extern pointer def_ref;
+
+extern void runaway(void);
+
+/*
+However, the 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|.
+*/
+
+# define token_list 0 /* |state| code when scanning a token list */
+# define token_type iindex /* type of current token list */
+# define param_start ilimit /* base of macro parameters in |param_stack| */
+
+
+typedef enum {
+ parameter = 0, /* |token_type| code for parameter */
+ u_template = 1, /* |token_type| code for \<u_j> template */
+ v_template = 2, /* |token_type| code for \<v_j> template */
+ backed_up = 3, /* |token_type| code for text to be reread */
+ inserted = 4, /* |token_type| code for inserted texts */
+ macro = 5, /* |token_type| code for defined control sequences */
+ output_text = 6, /* |token_type| code for output routines */
+ every_par_text = 7, /* |token_type| code for \.{\\everypar} */
+ every_math_text = 8, /* |token_type| code for \.{\\everymath} */
+ every_display_text = 9, /* |token_type| code for \.{\\everydisplay} */
+ every_hbox_text = 10, /* |token_type| code for \.{\\everyhbox} */
+ every_vbox_text = 11, /* |token_type| code for \.{\\everyvbox} */
+ every_job_text = 12, /* |token_type| code for \.{\\everyjob} */
+ every_cr_text = 13, /* |token_type| code for \.{\\everycr} */
+ mark_text = 14, /* |token_type| code for \.{\\topmark}, etc. */
+ every_eof_text = 15, /* |token_type| code for \.{\\everyeof} */
+ write_text = 16, /* |token_type| code for \.{\\write} */
+ local_text = 17, /* |token_type| code for special purposed */
+} token_types;
+
+extern pointer *param_stack;
+extern int param_ptr;
+extern int max_param_stack;
+
+extern int align_state;
+
+extern int base_ptr;
+
+extern void show_context(void);
+extern void set_trick_count(void);
+
+# define back_list(A) begin_token_list(A,backed_up) /* backs up a simple token list */
+# define ins_list(A) begin_token_list(A,inserted) /* inserts a simple token list */
+
+extern void begin_token_list(halfword p, quarterword t);
+extern void end_token_list(void);
+extern void back_input(void);
+extern void reinsert_token(boolean a, halfword pp);
+extern void begin_file_reading(void);
+extern void end_file_reading(void);
+extern void clear_for_error_prompt(void);
+
+extern void initialize_inputstack(void);
+
+extern halfword pseudo_files;
+extern void pseudo_from_string(void);
+extern void pseudo_start(void);
+extern void lua_string_start(void);
+extern boolean pseudo_input(void);
+extern void pseudo_close(void);
+
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.c b/Build/source/texk/web2c/luatexdir/tex/linebreak.c
new file mode 100644
index 00000000000..646a5a771f7
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.c
@@ -0,0 +1,2537 @@
+/*
+
+Copyright 2006-2008 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+ 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
+ the author in 1977, subsequently generalized and improved by the same two
+ people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice
+ \AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the
+ line-breaking problem can be regarded as a special case of the problem of
+ computing the shortest path in an acyclic network. The cited paper includes
+ numerous examples and describes the history of line breaking as it has been
+ practiced by printers through the ages. The present implementation adds two
+ new ideas to the algorithm of 1980: Memory space requirements are
+ considerably reduced by using smaller records for inactive nodes than for
+ active ones, and arithmetic overflow is avoided by using ``delta distances''
+ instead of keeping track of the total distance from the beginning of the
+ paragraph to the current point.
+
+ The |line_break| procedure should be invoked only in horizontal mode; it
+ leaves that mode and places its output into the current vlist of the
+ enclosing vertical mode (or internal vertical mode). There is one explicit
+ parameter: |d| is true for partial paragraphs preceding display math mode; in
+ this case the amount of additional penalty inserted before the final line is
+ |display_widow_penalty| instead of |widow_penalty|.
+
+ There are also a number of implicit parameters: The hlist to be broken starts
+ at |vlink(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} are
+ 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.
+
+*/
+
+/*tex The |hlist_node| for the last line of the new paragraph: */
+
+halfword just_box;
+
+/*tex
+
+ In it's complete form, |line_break| is a rather lengthy procedure---sort of a
+ small world unto itself---we must build it up little by little. Below you see
+ only the general outline.
+
+ The main task performed here 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.
+
+ That code assumes that a |glue_node| and a |penalty_node| occupy the same
+ number of |mem|~words.
+
+ Most other processing is delegated to external functions.
+
+*/
+
+void line_break(boolean d, int line_break_context)
+{
+ /*tex Main direction of paragraph: */
+ int paragraph_dir = 0;
+ halfword final_par_glue;
+ halfword start_of_par;
+ int callback_id;
+ /*tex this is for over/underfull box messages */
+ pack_begin_line = cur_list.ml_field;
+ alink(temp_head) = null;
+ vlink(temp_head) = vlink(cur_list.head_field);
+ new_hyphenation(temp_head, cur_list.tail_field);
+ cur_list.tail_field = new_ligkern(temp_head, cur_list.tail_field);
+ if (is_char_node(cur_list.tail_field)) {
+ tail_append(new_penalty(inf_penalty,line_penalty));
+ } else if (type(cur_list.tail_field) != glue_node) {
+ tail_append(new_penalty(inf_penalty,line_penalty));
+ } else {
+ halfword t = alink(cur_list.tail_field);
+ flush_node(cur_list.tail_field);
+ cur_list.tail_field = t;
+ tail_append(new_penalty(inf_penalty,line_penalty));
+ }
+ final_par_glue = new_param_glue(par_fill_skip_code);
+ couple_nodes(cur_list.tail_field, final_par_glue);
+ cur_list.tail_field = vlink(cur_list.tail_field);
+ lua_node_filter(pre_linebreak_filter_callback, line_break_context, temp_head, addressof(cur_list.tail_field));
+ last_line_fill = cur_list.tail_field;
+ pop_nest();
+ start_of_par = cur_list.tail_field;
+ callback_id = callback_defined(linebreak_filter_callback);
+ if (callback_id > 0) {
+ callback_id = lua_linebreak_callback(d, temp_head, addressof(cur_list.tail_field));
+ if (callback_id > 0) {
+ /*tex find the correct value for the |just_box| */
+ halfword box_search = cur_list.tail_field;
+ just_box = null;
+ if (box_search != null) {
+ do {
+ if (type(box_search) == hlist_node) {
+ just_box = box_search;
+ }
+ box_search = vlink(box_search);
+ } while (box_search != null);
+ }
+ if (just_box == null) {
+ help3(
+ "A linebreaking routine should return a non-empty list of nodes",
+ "and at least one of those has to be a \\hbox.",
+ "Sorry, I cannot recover from this."
+ );
+ print_err("Invalid linebreak_filter");
+ succumb();
+ }
+ } else {
+ if (tracing_paragraphs_par > 0) {
+ begin_diagnostic();
+ print_int(line);
+ end_diagnostic(true);
+ }
+ }
+ }
+ if (callback_id == 0) {
+ if ((!is_char_node(vlink(temp_head))) && ((type(vlink(temp_head)) == local_par_node))) {
+ paragraph_dir = local_par_dir(vlink(temp_head));
+ } else {
+ confusion("weird par dir");
+ }
+ ext_do_line_break(
+ paragraph_dir,
+ pretolerance_par,
+ tracing_paragraphs_par,
+ tolerance_par,
+ emergency_stretch_par,
+ looseness_par,
+ adjust_spacing_par,
+ par_shape_par_ptr,
+ adj_demerits_par,
+ protrude_chars_par,
+ line_penalty_par,
+ last_line_fit_par,
+ double_hyphen_demerits_par,
+ final_hyphen_demerits_par,
+ hang_indent_par,
+ hsize_par,
+ hang_after_par,
+ left_skip_par,
+ right_skip_par,
+ inter_line_penalties_par_ptr,
+ inter_line_penalty_par,
+ club_penalty_par,
+ club_penalties_par_ptr,
+ (d ? display_widow_penalties_par_ptr : widow_penalties_par_ptr),
+ (d ? display_widow_penalty_par : widow_penalty_par),
+ broken_penalty_par,
+ final_par_glue
+ );
+ }
+ lua_node_filter(post_linebreak_filter_callback, line_break_context, start_of_par, addressof(cur_list.tail_field));
+ pack_begin_line = 0;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+#define check_shrinkage(a) \
+ if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \
+ a=finite_shrink((a))
+
+/*tex Have we complained about infinite shrinkage? */
+
+static boolean no_shrink_error_yet;
+
+/*tex Recovers from infinite shrinkage. */
+
+static halfword finite_shrink(halfword p)
+{
+ const char *hlp[] = {
+ "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.",
+ NULL
+ };
+ if (no_shrink_error_yet) {
+ no_shrink_error_yet = false;
+ tex_error("Infinite glue shrinkage found in a paragraph", hlp);
+ }
+ shrink_order(p) = normal;
+ return p;
+}
+
+/*tex
+
+ 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|.
+
+*/
+
+/*tex is this our second attempt to break this paragraph? */
+
+static boolean second_pass;
+
+/*tex is this our final attempt to break this paragraph? */
+
+static boolean final_pass;
+
+/*tex maximum badness on feasible lines */
+
+static int threshold;
+
+/*tex
+
+ The maximum fill level for |hlist_stack|. Maybe good if larger than |2 *
+ max_quarterword|, so that box nesting level would overflow first.
+
+*/
+
+#define max_hlist_stack 512
+
+/*tex stack for |find_protchar_left()| and |find_protchar_right()| */
+
+static halfword hlist_stack[max_hlist_stack];
+
+/*tex fill level for |hlist_stack| */
+
+static short hlist_stack_level = 0;
+
+static void push_node(halfword p)
+{
+ if (hlist_stack_level >= max_hlist_stack)
+ normal_error("push_node","stack overflow");
+ hlist_stack[hlist_stack_level++] = p;
+}
+
+static halfword pop_node(void)
+{
+ if (hlist_stack_level <= 0) {
+ /*tex This can point to some bug. */
+ normal_error("pop_node","stack underflow (internal error)");
+ }
+ return hlist_stack[--hlist_stack_level];
+}
+
+/*tex maximal stretch ratio of expanded fonts */
+
+static int max_stretch_ratio = 0;
+
+/*tex maximal shrink ratio of expanded fonts */
+
+static int max_shrink_ratio = 0;
+
+/*tex the current step of expanded fonts */
+
+static int cur_font_step = 0;
+
+static boolean check_expand_pars(internal_font_number f)
+{
+ int m;
+ if ((font_step(f) == 0) || ((font_max_stretch(f) == 0) && (font_max_shrink(f) == 0)))
+ return false;
+ if (cur_font_step < 0)
+ cur_font_step = font_step(f);
+ else if (cur_font_step != font_step(f))
+ normal_error("font expansion","using fonts with different step of expansion in one paragraph is not allowed");
+ m = font_max_stretch(f);
+ if (m != 0) {
+ if (max_stretch_ratio < 0)
+ max_stretch_ratio = m;
+ else if (max_stretch_ratio != m)
+ normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed");
+ }
+ m = font_max_shrink(f);
+ if (m != 0) {
+ if (max_shrink_ratio < 0)
+ max_shrink_ratio = -m;
+ else if (max_shrink_ratio != -m)
+ normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed");
+ }
+ return true;
+}
+
+/*tex Search left to right from list head |l|, returns 1st non-skipable item */
+
+halfword find_protchar_left(halfword l, boolean d)
+{
+ halfword t;
+ boolean run;
+ boolean done = false ;
+ while ((vlink(l) != null) && (type(l) == hlist_node) && zero_dimensions(l) && (list_ptr(l) == null)) {
+ /*tex For paragraph start with \.{\\parindent} = 0pt or any empty hbox. */
+ l = vlink(l);
+ done = true ;
+ }
+ if ((!done) && (type(l) == local_par_node)) {
+ l = vlink(l);
+ done = true ;
+ }
+ if ((!done) && d) {
+ while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) {
+ /*tex standard discardables at line break, \TeX book, p 95 */
+ l = vlink(l);
+ }
+ }
+ if (type(l) != glyph_node) {
+ hlist_stack_level = 0;
+ run = true;
+ do {
+ t = l;
+ while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) {
+ push_node(l);
+ l = list_ptr(l);
+ }
+ while (run && cp_skipable(l)) {
+ while ((vlink(l) == null) && (hlist_stack_level > 0)) {
+ /*tex Don't visit this node again. */
+ l = pop_node();
+ run = false;
+ }
+ if ((vlink(l) != null) && (type(l) == boundary_node) && (subtype(l) == protrusion_boundary) &&
+ ((boundary_value(l) == 1) || (boundary_value(l) == 3))) {
+ /*tex Skip next node. */
+ l = vlink(l);
+ }
+ if (vlink(l) != null) {
+ l = vlink(l);
+ } else if (hlist_stack_level == 0) {
+ run = false;
+ }
+ }
+ } while (t != l);
+ }
+ return l;
+}
+
+/*tex
+
+ Search right to left from list tail |r| to head |l|, returns 1st non-skipable
+ item.
+
+*/
+
+halfword find_protchar_right(halfword l, halfword r)
+{
+ halfword t;
+ boolean run = true;
+ if (r == null)
+ return null;
+ hlist_stack_level = 0;
+ do {
+ t = r;
+ while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) {
+ push_node(l);
+ push_node(r);
+ l = list_ptr(r);
+ r = l;
+ while (vlink(r) != null) {
+ halfword s = r;
+ r = vlink(r);
+ alink(r) = s;
+ }
+ }
+ while (run && cp_skipable(r)) {
+ while ((r == l) && (hlist_stack_level > 0)) {
+ /*tex Don't visit this node again. */
+ r = pop_node();
+ l = pop_node();
+ }
+ if ((r != l) && (r != null)) {
+ if ((alink(r) != null) && (type(r) == boundary_node) && (subtype(r) == protrusion_boundary) &&
+ ((boundary_value(r) == 2) || (boundary_value(r) == 3))) {
+ /*tex Skip next node. */
+ r = alink(r);
+ }
+ if (alink(r) != null) {
+ r = alink(r);
+ } else {
+ /*tex This is the input: \.{\\leavevmode\\penalty-10000\\penalty-10000} (bug \#268). */
+ run = false;
+ }
+ } else if ((r == l) && (hlist_stack_level == 0))
+ run = false;
+ }
+ } while (t != r);
+ return r;
+}
+
+#define left_pw(a) char_pw((a), left_side)
+#define right_pw(a) char_pw((a), right_side)
+
+/*tex
+
+ 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|.
+
+*/
+
+typedef enum {
+ /*tex fitness classification for lines stretching more than their stretchability */
+ very_loose_fit = 0,
+ /*tex fitness classification for lines stretching 0.5 to 1.0 of their stretchability */
+ loose_fit,
+ /*tex fitness classification for all other lines */
+ decent_fit,
+ /*tex fitness classification for lines shrinking 0.5 to 1.0 of their shrinkability */
+ tight_fit
+} fitness_value;
+
+/*tex
+
+ 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:
+
+ \startitemize[n]
+
+ \startitem
+ |vlink| points to the next node in the list of active nodes; the last
+ active node has |vlink=active|.
+ \stopitem
+
+ \startitem
+ |break_node| points to the passive node associated with this
+ breakpoint.
+ \stopitem
+
+ \startitem
+ |line_number| is the number of the line that follows this breakpoint.
+ \stopitem
+
+ \startitem
+ |fitness| is the fitness classification of the line ending at this
+ breakpoint.
+ \stopitem
+
+ \startitem
+ |type| is either |hyphenated_node| or |unhyphenated_node|, depending
+ on whether this breakpoint is a |disc_node|.
+ \stopitem
+
+ \startitem
+ |total_demerits| is the minimum possible sum of demerits over all
+ lines leading from the beginning of the paragraph to this breakpoint.
+ \stopitem
+
+ \stopitemize
+
+ The value of |vlink(active)| points to the first active node on a vlinked
+ 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.
+
+*/
+
+void initialize_active(void)
+{
+ type(active) = hyphenated_node;
+ line_number(active) = max_halfword;
+ /*tex The |subtype| is never examined. */
+ subtype(active) = 0;
+}
+
+/*tex
+
+ The passive node for a given breakpoint contains eight fields:
+
+ \startitemize
+
+ \startitem
+ |vlink| points to the passive node created just before this one, if
+ any, otherwise it is |null|.
+ \stopitem
+
+ \startitem
+ |cur_break| points to the position of this breakpoint in the
+ horizontal list for the paragraph being broken.
+ \stopitem
+
+ \startitem
+ |prev_break| points to the passive node that should precede this one
+ in an optimal path to this breakpoint.
+ \stopitem
+
+ \startitem
+ |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.)
+ \stopitem
+
+ \startitem
+ |passive_pen_inter| holds the current \.{\\localinterlinepenalty}
+ \stopitem
+
+ \startitem
+ |passive_pen_broken| holds the current \.{\\localbrokenpenalty}
+ \stopitem
+
+ \stopitemize
+
+ 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.
+
+*/
+
+/*tex most recent node on passive list */
+
+static halfword passive;
+
+/*tex most recent node that has been printed */
+
+static halfword printed_node;
+
+/*tex the number of passive nodes allocated on this pass */
+
+static halfword pass_number;
+
+/*tex
+
+ 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 |vlink(p)=q| and |vlink(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 seven 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,
+ sfi, fil, fill, and filll appear in |mem[q+2..q+6].sc|; and the shrink
+ difference appears in |mem[q+7].sc|. The |subtype| field of a delta node is
+ not used.
+
+ Actually, we have two more fields that are used by |pdftex|.
+
+ As the algorithm runs, it maintains a set of seven 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 seven 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:
+
+ \starttyping
+ for k := 1 to 7 do
+ cur_active_width[k] := cur_active_width[k] + mem[q+k].sc|};
+ \stoptyping
+
+ and we want to do this without the overhead of |for| loops. The |do_all_six|
+ macro makes such six-tuples convenient.
+
+*/
+
+/*tex distance from first active node to~|cur_p| */
+
+static scaled active_width[10] = { 0 };
+
+/*tex length of an ``empty'' line */
+
+static scaled background[10] = { 0 };
+
+/*tex length being computed after current break */
+
+static scaled break_width[10] = { 0 };
+
+/*tex Make |auto_breaking| accessible out of |line_break|: */
+
+static boolean auto_breaking;
+
+/*tex
+
+ 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.
+
+ 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_node| or
+ |unhyphenated_node|, depending on whether or not the current break is at a
+ |disc_node|. The end of a paragraph is also regarded as `|hyphenated_node|';
+ this case is distinguishable by the condition |cur_p=null|.
+
+*/
+
+/*tex running \.{\\localinterlinepenalty} */
+
+static int internal_pen_inter;
+
+/*tex running \.{\\localbrokenpenalty} */
+
+static int internal_pen_broken;
+
+/*tex running \.{\\localleftbox} */
+
+static halfword internal_left_box;
+
+/*tex running \.{\\localleftbox} width */
+
+static int internal_left_box_width;
+
+/*tex running \.{\\localleftbox} */
+
+static halfword init_internal_left_box;
+
+/*tex running \.{\\localleftbox} width */
+
+static int init_internal_left_box_width;
+
+/*tex running \.{\\localrightbox} */
+
+static halfword internal_right_box;
+
+/*tex running \.{\\localrightbox} width */
+
+static int internal_right_box_width;
+
+/*tex the length of discretionary material preceding a break */
+
+static scaled disc_width[10] = { 0 };
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex best total demerits known for current line class and position, given the fitness */
+
+static int minimal_demerits[4];
+
+/*tex best total demerits known for current line class and position */
+
+static int minimum_demerits;
+
+/*tex how to achieve |minimal_demerits| */
+
+static halfword best_place[4];
+
+/*tex corresponding line number */
+
+static halfword best_pl_line[4];
+
+/*tex
+
+ 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 |vinfo| 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.
+
+*/
+
+/*tex line numbers |>easy_line| are equivalent in break nodes */
+
+static halfword easy_line;
+
+/*tex line numbers |>last_special_line| all have the same width */
+
+static halfword last_special_line;
+
+/*tex the width of all lines |<=last_special_line|, if no \.{\\parshape} has been specified */
+
+static scaled first_width;
+
+/*tex the width of all lines |>last_special_line| */
+
+static scaled second_width;
+
+/*tex left margin to go with |first_width| */
+
+static scaled first_indent;
+
+/*tex left margin to go with |second_width| */
+
+static scaled second_indent;
+
+/*tex use this passive node and its predecessors */
+
+static halfword best_bet;
+
+/*tex the demerits associated with |best_bet| */
+
+static int fewest_demerits;
+
+/*tex line number following the last line of the new paragraph */
+
+static halfword best_line;
+
+/*tex the difference between |line_number(best_bet)| and the optimum |best_line| */
+
+static int actual_looseness;
+
+/*tex the difference between the current line number and the optimum |best_line| */
+
+static int line_diff;
+
+/*tex
+
+ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, |rule_node|,
+ |ins_node|, |mark_node|, |adjust_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.
+
+*/
+
+#define do_all_six(a) a(1);a(2);a(3);a(4);a(5);a(6);a(7)
+#define do_seven_eight(a) if (adjust_spacing > 1) { a(8);a(9); }
+#define do_all_eight(a) do_all_six(a); do_seven_eight(a)
+#define do_one_seven_eight(a) a(1); do_seven_eight(a)
+
+#define store_background(a) {active_width[a]=background[a];}
+
+#define kern_break() { \
+ if ((!is_char_node(vlink(cur_p))) && auto_breaking) \
+ if (type(vlink(cur_p))==glue_node) \
+ ext_try_break(\
+ 0, \
+ unhyphenated_node, \
+ line_break_dir, \
+ adjust_spacing, \
+ par_shape_ptr, \
+ adj_demerits, \
+ tracing_paragraphs, \
+ protrude_chars, \
+ line_penalty, \
+ last_line_fit, \
+ double_hyphen_demerits, \
+ final_hyphen_demerits, \
+ first_p, \
+ cur_p \
+ ); \
+ if (type(cur_p)!=math_node) \
+ active_width[1] += width(cur_p); \
+ else \
+ active_width[1] += surround(cur_p); \
+}
+
+#define clean_up_the_memory() { \
+ q=vlink(active); \
+ while (q!=active) { \
+ cur_p = vlink(q); \
+ if (type(q)==delta_node) \
+ flush_node(q); \
+ else \
+ flush_node(q); \
+ q = cur_p; \
+ } \
+ q = passive; \
+ while (q!=null) { \
+ cur_p = vlink(q); \
+ flush_node(q); \
+ q = cur_p; \
+ } \
+}
+
+/*tex special algorithm for last line of paragraph? */
+
+static boolean do_last_line_fit;
+
+/*tex infinite stretch components of |par_fill_skip| */
+
+static scaled fill_width[4];
+
+/*tex |shortfall| corresponding to |minimal_demerits| */
+
+static scaled best_pl_short[4];
+
+/*tex corresponding glue stretch or shrink */
+
+static scaled best_pl_glue[4];
+
+#define reset_disc_width(a) disc_width[(a)] = 0
+
+#define add_disc_width_to_break_width(a) break_width[(a)] += disc_width[(a)]
+#define sub_disc_width_from_active_width(a) active_width[(a)] -= disc_width[(a)]
+
+#define add_char_shrink(a,b) a += char_shrink((b))
+#define add_char_stretch(a,b) a += char_stretch((b))
+#define sub_char_shrink(a,b) a -= char_shrink((b))
+#define sub_char_stretch(a,b) a -= char_stretch((b))
+
+#define add_kern_shrink(a,b) a += kern_shrink((b))
+#define add_kern_stretch(a,b) a += kern_stretch((b))
+#define sub_kern_shrink(a,b) a -= kern_shrink((b))
+#define sub_kern_stretch(a,b) a -= kern_stretch((b))
+
+/*tex
+
+ This function is used to add the width of a list of nodes (from a
+ discretionary) to one of the width arrays.
+
+ Replacement texts and discretionary texts are supposed to contain only
+ character nodes, kern nodes, and box or rule nodes.
+
+*/
+
+#define bad_node_in_disc_error(p) { \
+ if (type(p) == whatsit_node) { \
+ formatted_error("linebreak","invalid node with type %s and subtype %i found in discretionary",node_data[type(p)].name,subtype(p)); \
+ } else { \
+ formatted_error("linebreak","invalid node with type %s found in discretionary",node_data[type(p)].name); \
+ } \
+}
+
+static void add_to_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
+{
+ while (s != null) {
+ if (is_char_node(s)) {
+ widths[1] += pack_width(line_break_dir, dir_TRT, s, true);
+ if ((adjust_spacing > 1) && check_expand_pars(font(s))) {
+ set_prev_char_p(s);
+ add_char_stretch(widths[8], s);
+ add_char_shrink(widths[9], s);
+ };
+ } else {
+ switch (type(s)) {
+ case hlist_node:
+ case vlist_node:
+ widths[1] += pack_width(line_break_dir, box_dir(s), s, false);
+ break;
+ case kern_node:
+ if ((adjust_spacing == 2) && (subtype(s) == normal)) {
+ add_kern_stretch(widths[8], s);
+ add_kern_shrink(widths[9], s);
+ }
+ /*tex fall through */
+ case rule_node:
+ widths[1] += width(s);
+ break;
+ case disc_node:
+ break;
+ default:
+ bad_node_in_disc_error(s);
+ break;
+ }
+ }
+ s = vlink(s);
+ }
+}
+
+/*tex
+
+ This function is used to substract the width of a list of nodes (from a
+ discretionary) from one of the width arrays. It is used only once, but
+ deserves it own function because of orthogonality with the |add_to_widths|
+ function.
+
+*/
+
+static void sub_from_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
+{
+ while (s != null) {
+ /*tex Subtract the width of node |s| from |break_width|; */
+ if (is_char_node(s)) {
+ widths[1] -= pack_width(line_break_dir, dir_TRT, s, true);
+ if ((adjust_spacing > 1) && check_expand_pars(font(s))) {
+ set_prev_char_p(s);
+ sub_char_stretch(widths[8], s);
+ sub_char_shrink(widths[9], s);
+ }
+ } else {
+ switch (type(s)) {
+ case hlist_node:
+ case vlist_node:
+ widths[1] -= pack_width(line_break_dir, box_dir(s), s, false);
+ break;
+ case kern_node:
+ if ((adjust_spacing == 2) && (subtype(s) == normal)) {
+ sub_kern_stretch(widths[8], s);
+ sub_kern_shrink(widths[9], s);
+ }
+ /*tex fall through */
+ case rule_node:
+ widths[1] -= width(s);
+ break;
+ case disc_node:
+ break;
+ default:
+ bad_node_in_disc_error(s);
+ break;
+ }
+ }
+ s = vlink(s);
+ }
+}
+
+/*tex
+
+ 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|.
+
+*/
+
+static void compute_break_width(int break_type, int line_break_dir, int adjust_spacing, halfword p)
+{
+ /*tex
+
+ Glue and other 'whitespace' to be skipped after a break; used if
+ unhyphenated, or |post_break==empty|.
+
+ */
+ halfword s = p;
+ if (break_type > unhyphenated_node && p != null) {
+ /*tex
+
+ Compute the discretionary |break_width| values.
+
+ When |p| is a discretionary break, the length of a line ``from |p| to
+ |p|'' has to be defined properly so that the other calculations work
+ out. Suppose that the pre-break text at |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 |p| to |q| will be computed as
+ $\gamma+\beta(q)-\alpha(|p|)$, where $\beta(q)=\beta(|p|)-l_0+l$. The
+ actual length will be the background plus $l_1$, so the length from
+ |p| to |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|.
+
+ In case of nested discretionaries, we always follow the no-break
+ path, as we are talking about the breaking on {\it this} position.
+
+ */
+ sub_from_widths(vlink_no_break(p), line_break_dir, adjust_spacing, break_width);
+ add_to_widths(vlink_post_break(p), line_break_dir, adjust_spacing, break_width);
+ do_one_seven_eight(add_disc_width_to_break_width);
+ if (vlink_post_break(p) == null) {
+ /*tex no |post_break|: 'skip' any 'whitespace' following */
+ s = vlink(p);
+ } else {
+ s = null;
+ }
+ }
+ while (s != null) {
+ switch (type(s)) {
+ case math_node:
+ /*tex begin mathskip code */
+ if (glue_is_zero(s)) {
+ break_width[1] -= surround(s);
+ break;
+ } else {
+ /*tex fall through */
+ }
+ /*tex end mathskip code */
+ case glue_node:
+ /*tex Subtract glue from |break_width|; */
+ break_width[1] -= width(s);
+ break_width[2 + stretch_order(s)] -= stretch(s);
+ break_width[7] -= shrink(s);
+ break;
+ case penalty_node:
+ break;
+ case kern_node:
+ if (subtype(s) != explicit_kern && subtype(s) != italic_kern)
+ return;
+ else
+ break_width[1] -= width(s);
+ break;
+ default:
+ return;
+ };
+ s = vlink(s);
+ }
+}
+
+static void print_break_node(halfword q, fitness_value fit_class, quarterword break_type, halfword cur_p)
+{
+ /*tex Print a symbolic description of the new break node. */
+ tprint_nl("@@");
+ print_int(serial(passive));
+ tprint(": line ");
+ print_int(line_number(q) - 1);
+ print_char('.');
+ print_int(fit_class);
+ if (break_type == hyphenated_node)
+ print_char('-');
+ tprint(" t=");
+ print_int(total_demerits(q));
+ if (do_last_line_fit) {
+ /*tex Print additional data in the new active node. */
+ tprint(" s=");
+ print_scaled(active_short(q));
+ if (cur_p == null)
+ tprint(" a=");
+ else
+ tprint(" g=");
+ print_scaled(active_glue(q));
+ }
+ tprint(" -> @");
+ if (prev_break(passive) == null)
+ print_char('0');
+ else
+ print_int(serial(prev_break(passive)));
+}
+
+static void print_feasible_break(halfword cur_p, pointer r, halfword b, int pi, int d, boolean artificial_demerits)
+{
+ /*tex
+
+ Print a symbolic description of this feasible break.
+
+ */
+ if (printed_node != cur_p) {
+ /*tex
+
+ Print the list between |printed_node| and |cur_p|, then set
+ |printed_node:=cur_p|.
+
+ */
+ tprint_nl("");
+ if (cur_p == null) {
+ short_display(vlink(printed_node));
+ } else {
+ halfword save_link = vlink(cur_p);
+ vlink(cur_p) = null;
+ tprint_nl("");
+ short_display(vlink(printed_node));
+ vlink(cur_p) = save_link;
+ }
+ printed_node = cur_p;
+ }
+ tprint_nl("@");
+ if (cur_p == null) {
+ tprint_esc("par");
+ } else if (type(cur_p) != glue_node) {
+ if (type(cur_p) == penalty_node)
+ tprint_esc("penalty");
+ else if (type(cur_p) == disc_node)
+ tprint_esc("discretionary");
+ else if (type(cur_p) == kern_node)
+ tprint_esc("kern");
+ else
+ tprint_esc("math");
+ }
+ tprint(" via @");
+ if (break_node(r) == null)
+ print_char('0');
+ else
+ print_int(serial(break_node(r)));
+ tprint(" b=");
+ if (b > inf_bad)
+ print_char('*');
+ else
+ print_int(b);
+ tprint(" p=");
+ print_int(pi);
+ tprint(" d=");
+ if (artificial_demerits)
+ print_char('*');
+ else
+ print_int(d);
+}
+
+#define add_disc_width_to_active_width(a) active_width[a] += disc_width[a]
+#define update_width(a) cur_active_width[a] += varmem[(r+(a))].cint
+
+#define set_break_width_to_background(a) break_width[a]=background[(a)]
+
+#define convert_to_break_width(a) \
+ varmem[(prev_r+(a))].cint = varmem[(prev_r+(a))].cint-cur_active_width[(a)]+break_width[(a)]
+
+#define store_break_width(a) active_width[(a)]=break_width[(a)]
+
+#define new_delta_to_break_width(a) \
+ varmem[(q+(a))].cint=break_width[(a)]-cur_active_width[(a)]
+
+#define new_delta_from_break_width(a) \
+ varmem[(q+(a))].cint=cur_active_width[(a)]-break_width[(a)]
+
+#define copy_to_cur_active(a) cur_active_width[(a)]=active_width[(a)]
+
+#define combine_two_deltas(a) varmem[(prev_r+(a))].cint += varmem[(r+(a))].cint
+#define downdate_width(a) cur_active_width[(a)] -= varmem[(prev_r+(a))].cint
+#define update_active(a) active_width[(a)]+=varmem[(r+(a))].cint
+
+#define total_font_stretch cur_active_width[8]
+#define total_font_shrink cur_active_width[9]
+
+#define cal_margin_kern_var(a) { \
+ character(cp) = character((a)); \
+ font(cp) = font((a)); \
+ do_subst_font(cp, 1000); \
+ if (font(cp) != font((a))) \
+ margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \
+ font(cp) = font((a)); \
+ do_subst_font(cp, -1000); \
+ if (font(cp) != font((a))) \
+ margin_kern_shrink += (left_pw(cp) - left_pw((a))); \
+}
+
+static void ext_try_break(
+ int pi,
+ quarterword break_type,
+ int line_break_dir,
+ int adjust_spacing,
+ int par_shape_ptr,
+ int adj_demerits,
+ int tracing_paragraphs,
+ int protrude_chars,
+ int line_penalty,
+ int last_line_fit,
+ int double_hyphen_demerits,
+ int final_hyphen_demerits, halfword first_p, halfword cur_p
+)
+{
+ /*tex runs through the active list */
+ pointer r;
+ scaled margin_kern_stretch;
+ scaled margin_kern_shrink;
+ halfword lp, rp, cp;
+ /*tex stays a step behind |r| */
+ halfword prev_r = active;
+ /*tex a step behind |prev_r|, if |type(prev_r)=delta_node| */
+ halfword prev_prev_r = null;
+ /*tex maximum line number in current equivalence class of lines */
+ halfword old_l = 0;
+ /*tex have we found a feasible break at |cur_p|? */
+ boolean no_break_yet = true;
+ /*tex points to a new node being created */
+ halfword q;
+ /*tex line number of current active node */
+ halfword l;
+ /*tex should node |r| remain in the active list? */
+ boolean node_r_stays_active;
+ /*tex the current line will be justified to this width */
+ scaled line_width = 0;
+ /*tex possible fitness class of test line */
+ fitness_value fit_class;
+ /*tex badness of test line */
+ halfword b;
+ /*tex demerits of test line */
+ int d;
+ /*tex has |d| been forced to zero? */
+ boolean artificial_demerits;
+ /*tex used in badness calculations */
+ scaled shortfall;
+ /*tex glue stretch or shrink of test line, adjustment for last line */
+ scaled g = 0;
+ /*tex distance from current active node */
+ scaled cur_active_width[10] = { 0 };
+ /*tex Make sure that |pi| is in the proper range; */
+ if (pi >= inf_penalty) {
+ /*tex this breakpoint is inhibited by infinite penalty */
+ return;
+ } else if (pi <= -inf_penalty) {
+ /*tex this breakpoint will be forced */
+ pi = eject_penalty;
+ }
+
+ do_all_eight(copy_to_cur_active);
+
+ while (1) {
+ r = vlink(prev_r);
+ /*tex
+
+ If node |r| is of type |delta_node|, update |cur_active_width|, set
+ |prev_r| and |prev_prev_r|, then |goto continue|. The following code
+ uses the fact that |type(active)<>delta_node|.
+
+ */
+ if (type(r) == delta_node) {
+ /*tex implicit */
+ do_all_eight(update_width);
+ prev_prev_r = prev_r;
+ prev_r = r;
+ continue;
+ }
+ /*tex
+
+ If a line number class has ended, create new active nodes for the
+ best feasible breaks in that class; then |return| if |r=active|,
+ otherwise compute the new |line_width|.
+
+ 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=active| and |line_number(active)>old_l|.
+
+ */
+ l = line_number(r);
+ if (l > old_l) {
+ /*tex now we are no longer in the inner loop */
+ if ((minimum_demerits < awful_bad)
+ && ((old_l != easy_line) || (r == active))) {
+ /*tex
+
+ Create new active nodes for the best feasible breaks just
+ found. 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.
+
+ */
+ if (no_break_yet) {
+ no_break_yet = false;
+ do_all_eight(set_break_width_to_background);
+ compute_break_width(break_type, line_break_dir, adjust_spacing, cur_p);
+ }
+ /*tex
+
+ Insert a delta node to prepare for breaks at |cur_p|. We use
+ the fact that |type(active)<>delta_node|.
+
+ */
+ if (type(prev_r) == delta_node) {
+ /*tex modify an existing delta node */
+ do_all_eight(convert_to_break_width);
+ } else if (prev_r == active) {
+ /*tex no delta node needed at the beginning */
+ do_all_eight(store_break_width);
+ } else {
+ q = new_node(delta_node, 0);
+ vlink(q) = r;
+ do_all_eight(new_delta_to_break_width);
+ vlink(prev_r) = q;
+ prev_prev_r = prev_r;
+ prev_r = q;
+ }
+ if (abs(adj_demerits) >= awful_bad - minimum_demerits)
+ minimum_demerits = awful_bad - 1;
+ else
+ minimum_demerits += abs(adj_demerits);
+ for (fit_class = very_loose_fit; fit_class <= tight_fit;
+ fit_class++) {
+ if (minimal_demerits[fit_class] <= minimum_demerits) {
+ /*tex
+
+ Insert a new active node from |best_place[fit_class]|
+ to |cur_p|. When we create an active node, we also
+ create the corresponding passive node.
+
+ */
+ q = new_node(passive_node, 0);
+ vlink(q) = passive;
+ passive = q;
+ cur_break(q) = cur_p;
+ incr(pass_number);
+ serial(q) = pass_number;
+ prev_break(q) = best_place[fit_class];
+ /*tex
+
+ Here we keep track of the subparagraph penalties in
+ the break nodes.
+
+ */
+ passive_pen_inter(q) = internal_pen_inter;
+ passive_pen_broken(q) = internal_pen_broken;
+ passive_last_left_box(q) = internal_left_box;
+ passive_last_left_box_width(q) =
+ internal_left_box_width;
+ if (prev_break(q) != null) {
+ passive_left_box(q) = passive_last_left_box(prev_break(q));
+ passive_left_box_width(q) = passive_last_left_box_width(prev_break(q));
+ } else {
+ passive_left_box(q) = init_internal_left_box;
+ passive_left_box_width(q) = init_internal_left_box_width;
+ }
+ passive_right_box(q) = internal_right_box;
+ passive_right_box_width(q) = internal_right_box_width;
+ q = new_node(break_type, fit_class);
+ break_node(q) = passive;
+ line_number(q) = best_pl_line[fit_class] + 1;
+ total_demerits(q) = minimal_demerits[fit_class];
+ if (do_last_line_fit) {
+ /*tex
+
+ Store additional data in the new active node.
+ Here we save these data in the active node
+ representing a potential line break.
+
+ */
+ active_short(q) = best_pl_short[fit_class];
+ active_glue(q) = best_pl_glue[fit_class];
+ }
+ vlink(q) = r;
+ vlink(prev_r) = q;
+ prev_r = q;
+ if (tracing_paragraphs > 0)
+ print_break_node(q, fit_class, break_type, cur_p);
+ }
+ minimal_demerits[fit_class] = awful_bad;
+ }
+ minimum_demerits = awful_bad;
+ /*tex
+
+ Insert a delta node to prepare for the next active node. When
+ the following code is performed, we will have just inserted
+ at least one active node before |r|, so
+ |type(prev_r)<>delta_node|.
+
+ */
+ if (r != active) {
+ q = new_node(delta_node, 0);
+ vlink(q) = r;
+ do_all_eight(new_delta_from_break_width);
+ vlink(prev_r) = q;
+ prev_prev_r = prev_r;
+ prev_r = q;
+ }
+ }
+ if (r == active)
+ return;
+ /*tex
+
+ Compute the new line width. 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|.
+
+ */
+ if (l > easy_line) {
+ old_l = max_halfword - 1;
+ line_width = second_width;
+ } 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 = varmem[(par_shape_ptr + 2 * l + 1)].cint;
+ }
+ }
+ }
+ /*tex
+
+ If a line number class has ended, create new active nodes for the
+ best feasible breaks in that class; then |return| if |r=active|,
+ otherwise compute the new |line_width|.
+
+ Consider the demerits for a line from |r| to |cur_p|; deactivate node
+ |r| if it should no longer be active; then |goto continue| if a line
+ from |r| to |cur_p| is infeasible, otherwise record a new feasible
+ break.
+
+ */
+ artificial_demerits = false;
+ shortfall = line_width - cur_active_width[1];
+ if (break_node(r) == null)
+ shortfall -= init_internal_left_box_width;
+ else
+ shortfall -= passive_last_left_box_width(break_node(r));
+ shortfall -= internal_right_box_width;
+ if (protrude_chars > 1) {
+ halfword l1, o;
+ l1 = (break_node(r) == null) ? first_p : cur_break(break_node(r));
+ if (cur_p == null) {
+ o = null;
+ } else {
+ o = alink(cur_p);
+ assert(vlink(o) == cur_p);
+ }
+ /*tex
+
+ The disc could be a SELECT subtype, to we might need to get the
+ last character as |pre_break| from either the |pre_break| list
+ (if the previous INIT disc was taken), or the |no_break| (sic)
+ list (if the previous INIT disc was not taken).
+
+ The last characters (hyphenation character) if these two list
+ should always be the same anyway, so we just look at |pre_break|.
+
+ Let's look at the right margin first.
+
+ */
+ if ((cur_p != null) && (type(cur_p) == disc_node) && (vlink_pre_break(cur_p) != null)) {
+ /*tex a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */
+ o = tlink_pre_break(cur_p);
+ } else {
+ o = find_protchar_right(l1, o);
+ }
+ /*tex now the left margin */
+ if ((l1 != null) && (type(l1) == disc_node) && (vlink_post_break(l1) != null)) {
+ /*tex The first char could be a disc! Protrude the first char. */
+ l1 = vlink_post_break(l1);
+ } else {
+ l1 = find_protchar_left(l1, true);
+ }
+ shortfall += (left_pw(l1) + right_pw(o));
+ }
+ if (shortfall != 0) {
+ margin_kern_stretch = 0;
+ margin_kern_shrink = 0;
+ if (protrude_chars > 1) {
+ /*tex Calculate variations of marginal kerns. */
+ lp = last_leftmost_char;
+ rp = last_rightmost_char;
+ cp = raw_glyph_node();
+ if (lp != null) {
+ cal_margin_kern_var(lp);
+ }
+ if (rp != null) {
+ cal_margin_kern_var(rp);
+ }
+ flush_node(cp);
+ }
+ if ((shortfall > 0) && ((total_font_stretch + margin_kern_stretch) > 0)) {
+ if ((total_font_stretch + margin_kern_stretch) > shortfall)
+ shortfall = ((total_font_stretch + margin_kern_stretch) / (max_stretch_ratio / cur_font_step)) / 2;
+ else
+ shortfall -= (total_font_stretch + margin_kern_stretch);
+ } else if ((shortfall < 0) && ((total_font_shrink + margin_kern_shrink) > 0)) {
+ if ((total_font_shrink + margin_kern_shrink) > -shortfall)
+ shortfall = -((total_font_shrink + margin_kern_shrink) / (max_shrink_ratio / cur_font_step)) / 2;
+ else
+ shortfall += (total_font_shrink + margin_kern_shrink);
+ }
+ }
+ if (shortfall > 0) {
+ /*tex
+
+ Set the value of |b| to the badness for stretching the line, and
+ compute the corresponding |fit_class|.
+
+ When a line must stretch, the available stretchability can be
+ found in the subarray |cur_active_width[2..6]|, in units of
+ points, sfi, 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.
+
+ */
+ if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) ||
+ (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) {
+ if (do_last_line_fit) {
+ if (cur_p == null) {
+ /*tex
+
+ The last line of a paragraph. Perform computations
+ for last line and |goto found|.
+
+ Here we compute the adjustment |g| and badness |b|
+ for a line from |r| to the end of the paragraph. When
+ any of the criteria for adjustment is violated we
+ fall through to the normal algorithm.
+
+ The last line must be too short, and have infinite
+ stretch entirely due to |par_fill_skip|.
+
+ */
+ if ((active_short(r) == 0) || (active_glue(r) <= 0))
+ /*tex
+
+ Previous line was neither stretched nor shrunk,
+ or was infinitely bad.
+
+ */
+ goto NOT_FOUND;
+ if ((cur_active_width[3] != fill_width[0]) || (cur_active_width[4] != fill_width[1]) ||
+ (cur_active_width[5] != fill_width[2]) || (cur_active_width[6] != fill_width[3]))
+ /*tex
+
+ Infinite stretch of this line not entirely due to |par_fill_skip|.
+
+ */
+ goto NOT_FOUND;
+ if (active_short(r) > 0)
+ g = cur_active_width[2];
+ else
+ g = cur_active_width[7];
+ if (g <= 0)
+ /*tex No finite stretch resp.\ no shrink. */
+ goto NOT_FOUND;
+ arith_error = false;
+ g = fract(g, active_short(r), active_glue(r),
+ max_dimen);
+ if (last_line_fit < 1000)
+ g = fract(g, last_line_fit, 1000, max_dimen);
+ if (arith_error) {
+ if (active_short(r) > 0)
+ g = max_dimen;
+ else
+ g = -max_dimen;
+ }
+ if (g > 0) {
+ /*tex
+
+ Set the value of |b| to the badness of the last
+ line for stretching, compute the corresponding
+ |fit_class, and |goto found|. These badness
+ computations are rather similar to those of the
+ standard algorithm, with the adjustment amount
+ |g| replacing the |shortfall|.
+
+ */
+ if (g > shortfall)
+ g = shortfall;
+ if (g > 7230584) {
+ if (cur_active_width[2] < 1663497) {
+ b = inf_bad;
+ fit_class = very_loose_fit;
+ goto FOUND;
+ }
+ }
+ b = badness(g, cur_active_width[2]);
+ if (b > 99) {
+ fit_class = very_loose_fit;
+ } else if (b > 12) {
+ fit_class = loose_fit;
+ } else {
+ fit_class = decent_fit;
+ }
+ goto FOUND;
+ } else if (g < 0) {
+ /*tex
+
+ Set the value of |b| to the badness of the last
+ line for shrinking, compute the corresponding
+ |fit_class, and |goto found||.
+
+ */
+ if (-g > cur_active_width[7])
+ g = -cur_active_width[7];
+ b = badness(-g, cur_active_width[7]);
+ if (b > 12)
+ fit_class = tight_fit;
+ else
+ fit_class = decent_fit;
+ goto FOUND;
+ }
+ }
+ NOT_FOUND:
+ shortfall = 0;
+ }
+ b = 0;
+ /*tex Infinite stretch. */
+ fit_class = decent_fit;
+ } else if (shortfall > 7230584 && cur_active_width[2] < 1663497) {
+ b = inf_bad;
+ fit_class = very_loose_fit;
+ } else {
+ b = badness(shortfall, cur_active_width[2]);
+ if (b > 99) {
+ fit_class = very_loose_fit;
+ } else if (b > 12) {
+ fit_class = loose_fit;
+ } else {
+ fit_class = decent_fit;
+ }
+ }
+ } else {
+ /*tex
+
+ Set the value of |b| to the badness for shrinking the line, and
+ compute the corresponding |fit_class|. Shrinkability is never
+ infinite in a paragraph; we can shrink the line from |r| to
+ |cur_p| by at most |cur_active_width[7]|.
+
+ */
+ if (-shortfall > cur_active_width[7])
+ b = inf_bad + 1;
+ else
+ b = badness(-shortfall, cur_active_width[7]);
+ if (b > 12)
+ fit_class = tight_fit;
+ else
+ fit_class = decent_fit;
+ }
+ if (do_last_line_fit) {
+ /*tex Adjust the additional data for last line; */
+ if (cur_p == null)
+ shortfall = 0;
+ if (shortfall > 0) {
+ g = cur_active_width[2];
+ } else if (shortfall < 0) {
+ g = cur_active_width[7];
+ } else {
+ g = 0;
+ }
+ }
+ FOUND:
+ if ((b > inf_bad) || (pi == eject_penalty)) {
+ /*tex
+
+ Prepare to deactivate node~|r|, and |goto deactivate| unless
+ there is a reason to consider lines of text from |r| to |cur_p|.
+ 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.
+
+ */
+ if (final_pass && (minimum_demerits == awful_bad) &&
+ (vlink(r) == active) && (prev_r == active)) {
+ /*tex Set demerits zero, this break is forced. */
+ artificial_demerits = true;
+ } else if (b > threshold) {
+ goto DEACTIVATE;
+ }
+ node_r_stays_active = false;
+ } else {
+ prev_r = r;
+ if (b > threshold)
+ continue;
+ node_r_stays_active = true;
+ }
+ /*tex
+
+ Record a new feasible break. 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.
+
+ */
+ if (artificial_demerits) {
+ d = 0;
+ } else {
+ /*tex 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 += (pi * pi);
+ } else if (pi > eject_penalty) {
+ d -= (pi * pi);
+ }
+ }
+ if ((break_type == hyphenated_node) && (type(r) == hyphenated_node)) {
+ if (cur_p != null)
+ d += double_hyphen_demerits;
+ else
+ d += final_hyphen_demerits;
+ }
+ /*tex
+
+ Direct calculation of the absolute value in ((|fit_class| - |fitness(r)|) > 1)
+ can lead to unexpected results even if the type of the members of |fit_class|, which is |int|
+ (see C99 §6.7.2.2), and the integer promotion rules for |fitness(r)|, whose also
+ give an |int| type, should set the expression as substraction between two |int|.
+ In this case GCC set the type of |fit_class| to |unsigned int| (perhaps because the members are all positives?)
+ and hence the expression is converted to a sum of |unsigned int|, leading to a different result.
+ The choice of type is implementation-defined, as stated in C99 §6.7.2.2.4:
+
+ "Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type.
+ The choice of type is implementation-defined, but shall be capable of representing the values
+ of all the members of the enumeration."
+
+ It's better to use the equivalent expanded expression.
+ */
+ if ( (fit_class>(fitness(r)+1)) || (fitness(r)>(fit_class+1)) )
+ d = d + adj_demerits;
+
+ }
+ if (tracing_paragraphs > 0) {
+ print_feasible_break(cur_p, r, b, pi, d, artificial_demerits);
+ }
+ /*tex This is the minimum total demerits from the beginning to |cur_p| via |r|. */
+ d += total_demerits(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 (do_last_line_fit) {
+ /*tex
+
+ Store additional data for this feasible break. For each
+ feasible break we record the shortfall and glue stretch or
+ shrink (or adjustment).
+
+ */
+ best_pl_short[fit_class] = shortfall;
+ best_pl_glue[fit_class] = g;
+ }
+ if (d < minimum_demerits)
+ minimum_demerits = d;
+ }
+ /*tex Record a new feasible break. */
+ if (node_r_stays_active) {
+ /*tex |prev_r| has been set to |r|. */
+ continue;
+ }
+ DEACTIVATE:
+ /*tex
+
+ Deactivate node |r|. 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 |vlink(prev_r)| to~|cur_p|.
+
+ */
+ vlink(prev_r) = vlink(r);
+ flush_node(r);
+ if (prev_r == active) {
+ /*tex
+
+ Update the active widths, since the first active node has been
+ deleted. The following code uses the fact that
+ |type(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.
+
+ */
+ r = vlink(active);
+ if (type(r) == delta_node) {
+ do_all_eight(update_active);
+ do_all_eight(copy_to_cur_active);
+ vlink(active) = vlink(r);
+ flush_node(r);
+ }
+ } else if (type(prev_r) == delta_node) {
+ r = vlink(prev_r);
+ if (r == active) {
+ do_all_eight(downdate_width);
+ vlink(prev_prev_r) = active;
+ flush_node(prev_r);
+ prev_r = prev_prev_r;
+ } else if (type(r) == delta_node) {
+ do_all_eight(update_width);
+ do_all_eight(combine_two_deltas);
+ vlink(prev_r) = vlink(r);
+ flush_node(r);
+ }
+ }
+ }
+}
+
+void ext_do_line_break(
+ int paragraph_dir,
+ int pretolerance,
+ int tracing_paragraphs,
+ int tolerance,
+ scaled emergency_stretch,
+ int looseness,
+ int adjust_spacing,
+ halfword par_shape_ptr,
+ int adj_demerits,
+ int protrude_chars,
+ int line_penalty,
+ int last_line_fit,
+ int double_hyphen_demerits,
+ int final_hyphen_demerits,
+ int hang_indent,
+ int hsize,
+ int hang_after,
+ halfword left_skip,
+ halfword right_skip,
+ halfword inter_line_penalties_ptr,
+ int inter_line_penalty,
+ int club_penalty,
+ halfword club_penalties_ptr,
+ halfword widow_penalties_ptr,
+ int widow_penalty,
+ int broken_penalty,
+ halfword final_par_glue
+)
+{
+ /*tex Miscellaneous nodes of temporary interest. */
+ halfword cur_p, q, r, s;
+ int line_break_dir = paragraph_dir;
+ /*tex 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;
+ fewest_demerits = 0;
+ actual_looseness = 0;
+ /*tex
+
+ We compute the values of |easy_line| and the other local variables
+ relating to line length when the |line_break| procedure is initializing
+ itself.
+
+ */
+ if (par_shape_ptr == null) {
+ if (hang_indent == 0) {
+ last_special_line = 0;
+ second_width = hsize;
+ second_indent = 0;
+ } else {
+ halfword used_hang_indent = swap_hang_indent(hang_indent);
+ /*tex
+
+ Set line length parameters in preparation for hanging
+ indentation. We compute the values of |easy_line| and the other
+ local variables relating to line length when the |line_break|
+ procedure is initializing itself.
+
+ */
+ last_special_line = abs(hang_after);
+ if (hang_after < 0) {
+ first_width = hsize - abs(used_hang_indent);
+ if (used_hang_indent >= 0)
+ first_indent = used_hang_indent;
+ else
+ first_indent = 0;
+ second_width = hsize;
+ second_indent = 0;
+ } else {
+ first_width = hsize;
+ first_indent = 0;
+ second_width = hsize - abs(used_hang_indent);
+ if (used_hang_indent >= 0)
+ second_indent = used_hang_indent;
+ else
+ second_indent = 0;
+ }
+ }
+ } else {
+ last_special_line = vinfo(par_shape_ptr + 1) - 1;
+ second_indent = varmem[(par_shape_ptr + 2 * (last_special_line + 1))].cint;
+ second_width = varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint;
+ second_indent = swap_parshape_indent(second_indent,second_width);
+ }
+ if (looseness == 0)
+ easy_line = last_special_line;
+ else
+ easy_line = max_halfword;
+ 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[6] = 0;
+ background[2 + stretch_order(q)] = stretch(q);
+ background[2 + stretch_order(r)] += stretch(r);
+ background[7] = shrink(q) + shrink(r);
+ if (adjust_spacing > 1) {
+ background[8] = 0;
+ background[9] = 0;
+ max_stretch_ratio = -1;
+ max_shrink_ratio = -1;
+ cur_font_step = -1;
+ set_prev_char_p(null);
+ }
+ /*tex
+
+ Check for special treatment of last line of paragraph. The new algorithm
+ for the last line requires that the stretchability |par_fill_skip| is
+ infinite and the stretchability of |left_skip| plus |right_skip| is
+ finite.
+
+ */
+ do_last_line_fit = false;
+ if (last_line_fit > 0) {
+ q = last_line_fill;
+ if ((stretch(q) > 0) && (stretch_order(q) > normal)) {
+ if ((background[3] == 0) && (background[4] == 0) && (background[5] == 0) && (background[6] == 0)) {
+ do_last_line_fit = true;
+ fill_width[0] = 0;
+ fill_width[1] = 0;
+ fill_width[2] = 0;
+ fill_width[3] = 0;
+ fill_width[stretch_order(q) - 1] = stretch(q);
+ }
+ }
+ }
+ /*tex Initialize |dir_ptr| for |line_break|. */
+ if (dir_ptr != null) {
+ flush_node_list(dir_ptr);
+ dir_ptr = null;
+ }
+ /*tex Find optimal breakpoints. */
+ threshold = pretolerance;
+ if (threshold >= 0) {
+ if (tracing_paragraphs > 0) {
+ begin_diagnostic();
+ tprint_nl("@firstpass");
+ }
+ second_pass = false;
+ final_pass = false;
+ } else {
+ threshold = tolerance;
+ second_pass = true;
+ final_pass = (emergency_stretch <= 0);
+ if (tracing_paragraphs > 0)
+ begin_diagnostic();
+ }
+ while (1) {
+ halfword first_p;
+ halfword nest_stack[10];
+ int nest_index = 0;
+ if (threshold > inf_bad)
+ threshold = inf_bad;
+ /*tex Create an active breakpoint representing the beginning of the paragraph. */
+ q = new_node(unhyphenated_node, decent_fit);
+ vlink(q) = active;
+ break_node(q) = null;
+ line_number(q) = cur_list.pg_field + 1;
+ total_demerits(q) = 0;
+ active_short(q) = 0;
+ active_glue(q) = 0;
+ vlink(active) = q;
+ do_all_eight(store_background);
+ passive = null;
+ printed_node = temp_head;
+ pass_number = 0;
+ font_in_short_display = null_font;
+ /*tex Create an active breakpoint representing the beginning of the paragraph. */
+ auto_breaking = true;
+ cur_p = vlink(temp_head);
+ /*tex Initialize with first |local_paragraph| node. */
+ if ((cur_p != null) && (type(cur_p) == local_par_node)) {
+ /*tex This used to be an assert, but may as well force it. */
+ alink(cur_p) = temp_head;
+ internal_pen_inter = local_pen_inter(cur_p);
+ internal_pen_broken = local_pen_broken(cur_p);
+ init_internal_left_box = local_box_left(cur_p);
+ init_internal_left_box_width = local_box_left_width(cur_p);
+ internal_left_box = init_internal_left_box;
+ internal_left_box_width = init_internal_left_box_width;
+ internal_right_box = local_box_right(cur_p);
+ internal_right_box_width = local_box_right_width(cur_p);
+ } else {
+ internal_pen_inter = 0;
+ internal_pen_broken = 0;
+ init_internal_left_box = null;
+ init_internal_left_box_width = 0;
+ internal_left_box = init_internal_left_box;
+ internal_left_box_width = init_internal_left_box_width;
+ internal_right_box = null;
+ internal_right_box_width = 0;
+ }
+ /*tex Initialize with first |local_paragraph| node. */
+ set_prev_char_p(null);
+ first_p = cur_p;
+ /*tex
+
+ To access the first node of paragraph as the first active node has
+ |break_node=null|.
+
+ */
+ while ((cur_p != null) && (vlink(active) != active)) {
+ /*tex
+
+ |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass,
+ also look at |disc_node|s.
+
+ */
+ while (is_char_node(cur_p)) {
+ /*tex
+
+ Advance |cur_p| to the node following the present string of
+ characters. 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
+ |vlink(cur_p)=null| when |cur_p| is a character node.
+
+ */
+ active_width[1] += pack_width(line_break_dir, dir_TRT, cur_p, true);
+ if ((adjust_spacing > 1) && check_expand_pars(font(cur_p))) {
+ set_prev_char_p(cur_p);
+ add_char_stretch(active_width[8], cur_p);
+ add_char_shrink(active_width[9], cur_p);
+ }
+ cur_p = vlink(cur_p);
+ while (cur_p == null && nest_index > 0) {
+ cur_p = nest_stack[--nest_index];
+ }
+ }
+ if (cur_p == null) {
+ normal_error("linebreak","invalid list tail, probably missing glue");
+ }
+ /*tex
+
+ Determine legal breaks: 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 |active_width[1]| for the
+ component of active width that represents real width as opposed
+ to glue.
+
+ */
+ switch (type(cur_p)) {
+ case hlist_node:
+ case vlist_node:
+ active_width[1] += pack_width(line_break_dir, box_dir(cur_p), cur_p, false);
+ break;
+ case rule_node:
+ active_width[1] += width(cur_p);
+ break;
+ case dir_node:
+ /*tex Adjust the dir stack for the |line_break| routine. */
+ if (subtype(cur_p) == normal_dir) {
+ line_break_dir = dir_dir(cur_p);
+ /* Adds to |dir_ptr|. */
+ push_dir_node(dir_ptr,cur_p);
+ } else {
+ pop_dir_node(dir_ptr);
+ if (dir_ptr != null) {
+ line_break_dir = dir_dir(dir_ptr);
+ }
+ }
+ break;
+ case local_par_node:
+ /*tex Advance past a |local_paragraph| node. */
+ internal_pen_inter = local_pen_inter(cur_p);
+ internal_pen_broken = local_pen_broken(cur_p);
+ internal_left_box = local_box_left(cur_p);
+ internal_left_box_width = local_box_left_width(cur_p);
+ internal_right_box = local_box_right(cur_p);
+ internal_right_box_width = local_box_right_width(cur_p);
+ break;
+ case math_node:
+ auto_breaking = (subtype(cur_p) == after);
+ /*tex begin mathskip code */
+ if (glue_is_zero(cur_p) || ignore_math_skip(cur_p)) {
+ kern_break();
+ break;
+ } else {
+ /*tex fall through */
+ }
+ /*tex end mathskip code */
+ case glue_node:
+ /*tex
+
+ 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)|.
+
+ When node |cur_p| is a glue node, we look at the previous
+ to see whether or not a breakpoint is legal at |cur_p|,
+ as explained above.
+
+ We only break after certain nodes (see texnodes.h), a
+ font related kern and a dir node when
+ |\breakafterdirmode=1|.
+
+ */
+ if (auto_breaking) {
+ halfword prev_p = alink(cur_p);
+ if (prev_p != temp_head && (is_char_node(prev_p)
+ || precedes_break(prev_p) || precedes_kern(prev_p) || precedes_dir(prev_p))) {
+ ext_try_break(
+ 0,
+ unhyphenated_node,
+ line_break_dir,
+ adjust_spacing,
+ par_shape_ptr,
+ adj_demerits,
+ tracing_paragraphs,
+ protrude_chars,
+ line_penalty,
+ last_line_fit,
+ double_hyphen_demerits,
+ final_hyphen_demerits,
+ first_p,
+ cur_p
+ );
+ }
+ }
+ check_shrinkage(cur_p);
+ active_width[1] += width(cur_p);
+ active_width[2 + stretch_order(cur_p)] += stretch(cur_p);
+ active_width[7] += shrink(cur_p);
+ break;
+ case kern_node:
+ if (subtype(cur_p) == explicit_kern || subtype(cur_p) == italic_kern) {
+ kern_break();
+ } else {
+ active_width[1] += width(cur_p);
+ if ((adjust_spacing == 2) && (subtype(cur_p) == normal)) {
+ add_kern_stretch(active_width[8], cur_p);
+ add_kern_shrink(active_width[9], cur_p);
+ }
+ }
+ break;
+ case disc_node:
+ /*tex
+
+ |select_disc|s are handled by the leading |init_disc|.
+
+ */
+ if (subtype(cur_p) == select_disc)
+ break;
+ /*tex
+
+ Try to break after a discretionary fragment, then |goto
+ done5|. The following code knows that discretionary texts
+ contain only character nodes, kern nodes, box nodes, and
+ rule nodes. This branch differs a bit from older engines
+ because in \LUATEX\ we already have hyphenated the list.
+ This means that we need to skip automatic disc nodes. Of
+ better, we need to treat discretionaries and explicit
+ hyphens always, even in the first pass.
+
+ */
+ if (second_pass || subtype(cur_p) <= automatic_disc) {
+ int actual_penalty = (int) disc_penalty(cur_p);
+ s = vlink_pre_break(cur_p);
+ do_one_seven_eight(reset_disc_width);
+ if (s == null) {
+ /*tex trivial pre-break */
+ ext_try_break(actual_penalty, hyphenated_node,
+ line_break_dir, adjust_spacing,
+ par_shape_ptr, adj_demerits,
+ tracing_paragraphs, protrude_chars,
+ line_penalty, last_line_fit,
+ double_hyphen_demerits,
+ final_hyphen_demerits, first_p, cur_p);
+ } else {
+ add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
+ do_one_seven_eight(add_disc_width_to_active_width);
+ ext_try_break(actual_penalty, hyphenated_node,
+ line_break_dir, adjust_spacing,
+ par_shape_ptr, adj_demerits,
+ tracing_paragraphs, protrude_chars,
+ line_penalty, last_line_fit,
+ double_hyphen_demerits,
+ final_hyphen_demerits, first_p, cur_p);
+ if (subtype(cur_p) == init_disc) {
+ /*tex
+
+ We should at two break points after the one
+ we added above:
+
+ \startitemize[n]
+ \startitem
+ which does a possible break in INIT's
+ |post_break|
+ \stopitem
+ \startitem
+ which means the |no_break| actually
+ was broken just a character later
+ \stopitem
+ \stopitemize
+
+ Do the select-0 case |f-f-i|:
+
+ */
+ s = vlink_pre_break(vlink(cur_p));
+ add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
+ ext_try_break(actual_penalty, hyphenated_node,
+ line_break_dir, adjust_spacing,
+ par_shape_ptr, adj_demerits,
+ tracing_paragraphs,
+ protrude_chars, line_penalty,
+ last_line_fit, double_hyphen_demerits,
+ final_hyphen_demerits, first_p,
+ vlink(cur_p));
+ /*tex This does not work. */
+#if 0
+ /*tex Go back to the starting situation. */
+ do_one_seven_eight(sub_disc_width_from_active_width);
+ do_one_seven_eight(reset_disc_width);
+ /*tex Add select |no_break| to |active_width|. */
+ s = vlink_no_break(vlink(cur_p));
+ add_to_widths(s, line_break_dir, adjust_spacing, disc_width);
+ ext_try_break(actual_penalty, hyphenated_node,
+ line_break_dir, adjust_spacing,
+ par_shape_ptr, adj_demerits,
+ tracing_paragraphs,
+ protrude_chars, line_penalty,
+ last_line_fit, double_hyphen_demerits,
+ final_hyphen_demerits, first_p,
+ vlink(cur_p));
+#endif
+ }
+ do_one_seven_eight(sub_disc_width_from_active_width);
+ }
+ }
+ s = vlink_no_break(cur_p);
+ add_to_widths(s, line_break_dir, adjust_spacing, active_width);
+ break;
+ case penalty_node:
+ ext_try_break(penalty(cur_p), unhyphenated_node, line_break_dir,
+ adjust_spacing, par_shape_ptr, adj_demerits,
+ tracing_paragraphs, protrude_chars,
+ line_penalty, last_line_fit,
+ double_hyphen_demerits, final_hyphen_demerits,
+ first_p, cur_p);
+ break;
+ case boundary_node:
+ case whatsit_node:
+ /*tex Advance past a whatsit node in the |line_break| loop. */
+ case mark_node:
+ case ins_node:
+ case adjust_node:
+ break;
+ case glue_spec_node:
+ normal_warning("parbuilder","found a glue_spec in a paragraph");
+ break;
+ default:
+ formatted_error("parbuilder","weird node %d in paragraph",type(cur_p));
+ }
+ cur_p = vlink(cur_p);
+ while (cur_p == null && nest_index > 0) {
+ cur_p = nest_stack[--nest_index];
+ }
+ }
+ if (cur_p == null) {
+ /*tex
+
+ Try the final line break at the end of the paragraph, and |goto
+ done| if the desired breakpoints have been found.
+
+ 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.
+
+ */
+ ext_try_break(eject_penalty, hyphenated_node, line_break_dir,
+ adjust_spacing, par_shape_ptr, adj_demerits,
+ tracing_paragraphs, protrude_chars, line_penalty,
+ last_line_fit, double_hyphen_demerits,
+ final_hyphen_demerits, first_p, cur_p);
+ if (vlink(active) != active) {
+ /*tex Find an active node with fewest demerits; */
+ r = vlink(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 = vlink(r);
+ } while (r != active);
+ best_line = line_number(best_bet);
+ /*tex
+ Find an active node with fewest demerits;
+ */
+ if (looseness == 0)
+ goto DONE;
+ /*tex
+
+ Find the best active node for the desired looseness;
+
+ 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.
+
+ */
+ r = vlink(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 = vlink(r);
+ } while (r != active);
+ best_line = line_number(best_bet);
+ /*tex
+ Find the best active node for the desired looseness.
+ */
+ if ((actual_looseness == looseness) || final_pass)
+ goto DONE;
+ }
+ }
+ /*tex Clean up the memory by removing the break nodes. */
+ clean_up_the_memory();
+ /*tex Clean up the memory by removing the break nodes. */
+ if (!second_pass) {
+ if (tracing_paragraphs > 0)
+ tprint_nl("@secondpass");
+ threshold = tolerance;
+ second_pass = true;
+ final_pass = (emergency_stretch <= 0);
+ } else {
+ /*tex If at first you do not succeed, then: */
+ if (tracing_paragraphs > 0)
+ tprint_nl("@emergencypass");
+ background[2] += emergency_stretch;
+ final_pass = true;
+ }
+ }
+
+ DONE:
+ if (tracing_paragraphs > 0) {
+ end_diagnostic(true);
+ normalize_selector();
+ }
+ if (do_last_line_fit) {
+ /*tex
+ Adjust the final line of the paragraph; here we either reset
+ |do_last_line_fit| or adjust the |par_fill_skip| glue.
+ */
+ if (active_short(best_bet) == 0) {
+ do_last_line_fit = false;
+ } else {
+ width(last_line_fill) += (active_short(best_bet) - active_glue(best_bet));
+ stretch(last_line_fill) = 0;
+ }
+ }
+ /*tex
+ Break the paragraph at the chosen. 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.
+
+ the first thing |ext_post_line_break| does is reset |dir_ptr|.
+
+ */
+ flush_node_list(dir_ptr);
+ dir_ptr = null;
+ ext_post_line_break(paragraph_dir,
+ right_skip,
+ left_skip,
+ protrude_chars,
+ par_shape_ptr,
+ adjust_spacing,
+ inter_line_penalties_par_ptr,
+ inter_line_penalty,
+ club_penalty,
+ club_penalties_ptr,
+ widow_penalties_ptr,
+ widow_penalty,
+ broken_penalty,
+ final_par_glue,
+ best_bet,
+ last_special_line,
+ second_width,
+ second_indent, first_width, first_indent, best_line);
+ /*tex
+
+ Clean up the memory by removing the break nodes.
+
+ */
+ clean_up_the_memory();
+}
+
+void get_linebreak_info (int *f, int *a)
+{
+ *f = fewest_demerits;
+ *a = actual_looseness;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.h b/Build/source/texk/web2c/luatexdir/tex/linebreak.h
new file mode 100644
index 00000000000..d1e1768f704
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.h
@@ -0,0 +1,98 @@
+/* linebreak.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LINEBREAK_H
+# define LINEBREAK_H
+
+extern halfword just_box; /* the |hlist_node| for the last line of the new paragraph */
+
+extern void line_break(boolean d, int line_break_context);
+
+# define inf_bad 10000 /* infinitely bad value */
+# define awful_bad 07777777777 /* more than a billion demerits */
+
+extern void initialize_active(void);
+
+extern void ext_do_line_break(int paragraph_dir,
+ int pretolerance,
+ int tracing_paragraphs,
+ int tolerance,
+ scaled emergency_stretch,
+ int looseness,
+ /*
+ int hyphen_penalty,
+ int ex_hyphen_penalty,
+ */
+ int adjust_spacing,
+ halfword par_shape_ptr,
+ int adj_demerits,
+ int protrude_chars,
+ int line_penalty,
+ int last_line_fit,
+ int double_hyphen_demerits,
+ int final_hyphen_demerits,
+ int hang_indent,
+ int hsize,
+ int hang_after,
+ halfword left_skip,
+ halfword right_skip,
+ halfword inter_line_penalties_ptr,
+ int inter_line_penalty,
+ int club_penalty,
+ halfword club_penalties_ptr,
+ halfword widow_penalties_ptr,
+ int widow_penalty,
+ int broken_penalty, halfword final_par_glue);
+
+extern void get_linebreak_info(int *, int *);
+extern halfword find_protchar_left(halfword l, boolean d);
+extern halfword find_protchar_right(halfword l, halfword r);
+
+/* skipable nodes at the margins during character protrusion */
+
+# define zero_dimensions(a) ( \
+ (width((a)) == 0) && \
+ (height((a)) == 0) && \
+ (depth((a)) == 0) \
+)
+
+# define empty_disc(a) ( \
+ (vlink_pre_break(a) == null) && \
+ (vlink_post_break(a) == null) && \
+ (vlink_no_break(a) == null) \
+)
+
+# define cp_skipable(a) ( (! is_char_node((a))) && ( \
+ ((type((a)) == glue_node) && (glue_is_zero((a)))) \
+ || (type((a)) == penalty_node) \
+ || ((type((a)) == disc_node) && empty_disc(a)) \
+ || ((type((a)) == kern_node) && ((width((a)) == 0) || (subtype((a)) == normal))) \
+ || ((type((a)) == rule_node) && zero_dimensions(a)) \
+ || ((type((a)) == math_node) && (surround((a)) == 0 || (glue_is_zero((a))))) \
+ || (type((a)) == dir_node) \
+ || ((type((a)) == hlist_node) && (list_ptr((a)) == null) && zero_dimensions(a)) \
+ || (type((a)) == local_par_node) \
+ || (type((a)) == ins_node) \
+ || (type((a)) == mark_node) \
+ || (type((a)) == adjust_node) \
+ || (type((a)) == boundary_node) \
+ || (type((a)) == whatsit_node) \
+) )
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/mainbody.c b/Build/source/texk/web2c/luatexdir/tex/mainbody.c
new file mode 100644
index 00000000000..2f45f9989d2
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/mainbody.c
@@ -0,0 +1,780 @@
+/*
+
+mainbody.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+This is where the action starts. We're speaking of \LUATEX, a continuation of
+\PDFTEX\ (which included \ETEX) and \ALEPH. As \TEX, \LUATEX\ is a document
+compiler intended to simplify high quality typesetting for many of the world's
+languages. It is an extension of D. E. Knuth's \TEX, which was designed
+essentially for the typesetting of languages using the Latin alphabet. Although
+it is a direct decendant of \TEX, and therefore mostly compatible, there are
+some subtle differences that relate to \UNICODE\ support and \OPENTYPE\ math.
+
+The \ALEPH\ subsystem loosens many of the restrictions imposed by~\TeX: register
+numbers are no longer limited to 8~bits. Fonts may have more than 256~characters,
+more than 256~fonts may be used, etc. We use a similar model. We also borrowed
+the directional model but have upgraded it a bit as well as integrated it more
+tightly.
+
+This program is directly derived from Donald E. Knuth's \TEX; the change history
+which follows and the reward offered for finders of bugs refer specifically to
+\TEX; they should not be taken as referring to \LUATEX, \PDFTEX, nor \ETEX,
+although the change history is relevant in that it demonstrates the evolutionary
+path followed. This program is not \TEX; that name is reserved strictly for the
+program which is the creation and sole responsibility of Professor Knuth.
+
+\starttyping
+% 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).
+\stoptyping
+
+Although considerable effort has been expended to make the LuaTeX program correct
+and reliable, no warranty is implied; the authors disclaim any obligation or
+liability for damages, including but not limited to special, indirect, or
+consequential damages arising out of or in connection with the use or performance
+of this software. This work has been a ``labor of love'' and the authors hope
+that users enjoy it.
+
+{\em You will find a lot of comments that originate in original \TEX. We kept them
+as a side effect of the conversion from \WEB\ to \CWEB. Because there is not much
+webbing going on here eventually the files became regular \CCODE\ files with still
+potentially typeset comments. As we add our own comments, and also comments are
+there from \PDFTEX, \ALEPH\ and \ETEX, we get a curious mix. The best comments are
+of course from Don Knuth. All bad comments are ours. All errors are ours too!
+
+Not all comments make sense, because some things are implemented differently, for
+instance some memory management. But the principles of tokens and nodes stayed.
+It anyway means that you sometimes need to keep in mind that the explanation is
+more geared to traditional \TEX. But that's not a bad thing. Sorry Don for any
+confusion we introduced. The readers should have a copy of the \TEX\ books at hand
+anyway.}
+
+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}.
+
+The present implementation has a long ancestry, beginning in the summer of~1977,
+when Michael~F. Plass and Frank~M. Liang designed and coded a prototype @^Plass,
+Michael Frederick@> @^Liang, Franklin Mark@> @^Knuth, Donald Ervin@> based on
+some specifications that the author had made in May of that year. This original
+proto\TeX\ included macro definitions and elementary manipulations on boxes and
+glue, but it did not have line-breaking, page-breaking, mathematical formulas,
+alignment routines, error recovery, or the present semantic nest; furthermore, it
+used character lists instead of token lists, so that a control sequence like
+\.{\\halign} was represented by a list of seven characters. A complete version of
+\TeX\ was designed and coded by the author in late 1977 and early 1978; that
+program, like its prototype, was written in the {\mc SAIL} language, for which an
+excellent debugging system was available. Preliminary plans to convert the {\mc
+SAIL} code into a form somewhat like the present ``web'' were developed by Luis
+Trabb~Pardo and @^Trabb Pardo, Luis Isidoro@> the author at the beginning of
+1979, and a complete implementation was created by Ignacio~A. Zabala in 1979 and
+1980. The \TeX82 program, which @^Zabala Salelles, Ignacio Andr\'es@> was written
+by the author during the latter part of 1981 and the early part of 1982, also
+incorporates ideas from the 1979 implementation of @^Guibas, Leonidas Ioannis@>
+@^Sedgewick, Robert@> @^Wyatt, Douglas Kirk@> \TeX\ in {\mc MESA} that was
+written by Leonidas Guibas, Robert Sedgewick, and Douglas Wyatt at the Xerox Palo
+Alto Research Center. Several hundred refinements were introduced into \TeX82
+based on the experiences gained with the original implementations, so that
+essentially every part of the system has been substantially improved. After the
+appearance of ``Version 0'' in September 1982, this program benefited greatly
+from the comments of many other people, notably David~R. Fuchs and Howard~W.
+Trickey. A final revision in September 1989 extended the input character set to
+eight-bit codes and introduced the ability to hyphenate words from different
+languages, based on some ideas of Michael~J. Ferguson. @^Fuchs, David Raymond@>
+@^Trickey, Howard Wellington@> @^Ferguson, Michael John@>
+
+No doubt there still is plenty of room for improvement, but the author is firmly
+committed to keeping \TeX82 ``frozen'' from now on; stability and reliability are
+to be its main virtues.
+On the other hand, the \.{WEB} description can be extended without changing the
+core of \TeX82 itself, and the program has been designed so that such extensions
+are not extremely difficult to make. The |banner| string defined here should be
+changed whenever \TeX\ undergoes any modifications, so that it will be clear
+which version of \TeX\ might be the guilty party when a problem arises.
+@^extensions to \TeX@> @^system dependencies@>
+
+This program contains code for various features extending \TeX, therefore this
+program is called `\eTeX' and not `\TeX'; the official name `\TeX' by itself is
+reserved for software systems that are fully compatible with each other. A
+special test suite called the ``\.{TRIP} test'' is available for helping to
+determine whether a particular implementation deserves to be known as `\TeX'
+[cf.~Stanford Computer Science report CS1027, November 1984].
+
+A similar test suite called the ``\.{e-TRIP} test'' is available for helping to
+determine whether a particular implementation deserves to be known as `\eTeX'.
+
+This is the first of many sections of \TeX\ where global variables are defined.
+
+*/
+
+/*tex Are we using lua for initializations? */
+
+boolean luainit;
+
+/*tex Print file open and close info? */
+
+boolean tracefilenames;
+
+/*tex
+
+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.
+
+*/
+
+/*tex are we \.{INITEX}? */
+
+boolean ini_version;
+
+/*tex was the dump name option used? */
+
+boolean dump_option;
+
+/*tex was a \.{\%\AM format} line seen? */
+
+boolean dump_line;
+
+/*tex temporary for setup */
+
+int bound_default;
+
+/*tex temporary for setup */
+
+char *bound_name;
+
+/*tex width of context lines on terminal error messages */
+
+int error_line;
+
+/*tex
+ width of first lines of contexts in terminal error messages; should be
+ between 30 and |error_line-15|
+*/
+
+int half_error_line;
+
+/*tex width of longest text lines output; should be at least 60 */
+
+int max_print_line;
+
+/*tex maximum number of strings; must not exceed |max_halfword| */
+
+int max_strings;
+
+/*tex strings available after format loaded */
+
+int strings_free;
+
+/*tex loop variable for initialization */
+
+int font_k;
+
+/*tex
+ putting a character in front of a macro trace (feature suggested by PO on
+ tex implementor list
+*/
+
+int level_max;
+int level_chr;
+
+/*tex
+ 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|
+*/
+
+int buf_size;
+
+/*tex maximum number of simultaneous input sources */
+
+int stack_size;
+
+/*tex
+ maximum number of input files and error insertions that can be going on
+ simultaneously
+*/
+
+int max_in_open;
+
+/*tex maximum number of simultaneous macro parameters */
+
+int param_size;
+
+/*tex maximum number of semantic levels simultaneously active */
+
+int nest_size;
+
+/*tex
+ space for saving values outside of current group; must be at most
+ |max_halfword|
+*/
+
+int save_size;
+
+/*tex limits recursive calls of the |expand| procedure */
+
+int expand_depth;
+
+/*tex parse the first line for options */
+
+int parsefirstlinep;
+
+/*tex format messages as file:line:error */
+
+int filelineerrorstylep;
+
+/*tex stop at first error */
+
+int haltonerrorp;
+
+/*tex current filename is quoted */
+
+boolean quoted_filename;
+
+int get_luatexversion(void)
+{
+ return luatex_version;
+}
+
+/*tex the number of pages that have been shipped out */
+
+int total_pages = 0;
+
+/*tex recent outputs that didn't ship anything out */
+
+int dead_cycles = 0;
+
+str_number get_luatexrevision(void)
+{
+ return luatex_revision;
+}
+
+/*tex
+
+This is it: the part of \TeX\ that executes all those procedures we have written.
+
+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.
+
+For \LUATEX\ it is important to know that we still dump a format. But, in order
+to gain speed and a smaller footprint, we gzip the format (level 3). We also
+store some information that makes an abort possible in case of an incompatible
+engine version, which is important as \LUATEX\ develops. It is possible to store
+\LUA\ code in the format but not the current upvalues so you still need to
+initialize. Also, traditional fonts are stored, as are extended fonts but any
+additional information needed for instance to deal with \OPENTYPE\ fonts is to be
+handled by \LUA\ code and therefore not present in the format.
+
+*/
+
+#define const_chk(A) do { \
+ if (A < inf_##A) \
+ A = inf_##A; \
+ if (A > sup_##A) \
+ A = sup_##A; \
+} while (0)
+
+#define setup_bound_var(A,B,C) do { \
+ if (luainit>0) { \
+ get_lua_number("texconfig",B,&C); \
+ if (C==0) \
+ C=A; \
+ } else { \
+ integer x; \
+ setupboundvariable(&x, B, A); \
+ C = (int)x; \
+ } \
+} while (0)
+
+int ready_already = 0;
+
+int main_initialize(void)
+{
+ /*
+ In case somebody has inadvertently made bad settings of the
+ ``constants,'' \LUATEX\ checks them using a variable called |bad|.
+ */
+ int bad = 0;
+ /*tex
+ Bounds that may be set from the configuration file. We want the user to
+ be able to specify the names with underscores, but \.{TANGLE} removes
+ underscores, so we're stuck giving the names twice, once as a string,
+ once as the identifier. How ugly. (We can change that now.)
+ */
+ setup_bound_var(15000, "max_strings", max_strings);
+ setup_bound_var(100, "strings_free", strings_free);
+ setup_bound_var(3000, "buf_size", buf_size);
+ setup_bound_var(50, "nest_size", nest_size);
+ setup_bound_var(15, "max_in_open", max_in_open);
+ setup_bound_var(60, "param_size", param_size);
+ setup_bound_var(4000, "save_size", save_size);
+ setup_bound_var(300, "stack_size", stack_size);
+ setup_bound_var(16384, "dvi_buf_size", dvi_buf_size);
+ setup_bound_var(79, "error_line", error_line);
+ setup_bound_var(50, "half_error_line", half_error_line);
+ setup_bound_var(79, "max_print_line", max_print_line);
+ setup_bound_var(0, "hash_extra", hash_extra);
+ setup_bound_var(72, "pk_dpi", pk_dpi);
+ setup_bound_var(10000, "expand_depth", expand_depth);
+ setup_bound_var(0, "level_max", level_max);
+ setup_bound_var('.', "level_chr", level_chr);
+ /*tex
+ Check other constants against their sup and inf.
+ */
+ const_chk(buf_size);
+ const_chk(nest_size);
+ const_chk(max_in_open);
+ const_chk(param_size);
+ const_chk(save_size);
+ const_chk(stack_size);
+ const_chk(dvi_buf_size);
+ const_chk(max_strings);
+ const_chk(strings_free);
+ const_chk(hash_extra);
+ const_chk(pk_dpi);
+ if (error_line > ssup_error_line) {
+ error_line = ssup_error_line;
+ }
+ /*tex
+ Array memory allocation
+ */
+ buffer = xmallocarray(packed_ASCII_code, (unsigned) buf_size);
+ nest = xmallocarray(list_state_record, (unsigned) nest_size);
+ save_stack = xmallocarray(save_record, (unsigned) save_size);
+ input_stack = xmallocarray(in_state_record, (unsigned) stack_size);
+ input_file = xmallocarray(alpha_file, (unsigned) max_in_open);
+ input_file_callback_id = xmallocarray(int, (unsigned) max_in_open);
+ line_stack = xmallocarray(int, (unsigned) max_in_open);
+ eof_seen = xmallocarray(boolean, (unsigned) max_in_open);
+ grp_stack = xmallocarray(save_pointer, (unsigned) max_in_open);
+ if_stack = xmallocarray(pointer, (unsigned) max_in_open);
+ source_filename_stack = xmallocarray(str_number, (unsigned) max_in_open);
+ full_source_filename_stack = xmallocarray(char *, (unsigned) max_in_open);
+ param_stack = xmallocarray(halfword, (unsigned) param_size);
+ dvi_buf = xmallocarray(eight_bits, (unsigned) dvi_buf_size);
+ /*tex
+ Only in ini mode:
+ */
+ if (ini_version) {
+ fixmem = xmallocarray(smemory_word, fix_mem_init + 1);
+ memset(voidcast(fixmem), 0, (fix_mem_init + 1) * sizeof(smemory_word));
+ fix_mem_min = 0;
+ fix_mem_max = fix_mem_init;
+ eqtb_top = eqtb_size + hash_extra;
+ if (hash_extra == 0)
+ hash_top = undefined_control_sequence;
+ else
+ hash_top = eqtb_top;
+ hash = xmallocarray(two_halves, (unsigned) (hash_top + 1));
+ memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1));
+ eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1));
+ memset(eqtb, 0, sizeof(memory_word) * (unsigned) (eqtb_top + 1));
+ init_string_pool_array((unsigned) max_strings);
+ reset_cur_string();
+ }
+ /*tex
+ Check the ``constant'' values...
+ */
+ 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 (hash_prime > hash_size)
+ bad = 5;
+ if (max_in_open >= (sup_max_in_open+1)) /* 128 */
+ bad = 6;
+ /*tex
+ Here are the inequalities that the quarterword and halfword values
+ must satisfy (or rather, the inequalities that they mustn't satisfy):
+ */
+ if ((min_quarterword > 0) || (max_quarterword < 0x7FFF))
+ bad = 11;
+ if ((min_halfword > 0) || (max_halfword < 0x3FFFFFFF))
+ bad = 12;
+ if ((min_quarterword < min_halfword) || (max_quarterword > max_halfword))
+ bad = 13;
+ if (font_base < min_quarterword)
+ bad = 15;
+ if ((save_size > max_halfword) || (max_strings > max_halfword))
+ bad = 17;
+ if (buf_size > max_halfword)
+ bad = 18;
+ if (max_quarterword - min_quarterword < 0xFFFF)
+ bad = 19;
+ if (cs_token_flag + eqtb_size + hash_extra > max_halfword)
+ bad = 21;
+ if (bad > 0) {
+ wterm_cr();
+ fprintf(term_out,
+ "Ouch---my internal constants have been clobbered! ---case %d",
+ (int) bad
+ );
+ } else {
+ /*tex Set global variables to their starting values. */
+ initialize();
+ if (ini_version) {
+ /*tex Initialize all the primitives. */
+ no_new_control_sequence = false;
+ first = 0;
+ initialize_commands();
+ initialize_etex_commands();
+ init_str_ptr = str_ptr;
+ no_new_control_sequence = true;
+ fix_date_and_time();
+ }
+ ready_already = 314159;
+ }
+ return bad;
+}
+
+
+void main_body(void)
+{
+ static char pdftex_map[] = "pdftex.map";
+ int bad = main_initialize();
+ /*tex in case we quit during initialization */
+ history = fatal_error_stop;
+ /*tex open the terminal for output */
+ t_open_out();
+ if (!luainit)
+ tracefilenames = true;
+ if (bad > 0) {
+ goto FINAL_END;
+ }
+ print_banner(luatex_version_string);
+ /*tex
+ Get the first line of input and prepare to start 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.
+ */
+ /*tex
+ This copies the command line,
+ */
+ initialize_inputstack();
+ if (buffer[iloc] == '*')
+ incr(iloc);
+ if ((format_ident == 0) || (buffer[iloc] == '&') || dump_line) {
+ char *fname = NULL;
+ if (format_ident != 0 && !ini_version) {
+ /*tex Erase preloaded format. */
+ initialize();
+ }
+ if ((fname = open_fmt_file()) == NULL)
+ goto FINAL_END;
+ if (!load_fmt_file(fname)) {
+ zwclose(fmt_file);
+ goto FINAL_END;
+ }
+ zwclose(fmt_file);
+ while ((iloc < ilimit) && (buffer[iloc] == ' '))
+ incr(iloc);
+ }
+ if (output_mode_option != 0)
+ output_mode_par = output_mode_value;
+ if (draft_mode_option != 0) {
+ draft_mode_par = draft_mode_value;
+ }
+ /*tex can this be moved? */
+ pdf_init_map_file((char *) pdftex_map);
+ /* */
+ if (end_line_char_inactive)
+ decr(ilimit);
+ else
+ buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+ fix_date_and_time();
+ random_seed = (microseconds * 1000) + (epochseconds % 1000000);
+ init_randoms(random_seed);
+ initialize_math();
+ fixup_selector(log_opened_global);
+ check_texconfig_init();
+ if ((iloc < ilimit) && (get_cat_code(cat_code_table_par, buffer[iloc]) != escape_cmd)) {
+ /*tex \.{\\input} assumed */
+ start_input();
+ }
+ /*tex Initialize |text_dir_ptr| */
+ text_dir_ptr = new_dir(0);
+ /*tex Ready to go! */
+ history = spotless;
+ /*tex Initialize synctex primitive */
+ synctexinitcommand();
+ /*tex Come to life. */
+ main_control();
+ flush_node(text_dir_ptr);
+ /*tex Prepare for death. */
+ final_cleanup();
+ close_files_and_terminate();
+ FINAL_END:
+ do_final_end();
+}
+
+/*tex
+
+Here we do whatever is needed to complete \TeX's job gracefully on the local
+operating system. The code here might come into play after a fatal error; it must
+therefore consist entirely of ``safe'' operations that cannot produce error
+messages. For example, it would be a mistake to call |str_room| or |make_string|
+at this time, because a call on |overflow| might lead to an infinite loop.
+@^system dependencies@>
+
+Actually there's one way to get error messages, via |prepare_mag|; but that can't
+cause infinite recursion. @^recursion@>
+
+This program doesn't bother to close the input files that may still be open.
+
+*/
+
+void close_files_and_terminate(void)
+{
+ int callback_id;
+ callback_id = callback_defined(stop_run_callback);
+ finalize_write_files();
+ if (tracing_stats_par > 0) {
+ if (callback_id == 0) {
+ /*tex
+ Output statistics about this job. 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
+ |string_pool| memory when a non-{\bf stat} version of \TeX\ is
+ being used.
+ */
+ if (log_opened_global) {
+ fprintf(log_file,
+ "\n\nHere is how much of LuaTeX's memory you used:\n"
+ );
+ fprintf(log_file, " %d string%s out of %d\n",
+ (int) (str_ptr - init_str_ptr),
+ (str_ptr == (init_str_ptr + 1) ? "" : "s"),
+ (int) (max_strings - init_str_ptr + STRING_OFFSET)
+ );
+ fprintf(log_file, " %d,%d words of node,token memory allocated",
+ (int) var_mem_max, (int) fix_mem_max
+ );
+ print_node_mem_stats();
+ fprintf(log_file,
+ " %d multiletter control sequences out of %ld+%d\n",
+ (int) cs_count, (long) hash_size, (int) hash_extra
+ );
+ fprintf(log_file, " %d font%s using %d bytes\n",
+ (int) max_font_id(), (max_font_id() == 1 ? "" : "s"),
+ (int) font_bytes
+ );
+ fprintf(log_file,
+ " %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds\n",
+ (int) max_in_stack, (int) max_nest_stack,
+ (int) max_param_stack, (int) max_buf_stack,
+ (int) max_save_stack + 6, (int) stack_size,
+ (int) nest_size, (int) param_size, (int) buf_size,
+ (int) save_size
+ );
+ }
+ }
+ }
+ wake_up_terminal();
+ /*tex
+ Rubish, these \PDF arguments, passed, needs to be fixed, e.g. with a
+ dummy in \DVI.
+ */
+ wrapup_backend();
+ /*tex
+ Close {\sl Sync\TeX} file and write status.
+ */
+ synctexterminate(log_opened_global);
+ /*tex
+ The following is needed because synctex removes files and we want to keep
+ them which means renaming a temp file .. we can't bypass the terminate
+ because it might do mem cleanup.
+ */
+ if (synctex_get_mode() > 0) {
+ callback_id = callback_defined(finish_synctex_callback);
+ if (callback_id > 0) {
+ run_callback(callback_id, "->");
+ }
+ }
+ /* free_text_codes(); */
+ /* free_math_codes(); */
+ if (log_opened_global) {
+ wlog_cr();
+ selector = selector - 2;
+ if ((selector == term_only) && (callback_id == 0)) {
+ tprint_nl("Transcript written on ");
+ tprint_file_name(NULL, texmf_log_name, NULL);
+ print_char('.');
+ print_ln();
+ }
+ lua_a_close_out(log_file);
+ log_opened_global = false;
+ }
+ callback_id = callback_defined(wrapup_run_callback);
+ if (callback_id > 0) {
+ run_callback(callback_id, "->");
+ }
+ free_text_codes();
+ free_math_codes();
+}
+
+/*tex
+
+We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has been
+scanned and |its_all_over|.
+
+*/
+
+void final_cleanup(void)
+{
+ /*tex This one gets the value 0 for \.{\\end}, 1 for \.{\\dump}. */
+ int c;
+ /*tex Here's one for looping marks: */
+ halfword i;
+ /*tex This was a global temp_ptr: */
+ halfword t;
+ c = cur_chr;
+ if (job_name == 0)
+ open_log_file();
+ while (input_ptr > 0)
+ if (istate == token_list)
+ end_token_list();
+ else
+ end_file_reading();
+ while (open_parens > 0) {
+ report_stop_file(filetype_tex);
+ decr(open_parens);
+ }
+ if (cur_level > level_one) {
+ tprint_nl("(\\end occurred inside a group at level ");
+ print_int(cur_level - level_one);
+ print_char(')');
+ show_save_groups();
+ }
+ while (cond_ptr != null) {
+ tprint_nl("(\\end occurred when ");
+ print_cmd_chr(if_test_cmd, cur_if);
+ if (if_line != 0) {
+ tprint(" on line ");
+ print_int(if_line);
+ }
+ tprint(" was incomplete)");
+ if_line = if_line_field(cond_ptr);
+ cur_if = subtype(cond_ptr);
+ t = cond_ptr;
+ cond_ptr = vlink(cond_ptr);
+ flush_node(t);
+ }
+ if (callback_defined(stop_run_callback) == 0)
+ if (history != spotless)
+ if ((history == warning_issued) || (interaction < error_stop_mode))
+ if (selector == term_and_log) {
+ selector = term_only;
+ tprint_nl("(see the transcript file for additional information)");
+ selector = term_and_log;
+ }
+ if (c == 1) {
+ if (ini_version) {
+ for (i = 0; i <= biggest_used_mark; i++) {
+ delete_top_mark(i);
+ delete_first_mark(i);
+ delete_bot_mark(i);
+ delete_split_first_mark(i);
+ delete_split_bot_mark(i);
+ }
+ for (c = last_box_code; c <= vsplit_code; c++)
+ flush_node_list(disc_ptr[c]);
+ if (last_glue != max_halfword) {
+ flush_node(last_glue);
+ }
+ /*tex Flush the pseudo files. */
+ while (pseudo_files != null) {
+ pseudo_close();
+ }
+ store_fmt_file();
+ return;
+ }
+ tprint_nl("(\\dump is performed only by INITEX)");
+ return;
+ }
+}
+
+/*tex
+
+Once \TeX\ is working, you should be able to diagnose most errors with the
+\.{\\show} commands and other diagnostic features.
+
+Because we have made some internal changes the optional debug interface
+has been removed.
+
+*/
diff --git a/Build/source/texk/web2c/luatexdir/tex/mainbody.h b/Build/source/texk/web2c/luatexdir/tex/mainbody.h
new file mode 100644
index 00000000000..0d274aeffbe
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/mainbody.h
@@ -0,0 +1,246 @@
+/* mainbody.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef MAINBODY_H
+# define MAINBODY_H
+
+extern int luatex_version;
+extern int luatex_revision;
+extern const char *luatex_version_string;
+extern const char *engine_name;
+
+/*
+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@>
+*/
+
+# define ssup_max_strings 2097151
+
+# define inf_max_strings 100000
+# define sup_max_strings ssup_max_strings
+# define inf_strings_free 100
+# define sup_strings_free sup_max_strings
+
+# define inf_buf_size 500
+# define sup_buf_size 100000000
+
+# define inf_nest_size 40
+# define sup_nest_size 4000
+
+# define inf_max_in_open 6
+# define sup_max_in_open 600 /* was 127. now okay for some 512 nested lua calls */
+
+# define inf_param_size 60
+# define sup_param_size 32767
+
+# define inf_save_size 600
+# define sup_save_size 500000
+
+# define inf_stack_size 200
+# define sup_stack_size 50000
+
+# define inf_dvi_buf_size 800
+# define sup_dvi_buf_size 65536
+
+# define sup_hash_extra sup_max_strings
+# define inf_hash_extra 0
+
+# define inf_expand_depth 100
+# define sup_expand_depth 10000000
+
+# define inf_level_max 0
+# define sup_level_max 500
+
+# define inf_level_chr 1
+# define sup_level_chr 126
+
+# include <stdio.h>
+
+/* Types in the outer block */
+typedef int ASCII_code; /* characters */
+typedef unsigned char eight_bits; /* unsigned one-byte quantity */
+typedef FILE *alpha_file; /* files that contain textual data */
+typedef FILE *byte_file; /* files that contain binary data */
+
+typedef int str_number;
+typedef int pool_pointer;
+typedef unsigned char packed_ASCII_code;
+
+typedef int scaled; /* this type is used for scaled integers */
+typedef char small_number; /* this type is self-explanatory */
+
+typedef float glue_ratio; /* one-word representation of a glue expansion factor */
+
+typedef unsigned short quarterword;
+typedef int halfword;
+
+typedef unsigned char glue_ord; /* infinity to the 0, 1, 2, 3, or 4 power */
+
+typedef unsigned short group_code; /* |save_level| for a level boundary */
+
+typedef int font_index; /* index into |font_info| */
+
+typedef int save_pointer;
+
+/*
+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.
+
+We are assuming that our runtime system is able to read and write UTF-8.
+
+Some of the ASCII codes without visible characters have been given symbolic
+names in this program because they are used with a special meaning.
+*/
+
+
+# define null_code '\0' /* ASCII code that might disappear */
+# define carriage_return '\r' /* ASCII code used at end of line */
+
+/* Global variables */
+extern boolean luainit; /* are we using lua for initializations */
+extern boolean tracefilenames; /* print file open-close info? */
+
+
+extern boolean ini_version; /* are we \.{INITEX}? */
+extern boolean dump_option;
+extern boolean dump_line;
+extern int bound_default;
+extern char *bound_name;
+extern int error_line;
+extern int half_error_line;
+extern int max_print_line;
+extern int max_strings;
+extern int level_max;
+extern int level_chr;
+extern int strings_free;
+extern int font_k;
+extern int buf_size;
+extern int stack_size;
+extern int max_in_open;
+extern int param_size;
+extern int nest_size;
+extern int save_size;
+extern int expand_depth;
+extern int parsefirstlinep;
+extern int filelineerrorstylep;
+extern int haltonerrorp;
+extern boolean quoted_filename;
+
+extern int total_pages;
+extern int dead_cycles;
+
+/*
+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|&.|int|&(an |integer|)\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.
+
+We are assuming 32-bit integers, a halfword must contain at least
+32 bits, and a quarterword must contain at least 16 bits.
+@^system dependencies@>
+
+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 |real| on some computers. Some
+\PASCAL\ compilers will pack an integer whose subrange is `|0..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..127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword..max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword..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.
+
+We cannot use the full range of 32 bits in a halfword, because we have
+to allow negative values for potential backend tricks like web2c's
+dynamic allocation, and parshapes pointers have to be able to store at
+least twice the value |max_halfword| (see below). Therefore,
+|max_halfword| is $2^{30}-1$
+*/
+
+# include "tex/memoryword.h"
+
+# define min_quarterword 0 /*smallest allowable value in a |quarterword| */
+# define max_quarterword 65535 /* largest allowable value in a |quarterword| */
+# define min_halfword -0x3FFFFFFF /* smallest allowable value in a |halfword| */
+# define max_halfword 0x3FFFFFFF
+ /* largest allowable value in a |halfword| */
+
+
+/*
+The following procedure, which is called just before \TeX\ initializes its
+input and output, establishes the initial values of the date and time.
+It calls a macro-defined |dateandtime| routine. |dateandtime| in turn is
+also a C macro, which calls |get_date_and_time|, passing it the addresses of
+the day, month, etc., so they can be set by the routine.
+|get_date_and_time| also sets up interrupt catching if that
+is conditionally compiled in the C code.
+@^system dependencies@>
+*/
+
+# define fix_date_and_time() dateandtime(time_par,day_par,month_par,year_par)
+
+extern int get_luatexversion(void);
+extern str_number get_luatexrevision(void);
+
+extern int ready_already;
+
+extern void main_body(void);
+extern void close_files_and_terminate(void);
+
+extern void final_cleanup(void);
+extern void debug_help(void); /* routine to display various things */
+
+extern int main_initialize(void);
+
+/* lazy me */
+# define get_cur_font() equiv(cur_font_loc)
+# define zset_cur_font set_cur_font
+
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.c b/Build/source/texk/web2c/luatexdir/tex/maincontrol.c
new file mode 100644
index 00000000000..22f601ba275
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.c
@@ -0,0 +1,4245 @@
+/*
+
+maincontrol.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+#define mode mode_par
+#define tail tail_par
+#define head head_par
+#define dir_save dirs_par
+
+/*tex
+
+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 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.
+
+*/
+
+void adjust_space_factor(void)
+{
+ halfword s = get_sf_code(cur_chr);
+ if (s == 1000) {
+ space_factor_par = 1000;
+ } else if (s < 1000) {
+ if (s > 0)
+ space_factor_par = s;
+ } else if (space_factor_par < 1000) {
+ space_factor_par = 1000;
+ } else {
+ space_factor_par = s;
+ }
+}
+
+/*tex
+
+To handle the execution state of |main_control|'s eternal loop, an extra global
+variable is used, along with a macro to define its values.
+
+*/
+
+#define goto_next 0
+#define goto_skip_token 1
+#define goto_return 2
+
+static int main_control_state;
+static int local_level = 0;
+
+/*tex
+
+@* Main control helpers.
+
+Here are all the functions that are called from |main_control| that
+are not already defined elsewhere. For the moment, this list simply
+in the order that the appear in |init_main_control|, below.
+
+*/
+
+static void run_char_num (void) {
+ scan_char_num();
+ cur_chr = cur_val;
+ adjust_space_factor();
+ tail_append(new_char(cur_font_par, cur_chr));
+}
+
+static void run_char (void) {
+ adjust_space_factor();
+ tail_append(new_char(cur_font_par, cur_chr));
+}
+
+static void run_node (void) {
+ halfword n = cur_chr;
+ if (copy_lua_input_nodes_par) {
+ n = copy_node_list(n);
+ }
+ tail_append(n);
+ if (nodetype_has_attributes(type(n)) && node_attr(n) == null) {
+ build_attribute_list(n);
+ }
+ while (vlink(n) != null) {
+ n = vlink(n);
+ tail_append(n);
+ if (nodetype_has_attributes(type(n)) && node_attr(n) == null) {
+ build_attribute_list(n);
+ }
+ }
+}
+
+static void run_lua_call(void) {
+ if (cur_chr <= 0) {
+ normal_error("luacall", "invalid number");
+ } else {
+ str_number u = save_cur_string();
+ luacstrings = 0;
+ luafunctioncall(cur_chr);
+ restore_cur_string(u);
+ if (luacstrings > 0)
+ lua_string_start();
+ }
+}
+
+/*tex
+
+The occurrence of blank spaces is almost part of \TeX's 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.
+
+*/
+
+static void run_app_space (void) {
+ halfword p; /* was a global temp_ptr */
+ int method = disable_space_par ;
+ if (method == 1) {
+ /*tex Don't inject anything, not even zero skip. */
+ } else if (method == 2) {
+ p = new_glue(zero_glue);
+ couple_nodes(tail,p);
+ tail = p;
+ } else if ((abs(mode) + cur_cmd == hmode + spacer_cmd) && (!(space_factor_par == 1000))) {
+ app_space();
+ } else {
+ /*tex Append a normal inter-word space to the current list. */
+ if (glue_is_zero(space_skip_par)) {
+ /*tex Find the glue specification for text spaces in the current font. */
+ p = new_glue(zero_glue);
+ width(p) = space(cur_font_par);
+ stretch(p) = space_stretch(cur_font_par);
+ shrink(p) = space_shrink(cur_font_par);
+
+ } else {
+ p = new_param_glue(space_skip_code);
+ }
+ /*tex So from now we have a subtype with spaces: */
+ subtype(p) = space_skip_code + 1 ;
+ couple_nodes(tail,p);
+ tail = p;
+ }
+}
+
+/*tex
+
+Append a |boundary_node|
+
+*/
+
+static void run_boundary (void) {
+ halfword n ;
+ n = new_node(boundary_node,cur_chr);
+ if ((cur_chr == 1) || (cur_chr == 2) ) {
+ /*tex We expect a user boundary or protrusion boundary. */
+ scan_int();
+ boundary_value(n) = cur_val;
+ }
+ couple_nodes(tail, n);
+ tail = n;
+}
+
+static void run_char_ghost (void) {
+ int t;
+ t = cur_chr;
+ get_x_token();
+ if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd)
+ || (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) {
+ halfword p = new_glyph(get_cur_font(), cur_chr);
+ if (t == 0) {
+ set_is_leftghost(p);
+ } else {
+ set_is_rightghost(p);
+ }
+ tail_append(p);
+ }
+}
+
+static void run_relax (void) {
+ return;
+}
+
+/*tex
+
+|ignore_spaces| is a special case: after it has acted, |get_x_token| has already
+fetched the next token from the input, so that operation in |main_control| should
+be skipped.
+
+*/
+
+static void run_ignore_spaces (void) {
+ if (cur_chr == 0) {
+ /*tex Get the next non-blank non-call... */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ main_control_state = goto_skip_token;
+ } else {
+ int t = scanner_status;
+ scanner_status = normal;
+ get_next();
+ scanner_status = t;
+ cur_cs = prim_lookup(cs_text(cur_cs));
+ if (cur_cs != undefined_primitive) {
+ cur_cmd = get_prim_eq_type(cur_cs);
+ cur_chr = get_prim_equiv(cur_cs);
+ cur_tok = (cur_cmd * STRING_OFFSET) + cur_chr;
+ main_control_state = goto_skip_token;
+ }
+ }
+}
+
+/*tex
+
+|stop| is the second special case. We want |main_control| to return to its caller
+if there is nothing left to do.
+
+*/
+
+static void run_stop (void) {
+ if (its_all_over()) {
+ /*tex this is the only way out */
+ main_control_state= goto_return;
+ }
+}
+
+static void run_non_math_math (void) {
+ back_input();
+ new_graf(true);
+}
+
+/*tex
+
+ We build up an argument to |set_math_char|:
+
+*/
+
+static void run_math_char_num (void) {
+ mathcodeval mval;
+ if (cur_chr == 0)
+ mval = scan_mathchar(tex_mathcode);
+ else if (cur_chr == 1)
+ mval = scan_mathchar(umath_mathcode);
+ else
+ mval = scan_mathchar(umathnum_mathcode);
+ math_char_in_text(mval);
+}
+
+/*tex
+
+ We build up an argument to |set_math_char|:
+*/
+
+static void run_math_given (void) {
+ mathcodeval mval;
+ mval = mathchar_from_integer(cur_chr, tex_mathcode);
+ math_char_in_text(mval);
+}
+
+/*tex
+
+ We build up an argument to |set_math_char| the \LUATEX\ way:
+*/
+
+static void run_xmath_given (void) {
+ mathcodeval mval;
+ mval = mathchar_from_integer(cur_chr, umath_mathcode);
+ math_char_in_text(mval);
+}
+
+/*tex
+
+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|.
+
+*/
+
+static void run_rule (void) {
+ tail_append(scan_rule_spec());
+ if (abs(mode) == vmode)
+ prev_depth_par = ignore_depth;
+ else if (abs(mode) == hmode)
+ space_factor_par = 1000;
+}
+
+/*tex
+
+Many of the actions related to box-making are triggered by the appearance of
+braces in the input. For example, when the user says `\.{\\hbox} \.{to}
+\.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode, the information
+about the box size (100pt, |exactly|) is put onto |save_stack| with a level
+boundary word just above it, and |cur_group:=adjusted_hbox_group|; \TeX\ enters
+restricted horizontal mode to process the hlist. The right brace eventually
+causes |save_stack| to be restored to its former state, at which time the
+information about the box size (100pt, |exactly|) is available once again; a box
+is packaged and we leave restricted horizontal mode, appending the new box to the
+current list of the enclosing mode (in this case to the current list of vertical
+mode), followed by any vertical adjustments that were removed from the box by
+|hpack|.
+
+The next few sections of the program are therefore concerned with the treatment
+of left and right curly braces.
+
+If a left brace occurs in the middle of a page or paragraph, it simply introduces
+a new level of grouping, and the matching right brace will not have such a
+drastic effect. Such grouping affects neither the mode nor the current list.
+
+*/
+
+static void run_left_brace (void) {
+ new_save_level(simple_group);
+ eq_word_define(int_base + no_local_whatsits_code, 0);
+ eq_word_define(int_base + no_local_dirs_code, 0);
+}
+
+static void run_begin_group (void) {
+ new_save_level(semi_simple_group);
+ eq_word_define(int_base + no_local_whatsits_code, 0);
+ eq_word_define(int_base + no_local_dirs_code, 0);
+}
+
+static void run_end_group (void) {
+ if (cur_group == semi_simple_group) {
+ fixup_directions();
+ } else {
+ off_save();
+ }
+}
+
+/*tex
+
+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|.
+
+*/
+
+static void run_move (void) {
+ int t = cur_chr;
+ scan_normal_dimen();
+ if (t == 0)
+ scan_box(cur_val);
+ else
+ scan_box(-cur_val);
+}
+
+static void run_leader_ship (void) {
+ scan_box(leader_flag - a_leaders + cur_chr);
+}
+
+static void run_make_box (void) {
+ begin_box(0);
+}
+
+static void run_box_dir (void) {
+ scan_register_num();
+ cur_box = box(cur_val);
+ scan_optional_equals();
+ scan_direction();
+ if (cur_box != null)
+ box_dir(cur_box) = cur_val;
+}
+
+static void run_box_direction (void) {
+ scan_register_num();
+ cur_box = box(cur_val);
+ scan_optional_equals();
+ scan_int();
+ check_dir_value(cur_val);
+ if (cur_box != null)
+ box_dir(cur_box) = cur_val;
+}
+
+/*tex
+
+There is a really small patch to add a new primitive called \.{\\quitvmode}. In
+vertical modes, it is identical to \.{\\indent}, but in horizontal and math modes
+it is really a no-op (as opposed to \.{\\indent}, which executes the
+|indent_in_hmode| procedure).
+
+A paragraph begins when horizontal-mode material occurs in vertical mode, or when
+the paragraph is explicitly started by `\.{\\quitvmode}', `\.{\\indent}' or
+`\.{\\noindent}'.
+
+*/
+
+static void run_start_par_vmode (void) {
+ new_graf((cur_chr > 0));
+}
+
+static void run_start_par (void) {
+ if (cur_chr != 2)
+ indent_in_hmode();
+}
+
+static void run_new_graf (void) {
+ back_input();
+ new_graf(true);
+}
+
+/*tex
+
+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}.
+
+*/
+
+static void run_par_end_vmode (void) {
+ normal_paragraph();
+ if (mode > 0) {
+ checked_page_filter(vmode_par);
+ build_page();
+ }
+}
+
+static void run_par_end_hmode (void) {
+ if (align_state < 0) {
+ /*tex This tries to recover from an alignment that didn't end properly. */
+ off_save();
+ }
+ /* This takes us to the enclosing mode, if |mode>0|. */
+ end_graf(bottom_level);
+ if (mode == vmode) {
+ checked_page_filter(hmode_par);
+ build_page();
+ }
+}
+
+static void append_italic_correction_mmode (void) {
+ tail_append(new_kern(0));
+}
+
+static void run_local_box (void) {
+ append_local_box(cur_chr);
+}
+
+static void run_halign_mmode (void) {
+ if (privileged()) {
+ if (cur_group == math_shift_group)
+ init_align();
+ else
+ off_save();
+ }
+}
+
+static void run_eq_no (void) {
+ if (privileged()) {
+ if (cur_group == math_shift_group)
+ start_eq_no();
+ else
+ off_save();
+ }
+}
+
+static void run_letter_mmode (void) {
+ set_math_char(get_math_code(cur_chr));
+}
+
+static void run_char_num_mmode (void) {
+ scan_char_num();
+ cur_chr = cur_val;
+ set_math_char(get_math_code(cur_chr));
+}
+
+static void run_math_char_num_mmode (void) {
+ mathcodeval mval;
+ if (cur_chr == 0)
+ mval = scan_mathchar(tex_mathcode);
+ else if (cur_chr == 1)
+ mval = scan_mathchar(umath_mathcode);
+ else
+ mval = scan_mathchar(umathnum_mathcode);
+ set_math_char(mval);
+}
+
+static void run_math_given_mmode (void) {
+ mathcodeval mval;
+ mval = mathchar_from_integer(cur_chr, tex_mathcode);
+ set_math_char(mval);
+}
+
+static void run_xmath_given_mmode (void) {
+ mathcodeval mval;
+ mval = mathchar_from_integer(cur_chr, umath_mathcode);
+ set_math_char(mval);
+}
+
+static void run_delim_num (void) {
+ mathcodeval mval;
+ if (cur_chr == 0)
+ mval = scan_delimiter_as_mathchar(tex_mathcode);
+ else
+ mval = scan_delimiter_as_mathchar(umath_mathcode);
+ set_math_char(mval);
+}
+
+static void run_vcenter (void) {
+ scan_spec(vcenter_group);
+ normal_paragraph();
+ push_nest();
+ mode = -vmode;
+ prev_depth_par = ignore_depth;
+ if (every_vbox_par != null)
+ begin_token_list(every_vbox_par, every_vbox_text);
+}
+
+static void run_math_style (void) {
+ tail_append(new_style((small_number) cur_chr));
+}
+
+static void run_non_script (void) {
+ tail_append(new_glue(zero_glue));
+ subtype(tail) = cond_math_glue;
+}
+
+static void run_math_choice (void) {
+ if (cur_chr == 0)
+ append_choices();
+ else
+ setup_math_style();
+}
+
+static void run_math_shift (void) {
+ if (cur_group == math_shift_group)
+ after_math();
+ else
+ off_save();
+}
+
+static void run_after_assignment (void) {
+ get_token();
+ after_token = cur_tok;
+}
+
+static void run_after_group (void) {
+ get_token();
+ save_for_after(cur_tok);
+}
+
+static void run_extension (void) {
+ do_extension(0);
+}
+
+static void run_normal (void) {
+{
+ switch (cur_chr) {
+ case save_pos_code:
+ new_whatsit(save_pos_node);
+ break;
+ case save_cat_code_table_code:
+ scan_int();
+ if ((cur_val < 0) || (cur_val > 0x7FFF)) {
+ print_err("Invalid \\catcode table");
+ help1(
+ "All \\catcode table ids must be between 0 and 0x7FFF"
+ );
+ error();
+ } else {
+ if (cur_val == cat_code_table_par) {
+ print_err("Invalid \\catcode table");
+ help1(
+ "You cannot overwrite the current \\catcode table"
+ );
+ error();
+ } else {
+ copy_cat_codes(cat_code_table_par, cur_val);
+ }
+ }
+ break;
+ case init_cat_code_table_code:
+ scan_int();
+ if ((cur_val < 0) || (cur_val > 0x7FFF)) {
+ print_err("Invalid \\catcode table");
+ help1(
+ "All \\catcode table ids must be between 0 and 0x7FFF"
+ );
+ error();
+ } else {
+ if (cur_val == cat_code_table_par) {
+ print_err("Invalid \\catcode table");
+ help1(
+ "You cannot overwrite the current \\catcode table"
+ );
+ error();
+ } else {
+ initex_cat_codes(cur_val);
+ }
+ }
+ break;
+ case set_random_seed_code:
+ /* Negative random seed values are silently converted to positive ones */
+ scan_int();
+ if (cur_val < 0)
+ negate(cur_val);
+ random_seed = cur_val;
+ init_randoms(random_seed);
+ break;
+ case late_lua_code:
+ new_whatsit(late_lua_node); /* type == normal */
+ late_lua_name(tail) = scan_lua_state();
+ (void) scan_toks(false, false);
+ late_lua_data(tail) = def_ref;
+ break;
+ case late_lua_call_code:
+ new_whatsit(late_lua_node);
+ late_lua_type(tail) = lua_refid_call;
+ scan_int();
+ late_lua_data(tail) = cur_val;
+ break;
+ case expand_font_code:
+ read_expand_font();
+ break;
+ default:
+ confusion("int1");
+ break;
+ }
+ }
+}
+
+/*tex
+
+This is experimental and not used for production, only for testing and writing
+macros (some options stay).
+
+*/
+
+#define mathoption_set_int(A) \
+ scan_int(); \
+ word_define(mathoption_int_base+A, cur_val);
+
+static void run_option(void) {
+ int a = 0 ;
+ switch (cur_chr) {
+ case math_option_code:
+ if (scan_keyword("old")) {
+ mathoption_set_int(c_mathoption_old_code);
+ /*
+ } else if (scan_keyword("umathcodemeaning")) {
+ mathoption_set_int(c_mathoption_umathcode_meaning_code);
+ */
+ } else {
+ normal_warning("mathoption","unknown key");
+ }
+ break;
+ default:
+ /* harmless */
+ break;
+ }
+}
+
+static void lua_function_call(void) {
+ scan_int();
+ if (cur_val <= 0) {
+ normal_error("luafunctioncall", "invalid number");
+ } else {
+ str_number u = save_cur_string();
+ luacstrings = 0;
+ luafunctioncall(cur_val);
+ restore_cur_string(u);
+ if (luacstrings > 0)
+ lua_string_start();
+ }
+}
+
+static void lua_bytecode_call(void) {
+ scan_int();
+ if (cur_val < 0 || cur_val > 65535) {
+ normal_error("luabytecodecall", "invalid number");
+ } else {
+ str_number u = save_cur_string();
+ luacstrings = 0;
+ luabytecodecall(cur_val);
+ restore_cur_string(u);
+ if (luacstrings > 0)
+ lua_string_start();
+ }
+}
+
+/*tex
+
+For mode-independent commands, the following macro is useful.
+
+Also, there 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, and it makes sense ot have a macro for that as well.
+
+*/
+
+#define any_mode(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; jump_table[mmode+(A)]=B
+#define non_math(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B;
+
+/*tex
+
+The |main_control| uses a jump table, and |init_main_control| sets that table up.
+
+*/
+
+typedef void (*main_control_function) (void);
+
+main_control_function *jump_table;
+
+static void init_main_control (void) {
+ jump_table = xmalloc((mmode+max_command_cmd+1) * sizeof(main_control_function)) ;
+
+ jump_table[hmode + char_num_cmd] = run_char_num;
+ jump_table[hmode + letter_cmd] = run_char;
+ jump_table[hmode + other_char_cmd] = run_char;
+ jump_table[hmode + char_given_cmd] = run_char;
+ jump_table[hmode + spacer_cmd] = run_app_space;
+ jump_table[hmode + ex_space_cmd] = run_app_space;
+ jump_table[mmode + ex_space_cmd] = run_app_space;
+ jump_table[hmode + boundary_cmd] = run_boundary;
+ jump_table[hmode + char_ghost_cmd] = run_char_ghost;
+ jump_table[mmode + char_ghost_cmd] = run_char_ghost;
+ any_mode(relax_cmd, run_relax);
+ jump_table[vmode + spacer_cmd] = run_relax;
+ jump_table[mmode + spacer_cmd] = run_relax;
+ jump_table[mmode + boundary_cmd] = run_relax;
+ any_mode(ignore_spaces_cmd,run_ignore_spaces);
+ jump_table[vmode + stop_cmd] = run_stop;
+ jump_table[vmode + math_char_num_cmd] = run_non_math_math;
+ jump_table[vmode + math_given_cmd] = run_non_math_math;
+ jump_table[vmode + xmath_given_cmd] = run_non_math_math;
+ jump_table[hmode + math_char_num_cmd] = run_math_char_num;
+ jump_table[hmode + math_given_cmd] = run_math_given;
+ jump_table[hmode + xmath_given_cmd] = run_xmath_given;
+
+ jump_table[vmode + vmove_cmd] = report_illegal_case;
+ jump_table[hmode + hmove_cmd] = report_illegal_case;
+ jump_table[mmode + hmove_cmd] = report_illegal_case;
+ any_mode(last_item_cmd, report_illegal_case);
+ jump_table[vmode + vadjust_cmd] = report_illegal_case;
+ jump_table[vmode + ital_corr_cmd] = report_illegal_case;
+ non_math(eq_no_cmd,report_illegal_case);
+ any_mode(mac_param_cmd,report_illegal_case);
+
+ non_math(sup_mark_cmd, insert_dollar_sign);
+ non_math(sub_mark_cmd, insert_dollar_sign);
+ non_math(super_sub_script_cmd, insert_dollar_sign);
+ non_math(no_super_sub_script_cmd, insert_dollar_sign);
+ non_math(math_comp_cmd, insert_dollar_sign);
+ non_math(delim_num_cmd, insert_dollar_sign);
+ non_math(left_right_cmd, insert_dollar_sign);
+ non_math(above_cmd, insert_dollar_sign);
+ non_math(radical_cmd, insert_dollar_sign);
+ non_math(math_style_cmd, insert_dollar_sign);
+ non_math(math_choice_cmd, insert_dollar_sign);
+ non_math(vcenter_cmd, insert_dollar_sign);
+ non_math(non_script_cmd, insert_dollar_sign);
+ non_math(mkern_cmd, insert_dollar_sign);
+ non_math(limit_switch_cmd, insert_dollar_sign);
+ non_math(mskip_cmd, insert_dollar_sign);
+ non_math(math_accent_cmd, insert_dollar_sign);
+ jump_table[mmode + endv_cmd] = insert_dollar_sign;
+ jump_table[mmode + par_end_cmd] = insert_dollar_sign_par_end;
+ jump_table[mmode + stop_cmd] = insert_dollar_sign;
+ jump_table[mmode + vskip_cmd] = insert_dollar_sign;
+ jump_table[mmode + un_vbox_cmd] = insert_dollar_sign;
+ jump_table[mmode + valign_cmd] = insert_dollar_sign;
+ jump_table[mmode + hrule_cmd] = insert_dollar_sign;
+ jump_table[mmode + no_hrule_cmd] = insert_dollar_sign;
+ jump_table[vmode + hrule_cmd] = run_rule;
+ jump_table[vmode + no_hrule_cmd] = run_rule;
+ jump_table[hmode + vrule_cmd] = run_rule;
+ jump_table[hmode + no_vrule_cmd] = run_rule;
+ jump_table[mmode + vrule_cmd] = run_rule;
+ jump_table[mmode + no_vrule_cmd] = run_rule;
+ jump_table[vmode + vskip_cmd] = append_glue;
+ jump_table[hmode + hskip_cmd] = append_glue;
+ jump_table[mmode + hskip_cmd] = append_glue;
+ jump_table[mmode + mskip_cmd] = append_glue;
+ any_mode(kern_cmd, append_kern);
+ jump_table[mmode + mkern_cmd] = append_kern;
+ non_math(left_brace_cmd, run_left_brace);
+ any_mode(begin_group_cmd,run_begin_group);
+ any_mode(end_group_cmd, run_end_group);
+ any_mode(right_brace_cmd, handle_right_brace);
+ jump_table[vmode + hmove_cmd] = run_move;
+ jump_table[hmode + vmove_cmd] = run_move;
+ jump_table[mmode + vmove_cmd] = run_move;
+ any_mode(leader_ship_cmd, run_leader_ship);
+ any_mode(make_box_cmd, run_make_box);
+ any_mode(assign_box_dir_cmd, run_box_dir);
+ any_mode(assign_box_direction_cmd, run_box_direction);
+ jump_table[vmode + start_par_cmd] = run_start_par_vmode;
+ jump_table[hmode + start_par_cmd] = run_start_par;
+ jump_table[mmode + start_par_cmd] = run_start_par;
+ jump_table[vmode + letter_cmd] = run_new_graf;
+ jump_table[vmode + other_char_cmd] = run_new_graf;
+ jump_table[vmode + char_num_cmd] = run_new_graf;
+ jump_table[vmode + char_given_cmd] = run_new_graf;
+ jump_table[vmode + char_ghost_cmd] = run_new_graf;
+ jump_table[vmode + math_shift_cmd] = run_new_graf;
+ jump_table[vmode + math_shift_cs_cmd] = run_new_graf;
+ jump_table[vmode + un_hbox_cmd] = run_new_graf;
+ jump_table[vmode + vrule_cmd] = run_new_graf;
+ jump_table[vmode + no_vrule_cmd] = run_new_graf;
+ jump_table[vmode + accent_cmd] = run_new_graf;
+ jump_table[vmode + discretionary_cmd] = run_new_graf;
+ jump_table[vmode + hskip_cmd] = run_new_graf;
+ jump_table[vmode + valign_cmd] = run_new_graf;
+ jump_table[vmode + ex_space_cmd] = run_new_graf;
+ jump_table[vmode + boundary_cmd] = run_new_graf;
+ jump_table[vmode + par_end_cmd] = run_par_end_vmode;
+ jump_table[hmode + par_end_cmd] = run_par_end_hmode;
+ jump_table[hmode + stop_cmd] = head_for_vmode;
+ jump_table[hmode + vskip_cmd] = head_for_vmode;
+ jump_table[hmode + hrule_cmd] = head_for_vmode;
+ jump_table[hmode + no_hrule_cmd] = head_for_vmode;
+ jump_table[hmode + un_vbox_cmd] = head_for_vmode;
+ jump_table[hmode + halign_cmd] = head_for_vmode;
+ any_mode(insert_cmd,begin_insert_or_adjust);
+ jump_table[hmode + vadjust_cmd] = begin_insert_or_adjust;
+ jump_table[mmode + vadjust_cmd] = begin_insert_or_adjust;
+ any_mode(mark_cmd, handle_mark);
+ any_mode(break_penalty_cmd, append_penalty);
+ any_mode(remove_item_cmd, delete_last);
+ jump_table[vmode + un_vbox_cmd] = unpackage;
+ jump_table[hmode + un_hbox_cmd] = unpackage;
+ jump_table[mmode + un_hbox_cmd] = unpackage;
+ jump_table[hmode + ital_corr_cmd] = append_italic_correction;
+ jump_table[mmode + ital_corr_cmd] = append_italic_correction_mmode;
+ jump_table[hmode + discretionary_cmd] = append_discretionary;
+ jump_table[mmode + discretionary_cmd] = append_discretionary;
+ any_mode(assign_local_box_cmd, run_local_box);
+ jump_table[hmode + accent_cmd] = make_accent;
+ any_mode(car_ret_cmd,align_error);
+ any_mode(tab_mark_cmd,align_error);
+ any_mode(no_align_cmd,no_align_error);
+ any_mode(omit_cmd, omit_error);
+ jump_table[vmode + halign_cmd] = init_align;
+ jump_table[hmode + valign_cmd] = init_align;
+ jump_table[mmode + halign_cmd] = run_halign_mmode;
+ jump_table[vmode + endv_cmd] = do_endv;
+ jump_table[hmode + endv_cmd] = do_endv;
+ any_mode(end_cs_name_cmd, cs_error);
+ jump_table[hmode + math_shift_cmd] = init_math;
+ jump_table[hmode + math_shift_cs_cmd] = init_math;
+ jump_table[mmode + eq_no_cmd] = run_eq_no;
+ jump_table[mmode + left_brace_cmd] = math_left_brace;
+ jump_table[mmode + letter_cmd] = run_letter_mmode;
+ jump_table[mmode + other_char_cmd] = run_letter_mmode;
+ jump_table[mmode + char_given_cmd] = run_letter_mmode;
+ jump_table[mmode + char_num_cmd] = run_char_num_mmode;
+ jump_table[mmode + math_char_num_cmd] = run_math_char_num_mmode;
+ jump_table[mmode + math_given_cmd] = run_math_given_mmode;
+ jump_table[mmode + xmath_given_cmd] = run_xmath_given_mmode;
+ jump_table[mmode + delim_num_cmd] = run_delim_num;
+ jump_table[mmode + math_comp_cmd] = math_math_comp;
+ jump_table[mmode + limit_switch_cmd] = math_limit_switch;
+ jump_table[mmode + radical_cmd] = math_radical;
+ jump_table[mmode + accent_cmd] = math_ac;
+ jump_table[mmode + math_accent_cmd] = math_ac;
+ jump_table[mmode + vcenter_cmd] = run_vcenter;
+ jump_table[mmode + math_style_cmd] = run_math_style;
+ jump_table[mmode + non_script_cmd] = run_non_script;
+ jump_table[mmode + math_choice_cmd] = run_math_choice;
+ jump_table[mmode + above_cmd] = math_fraction;
+ jump_table[mmode + sub_mark_cmd] = sub_sup;
+ jump_table[mmode + sup_mark_cmd] = sub_sup;
+ jump_table[mmode + super_sub_script_cmd] = sub_sup;
+ jump_table[mmode + no_super_sub_script_cmd] = no_sub_sup;
+ jump_table[mmode + left_right_cmd] = math_left_right;
+ jump_table[mmode + math_shift_cmd] = run_math_shift;
+ jump_table[mmode + math_shift_cs_cmd] = run_math_shift;
+ any_mode(toks_register_cmd, prefixed_command);
+ any_mode(assign_toks_cmd, prefixed_command);
+ any_mode(assign_int_cmd, prefixed_command);
+ any_mode(assign_attr_cmd, prefixed_command);
+ any_mode(assign_dir_cmd, prefixed_command);
+ any_mode(assign_direction_cmd, prefixed_command);
+ any_mode(assign_dimen_cmd, prefixed_command);
+ any_mode(assign_glue_cmd, prefixed_command);
+ any_mode(assign_mu_glue_cmd, prefixed_command);
+ any_mode(assign_font_dimen_cmd, prefixed_command);
+ any_mode(assign_font_int_cmd, prefixed_command);
+ any_mode(set_aux_cmd, prefixed_command);
+ any_mode(set_prev_graf_cmd, prefixed_command);
+ any_mode(set_page_dimen_cmd, prefixed_command);
+ any_mode(set_page_int_cmd, prefixed_command);
+ any_mode(set_box_dimen_cmd, prefixed_command);
+ any_mode(set_tex_shape_cmd, prefixed_command);
+ any_mode(set_etex_shape_cmd, prefixed_command);
+ any_mode(def_char_code_cmd, prefixed_command);
+ any_mode(def_del_code_cmd, prefixed_command);
+ any_mode(extdef_math_code_cmd, prefixed_command);
+ any_mode(extdef_del_code_cmd, prefixed_command);
+ any_mode(def_family_cmd, prefixed_command);
+ any_mode(set_math_param_cmd, prefixed_command);
+ any_mode(set_font_cmd, prefixed_command);
+ any_mode(def_font_cmd, prefixed_command);
+ any_mode(letterspace_font_cmd, prefixed_command);
+ any_mode(copy_font_cmd, prefixed_command);
+ any_mode(set_font_id_cmd, prefixed_command);
+ any_mode(register_cmd, prefixed_command);
+ any_mode(advance_cmd, prefixed_command);
+ any_mode(multiply_cmd, prefixed_command);
+ any_mode(divide_cmd, prefixed_command);
+ any_mode(prefix_cmd, prefixed_command);
+ any_mode(let_cmd, prefixed_command);
+ any_mode(shorthand_def_cmd, prefixed_command);
+ any_mode(read_to_cs_cmd, prefixed_command);
+ any_mode(def_cmd, prefixed_command);
+ any_mode(set_box_cmd, prefixed_command);
+ any_mode(hyph_data_cmd, prefixed_command);
+ any_mode(set_interaction_cmd, prefixed_command);
+ any_mode(after_assignment_cmd,run_after_assignment);
+ any_mode(after_group_cmd,run_after_group);
+ any_mode(in_stream_cmd,open_or_close_in);
+ any_mode(message_cmd,issue_message);
+ any_mode(case_shift_cmd, shift_case);
+ any_mode(xray_cmd, show_whatever);
+ any_mode(normal_cmd, run_normal);
+ any_mode(extension_cmd, run_extension);
+ any_mode(option_cmd, run_option);
+
+ any_mode(lua_function_call_cmd, lua_function_call);
+ any_mode(lua_bytecode_call_cmd, lua_bytecode_call);
+ any_mode(def_lua_call_cmd, prefixed_command);
+ any_mode(lua_call_cmd, run_lua_call);
+ /* any_mode(lua_expandable_call_cmd, run_lua_call); */ /* no! outside jump table anyway, handled in expand() */
+ any_mode(node_cmd, run_node);
+
+ any_mode(combine_toks_cmd, combine_the_toks);
+}
+
+/*tex
+
+ And here is |main_control| itself. It is quite short nowadays.
+
+*/
+
+void main_control(void)
+{
+ main_control_state = goto_next;
+ init_main_control() ;
+ if (equiv(every_job_loc) != null) {
+ begin_token_list(equiv(every_job_loc), every_job_text);
+ }
+ while (1) {
+ if (main_control_state == goto_skip_token) {
+ main_control_state = goto_next;
+ } else {
+ get_x_token();
+ }
+ /*tex
+ Give diagnostic information, if requested When a new token has just
+ been fetched at |big_switch|, we have an ideal place to monitor
+ \TeX's activity.
+ */
+ if (interrupt != 0 && OK_to_interrupt) {
+ back_input();
+ check_interrupt();
+ continue;
+ }
+ if (tracing_commands_par > 0) {
+ show_cur_cmd_chr();
+ }
+ /*tex run the command */
+ (jump_table[(abs(mode) + cur_cmd)])();
+ if (main_control_state == goto_return) {
+ return;
+ }
+ }
+ /*tex not reached */
+ return;
+}
+
+/*tex
+
+We assume a trailing relax: |{...}\relax|, so we don't need a |back_input()| here.
+
+*/
+
+extern void local_control_message(const char *s)
+{
+ tprint("local control level ");
+ print_int(local_level);
+ tprint(": ");
+ tprint(s);
+ tprint_nl("");
+}
+
+/*tex Note for me (HH): in luametatex I have the option to use the save stack which for
+ some experiments seems to work better. But I'll probably not go forward with
+ some intended extension anyway (too messy), so we're currently in sync.
+ Instead there are now two optional parameters in |runtoks|, plus a |quittoks|
+ companion. I also added some more tracing as well as a warning when we jump
+ out of control too often.
+*/
+
+void local_control(void)
+{
+ /*tex Wrr to saving the state local_control is like some of the conv_toks which
+ is not entirely correct but good enough for now. Future extensions might
+ demand another solution.
+ */
+ int save_scanner_status = scanner_status;
+ halfword save_def_ref = def_ref;
+ halfword save_warning_index = warning_index;
+ int ll = local_level;
+ main_control_state = goto_next;
+ local_level += 1;
+ if (tracing_nesting_par > 2) {
+ local_control_message("entering local control");
+ }
+ while (1) {
+ if (main_control_state == goto_skip_token) {
+ main_control_state = goto_next;
+ } else {
+ get_x_token();
+ }
+ if (interrupt != 0 && OK_to_interrupt) {
+ back_input();
+ check_interrupt();
+ continue;
+ }
+ if (tracing_commands_par > 0) {
+ show_cur_cmd_chr();
+ }
+ (jump_table[(abs(mode) + cur_cmd)])();
+ if (local_level <= ll) {
+ main_control_state = goto_next;
+ if (tracing_nesting_par > 2) {
+ local_control_message("leaving local control due to level change");
+ }
+ break;
+ } else if (main_control_state == goto_return) {
+ if (tracing_nesting_par > 2) {
+ local_control_message("leaving local control due to triggering");
+ }
+ break;
+ }
+ }
+ /*tex From the perspective of ending and changing the level this is the
+ wrong spot, as it should be done in |end_local_control| in which case
+ we should use the save stack. Maybe some day.
+ */
+ scanner_status = save_scanner_status;
+ def_ref = save_def_ref;
+ warning_index = save_warning_index;
+}
+
+void end_local_control(void )
+{
+ if (local_level > 0) {
+ local_level -= 1;
+ } else {
+ local_control_message("redundant end local control");
+ }
+}
+
+/*tex
+ We need to go back to the main loop. This is rather nasty and dirty
+ and counterintuive code and there might be a cleaner way. Basically
+ we trigger the main control state from here.
+
+ \starttyping
+ 0 0 \directlua{token.scan_list()}\hbox{!}
+ -1 0 \setbox0\hbox{x}\directlua{token.scan_list()}\box0
+ 1 1 \toks0={\directlua{token.scan_list()}\hbox{x}}\directlua{tex.runtoks(0)}
+ 0 0 1 1 \directlua{tex.box[0]=token.scan_list()}\hbox{x\directlua{node.write(token.scan_list())}\hbox{x}}
+ 0 0 0 1 \setbox0\hbox{x}\directlua{tex.box[0]=token.scan_list()}\hbox{x\directlua{node.write(token.scan_list())}\box0}
+ \stoptyping
+
+ It's rather fragile code so we added some tracing options.
+
+*/
+
+halfword local_scan_box(void)
+{
+ int old_mode = mode;
+ int ll = local_level;
+ mode = -hmode;
+ scan_box(lua_scan_flag);
+ if (local_level == ll) {
+ /*tex |\directlua{print(token.scan_list())}\hbox{!}| (n n) */
+ if (tracing_nesting_par > 2) {
+ local_control_message("entering at end of box scanning");
+ }
+ local_control();
+ } else {
+ /*tex |\directlua{print(token.scan_list())}\box0| (n-1 n) */
+ /*
+ if (tracing_nesting_par > 2) {
+ local_control_message("setting level after box scanning");
+ }
+ */
+ local_level = ll;
+ }
+ mode = old_mode;
+ return cur_box;
+}
+
+/*tex
+
+ We have an issue with modes when we quit here because we're coming
+ from and still staying at the \LUA\ end. So, unless we're already
+ nested, we trigger an end_local_level token (an extension code).
+
+*/
+
+static void wrapup_local_scan_box(void)
+{
+ /*
+ if (tracing_nesting_par > 2) {
+ local_control_message("leaving box scanner");
+ }
+ */
+ local_level -= 1;
+}
+
+int current_local_level(void)
+{
+ return local_level;
+}
+
+void app_space(void)
+{ /* handle spaces when |space_factor<>1000| */
+ halfword q; /* glue node */
+ if ((space_factor_par >= 2000) && (! glue_is_zero(xspace_skip_par))) {
+ q = new_param_glue(xspace_skip_code);
+ /* so from now we have a subtype with spaces: */
+ subtype(q) = xspace_skip_code + 1;
+ } else {
+ if (!glue_is_zero(space_skip_par)) {
+ q = new_glue(space_skip_par);
+ } else {
+ q = new_glue(zero_glue);
+ width(q) = space(cur_font_par);
+ stretch(q) = space_stretch(cur_font_par);
+ shrink(q) = space_shrink(cur_font_par);
+ }
+ /* Modify the glue specification in |q| according to the space factor */
+ if (space_factor_par >= 2000)
+ width(q) = width(q) + extra_space(cur_font_par);
+ stretch(q) = xn_over_d(stretch(q), space_factor_par, 1000);
+ shrink(q) = xn_over_d(shrink(q), 1000, space_factor_par);
+
+ /* so from now we have a subtype with spaces: */
+ subtype(q) = space_skip_code + 1;
+ }
+ couple_nodes(tail, q);
+ tail = q;
+}
+
+void insert_dollar_sign(void)
+{
+ back_input();
+ cur_tok = math_shift_token + '$';
+ print_err("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();
+}
+
+/*tex
+
+ We can silently ignore \.{\\par}s in a math formula.
+
+*/
+
+void insert_dollar_sign_par_end(void)
+{
+ if (!suppress_mathpar_error_par) {
+ insert_dollar_sign() ;
+ }
+}
+
+/*tex
+
+The `|you_cant|' procedure prints a line saying that the current command is
+illegal in the current mode; it identifies these things symbolically.
+
+*/
+
+void you_cant(void)
+{
+ print_err("You can't use `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ print_in_mode(mode);
+}
+
+/*tex
+
+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.'
+
+*/
+
+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();
+}
+
+/*tex
+
+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.
+
+*/
+
+boolean privileged(void)
+{
+ if (mode > 0) {
+ return true;
+ } else {
+ report_illegal_case();
+ return false;
+ }
+}
+
+/*tex
+
+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.''
+
+*/
+
+
+boolean 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_par;
+ tail_append(new_glue(fill_glue));
+ tail_append(new_penalty(-010000000000,final_penalty));
+ normal_page_filter(end);
+ build_page(); /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */
+ }
+ return false;
+}
+
+/*tex
+
+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|.
+
+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.
+
+*/
+
+void append_glue(void)
+{
+ int s = cur_chr;
+ switch (s) {
+ case fil_code:
+ cur_val = new_glue(fil_glue);
+ break;
+ case fill_code:
+ cur_val = new_glue(fill_glue);
+ break;
+ case ss_code:
+ cur_val = new_glue(ss_glue);
+ break;
+ case fil_neg_code:
+ cur_val = new_glue(fil_neg_glue);
+ break;
+ case skip_code:
+ scan_glue(glue_val_level);
+ break;
+ case mskip_code:
+ scan_glue(mu_val_level);
+ break;
+ }
+ /* now |cur_val| points to the glue specification */
+ tail_append(new_glue(cur_val));
+ flush_node(cur_val);
+ if (s > skip_code) {
+ subtype(tail) = mu_glue;
+ }
+}
+
+void append_kern(void)
+{
+ int s = cur_chr; /* |subtype| of the kern node */
+ scan_dimen((s == mu_glue), false, false);
+ tail_append(new_kern(cur_val));
+ subtype(tail) = (quarterword) s;
+}
+
+/*tex
+
+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.
+
+*/
+
+void off_save(void)
+{
+ halfword p, q;
+ if (cur_group == bottom_level) {
+ /*tex Drop current token and complain that it was unmatched */
+ print_err("Extra ");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ help1(
+ "Things are pretty mixed up, but I think the worst is over."
+ );
+ error();
+
+ } else {
+ back_input();
+ p = get_avail();
+ set_token_link(temp_token_head, p);
+ print_err("Missing ");
+ /*tex
+ Prepare to insert a token that matches |cur_group|, and print what it
+ is. At this point, |link(temp_token_head)=p|, a pointer to an empty
+ one-word node.
+ */
+ switch (cur_group) {
+ case semi_simple_group:
+ set_token_info(p, cs_token_flag + frozen_end_group);
+ tprint_esc("endgroup");
+ break;
+ case math_shift_group:
+ set_token_info(p, math_shift_token + '$');
+ print_char('$');
+ break;
+ case math_left_group:
+ set_token_info(p, cs_token_flag + frozen_right);
+ q = get_avail();
+ set_token_link(p, q);
+ p = token_link(p);
+ set_token_info(p, other_token + '.');
+ tprint_esc("right.");
+ break;
+ default:
+ set_token_info(p, right_brace_token + '}');
+ print_char('}');
+ break;
+ }
+ tprint(" inserted");
+ ins_list(token_link(temp_token_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();
+ }
+}
+/*tex
+
+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.
+
+*/
+
+void handle_right_brace(void)
+{
+ halfword p, q; /* for short-term use */
+ scaled d; /* holds |split_max_depth| in |insert_group| */
+ int f; /* holds |floating_penalty| in |insert_group| */
+ p = null;
+ switch (cur_group) {
+ case simple_group:
+ fixup_directions();
+ break;
+ case bottom_level:
+ print_err("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;
+ /*tex
+ 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.
+ */
+ case hbox_group:
+ if (fixup_boxes_par) {
+ /*tex
+ This is unofficial! Fixing up (also elsewhere) might become default
+ some day but for a while I will test this in ConTeXt.
+ */
+ fixup_directions_only();
+ }
+ package(0);
+ break;
+ case adjusted_hbox_group:
+ adjust_tail = adjust_head;
+ pre_adjust_tail = pre_adjust_head;
+ package(0);
+ break;
+ case vbox_group:
+ end_graf(vbox_group);
+ package(0);
+ break;
+ case vtop_group:
+ end_graf(vtop_group);
+ package(vtop_code);
+ break;
+ case insert_group:
+ end_graf(insert_group);
+ q = new_glue(split_top_skip_par);
+ d = split_max_depth_par;
+ f = floating_penalty_par;
+ unsave();
+ save_ptr--;
+ /*tex
+ Now |saved_value(0)| is the insertion number, or the |vadjust| subtype.
+ */
+ p = vpack(vlink(head), 0, additional, -1);
+ pop_nest();
+ if (saved_type(0) == saved_insert) {
+ tail_append(new_node(ins_node, saved_value(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 if (saved_type(0) == saved_adjust) {
+ tail_append(new_node(adjust_node, saved_value(0)));
+ adjust_ptr(tail) = list_ptr(p);
+ flush_node(q);
+ } else {
+ confusion("insert_group");
+ }
+ list_ptr(p) = null;
+ flush_node(p);
+ if (nest_ptr == 0) {
+ checked_page_filter(insert);
+ build_page();
+ }
+ break;
+ case output_group:
+ /*tex
+ this is needed in case the \.{\\output} executes a \.{\\textdir} command.
+ */
+ if (dir_level(text_dir_ptr) == cur_level) {
+ /*tex Remove from |text_dir_ptr| */
+ halfword text_dir_tmp = vlink(text_dir_ptr);
+ flush_node(text_dir_ptr);
+ text_dir_ptr = text_dir_tmp;
+ }
+ resume_after_output();
+ break;
+ case disc_group:
+ build_discretionary();
+ break;
+ case local_box_group:
+ build_local_box();
+ break;
+ case align_group:
+ back_input();
+ cur_tok = cs_token_flag + frozen_cr;
+ print_err("Missing \\cr inserted");
+ help1(
+ "I'm guessing that you meant to end an alignment here."
+ );
+ ins_error();
+ break;
+ case no_align_group:
+ end_graf(no_align_group);
+ unsave();
+ align_peek();
+ break;
+ case vcenter_group:
+ end_graf(vcenter_group);
+ finish_vcenter();
+ break;
+ case math_choice_group:
+ build_choices();
+ break;
+ case math_group:
+ close_math_group(p);
+ break;
+ default:
+ confusion("rightbrace");
+ break;
+ }
+}
+
+void extra_right_brace(void)
+{
+ print_err("Extra }, or forgotten ");
+ switch (cur_group) {
+ case semi_simple_group:
+ tprint_esc("endgroup");
+ break;
+ case math_shift_group:
+ print_char('$');
+ break;
+ case math_left_group:
+ tprint_esc("right");
+ break;
+ }
+ 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);
+}
+
+/*tex
+
+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.
+
+*/
+
+void normal_paragraph(void)
+{
+ if (looseness_par != 0)
+ eq_word_define(int_base + looseness_code, 0);
+ if (hang_indent_par != 0)
+ eq_word_define(dimen_base + hang_indent_code, 0);
+ if (hang_after_par != 1)
+ eq_word_define(int_base + hang_after_code, 1);
+ if (par_shape_par_ptr != null)
+ eq_define(par_shape_loc, shape_ref_cmd, null);
+ if (inter_line_penalties_par_ptr != null)
+ eq_define(inter_line_penalties_loc, shape_ref_cmd, null);
+ if (shape_mode_par > 0)
+ eq_word_define(dimen_base + shape_mode_code, 0);
+}
+
+/*tex
+
+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.
+
+*/
+
+halfword cur_box; /* box to be placed into its context */
+
+/*tex
+
+The |box_end| procedure does the right thing with |cur_box|, if |box_context|
+represents the context as explained above.
+
+*/
+
+void box_end(int box_context)
+{
+ if (box_context < box_flag) {
+ /*tex
+
+ Append box |cur_box| to the current list, shifted by |box_context|.
+ 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.
+
+ */
+
+ if (cur_box != null) {
+ shift_amount(cur_box) = box_context;
+ if (abs(mode) == vmode) {
+ if (pre_adjust_tail != null) {
+ if (pre_adjust_head != pre_adjust_tail)
+ append_list(pre_adjust_head, pre_adjust_tail);
+ pre_adjust_tail = null;
+ }
+ append_to_vlist(cur_box,lua_key_index(box));
+ if (adjust_tail != null) {
+ if (adjust_head != adjust_tail)
+ append_list(adjust_head, adjust_tail);
+ adjust_tail = null;
+ }
+ if (mode > 0) {
+ checked_page_filter(box);
+ build_page();
+ }
+ } else {
+ if (abs(mode) == hmode)
+ space_factor_par = 1000;
+ else
+ cur_box = new_sub_box(cur_box);
+ couple_nodes(tail, cur_box);
+ tail = cur_box;
+ }
+ }
+ } else if (box_context < ship_out_flag) {
+ /*tex
+ Store |cur_box| in a box register
+ */
+ if (box_context < global_box_flag)
+ eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box);
+ else
+ geq_define(box_base + box_context - global_box_flag, box_ref_cmd, cur_box);
+ } else if (box_context == lua_scan_flag) {
+ /*tex
+ We are done with scanning so let's return to the caller.
+ */
+ wrapup_local_scan_box();
+ } else if (cur_box != null) {
+ /*tex
+ The leaders contexts come after shipout and luascan contexts.
+ */
+ /* if (box_context > lua_scan_flag) { */
+ if (box_context >= leader_flag) {
+ /*tex
+ Append a new leader node that uses |cur_box| and get the next
+ non-blank non-relax...
+ */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (((cur_cmd == hskip_cmd) && (abs(mode) != vmode)) ||
+ ((cur_cmd == vskip_cmd) && (abs(mode) == vmode))) {
+ append_glue();
+ subtype(tail) = (quarterword) (box_context - (leader_flag - a_leaders));
+ leader_ptr(tail) = cur_box;
+ } else {
+ print_err("Leaders not followed by proper glue");
+ 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);
+ }
+ } else {
+ if (box_context != ship_out_flag) {
+ normal_error("scanner","shipout expected");
+ }
+ ship_out(static_pdf, cur_box, SHIPPING_PAGE);
+ }
+ }
+}
+
+/*tex
+
+the next input should specify a box or perhaps a rule
+
+*/
+
+void scan_box(int box_context)
+{
+ /*tex Get the next non-blank non-relax... */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (cur_cmd == make_box_cmd) {
+ begin_box(box_context);
+ } else if ((box_context >= leader_flag) &&
+ ((cur_cmd == hrule_cmd) || (cur_cmd == vrule_cmd) ||
+ (cur_cmd == no_hrule_cmd) || (cur_cmd == no_vrule_cmd))) {
+ cur_box = scan_rule_spec();
+ box_end(box_context);
+ } else {
+ print_err("A <box> was supposed to be here");
+ 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();
+ if (box_context == lua_scan_flag) {
+ cur_box = null;
+ box_end(box_context);
+ }
+ }
+}
+
+void new_graf(boolean indented)
+{
+ halfword p, q, dir_graf_tmp;
+ halfword dir_rover;
+ int callback_id;
+ if ((mode == vmode) || (head != tail)) {
+ tail_append(new_param_glue(par_skip_code));
+ }
+ callback_id = callback_defined(new_graf_callback);
+ if (callback_id > 0) {
+ run_callback(callback_id, "db->b", cur_list.mode_field,indented,&indented);
+ }
+ prev_graf_par = 0;
+ push_nest();
+ mode = hmode;
+ space_factor_par = 1000;
+ /*tex Add local paragraph node */
+ tail_append(make_local_par_node(new_graf_par_code));
+ if (indented) {
+ p = new_null_box();
+ box_dir(p) = par_direction_par;
+ width(p) = par_indent_par;
+ subtype(p) = indent_list;
+ q = tail;
+ tail_append(p);
+ } else {
+ q = tail;
+ }
+ dir_rover = text_dir_ptr;
+ while (dir_rover != null) {
+ if ((vlink(dir_rover) != null) || (dir_dir(dir_rover) != par_direction_par)) {
+ dir_graf_tmp = new_dir(dir_dir(dir_rover));
+ try_couple_nodes(dir_graf_tmp,vlink(q));
+ couple_nodes(q,dir_graf_tmp);
+ }
+ dir_rover = vlink(dir_rover);
+ }
+ q = head;
+ while (vlink(q) != null)
+ q = vlink(q);
+ tail = q;
+ if (every_par_par != null)
+ begin_token_list(every_par_par, every_par_text);
+ if (nest_ptr == 1) {
+ checked_page_filter(new_graf);
+ /*tex put |par_skip| glue on current page */
+ build_page();
+ }
+}
+
+void indent_in_hmode(void)
+{
+ halfword p;
+ if (cur_chr > 0) {
+ /*tex \.{\\indent} */
+ p = new_null_box();
+ width(p) = par_indent_par;
+ if (abs(mode) == hmode)
+ space_factor_par = 1000;
+ else
+ p = new_sub_box(p);
+ tail_append(p);
+ }
+}
+
+void head_for_vmode(void)
+{
+ if (mode < 0) {
+ if ((cur_cmd != hrule_cmd) && (cur_cmd != no_hrule_cmd)) {
+ off_save();
+ } else {
+ print_err("You can't use `\\hrule' here except with leaders");
+ 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;
+ }
+}
+
+/*tex
+
+|dir_save| would have been set by |line_break| by means of |post_line_break|, but
+this is not done right now, as it introduces pretty heavy memory leaks. This
+means the current code might be wrong in some way that relates to in-paragraph
+displays.
+
+*/
+
+void end_graf(int line_break_context)
+{
+ if (mode == hmode) {
+ if ((head == tail) || (vlink(head) == tail)) {
+ if (vlink(head) == tail)
+ flush_node(vlink(head));
+ /*tex |null| paragraphs are ignored, all contain a |local_paragraph| node */
+ pop_nest();
+ } else {
+ line_break(false, line_break_context);
+ }
+ if (dir_save != null) {
+ flush_node_list(dir_save);
+ dir_save = null;
+ }
+ normal_paragraph();
+ error_count = 0;
+ }
+}
+
+void begin_insert_or_adjust(void)
+{
+ if (cur_cmd != vadjust_cmd) {
+ scan_register_num();
+ if (cur_val == output_box_par) {
+ print_err("You can't \\insert");
+ print_int(output_box_par);
+ help1(
+ "I'm changing to \\insert0; box \\outputbox is special."
+ );
+ error();
+ cur_val = 0;
+ }
+ set_saved_record(0, saved_insert, 0, cur_val);
+ } else if (scan_keyword("pre")) {
+ set_saved_record(0, saved_adjust, 0, 1);
+ } else {
+ set_saved_record(0, saved_adjust, 0, 0);
+ }
+ save_ptr++;
+ new_save_level(insert_group);
+ scan_left_brace();
+ normal_paragraph();
+ push_nest();
+ mode = -vmode;
+ prev_depth_par = ignore_depth;
+}
+
+/*tex
+
+I (TH)'ve renamed the |make_mark| procedure to this, because if the current chr
+code is 1, then the actual command was \.{\\clearmarks}, which does not generate
+a mark node but instead destroys the current mark tokenlists.
+
+*/
+
+void handle_mark(void)
+{
+ halfword p; /* new node */
+ halfword c; /* the mark class */
+ if (cur_chr == clear_marks_code) {
+ scan_mark_num();
+ c = cur_val;
+ delete_top_mark(c);
+ delete_bot_mark(c);
+ delete_first_mark(c);
+ delete_split_first_mark(c);
+ delete_split_bot_mark(c);
+ } else {
+ if (cur_chr == 0) {
+ c = 0;
+ } else {
+ scan_mark_num();
+ c = cur_val;
+ if (c > biggest_used_mark)
+ biggest_used_mark = c;
+ }
+ p = scan_toks(false, true);
+ p = new_node(mark_node, 0); /* the |subtype| is not used */
+ mark_class(p) = c;
+ mark_ptr(p) = def_ref;
+ couple_nodes(tail, p);
+ tail = p;
+ }
+}
+
+void append_penalty(void)
+{
+ scan_int();
+ tail_append(new_penalty(cur_val,user_penalty));
+ if (mode == vmode) {
+ checked_page_filter(penalty);
+ build_page();
+ }
+}
+
+/*tex
+
+When |delete_last| is called, |cur_chr| is the |type| of node that will be
+deleted, if present.
+
+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.
+
+*/
+
+void delete_last(void)
+{
+ halfword p, q; /* run through the current list */
+ if ((mode == vmode) && (tail == head)) {
+ /*tex
+ Apologize for inability to do the operation now, unless \.{\\unskip}
+ follows non-glue
+ */
+ if ((cur_chr != glue_node) || (last_glue != max_halfword)) {
+ you_cant();
+ if (cur_chr == kern_node) {
+ help2(
+ "Sorry...I usually can't take things from the current page.",
+ "Try `I\\kern-\\lastkern' instead."
+ );
+ } else if (cur_chr != glue_node) {
+ help2
+ ("Sorry...I usually can't take things from the current page.",
+ "Perhaps you can make the output routine do it.");
+ } else {
+ help2
+ ("Sorry...I usually can't take things from the current page.",
+ "Try `I\\vskip-\\lastskip' instead.");
+ }
+ error();
+ }
+ } else {
+ /*tex Todo: clean this up! */
+ 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) {
+ if (p == tail)
+ return;
+ }
+ }
+ q = vlink(p);
+ } while (q != tail);
+ vlink(p) = null;
+ flush_node_list(tail);
+ tail = p;
+ }
+ }
+ }
+}
+
+void unpackage(void)
+{
+ halfword p; /* the box */
+ halfword r; /* to remove marginal kern nodes */
+ int c; /* should we copy? */
+ halfword s; /* for varmem assignment */
+ if (cur_chr > copy_code) {
+ /*tex Handle saved items and |goto done| */
+ try_couple_nodes(tail, disc_ptr[cur_chr]);
+ disc_ptr[cur_chr] = null;
+ goto DONE;
+ }
+ c = cur_chr;
+ scan_register_num();
+ p = box(cur_val);
+ 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");
+ 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) {
+ s = copy_node_list(list_ptr(p));
+ try_couple_nodes(tail,s);
+ } else {
+ try_couple_nodes(tail,list_ptr(p));
+ box(cur_val) = null;
+ list_ptr(p) = null;
+ flush_node(p);
+ }
+ DONE:
+ while (vlink(tail) != null) {
+ r = vlink(tail);
+ if (!is_char_node(r) && (type(r) == margin_kern_node)) {
+ try_couple_nodes(tail,vlink(r));
+ flush_node(r);
+ }
+ tail = vlink(tail);
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+void append_italic_correction(void)
+{
+ halfword 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
+ return;
+ f = font(p);
+ tail_append(new_kern(char_italic(f, character(p))));
+ subtype(tail) = italic_kern;
+ }
+}
+
+void append_local_box(int kind)
+{
+ incr(save_ptr);
+ set_saved_record(-1, saved_boxtype, 0, kind);
+ new_save_level(local_box_group);
+ scan_left_brace();
+ push_nest();
+ mode = -hmode;
+ space_factor_par = 1000;
+}
+
+/*tex
+
+Discretionary nodes are easy in the common case `\.{\\-}', but in the general
+case we must process three braces full of items.
+
+The space factor does not change when we append a discretionary node, but it
+starts out as 1000 in the subsidiary lists.
+
+*/
+
+void append_discretionary(void)
+{
+ int c;
+ tail_append(new_disc());
+ subtype(tail) = (quarterword) cur_chr;
+ if (cur_chr == explicit_disc) {
+ /* |\-| */
+ c = get_pre_hyphen_char(cur_lang_par);
+ if (c > 0) {
+ vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c);
+ alink(vlink(pre_break(tail))) = pre_break(tail);
+ tlink(pre_break(tail)) = vlink(pre_break(tail));
+ }
+ c = get_post_hyphen_char(cur_lang_par);
+ if (c > 0) {
+ vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c);
+ alink(vlink(post_break(tail))) = post_break(tail);
+ tlink(post_break(tail)) = vlink(post_break(tail));
+ }
+ set_explicit_disc_penalty(tail);
+ } else if (cur_chr == automatic_disc) {
+ /*tex As done in hyphenator: */
+ c = get_pre_exhyphen_char(cur_lang_par);
+ if (c <= 0) {
+ c = ex_hyphen_char_par;
+ }
+ if (c > 0) {
+ vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c);
+ alink(vlink(pre_break(tail))) = pre_break(tail);
+ tlink(pre_break(tail)) = vlink(pre_break(tail));
+ }
+ c = get_post_exhyphen_char(cur_lang_par);
+ if (c > 0) {
+ vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c);
+ alink(vlink(post_break(tail))) = post_break(tail);
+ tlink(post_break(tail)) = vlink(post_break(tail));
+ }
+ c = ex_hyphen_char_par;
+ if (c > 0) {
+ vlink(no_break(tail)) = new_char(equiv(cur_font_loc), c);
+ alink(vlink(no_break(tail))) = no_break(tail);
+ tlink(no_break(tail)) = vlink(no_break(tail));
+ }
+ set_automatic_disc_penalty(tail);
+ } else {
+ /*tex |\discretionary| */
+ if (scan_keyword("penalty")) {
+ scan_int();
+ disc_penalty(tail) = cur_val;
+ }
+ incr(save_ptr);
+ set_saved_record(-1, saved_disc, 0, 0);
+ new_save_level(disc_group);
+ scan_left_brace();
+ push_nest();
+ mode = -hmode;
+ space_factor_par = 1000;
+ /*tex Already preset: |disc_penalty(tail) = hyphen_penalty_par;| */
+ }
+}
+
+/*tex
+
+The test for |p != null| ensures that empty \.{\\localleftbox} and
+\.{\\localrightbox} commands are not applied.
+
+*/
+
+void build_local_box(void)
+{
+ halfword p;
+ int kind;
+ unsave();
+ assert(saved_type(-1) == saved_boxtype);
+ kind = saved_value(-1);
+ decr(save_ptr);
+ p = vlink(head);
+ pop_nest();
+ if (p != null) {
+ /*tex
+ Somehow |filtered_hpack| goes beyond the first node so we loose it.
+ */
+ /*tex
+ There is no need for |new_hyphenation(p, null);| here as we're in
+ an |\hbox|.
+ */
+ (void) new_ligkern(p, null);
+ p = lua_hpack_filter(p, 0, additional, local_box_group, -1, null);
+ /*tex
+ We really need something packed so we play safe! This feature is inherited
+ but could have been delegated to a callback anyway.
+ */
+ p = hpack(p, 0, additional, -1);
+ }
+ if (kind == 0)
+ eq_define(local_left_box_base, box_ref_cmd, p);
+ else
+ eq_define(local_right_box_base, box_ref_cmd, p);
+ if (abs(mode) == hmode) {
+ /*tex Add local paragraph node */
+ tail_append(make_local_par_node(local_box_par_code));
+ }
+ eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1);
+}
+
+/*tex
+
+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.)
+
+*/
+
+void build_discretionary(void)
+{
+ halfword p, q; /* for link manipulation */
+ int n; /* length of discretionary list */
+ unsave();
+ /*tex
+ Prune the current list, if necessary, until it contains only |char_node|,
+ |kern_node|, |hlist_node|, |vlist_node| and |rule_node| items; set |n| to
+ the length of the list, and set |q| to the lists tail. During this loop,
+ |p=vlink(q)| and there are |n| items preceding |p|.
+ */
+ q = head;
+ p = vlink(q);
+ n = 0;
+ while (p != null) {
+ if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) {
+ print_err("Improper discretionary list");
+ help1(
+ "Discretionary lists must contain only boxes and kerns."
+ );
+ error();
+ begin_diagnostic();
+ tprint_nl("The following discretionary sublist has been deleted:");
+ show_box(p);
+ end_diagnostic(true);
+ flush_node_list(p);
+ vlink(q) = null;
+ break;
+ }
+ alink(p) = q;
+ q = p;
+ p = vlink(q);
+ incr(n);
+ }
+
+ p = vlink(head);
+ pop_nest();
+ assert(saved_type(-1) == saved_disc);
+ switch (saved_value(-1)) {
+ case 0:
+ if (n > 0) {
+ vlink(pre_break(tail)) = p;
+ alink(p) = pre_break(tail);
+ tlink(pre_break(tail)) = q;
+ }
+ break;
+ case 1:
+ if (n > 0) {
+ vlink(post_break(tail)) = p;
+ alink(p) = post_break(tail);
+ tlink(post_break(tail)) = q;
+ }
+ break;
+ case 2:
+ /*tex
+ Attach list |p| to the current list, and record its length; then
+ finish up and |return|
+ */
+ if ((n > 0) && (abs(mode) == mmode)) {
+ print_err("Illegal math \\discretionary");
+ 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);
+ error();
+ } else {
+ if (n > 0) {
+ vlink(no_break(tail)) = p;
+ alink(p) = no_break(tail);
+ tlink(no_break(tail)) = q;
+ }
+ }
+ decr(save_ptr);
+ /*tex There are no other cases. */
+ return;
+ break;
+ }
+ set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1));
+ new_save_level(disc_group);
+ scan_left_brace();
+ push_nest();
+ mode = -hmode;
+ space_factor_par = 1000;
+}
+
+/*tex
+
+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.
+
+*/
+
+void make_accent(void)
+{
+ double s, t; /* amount of slant */
+ halfword 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 */
+ scan_char_num();
+ f = equiv(cur_font_loc);
+ p = new_glyph(f, cur_val);
+ if (p != null) {
+ x = x_height(f);
+ /*tex real division */
+ s = float_cast(slant(f)) / float_constant(65536);
+ a = glyph_width(p);
+ do_assignments();
+ /*tex
+ Create a character node |q| for the next character, but set |q:=null|
+ if problems arise
+ */
+ q = null;
+ f = equiv(cur_font_loc);
+ if ((cur_cmd == letter_cmd) ||
+ (cur_cmd == other_char_cmd) || (cur_cmd == char_given_cmd)) {
+ q = new_glyph(f, cur_chr);
+ } else if (cur_cmd == char_num_cmd) {
+ scan_char_num();
+ q = new_glyph(f, cur_val);
+ } else {
+ back_input();
+ }
+
+ if (q != null) {
+ /*tex
+ Append the accent with appropriate kerns, then set |p:=q|. 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) |real| 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}.
+ */
+ t = float_cast(slant(f)) / float_constant(65536); /* real division */
+ w = glyph_width(q);
+ h = glyph_height(q);
+ if (h != x) {
+ /*tex the accent must be shifted up or down */
+ p = hpack(p, 0, additional, -1);
+ shift_amount(p) = x - h;
+ }
+ /*tex real multiplication */
+ delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s);
+ r = new_kern(delta);
+ subtype(r) = accent_kern;
+ couple_nodes(tail, r);
+ couple_nodes(r, p);
+ tail = new_kern(-a - delta);
+ subtype(tail) = accent_kern;
+ couple_nodes(p, tail);
+ p = q;
+
+ }
+ couple_nodes(tail, p);
+ tail = p;
+ space_factor_par = 1000;
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+void align_error(void)
+{
+ if (abs(align_state) > 2) {
+ /*tex
+ Express consternation over the fact that no alignment is in progress.
+ */
+ print_err("Misplaced ");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ 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();
+
+ } else {
+ back_input();
+ if (align_state < 0) {
+ print_err("Missing { inserted");
+ incr(align_state);
+ cur_tok = left_brace_token + '{';
+ } else {
+ print_err("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();
+ }
+}
+
+/*tex
+
+The help messages here contain a little white lie, since \.{\\noalign} and
+\.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
+
+*/
+
+void no_align_error(void)
+{
+ print_err("Misplaced \\noalign");
+ help2(
+ "I expect to see \\noalign only after the \\cr of",
+ "an alignment. Proceed, and I'll ignore this case."
+ );
+ error();
+}
+
+void omit_error(void)
+{
+ print_err("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();
+}
+
+/*tex
+
+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.
+
+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.
+
+*/
+
+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)");
+ /*tex interwoven alignment preambles... */
+ if (cur_group == align_group) {
+ end_graf(align_group);
+ if (fin_col())
+ fin_row();
+ } else {
+ off_save();
+ }
+}
+
+/*tex
+
+Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
+
+*/
+
+void cs_error(void)
+{
+ print_err("Extra \\endcsname");
+ help1(
+ "I'm ignoring this, since I wasn't doing a \\csname."
+ );
+ error();
+}
+
+/*tex
+
+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.)
+
+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.
+
+*/
+
+/*tex
+
+If the user says, e.g., `\.{\\global\\global}', the redundancy is silently
+accepted. The different types of code values have different legal ranges; the
+following program is careful to check each case properly.
+
+*/
+
+#define check_def_code(A) do { \
+ if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \
+ print_err("Invalid code ("); \
+ print_int(cur_val); \
+ if (p<(A)) \
+ tprint("), should be in the range 0.."); \
+ else \
+ tprint("), should be at most "); \
+ print_int(n); \
+ help1( \
+ "I'm going to use 0 instead of that illegal code value." \
+ ); \
+ error(); \
+ cur_val=0; \
+ } \
+} while (0)
+
+/*
+halfword swap_hang_indent(halfword indentation, halfword shape_mode) {
+ if (shape_mode == 1 || shape_mode == 3 || shape_mode == -1 || shape_mode == -3) {
+ return negate(indentation);
+ } else {
+ return indentation;
+ }
+}
+
+halfword swap_parshape_indent(halfword indentation, halfword width, halfword shape_mode) {
+ if (shape_mode == 2 || shape_mode == 3 || shape_mode == -2 || shape_mode == -3) {
+ return hsize_par - width - indentation;
+ } else {
+ return indentation;
+ }
+}
+
+*/
+
+void prefixed_command(void)
+{
+ int a; /* accumulated prefix codes so far */
+ internal_font_number f; /* identifies a font */
+ halfword j; /* index into a \.{\\parshape} specification */
+ halfword p, q; /* for temporary short-term use */
+ int n; /* ditto */
+ boolean e, check_glue; /* should a definition be expanded? or was \.{\\let} not done? */
+ mathcodeval mval; /* for handling of \.{\\mathchardef}s */
+ a = 0;
+ while (cur_cmd == prefix_cmd) {
+ if (!odd(a / cur_chr))
+ a = a + cur_chr;
+ /*tex
+ Get the next non-blank non-relax...
+ */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+ if (cur_cmd <= max_non_prefixed_command) {
+ /*tex
+ Discard erroneous prefixes and |return|
+ */
+ print_err("You can't use a prefix with `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ print_char('\'');
+ help2(
+ "I'll pretend you didn't say \\long or \\outer or \\global or",
+ "\\protected."
+ );
+ back_error();
+ return;
+ }
+ if (tracing_commands_par > 2)
+ show_cur_cmd_chr();
+ }
+ /*tex
+ Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant
+ */
+ if (a >= 8) {
+ j = protected_token;
+ a = a - 8;
+ } else {
+ j = 0;
+ }
+ if ((cur_cmd != def_cmd) && (cur_cmd != def_lua_call_cmd) && ((a % 4 != 0) || (j != 0))) {
+ print_err("You can't use `\\long' or `\\outer' or `\\protected' with `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ print_char('\'');
+ help1(
+ "I'll pretend you didn't say \\long or \\outer or \\protected here."
+ );
+ error();
+ }
+ /*tex
+ Adjust for the setting of \.{\\globaldefs}
+ */
+ if (global_defs_par != 0) {
+ if (global_defs_par < 0) {
+ if (is_global(a))
+ a = a - 4;
+ } else {
+ if (!is_global(a))
+ a = a + 4;
+ }
+ }
+ switch (cur_cmd) {
+ case set_font_cmd:
+ /*tex
+ Here's an example of the way many of the following routines operate.
+ (Unfortunately, they aren't all as simple as this.)
+ */
+ define(cur_font_loc, data_cmd, cur_chr);
+ break;
+ case def_cmd:
+ /*tex
+ 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.
+ */
+ if (odd(cur_chr) && !is_global(a) && (global_defs_par >= 0))
+ a = a + 4;
+ e = (cur_chr >= 2);
+ get_r_token();
+ p = cur_cs;
+ q = scan_toks(true, e);
+ if (j != 0) {
+ q = get_avail();
+ set_token_info(q, j);
+ set_token_link(q, token_link(def_ref));
+ set_token_link(def_ref, q);
+ }
+ define(p, call_cmd + (a % 4), def_ref);
+ break;
+ case let_cmd:
+ n = cur_chr;
+ switch (n) {
+ case 0:
+ /*tex |glet| */
+ if (!is_global(a) && (global_defs_par >= 0)) {
+ a = a + 4;
+ }
+ case 1:
+ /*tex |let| */
+ get_r_token();
+ p = cur_cs;
+ do {
+ get_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_tok == other_token + '=') {
+ get_token();
+ if (cur_cmd == spacer_cmd)
+ get_token();
+ }
+ break;
+ case 2:
+ /*tex |futurelet| */
+ get_r_token();
+ p = cur_cs;
+ get_token();
+ q = cur_tok;
+ get_token();
+ back_input();
+ cur_tok = q;
+ /*tex
+ We look ahead and then back up. Note that |back_input| doesn't
+ affect |cur_cmd|, |cur_chr|
+ */
+ back_input();
+ break;
+ case 3:
+ /*tex |letcharcode| */
+ scan_int();
+ if (cur_val > 0) {
+ /*tex HH: I need to do a more extensive test later. */
+ p = active_to_cs(cur_val, true);
+ do {
+ get_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_tok == other_token + '=') {
+ get_token();
+ if (cur_cmd == spacer_cmd)
+ get_token();
+ }
+ } else {
+ p = null;
+ tex_error("invalid number for \\letcharcode",NULL);
+ }
+ break;
+ default:
+ /*tex We please the compiler. */
+ p = null;
+ confusion("let");
+ break;
+ }
+ if (cur_cmd >= call_cmd)
+ add_token_ref(cur_chr);
+ define(p, cur_cmd, cur_chr);
+ break;
+ case shorthand_def_cmd:
+ /*tex
+ 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}'.
+ */
+ n = cur_chr;
+ get_r_token();
+ p = cur_cs;
+ define(p, relax_cmd, too_big_char);
+ scan_optional_equals();
+ switch (n) {
+ case char_def_code:
+ scan_char_num();
+ define(p, char_given_cmd, cur_val);
+ break;
+ case math_char_def_code:
+ mval = scan_mathchar(tex_mathcode);
+ /* if (math_umathcode_meaning_par == 1) { */
+ /* cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; */
+ /* define(p, xmath_given_cmd, cur_val); */
+ /* } else { */
+ cur_val = (mval.class_value * 16 + mval.family_value) * 256 + mval.character_value;
+ define(p, math_given_cmd, cur_val);
+ /* } */
+ break;
+ case xmath_char_def_code:
+ mval = scan_mathchar(umath_mathcode);
+ cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value;
+ define(p, xmath_given_cmd, cur_val);
+ break;
+ case umath_char_def_code:
+ mval = scan_mathchar(umathnum_mathcode);
+ cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value;
+ define(p, xmath_given_cmd, cur_val);
+ break;
+ default:
+ scan_register_num();
+ switch (n) {
+ case count_def_code:
+ define(p, assign_int_cmd, count_base + cur_val);
+ break;
+ case attribute_def_code:
+ define(p, assign_attr_cmd, attribute_base + cur_val);
+ break;
+ case dimen_def_code:
+ define(p, assign_dimen_cmd, scaled_base + cur_val);
+ break;
+ case skip_def_code:
+ define(p, assign_glue_cmd, skip_base + cur_val);
+ break;
+ case mu_skip_def_code:
+ define(p, assign_mu_glue_cmd, mu_skip_base + cur_val);
+ break;
+ case toks_def_code:
+ define(p, assign_toks_cmd, toks_base + cur_val);
+ break;
+ default:
+ confusion("shorthand_def");
+ break;
+ }
+ break;
+ }
+ break;
+ case read_to_cs_cmd:
+ j = cur_chr;
+ scan_int();
+ n = cur_val;
+ if (!scan_keyword("to")) {
+ print_err("Missing `to' inserted");
+ 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_cmd, cur_val);
+ break;
+ case toks_register_cmd:
+ case assign_toks_cmd:
+ /*tex
+ 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.)
+ */
+ q = cur_cs;
+ if (cur_cmd == toks_register_cmd) {
+ scan_register_num();
+ p = toks_base + cur_val;
+ } else {
+ /*tex |p=every_par_loc| or |output_routine_loc| or \dots */
+ p = cur_chr;
+ }
+ scan_optional_equals();
+ /*tex Get the next non-blank non-relax non-call token */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+ if (cur_cmd != left_brace_cmd) {
+ /*tex
+ If the right-hand side is a token parameter or token
+ register, finish the assignment and |goto done|
+ */
+ if (cur_cmd == toks_register_cmd) {
+ scan_register_num();
+ cur_cmd = assign_toks_cmd;
+ cur_chr = toks_base + cur_val;
+ }
+ if (cur_cmd == assign_toks_cmd) {
+ q = equiv(cur_chr);
+ if (q == null) {
+ define(p, undefined_cs_cmd, null);
+ } else {
+ add_token_ref(q);
+ define(p, call_cmd, q);
+ }
+ goto DONE;
+ }
+ }
+ back_input();
+ cur_cs = q;
+ q = scan_toks(false, false);
+ if (token_link(def_ref) == null) { /* empty list: revert to the default */
+ define(p, undefined_cs_cmd, null);
+ free_avail(def_ref);
+ } else {
+ if (p == output_routine_loc) { /* enclose in curlies */
+ p = get_avail();
+ set_token_link(q, p);
+ p = output_routine_loc;
+ q = token_link(q);
+ set_token_info(q, right_brace_token + '}');
+ q = get_avail();
+ set_token_info(q, left_brace_token + '{');
+ set_token_link(q, token_link(def_ref));
+ set_token_link(def_ref, q);
+ }
+ define(p, call_cmd, def_ref);
+ }
+ break;
+ case assign_int_cmd:
+ /*tex Similar routines are used to assign values to the numeric parameters. */
+ p = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ assign_internal_value(a, p, cur_val);
+ break;
+ case assign_attr_cmd:
+ p = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ if ((p - attribute_base) > max_used_attr)
+ max_used_attr = (p - attribute_base);
+ attr_list_cache = cache_disabled;
+ word_define(p, cur_val);
+ break;
+ case assign_direction_cmd:
+ case assign_dir_cmd:
+ /*tex Assign direction codes. */
+ if (cur_cmd == assign_direction_cmd) {
+ p = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ check_dir_value(cur_val);
+ cur_chr = p;
+ } else {
+ scan_direction();
+ }
+ switch (cur_chr) {
+ case int_base + page_direction_code:
+ eq_word_define(int_base + page_direction_code, cur_val);
+ break;
+ case int_base + body_direction_code:
+ eq_word_define(int_base + body_direction_code, cur_val);
+ break;
+ case int_base + par_direction_code:
+ eq_word_define(int_base + par_direction_code, cur_val);
+ break;
+ case int_base + math_direction_code:
+ eq_word_define(int_base + math_direction_code, cur_val);
+ break;
+ case int_base + text_direction_code:
+ case int_base + line_direction_code:
+ /*
+ pre version 0.97 this was a commented section because various tests hint that this
+ is unnecessary and sometimes even produces weird results, like:
+
+ (\hbox{\textdir TRT ABC\textdir TLT DEF}))
+
+ becomes
+
+ (DEFCBA)
+
+ in the output when we use
+
+ tail_append(new_dir(text_direction_par)
+
+ but when we append the reverse of the current it goes better
+
+ */
+ check_glue = (cur_chr == (int_base + line_direction_code));
+ if (check_glue) {
+ cur_chr = int_base + text_direction_code ;
+ }
+ if (abs(mode) == hmode) {
+ if (no_local_dirs_par > 0) {
+ /*tex |tail| is non zero but we test anyway. */
+ if (check_glue && (tail != null && type(tail) == glue_node)) {
+ halfword prev = alink(tail);
+ halfword dirn = new_dir(text_direction_par);
+ subtype(dirn) = cancel_dir;
+ couple_nodes(prev,dirn);
+ couple_nodes(dirn,tail);
+ } else {
+ tail_append(new_dir(text_direction_par));
+ subtype(tail) = cancel_dir;
+ }
+ } else {
+ /*tex What is the use of nolocaldirs? Maybe we should get rid of it. */
+ }
+ update_text_dir_ptr(cur_val);
+ tail_append(new_dir(cur_val));
+ dir_level(tail) = cur_level;
+ } else {
+ update_text_dir_ptr(cur_val);
+ }
+ eq_word_define(int_base + text_direction_code, cur_val);
+ eq_word_define(int_base + no_local_dirs_code, no_local_dirs_par + 1);
+ break;
+ }
+ break;
+ case assign_dimen_cmd:
+ p = cur_chr;
+ scan_optional_equals();
+ scan_normal_dimen();
+ assign_internal_value(a, p, cur_val);
+ break;
+ case assign_glue_cmd:
+ case assign_mu_glue_cmd:
+ p = cur_chr;
+ n = cur_cmd;
+ scan_optional_equals();
+ if (n == assign_mu_glue_cmd)
+ scan_glue(mu_val_level);
+ else
+ scan_glue(glue_val_level);
+ define(p, glue_ref_cmd, cur_val);
+ break;
+ case def_char_code_cmd:
+ case def_del_code_cmd:
+ /*tex Let |n| be the largest legal code value, based on |cur_chr| */
+ if (cur_chr == cat_code_base)
+ n = max_char_code;
+ else if (cur_chr == sf_code_base)
+ n = 077777;
+ else
+ n = biggest_char;
+ p = cur_chr;
+ if (cur_chr == math_code_base) {
+ if (is_global(a))
+ cur_val1 = level_one;
+ else
+ cur_val1 = cur_level;
+ scan_extdef_math_code(cur_val1, tex_mathcode);
+ } else if (cur_chr == lc_code_base) {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ check_def_code(lc_code_base);
+ define_lc_code(p, cur_val);
+ } else if (cur_chr == uc_code_base) {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ check_def_code(uc_code_base);
+ define_uc_code(p, cur_val);
+ } else if (cur_chr == sf_code_base) {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ check_def_code(sf_code_base);
+ define_sf_code(p, cur_val);
+ } else if (cur_chr == cat_code_base) {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ check_def_code(cat_code_base);
+ define_cat_code(p, cur_val);
+ } else if (cur_chr == del_code_base) {
+ if (is_global(a))
+ cur_val1 = level_one;
+ else
+ cur_val1 = cur_level;
+ scan_extdef_del_code(cur_val1, tex_mathcode);
+ }
+ break;
+ case extdef_math_code_cmd:
+ case extdef_del_code_cmd:
+ if (is_global(a))
+ cur_val1 = level_one;
+ else
+ cur_val1 = cur_level;
+ if (cur_chr == math_code_base)
+ scan_extdef_math_code(cur_val1, umath_mathcode);
+ else if (cur_chr == math_code_base + 1)
+ scan_extdef_math_code(cur_val1, umathnum_mathcode);
+ else if (cur_chr == del_code_base)
+ scan_extdef_del_code(cur_val1, umath_mathcode);
+ else if (cur_chr == del_code_base + 1)
+ scan_extdef_del_code(cur_val1, umathnum_mathcode);
+ break;
+ case def_family_cmd:
+ p = cur_chr;
+ scan_math_family_int();
+ cur_val1 = cur_val;
+ scan_optional_equals();
+ scan_font_ident();
+ define_fam_fnt(cur_val1, p, cur_val);
+ break;
+ case set_math_param_cmd:
+ p = cur_chr;
+ get_token();
+ if (cur_cmd != math_style_cmd) {
+ print_err("Missing math style, treated as \\displaystyle");
+ help1(
+ "A style should have been here; I inserted `\\displaystyle'."
+ );
+ cur_val1 = display_style;
+ back_error();
+ } else {
+ cur_val1 = cur_chr;
+ }
+ scan_optional_equals();
+ if (p < math_param_first_mu_glue) {
+ if (p == math_param_radical_degree_raise)
+ scan_int();
+ else
+ scan_dimen(false, false, false);
+ } else {
+ scan_glue(mu_val_level);
+ if (cur_val == thin_mu_skip_par)
+ cur_val = thin_mu_skip_code;
+ else if (cur_val == med_mu_skip_par)
+ cur_val = med_mu_skip_code;
+ else if (cur_val == thick_mu_skip_par)
+ cur_val = thick_mu_skip_code;
+ }
+ define_math_param(p, cur_val1, cur_val);
+ break;
+ case register_cmd:
+ case advance_cmd:
+ case multiply_cmd:
+ case divide_cmd:
+ do_register_command(a);
+ break;
+ case set_box_cmd:
+ /*tex
+ 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.
+ */
+ scan_register_num();
+ if (is_global(a))
+ 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 \\setbox");
+ help3(
+ "Sorry, \\setbox is not allowed after \\halign in a display,",
+ "between \\accent and an accented character, or in immediate",
+ "assignments."
+ );
+ error();
+ }
+ break;
+ case set_aux_cmd:
+ /*tex
+ 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.
+ */
+ alter_aux();
+ break;
+ case set_prev_graf_cmd:
+ alter_prev_graf();
+ break;
+ case set_page_dimen_cmd:
+ alter_page_so_far();
+ break;
+ case set_page_int_cmd:
+ alter_integer();
+ break;
+ case set_box_dimen_cmd:
+ /*tex
+ When some dimension of a box register is changed, the change
+ isn't exactly global; but \TeX\ does not look at the \.{\\global}
+ switch.
+ */
+ alter_box_dimen();
+ break;
+ case set_tex_shape_cmd:
+ q = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ n = cur_val;
+ if (n <= 0) {
+ p = null;
+ } else {
+ p = new_node(shape_node, 2 * (n + 1) + 1);
+ vinfo(p + 1) = n;
+ for (j = 1; j <= n; j++) {
+ scan_normal_dimen();
+ varmem[p + 2 * j].cint = cur_val; /* indentation */
+ scan_normal_dimen();
+ varmem[p + 2 * j + 1].cint = cur_val; /* width */
+ }
+ }
+ define(q, shape_ref_cmd, p);
+ break;
+ case set_etex_shape_cmd:
+ q = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ n = cur_val;
+ if (n <= 0) {
+ p = null;
+ } else {
+ n = (cur_val / 2) + 1;
+ p = new_node(shape_node, 2 * n + 1 + 1);
+ vinfo(p + 1) = n;
+ n = cur_val;
+ varmem[p + 2].cint = n; /* number of penalties */
+ for (j = p + 3; j <= p + n + 2; j++) {
+ scan_int();
+ varmem[j].cint = cur_val; /* penalty values */
+ }
+ if (!odd(n))
+ varmem[p + n + 3].cint = 0; /* unused */
+ }
+ define(q, shape_ref_cmd, p);
+ break;
+ case hyph_data_cmd:
+ /*tex
+ All of \TeX's parameters are kept in |eqtb| except the font
+ information, the interaction mode, and the hyphenation tables;
+ these are strictly global.
+ */
+ switch (cur_chr) {
+ case 0:
+ new_hyph_exceptions();
+ break;
+ case 1:
+ new_patterns();
+ break;
+ case 2:
+ new_pre_hyphen_char();
+ break;
+ case 3:
+ new_post_hyphen_char();
+ break;
+ case 4:
+ new_pre_exhyphen_char();
+ break;
+ case 5:
+ new_post_exhyphen_char();
+ break;
+ case 6:
+ new_hyphenation_min();
+ break;
+ case 7:
+ new_hj_code();
+ break;
+ }
+ break;
+ case assign_font_dimen_cmd:
+ set_font_dimen();
+ break;
+ case assign_font_int_cmd:
+ n = cur_chr;
+ scan_font_ident();
+ f = cur_val;
+ if (n == no_lig_code) {
+ set_no_ligatures(f);
+ } else if (n < lp_code_base) {
+ scan_optional_equals();
+ scan_int();
+ if (n == 0)
+ set_hyphen_char(f, cur_val);
+ else
+ set_skew_char(f, cur_val);
+ } else {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ switch (n) {
+ case lp_code_base:
+ set_lp_code(f, p, cur_val);
+ break;
+ case rp_code_base:
+ set_rp_code(f, p, cur_val);
+ break;
+ case ef_code_base:
+ set_ef_code(f, p, cur_val);
+ break;
+ case tag_code:
+ set_tag_code(f, p, cur_val);
+ break;
+ }
+ }
+ break;
+ case def_font_cmd:
+ /*tex Here is where the information for a new font gets loaded. */
+ tex_def_font((small_number) a);
+ break;
+ case def_lua_call_cmd:
+ get_r_token();
+ p = cur_cs;
+ scan_optional_equals();
+ scan_int();
+ if (j != 0) {
+ define(p, lua_call_cmd, cur_val);
+ } else {
+ define(p, lua_expandable_call_cmd, cur_val);
+ }
+ break;
+ case letterspace_font_cmd:
+ new_letterspaced_font((small_number) a);
+ break;
+ case copy_font_cmd:
+ make_font_copy((small_number) a);
+ break;
+ case set_font_id_cmd:
+ scan_int();
+ if (is_valid_font(cur_val))
+ zset_cur_font(cur_val);
+ break ;
+ case set_interaction_cmd:
+ new_interaction();
+ break;
+ default:
+ confusion("prefix");
+ break;
+ }
+ /*tex End of assignments cases. */
+ DONE:
+ /*tex Insert a token saved by \.{\\afterassignment}, if any. */
+ if (after_token != 0) {
+ cur_tok = after_token;
+ back_input();
+ after_token = 0;
+ }
+}
+
+void fixup_directions(void)
+{
+ int temp_no_whatsits = no_local_whatsits_par;
+ int temp_no_dirs = no_local_dirs_par;
+ int temporary_dir = text_direction_par;
+ if (dir_level(text_dir_ptr) == cur_level) {
+ /* Remove from |text_dir_ptr|. */
+ halfword text_dir_tmp = vlink(text_dir_ptr);
+ flush_node(text_dir_ptr);
+ text_dir_ptr = text_dir_tmp;
+ }
+ unsave();
+ if (abs(mode) == hmode) {
+ if (temp_no_dirs != 0) {
+ /* Add local dir node. */
+ tail_append(new_dir(text_direction_par));
+ dir_dir(tail) = temporary_dir;
+ subtype(tail) = cancel_dir;
+ }
+ if (temp_no_whatsits != 0) {
+ /*tex Add local paragraph node. */
+ tail_append(make_local_par_node(hmode_par_par_code));
+ }
+ }
+}
+
+/*tex
+
+ This is experimental and needs more checking!
+
+*/
+
+void fixup_directions_only(void)
+{
+ int temp_no_dirs = no_local_dirs_par;
+ int temporary_dir = text_direction_par;
+ if (dir_level(text_dir_ptr) == cur_level) {
+ /* Remove from |text_dir_ptr|. */
+ halfword text_dir_tmp = vlink(text_dir_ptr);
+ flush_node(text_dir_ptr);
+ text_dir_ptr = text_dir_tmp;
+ }
+ if (temp_no_dirs != 0) {
+ /* Add local dir node. */
+ tail_append(new_dir(text_direction_par));
+ dir_dir(tail) = temporary_dir;
+ subtype(tail) = cancel_dir;
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+void get_r_token(void)
+{
+ RESTART:
+ do {
+ get_token();
+ } while (cur_tok == space_token);
+ if ((cur_cs == 0) || (cur_cs > eqtb_top) ||
+ ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) {
+ print_err("Missing control sequence inserted");
+ 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."
+ );
+ if (cur_cs == 0)
+ back_input();
+ cur_tok = cs_token_flag + frozen_protection;
+ ins_error();
+ goto RESTART;
+ }
+}
+
+void assign_internal_value(int a, halfword p, int val)
+{
+ halfword n;
+ if ((p >= int_base) && (p < attribute_base)) {
+ switch ((p - int_base)) {
+ case cat_code_table_code:
+ if (valid_catcode_table(val)) {
+ if (val != cat_code_table_par)
+ word_define(p, val);
+ } else {
+ print_err("Invalid \\catcode table");
+ help2(
+ "You can only switch to a \\catcode table that is initialized",
+ "using \\savecatcodetable or \\initcatcodetable, or to table 0"
+ );
+ error();
+ }
+ break;
+ case output_box_code:
+ if ((val > 65535) | (val < 0)) {
+ print_err("Invalid \\outputbox");
+ help1(
+ "The value for \\outputbox has to be between 0 and 65535."
+ );
+ error();
+ } else {
+ word_define(p, val);
+ }
+ break;
+ case new_line_char_code:
+ if (val > 127) {
+ print_err("Invalid \\newlinechar");
+ help2(
+ "The value for \\newlinechar has to be no higher than 127.",
+ "Your invalid assignment will be ignored."
+ );
+ error();
+ } else {
+ word_define(p, val);
+ }
+ break;
+ case end_line_char_code:
+ if (val > 127) {
+ print_err("Invalid \\endlinechar");
+ help2(
+ "The value for \\endlinechar has to be no higher than 127.",
+ "Your invalid assignment will be ignored."
+ );
+ error();
+ } else {
+ word_define(p, val);
+ }
+ break;
+ case language_code:
+ if (val < 0) {
+ word_define(int_base + cur_lang_code, -1);
+ word_define(p, -1);
+ } else if (val > 16383) {
+ print_err("Invalid \\language");
+ help2(
+ "The absolute value for \\language has to be no higher than 16383.",
+ "Your invalid assignment will be ignored."
+ );
+ error();
+ } else {
+ word_define(int_base + cur_lang_code, val);
+ word_define(p, val);
+ }
+ break;
+ default:
+ word_define(p, val);
+ break;
+ }
+ /*tex
+ If we are defining subparagraph penalty levels while we are in hmode,
+ then we put out a whatsit immediately, otherwise we leave it alone.
+ This mechanism might not be sufficiently powerful, and some other
+ algorithm, searching down the stack, might be necessary. Good first
+ step.
+ */
+ if ((abs(mode) == hmode) &&
+ ((p == (int_base + local_inter_line_penalty_code)) ||
+ (p == (int_base + local_broken_penalty_code)))) {
+ /*tex Add local paragraph node */
+ tail_append(make_local_par_node(penalty_par_code));
+ eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1);
+ }
+ } else if ((p >= dimen_base) && (p <= eqtb_size)) {
+ if (p == (dimen_base + page_left_offset_code)) {
+ n = val - one_true_inch;
+ word_define(dimen_base + h_offset_code, n);
+ } else if (p == (dimen_base + h_offset_code)) {
+ n = val + one_true_inch;
+ word_define(dimen_base + page_left_offset_code, n);
+ } else if (p == (dimen_base + page_top_offset_code)) {
+ n = val - one_true_inch;
+ word_define(dimen_base + v_offset_code, n);
+ } else if (p == (dimen_base + v_offset_code)) {
+ n = val + one_true_inch;
+ word_define(dimen_base + page_top_offset_code, n);
+ }
+ word_define(p, val);
+ } else if ((p >= local_base) && (p < toks_base)) {
+ /*tex internal locals */
+ define(p, call_cmd, val);
+ } else {
+ confusion("assign internal value");
+ }
+}
+
+/*tex
+
+We use the fact that |register| $<$ |advance| $<$ |multiply| $<$ |divide|/ We
+compute the register location |l| and its type |p| but |return| if invalid. Here
+we use the fact that the consecutive codes |int_val..mu_val| and
+|assign_int..assign_mu_glue| correspond to each other nicely.
+
+*/
+
+void do_register_command(int a)
+{
+ int p;
+ halfword q = cur_cmd;
+ halfword l = 0;
+ if (q != register_cmd) {
+ get_x_token();
+ if ((cur_cmd >= assign_int_cmd) && (cur_cmd <= assign_mu_glue_cmd)) {
+ l = cur_chr;
+ p = cur_cmd - assign_int_cmd;
+ goto FOUND;
+ }
+ if (cur_cmd != register_cmd) {
+ print_err("You can't use `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ tprint("' after ");
+ print_cmd_chr((quarterword) q, 0);
+ help1(
+ "I'm forgetting what you said and not changing anything."
+ );
+ error();
+ return;
+ }
+ }
+ p = cur_chr;
+ scan_register_num();
+ if (p == int_val_level)
+ l = cur_val + count_base;
+ else if (p == attr_val_level)
+ l = cur_val + attribute_base;
+ else if (p == dimen_val_level)
+ l = cur_val + scaled_base;
+ else if (p == glue_val_level)
+ l = cur_val + skip_base;
+ else if (p == mu_val_level)
+ l = cur_val + mu_skip_base;
+ FOUND:
+ if (q == register_cmd) {
+ scan_optional_equals();
+ } else if (scan_keyword("by")) {
+ /*tex optional `\.{by}' */
+ }
+ arith_error = false;
+ if (q < multiply_cmd) {
+ /*tex Compute result of |register| or |advance|, put it in |cur_val|. */
+ if (p < glue_val_level) {
+ if ((p == int_val_level) || (p == attr_val_level))
+ scan_int();
+ else
+ scan_normal_dimen();
+ if (q == advance_cmd)
+ cur_val = cur_val + eqtb[l].cint;
+ } else {
+ scan_glue(p);
+ if (q == advance_cmd) {
+ /* Compute the sum of two glue specs */
+ halfword r = equiv(l);
+ q = new_spec(cur_val);
+ flush_node(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;
+ }
+ }
+ } else {
+ /*tex Compute result of |multiply| or |divide|, put it in |cur_val| */
+ scan_int();
+ if (p < glue_val_level) {
+ if (q == multiply_cmd) {
+ if ((p == int_val_level) || (p == attr_val_level)) {
+ cur_val = mult_integers(eqtb[l].cint, cur_val);
+ } else {
+ cur_val = nx_plus_y(eqtb[l].cint, cur_val, 0);
+ }
+ } else {
+ cur_val = x_over_n(eqtb[l].cint, cur_val);
+ }
+ } else {
+ halfword s = equiv(l);
+ halfword r = new_spec(s);
+ if (q == multiply_cmd) {
+ 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;
+ }
+ }
+ if (arith_error) {
+ print_err("Arithmetic overflow");
+ help2(
+ "I can't carry out that multiplication or division,",
+ "since the result is out of range."
+ );
+ if (p >= glue_val_level)
+ flush_node(cur_val);
+ error();
+ return;
+ }
+ if (p < glue_val_level) {
+ if (p == attr_val_level) {
+ if ((l - attribute_base) > max_used_attr)
+ max_used_attr = (l - attribute_base);
+ attr_list_cache = cache_disabled;
+ }
+ if ((p == int_val_level) || (p == dimen_val_level))
+ assign_internal_value(a, l, cur_val);
+ else
+ word_define(l, cur_val);
+ } else {
+ define(l, glue_ref_cmd, cur_val);
+ }
+}
+
+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_par = cur_val;
+ } else {
+ scan_int();
+ if ((cur_val <= 0) || (cur_val > 32767)) {
+ print_err("Bad space factor");
+ help1(
+ "I allow only values in the range 1..32767 here."
+ );
+ int_error(cur_val);
+ } else {
+ space_factor_par = cur_val;
+ }
+ }
+ }
+}
+
+void alter_prev_graf(void)
+{
+ int p = nest_ptr; /* index into |nest| */
+ while (abs(nest[p].mode_field) != vmode)
+ decr(p);
+ scan_optional_equals();
+ scan_int();
+ if (cur_val < 0) {
+ print_err("Bad \\prevgraf");
+ help1(
+ "I allow only nonnegative values here."
+ );
+ int_error(cur_val);
+ } else {
+ nest[p].pg_field = cur_val;
+ }
+}
+
+void alter_page_so_far(void)
+{
+ int c = cur_chr; /* index into |page_so_far| */
+ scan_optional_equals();
+ scan_normal_dimen();
+ page_so_far[c] = cur_val;
+}
+
+/*tex
+ The value of |c| is 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc.
+*/
+
+void alter_integer(void)
+{
+ int c = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ if (c == 0) {
+ dead_cycles = cur_val;
+ } else if (c == 2) {
+ if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) {
+ print_err("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();
+ }
+ } else {
+ insert_penalties = cur_val;
+ }
+}
+
+void alter_box_dimen(void)
+{
+ int c; /* |width_offset| or |height_offset| or |depth_offset| */
+ int b; /* box number */
+ c = cur_chr;
+ scan_register_num();
+ b = cur_val;
+ scan_optional_equals();
+ scan_normal_dimen();
+ if (box(b) != null)
+ varmem[box(b) + c].cint = cur_val;
+}
+
+void new_interaction(void)
+{
+ print_ln();
+ interaction = cur_chr;
+ if (interaction == batch_mode)
+ kpse_make_tex_discard_errors = 1;
+ else
+ kpse_make_tex_discard_errors = 0;
+ fixup_selector(log_opened_global);
+}
+
+/*tex
+
+The \.{\\afterassignment} command puts a token into the global variable
+|after_token|. This global variable is examined just after every assignment has
+been performed. It's value is zero, or a saved token.
+
+*/
+
+halfword after_token;
+
+/*tex
+
+ Here is a procedure that might be called `Get the next non-blank non-relax
+ non-call non-assignment token'.
+
+*/
+
+void do_assignments(void)
+{
+ while (true) {
+ /*tex Get the next non-blank non-relax... */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (cur_cmd <= max_non_prefixed_command)
+ return;
+ set_box_allowed = false;
+ prefixed_command();
+ set_box_allowed = true;
+ }
+}
+
+/*tex |cur_chr| is 1 for \.{\\openin}, 0 for \.{\\closein}: */
+
+void open_or_close_in(void)
+{
+ int c = cur_chr;
+ int n;
+ char *fn;
+ scan_four_bit_int();
+ n = cur_val;
+ if (read_open[n] != closed) {
+ lua_a_close_in(read_file[n], (n + 1));
+ read_open[n] = closed;
+ }
+ if (c != 0) {
+ scan_optional_equals();
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ back_input();
+ if (cur_cmd != left_brace_cmd) {
+ /*tex Set |cur_name| to desired file name. */
+ scan_file_name();
+ } else {
+ scan_file_name_toks();
+ }
+ fn = pack_file_name(cur_name, cur_area, cur_ext);
+ if (lua_a_open_in(&(read_file[n]), fn, (n + 1))) {
+ read_open[n] = just_open;
+ }
+ }
+}
+
+/*tex
+ Has the long \.{\\errmessage} help been used?
+*/
+
+boolean long_help_seen;
+
+void issue_message(void)
+{
+ int old_setting; /* holds |selector| setting */
+ int c; /* identifies \.{\\message} and \.{\\errmessage} */
+ str_number s; /* the message */
+ c = cur_chr;
+ (void) 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) {
+ /*tex Print string |s| on the terminal */
+ if (term_offset + (int) str_length(s) > max_print_line - 2)
+ print_ln();
+ else if ((term_offset > 0) || (file_offset > 0))
+ print_char(' ');
+ print(s);
+ update_terminal();
+
+ } else {
+ /*tex
+ Print string |s| as an error message. 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.
+ */
+ print_err("");
+ print(s);
+ if (err_help_par != 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,",
+ "and deduce the truth by order and method."
+ );
+ }
+ error();
+ use_err_help = false;
+
+ }
+ flush_str(s);
+}
+
+/*tex
+
+The |error| routine calls on |give_err_help| if help is requested from the
+|err_help| parameter.
+
+*/
+
+void give_err_help(void)
+{
+ token_show(err_help_par);
+}
+
+/*tex
+
+The \.{\\uppercase} and \.{\\lowercase} commands are implemented by building a
+token list and then changing the cases of the letters in it.
+
+*/
+
+void shift_case(void)
+{
+ halfword b; /* |lc_code_base| or |uc_code_base| */
+ halfword p; /* runs through the token list */
+ halfword t; /* token */
+ halfword c; /* character code */
+ halfword i; /* inbetween */
+ b = cur_chr;
+ p = scan_toks(false, false);
+ p = token_link(def_ref);
+ while (p != null) {
+ /*tex
+ Change the case of the token in |p|, if a change is appropriate. When
+ the case of a |chr_code| changes, we don't change the |cmd|. We also
+ change active characters.
+ */
+ t = token_info(p);
+ if (t < cs_token_flag) {
+ c = t % STRING_OFFSET;
+ if (b == uc_code_base)
+ i = get_uc_code(c);
+ else
+ i = get_lc_code(c);
+ if (i != 0)
+ set_token_info(p, t - c + i);
+ } else if (is_active_cs(cs_text(t - cs_token_flag))) {
+ c = active_cs_value(cs_text(t - cs_token_flag));
+ if (b == uc_code_base)
+ i = get_uc_code(c);
+ else
+ i = get_lc_code(c);
+ if (i != 0)
+ set_token_info(p, active_to_cs(i, true) + cs_token_flag);
+ }
+ p = token_link(p);
+ }
+ back_list(token_link(def_ref));
+ free_avail(def_ref);
+}
+
+/*tex
+
+We come finally to the last pieces missing from |main_control|, namely the
+`\.{\\show}' commands that are useful when debugging.
+
+*/
+
+void show_whatever(void)
+{
+ halfword p; /* tail of a token list to show */
+ int t; /* type of conditional being shown */
+ int m; /* upper bound on |fi_or_else| codes */
+ int l; /* line where that conditional began */
+ int n; /* level of \.{\\if...\\fi} nesting */
+ switch (cur_chr) {
+ case show_lists:
+ begin_diagnostic();
+ show_activities();
+ break;
+ case show_box_code:
+ /*tex Show the current contents of a box. */
+ scan_register_num();
+ begin_diagnostic();
+ tprint_nl("> \\box");
+ print_int(cur_val);
+ print_char('=');
+ if (box(cur_val) == null)
+ tprint("void");
+ else
+ show_box(box(cur_val));
+ break;
+ case show_code:
+ /*tex Show the current meaning of a token, then |goto common_ending|. */
+ get_token();
+ if (interaction == error_stop_mode)
+ wake_up_terminal();
+ tprint_nl("> ");
+ if (cur_cs != 0) {
+ sprint_cs(cur_cs);
+ print_char('=');
+ }
+ print_meaning();
+ goto COMMON_ENDING;
+ break;
+ /*tex Cases for |show_whatever| */
+ case show_groups:
+ begin_diagnostic();
+ show_save_groups();
+ break;
+ case show_ifs:
+ begin_diagnostic();
+ tprint_nl("");
+ print_ln();
+ if (cond_ptr == null) {
+ tprint_nl("### ");
+ tprint("no active conditionals");
+ } else {
+ p = cond_ptr;
+ n = 0;
+ do {
+ incr(n);
+ p = vlink(p);
+ } while (p != null);
+ p = cond_ptr;
+ t = cur_if;
+ l = if_line;
+ m = if_limit;
+ do {
+ tprint_nl("### level ");
+ print_int(n);
+ tprint(": ");
+ print_cmd_chr(if_test_cmd, t);
+ if (m == fi_code)
+ tprint_esc("else");
+ print_if_line(l);
+ decr(n);
+ t = if_limit_subtype(p);
+ l = if_line_field(p);
+ m = if_limit_type(p);
+ p = vlink(p);
+ } while (p != null);
+ }
+ break;
+ default:
+ /*tex
+ Show the current value of some parameter or register, then |goto
+ common_ending|.
+ */
+ p = the_toks();
+ if (interaction == error_stop_mode)
+ wake_up_terminal();
+ tprint_nl("> ");
+ token_show(temp_token_head);
+ flush_list(token_link(temp_token_head));
+ goto COMMON_ENDING;
+ break;
+ }
+ /*tex Complete a potentially long \.{\\show} command: */
+ end_diagnostic(true);
+ print_err("OK");
+ if (selector == term_and_log) {
+ if (tracing_online_par <= 0) {
+ selector = term_only;
+ tprint(" (see the transcript file)");
+ selector = term_and_log;
+ }
+ }
+ COMMON_ENDING:
+ if (interaction < error_stop_mode) {
+ help0();
+ decr(error_count);
+ } else if (tracing_online_par > 0) {
+ 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 {
+ 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();
+}
+
+/*tex
+ This procedure gets things started properly:
+*/
+
+void initialize(void)
+{
+ int k; /* index into |mem|, |eqtb|, etc. */
+ /*tex
+ Initialize whatever \TeX\ might access and set initial values of key
+ variables
+ */
+ initialize_errors();
+ initialize_arithmetic();
+ max_used_attr = -1;
+ attr_list_cache = cache_disabled;
+ initialize_nesting();
+ /*tex Start a new current page: */
+ page_contents = empty;
+ page_tail = page_head;
+#if 0
+ vlink(page_head) = null;
+#endif
+ last_glue = max_halfword;
+ last_penalty = 0;
+ last_kern = 0;
+ last_node_type = -1;
+ page_depth = 0;
+ page_max_depth = 0;
+
+ initialize_equivalents();
+ no_new_control_sequence = true; /* new identifiers are usually forbidden */
+ init_primitives();
+
+ mag_set = 0;
+ initialize_marks();
+ initialize_read();
+
+ static_pdf = init_pdf_struct(static_pdf); /* should be init_backend() */
+
+ format_ident = 0;
+ format_name = get_nullstr();
+ initialize_directions();
+ initialize_write_files();
+ seconds_and_micros(epochseconds, microseconds);
+ initialize_start_time(static_pdf);
+
+ edit_name_start = 0;
+ stop_at_space = true;
+
+ if (ini_version) {
+ /*tex Initialize table entries (done by \.{INITEX} only). */
+ init_node_mem(500);
+ initialize_tokens();
+ /*tex Initialize the special list heads and constant nodes. */
+ initialize_alignments();
+ initialize_buildpage();
+
+ initialize_active();
+
+ set_eq_type(undefined_control_sequence, undefined_cs_cmd);
+ set_equiv(undefined_control_sequence, null);
+ set_eq_level(undefined_control_sequence, level_zero);
+ for (k = null_cs; k <= (eqtb_top - 1); k++)
+ eqtb[k] = eqtb[undefined_control_sequence];
+ set_equiv(glue_base, zero_glue);
+ set_eq_level(glue_base, level_one);
+ set_eq_type(glue_base, glue_ref_cmd);
+ for (k = glue_base + 1; k <= local_base - 1; k++) {
+ eqtb[k] = eqtb[glue_base];
+ }
+ par_shape_par_ptr = null;
+ set_eq_type(par_shape_loc, shape_ref_cmd);
+ set_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 + biggest_reg; k++)
+ eqtb[k] = eqtb[undefined_control_sequence];
+ box(0) = null;
+ set_eq_type(box_base, box_ref_cmd);
+ set_eq_level(box_base, level_one);
+ for (k = box_base + 1; k <= (box_base + biggest_reg); k++)
+ eqtb[k] = eqtb[box_base];
+ cur_font_par = null_font;
+ set_eq_type(cur_font_loc, data_cmd);
+ set_eq_level(cur_font_loc, level_one);
+ set_equiv(cat_code_base, 0);
+ set_eq_type(cat_code_base, data_cmd);
+ set_eq_level(cat_code_base, level_one);
+ eqtb[internal_math_param_base] = eqtb[cat_code_base];
+ eqtb[lc_code_base] = eqtb[cat_code_base];
+ eqtb[uc_code_base] = eqtb[cat_code_base];
+ eqtb[sf_code_base] = eqtb[cat_code_base];
+ eqtb[math_code_base] = eqtb[cat_code_base];
+ cat_code_table_par = 0;
+ initialize_math_codes();
+ initialize_text_codes();
+ initex_cat_codes(0);
+ for (k = '0'; k <= '9'; k++)
+ set_math_code(k, math_use_current_family_code, 0, k, level_one);
+ for (k = 'A'; k <= 'Z'; k++) {
+ set_math_code(k, math_use_current_family_code, 1, k, level_one);
+ set_math_code((k + 32), math_use_current_family_code, 1, (k + 32), level_one);
+ set_lc_code(k, k + 32, level_one);
+ set_lc_code(k + 32, k + 32, level_one);
+ set_uc_code(k, k, level_one);
+ set_uc_code(k + 32, k, level_one);
+ set_sf_code(k, 999, level_one);
+ }
+ for (k = int_base; k <= attribute_base - 1; k++)
+ eqtb[k].cint = 0;
+ for (k = attribute_base; k <= del_code_base - 1; k++)
+ eqtb[k].cint = UNUSED_ATTRIBUTE;
+ mag_par = 1000;
+ tolerance_par = 10000;
+ hang_after_par = 1;
+ max_dead_cycles_par = 25;
+ math_pre_display_gap_factor_par = 2000;
+ pre_bin_op_penalty_par = inf_penalty;
+ math_script_box_mode_par = 1;
+ math_script_char_mode_par = 1;
+ pre_rel_penalty_par = inf_penalty;
+ compound_hyphen_mode_par = 1;
+ escape_char_par = '\\';
+ end_line_char_par = carriage_return;
+ set_del_code('.', 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */
+ ex_hyphen_char_par = '-';
+ output_box_par = 255;
+ for (k = dimen_base; k <= eqtb_size; k++)
+ eqtb[k].cint = 0;
+ page_left_offset_par = one_inch;
+ page_top_offset_par = one_inch;
+ page_right_offset_par = one_inch;
+ page_bottom_offset_par = one_inch;
+ ini_init_primitives();
+ hash_used = frozen_control_sequence;
+ hash_high = 0;
+ cs_count = 0;
+ set_eq_type(frozen_dont_expand, dont_expand_cmd);
+ cs_text(frozen_dont_expand) = maketexstring("notexpanded:");
+ set_eq_type(frozen_primitive, ignore_spaces_cmd);
+ set_equiv(frozen_primitive, 1);
+ set_eq_level(frozen_primitive, level_one);
+ cs_text(frozen_primitive) = maketexstring("primitive");
+ create_null_font();
+ font_bytes = 0;
+ px_dimen_par = one_bp;
+ math_eqno_gap_step_par = 1000 ;
+ math_flatten_mode_par = 1; /* ord */
+ cs_text(frozen_protection) = maketexstring("inaccessible");
+ format_ident = maketexstring(" (INITEX)");
+ cs_text(end_write) = maketexstring("endwrite");
+ set_eq_level(end_write, level_one);
+ set_eq_type(end_write, outer_call_cmd);
+ set_equiv(end_write, null);
+ /*tex Bah, this is a bad place do do this. */
+ set_pdf_major_version(1);
+ set_pdf_minor_version(0);
+ }
+ synctexoffset = int_base + synctex_code;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.h b/Build/source/texk/web2c/luatexdir/tex/maincontrol.h
new file mode 100644
index 00000000000..04608c11e9b
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.h
@@ -0,0 +1,217 @@
+/* maincontrol.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef MAINCONTROL_H
+# define MAINCONTROL_H
+
+# define fil_code 0 /* identifies \.{\\hfil} and \.{\\vfil} */
+# define fill_code 1 /* identifies \.{\\hfill} and \.{\\vfill} */
+# define ss_code 2 /* identifies \.{\\hss} and \.{\\vss} */
+# define fil_neg_code 3 /* identifies \.{\\hfilneg} and \.{\\vfilneg} */
+# define skip_code 4 /* identifies \.{\\hskip} and \.{\\vskip} */
+# define mskip_code 5 /* identifies \.{\\mskip} */
+
+/* The |prefixed_command| does not have to adjust |a| so that |a mod 4=0|,
+since the following routines test for the \.{\\global} prefix as follows.
+*/
+
+# define is_global(a) (a>=4)
+
+# define define(A,B,C) do { \
+ if (is_global(a)) \
+ geq_define((A),(quarterword)(B),(C)); \
+ else \
+ eq_define((A),(quarterword)(B),(C)); \
+ } while (0)
+
+# define word_define(A,B) do { \
+ if (is_global(a)) \
+ geq_word_define((A),(B)); \
+ else \
+ eq_word_define((A),(B)); \
+ } while (0)
+
+# define define_lc_code(A,B) do { \
+ if (is_global(a)) \
+ set_lc_code((A),(B),level_one); \
+ else \
+ set_lc_code((A),(B),cur_level); \
+ } while (0)
+
+# define define_uc_code(A,B) do { \
+ if (is_global(a)) \
+ set_uc_code((A),(B),level_one); \
+ else \
+ set_uc_code((A),(B),cur_level); \
+} while (0)
+
+# define define_sf_code(A,B) do { \
+ if (is_global(a)) \
+ set_sf_code((A),(B),level_one); \
+ else \
+ set_sf_code((A),(B),cur_level); \
+ } while (0)
+
+# define define_cat_code(A,B) do { \
+ if (is_global(a)) \
+ set_cat_code(cat_code_table_par,(A),(B),level_one); \
+ else \
+ set_cat_code(cat_code_table_par,(A),(B),cur_level); \
+ } while (0)
+
+# define define_fam_fnt(A,B,C) do { \
+ if (is_global(a)) \
+ def_fam_fnt((A),(B),(C),level_one); \
+ else \
+ def_fam_fnt((A),(B),(C),cur_level); \
+ } while (0)
+
+# define define_math_param(A,B,C) do { \
+ if (is_global(a)) \
+ def_math_param((A),(B),(C),level_one); \
+ else \
+ def_math_param((A),(B),(C),cur_level); \
+ } while (0)
+
+extern halfword cur_box; /* box to be placed into its context */
+
+/*
+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.
+*/
+
+# define char_def_code 0 /* |shorthand_def| for \.{\\chardef} */
+# define math_char_def_code 1 /* |shorthand_def| for \.{\\mathchardef} */
+# define xmath_char_def_code 2 /* |shorthand_def| for \.{\\Umathchardef} */
+# define count_def_code 3 /* |shorthand_def| for \.{\\countdef} */
+# define attribute_def_code 4 /* |shorthand_def| for \.{\\attributedef} */
+# define dimen_def_code 5 /* |shorthand_def| for \.{\\dimendef} */
+# define skip_def_code 6 /* |shorthand_def| for \.{\\skipdef} */
+# define mu_skip_def_code 7 /* |shorthand_def| for \.{\\muskipdef} */
+# define toks_def_code 8 /* |shorthand_def| for \.{\\toksdef} */
+# define umath_char_def_code 9 /* |shorthand_def| for \.{\\Umathcharnumdef} */
+
+extern void adjust_space_factor(void);
+extern void main_control(void);
+extern void app_space(void);
+extern void insert_dollar_sign(void);
+extern void insert_dollar_sign_par_end(void);
+extern void you_cant(void);
+extern void report_illegal_case(void);
+extern boolean privileged(void);
+extern boolean its_all_over(void);
+extern void append_glue(void);
+extern void append_kern(void);
+extern void off_save(void);
+extern void handle_right_brace(void);
+extern void extra_right_brace(void);
+extern void normal_paragraph(void);
+extern void box_end(int box_context);
+extern void scan_box(int box_context);
+extern void new_graf(boolean indented);
+extern void indent_in_hmode(void);
+extern void head_for_vmode(void);
+extern void end_graf(int);
+extern void begin_insert_or_adjust(void);
+extern void handle_mark(void);
+extern void append_penalty(void);
+extern void delete_last(void);
+extern void unpackage(void);
+extern void append_italic_correction(void);
+extern void append_local_box(int kind);
+extern void append_discretionary(void);
+extern void build_local_box(void);
+extern void build_discretionary(void);
+extern void make_accent(void);
+extern void align_error(void);
+extern void no_align_error(void);
+extern void omit_error(void);
+extern void do_endv(void);
+extern void cs_error(void);
+extern void prefixed_command(void);
+extern void fixup_directions(void);
+extern void fixup_directions_only(void);
+
+
+/* Assignments from Lua need helpers. */
+
+# define is_int_assign(cmd) (cmd==assign_int_cmd)
+# define is_attr_assign(cmd) (cmd==assign_attr_cmd)
+# define is_dim_assign(cmd) (cmd==assign_dimen_cmd)
+# define is_glue_assign(cmd) (cmd==assign_glue_cmd)
+# define is_mu_glue_assign(cmd) (cmd==assign_mu_glue_cmd)
+# define is_toks_assign(cmd) (cmd==assign_toks_cmd)
+
+# define show_code 0 /* \.{\\show} */
+# define show_box_code 1 /* \.{\\showbox} */
+# define show_the_code 2 /* \.{\\showthe} */
+# define show_lists 3 /* \.{\\showlists} */
+# define show_groups 4 /* \.{\\showgroups} */
+# define show_tokens 5 /* \.{\\showtokens} , must be odd! */
+# define show_ifs 6 /* \.{\\showifs} */
+
+# define print_if_line(A) if ((A)!=0) { \
+ tprint(" entered on line "); print_int((A)); \
+ }
+
+/*
+ extern halfword swap_hang_indent(halfword indentation, halfword shape_mode);
+ extern halfword swap_parshape_indent(halfword indentation, halfword width, halfword shape_mode);
+*/
+
+#define swap_hang_indent(indentation) \
+ ( ((shape_mode_par == 1 || shape_mode_par == 3 || shape_mode_par == -1 || shape_mode_par == -3)) ? negate(indentation) : indentation )
+
+#define swap_parshape_indent(indentation,width) \
+ ( ((shape_mode_par == 2 || shape_mode_par == 3 || shape_mode_par == -2 || shape_mode_par == -3)) ? (hsize_par - width - indentation) : indentation )
+
+extern void get_r_token(void);
+extern void assign_internal_value(int a, halfword p, int cur_val);
+extern void do_register_command(int a);
+extern void alter_aux(void);
+extern void alter_prev_graf(void);
+extern void alter_page_so_far(void);
+extern void alter_integer(void);
+extern void alter_box_dimen(void);
+extern void new_interaction(void);
+extern halfword after_token;
+extern void do_assignments(void);
+extern void open_or_close_in(void);
+extern boolean long_help_seen;
+extern void issue_message(void);
+extern void give_err_help(void);
+extern void shift_case(void);
+extern void show_whatever(void);
+
+extern void initialize(void); /* this procedure gets things started properly */
+
+/*extern int local_level;*/
+
+extern void local_control(void);
+extern halfword local_scan_box(void);
+extern int current_local_level(void);
+extern void end_local_control(void);
+extern void local_control_message(const char *s);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/mathcodes.c b/Build/source/texk/web2c/luatexdir/tex/mathcodes.c
new file mode 100644
index 00000000000..42e8b0b15b9
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/mathcodes.c
@@ -0,0 +1,360 @@
+/*
+
+mathnodes.w
+
+Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+We support tre traditional math codes as well as larger ones suitable for
+\UNICODE\ input and fonts.
+
+*/
+
+static sa_tree mathcode_head = NULL;
+
+/*tex the |0xFFFFFFFF| is a flag value. */
+
+#define MATHCODESTACK 8
+#define MATHCODEDEFAULT 0xFFFFFFFF
+#define MATHCODEACTIVE 0xFFFFFFFE
+
+/*tex
+
+Delcodes are also went larger.
+
+*/
+
+static sa_tree delcode_head = NULL;
+
+#define DELCODESTACK 4
+#define DELCODEDEFAULT 0xFFFFFFFF
+
+/*tex
+
+We now get lots of helpers for definitions and printing. The storage model
+that we use is different because we can hav emany more so we need to be
+sparse. Therefore we use trees.
+
+*/
+
+
+#define print_hex_digit(A) do { \
+ if ((A)>=10) print_char('A'+(A)-10); \
+ else print_char('0'+(A)); \
+ } while (0)
+
+#define two_hex(A) do { \
+ print_hex_digit((A)/16); \
+ print_hex_digit((A)%16); \
+ } while (0)
+
+#define four_hex(A) do { \
+ two_hex((A)/256); \
+ two_hex((A)%256); \
+ } while (0)
+
+#define six_hex(A) do { \
+ two_hex((A)/65536); \
+ two_hex(((A)%65536)/256); \
+ two_hex((A)%256); \
+ } while (0)
+
+mathcodeval mathchar_from_integer(int value, int extcode)
+{
+ mathcodeval mval;
+ if (extcode == tex_mathcode) {
+ mval.class_value = (value / 0x1000);
+ mval.family_value = ((value % 0x1000) / 0x100);
+ mval.character_value = (value % 0x100);
+ } else {
+ int mfam = (value / 0x200000) & 0x7FF;
+ mval.class_value = mfam % 0x08;
+ mval.family_value = mfam / 0x08;
+ mval.character_value = value & 0x1FFFFF;
+ }
+ return mval;
+}
+
+void show_mathcode_value_old(int value)
+{
+ print_char('"');
+ four_hex(value);
+}
+
+void show_mathcode_value(mathcodeval c)
+{
+ print_char('"');
+ print_hex_digit(c.class_value);
+ print_char('"');
+ two_hex(c.family_value);
+ print_char('"');
+ six_hex(c.character_value);
+}
+
+static void show_mathcode(int n)
+{
+ mathcodeval c = get_math_code(n);
+ tprint_esc("Umathcode");
+ print_int(n);
+ print_char('=');
+ show_mathcode_value(c);
+}
+
+static void unsavemathcode(quarterword gl)
+{
+ sa_stack_item st;
+ if (mathcode_head->stack == NULL)
+ return;
+ while (mathcode_head->stack_ptr > 0 && abs(mathcode_head->stack[mathcode_head->stack_ptr].level) >= gl) {
+ st = mathcode_head->stack[mathcode_head->stack_ptr];
+ if (st.level > 0) {
+ rawset_sa_item(mathcode_head, st.code, st.value);
+ if (tracing_restores_par > 1) {
+ begin_diagnostic();
+ print_char('{');
+ tprint("restoring");
+ print_char(' ');
+ show_mathcode(st.code);
+ print_char('}');
+ end_diagnostic(false);
+ }
+ }
+ (mathcode_head->stack_ptr)--;
+ }
+}
+
+void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword level)
+{
+ sa_tree_item v;
+ if (mathclass == 8 && mathfamily == 0 && mathcharacter == 0) {
+ v.uint_value = MATHCODEACTIVE;
+ } else {
+ v.math_code_value.class_value = mathclass;
+ v.math_code_value.family_value = mathfamily;
+ v.math_code_value.character_value = mathcharacter;
+ }
+ set_sa_item(mathcode_head, n, v, level);
+ if (tracing_assigns_par > 1) {
+ begin_diagnostic();
+ print_char('{');
+ tprint("assigning");
+ print_char(' ');
+ show_mathcode(n);
+ print_char('}');
+ end_diagnostic(false);
+ }
+}
+
+mathcodeval get_math_code(int n)
+{
+ mathcodeval d;
+ sa_tree_item v = get_sa_item(mathcode_head, n);
+ if (v.uint_value == MATHCODEDEFAULT) {
+ d.class_value = 0;
+ d.family_value = 0;
+ d.character_value = n;
+ } else if (v.uint_value == MATHCODEACTIVE) {
+ d.class_value = 8;
+ d.family_value = 0;
+ d.character_value = 0;
+ } else {
+ d.class_value = v.math_code_value.class_value;
+ if (d.class_value == 8) {
+ d.family_value = 0;
+ d.character_value = n;
+ } else {
+ d.family_value = v.math_code_value.family_value;
+ d.character_value = v.math_code_value.character_value;
+ }
+ }
+ return d;
+}
+
+int get_math_code_num(int n)
+{
+ mathcodeval d = get_math_code(n);
+ return (d.class_value + (d.family_value * 8)) * (65536 * 32) + d.character_value;
+}
+
+static void initializemathcode(void)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_value.uint_value = MATHCODEDEFAULT;
+ mathcode_head = new_sa_tree(MATHCODESTACK, 1, sa_value);
+}
+
+static void dumpmathcode(void)
+{
+ dump_sa_tree(mathcode_head,"mathcodes");
+}
+
+static void undumpmathcode(void)
+{
+ mathcode_head = undump_sa_tree("mathcodes");
+}
+
+static void show_delcode(int n)
+{
+ delcodeval c;
+ c = get_del_code(n);
+ tprint_esc("Udelcode");
+ print_int(n);
+ print_char('=');
+ if (c.small_family_value < 0) {
+ print_char('-');
+ print_char('1');
+ } else {
+ print_char('"');
+ two_hex(c.small_family_value);
+ six_hex(c.small_character_value);
+ }
+}
+
+static void unsavedelcode(quarterword gl)
+{
+ sa_stack_item st;
+ if (delcode_head->stack == NULL)
+ return;
+ while (delcode_head->stack_ptr > 0 && abs(delcode_head->stack[delcode_head->stack_ptr].level) >= gl) {
+ st = delcode_head->stack[delcode_head->stack_ptr];
+ if (st.level > 0) {
+ rawset_sa_item(delcode_head, st.code, st.value);
+ if (tracing_restores_par > 1) {
+ begin_diagnostic();
+ print_char('{');
+ tprint("restoring");
+ print_char(' ');
+ show_delcode(st.code);
+ print_char('}');
+ end_diagnostic(false);
+ }
+ }
+ (delcode_head->stack_ptr)--;
+ }
+}
+
+void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl)
+{
+ sa_tree_item v;
+ v.del_code_value.class_value = 0;
+ v.del_code_value.small_family_value = smathfamily;
+ v.del_code_value.small_character_value = smathcharacter;
+ v.del_code_value.dummy_value = 0;
+ v.del_code_value.large_family_value = lmathfamily;
+ v.del_code_value.large_character_value = lmathcharacter;
+ /*tex Always global! */
+ set_sa_item(delcode_head, n, v, gl);
+ if (tracing_assigns_par > 1) {
+ begin_diagnostic();
+ print_char('{');
+ tprint("assigning");
+ print_char(' ');
+ show_delcode(n);
+ print_char('}');
+ end_diagnostic(false);
+ }
+}
+
+delcodeval get_del_code(int n)
+{
+ delcodeval d;
+ sa_tree_item v = get_sa_item(delcode_head, n);
+ if (v.uint_value == DELCODEDEFAULT) {
+ d.class_value = 0;
+ d.small_family_value = -1;
+ d.small_character_value = 0;
+ d.large_family_value = 0;
+ d.large_character_value = 0;
+ } else {
+ d.class_value = v.del_code_value.class_value;
+ d.small_family_value = v.del_code_value.small_family_value;
+ d.small_character_value = v.del_code_value.small_character_value;
+ d.large_family_value = v.del_code_value.large_family_value;
+ d.large_character_value = v.del_code_value.large_character_value;
+ }
+ return d;
+}
+
+/*tex
+
+This really only works for old-style delcodes!
+
+*/
+
+int get_del_code_num(int n)
+{
+ delcodeval d = get_del_code(n);
+ if (d.small_family_value < 0) {
+ return -1;
+ } else {
+ return ((d.small_family_value * 256 + d.small_character_value) * 4096 +
+ (d.large_family_value * 256) + d.large_character_value);
+ }
+}
+
+static void initializedelcode(void)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_value.uint_value = DELCODEDEFAULT;
+ delcode_head = new_sa_tree(DELCODESTACK, 2, sa_value);
+}
+
+static void dumpdelcode(void)
+{
+ dump_sa_tree(delcode_head,"delcodes");
+}
+
+static void undumpdelcode(void)
+{
+ delcode_head = undump_sa_tree("delcodes");
+}
+
+void unsave_math_codes(quarterword grouplevel)
+{
+ unsavemathcode(grouplevel);
+ unsavedelcode(grouplevel);
+}
+
+void initialize_math_codes(void)
+{
+ initializemathcode();
+ initializedelcode();
+}
+
+void free_math_codes(void)
+{
+ destroy_sa_tree(mathcode_head);
+ destroy_sa_tree(delcode_head);
+}
+
+void dump_math_codes(void)
+{
+ dumpmathcode();
+ dumpdelcode();
+}
+
+void undump_math_codes(void)
+{
+ undumpmathcode();
+ undumpdelcode();
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/mathcodes.h b/Build/source/texk/web2c/luatexdir/tex/mathcodes.h
new file mode 100644
index 00000000000..fe731ff2be5
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/mathcodes.h
@@ -0,0 +1,67 @@
+/* mathcodes.h
+
+ Copyright 2009-2012 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef MATHCODES_H
+# define MATHCODES_H
+
+/* this is a flag for |scan_delimiter| */
+
+# define no_mathcode 0
+# define tex_mathcode 8
+# define umath_mathcode 21
+# define umathnum_mathcode 22
+
+typedef struct mathcodeval {
+ int class_value;
+ int family_value;
+ int character_value;
+} mathcodeval;
+
+void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword gl);
+
+mathcodeval get_math_code(int n);
+int get_math_code_num(int n);
+int get_del_code_num(int n);
+mathcodeval scan_mathchar(int extcode);
+mathcodeval scan_delimiter_as_mathchar(int extcode);
+
+mathcodeval mathchar_from_integer(int value, int extcode);
+void show_mathcode_value(mathcodeval d);
+void show_mathcode_value_old(int value);
+
+typedef struct delcodeval {
+ int class_value;
+ int small_family_value;
+ int small_character_value;
+ int large_family_value;
+ int large_character_value;
+} delcodeval;
+
+void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl);
+
+delcodeval get_del_code(int n);
+
+void unsave_math_codes(quarterword grouplevel);
+void initialize_math_codes(void);
+void dump_math_codes(void);
+void undump_math_codes(void);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/memoryword.c b/Build/source/texk/web2c/luatexdir/tex/memoryword.c
new file mode 100644
index 00000000000..7bf758dd0d4
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/memoryword.c
@@ -0,0 +1,29 @@
+/*
+memoryword.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+The debug code is no longer present!
+
+*/
diff --git a/Build/source/texk/web2c/luatexdir/tex/memoryword.h b/Build/source/texk/web2c/luatexdir/tex/memoryword.h
new file mode 100644
index 00000000000..5db2bb8cb36
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/memoryword.h
@@ -0,0 +1,128 @@
+/* memoryword.h
+
+ Copyright 2009-2010 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+/* This header file is extra special because it is read in from
+ within the pascal source */
+
+#ifndef MEMORYWORD_H
+# define MEMORYWORD_H
+
+/* texmfmem.h: the memory_word type, which is too hard to translate
+ automatically from Pascal. We have to make sure the byte-swapping
+ that the (un)dumping routines do suffices to put things in the right
+ place in memory.
+
+ A memory_word can be broken up into a `twohalves' or a
+ `fourquarters', and a `twohalves' can be further broken up. Here is
+ a picture. ..._M = most significant byte, ..._L = least significant
+ byte.
+
+
+ BigEndian:
+ twohalves.v: RH_MM RH_ML RH_LM RH_LL LH_MM LH_ML LH_LM LH_LL
+ twohalves.u: ---------JUNK---------- ----B0----- ----B1-----
+ fourquarters: ----B0----- ----B1----- ----B2----- ----B3-----
+ twoints: ---------CINT0--------- ---------CINT1---------
+
+ LittleEndian:
+ twohalves.v: LH_LL LH_LM LH_ML LH_MM RH_LL RH_LM RH_ML RH_MM
+ twohalves.u: ----B1----- ----B0-----
+ fourquarters: ----B3----- ----B2----- ----B1----- ----B0-----
+ twoints: ---------CINT1--------- ---------CINT0---------
+
+*/
+
+
+typedef union {
+ struct {
+# ifdef WORDS_BIGENDIAN
+ halfword RH, LH;
+# else
+ halfword LH, RH;
+# endif
+ } v;
+
+ struct { /* Make B0,B1 overlap the most significant bytes of LH. */
+# ifdef WORDS_BIGENDIAN
+ halfword junk;
+ quarterword B0, B1;
+# else /* not WORDS_BIGENDIAN */
+ /* If 32-bit memory words, have to do something. */
+ quarterword B1, B0;
+# endif /* LittleEndian */
+ } u;
+} two_halves;
+
+typedef struct {
+ struct {
+# ifdef WORDS_BIGENDIAN
+ quarterword B0, B1, B2, B3;
+# else
+ quarterword B3, B2, B1, B0;
+# endif
+ } u;
+} four_quarters;
+
+typedef struct {
+# ifdef WORDS_BIGENDIAN
+ int CINT0, CINT1;
+# else
+ int CINT1, CINT0;
+# endif
+} two_ints;
+
+typedef struct {
+ glue_ratio GLUE;
+} glues;
+
+typedef union {
+ two_halves hh;
+ four_quarters qqqq;
+ two_ints ii;
+ glues gg;
+} memory_word;
+
+# define b0 u.B0
+# define b1 u.B1
+# define b2 u.B2
+# define b3 u.B3
+
+# define rh v.RH
+# define lhfield v.LH
+
+# define cint ii.CINT0
+# define cint1 ii.CINT1
+
+# define gr gg.GLUE
+
+/* the next five defines are needed for the prototypes in web2c's coerce.h */
+
+# define memoryword memory_word
+# define strnumber str_number
+# define packedASCIIcode packed_ASCII_code
+# define poolpointer pool_pointer
+
+typedef FILE *word_file;
+
+# ifdef DEBUG
+extern void print_word(memory_word w);
+# endif
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.c b/Build/source/texk/web2c/luatexdir/tex/mlist.c
new file mode 100644
index 00000000000..2ac631ea339
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/mlist.c
@@ -0,0 +1,4830 @@
+/*
+
+Copyright 2006-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation;
+either version 2 of the License, or (at your option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+/*tex
+
+ In traditional \TeX\ the italic correction is added to the width of the
+ glyph. This is part of the engine design and related font design. In opentype
+ math this is different. There the italic correction had more explicit usage.
+ The 1.7 spec says:
+
+ \startitemize
+
+ \startitem
+ {\em italic correction:} When a run of slanted characters is followed by
+ a straight character (such as an operator or a delimiter), the italics
+ correction of the last glyph is added to its advance width.
+
+ When positioning limits on an N-ary operator (e.g., integral sign), the
+ horizontal position of the upper limit is moved to the right by ½ of the
+ italics correction, while the position of the lower limit is moved to the
+ left by the same distance.
+
+ When positioning superscripts and subscripts, their default horizontal
+ positions are also different by the amount of the italics correction of
+ the preceding glyph.
+ \stopitem
+
+ \startitem
+ {\em math kerning:} Set the default horizontal position for the
+ superscript as shifted relative to the position of the subscript by the
+ italics correction of the base glyph.
+ \stopitem
+
+ \stopitemize
+
+ Before this was specified we had to gamble a bit and assume that cambria was
+ the font benchmark and trust our eyes (and msword) for the logic. I must
+ admit that I have been fighting these italics in fonts (and the heuristics
+ that Lua\TeX\ provided) right from the start (e.g. using Lua based
+ postprocessing) but by now we know more and have more fonts to test with.
+ More fonts are handy because not all fonts are alike when it comes to
+ italics. Axis are another area of concern, as it looks like opentype math
+ fonts often already apply that shift.
+
+*/
+
+#define is_new_mathfont(A) ((font_math_params(A) >0) && (math_old_par == 0))
+#define is_old_mathfont(A,B) ((font_math_params(A)==0) && (font_params(A)>=(B)))
+#define do_new_math(A) ((font_math_params(A) >0) && (font_oldmath(A) == 0) && (math_old_par == 0))
+#define protect_glyph(A) subtype(A)=256
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+#define reset_attributes(p,newatt) do { \
+ delete_attribute_ref(node_attr(p)); \
+ node_attr(p) = newatt; \
+ if (newatt!=null) { \
+ add_node_attr_ref(node_attr(p)); \
+ } \
+ } while (0)
+
+#define DEFINE_MATH_PARAMETERS(A,B,C,D) do { \
+ if (B==text_size) { \
+ def_math_param(A, text_style, (C),D); \
+ def_math_param(A, cramped_text_style, (C),D); \
+ } else if (B==script_size) { \
+ def_math_param(A, script_style, (C),D); \
+ def_math_param(A, cramped_script_style, (C),D); \
+ } else if (B==script_script_size) { \
+ def_math_param(A, script_script_style, (C),D); \
+ def_math_param(A, cramped_script_script_style, (C),D); \
+ } \
+ } while (0)
+
+#define DEFINE_DMATH_PARAMETERS(A,B,C,D) do { \
+ if (B==text_size) { \
+ def_math_param(A, display_style,(C),D); \
+ def_math_param(A, cramped_display_style,(C),D); \
+ } \
+ } while (0)
+
+#define font_MATH_par(a,b) \
+ (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter)
+
+/*tex
+
+ Here are the math parameters that are font-dependant.
+
+ 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.
+
+*/
+
+#define total_mathsy_params 22
+#define total_mathex_params 13
+
+#define mathsy(A,B) font_param(fam_fnt(2,A),B)
+
+/*tex height of `\.x' */
+
+#define math_x_height(A) mathsy(A,5)
+
+/*tex \.{18mu} */
+
+#define math_quad(A) mathsy(A,6)
+
+/*tex numerator shift-up in display styles */
+
+#define num1(A) mathsy(A,8)
+
+/*tex numerator shift-up in non-display, non-\.{\\atop} */
+
+#define num2(A) mathsy(A,9)
+
+/*tex numerator shift-up in non-display \.{\\atop} */
+
+#define num3(A) mathsy(A,10)
+
+/*tex denominator shift-down in display styles */
+
+#define denom1(A) mathsy(A,11)
+
+/*tex denominator shift-down in non-display styles */
+
+#define denom2(A) mathsy(A,12)
+
+/*tex superscript shift-up in uncramped display style */
+
+#define sup1(A) mathsy(A,13)
+
+/*tex superscript shift-up in uncramped non-display */
+
+#define sup2(A) mathsy(A,14)
+
+/*tex superscript shift-up in cramped styles */
+
+#define sup3(A) mathsy(A,15)
+
+/*tex subscript shift-down if superscript is absent */
+
+#define sub1(A) mathsy(A,16)
+
+/*tex subscript shift-down if superscript is present */
+
+#define sub2(A) mathsy(A,17)
+
+/*tex superscript baseline below top of large box */
+
+#define sup_drop(A) mathsy(A,18)
+
+/*tex subscript baseline below bottom of large box */
+
+#define sub_drop(A) mathsy(A,19)
+
+/*tex size of \.{\\atopwithdelims} delimiters in display styles */
+
+#define delim1(A) mathsy(A,20)
+
+/*tex size of \.{\\atopwithdelims} delimiters in non-displays */
+
+#define delim2(A) mathsy(A,21)
+
+/*tex height of fraction lines above the baseline */
+
+#define axis_height(A) mathsy(A,22)
+
+/*tex
+
+ 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).
+
+*/
+
+#define mathex(A,B) font_param(fam_fnt(3,A),B)
+
+/*tex thickness of \.{\\over} bars */
+
+#define default_rule_thickness(A) mathex(A,8)
+
+/*tex minimum clearance above a displayed op */
+
+#define big_op_spacing1(A) mathex(A,9)
+
+/*tex minimum clearance below a displayed op */
+
+#define big_op_spacing2(A) mathex(A,10)
+
+/*tex minimum baselineskip above displayed op */
+
+#define big_op_spacing3(A) mathex(A,11)
+
+/*tex minimum baselineskip below displayed op */
+
+#define big_op_spacing4(A) mathex(A,12)
+
+/*tex padding above and below displayed limits */
+
+#define big_op_spacing5(A) mathex(A,13)
+
+/*tex
+
+ \LUATEX makes a bunch of extensions cf. the |MATH| table in \OPENTYPE, but
+ some of the |MathConstants| values have no matching usage in \LUATEX\ right
+ now.
+
+ \startitemize
+
+ \startitem
+ |ScriptPercentScaleDown| |ScriptScriptPercentScaleDown|: These should
+ be handled by the macro package, on the engine side there are three
+ separate fonts.
+ \stopitem
+
+ \startitem
+ |DelimitedSubFormulaMinHeight|: This is perhaps related to word's
+ natural math input? We have no idea what to do about it.
+ \stopitem
+
+ \startitem
+ |MathLeading|: \LUATEX does not currently handle multi-line displays,
+ and the parameter does not seem to make much sense elsewhere.
+ \stopitem
+
+ \startitem
+ |FlattenedAccentBaseHeight|: This is based on the |flac| |GSUB|
+ feature. It would not be hard to support that, but proper math accent
+ placements cf.\ |MATH| needs support for |MathTopAccentAttachment|
+ table to be implemented first.
+ \stopitem
+
+ \stopitemize
+
+*/
+
+static void math_param_error(const char *param, int style)
+{
+ char s[256];
+ const char *hlp[] = {
+ "Sorry, but I can't typeset math unless various parameters have",
+ "been set. This is normally done by loading special math fonts",
+ "into the math family slots. Your font set is lacking at least",
+ "the parameter mentioned earlier.",
+ NULL
+ };
+ snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", param, math_style_names[style]);
+ tex_error(s, hlp);
+ return;
+}
+
+static scaled accent_base_height(int f)
+{
+ scaled a;
+ if (do_new_math(f)) {
+ a = font_MATH_par(f, AccentBaseHeight);
+ if (a == undefined_math_parameter)
+ a = x_height(f);
+ } else {
+ a = x_height(f);
+ }
+ return a;
+}
+
+/*tex
+
+ The non-staticness of this function is for the benefit of |texmath.w|.
+ Watch out, this one uses the style! The style and size numbers don't
+ match because we have cramped styles.
+
+*/
+
+scaled get_math_quad_style(int var)
+{
+ scaled a = get_math_param(math_param_quad, var);
+ if (a == undefined_math_parameter) {
+ math_param_error("quad", var);
+ return 0;
+ } else {
+ return a;
+ }
+}
+
+/*tex
+
+ For this reason the next one is different because it is called with a size
+ specifier instead of a style specifier.
+
+*/
+
+static scaled math_axis_size(int b)
+{
+ scaled a;
+ int var;
+ if (b == script_size)
+ var = script_style;
+ else if (b == script_script_size)
+ var = script_script_style;
+ else
+ var = text_style;
+ a = get_math_param(math_param_axis, var);
+ if (a == undefined_math_parameter) {
+ math_param_error("axis", var);
+ return 0;
+ } else {
+ return a;
+ }
+}
+
+scaled get_math_quad_size(int b)
+{
+ int var;
+ if (b == script_size)
+ var = script_style;
+ else if (b == script_script_size)
+ var = script_script_style;
+ else
+ var = text_style;
+ return get_math_param(math_param_quad, var);
+}
+
+static scaled minimum_operator_size(int var)
+{
+ scaled a = get_math_param(math_param_operator_size, var);
+ return a;
+}
+
+/*tex
+
+ Old-style fonts do not define the |radical_rule|. This allows |make_radical|
+ to select the backward compatibility code, and it means that we can't raise
+ an error here.
+
+*/
+
+static scaled radical_rule_par(int var)
+{
+ scaled a = get_math_param(math_param_radical_rule, var);
+ return a;
+}
+
+/*tex
+
+ Now follow all the trivial math parameters.
+
+*/
+
+#define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b)
+#define get_math_param_or_zero(a,b) do_get_math_param_or_zero(a, math_param_##b, #b)
+
+static scaled do_get_math_param_or_error(int var, int param, const char *name)
+{
+ scaled a = get_math_param(param, var);
+ if (a == undefined_math_parameter) {
+ math_param_error(name, var);
+ a = 0;
+ }
+ return a;
+}
+
+static scaled do_get_math_param_or_zero(int var, int param, const char *name)
+{
+ scaled a = get_math_param(param, var);
+ if (a == undefined_math_parameter) {
+ a = 0;
+ }
+ return a;
+}
+
+/*tex
+
+ A variant on a suggestion on the list based on analysis by UV.
+
+*/
+
+static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
+ scaled delta, delta1, delta2;
+ if (axis) {
+ delta2 = max_d + math_axis_size(cur_size);
+ } else {
+ delta2 = max_d;
+ }
+ delta1 = max_h + max_d - delta2;
+ if (delta2 > delta1) {
+ /*tex |delta1| is max distance from axis */
+ delta1 = delta2;
+ }
+ delta = (delta1 / 500) * delimiter_factor_par;
+ delta2 = delta1 + delta1 - delimiter_shortfall_par;
+ if (delta < delta2) {
+ return delta2;
+ } else {
+ return delta;
+ }
+}
+
+#define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before)
+#define radical_degree_after(a) get_math_param_or_error(a, radical_degree_after)
+#define radical_degree_raise(a) get_math_param_or_error(a, radical_degree_raise)
+
+#define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min)
+
+#define overbar_rule(a) get_math_param_or_error(a, overbar_rule)
+#define overbar_kern(a) get_math_param_or_error(a, overbar_kern)
+#define overbar_vgap(a) get_math_param_or_error(a, overbar_vgap)
+
+#define underbar_rule(a) get_math_param_or_error(a, underbar_rule)
+#define underbar_kern(a) get_math_param_or_error(a, underbar_kern)
+#define underbar_vgap(a) get_math_param_or_error(a, underbar_vgap)
+
+#define under_delimiter_vgap(a) get_math_param_or_error(a, under_delimiter_vgap)
+#define under_delimiter_bgap(a) get_math_param_or_error(a, under_delimiter_bgap)
+
+#define over_delimiter_vgap(a) get_math_param_or_error(a, over_delimiter_vgap)
+#define over_delimiter_bgap(a) get_math_param_or_error(a, over_delimiter_bgap)
+
+#define radical_vgap(a) get_math_param_or_error(a, radical_vgap)
+#define radical_kern(a) get_math_param_or_error(a, radical_kern)
+
+#define stack_vgap(a) get_math_param_or_error(a, stack_vgap)
+#define stack_num_up(a) get_math_param_or_error(a, stack_num_up)
+#define stack_denom_down(a) get_math_param_or_error(a, stack_denom_down)
+
+#define fraction_rule(a) get_math_param_or_error(a, fraction_rule)
+#define fraction_num_vgap(a) get_math_param_or_error(a, fraction_num_vgap)
+#define fraction_denom_vgap(a) get_math_param_or_error(a, fraction_denom_vgap)
+#define fraction_num_up(a) get_math_param_or_error(a, fraction_num_up)
+#define fraction_denom_down(a) get_math_param_or_error(a, fraction_denom_down)
+#define fraction_del_size_new(a) get_math_param_or_error(a, fraction_del_size)
+/* fraction_del_size_old(a) get_math_param (a, math_param_fraction_del_size) */
+#define fraction_del_size_old(a) get_math_param_or_error(a, fraction_del_size)
+
+#define skewed_fraction_hgap(a) get_math_param_or_error(a, skewed_fraction_hgap)
+#define skewed_fraction_vgap(a) get_math_param_or_error(a, skewed_fraction_vgap)
+
+#define limit_above_vgap(a) get_math_param_or_error(a, limit_above_vgap)
+#define limit_above_bgap(a) get_math_param_or_error(a, limit_above_bgap)
+#define limit_above_kern(a) get_math_param_or_error(a, limit_above_kern)
+
+#define limit_below_vgap(a) get_math_param_or_error(a, limit_below_vgap)
+#define limit_below_bgap(a) get_math_param_or_error(a, limit_below_bgap)
+#define limit_below_kern(a) get_math_param_or_error(a, limit_below_kern)
+
+#define nolimit_sub_factor(a) get_math_param_or_zero(a, nolimit_sub_factor)
+#define nolimit_sup_factor(a) get_math_param_or_zero(a, nolimit_sup_factor)
+
+#define sub_shift_drop(a) get_math_param_or_error(a, sub_shift_drop)
+#define sup_shift_drop(a) get_math_param_or_error(a, sup_shift_drop)
+#define sub_shift_down(a) get_math_param_or_error(a, sub_shift_down)
+#define sub_sup_shift_down(a) get_math_param_or_error(a, sub_sup_shift_down)
+#define sup_shift_up(a) get_math_param_or_error(a, sup_shift_up)
+#define sub_top_max(a) get_math_param_or_error(a, sub_top_max)
+#define sup_bottom_min(a) get_math_param_or_error(a, sup_bottom_min)
+#define sup_sub_bottom_max(a) get_math_param_or_error(a, sup_sub_bottom_max)
+#define subsup_vgap(a) get_math_param_or_error(a, subsup_vgap)
+
+#define space_after_script(a) get_math_param_or_error(a, space_after_script)
+
+void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
+{
+ if (is_new_mathfont(f)) {
+
+ /*tex Fix all known parameters. */
+
+ DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
+ font_size(f), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
+ font_size(f), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
+ font_MATH_par(f, AxisHeight), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
+ font_MATH_par(f, AxisHeight), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
+ font_MATH_par(f, OverbarExtraAscender), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
+ font_MATH_par(f, OverbarExtraAscender), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
+ font_MATH_par(f, OverbarRuleThickness), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
+ font_MATH_par(f, OverbarRuleThickness), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
+ font_MATH_par(f, OverbarVerticalGap), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
+ font_MATH_par(f, OverbarVerticalGap), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
+ font_MATH_par(f, UnderbarExtraDescender), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
+ font_MATH_par(f, UnderbarExtraDescender), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
+ font_MATH_par(f, UnderbarRuleThickness), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
+ font_MATH_par(f, UnderbarRuleThickness), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
+ font_MATH_par(f, UnderbarVerticalGap), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
+ font_MATH_par(f, UnderbarVerticalGap), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
+ font_MATH_par(f, StretchStackGapAboveMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
+ font_MATH_par(f, StretchStackGapAboveMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
+ font_MATH_par(f, StretchStackBottomShiftDown), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
+ font_MATH_par(f, StretchStackBottomShiftDown), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
+ font_MATH_par(f, StretchStackGapBelowMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
+ font_MATH_par(f, StretchStackGapBelowMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
+ font_MATH_par(f, StretchStackTopShiftUp), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
+ font_MATH_par(f, StretchStackTopShiftUp), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
+ font_MATH_par(f, StackTopShiftUp), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
+ font_MATH_par(f, StackTopDisplayStyleShiftUp), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
+ font_MATH_par(f, StackBottomShiftDown), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
+ font_MATH_par(f, StackBottomDisplayStyleShiftDown), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
+ font_MATH_par(f, StackGapMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
+ font_MATH_par(f, StackDisplayStyleGapMin), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
+ font_MATH_par(f, RadicalExtraAscender), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
+ font_MATH_par(f, RadicalExtraAscender), lvl);
+
+ DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id,
+ font_MATH_par(f, DisplayOperatorMinHeight), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id,
+ font_MATH_par(f, RadicalRuleThickness), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id,
+ font_MATH_par(f, RadicalRuleThickness), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
+ font_MATH_par(f, RadicalVerticalGap), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
+ font_MATH_par(f, RadicalDisplayStyleVerticalGap), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
+ font_MATH_par(f, RadicalKernBeforeDegree), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
+ font_MATH_par(f, RadicalKernBeforeDegree), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
+ font_MATH_par(f, RadicalKernAfterDegree), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
+ font_MATH_par(f, RadicalKernAfterDegree), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
+ font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
+ font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl);
+
+ if (size_id == text_size) {
+ def_math_param(math_param_sup_shift_up, display_style,
+ font_MATH_par(f, SuperscriptShiftUp), lvl);
+ def_math_param(math_param_sup_shift_up, cramped_display_style,
+ font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
+ def_math_param(math_param_sup_shift_up, text_style,
+ font_MATH_par(f, SuperscriptShiftUp), lvl);
+ def_math_param(math_param_sup_shift_up, cramped_text_style,
+ font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
+ } else if (size_id == script_size) {
+ def_math_param(math_param_sup_shift_up, script_style,
+ font_MATH_par(f, SuperscriptShiftUp), lvl);
+ def_math_param(math_param_sup_shift_up, cramped_script_style,
+ font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
+ } else if (size_id == script_script_size) {
+ def_math_param(math_param_sup_shift_up, script_script_style,
+ font_MATH_par(f, SuperscriptShiftUp), lvl);
+ def_math_param(math_param_sup_shift_up, cramped_script_script_style,
+ font_MATH_par(f, SuperscriptShiftUpCramped), lvl);
+ }
+
+ DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id,
+ font_MATH_par(f, SubscriptBaselineDropMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id,
+ font_MATH_par(f, SubscriptBaselineDropMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id,
+ font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id,
+ font_MATH_par(f, SuperscriptBaselineDropMax), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
+ font_MATH_par(f, SubscriptShiftDown), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
+ font_MATH_par(f, SubscriptShiftDown), lvl);
+
+ if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) {
+ DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+ font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+ font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl);
+ } else {
+ DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+ font_MATH_par(f, SubscriptShiftDown), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+ font_MATH_par(f, SubscriptShiftDown), lvl);
+ }
+
+ DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
+ font_MATH_par(f, SubscriptTopMax), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
+ font_MATH_par(f, SubscriptTopMax), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
+ font_MATH_par(f, SuperscriptBottomMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
+ font_MATH_par(f, SuperscriptBottomMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
+ font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
+ font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
+ font_MATH_par(f, SubSuperscriptGapMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
+ font_MATH_par(f, SubSuperscriptGapMin), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
+ font_MATH_par(f, UpperLimitGapMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
+ font_MATH_par(f, UpperLimitGapMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
+ font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
+ font_MATH_par(f, UpperLimitBaselineRiseMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
+ 0, lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
+ 0, lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
+ font_MATH_par(f, LowerLimitGapMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
+ font_MATH_par(f, LowerLimitGapMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
+ font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
+ font_MATH_par(f, LowerLimitBaselineDropMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
+ 0, lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
+ 0, lvl);
+ DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
+ font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
+ DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
+ font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
+ DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
+ font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
+ DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
+ font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
+
+ DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
+ font_MATH_par(f, FractionRuleThickness), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
+ font_MATH_par(f, FractionRuleThickness), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
+ font_MATH_par(f, FractionNumeratorGapMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
+ font_MATH_par(f, FractionNumeratorDisplayStyleGapMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
+ font_MATH_par(f, FractionNumeratorShiftUp), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
+ font_MATH_par(f, FractionNumeratorDisplayStyleShiftUp), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
+ font_MATH_par(f, FractionDenominatorGapMin), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
+ font_MATH_par(f,FractionDenominatorDisplayStyleGapMin), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
+ font_MATH_par(f, FractionDenominatorShiftDown), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
+ font_MATH_par(f, FractionDenominatorDisplayStyleShiftDown), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
+ font_MATH_par(f, FractionDelimiterSize), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
+ font_MATH_par(f, FractionDelimiterDisplayStyleSize), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
+ font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
+ font_MATH_par(f, SkewedFractionHorizontalGap), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
+ font_MATH_par(f, SkewedFractionVerticalGap), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
+ font_MATH_par(f, SkewedFractionVerticalGap), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id,
+ font_MATH_par(f, SpaceAfterScript), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id,
+ font_MATH_par(f, SpaceAfterScript), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
+ font_MATH_par(f, MinConnectorOverlap), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
+ font_MATH_par(f, MinConnectorOverlap), lvl);
+
+ } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
+
+ /*tex Fix old-style |sy| parameters. */
+
+ DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
+ math_quad(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_quad, size_id,
+ math_quad(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_axis, size_id,
+ axis_height(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_axis, size_id,
+ axis_height(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id,
+ num3(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id,
+ num1(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id,
+ denom2(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id,
+ denom1(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id,
+ num2(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id,
+ num1(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id,
+ denom2(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id,
+ denom1(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id,
+ delim2(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id,
+ delim1(size_id), lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
+ 0, lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id,
+ 0, lvl);
+ DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
+ 0, lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id,
+ 0, lvl);
+
+ if (size_id == text_size) {
+ def_math_param(math_param_sup_shift_up, display_style,
+ sup1(size_id), lvl);
+ def_math_param(math_param_sup_shift_up, cramped_display_style,
+ sup3(size_id), lvl);
+ def_math_param(math_param_sup_shift_up, text_style,
+ sup2(size_id), lvl);
+ def_math_param(math_param_sup_shift_up, cramped_text_style,
+ sup3(size_id), lvl);
+ } else if (size_id == script_size) {
+ def_math_param(math_param_sub_shift_drop, display_style,
+ sub_drop(size_id), lvl);
+ def_math_param(math_param_sub_shift_drop, cramped_display_style,
+ sub_drop(size_id), lvl);
+ def_math_param(math_param_sub_shift_drop, text_style,
+ sub_drop(size_id), lvl);
+ def_math_param(math_param_sub_shift_drop, cramped_text_style,
+ sub_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_drop, display_style,
+ sup_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_drop, cramped_display_style,
+ sup_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_drop, text_style,
+ sup_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_drop, cramped_text_style,
+ sup_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_up, script_style,
+ sup2(size_id), lvl);
+ def_math_param(math_param_sup_shift_up, cramped_script_style,
+ sup3(size_id), lvl);
+ } else if (size_id == script_script_size) {
+ def_math_param(math_param_sub_shift_drop, script_style,
+ sub_drop(size_id), lvl);
+ def_math_param(math_param_sub_shift_drop, cramped_script_style,
+ sub_drop(size_id), lvl);
+ def_math_param(math_param_sub_shift_drop, script_script_style,
+ sub_drop(size_id), lvl);
+ def_math_param(math_param_sub_shift_drop,
+ cramped_script_script_style, sub_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_drop, script_style,
+ sup_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_drop, cramped_script_style,
+ sup_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_drop, script_script_style,
+ sup_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_drop,
+ cramped_script_script_style, sup_drop(size_id), lvl);
+ def_math_param(math_param_sup_shift_up, script_script_style,
+ sup2(size_id), lvl);
+ def_math_param(math_param_sup_shift_up, cramped_script_script_style,
+ sup3(size_id), lvl);
+ }
+
+ DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id,
+ sub1(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id,
+ sub1(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+ sub2(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id,
+ sub2(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id,
+ (abs(math_x_height(size_id) * 4) / 5), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id,
+ (abs(math_x_height(size_id) * 4) / 5), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id,
+ (abs(math_x_height(size_id)) / 4), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id,
+ (abs(math_x_height(size_id)) / 4), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
+ (abs(math_x_height(size_id) * 4) / 5), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
+ (abs(math_x_height(size_id) * 4) / 5), lvl);
+
+ /*tex
+
+ The display-size |radical_vgap| is done twice because it needs values
+ from both the sy and the ex font.
+
+ */
+
+ DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
+ (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id,
+ 60, lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id,
+ 60, lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id,
+ xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id,
+ xn_over_d(get_math_quad_size(size_id), 5, 18), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id,
+ (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id,
+ (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl);
+
+ } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
+
+ /*tex Fix old-style |ex| parameters. */
+
+ DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id,
+ 3 * default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id,
+ 3 * default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id,
+ 3 * default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id,
+ 3 * default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id,
+ (default_rule_thickness(size_id) + (abs(default_rule_thickness(size_id)) / 4)), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id,
+ 3 * default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id,
+ 7 * default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id,
+ 3 * default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
+ default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id,
+ 3 * default_rule_thickness(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id,
+ big_op_spacing1(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id,
+ big_op_spacing1(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id,
+ big_op_spacing3(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id,
+ big_op_spacing3(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id,
+ big_op_spacing5(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id,
+ big_op_spacing5(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id,
+ big_op_spacing2(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id,
+ big_op_spacing2(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id,
+ big_op_spacing4(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id,
+ big_op_spacing4(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id,
+ big_op_spacing5(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id,
+ big_op_spacing5(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
+ font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
+ DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id,
+ font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */
+ DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
+ font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
+ DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id,
+ font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */
+ DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id,
+ 4 * default_rule_thickness(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
+ 4 * default_rule_thickness(size_id), lvl);
+
+ /*tex
+
+ All of the |space_after_script|s are done in
+ |finalize_math_parameters| because the \.{\\scriptspace} may have
+ been altered by the user.
+
+ */
+
+ DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id,
+ 0, lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id,
+ 0, lvl);
+
+ DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
+ big_op_spacing2(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id,
+ big_op_spacing2(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
+ big_op_spacing4(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id,
+ big_op_spacing4(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
+ big_op_spacing1(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id,
+ big_op_spacing1(size_id), lvl);
+ DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
+ big_op_spacing3(size_id), lvl);
+ DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
+ big_op_spacing3(size_id), lvl);
+
+ /*tex
+
+ The display-size |radical_vgap| is done twice because it needs values
+ from both the sy and the ex font.
+
+ */
+
+ DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id,
+ (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl);
+
+ }
+}
+
+/*tex
+
+ This needs to be called just at the start of |mlist_to_hlist|, for backward
+ compatibility with \.{\\scriptspace}.
+
+*/
+
+static void finalize_math_parameters(void)
+{
+ int saved_trace = tracing_assigns_par;
+ tracing_assigns_par = 0;
+ if (get_math_param(math_param_space_after_script, display_style) == undefined_math_parameter) {
+ def_math_param(math_param_space_after_script, display_style,
+ script_space_par, level_one);
+ def_math_param(math_param_space_after_script, text_style,
+ script_space_par, level_one);
+ def_math_param(math_param_space_after_script, script_style,
+ script_space_par, level_one);
+ def_math_param(math_param_space_after_script, script_script_style,
+ script_space_par, level_one);
+ def_math_param(math_param_space_after_script, cramped_display_style,
+ script_space_par, level_one);
+ def_math_param(math_param_space_after_script, cramped_text_style,
+ script_space_par, level_one);
+ def_math_param(math_param_space_after_script, cramped_script_style,
+ script_space_par, level_one);
+ def_math_param(math_param_space_after_script, cramped_script_script_style,
+ script_space_par, level_one);
+ }
+ tracing_assigns_par = saved_trace;
+}
+
+/*tex
+
+ 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 256, 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.)
+
+*/
+
+static const char *math_size_string(int s)
+{
+ if (s == text_size)
+ return "textfont";
+ else if (s == script_size)
+ return "scriptfont";
+ else
+ return "scriptscriptfont";
+}
+
+/*tex
+
+ When the style changes, the following piece of program computes associated
+ information:
+
+*/
+
+#define setup_cur_size(a) do { \
+ if (a==script_style || a==cramped_script_style) \
+ cur_size = script_size; \
+ else if (a==script_script_style || a==cramped_script_script_style) \
+ cur_size = script_script_size; \
+ else \
+ cur_size = text_size; \
+} while (0)
+
+
+/*tex
+
+ A simple routine that creates a flat copy of a nucleus.
+
+*/
+
+static pointer math_clone(pointer q)
+{
+ pointer x;
+ if (q == null)
+ return null;
+ x = new_node(type(q), 0);
+ reset_attributes(x, node_attr(q));
+ if (type(q) == math_char_node) {
+ math_fam(x) = math_fam(q);
+ math_character(x) = math_character(q);
+ } else {
+ math_list(x) = math_list(q);
+ }
+ return x;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfword cur_size, halfword cur_fam)
+{
+ pointer p;
+ if (math_rules_mode_par) {
+ p = new_rule(some_rule);
+ rule_math_size(p) = cur_size;
+ rule_math_font(p) = fam_fnt(cur_fam, cur_size);
+ } else {
+ p = new_rule(normal_rule);
+ }
+ rule_dir(p) = math_direction_par;
+ height(p) = t;
+ depth(p) = 0;
+ reset_attributes(p, att);
+ return p;
+}
+
+/*tex
+
+ 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 |ht|.
+
+*/
+
+static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, halfword index, halfword cur_size, halfword cur_fam)
+{
+ pointer p, q;
+ p = new_kern(k);
+ reset_attributes(p, att);
+ couple_nodes(p,b);
+ q = do_fraction_rule(t, att, index, cur_size, cur_fam);
+ couple_nodes(q,p);
+ p = new_kern(ht);
+ reset_attributes(p, att);
+ couple_nodes(p,q);
+ q = vpackage(p, 0, additional, max_dimen, math_direction_par);
+ reset_attributes(q, att);
+ return q;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static pointer char_box(internal_font_number f, int c, pointer bb)
+{
+ /*tex The new box and its character node. */
+ pointer b, p;
+ b = new_null_box();
+ if (do_new_math(f))
+ width(b) = char_width(f, c);
+ else
+ width(b) = char_width(f, c) + char_italic(f, c);
+ height(b) = char_height(f, c);
+ depth(b) = char_depth(f, c);
+ subtype(b) = math_char_list ;
+ reset_attributes(b, bb);
+ p = new_glyph(f, c);
+ protect_glyph(p);
+ reset_attributes(p, bb);
+ list_ptr(b) = p;
+ return b;
+}
+
+/*tex
+
+ Another handy subroutine computes the height plus depth of a given character:
+
+*/
+
+static scaled height_plus_depth(internal_font_number f, int c)
+{
+ return (char_height(f, c) + char_depth(f, c));
+}
+
+/*tex
+
+ 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|:
+
+*/
+
+static scaled stack_into_box(pointer b, internal_font_number f, int c)
+{
+ /*tex New node placed into |b|: */
+ pointer p, q;
+ /*tex Italic gets added to width. */
+ p = char_box(f, c, node_attr(b));
+ if (type(b) == vlist_node) {
+ try_couple_nodes(p,list_ptr(b));
+ list_ptr(b) = p;
+ height(b) = height(p);
+ if (width(b) < width(p))
+ width(b) = width(p);
+ return height_plus_depth(f, c);
+ } else {
+ q = list_ptr(b);
+ if (q == null) {
+ list_ptr(b) = p;
+ } else {
+ while (vlink(q) != null)
+ q = vlink(q);
+ couple_nodes(q,p);
+ }
+ if (height(b) < height(p))
+ height(b) = height(p);
+ if (depth(b) < depth(p))
+ depth(b) = depth(p);
+ return char_width(f, c);
+ }
+}
+
+static void stack_glue_into_box(pointer b, scaled min, scaled max) {
+ halfword p = new_glue(zero_glue);
+ width(p) = min;
+ stretch(p) = max - min;
+ if (node_attr(b) != null) {
+ reset_attributes(p, node_attr(b));
+ }
+ if (type(b) == vlist_node) {
+ try_couple_nodes(p,list_ptr(b));
+ list_ptr(b) = p;
+ } else {
+ halfword q = list_ptr(b);
+ if (q == null) {
+ list_ptr(b) = p;
+ } else {
+ while (vlink(q) != null) {
+ q = vlink(q);
+ }
+ couple_nodes(q,p);
+ }
+ }
+}
+
+/*tex
+
+ \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. The explicit parameter |cur_mlist|
+ points to the first node or noad in the given mlist (and it might be |null|);
+ the parameter |penalties| is |true| if penalty nodes for potential line
+ breaks are to be inserted into the resulting hlist, the parameter |cur_style|
+ is a style code. After |mlist_to_hlist| has acted, |vlink(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.
+
+*/
+
+/*tex Size code corresponding to |cur_style|: */
+
+int cur_size = 0;
+
+static pointer get_delim_box(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att)
+{
+ int callback_id = callback_defined(make_extensible_callback);
+ if (callback_id > 0) {
+ /*tex
+ This call is not optimized as it hardly makes sense to use it ... special
+ and a it of feature creep too.
+ */
+ halfword b = null;
+ run_callback(callback_id, "ddddbN->N",fnt,chr,v,min_overlap,horizontal,att,&b);
+ if (b == null) {
+ /*tex
+ We see this as a signal to do it the \TEX\ way.
+ */
+ } else if (type(b) == hlist_node || type(b) == vlist_node) {
+ return b;
+ } else {
+ formatted_error("fonts","invalid extensible character %i created for font %i, [h|v]list expected",chr,fnt);
+ }
+ }
+ return make_extensible(fnt, chr, v, min_overlap, horizontal, att);
+}
+
+pointer make_extensible(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att)
+{
+ /*tex new box */
+ pointer b;
+ /*tex natural (maximum) size of the stack */
+ scaled b_max;
+ /*tex amount of possible shrink in the stack */
+ scaled s_max;
+ extinfo *cur;
+ extinfo *ext;
+ scaled prev_overlap;
+ /*tex a temporary counter number of extensible pieces */
+ int i;
+ /*tex number of times to repeat each repeatable item in |ext| */
+ int with_extenders;
+ int num_extenders, num_normal;
+ scaled a, c, d;
+ b = new_null_box();
+ with_extenders = -1;
+ num_extenders = 0;
+ num_normal = 0;
+ if (min_overlap < 0) {
+ min_overlap = 0;
+ }
+ if (horizontal) {
+ type(b) = (quarterword) hlist_node;
+ ext = get_charinfo_hor_variants(char_info(fnt,chr));
+ } else {
+ type(b) = (quarterword) vlist_node;
+ ext = get_charinfo_vert_variants(char_info(fnt,chr));
+ }
+ if (att != null) {
+ reset_attributes(b,att);
+ }
+ cur = ext;
+ while (cur != NULL) {
+ if (!char_exists(fnt, cur->glyph)) {
+ const char *hlp[] = {
+ "Each glyph part in an extensible item should exist in the font.",
+ "I will give up trying to find a suitable size for now. Fix your font!",
+ NULL
+ };
+ tex_error("Variant part doesn't exist.", hlp);
+ width(b) = null_delimiter_space_par;
+ return b;
+ }
+ if (cur->extender > 0)
+ num_extenders++;
+ else
+ num_normal++;
+ /*tex No negative overlaps or advances are allowed. */
+ if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) {
+ const char *hlp[] = {
+ "All measurements in extensible items should be positive.",
+ "To get around this problem, I have changed the font metrics.",
+ "Fix your font!",
+ NULL
+ };
+ tex_error("Extensible recipe has negative fields.", hlp);
+ if (cur->start_overlap < 0)
+ cur->start_overlap = 0;
+ if (cur->end_overlap < 0)
+ cur->end_overlap = 0;
+ if (cur->advance < 0)
+ cur->advance = 0;
+ }
+ cur = cur->next;
+ }
+ if (num_normal == 0) {
+ const char *hlp[] = {
+ "Each extensible recipe should have at least one non-repeatable part.",
+ "To get around this problem, I have changed the first part to be",
+ "non-repeatable. Fix your font!",
+ NULL
+ };
+ tex_error("Extensible recipe has no fixed parts.", hlp);
+ ext->extender = 0;
+ num_normal = 1;
+ num_extenders--;
+ }
+ /*tex
+
+ |ext| holds a linked list of numerous items that may or may not be
+ repeatable. For the total height, we have to figure out how many items
+ are needed to create a stack of at least |v|.
+
+ The next |while| loop does that. It has two goals: it finds out the
+ natural height |b_max| of the all the parts needed to reach at least |v|,
+ and it sets |with_extenders| to the number of times each of the
+ repeatable items in |ext| has to be repeated to reach that height.
+
+ */
+ cur = ext;
+ b_max = 0;
+ while (b_max < v && num_extenders > 0) {
+ b_max = 0;
+ prev_overlap = 0;
+ with_extenders++;
+ for (cur = ext; cur != NULL; cur = cur->next) {
+ if (cur->extender == 0) {
+ c = cur->start_overlap;
+ if (min_overlap < c)
+ c = min_overlap;
+ if (prev_overlap < c)
+ c = prev_overlap;
+ a = cur->advance;
+ if (a == 0) {
+ /*tex for tfm fonts */
+ if (horizontal) {
+ a = char_width(fnt, cur->glyph);
+ } else {
+ a = height_plus_depth(fnt, cur->glyph);
+ }
+ if (a < 0) {
+ formatted_error("fonts","bad extensible character %i in font %i",chr,fnt);
+ }
+ }
+ b_max += a - c;
+ prev_overlap = cur->end_overlap;
+ } else {
+ i = with_extenders;
+ while (i > 0) {
+ c = cur->start_overlap;
+ if (min_overlap < c)
+ c = min_overlap;
+ if (prev_overlap < c)
+ c = prev_overlap;
+ a = cur->advance;
+ if (a == 0) {
+ /*tex for tfm fonts */
+ if (horizontal) {
+ a = char_width(fnt, cur->glyph);
+ } else {
+ a = height_plus_depth(fnt, cur->glyph);
+ }
+ if (a < 0) {
+ formatted_error("fonts","bad extensible character %i in font %i",chr,fnt);
+ }
+ }
+ b_max += a - c;
+ prev_overlap = cur->end_overlap;
+ i--;
+ }
+ }
+ }
+ }
+ /*tex
+
+ Assemble box using |with_extenders| copies of each extender, with
+ appropriate glue wherever an overlap occurs.
+
+ */
+ prev_overlap = 0;
+ b_max = 0;
+ s_max = 0;
+ for (cur = ext; cur != NULL; cur = cur->next) {
+ if (cur->extender == 0) {
+ c = cur->start_overlap;
+ if (prev_overlap < c)
+ c = prev_overlap;
+ d = c;
+ if (min_overlap < c)
+ c = min_overlap;
+ if (d > 0) {
+ stack_glue_into_box(b, -d, -c);
+ s_max += (-c) - (-d);
+ b_max -= d;
+ }
+ b_max += stack_into_box(b, fnt, cur->glyph);
+ prev_overlap = cur->end_overlap;
+ i--;
+ } else {
+ i = with_extenders;
+ while (i > 0) {
+ c = cur->start_overlap;
+ if (prev_overlap < c)
+ c = prev_overlap;
+ d = c;
+ if (min_overlap < c)
+ c = min_overlap;
+ if (d > 0) {
+ stack_glue_into_box(b, -d, -c);
+ s_max += (-c) - (-d);
+ b_max -= d;
+ }
+ b_max += stack_into_box(b, fnt, cur->glyph);
+ prev_overlap = cur->end_overlap;
+ i--;
+ }
+ }
+ }
+ /*tex Set glue so as to stretch the connections if needed. */
+ d = 0;
+ if (v > b_max && s_max > 0) {
+ d = v-b_max;
+ /*tex Don't stretch more than |s_max|. */
+ if (d > s_max)
+ d = s_max;
+ glue_order(b) = normal;
+ glue_sign(b) = stretching;
+ glue_set(b) = unfloat(d/(float) s_max);
+ b_max += d;
+ }
+ if (horizontal) {
+ width(b) = b_max;
+ subtype(b) = math_h_extensible_list;
+ } else {
+ height(b) = b_max;
+ subtype(b) = math_v_extensible_list;
+ }
+ return b;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static void endless_loop_error(internal_font_number g, int y)
+{
+ char s[256];
+ const char *hlp[] = {
+ "You managed to create a seemingly endless charlist chain in the current",
+ "font. I have counted until 10000 already and still have not escaped, so"
+ "I will jump out of the loop all by myself now. Fix your font!",
+ NULL
+ };
+ snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)", (int) y, font_name(g));
+ tex_error(s, hlp);
+}
+
+static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, int cur_style, boolean shift, boolean *stack, scaled *delta, int *same)
+{
+ /*tex the box that will be constructed */
+ pointer b;
+ /*tex best-so-far and tentative font codes */
+ internal_font_number f, g;
+ /*tex best-so-far and tentative character codes */
+ int c, i, x, y;
+ /*tex height-plus-depth of a tentative character */
+ scaled u;
+ /*tex largest height-plus-depth so far */
+ scaled w = 0;
+ /*tex runs through font family members */
+ int z;
+ /*tex are we trying the ``large'' variant? */
+ boolean large_attempt = false;
+ /*tex to save the current attribute list */
+ pointer att = null;
+ int emas = 0 ;
+ boolean do_parts = false;
+ boolean parts_done = false;
+ extinfo *ext;
+
+ if (d && ! small_fam(d) && ! large_fam(d) && ! small_char(d) && ! large_char(d)) {
+ halfword b = new_null_box();
+ subtype(b) = math_v_delimiter_list;
+ if (! flat)
+ width(b) = null_delimiter_space_par;
+ node_attr(b) = node_attr(d);
+ node_attr(d) = null;
+ flush_node(d);
+ return b;
+ }
+
+ f = null_font;
+ c = 0;
+ if (d == null) {
+ goto FOUND;
+ }
+ z = small_fam(d);
+ x = small_char(d);
+ i = 0;
+ if (same != NULL) {
+ emas = *same ;
+ same = 0;
+ }
+ while (true) {
+ /*tex
+
+ 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.
+
+ */
+ if ((z != 0) || (x != 0)) {
+ g = fam_fnt(z, s);
+ if (g != null_font) {
+ y = x;
+ CONTINUE:
+ i++;
+ if (char_exists(g, y)) {
+ if (flat)
+ u = char_width(g, y);
+ else
+ u = height_plus_depth(g, y);
+ if (u > w) {
+ f = g;
+ c = y;
+ w = u;
+ if (u >= v)
+ goto FOUND;
+ }
+ if (char_tag(g, y) == ext_tag) {
+ f = g;
+ c = y;
+ do_parts = true;
+ goto FOUND;
+ }
+ if (i > 10000) {
+ endless_loop_error(g, y);
+ goto FOUND;
+ }
+ if (char_tag(g, y) == list_tag) {
+ y = char_remainder(g, y);
+ goto CONTINUE;
+ }
+ }
+ }
+ }
+ if (large_attempt) {
+ /*tex There were none large enough. */
+ goto FOUND;
+ }
+ large_attempt = true;
+ z = large_fam(d);
+ x = large_char(d);
+ }
+ FOUND:
+ if (d != null) {
+ att = node_attr(d);
+ node_attr(d) = null;
+ flush_node(d);
+ }
+ if (f != null_font) {
+ /*tex
+
+ When the following code is executed, |do_parts| will be true if a
+ built-up symbol is supposed to be returned.
+
+ */
+ ext = NULL;
+ if ((do_parts) && ((!flat && (ext = get_charinfo_vert_variants(char_info(f,c))) != NULL)
+ || ( flat && (ext = get_charinfo_hor_variants (char_info(f,c))) != NULL))) {
+ parts_done = true;
+ if (flat) {
+ b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 1, att);
+ } else {
+ b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 0, att);
+ }
+ if (delta != NULL) {
+ if (do_new_math(f)) {
+ *delta = char_vert_italic(f,x);
+ } else {
+ *delta = char_italic(f,x);
+ }
+ }
+ if (stack != NULL)
+ *stack = true ;
+ } else {
+ parts_done = false;
+ if (same != NULL && x == c) {
+ *same = emas;
+ }
+ /*tex
+ Here italic is added to width in traditional fonts which makes the delimiter get
+ the real width. An \OPENTYPE\ font already has the right width.
+ */
+ b = char_box(f, c, att);
+ /*tex
+ There is one case where |delta| (ic) gets subtracted but only for a traditional
+ font. In that case the traditional width (which is fake width + italic) becomes
+ less and the delta is added. See (**). (On the mailing list font |ntxexx| was
+ mentioned as test case by MK.)
+ */
+ if (delta != NULL) {
+ *delta = char_italic(f, c);
+ }
+ if (stack != NULL)
+ *stack = false ;
+ }
+ } else {
+ b = new_null_box();
+ reset_attributes(b, att);
+ if (flat) {
+ width(b) = 0;
+ subtype(b) = math_h_delimiter_list;
+ } else {
+ /*tex Use this width if no delimiter was found. */
+ width(b) = null_delimiter_space_par;
+ subtype(b) = math_v_delimiter_list;
+ }
+ if (delta != NULL) {
+ *delta = 0;
+ }
+ if (stack != NULL)
+ *stack = false ;
+ }
+ if (!flat) {
+ /*tex when emas ~= 0 then we have a non scaled character */
+ if (emas != 0 && delimitermodesamenos) {
+ /*tex same character and no shift when same forced */
+ goto DONE;
+ }
+ if (! parts_done && delimitermodecharnos) {
+ /*tex same character and no shift when same forced */
+ goto DONE;
+ }
+ if (delimitermodenoshift) {
+ /*tex no shift forced */
+ goto DONE;
+ }
+ /*tex vertical variant */
+ shift_amount(b) = half(height(b) - depth(b));
+ if (shift) {
+ shift_amount(b) -= math_axis_size(s);
+ }
+ }
+ DONE:
+ delete_attribute_ref(att);
+ return b;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static pointer rebox(pointer b, scaled w)
+{
+ /*tex temporary registers for list manipulation */
+ pointer p, q, r, att;
+ /*tex font in a one-character box */
+ internal_font_number f;
+ /*tex width of a character without italic correction */
+ scaled v;
+ if ((width(b) != w) && (list_ptr(b) != null)) {
+ if (type(b) == vlist_node) {
+ p = hpack(b, 0, additional, -1);
+ reset_attributes(p, node_attr(b));
+ b = p;
+ }
+ p = list_ptr(b);
+ att = node_attr(b);
+ add_node_attr_ref(att);
+ if ((is_char_node(p)) && (vlink(p) == null)) {
+ f = font(p);
+ v = char_width(f, character(p));
+ if (v != width(b)) {
+ q = new_kern(width(b) - v);
+ reset_attributes(q, att);
+ couple_nodes(p,q);
+ }
+ }
+ list_ptr(b) = null;
+ flush_node(b);
+ b = new_glue(ss_glue);
+ reset_attributes(b, att);
+ couple_nodes(b,p);
+ while (vlink(p) != null)
+ p = vlink(p);
+ q = new_glue(ss_glue);
+ reset_attributes(q, att);
+ couple_nodes(p,q);
+ r = hpack(b, w, exactly, -1);
+ reset_attributes(r, att);
+ delete_attribute_ref(att);
+ return r;
+ } else {
+ width(b) = w;
+ return b;
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+#define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen)
+
+static pointer math_glue(pointer g, scaled m)
+{
+ /*tex integer part of |m| */
+ int n = x_over_n(m, unity);
+ /*tex fraction part of |m| */
+ scaled f = tex_remainder;
+ /*tex the new glue specification */
+ pointer p;
+ if (f < 0) {
+ decr(n);
+ f = f + unity;
+ }
+ p = new_node(glue_node, 0);
+ /* convert \.{mu} to \.{pt} */
+ width(p) = mu_mult(width(g));
+ 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;
+}
+
+static void math_glue_to_glue(pointer p, scaled m)
+{
+ /*tex integer part of |m| */
+ int n = x_over_n(m, unity);
+ /*tex fraction part of |m| */
+ scaled f = tex_remainder;
+ if (f < 0) {
+ decr(n);
+ f = f + unity;
+ }
+ /* convert \.{mu} to \.{pt} */
+ width(p) = mu_mult(width(p));
+ if (stretch_order(p) == normal)
+ stretch(p) = mu_mult(stretch(p));
+ if (shrink_order(p) == normal)
+ shrink(p) = mu_mult(shrink(p));
+ subtype(p) = normal;
+}
+
+/*tex
+
+ The |math_kern| subroutine removes |mu_glue| from a kern node, given the
+ value of the math unit.
+
+*/
+static void math_kern(pointer p, scaled m)
+{
+ /*tex integer part of |m| */
+ int n;
+ /*tex fraction part of |m| */
+ scaled f;
+ if (subtype(p) == mu_glue) {
+ n = x_over_n(m, unity);
+ f = tex_remainder;
+ if (f < 0) {
+ decr(n);
+ f = f + unity;
+ }
+ width(p) = mu_mult(width(p));
+ /* this is weird, it's not a italic but explicit_kern */
+ subtype(p) = italic_kern;
+ }
+}
+
+void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
+{
+ int callback_id;
+ int a, sfix, i;
+ if (p == null) {
+ vlink(temp_head) = null;
+ return;
+ }
+ finalize_math_parameters();
+ callback_id = callback_defined(mlist_to_hlist_callback);
+ if (callback_id > 0) {
+ sfix = lua_gettop(Luas);
+ if (!get_callback(Luas, callback_id)) {
+ lua_settop(Luas, sfix);
+ return;
+ }
+ alink(p) = null ;
+ nodelist_to_lua(Luas, p);
+ lua_push_math_style_name(Luas, mstyle);
+ lua_pushboolean(Luas, penalties);
+ if ((i=lua_pcall(Luas, 3, 1, 0)) != 0) {
+ formatted_warning("mlist to hlist","error: %s",lua_tostring(Luas, -1));
+ lua_settop(Luas, sfix);
+ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
+ return;
+ }
+ a = nodelist_from_lua(Luas,-1);
+ /* alink(vlink(a)) = null; */
+ vlink(temp_head) = a;
+ lua_settop(Luas, sfix);
+ } else if (callback_id == 0) {
+ mlist_to_hlist(p, penalties, mstyle);
+ } else {
+ vlink(temp_head) = null;
+ }
+}
+
+/*tex
+
+ 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|.
+
+ The box returned by |clean_box| is ``clean'' in the sense that its
+ |shift_amount| is zero.
+
+*/
+
+static pointer clean_box(pointer p, int s, int cur_style, halfword st)
+{
+ /*tex beginning of a list to be boxed */
+ pointer q;
+ /*tex box to be returned */
+ pointer x;
+ /*tex temporary pointer */
+ pointer r;
+ /*tex beginning of mlist to be translated */
+ pointer mlist = null;
+ switch (type(p)) {
+ case math_char_node:
+ mlist = new_noad();
+ r = math_clone(p);
+ nucleus(mlist) = r;
+ break;
+ case sub_box_node:
+ q = math_list(p);
+ goto FOUND;
+ break;
+ case sub_mlist_node:
+ mlist = math_list(p);
+ break;
+ default:
+ q = new_null_box();
+ subtype(q) = math_list_list;
+ goto FOUND;
+ }
+ mlist_to_hlist(mlist, false, s);
+ /*tex recursive call */
+ q = vlink(temp_head);
+ setup_cur_size(cur_style);
+ FOUND:
+ if (is_char_node(q) || (q == null))
+ x = hpack(q, 0, additional, -1);
+ else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0))
+ /*tex It's already clean. */
+ x = q;
+ else
+ x = hpack(q, 0, additional, -1);
+ if (x != q && q != null)
+ reset_attributes(x, node_attr(q));
+ subtype(x) = st;
+ /*tex Here we save memory space in a common case. */
+ q = list_ptr(x);
+ if (is_char_node(q)) {
+ r = vlink(q);
+ if (r != null) {
+ if (vlink(r) == null) {
+ if (!is_char_node(r)) {
+ if (type(r) == kern_node) {
+ /*tex Unneeded italic correction. */
+ flush_node(r);
+ vlink(q) = null;
+ }
+ }
+ }
+ }
+ }
+ return x;
+}
+
+/*tex
+
+ It is convenient to have a procedure that converts a |math_char| field to an
+ ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c| to the font
+ code and character code of a given noad field. It also takes care of issuing
+ error messages for nonexistent characters; in such cases,
+ |char_exists(cur_f,cur_c)| will be |false| after |fetch| has acted, and the
+ field will also have been reset to |null|.
+
+ The outputs of |fetch| are placed in global variables.
+
+*/
+
+/*tex the |font| field of a |math_char| */
+
+internal_font_number cur_f;
+
+/*tex the |character| field of a |math_char| */
+
+int cur_c;
+
+/*tex Here we unpack the |math_char| field |a|. */
+
+static void fetch(pointer a)
+{
+ cur_c = math_character(a);
+ cur_f = fam_fnt(math_fam(a), cur_size);
+ if (cur_f == null_font) {
+ char *msg;
+ const char *hlp[] = {
+ "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.",
+ NULL
+ };
+ msg = xmalloc(256);
+ snprintf(msg, 255, "\\%s%d is undefined (character %d)",
+ math_size_string(cur_size), (int) math_fam(a), (int) cur_c);
+ tex_error(msg, hlp);
+ free(msg);
+ } else if (!(char_exists(cur_f, cur_c))) {
+ char_warning(cur_f, cur_c);
+ }
+}
+
+/*tex
+
+ 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 |fence_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|.
+
+ The second pass eliminates all noads and inserts the correct glue and
+ penalties between nodes.
+
+*/
+
+static void assign_new_hlist(pointer q, pointer r)
+{
+ switch (type(q)) {
+ case fraction_noad:
+ math_list(numerator(q)) = null;
+ flush_node(numerator(q));
+ numerator(q) = null;
+ math_list(denominator(q)) = null;
+ flush_node(denominator(q));
+ denominator(q) = null;
+ break;
+ case radical_noad:
+ case simple_noad:
+ case accent_noad:
+ if (nucleus(q) != null) {
+ math_list(nucleus(q)) = null;
+ flush_node(nucleus(q));
+ nucleus(q) = null;
+ }
+ break;
+ }
+ new_hlist(q) = r;
+}
+
+#define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
+
+/*tex
+
+ 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.
+
+*/
+
+static void make_over(pointer q, int cur_style, int cur_size, int cur_fam)
+{
+ /*tex
+
+ No rule adaption yet, maybe never as overbars should be proper
+ extensibles.
+
+ */
+ pointer p;
+ scaled f, t;
+ scaled used_thickness = overbar_rule(cur_style);
+ scaled used_fam = cur_fam;
+ if (math_rule_thickness_mode_par > 0) {
+ f = noad_fam(q);
+ if (f >= 0) {
+ t = fam_fnt(f,cur_size);
+ if (do_new_math(t)) {
+ t = font_MATH_par(t, OverbarRuleThickness);
+ if (t != undefined_math_parameter) {
+ used_thickness = t;
+ used_fam = f;
+ }
+ }
+ }
+ }
+ p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style, math_nucleus_list),
+ overbar_vgap(cur_style), used_thickness, overbar_kern(cur_style),
+ node_attr(nucleus(q)), math_over_rule, cur_size, used_fam);
+ subtype(p) = math_over_list;
+ math_list(nucleus(q)) = p;
+ type(nucleus(q)) = sub_box_node;
+}
+
+static void make_under(pointer q, int cur_style, int cur_size, int cur_fam)
+{
+ /*tex
+
+ No rule adaption yet, maybe never as underbars should be proper
+ extensibles.
+
+ */
+ /*tex temporary registers for box construction */
+ pointer p, x, y, r;
+ /*tex overall height plus depth */
+ scaled delta;
+ scaled f, t;
+ scaled used_thickness = underbar_rule(cur_style);
+ scaled used_fam = cur_fam;
+ x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list);
+ p = new_kern(underbar_vgap(cur_style));
+ reset_attributes(p, node_attr(q));
+ couple_nodes(x,p);
+ if (math_rule_thickness_mode_par > 0) {
+ f = noad_fam(q);
+ if (f >= 0) {
+ t = fam_fnt(f,cur_size);
+ if (do_new_math(t)) {
+ t = font_MATH_par(t, UnderbarRuleThickness);
+ if (t != undefined_math_parameter) {
+ used_thickness = t;
+ used_fam = f;
+ }
+ }
+ }
+ }
+ r = do_fraction_rule(used_thickness, node_attr(q), math_under_rule, cur_size, used_fam);
+ couple_nodes(p,r);
+ y = vpackage(x, 0, additional, max_dimen, math_direction_par);
+ reset_attributes(y, node_attr(q));
+ subtype(y) = math_under_list;
+ delta = height(y) + depth(y) + underbar_kern(cur_style);
+ height(y) = height(x);
+ depth(y) = delta - height(y);
+ math_list(nucleus(q)) = y;
+ type(nucleus(q)) = sub_box_node;
+}
+
+static void make_vcenter(pointer q)
+{
+ /*tex the box that should be centered vertically */
+ pointer v;
+ /*tex its height plus depth */
+ scaled delta;
+ v = math_list(nucleus(q));
+ if (type(v) != vlist_node)
+ confusion("vcenter");
+ delta = height(v) + depth(v);
+ height(v) = math_axis_size(cur_size) + half(delta);
+ depth(v) = delta - height(v);
+}
+
+/*tex
+
+ According to the rules in the \.{DVI} file specifications, we ensure
+ alignment 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~|psi|. The symbol will be placed so that the actual clearance is
+ |psi| plus half the excess.
+
+*/
+
+static void make_hextension(pointer q, int cur_style)
+{
+ pointer e, p;
+ halfword w;
+ boolean stack = false;
+ e = do_delimiter(q, left_delimiter(q), cur_size, radicalwidth(q), true, cur_style, true, &stack, NULL, NULL);
+ w = width(e);
+ if (!stack&& (radicalwidth(q) != 0) && (radicalwidth(q) != width(e))) {
+ if (radicalmiddle(q)) {
+ p = new_kern(half(radicalwidth(q)-w));
+ reset_attributes(p, node_attr(q));
+ couple_nodes(p,e);
+ e = p;
+ w = radicalwidth(q);
+ } else if (radicalexact(q)) {
+ w = radicalwidth(q);
+ }
+ }
+ e = hpack(e, 0, additional, -1);
+ width(e) = w ;
+ reset_attributes(e, node_attr(q));
+ math_list(nucleus(q)) = e;
+ left_delimiter(q) = null;
+}
+
+static void make_radical(pointer q, int cur_style)
+{
+ /*tex temporary registers for box construction */
+ pointer x, y, p, l1, l2;
+ /*tex dimensions involved in the calculation */
+ scaled delta, clr, theta, h, f;
+ scaled t, used_fam ;
+ x = clean_box(nucleus(q), cramped_style(cur_style), cur_style, math_nucleus_list);
+ clr = radical_vgap(cur_style);
+ theta = radical_rule_par(cur_style);
+ used_fam = small_fam(left_delimiter(q));
+ /*tex
+
+ We can take the rule width from the fam/style of the delimiter or use the
+ most recent math parameters value.
+
+ */
+ if (math_rule_thickness_mode_par > 0) {
+ f = small_fam(left_delimiter(q));
+ if (f >= 0) {
+ t = fam_fnt(f,cur_size);
+ if (do_new_math(t)) {
+ t = font_MATH_par(t, RadicalRuleThickness);
+ if (t != undefined_math_parameter) {
+ theta = t;
+ used_fam = f;
+ }
+ }
+ }
+ }
+ if (theta == undefined_math_parameter) {
+ /*tex a real radical */
+ theta = fraction_rule(cur_style);
+ y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL);
+ /*tex
+
+ If |y| is a composite then set |theta| to the height of its top
+ character, else set it to the height of |y|.
+
+ */
+ l1 = list_ptr(y);
+ if ((l1 != null) && (type(l1) == hlist_node)) {
+ /*tex possible composite */
+ l2 = list_ptr(l1);
+ if ((l2 != null) && (type(l2) == glyph_node)) {
+ /*tex top character */
+ theta = char_height(font(l2), character(l2));
+ } else {
+ theta = height(y);
+ }
+ } else {
+ theta = height(y);
+ }
+ } else {
+ /*tex
+
+ Not really a radical but we use its node, historical sharing (like in
+ mathml).
+
+ */
+ y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL);
+ }
+ /*tex
+
+ Weird hack, in overbar we use small_fam(left_delimiter(q)) so actually
+ small_fam(0).
+
+ */
+ left_delimiter(q) = null;
+ delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr);
+ if (delta > 0) {
+ /*tex increase the actual clearance */
+ clr = clr + half(delta);
+ }
+ shift_amount(y) = (height(y) - theta) - (height(x) + clr);
+ h = depth(y) + height(y);
+ p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y), math_radical_rule, cur_size, used_fam);
+ subtype(p) = math_radical_list;
+ couple_nodes(y,p);
+ if (degree(q) != null) {
+ scaled wr, br, ar;
+ pointer r = clean_box(degree(q), script_script_style, cur_style, math_degree_list);
+ reset_attributes(r, node_attr(degree(q)));
+ wr = width(r);
+ if (wr == 0) {
+ flush_node(r);
+ } else {
+ br = radical_degree_before(cur_style);
+ ar = radical_degree_after(cur_style);
+ if (-ar > (wr + br))
+ ar = -(wr + br);
+ x = new_kern(ar);
+ reset_attributes(x, node_attr(degree(q)));
+ couple_nodes(x,y);
+ shift_amount(r) =
+ -((xn_over_d(h, radical_degree_raise(cur_style), 100)) -
+ depth(y) - shift_amount(y));
+ couple_nodes(r,x);
+ x = new_kern(br);
+ reset_attributes(x, node_attr(degree(q)));
+ couple_nodes(x,r);
+ y = x;
+ }
+ /*tex for \.{\\Uroot ..{<list>}{}} : */
+ math_list(degree(q)) = null;
+ flush_node(degree(q));
+ }
+ p = hpack(y, 0, additional, -1);
+ reset_attributes(p, node_attr(q));
+ math_list(nucleus(q)) = p;
+ type(nucleus(q)) = sub_box_node;
+}
+
+/*tex Construct a vlist box: */
+
+static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down, halfword st)
+{
+ pointer p;
+ pointer v = new_null_box();
+ type(v) = vlist_node;
+ subtype(v) = st;
+ height(v) = shift_up + height(x);
+ depth(v) = depth(y) + shift_down;
+ reset_attributes(v, node_attr(q));
+ p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
+ reset_attributes(p, node_attr(q));
+ couple_nodes(p,y);
+ couple_nodes(x,p);
+ list_ptr(v) = x;
+ return v;
+}
+
+/*tex When |exact| use radicalwidth (|y| is delimiter). */
+
+#define fixup_widths(q,x,y) do { \
+ if (width(y) >= width(x)) { \
+ if (radicalwidth(q) != 0) { \
+ shift_amount(x) += half(width(y)-width(x)) ; \
+ } \
+ width(x) = width(y); \
+ } else { \
+ if (radicalwidth(q) != 0) { \
+ shift_amount(y) += half(width(x)-width(y)) ; \
+ } \
+ width(y) = width(x); \
+ } \
+} while (0)
+
+
+#define check_radical(q,stack,r,t) do { \
+ if (!stack && (width(r) >= width(t)) && (radicalwidth(q) != 0) && (radicalwidth(q) != width(r))) { \
+ if (radicalleft(q)) { \
+ halfword p = new_kern(radicalwidth(q)-width(r)); \
+ reset_attributes(p, node_attr(q)); \
+ couple_nodes(p,r); \
+ r = hpack(p, 0, additional, -1); \
+ width(r) = radicalwidth(q); \
+ reset_attributes(r, node_attr(q)); \
+ } else if (radicalmiddle(q)) { \
+ halfword p = new_kern(half(radicalwidth(q)-width(r))); \
+ reset_attributes(p, node_attr(q)); \
+ couple_nodes(p,r); \
+ r = hpack(p, 0, additional, -1); \
+ width(r) = radicalwidth(q); \
+ reset_attributes(r, node_attr(q)); \
+ } else if (radicalright(q)) { \
+ /*tex also kind of exact compared to vertical */ \
+ r = hpack(r, 0, additional, -1); \
+ width(r) = radicalwidth(q); \
+ reset_attributes(r, node_attr(q)); \
+ } \
+ } \
+} while (0)
+
+#define check_widths(q,p) do { \
+ if (radicalwidth(q) != 0) { \
+ wd = radicalwidth(q); \
+ } else { \
+ wd = width(p); \
+ } \
+} while (0)
+
+/*tex
+
+ This has the |nucleus| box |x| as a limit above an extensible delimiter |y|.
+
+*/
+
+static void make_over_delimiter(pointer q, int cur_style)
+{
+ pointer x, y, v;
+ scaled shift_up, shift_down, clr, delta, wd;
+ boolean stack;
+ x = clean_box(nucleus(q), sub_style(cur_style), cur_style, math_nucleus_list);
+ check_widths(q,x);
+ y = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL, NULL);
+ left_delimiter(q) = null;
+ check_radical(q,stack,y,x);
+ fixup_widths(q, x, y);
+ shift_up = over_delimiter_bgap(cur_style);
+ shift_down = 0;
+ clr = over_delimiter_vgap(cur_style);
+ delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
+ if (delta > 0) {
+ shift_up = shift_up + delta;
+ }
+ v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down, math_over_delimiter_list);
+ /*tex This also equals |width(y)|: */
+ width(v) = width(x);
+ math_list(nucleus(q)) = v;
+ type(nucleus(q)) = sub_box_node;
+}
+
+/*tex
+
+ This has the extensible delimiter |x| as a limit below |nucleus| box |y|.
+
+*/
+
+static void make_under_delimiter(pointer q, int cur_style)
+{
+ pointer x, y, v;
+ scaled shift_up, shift_down, clr, delta, wd;
+ boolean stack;
+ y = clean_box(nucleus(q), sup_style(cur_style), cur_style, math_nucleus_list);
+ check_widths(q,y);
+ x = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL, NULL);
+ left_delimiter(q) = null;
+ check_radical(q,stack,x,y);
+ fixup_widths(q, x, y);
+ shift_up = 0;
+ shift_down = under_delimiter_bgap(cur_style);
+ clr = under_delimiter_vgap(cur_style);
+ delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down));
+ if (delta > 0) {
+ shift_down = shift_down + delta;
+ }
+ v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down, math_under_delimiter_list);
+ /*tex This also equals |width(y)|: */
+ width(v) = width(y);
+ math_list(nucleus(q)) = v;
+ type(nucleus(q)) = sub_box_node;
+}
+
+/*tex
+
+ This has the extensible delimiter |x| as a limit above |nucleus| box |y|.
+
+*/
+
+static void make_delimiter_over(pointer q, int cur_style)
+{
+ pointer x, y, v;
+ scaled shift_up, shift_down, clr, actual, wd;
+ boolean stack;
+ y = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list);
+ check_widths(q,y);
+ x = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL, NULL);
+ left_delimiter(q) = null;
+ check_radical(q,stack,x,y);
+ fixup_widths(q, x, y);
+ shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x);
+ shift_down = 0;
+ clr = over_delimiter_vgap(cur_style);
+ actual = shift_up - height(y);
+ if (actual < clr) {
+ shift_up = shift_up + (clr-actual);
+ }
+ v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down, math_over_delimiter_list);
+ /*tex This also equals |width(y)|: */
+ width(v) = width(x);
+ math_list(nucleus(q)) = v;
+ type(nucleus(q)) = sub_box_node;
+}
+
+/*tex
+
+ This has the extensible delimiter |y| as a limit below a |nucleus| box |x|.
+
+*/
+
+static void make_delimiter_under(pointer q, int cur_style)
+{
+ pointer x, y, v;
+ scaled shift_up, shift_down, clr, actual, wd;
+ boolean stack;
+ x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list);
+ check_widths(q,x);
+ y = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL, NULL);
+ left_delimiter(q) = null;
+ check_radical(q,stack,y,x);
+ fixup_widths(q, x, y);
+ shift_up = 0;
+ shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y);
+ clr = under_delimiter_vgap(cur_style);
+ actual = shift_down - depth(x);
+ if (actual<clr) {
+ shift_down += (clr-actual);
+ }
+ v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down, math_under_delimiter_list);
+ /*tex This also equals |width(y)|: */
+ width(v) = width(y);
+ math_list(nucleus(q)) = v;
+ type(nucleus(q)) = sub_box_node;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+#define TOP_CODE 1
+#define BOT_CODE 2
+#define OVERLAY_CODE 4
+#define STRETCH_ACCENT_CODE 8
+
+static boolean compute_accent_skew(pointer q, int flags, scaled *s)
+{
+ /*tex temporary register for box construction */
+ pointer p;
+ /*tex will be true if a top-accent is placed in |s| */
+ boolean s_is_absolute = false;
+ if (type(nucleus(q)) == math_char_node) {
+ fetch(nucleus(q));
+ if (do_new_math(cur_f)) {
+ /*tex
+ There is no bot_accent so let's assume similarity
+
+ \starttyping
+ if (flags & (TOP_CODE | OVERLAY_CODE)) {
+ *s = char_top_accent(cur_f, cur_c);
+ if (*s != INT_MIN) {
+ s_is_absolute = true;
+ }
+ } else {
+ *s = char_bot_accent(cur_f, cur_c);
+ if (*s != INT_MIN) {
+ s_is_absolute = true;
+ }
+ }
+ \stoptyping
+ */
+ *s = char_top_accent(cur_f, cur_c);
+ if (*s != INT_MIN) {
+ s_is_absolute = true;
+ }
+ } else {
+ if (flags & TOP_CODE) {
+ *s = get_kern(cur_f, cur_c, skew_char(cur_f));
+ } else {
+ *s = 0;
+ }
+ }
+ } else if (type(nucleus(q)) == sub_mlist_node) {
+ /*tex
+ If |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we:
+
+ \startitemize
+ \startitem
+ use the positioning of the nucleus of that noad, recursing until
+ \stopitem
+ \startitem
+ the inner most |accent_noad|. This way multiple stacked accents
+ are
+ \stopitem
+ \startitem
+ aligned to the inner most one.
+ \stopitem
+ \stoptitemize
+
+ The vlink test was added in version 1.06, so that we only consider a
+ lone noad:
+
+ $
+ \Umathaccent bottom 0 0 "023DF { \Umathaccent fixed 0 0 "00302 { m } r } \quad
+ \Umathaccent bottom 0 0 "023DF { l \Umathaccent fixed 0 0 "00302 { m } r } \quad
+ \Umathaccent bottom 0 0 "023DF { l \Umathaccent fixed 0 0 "00302 { m } } \quad
+ \Umathaccent bottom 0 0 "023DF { \Umathaccent fixed 0 0 "00302 { m } } \quad
+ \Umathaccent bottom 0 0 "023DF { l r }
+ $
+
+ */
+ p = math_list(nucleus(q));
+ if (type(p) == accent_noad && vlink(p) == null) {
+ s_is_absolute = compute_accent_skew(p, flags, s);
+ }
+ } else {
+ }
+
+ return s_is_absolute;
+}
+
+static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style)
+{
+ /*tex temporary registers for box construction */
+ pointer p, r, x, y;
+ /*tex amount to skew the accent to the right */
+ scaled s;
+ /*tex height of character being accented */
+ scaled h;
+ /*tex space to remove between accent and accentee */
+ scaled delta;
+ /*tex width of the accentee, not including sub/superscripts */
+ scaled w;
+ /*tex will be true if a top-accent is placed in |s| */
+ boolean s_is_absolute;
+ scaled fraction ;
+ scaled ic = 0;
+ scaled target ;
+ extinfo *ext;
+ pointer attr_p;
+ attr_p = (flags & TOP_CODE ? top_accent_chr(q) : flags & BOT_CODE ? bot_accent_chr(q) : overlay_accent_chr(q));
+ fraction = accentfraction(q);
+ c = cur_c;
+ f = cur_f;
+ s = 1;
+ if (fraction == 0) {
+ fraction = 1000;
+ }
+ /*tex Compute the amount of skew, or set |s| to an alignment point */
+ s_is_absolute = compute_accent_skew(q, flags, &s);
+ x = clean_box(nucleus(q), cramped_style(cur_style), cur_style, math_nucleus_list);
+ w = width(x);
+ h = height(x);
+ if (do_new_math(cur_f) && !s_is_absolute) {
+ s = half(w);
+ s_is_absolute = true;
+ }
+ /*tex Switch to a larger accent if available and appropriate */
+ y = null;
+ ext = NULL;
+ if (flags & OVERLAY_CODE) {
+ if (fraction > 0) {
+ target = xn_over_d(h,fraction,1000);
+ } else {
+ target = h;
+ }
+ } else {
+ if (fraction > 0) {
+ target = xn_over_d(w,fraction,1000);
+ } else {
+ target = w;
+ }
+ }
+ if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) {
+ while (1) {
+ if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) {
+ /*tex a bit weird for an overlay but anyway, here we don't need a factor as we don't step */
+ y = get_delim_box(f, c, w, connector_overlap_min(cur_style), 1, node_attr(attr_p));
+ break;
+ } else if (char_tag(f, c) != list_tag) {
+ break;
+ } else {
+ int yy = char_remainder(f, c);
+ if (!char_exists(f, yy)) {
+ break;
+ } else if (flags & OVERLAY_CODE) {
+ if (char_height(f, yy) > target) {
+ break;
+ }
+ } else {
+ if (char_width(f, yy) > target)
+ break;
+ }
+ c = yy;
+ }
+ }
+ }
+ if (y == null) {
+ /*tex italic gets added to width */
+ y = char_box(f, c, node_attr(attr_p));
+ }
+ if (flags & TOP_CODE) {
+ if (h < accent_base_height(f)) {
+ delta = h;
+ } else {
+ delta = accent_base_height(f);
+ }
+ } else if (flags & OVERLAY_CODE) {
+ /*tex center the accent vertically around the accentee */
+ delta = half(height(y) + depth(y) + height(x) + depth(x));
+ } else {
+ delta = 0; /* hm */
+ }
+ if ((supscr(q) != null) || (subscr(q) != null)) {
+ if (type(nucleus(q)) == math_char_node) {
+ /*tex swap the subscript and superscript into box |x| */
+ flush_node_list(x);
+ x = new_noad();
+ r = math_clone(nucleus(q));
+ nucleus(x) = r;
+ supscr(x) = supscr(q);
+ supscr(q) = null;
+ subscr(x) = subscr(q);
+ subscr(q) = null;
+ type(nucleus(q)) = sub_mlist_node;
+ math_list(nucleus(q)) = x;
+ x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list);
+ delta = delta + height(x) - h;
+ h = height(x);
+ }
+ } else if ((vlink(q) != null) && (type(nucleus(q)) == math_char_node)) {
+ /*tex only pure math char nodes */
+ internal_font_number f = fam_fnt(math_fam(nucleus(q)),cur_size);
+ if (do_new_math(f)) {
+ ic = char_italic(f,math_character(nucleus(q)));
+ }
+ }
+ /*tex the top accents of both characters are aligned */
+ if (s_is_absolute) {
+ scaled sa;
+ if (ext != NULL) {
+ /*tex if the accent is extensible just take the center */
+ sa = half(width(y));
+ } else {
+ /*tex
+ There is no bot_accent so let's assume similarity
+
+ \starttyping
+ if (flags & BOT_CODE) {
+ sa = char_bot_accent(f, c);
+ } else {
+ sa = char_top_accent(f, c);
+ }
+ \stoptyping
+ */
+ sa = char_top_accent(f, c);
+ }
+ if (sa == INT_MIN) {
+ /*tex just take the center */
+ sa = half(width(y));
+ }
+ if (math_direction_par == dir_TRT) {
+ shift_amount(y) = s + sa - width(y);
+ } else {
+ shift_amount(y) = s - sa;
+ }
+ } else {
+ if (width(y)== 0) {
+ shift_amount(y) = s + w;
+ } else if (math_direction_par == dir_TRT) {
+ shift_amount(y) = s + width(y); /* ok? */
+ } else {
+ shift_amount(y) = s + half(w - width(y));
+ }
+ }
+ width(y) = 0;
+ if (flags & (TOP_CODE | OVERLAY_CODE)) {
+ p = new_kern(-delta);
+ reset_attributes(p, node_attr(q));
+ couple_nodes(p,x);
+ couple_nodes(y,p);
+ } else {
+ couple_nodes(x,y);
+ y = x;
+ }
+ r = vpackage(y, 0, additional, max_dimen, math_direction_par);
+ reset_attributes(r, node_attr(q));
+ subtype(r) = math_accent_list;
+ width(r) = width(x);
+ y = r;
+ if (flags & (TOP_CODE | OVERLAY_CODE)) {
+ if (height(y) < h) {
+ /*tex make the height of box |y| equal to |h| */
+ p = new_kern(h - height(y));
+ reset_attributes(p, node_attr(q));
+ try_couple_nodes(p,list_ptr(y));
+ list_ptr(y) = p;
+ height(y) = h;
+ }
+ } else {
+ shift_amount(y) = -(h - height(y));
+ }
+ if (ic != 0) {
+ /*tex old font codepath has ic built in, new font code doesn't */
+ width(r) += ic ;
+ }
+ math_list(nucleus(q)) = y;
+ type(nucleus(q)) = sub_box_node;
+}
+
+static void make_math_accent(pointer q, int cur_style)
+{
+ int topstretch = !(subtype(q) % 2);
+ int botstretch = !(subtype(q) / 2);
+
+ if (top_accent_chr(q) != null) {
+ fetch(top_accent_chr(q));
+ if (char_exists(cur_f, cur_c)) {
+ do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
+ }
+ flush_node(top_accent_chr(q));
+ top_accent_chr(q) = null;
+ }
+ if (bot_accent_chr(q) != null) {
+ fetch(bot_accent_chr(q));
+ if (char_exists(cur_f, cur_c)) {
+ do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style);
+ }
+ flush_node(bot_accent_chr(q));
+ bot_accent_chr(q) = null;
+ }
+ if (overlay_accent_chr(q) != null) {
+ fetch(overlay_accent_chr(q));
+ if (char_exists(cur_f, cur_c)) {
+ do_make_math_accent(q, cur_f, cur_c, OVERLAY_CODE | STRETCH_ACCENT_CODE, cur_style);
+ }
+ flush_node(overlay_accent_chr(q));
+ overlay_accent_chr(q) = null;
+ }
+}
+
+/*tex
+
+ The |make_fraction| procedure is a bit different because it sets
+ |new_hlist(q)| directly rather than making a sub-box.
+
+*/
+
+static void make_fraction(pointer q, int cur_style)
+{
+ pointer p, p1, p2, v, x, y, z, l, r, m;
+ scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2, f, t;\
+ /*tex
+
+ We can take the rule width from an explicitly set fam, even if a fraction
+ itself has no character, otherwise we just use the math parameter.
+
+ */
+ scaled used_fam = math_rules_fam_par;
+ if (math_rule_thickness_mode_par > 0 && thickness(q) != 0) {
+ f = fraction_fam(q);
+ if (f >= 0) {
+ t = fam_fnt(f,cur_size);
+ if (do_new_math(t)) {
+ t = font_MATH_par(t, FractionRuleThickness);
+ if (t != undefined_math_parameter) {
+ thickness(q) = t;
+ used_fam = f;
+ }
+ }
+ }
+ }
+ if (thickness(q) == default_code)
+ thickness(q) = fraction_rule(cur_style);
+ /*tex
+
+ 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.
+
+ */
+ x = clean_box(numerator(q), num_style(cur_style), cur_style, math_numerator_list);
+ z = clean_box(denominator(q), denom_style(cur_style), cur_style, math_denominator_list);
+ if (middle_delimiter(q) != null) {
+ delta = 0;
+ m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
+ middle_delimiter(q) = null;
+ } else {
+ m = null ;
+ if (width(x) < width(z)) {
+ x = rebox(x, width(z));
+ } else {
+ z = rebox(z, width(x));
+ }
+ }
+ if (m != null) {
+ shift_up = 0;
+ shift_down = 0;
+ } else if (thickness(q) == 0) {
+ shift_up = stack_num_up(cur_style);
+ shift_down = stack_denom_down(cur_style);
+ /*tex
+
+ 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 |2delta|.
+
+ */
+ clr1 = stack_vgap(cur_style);
+ delta = half(clr1 - ((shift_up - depth(x)) - (height(z) - shift_down)));
+ if (delta > 0) {
+ shift_up = shift_up + delta;
+ shift_down = shift_down + delta;
+ }
+ } else {
+ shift_up = fraction_num_up(cur_style);
+ shift_down = fraction_denom_down(cur_style);
+ /*tex
+
+ In the case of a fraction line, the minimum clearance depends on the
+ actual thickness of the line.
+
+ */
+ clr1 = fraction_num_vgap(cur_style);
+ clr2 = fraction_denom_vgap(cur_style);
+ delta = half(thickness(q));
+ if (fractionexact(q)) {
+ delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis_size(cur_size) + delta));
+ delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta));
+ } else {
+ clr1 = ext_xn_over_d(clr1, thickness(q), fraction_rule(cur_style));
+ clr2 = ext_xn_over_d(clr2, thickness(q), fraction_rule(cur_style));
+ delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis_size(cur_size) + delta));
+ delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta));
+ }
+ if (delta1 > 0) {
+ shift_up = shift_up + delta1;
+ }
+ if (delta2 > 0) {
+ shift_down = shift_down + delta2;
+ }
+ }
+ if (m != null) {
+ /*tex
+
+ Construct a hlist box for the fraction, according to |hgap| and
+ |vgap|.
+
+ */
+ shift_up = skewed_fraction_vgap(cur_style);
+
+ if (!fractionnoaxis(q)) {
+ shift_up += half(math_axis_size(cur_size));
+ }
+ shift_down = shift_up;
+ v = new_null_box();
+ reset_attributes(v, node_attr(q));
+ type(v) = hlist_node;
+ subtype(v) = math_numerator_list;
+ list_ptr(v) = x;
+ width(v) = width(x);
+ height(v) = height(x) + shift_up;
+ depth(v) = depth(x);
+ shift_amount(v) = - shift_up;
+ x = v;
+ v = new_null_box();
+ reset_attributes(v, node_attr(q));
+ type(v) = hlist_node;
+ subtype(v) = math_denominator_list;
+ list_ptr(v) = z;
+ width(v) = width(z);
+ height(v) = height(z);
+ depth(v) = depth(z) + shift_down;
+ shift_amount(v) = shift_down;
+ z = v;
+ v = new_null_box();
+ subtype(v) = math_fraction_list;
+ reset_attributes(v, node_attr(q));
+ type(v) = hlist_node;
+ if (height(x) > height(z)) {
+ height(v) = height(x);
+ } else {
+ height(v) = height(z);
+ }
+ if (depth(x) > depth(z)) {
+ depth(v) = depth(x);
+ } else {
+ depth(v) = depth(z);
+ }
+ if (height(m) > height(v)) {
+ height(v) = height(m);
+ }
+ if (depth(m) > depth(v)) {
+ depth(v) = depth(m);
+ }
+ if (fractionexact(q)) {
+ delta1 = -half(skewed_fraction_hgap(cur_style));
+ delta2 = delta1;
+ width(v) = width(x) + width(z) + width(m) - skewed_fraction_hgap(cur_style);
+ } else {
+ delta1 = half(skewed_fraction_hgap(cur_style)-width(m));
+ delta2 = half(skewed_fraction_hgap(cur_style)+width(m));
+ width(v) = width(x) + width(z) + skewed_fraction_hgap(cur_style);
+ width(m) = 0;
+ }
+ p1 = new_kern(delta1);
+ reset_attributes(p1, node_attr(q));
+ p2 = new_kern(delta2);
+ reset_attributes(p2, node_attr(q));
+ couple_nodes(x,p1);
+ couple_nodes(p1,m);
+ couple_nodes(m,p2);
+ couple_nodes(p2,z);
+ list_ptr(v) = x;
+ } else {
+ /*tex
+
+ Construct a vlist box for the fraction, according to |shift_up| and
+ |shift_down|.
+
+ */
+ v = new_null_box();
+ type(v) = vlist_node;
+ subtype(v) = math_fraction_list;
+ height(v) = shift_up + height(x);
+ depth(v) = depth(z) + shift_down;
+ /*tex This also equals |width(z)|. */
+ width(v) = width(x);
+ reset_attributes(v, node_attr(q));
+ if (thickness(q) && ! fractionnorule(q)) {
+ y = do_fraction_rule(thickness(q), node_attr(q), math_fraction_rule, cur_size, used_fam);
+ p = new_kern((math_axis_size(cur_size) - delta) - (height(z) - shift_down));
+ reset_attributes(p, node_attr(q));
+ couple_nodes(y,p);
+ couple_nodes(p,z);
+ p = new_kern((shift_up - depth(x)) - (math_axis_size(cur_size) + delta));
+ couple_nodes(p,y);
+ } else {
+ p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
+ couple_nodes(p,z);
+ }
+ reset_attributes(p, node_attr(q));
+ couple_nodes(x,p);
+ list_ptr(v) = x;
+ }
+ /*tex
+
+ Put the fraction into a box with its delimiters, and make |new_hlist(q)|
+ point to it.
+
+ */
+ if (do_new_math(cur_f)) {
+ delta = fraction_del_size_new(cur_style);
+ if (delta == undefined_math_parameter) {
+ delta = get_delimiter_height(depth(v), height(v), true);
+ }
+ } else {
+ delta = fraction_del_size_old(cur_style);
+ }
+ l = do_delimiter(q, left_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
+ left_delimiter(q) = null;
+ r = do_delimiter(q, right_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
+ right_delimiter(q) = null;
+ couple_nodes(l,v);
+ couple_nodes(v,r);
+ y = hpack(l, 0, additional, -1);
+ reset_attributes(y, node_attr(q));
+ /*tex There can also be a nested one: */
+ subtype(y) = math_fraction_list;
+ assign_new_hlist(q, y);
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift);
+static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same);
+
+static scaled make_op(pointer q, int cur_style)
+{
+ /*tex offset between subscript and superscript */
+ scaled delta = 0;
+ scaled dummy = 0;
+ /*tex temporary registers for box construction */
+ pointer p, v, x, y, z, n;
+ /*tex register for character examination */
+ int c;
+ /*tex dimensions for box calculation */
+ scaled shift_up, shift_down;
+ boolean axis_shift = false;
+ scaled ok_size;
+ if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) {
+ subtype(q) = op_noad_type_limits;
+ }
+ if (type(nucleus(q)) == math_char_node) {
+ fetch(nucleus(q));
+ if (cur_style < text_style) {
+ /*tex try to make it larger */
+ ok_size = minimum_operator_size(cur_style);
+ if (ok_size != undefined_math_parameter) {
+ /*tex creating a temporary delimiter is the cleanest way */
+ y = new_node(delim_node, 0);
+ reset_attributes(y, node_attr(nucleus(q)));
+ small_fam(y) = math_fam(nucleus(q));
+ small_char(y) = math_character(nucleus(q));
+ x = do_delimiter(q, y, text_size, ok_size, false, cur_style, true, NULL, &delta, NULL);
+ if (delta != 0) {
+ if (do_new_math(cur_f)) {
+ /*tex
+ As we never added italic correction we don't need to compensate. The ic
+ is stored in a special field of the node and applied in some occasions.
+ */
+ } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
+ /*tex
+ Here we (selectively) remove the italic correction that always gets added
+ in a traditional font. See (**). In \OPENTYPE\ mode we insert italic kerns,
+ but in traditional mode it's width manipulation. This actually makes sense
+ because those fonts have a fake width and the italic correction sets that
+ right.
+ */
+ width(x) -= delta;
+ }
+ }
+ } else {
+ ok_size = height_plus_depth(cur_f, cur_c) + 1;
+ while ((char_tag(cur_f, cur_c) == list_tag) && height_plus_depth(cur_f, cur_c) < ok_size) {
+ c = char_remainder(cur_f, cur_c);
+ if (!char_exists(cur_f, c))
+ break;
+ cur_c = c;
+ math_character(nucleus(q)) = c;
+ }
+ delta = char_italic(cur_f, cur_c);
+ x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list);
+ if (delta != 0) {
+ if (do_new_math(cur_f)) {
+ /*tex we never added italic correction */
+ } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
+ /*tex remove italic correction */
+ width(x) -= delta;
+ }
+ }
+ axis_shift = true;
+ }
+ } else {
+ /*tex normal size */
+ delta = char_italic(cur_f, cur_c);
+ x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list);
+ if (delta != 0) {
+ if (do_new_math(cur_f)) {
+ /*tex we never added italic correction */
+ } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
+ /*tex remove italic correction */
+ width(x) -= delta;
+ }
+ }
+ axis_shift = true;
+ }
+ if (axis_shift) {
+ /*tex center vertically */
+ shift_amount(x) = half(height(x) - depth(x)) - math_axis_size(cur_size);
+ }
+ type(nucleus(q)) = sub_box_node;
+ math_list(nucleus(q)) = x;
+ }
+ /*tex we now handle op_nod_type_no_limits here too */
+ if (subtype(q) == op_noad_type_no_limits) {
+ if (do_new_math(cur_f)) {
+ /*tex
+ Not:
+
+ \starttyping
+ if (delta != 0) {
+ delta = half(delta) ;
+ }
+ \stoptyping
+ */
+ p = check_nucleus_complexity(q, &dummy, cur_style, NULL);
+ if ((subscr(q) == null) && (supscr(q) == null)) {
+ assign_new_hlist(q, p);
+ } else {
+ /*tex
+ Not:
+
+ \starttyping
+ make_scripts(q, p, 0, cur_style, delta, -delta);
+ \stoptyping
+ */
+ int mode = math_nolimits_mode_par; /* wins */
+ /*tex
+
+ For easy configuration ... fonts are somewhat inconsistent
+ and the values for italic correction run from 30 to 60\% of.
+ the width.
+
+ */
+ switch (mode) {
+ case 0 :
+ /*tex full bottom correction */
+ make_scripts(q, p, 0, cur_style, 0, -delta);
+ break;
+ case 1 :
+ /*tex |MathConstants| driven */
+ make_scripts(q, p, 0, cur_style,
+ round_xn_over_d(delta, nolimit_sup_factor(cur_style), 1000),
+ -round_xn_over_d(delta, nolimit_sub_factor(cur_style), 1000));
+ break ;
+ case 2 :
+ /*tex no correction */
+ make_scripts(q, p, 0, cur_style, 0, 0);
+ break ;
+ case 3 :
+ /*tex half bottom correction */
+ make_scripts(q, p, 0, cur_style, 0, -half(delta));
+ break;
+ case 4 :
+ /*tex half bottom and top correction */
+ make_scripts(q, p, 0, cur_style, half(delta), -half(delta));
+ break;
+ default :
+ if (mode > 15) {
+ /*tex for quickly testing values */
+ make_scripts(q, p, 0, cur_style, 0, -round_xn_over_d(delta, mode, 1000));
+ } else {
+ make_scripts(q, p, 0, cur_style, 0, 0);
+ }
+ break;
+ }
+ }
+ delta = 0;
+ } else {
+ /*tex similar code then the caller (before CHECK_DIMENSIONS) */
+ p = check_nucleus_complexity(q, &delta, cur_style, NULL);
+ if ((subscr(q) == null) && (supscr(q) == null)) {
+ assign_new_hlist(q, p);
+ } else {
+ make_scripts(q, p, delta, cur_style, 0, 0);
+ }
+ }
+ } else if (subtype(q) == op_noad_type_limits) {
+ /*tex
+
+ 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.
+
+ */
+ x = clean_box(supscr(q), sup_style(cur_style), cur_style, math_sup_list);
+ y = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list);
+ z = clean_box(subscr(q), sub_style(cur_style), cur_style, math_sub_list);
+ v = new_null_box();
+ reset_attributes(v, node_attr(q));
+ type(v) = vlist_node;
+ subtype(v) = math_limits_list;
+ if (do_new_math(cur_f)) {
+ n = nucleus(q);
+ if (n != null) {
+ if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) {
+ n = math_list(n);
+ if (n != null) {
+ if (type(n) == hlist_node) {
+ /*tex just a not scaled char */
+ n = list_ptr(n);
+ while (n != null) {
+ if (type(n) == glyph_node) {
+ delta = char_italic(font(n),character(n));
+ }
+ n = vlink(n);
+ }
+ } else {
+ while (n != null) {
+ if (type(n) == fence_noad) {
+ if (delimiteritalic(n) > delta) {
+ /*tex we can have dummies, the period ones */
+ delta = delimiteritalic(n);
+ }
+ }
+ n = vlink(n);
+ }
+ }
+ }
+ } else {
+ n = nucleus(q);
+ if (type(n) == math_char_node) {
+ delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n));
+ }
+ }
+ }
+ }
+ 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);
+ /*tex v is the still empty target */
+ height(v) = height(y);
+ depth(v) = depth(y);
+ /*tex
+
+ Attach the limits to |y| and adjust |height(v)|, |depth(v)| to
+ account for their presence.
+
+ 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;
+ |b| is baseline and |v| is the minumum gap.
+
+ */
+ if (supscr(q) == null) {
+ list_ptr(x) = null;
+ flush_node(x);
+ list_ptr(v) = y;
+ } else {
+ shift_up = limit_above_bgap(cur_style) - depth(x);
+ if (shift_up < limit_above_vgap(cur_style))
+ shift_up = limit_above_vgap(cur_style);
+ p = new_kern(shift_up);
+ reset_attributes(p, node_attr(q));
+ couple_nodes(p,y);
+ couple_nodes(x,p);
+ p = new_kern(limit_above_kern(cur_style));
+ reset_attributes(p, node_attr(q));
+ couple_nodes(p,x);
+ list_ptr(v) = p;
+ height(v) = height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + shift_up;
+ }
+ if (subscr(q) == null) {
+ list_ptr(z) = null;
+ flush_node(z);
+ } else {
+ shift_down = limit_below_bgap(cur_style) - height(z);
+ if (shift_down < limit_below_vgap(cur_style))
+ shift_down = limit_below_vgap(cur_style);
+ p = new_kern(shift_down);
+ reset_attributes(p, node_attr(q));
+ couple_nodes(y,p);
+ couple_nodes(p,z);
+ p = new_kern(limit_below_kern(cur_style));
+ reset_attributes(p, node_attr(q));
+ couple_nodes(z,p);
+ depth(v) = depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + shift_down;
+ }
+ if (subscr(q) != null) {
+ math_list(subscr(q)) = null;
+ flush_node(subscr(q));
+ subscr(q) = null;
+ }
+ if (supscr(q) != null) {
+ math_list(supscr(q)) = null;
+ flush_node(supscr(q));
+ supscr(q) = null;
+ }
+ assign_new_hlist(q, v);
+ if (do_new_math(cur_f)) {
+ delta = 0;
+ }
+ }
+ return delta;
+}
+
+/*tex
+
+ A ligature found in a math formula does not create a ligature, because there
+ is no question of hyphenation afterwards; the ligature will simply be stored
+ in an ordinary |glyph_node|, after residing in an |ord_noad|.
+
+ The |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.
+
+*/
+
+#define simple_char_noad(p) (\
+ (p != null) && \
+ (type(p) == simple_noad) && \
+ (subtype(p) <= punct_noad_type) && \
+ (type(nucleus(p)) == math_char_node) \
+)
+
+#define same_nucleus_fam(p,q) \
+ (math_fam(nucleus(p)) == math_fam(nucleus(q)))
+
+static void make_ord(pointer q)
+{
+ /*tex the left-side character for lig/kern testing */
+ int a;
+ /*tex temporary registers for list manipulation */
+ pointer p, r, s;
+ /*tex a kern */
+ scaled k;
+ /*tex a ligature */
+ liginfo lig;
+ RESTART:
+ if (subscr(q) == null && supscr(q) == null && type(nucleus(q)) == math_char_node) {
+ p = vlink(q);
+ if (simple_char_noad(p) && same_nucleus_fam(p,q)) {
+ type(nucleus(q)) = math_text_char_node;
+ fetch(nucleus(q));
+ a = cur_c;
+ /*tex add italic correction */
+ if (do_new_math(cur_f) && (char_italic(cur_f,math_character(nucleus(q))) != 0)) {
+ p = new_kern(char_italic(cur_f,math_character(nucleus(q))));
+ subtype(p) = italic_kern;
+ reset_attributes(p, node_attr(q));
+ couple_nodes(p,vlink(q));
+ couple_nodes(q,p);
+ return;
+ }
+ /*tex construct ligatures, quite unlikely in new math fonts */
+ if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) {
+ cur_c = math_character(nucleus(p));
+ /*tex
+
+ If character |a| has a kern with |cur_c|, attach the kern
+ after~|q|; or if it has a ligature with |cur_c|, combine
+ noads |q| and~|p| appropriately; then |return| if the cursor
+ has moved past a noad, or |goto restart|.
+
+ 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.
+
+ 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.
+
+ */
+ if (disable_lig_par == 0 && has_lig(cur_f, a)) {
+ lig = get_ligature(cur_f, a, cur_c);
+ if (is_valid_ligature(lig)) {
+ /*tex allow a way out of infinite ligature loop */
+ check_interrupt();
+ switch (lig_type(lig)) {
+ case 1:
+ /*tex \.{=:\char`\|} */
+ case 5:
+ /*tex \.{=:\char`\|>} */
+ math_character(nucleus(q)) = lig_replacement(lig);
+ break;
+ case 2:
+ /*tex \.{\char`\|=:} */
+ case 6:
+ /*tex \.{\char`\|=:>} */
+ math_character(nucleus(p)) = lig_replacement(lig);
+ break;
+ case 3:
+ /*tex \.{\char`\|=:\char`\|} */
+ case 7:
+ /*tex \.{\char`\|=:\char`\|>} */
+ case 11:
+ /*tex \.{\char`\|=:\char`\|>>} */
+ r = new_noad();
+ reset_attributes(r, node_attr(q));
+ s = new_node(math_char_node, 0);
+ reset_attributes(s, node_attr(q));
+ nucleus(r) = s;
+ math_character(nucleus(r)) = lig_replacement(lig);
+ math_fam(nucleus(r)) = math_fam(nucleus(q));
+ couple_nodes(q,r);
+ couple_nodes(r,p);
+ if (lig_type(lig) < 11) {
+ type(nucleus(r)) = math_char_node;
+ } else {
+ /*tex prevent combination */
+ type(nucleus(r)) = math_text_char_node;
+ }
+ break;
+ default:
+ try_couple_nodes(q,vlink(p));
+ math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */
+ subscr(q) = subscr(p);
+ supscr(q) = supscr(p);
+ subscr(p) = null ;
+ supscr(p) = null ;
+ flush_node(p);
+ break;
+ }
+ if (lig_type(lig) > 3)
+ return;
+ type(nucleus(q)) = math_char_node;
+ goto RESTART;
+ }
+ }
+ if (disable_kern_par == 0 && has_kern(cur_f, a)) {
+ /*tex todo: should this use mathkerns? */
+ k = get_kern(cur_f, a, cur_c);
+ if (k != 0) {
+ p = new_kern(k);
+ reset_attributes(p, node_attr(q));
+ couple_nodes(p,vlink(q));
+ couple_nodes(q,p);
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*tex
+
+ If the fonts for the left and right bits of a mathkern are not both new-style
+ fonts, then return a sentinel value meaning: please use old-style italic
+ correction placement
+
+*/
+
+#define MATH_KERN_NOT_FOUND 0x7FFFFFFF
+
+/*tex
+
+ This function tries to find the kern needed for proper cut-ins. The left side
+ doesn't move, but the right side does, so the first order of business is to
+ create a staggered fence line on the left side of the right character.
+
+ The microsoft spec says that there are four quadrants, but the actual images
+ say.
+
+*/
+
+static scaled math_kern_at(internal_font_number f, int c, int side, int v)
+{
+ int h, k, numkerns;
+ scaled *kerns_heights;
+ scaled kern = 0;
+ /*tex Known to exist: */
+ charinfo *co = char_info(f, c);
+ numkerns = get_charinfo_math_kerns(co, side);
+ if (numkerns == 0)
+ return kern;
+ if (side == top_left_kern) {
+ kerns_heights = co->top_left_math_kern_array;
+ } else if (side == bottom_left_kern) {
+ kerns_heights = co->bottom_left_math_kern_array;
+ } else if (side == top_right_kern) {
+ kerns_heights = co->top_right_math_kern_array;
+ } else if (side == bottom_right_kern) {
+ kerns_heights = co->bottom_right_math_kern_array;
+ } else {
+ /*tex Not reached: */
+ confusion("math_kern_at");
+ kerns_heights = NULL;
+ }
+ if (v < kerns_heights[0])
+ return kerns_heights[1];
+ for (k = 0; k < numkerns; k++) {
+ h = kerns_heights[(k * 2)];
+ kern = kerns_heights[(k * 2) + 1];
+ if (h > v) {
+ return kern;
+ }
+ }
+ return kern;
+}
+
+static scaled find_math_kern(internal_font_number l_f, int l_c, internal_font_number r_f, int r_c, int cmd, scaled shift)
+{
+ scaled corr_height_top = 0, corr_height_bot = 0;
+ scaled krn_l = 0, krn_r = 0, krn = 0;
+ if ((!do_new_math(l_f)) || (!do_new_math(r_f)) || (!char_exists(l_f,l_c)) || (!char_exists(r_f,r_c)))
+ return MATH_KERN_NOT_FOUND;
+ if (cmd == sup_mark_cmd) {
+ corr_height_top = char_height(l_f, l_c);
+ /*tex bottom of superscript */
+ corr_height_bot = -char_depth(r_f, r_c) + shift;
+ krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top);
+ krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top);
+ krn = (krn_l + krn_r);
+ krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot);
+ krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot);
+ if ((krn_l + krn_r) < krn)
+ krn = (krn_l + krn_r);
+ return (krn);
+ } else if (cmd == sub_mark_cmd) {
+ /*tex top of subscript */
+ corr_height_top = char_height(r_f, r_c) - shift;
+ corr_height_bot = -char_depth(l_f, l_c);
+ krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top);
+ krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top);
+ krn = (krn_l + krn_r);
+ krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot);
+ krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot);
+ if ((krn_l + krn_r) < krn)
+ krn = (krn_l + krn_r);
+ return (krn);
+ } else {
+ confusion("find_math_kern");
+ }
+ /*tex Not reached: */
+ return 0;
+}
+
+/*tex Just a small helper: */
+
+static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2, halfword subtyp)
+{
+ pointer y;
+ pointer z = new_kern(delta2);
+ if (subtyp != 0) {
+ subtype(z) = subtyp;
+ }
+ reset_attributes(z, node_attr(q));
+ if (new_hlist(q) == null) {
+ /*tex this is somewhat weird */
+ new_hlist(q) = z;
+ } else {
+ y = new_hlist(q);
+ while (vlink(y) != null)
+ y = vlink(y);
+ couple_nodes(y,z);
+ }
+ return new_hlist(q);
+}
+
+/*tex
+
+ The purpose of |make_scripts(q,it)| is to attach the subscript and/or
+ superscript of noad |q| to the list that starts at |new_hlist(q)|, given that
+ subscript and superscript aren't both empty. The superscript will be
+ horizontally shifted over |delta1|, the subscript over |delta2|.
+
+ We set |shift_down| and |shift_up| to the minimum amounts to shift the
+ baseline of subscripts and superscripts based on the given nucleus.
+
+ Note: We need to look at a character but also at the first one in a sub list
+ and there we ignore leading kerns and glue. Elsewhere is code that removes
+ kerns assuming that is italic correction. The heuristics are unreliable for
+ the new fonts so eventualy there will be an option to ignore such
+ corrections.
+
+*/
+
+#define analyze_script(init,su_n,su_f,su_c) do { \
+ su_n = init; \
+ if (su_n != null) { \
+ if (math_script_char_mode_par > 0 && type(su_n) == math_char_node) { \
+ fetch(su_n); \
+ if (char_exists(cur_f, cur_c)) { \
+ su_f = cur_f; \
+ su_c = cur_c; \
+ } else { \
+ su_n = null; \
+ } \
+ } else if (math_script_box_mode_par > 0 && type(su_n) == sub_mlist_node) { \
+ su_n = math_list(su_n); \
+ while (su_n != null) { \
+ if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
+ su_n = vlink(su_n); \
+ } else if (type(su_n) == simple_noad) { \
+ su_n = nucleus(su_n); \
+ if (type(su_n) == math_char_node) { \
+ fetch(su_n); \
+ if (char_exists(cur_f, cur_c)) { \
+ su_f = cur_f; \
+ su_c = cur_c; \
+ } else { \
+ su_n = null; \
+ } \
+ } else { \
+ su_n = null; \
+ } \
+ break; \
+ } else { \
+ su_n = null; \
+ break; \
+ } \
+ } \
+ } else if (type(su_n) == sub_box_node) { \
+ su_n = math_list(su_n); \
+ if (su_n != null) { \
+ if (type(su_n) == hlist_node) { \
+ su_n = list_ptr(su_n); \
+ } \
+ if (su_n != null) { \
+ if (math_script_box_mode_par == 2) { \
+ while (su_n != null) { \
+ if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
+ su_n = vlink(su_n); \
+ } else if (type(su_n) == glyph_node) { \
+ if (char_exists(font(su_n), character(su_n))) { \
+ su_f = font(su_n); \
+ su_c = character(su_n); \
+ } else { \
+ su_n = null; \
+ } \
+ break ; \
+ } else { \
+ su_n = null; \
+ break; \
+ } \
+ } \
+ } else if (math_script_box_mode_par == 3) { \
+ int boundary = -1; \
+ while (su_n != null) { \
+ if ((type(su_n) == boundary_node) && (subtype(su_n) == user_boundary)) { \
+ boundary = boundary_value(su_n); \
+ su_n = vlink(su_n); \
+ } else if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \
+ su_n = vlink(su_n); \
+ } else if ((boundary > -1) && (type(su_n) == glyph_node)) { \
+ if (char_exists(font(su_n), character(su_n))) { \
+ su_f = font(su_n); \
+ su_c = character(su_n); \
+ } else { \
+ su_n = null; \
+ } \
+ break ; \
+ } else { \
+ su_n = null; \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ } else { \
+ su_n = null; \
+ } \
+ } else { \
+ su_n = null; \
+ } \
+ } \
+ } while (0) \
+
+#define x_su_style(n,cur_style,su_style) \
+ (noadoptionnosubscript(n) ? cur_style : su_style(cur_style))
+
+static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift)
+{
+ pointer x, y, z;
+ scaled shift_up, shift_down, clr;
+ scaled delta1, delta2;
+ halfword sub_n, sup_n, subtyp;
+ internal_font_number sub_f, sup_f;
+ int sub_c, sup_c;
+ sub_n = null;
+ sup_n = null;
+ sub_f = 0;
+ sup_f = 0;
+ sub_c = 0;
+ sup_c = 0;
+ delta1 = it;
+ delta2 = 0;
+ subtyp = 0;
+ switch (type(nucleus(q))) {
+ case math_char_node:
+ case math_text_char_node:
+ if ((subscr(q) == null) && (delta1 != 0)) {
+ /*tex todo: selective italic correction */
+ x = new_kern(delta1);
+ subtype(x) = italic_kern;
+ reset_attributes(x, node_attr(nucleus(q)));
+ couple_nodes(p,x);
+ delta1 = 0;
+ }
+ }
+ assign_new_hlist(q, p);
+ if (is_char_node(p)) {
+ shift_up = 0;
+ shift_down = 0;
+ } else {
+ z = hpack(p, 0, additional, -1);
+ shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */
+ shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */
+ list_ptr(z) = null;
+ flush_node(z);
+ }
+ if (is_char_node(p)) {
+ /*tex We look at the subscript character (_i) or first character in a list (_{ij}). */
+ analyze_script(subscr(q),sub_n,sub_f,sub_c);
+ /*tex We look at the superscript character (^i) or first character in a list (^{ij}). */
+ analyze_script(supscr(q),sup_n,sup_f,sup_c);
+ }
+ if (supscr(q) == null) {
+ /*tex
+
+ Construct a subscript box |x| when there is no superscript. 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.
+
+ */
+ x = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style, math_sub_list);
+ width(x) = width(x) + space_after_script(cur_style);
+ switch (math_scripts_mode_par) {
+ case 1:
+ shift_down = sub_shift_down(cur_style) ;
+ break;
+ case 2:
+ shift_down = sub_sup_shift_down(cur_style) ;
+ break;
+ case 3:
+ shift_down = sub_sup_shift_down(cur_style) ;
+ break;
+ case 4:
+ shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
+ break;
+ case 5:
+ shift_down = sub_shift_down(cur_style) ;
+ break;
+ default:
+ if (shift_down < sub_shift_down(cur_style))
+ shift_down = sub_shift_down(cur_style);
+ clr = height(x) - sub_top_max(cur_style);
+ if (shift_down < clr)
+ shift_down = clr;
+ break;
+ }
+ shift_amount(x) = shift_down;
+ /*tex Now find and correct for horizontal shift. */
+ subtyp = 0;
+ if (sub_n != null) {
+ delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
+ if (delta2 == MATH_KERN_NOT_FOUND) {
+ delta2 = subshift ;
+ } else {
+ delta2 = delta2 + subshift ;
+ subtyp = font_kern;
+ }
+ } else {
+ delta2 = subshift ;
+ }
+ if (delta2 != 0) {
+ p = attach_hkern_to_new_hlist(q, delta2, subtyp);
+ }
+ } else {
+ /*tex
+
+ Construct a superscript box |x|. The bottom of a superscript should
+ never descend below the baseline plus one-fourth of the x-height.
+
+ */
+ x = clean_box(supscr(q), (noadoptionnosupscript(q) ? cur_style : sup_style(cur_style)), cur_style, math_sup_list);
+ width(x) = width(x) + space_after_script(cur_style);
+ switch (math_scripts_mode_par) {
+ case 1:
+ shift_up = sup_shift_up(cur_style);
+ break;
+ case 2:
+ shift_up = sup_shift_up(cur_style) ;
+ break;
+ case 3:
+ shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style) - sub_shift_down(cur_style) ;
+ break;
+ case 4:
+ shift_up = sup_shift_up(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
+ break;
+ case 5:
+ shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style)-sub_shift_down(cur_style) ;
+ break;
+ default:
+ clr = sup_shift_up(cur_style);
+ if (shift_up < clr)
+ shift_up = clr;
+ clr = depth(x) + sup_bottom_min(cur_style);
+ if (shift_up < clr)
+ shift_up = clr;
+ break;
+ }
+ if (subscr(q) == null) {
+ shift_amount(x) = -shift_up;
+ /*tex Now find and correct for horizontal shift. */
+ subtyp = 0;
+ if (sup_n != null) {
+ clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
+ if (clr == MATH_KERN_NOT_FOUND) {
+ clr = supshift ;
+ } else {
+ clr = clr + supshift ;
+ subtyp = font_kern;
+ }
+ } else {
+ clr = supshift;
+ }
+ if (clr != 0) {
+ p = attach_hkern_to_new_hlist(q, clr, subtyp);
+ }
+ } else {
+ /*tex
+
+ Construct a sub/superscript combination box |x|, with the
+ superscript offset by |delta|. 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.
+
+ */
+ y = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style, math_sub_list);
+ width(y) = width(y) + space_after_script(cur_style);
+ switch (math_scripts_mode_par) {
+ case 1:
+ shift_down = sub_shift_down(cur_style) ;
+ break;
+ case 2:
+ shift_down = sub_sup_shift_down(cur_style) ;
+ break;
+ case 3:
+ shift_down = sub_sup_shift_down(cur_style) ;
+ break;
+ case 4:
+ shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ;
+ break;
+ case 5:
+ shift_down = sub_shift_down(cur_style) ;
+ break;
+ default:
+ if (shift_down < sub_sup_shift_down(cur_style))
+ shift_down = sub_sup_shift_down(cur_style);
+ clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down));
+ if (clr > 0) {
+ shift_down = shift_down + clr;
+ clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x));
+ if (clr > 0) {
+ shift_up = shift_up + clr;
+ shift_down = shift_down - clr;
+ }
+ }
+ break;
+ }
+ /*tex Now find and correct for horizontal shift. */
+ subtyp = 0;
+ if (sub_n != null) {
+ delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down);
+ if (delta2 == MATH_KERN_NOT_FOUND) {
+ delta2 = subshift ;
+ } else {
+ delta2 = delta2 + subshift ;
+ subtyp = font_kern;
+ }
+ } else {
+ delta2 = subshift ;
+ }
+ if (delta2 != 0) {
+ p = attach_hkern_to_new_hlist(q, delta2, subtyp);
+ }
+ /*tex
+
+ Now the horizontal shift for the superscript; the superscript is
+ also to be shifted by |delta1| (the italic correction).
+
+ */
+ clr = MATH_KERN_NOT_FOUND;
+ if (sup_n != null) {
+ clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up);
+ }
+ /*tex
+
+ The delta can already have been applied and now be 0.
+
+ */
+ if (delta2 == MATH_KERN_NOT_FOUND)
+ delta2 = - supshift ;
+ else
+ delta2 = delta2 - supshift ;
+ if (clr != MATH_KERN_NOT_FOUND) {
+ shift_amount(x) = clr + delta1 - delta2;
+ } else {
+ shift_amount(x) = delta1 - delta2;
+ }
+ /*tex todo: only if kern != 0 */
+ p = new_kern((shift_up - depth(x)) - (height(y) - shift_down));
+ reset_attributes(p, node_attr(q));
+ couple_nodes(x,p);
+ couple_nodes(p,y);
+ /*tex We end up with funny dimensions. */
+ x = vpackage(x, 0, additional, max_dimen, math_direction_par);
+ reset_attributes(x, node_attr(q));
+ subtype(x) = math_scripts_list;
+ shift_amount(x) = shift_down;
+ }
+ }
+ if (new_hlist(q) == null) {
+ new_hlist(q) = x;
+ } else {
+ p = new_hlist(q);
+ while (vlink(p) != null)
+ p = vlink(p);
+ couple_nodes(p,x);
+ }
+ if (subscr(q) != null) {
+ math_list(subscr(q)) = null;
+ flush_node(subscr(q));
+ subscr(q) = null;
+ }
+ if (supscr(q) != null) {
+ math_list(supscr(q)) = null;
+ flush_node(supscr(q));
+ supscr(q) = null;
+ }
+}
+
+/*tex
+
+ 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
+ |left_noad_side| and |right_noad_side| will both be based on the original
+ |style|, so they will have consistent sizes.
+
+*/
+
+static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h)
+{
+ scaled delta;
+ pointer tmp, lst;
+ scaled ic = 0;
+ boolean stack = false;
+ boolean axis = false;
+ int same = subtype(q);
+ setup_cur_size(style);
+ if ((delimiterheight(q)!=0) || (delimiterdepth(q)!=0)) {
+
+ delta = delimiterheight(q) + delimiterdepth(q);
+ tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, false, &stack, &ic, &same);
+ delimiteritalic(q) = ic;
+ /*tex
+
+ Beware, a stacked delimiter has a shift but no corrected height/depth
+ (yet).
+
+ */
+ if (stack) {
+ shift_amount(tmp) = delimiterdepth(q);
+ }
+ if (delimiterexact(q)) {
+ delimiterheight(q) = height(tmp) - shift_amount(tmp);
+ delimiterdepth(q) = depth(tmp) + shift_amount(tmp);
+ }
+ if (delimiteraxis(q)) {
+ delimiterheight(q) += math_axis_size(cur_size);
+ delimiterdepth(q) -= math_axis_size(cur_size);
+ shift_amount(tmp) -= math_axis_size(cur_size);
+ }
+ lst = new_node(hlist_node,0);
+ reset_attributes(lst, node_attr(q));
+ box_dir(lst) = dir_TLT ;
+ height(lst) = delimiterheight(q);
+ depth(lst) = delimiterdepth(q);
+ width(lst) = width(tmp);
+ list_ptr(lst) = tmp;
+ tmp = lst ;
+ } else {
+ axis = ! delimiternoaxis(q);
+ delta = get_delimiter_height(max_d,max_h,axis);
+ tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, axis, &stack, &ic, &same);
+ delimiteritalic(q) = ic;
+ }
+ delimiter(q) = null;
+ assign_new_hlist(q, tmp);
+ delimitersamesize(q) = same;
+ if (delimiterclass(q) >= ord_noad_type) {
+ if (delimiterclass(q) <= inner_noad_type) {
+ return delimiterclass(q);
+ } else {
+ return ord_noad_type;
+ }
+ } else if (subtype(q) == left_noad_side) {
+ return open_noad_type;
+ } else {
+ return close_noad_type;
+ }
+}
+
+#define TEXT_STYLES(A,B) do { \
+ def_math_param(A,display_style,(B),level_one); \
+ def_math_param(A,cramped_display_style,(B),level_one); \
+ def_math_param(A,text_style,(B),level_one); \
+ def_math_param(A,cramped_text_style,(B),level_one); \
+ } while (0)
+
+#define SCRIPT_STYLES(A,B) do { \
+ def_math_param(A,script_style,(B),level_one); \
+ def_math_param(A,cramped_script_style,(B),level_one); \
+ def_math_param(A,script_script_style,(B),level_one); \
+ def_math_param(A,cramped_script_script_style,(B),level_one); \
+ } while (0)
+
+#define ALL_STYLES(A,B) do { \
+ TEXT_STYLES(A,(B)); \
+ SCRIPT_STYLES(A,(B)); \
+ } while (0)
+
+#define SPLIT_STYLES(A,B,C) do { \
+ TEXT_STYLES(A,(B)); \
+ SCRIPT_STYLES(A,(C)); \
+ } while (0)
+
+void initialize_math_spacing(void)
+{
+ ALL_STYLES (math_param_ord_ord_spacing, 0);
+ ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code);
+ SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip_code, 0);
+ ALL_STYLES (math_param_ord_open_spacing, 0);
+ ALL_STYLES (math_param_ord_close_spacing, 0);
+ ALL_STYLES (math_param_ord_punct_spacing, 0);
+ SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip_code, 0);
+
+ ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip_code);
+ ALL_STYLES (math_param_op_op_spacing, thin_mu_skip_code);
+ ALL_STYLES (math_param_op_bin_spacing, 0);
+ SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip_code, 0);
+ ALL_STYLES (math_param_op_open_spacing, 0);
+ ALL_STYLES (math_param_op_close_spacing, 0);
+ ALL_STYLES (math_param_op_punct_spacing, 0);
+ SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip_code, 0);
+
+ SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip_code, 0);
+ ALL_STYLES (math_param_bin_bin_spacing, 0);
+ ALL_STYLES (math_param_bin_rel_spacing, 0);
+ SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip_code, 0);
+ ALL_STYLES (math_param_bin_close_spacing, 0);
+ ALL_STYLES (math_param_bin_punct_spacing, 0);
+ SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip_code, 0);
+
+ SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip_code, 0);
+ ALL_STYLES (math_param_rel_bin_spacing, 0);
+ ALL_STYLES (math_param_rel_rel_spacing, 0);
+ SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip_code, 0);
+ ALL_STYLES (math_param_rel_close_spacing, 0);
+ ALL_STYLES (math_param_rel_punct_spacing, 0);
+ SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip_code, 0);
+
+ ALL_STYLES (math_param_open_ord_spacing, 0);
+ ALL_STYLES (math_param_open_op_spacing, 0);
+ ALL_STYLES (math_param_open_bin_spacing, 0);
+ ALL_STYLES (math_param_open_rel_spacing, 0);
+ ALL_STYLES (math_param_open_open_spacing, 0);
+ ALL_STYLES (math_param_open_close_spacing, 0);
+ ALL_STYLES (math_param_open_punct_spacing, 0);
+ ALL_STYLES (math_param_open_inner_spacing, 0);
+
+ ALL_STYLES (math_param_close_ord_spacing, 0);
+ ALL_STYLES (math_param_close_op_spacing, thin_mu_skip_code);
+ SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip_code, 0);
+ ALL_STYLES (math_param_close_open_spacing, 0);
+ ALL_STYLES (math_param_close_close_spacing, 0);
+ ALL_STYLES (math_param_close_punct_spacing, 0);
+ SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0);
+
+ SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip_code, 0);
+ ALL_STYLES (math_param_punct_bin_spacing, 0);
+ SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0);
+
+ SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip_code, 0);
+ ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip_code);
+ SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip_code, 0);
+ ALL_STYLES (math_param_inner_close_spacing, 0);
+ SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0);
+ SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0);
+}
+
+#define both_types(A,B) ((A)*16+(B))
+
+static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
+{
+ int x = -1;
+ pointer z = null;
+ if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits)
+ l_type = op_noad_type_normal;
+ if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits)
+ r_type = op_noad_type_normal;
+ switch (both_types(l_type, r_type)) {
+ case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break;
+ case both_types(ord_noad_type, op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break;
+ case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break;
+ case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break;
+ case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break;
+ case both_types(ord_noad_type, close_noad_type ): x = get_math_param(math_param_ord_close_spacing,mstyle); break;
+ case both_types(ord_noad_type, punct_noad_type ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break;
+ case both_types(ord_noad_type, inner_noad_type ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break;
+ case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break;
+ case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break;
+ /* shouldn't happen */
+ case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;
+ /* */
+ case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break;
+ case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break;
+ case both_types(op_noad_type_normal, close_noad_type ): x = get_math_param(math_param_op_close_spacing,mstyle); break;
+ case both_types(op_noad_type_normal, punct_noad_type ): x = get_math_param(math_param_op_punct_spacing,mstyle); break;
+ case both_types(op_noad_type_normal, inner_noad_type ): x = get_math_param(math_param_op_inner_spacing,mstyle); break;
+ case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break;
+ case both_types(bin_noad_type, op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break;
+ /* shouldn't happen */
+ case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break;
+ case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break;
+ /* */
+ case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break;
+ /* shouldn't happen */
+ case both_types(bin_noad_type, close_noad_type ): x = get_math_param(math_param_bin_close_spacing,mstyle); break;
+ case both_types(bin_noad_type, punct_noad_type ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break;
+ /* */
+ case both_types(bin_noad_type, inner_noad_type ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break;
+ case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break;
+ case both_types(rel_noad_type, op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break;
+ /* shouldn't happen */
+ case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
+ /* */
+ case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break;
+ case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break;
+ case both_types(rel_noad_type, close_noad_type ): x = get_math_param(math_param_rel_close_spacing,mstyle); break;
+ case both_types(rel_noad_type, punct_noad_type ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break;
+ case both_types(rel_noad_type, inner_noad_type ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break;
+ case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break;
+ case both_types(open_noad_type, op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break;
+ /* shouldn't happen */
+ case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;
+ /* */
+ case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break;
+ case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break;
+ case both_types(open_noad_type, close_noad_type ): x = get_math_param(math_param_open_close_spacing,mstyle); break;
+ case both_types(open_noad_type, punct_noad_type ): x = get_math_param(math_param_open_punct_spacing,mstyle); break;
+ case both_types(open_noad_type, inner_noad_type ): x = get_math_param(math_param_open_inner_spacing,mstyle); break;
+ case both_types(close_noad_type, ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break;
+ case both_types(close_noad_type, op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break;
+ case both_types(close_noad_type, bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break;
+ case both_types(close_noad_type, rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break;
+ case both_types(close_noad_type, open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break;
+ case both_types(close_noad_type, close_noad_type ): x = get_math_param(math_param_close_close_spacing,mstyle); break;
+ case both_types(close_noad_type, punct_noad_type ): x = get_math_param(math_param_close_punct_spacing,mstyle); break;
+ case both_types(close_noad_type, inner_noad_type ): x = get_math_param(math_param_close_inner_spacing,mstyle); break;
+ case both_types(punct_noad_type, ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break;
+ case both_types(punct_noad_type, op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break;
+ /* shouldn't happen */
+ case both_types(punct_noad_type, bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
+ /* */
+ case both_types(punct_noad_type, rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break;
+ case both_types(punct_noad_type, open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break;
+ case both_types(punct_noad_type, close_noad_type ): x = get_math_param(math_param_punct_close_spacing,mstyle); break;
+ case both_types(punct_noad_type, punct_noad_type ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break;
+ case both_types(punct_noad_type, inner_noad_type ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break;
+ case both_types(inner_noad_type, ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break;
+ case both_types(inner_noad_type, op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break;
+ case both_types(inner_noad_type, bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break;
+ case both_types(inner_noad_type, rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break;
+ case both_types(inner_noad_type, open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break;
+ case both_types(inner_noad_type, close_noad_type ): x = get_math_param(math_param_inner_close_spacing,mstyle); break;
+ case both_types(inner_noad_type, punct_noad_type ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break;
+ case both_types(inner_noad_type, inner_noad_type ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break;
+ }
+ if (x < 0) {
+ confusion("mathspacing");
+ }
+ if (x != 0) {
+ if (x <= thick_mu_skip_code) {
+ /*tex trap thin/med/thick settings cf.\ old \TeX */
+ z = math_glue(glue_par(x), mmu);
+ /*tex store a symbolic subtype */
+ subtype(z) = (quarterword) (x + 1);
+ } else {
+ z = math_glue(x, mmu);
+ }
+ }
+ return z;
+}
+
+static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same)
+{
+ pointer p = null;
+ pointer t = null;
+ if (same != NULL) {
+ *same = 0;
+ }
+ switch (type(nucleus(q))) {
+ case math_char_node:
+ case math_text_char_node:
+ fetch(nucleus(q));
+ if (char_exists(cur_f, cur_c)) {
+ /*tex we could look at neighbours */
+ if (do_new_math(cur_f)) {
+ /*tex cf spec only the last one */
+ *delta = 0 ;
+ } else {
+ *delta = char_italic(cur_f, cur_c);
+ }
+ p = new_glyph(cur_f, cur_c);
+ protect_glyph(p);
+ reset_attributes(p, node_attr(nucleus(q)));
+ if (do_new_math(cur_f)) {
+ if (get_char_cat_code(cur_c) == 11) {
+ /*tex no italic correction in mid-word of text font */
+ *delta = 0;
+ }
+ } else {
+ /*tex no italic correction in mid-word of text font */
+ if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) {
+ *delta = 0;
+ }
+ }
+ /*tex so we only add italic correction when we have no scripts */
+ if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) {
+ pointer x = new_kern(*delta);
+ subtype(x) = italic_kern;
+ reset_attributes(x, node_attr(nucleus(q)));
+ couple_nodes(p,x);
+ *delta = 0;
+ } else if (do_new_math(cur_f)) {
+ /*tex Needs checking but looks ok. It must be more selective. */
+ *delta = char_italic(cur_f, cur_c);
+ }
+ }
+ break;
+ case sub_box_node:
+ p = math_list(nucleus(q));
+ break;
+ case sub_mlist_node:
+ t = math_list(nucleus(q));
+ /*tex Recursive call: */
+ mlist_to_hlist(t, false, cur_style);
+ if (same != NULL && type(t) == fence_noad && delimitersamesize(t)) {
+ *same = delimitersamesize(t) ;
+ }
+ setup_cur_size(cur_style);
+ p = hpack(vlink(temp_head), 0, additional, -1);
+ reset_attributes(p, node_attr(nucleus(q)));
+ break;
+ default:
+ confusion("mlist2");
+ }
+ return p;
+}
+
+/*tex
+
+ Here is the overall plan of |mlist_to_hlist|, and the list of its local
+ variables.
+
+*/
+
+void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
+{
+ /*tex runs through the mlist */
+ pointer q = mlist;
+ /*tex the most recent noad preceding |q| */
+ pointer r = null;
+ /*tex tuck global parameter away as local variable */
+ int style = cur_style;
+ /*tex the |type| of noad |r|, or |op_noad| if |r=null| */
+ int r_type = simple_noad;
+ /*tex the |subtype| of noad |r| if |r_type| is |fence_noad| */
+ int r_subtype = op_noad_type_normal;
+ /*tex the effective |type| of noad |q| during the second pass */
+ int t;
+ /*tex the effective |subtype| of noad |q| during the second pass */
+ int t_subtype;
+ pointer p = null;
+ pointer pp = null;
+ pointer z = null;
+ halfword nxt ;
+ int same = 0;
+ /*tex a penalty to be inserted */
+ int pen;
+ /*tex a penalty to be inserted */
+ int prepen;
+ /*tex maximum height of the list translated so far */
+ scaled max_hl = 0;
+ /*tex maximum depth of the list translated so far */
+ scaled max_d = 0;
+ /*tex italic correction offset for subscript and superscript */
+ scaled delta;
+ /*tex the math unit width corresponding to |cur_size| */
+ scaled cur_mu;
+ r_subtype = op_noad_type_normal;
+ setup_cur_size(cur_style);
+ cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
+ if (math_penalties_mode_par) {
+ /*tex
+ We could do this via the callback but it's nice to have it as
+ primitive too.
+ */
+ penalties = 1;
+ }
+ while (q != null) {
+ /*tex
+
+ We use the fact that no character nodes appear in an mlist, hence the
+ field |type(q)| is always present.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.
+
+ */
+ RESWITCH:
+ delta = 0;
+ nxt = vlink(q);
+ switch (type(q)) {
+ case simple_noad:
+ switch (subtype(q)) {
+ case bin_noad_type:
+ switch (r_type) {
+ case simple_noad:
+ switch (r_subtype) {
+ case bin_noad_type:
+ case op_noad_type_normal:
+ case op_noad_type_limits:
+ case op_noad_type_no_limits:
+ case rel_noad_type:
+ case open_noad_type:
+ case punct_noad_type:
+ subtype(q) = ord_noad_type;
+ goto RESWITCH;
+ break;
+ }
+ break;
+ case fence_noad:
+ if (r_subtype == left_noad_side) {
+ /*tex So these can best be the same size. */
+ subtype(q) = ord_noad_type;
+ goto RESWITCH;
+ }
+ break;
+ }
+ break;
+ case over_noad_type:
+ make_over(q, cur_style, cur_size, math_rules_fam_par);
+ break;
+ case under_noad_type:
+ make_under(q, cur_style, cur_size, math_rules_fam_par);
+ break;
+ case vcenter_noad_type:
+ make_vcenter(q);
+ break;
+ case rel_noad_type:
+ case close_noad_type:
+ case punct_noad_type:
+ if (r_type == simple_noad && r_subtype == bin_noad_type) {
+ /*tex Assumes the same size; can't this go. */
+ type(r) = simple_noad;
+ subtype(r) = ord_noad_type;
+ }
+ break;
+ case op_noad_type_normal:
+ case op_noad_type_limits:
+ case op_noad_type_no_limits:
+ delta = make_op(q, cur_style);
+ if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits))
+ goto CHECK_DIMENSIONS;
+ break;
+ case ord_noad_type:
+ make_ord(q);
+ break;
+ case open_noad_type:
+ case inner_noad_type:
+ break;
+ }
+ break;
+ case fence_noad:
+ if (subtype(q) != left_noad_side) {
+ if (r_type == simple_noad && r_subtype == bin_noad_type) {
+ /*tex Assumes the same size. */
+ type(r) = simple_noad;
+ subtype(r) = ord_noad_type;
+ }
+ }
+ goto DONE_WITH_NOAD;
+ break;
+ case fraction_noad:
+ make_fraction(q, cur_style);
+ goto CHECK_DIMENSIONS;
+ break;
+ case radical_noad:
+ if (subtype(q) == 7)
+ make_hextension(q, cur_style);
+ else if (subtype(q) == 6)
+ make_delimiter_over(q, cur_style);
+ else if (subtype(q) == 5)
+ make_delimiter_under(q, cur_style);
+ else if (subtype(q) == 4)
+ make_over_delimiter(q, cur_style);
+ else if (subtype(q) == 3)
+ make_under_delimiter(q, cur_style);
+ else
+ make_radical(q, cur_style);
+ break;
+ case accent_noad:
+ make_math_accent(q, cur_style);
+ break;
+ case style_node:
+ cur_style = subtype(q);
+ setup_cur_size(cur_style);
+ cur_mu = x_over_n(get_math_quad_style(cur_style), 18);
+ goto DONE_WITH_NODE;
+ break;
+ case choice_node:
+ switch (cur_style / 2) {
+ case 0:
+ /*tex |display_style=0| */
+ choose_mlist(display_mlist);
+ break;
+ case 1:
+ /*tex |text_style=2| */
+ choose_mlist(text_mlist);
+ break;
+ case 2:
+ /*tex |script_style=4| */
+ choose_mlist(script_mlist);
+ break;
+ case 3:
+ /*tex |script_script_style=6| */
+ choose_mlist(script_script_mlist);
+ break;
+ }
+ 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) = (quarterword) cur_style;
+ if (p != null) {
+ z = vlink(q);
+ couple_nodes(q,p);
+ while (vlink(p) != null)
+ p = vlink(p);
+ try_couple_nodes(p,z);
+ }
+ goto DONE_WITH_NODE;
+ break;
+ case ins_node:
+ case mark_node:
+ case adjust_node:
+ case boundary_node:
+ case whatsit_node:
+ case penalty_node:
+ case disc_node:
+ goto DONE_WITH_NODE;
+ break;
+ case rule_node:
+ if (height(q) > max_hl)
+ max_hl = height(q);
+ if (depth(q) > max_d)
+ max_d = depth(q);
+ goto DONE_WITH_NODE;
+ break;
+ case glue_node:
+ /*tex
+
+ 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|.
+
+ */
+ if (subtype(q) == mu_glue) {
+ math_glue_to_glue(q, cur_mu);
+ } else if ((cur_size != text_size) && (subtype(q) == cond_math_glue)) {
+ p = vlink(q);
+ if (p != null)
+ if ((type(p) == glue_node) || (type(p) == kern_node)) {
+ if (vlink(p) != null) {
+ couple_nodes(q,vlink(p));
+ vlink(p) = null;
+ } else {
+ vlink(q) = null;
+ }
+ flush_node_list(p);
+ }
+ }
+ goto DONE_WITH_NODE;
+ break;
+ case kern_node:
+ math_kern(q, cur_mu);
+ goto DONE_WITH_NODE;
+ break;
+ default:
+ confusion("mlist1");
+ }
+ /*tex
+
+ 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.
+
+ */
+ same = 0 ;
+ p = check_nucleus_complexity(q, &delta, cur_style, &same);
+ if (same) {
+ noadextra4(q) = same ;
+ }
+ if ((subscr(q) == null) && (supscr(q) == null)) {
+ /*tex
+
+ Adding italic correction here is kind of fuzzy because some
+ characters already have that built in. However, we also add it in
+ the scripts so if it's optional here it also should be there.
+
+ */
+ if (nxt && (math_italics_mode_par > 0) && (delta != 0)) {
+ if (type(nxt) == simple_noad) {
+ switch (subtype(nxt)) {
+ case ord_noad_type:
+ case bin_noad_type:
+ case rel_noad_type:
+ case open_noad_type:
+ case close_noad_type:
+ case punct_noad_type:
+ delta = 0;
+ break;
+ case inner_noad_type:
+ if (! delimitermodeitalics) {
+ delta = 0;
+ }
+ break;
+ case op_noad_type_normal:
+ case op_noad_type_limits:
+ case op_noad_type_no_limits:
+ case under_noad_type:
+ case over_noad_type:
+ case vcenter_noad_type:
+ break;
+ default:
+ break;
+ }
+ }
+ if (delta != 0) {
+ pointer d = new_kern(delta);
+ subtype(d) = italic_kern;
+ reset_attributes(d, node_attr(q));
+ couple_nodes(p,d);
+ }
+ }
+ assign_new_hlist(q, p);
+ } else {
+ /*tex top, bottom */
+ make_scripts(q, p, delta, cur_style, 0, 0);
+ }
+ CHECK_DIMENSIONS:
+ z = hpack(new_hlist(q), 0, additional, -1);
+ if (height(z) > max_hl)
+ max_hl = height(z);
+ if (depth(z) > max_d)
+ max_d = depth(z);
+ list_ptr(z) = null;
+ /*tex only drop the \.{\\hbox} */
+ flush_node(z);
+ DONE_WITH_NOAD:
+ r = q;
+ r_type = type(r);
+ r_subtype = subtype(r);
+ if (r_type == fence_noad) {
+ r_subtype = left_noad_side;
+ cur_style = style;
+ setup_cur_size(cur_style);
+ /*tex style */
+ cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
+ }
+ DONE_WITH_NODE:
+ q = vlink(q);
+ }
+ if (r_type == simple_noad && r_subtype == bin_noad_type) {
+ type(r) = simple_noad;
+ subtype(r) = ord_noad_type;
+ }
+ /*tex
+
+ Make a second pass over the mlist, removing all noads and inserting the
+ proper spacing and penalties.
+
+ 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 |fence_noad|s that might be present, since |max_hl| and |max_d| are
+ now known. Variable |p| points to a node at the current end of the final
+ hlist.
+
+ */
+ p = temp_head;
+ vlink(p) = null;
+ q = mlist;
+ r_type = 0;
+ r_subtype = 0;
+ cur_style = style;
+ setup_cur_size(cur_style);
+ cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
+ NEXT_NODE:
+ while (q != null) {
+ /*tex
+
+ 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.
+
+ 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.
+
+ */
+ t = simple_noad;
+ t_subtype = ord_noad_type;
+ pen = inf_penalty;
+ prepen = inf_penalty;
+ switch (type(q)) {
+ case simple_noad:
+ t_subtype = subtype(q);
+ switch (t_subtype) {
+ case bin_noad_type:
+ pen = bin_op_penalty_par;
+ prepen = pre_bin_op_penalty_par;
+ break;
+ case rel_noad_type:
+ pen = rel_penalty_par;
+ prepen = pre_rel_penalty_par;
+ break;
+ case vcenter_noad_type:
+ case over_noad_type:
+ case under_noad_type:
+ t_subtype = ord_noad_type;
+ break;
+ }
+ case radical_noad:
+ break;
+ case accent_noad:
+ break;
+ case fraction_noad:
+ t = simple_noad;
+ t_subtype = inner_noad_type;
+ break;
+ case fence_noad:
+ t_subtype = make_left_right(q, style, max_d, max_hl);
+ break;
+ case style_node:
+ /*tex Change the current style and |goto delete_q| */
+ cur_style = subtype(q);
+ setup_cur_size(cur_style);
+ cur_mu = x_over_n(get_math_quad_style(cur_style), 18);
+ goto DELETE_Q;
+ break;
+ 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:
+ couple_nodes(p,q);
+ p = q;
+ q = vlink(q);
+ vlink(p) = null;
+ goto NEXT_NODE;
+ break;
+ default:
+ confusion("mlist3");
+ }
+ /*tex Append inter-element spacing based on |r_type| and |t| */
+ if (r_type > 0) {
+ /*tex not the first noad */
+ pp = p;
+ if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1) {
+ z = math_spacing_glue(r_subtype, ord_noad_type, cur_style, cur_mu);
+ } else {
+ z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu);
+ }
+ if (z != null) {
+ reset_attributes(z, node_attr(p));
+ couple_nodes(p,z);
+ p = z;
+ }
+ if (penalties && prepen < inf_penalty && type(pp) != penalty_node) {
+ /*tex no checking of prev node type */
+ z = new_penalty(prepen,noad_penalty);
+ reset_attributes(z, node_attr(p));
+ couple_nodes(p,z);
+ p = z;
+ }
+ }
+ /*tex
+
+ Append any |new_hlist| entries for |q|, and any appropriate
+ penalties. 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.
+
+ */
+ if (new_hlist(q) != null) {
+ couple_nodes(p,new_hlist(q));
+ do {
+ p = vlink(p);
+ } while (vlink(p) != null);
+ }
+ if (penalties && vlink(q) != null && pen < inf_penalty) {
+ r_type = type(vlink(q));
+ r_subtype = subtype(vlink(q));
+ if (r_type != penalty_node && (r_type != simple_noad || r_subtype != rel_noad_type)) {
+ z = new_penalty(pen,noad_penalty);
+ reset_attributes(z, node_attr(q));
+ couple_nodes(p,z);
+ p = z;
+ }
+ }
+ if (type(q) == fence_noad && subtype(q) == right_noad_side) {
+ t = simple_noad;
+ t_subtype = open_noad_type;
+ }
+ r_type = t;
+ r_subtype = t_subtype;
+ DELETE_Q:
+ r = q;
+ q = vlink(q);
+ /*tex
+
+ The m-to-hlist conversion takes place in-place, so the various
+ dependant fields may not be freed (as would happen if |flush_node|
+ was called). A low-level |free_node| is easier than attempting to
+ nullify such dependant fields for all possible node and noad types.
+
+ */
+ if (nodetype_has_attributes(type(r))) {
+ delete_attribute_ref(node_attr(r));
+ }
+ reset_node_properties(r);
+ free_node(r, get_node_size(type(r), subtype(r)));
+ }
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.h b/Build/source/texk/web2c/luatexdir/tex/mlist.h
new file mode 100644
index 00000000000..dc08e0cdd6f
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/mlist.h
@@ -0,0 +1,41 @@
+/* mlist.h
+
+ Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
+ Copyright 2006-2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef MLIST_H
+# define MLIST_H 1
+
+extern int cur_size;
+
+extern halfword del_width;
+extern halfword del_height;
+extern halfword del_depth;
+extern halfword del_shift;
+
+extern void run_mlist_to_hlist(halfword, boolean, int);
+extern void mlist_to_hlist(halfword, boolean, int);
+extern void fixup_math_parameters(int fam_id, int size_id, int f, int lvl);
+
+extern scaled get_math_quad_style(int a);
+extern scaled get_math_quad_size(int a);
+
+extern pointer make_extensible(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.c b/Build/source/texk/web2c/luatexdir/tex/nesting.c
new file mode 100644
index 00000000000..bbad50b3cdb
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/nesting.c
@@ -0,0 +1,429 @@
+/*
+
+nesting.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex These are for |show_activities|: */
+
+#define page_goal page_so_far[0]
+
+/*tex
+
+\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:
+
+|vmode| stands for vertical mode (the page builder);
+
+|hmode| stands for horizontal mode (the paragraph builder);
+
+|mmode| stands for displayed formula mode;
+
+|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox});
+
+|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox});
+
+|-mmode| stands for math formula mode (not displayed).
+
+The mode is temporarily set to zero while processing \.{\\write} texts in the
+|ship_out| routine.
+
+Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that \TeX's ``big
+semantic switch'' can select the appropriate thing to do by computing the value
+|abs(mode)+cur_cmd|, where |mode| is the current mode and |cur_cmd| is the
+current command code.
+
+*/
+
+static const char *string_mode(int m)
+{
+ if (m > 0) {
+ switch (m / (max_command_cmd + 1)) {
+ case 0:
+ return "vertical mode";
+ break;
+ case 1:
+ return "horizontal mode";
+ break;
+ case 2:
+ return "display math mode";
+ break;
+ default:
+ break;
+ }
+ } else if (m == 0) {
+ return "no mode";
+ } else {
+ switch ((-m) / (max_command_cmd + 1)) {
+ case 0:
+ return "internal vertical mode";
+ break;
+ case 1:
+ return "restricted horizontal mode";
+ break;
+ case 2:
+ return "math mode";
+ break;
+ default:
+ break;
+ }
+ }
+ return "unknown mode";
+}
+
+void print_mode(int m)
+{
+ tprint(string_mode(m));
+}
+
+void print_in_mode(int m)
+{
+ tprint("' in ");
+ tprint(string_mode(m));
+}
+
+int get_mode_id(void)
+{
+ int m = cur_list.mode_field;
+ if (m > 0) {
+ switch (m / (max_command_cmd + 1)) {
+ case 0:
+ return 'v';
+ break;
+ case 1:
+ return 'h';
+ break;
+ case 2:
+ return 'm';
+ break;
+ default:
+ return '\0';
+ break;
+ }
+ } else if (m == 0) {
+ return 'n';;
+ } else {
+ switch ((-m) / (max_command_cmd + 1)) {
+ case 0:
+ return 'V';
+ break;
+ case 1:
+ return 'H';
+ break;
+ case 2:
+ return 'M';
+ break;
+ default:
+ return '\0';
+ break;
+ }
+ }
+}
+
+/*tex
+
+The state of affairs at any semantic level can be represented by five values:
+
+|mode| is the number representing the semantic mode, as just explained.
+
+|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.
+
+|tail| is a |pointer| to the final node of the list being built; thus,
+|tail=head| if and only if the list is empty.
+
+|prev_graf| is the number of lines of the current paragraph that have already
+been put into the present vertical list.
+
+|aux| is an auxiliary |memory_word| that gives further information that is needed
+to characterize the situation.
+
+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|;
+it holds the current space factor used in spacing calculations. 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 math
+mode it is known as |delim_ptr| and points to the most recent |fence_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 struct that is ready to be pushed onto |nest| if
+necessary.
+
+The math field is used by various bits and pieces in |texmath.w|
+
+This implementation of \TeX\ uses two different conventions for representing
+sequential stacks. @^stack conventions@>@^conventions for representing stacks@>
+
+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)]$. The semantic stack is handled this way.
+
+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.
+
+In |nest_ptr| we have the first unused location of |nest|, and |max_nest_stack|
+has the maximum of |nest_ptr| when pushing. In |shown_mode| we store the most
+recent mode shown by \.{\\tracingcommands} and with |save_tail| we can examine
+whether we have an auto kern before a glue.
+
+*/
+
+list_state_record *nest;
+int nest_ptr, max_nest_stack, shown_mode;
+halfword save_tail;
+
+/*tex
+
+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.
+
+*/
+
+void initialize_nesting(void)
+{
+ nest_ptr = 0;
+ max_nest_stack = 0;
+ shown_mode = 0;
+ cur_list.mode_field = vmode;
+ cur_list.head_field = contrib_head;
+ cur_list.tail_field = contrib_head;
+ cur_list.eTeX_aux_field = null;
+ cur_list.prev_depth_field = ignore_depth;
+ cur_list.space_factor_field = 1000;
+ cur_list.incompleat_noad_field = null;
+ cur_list.ml_field = 0;
+ cur_list.pg_field = 0;
+ cur_list.dirs_field = null;
+ init_math_fields();
+}
+
+/*tex
+
+Here is a common way to make the current list grow:
+
+*/
+
+void tail_append(halfword p)
+{
+ couple_nodes(cur_list.tail_field, p);
+ cur_list.tail_field = vlink(cur_list.tail_field);
+}
+
+halfword pop_tail(void)
+{
+ halfword n, r;
+ if (cur_list.tail_field != cur_list.head_field) {
+ r = cur_list.tail_field;
+ if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) {
+ n = alink(cur_list.tail_field);
+ } else {
+ n = cur_list.head_field;
+ while (vlink(n) != cur_list.tail_field)
+ n = vlink(n);
+ }
+ cur_list.tail_field = n;
+ alink(r) = null;
+ vlink(n) = null;
+ return r;
+ } else {
+ return null;
+ }
+}
+
+/*tex
+
+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|.
+
+*/
+
+void push_nest(void)
+{
+ if (nest_ptr > max_nest_stack) {
+ max_nest_stack = nest_ptr;
+ if (nest_ptr == nest_size)
+ overflow("semantic nest size", (unsigned) nest_size);
+ }
+ incr(nest_ptr);
+ cur_list.mode_field = nest[nest_ptr - 1].mode_field;
+ cur_list.head_field = new_node(temp_node, 0);
+ cur_list.tail_field = cur_list.head_field;
+ cur_list.eTeX_aux_field = null;
+ cur_list.ml_field = line;
+ cur_list.pg_field = 0;
+ cur_list.dirs_field = null;
+ cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field;
+ cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field;
+ cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field;
+ init_math_fields();
+}
+
+/*tex
+
+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.
+
+*/
+
+void pop_nest(void)
+{
+ flush_node(cur_list.head_field);
+ decr(nest_ptr);
+}
+
+/*tex
+
+Here is a procedure that displays what \TeX\ is working on, at all levels.
+
+*/
+
+void show_activities(void)
+{
+ /*tex Index into |nest|: */
+ int p;
+ /*tex The mode: */
+ int m;
+ /*tex For showing the current page: */
+ halfword q, r;
+ /*tex Ditto: */
+ int t;
+ tprint_nl("");
+ print_ln();
+ for (p = nest_ptr; p >= 0; p--) {
+ m = nest[p].mode_field;
+ tprint_nl("### ");
+ print_mode(m);
+ tprint(" entered at line ");
+ print_int(abs(nest[p].ml_field));
+ if (nest[p].ml_field < 0)
+ tprint(" (\\output routine)");
+ if (p == 0) {
+ /*tex Show the status of the current page */
+ if (page_head != page_tail) {
+ tprint_nl("### current page:");
+ if (output_active)
+ tprint(" (held over for next output)");
+ show_box(vlink(page_head));
+ if (page_contents > empty) {
+ tprint_nl("total height ");
+ print_totals();
+ tprint_nl(" goal height ");
+ print_scaled(page_goal);
+ r = vlink(page_ins_head);
+ while (r != page_ins_head) {
+ print_ln();
+ tprint_esc("insert");
+ t = subtype(r);
+ print_int(t);
+ tprint(" 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_node) {
+ q = page_head;
+ t = 0;
+ do {
+ q = vlink(q);
+ if ((type(q) == ins_node)
+ && (subtype(q) == subtype(r)))
+ incr(t);
+ } while (q != broken_ins(r));
+ tprint(", #");
+ print_int(t);
+ tprint(" might split");
+ }
+ r = vlink(r);
+ }
+ }
+ }
+ if (vlink(contrib_head) != null)
+ tprint_nl("### recent contributions:");
+ }
+ show_box(vlink(nest[p].head_field));
+ /*tex Show the auxiliary field, |a|. */
+ switch (abs(m) / (max_command_cmd + 1)) {
+ case 0:
+ tprint_nl("prevdepth ");
+ if (nest[p].prev_depth_field <= ignore_depth)
+ tprint("ignored");
+ else
+ print_scaled(nest[p].prev_depth_field);
+ if (nest[p].pg_field != 0) {
+ tprint(", prevgraf ");
+ print_int(nest[p].pg_field);
+ if (nest[p].pg_field != 1)
+ tprint(" lines");
+ else
+ tprint(" line");
+ }
+ break;
+ case 1:
+ tprint_nl("spacefactor ");
+ print_int(nest[p].space_factor_field);
+ break;
+ case 2:
+ if (nest[p].incompleat_noad_field != null) {
+ tprint("this will be denominator of:");
+ show_box(nest[p].incompleat_noad_field);
+ }
+ break;
+ }
+ }
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.h b/Build/source/texk/web2c/luatexdir/tex/nesting.h
new file mode 100644
index 00000000000..238522a77d1
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/nesting.h
@@ -0,0 +1,70 @@
+/* nesting.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef NESTING_H
+# define NESTING_H
+
+# define vmode 1 /* vertical mode */
+# define hmode (vmode+max_command_cmd+1)
+ /* horizontal mode */
+# define mmode (hmode+max_command_cmd+1)
+ /* math mode */
+
+extern void print_mode(int m);
+extern void print_in_mode(int m);
+extern int get_mode_id(void);
+
+# define ignore_depth -65536000 /* magic dimension value to mean `ignore me' */
+
+typedef struct list_state_record_ {
+ int mode_field;
+ halfword head_field;
+ halfword tail_field;
+ halfword eTeX_aux_field;
+ int pg_field;
+ int ml_field;
+ halfword prev_depth_field;
+ halfword space_factor_field;
+ halfword incompleat_noad_field;
+ halfword dirs_field;
+ int math_field;
+ int math_style_field;
+} list_state_record;
+
+extern list_state_record *nest;
+extern int nest_ptr;
+extern int max_nest_stack;
+# define cur_list nest[nest_ptr]
+ /* the ``top'' semantic state */
+extern int shown_mode;
+extern halfword save_tail;
+
+extern void push_nest(void);
+extern void pop_nest(void);
+extern void initialize_nesting(void);
+
+extern void tail_append(halfword p);
+extern halfword pop_tail(void);
+
+
+extern void show_activities(void);
+
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/packaging.c b/Build/source/texk/web2c/luatexdir/tex/packaging.c
new file mode 100644
index 00000000000..3d23c8b7320
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/packaging.c
@@ -0,0 +1,2145 @@
+/*
+
+Copyright 2009-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation;
+either version 2 of the License, or (at your option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+ 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|.
+
+ 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-1;|\cr |hpack(p,saved_value(0),saved_level(0)).|\cr}}$$
+
+*/
+
+/*tex Scan a box specification and left brace: */
+
+void scan_spec(group_code c)
+{
+ int spec_code;
+ boolean done = false ;
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (cur_cmd == left_brace_cmd) {
+ spec_code = additional;
+ cur_val = 0;
+ done = true;
+ } else {
+ /*tex todo: attr */
+ back_input();
+ if (scan_keyword("to")) {
+ spec_code = exactly;
+ scan_normal_dimen();
+ } else if (scan_keyword("spread")) {
+ spec_code = additional;
+ scan_normal_dimen();
+ } else {
+ spec_code = additional;
+ cur_val = 0;
+ }
+ }
+ set_saved_record(0, saved_boxspec, spec_code, cur_val);
+ save_ptr++;
+ new_save_level(c);
+ if (!done) {
+ scan_left_brace();
+ }
+}
+
+/*tex
+
+ When scanning, 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.
+
+ This coincides with the text on |dir| and |attr| keywords, as these are
+ exaclty the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the input
+ stream (the others are \.{\\vcenter}, \.{\\valign}, and \.{\\halign}).
+
+*/
+
+/*tex Scan a box specification and left brace: */
+
+#define first_char_is(a,A) (cur_cs == 0 && (cur_chr == a || cur_chr == A))
+
+void scan_full_spec(group_code c, int spec_direction, int just_pack)
+{
+ int s, i, v, spec_code;
+ boolean done = false ;
+ halfword attr_list;
+ boolean attr_done = false ;
+ boolean dir_done = false ;
+ if (attr_list_cache == cache_disabled)
+ update_attribute_cache();
+ attr_list = attr_list_cache;
+ /*tex The box context: */
+ s = saved_value(0);
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (cur_cmd == left_brace_cmd) {
+ goto QUICK;
+ } else {
+ back_input();
+ goto KEYWORDS;
+ }
+ CONTINUE:
+ while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
+ get_x_token();
+ if (cur_cmd == left_brace_cmd) {
+ goto QUICK;
+ } else if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd) {
+ back_input();
+ break;
+ }
+ }
+ KEYWORDS:
+ /*tex Multiple |attr| keys possible (before or after dir). */
+ if (scan_keyword("attr")) {
+ scan_register_num();
+ i = cur_val;
+ scan_optional_equals();
+ scan_int();
+ v = cur_val;
+ if (! attr_done) {
+ attr_list = copy_attribute_list(attr_list_cache);
+ attr_done = true;
+ }
+ attr_list = do_set_attribute(attr_list, i, v);
+ goto CONTINUE;
+ }
+ /*tex We only permit one |(b)dir| directive. */
+ if (! dir_done) {
+ if (scan_keyword("bdir")) {
+ scan_int();
+ check_dir_value(cur_val);
+ spec_direction = cur_val;
+ dir_done = true;
+ goto CONTINUE;
+ }
+ if (scan_keyword("dir")) {
+ scan_direction();
+ spec_direction = cur_val;
+ dir_done = true;
+ goto CONTINUE;
+ }
+ }
+ /*tex Only one |to| or |spread| key possible and it comes last. */
+ if (scan_keyword("to")) {
+ spec_code = exactly;
+ } else if (scan_keyword("spread")) {
+ spec_code = additional;
+ } else {
+ spec_code = additional;
+ cur_val = 0;
+ goto FOUND;
+ }
+ scan_normal_dimen();
+ goto FOUND;
+ QUICK:
+ spec_code = additional;
+ cur_val = 0;
+ done = true;
+ FOUND:
+ add_node_attr_ref(attr_list);
+ set_saved_record(0, saved_boxcontext, 0, s);
+ set_saved_record(1, saved_boxspec, spec_code, cur_val);
+ /*tex Adjust |text_dir_ptr| for |scan_spec|. */
+ if (spec_direction != -1) {
+ set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr);
+ text_dir_ptr = new_dir(spec_direction);
+ } else {
+ set_saved_record(2, saved_boxdir, spec_direction, null);
+ }
+ set_saved_record(3, saved_boxattr, 0, attr_list);
+ set_saved_record(4, saved_boxpack, 0, just_pack);
+ save_ptr += 5;
+ new_save_level(c);
+ if (! done) {
+ scan_left_brace();
+ }
+ /*tex No gain in |if (body_direction_par != spec_direction)| etc. */
+ eq_word_define(int_base + body_direction_code, spec_direction);
+ eq_word_define(int_base + par_direction_code, spec_direction);
+ eq_word_define(int_base + text_direction_code, spec_direction);
+}
+
+/*tex
+
+ 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}.
+
+*/
+
+/*tex Glue found by |hpack| or |vpack|. */
+
+scaled total_stretch[5];
+scaled total_shrink[5];
+
+/*tex Badness of the most recently packaged box. */
+
+int last_badness;
+
+/*tex
+
+ 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|.
+
+*/
+
+/*tex Tail of adjustment list. */
+
+halfword adjust_tail;
+
+/*tex
+
+ Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to
+ |pre_adjust_tail| instead of |adjust_tail|.
+
+*/
+
+halfword pre_adjust_tail;
+
+halfword last_leftmost_char;
+halfword last_rightmost_char;
+
+/*tex Pointers to the prev and next char of an implicit kern. */
+
+halfword next_char_p;
+halfword prev_char_p;
+
+/*tex This procedure is called repeatedly from inside the line break algorithm. */
+
+void set_prev_char_p(halfword p)
+{
+ prev_char_p = p;
+}
+
+/*tex
+
+ The kern stretch / shrink code was (or had become) rather weird ... the width
+ field is set, and then used in a second calculation, repeatedly, so why is
+ that ... maybe some some weird left-over ... anyway, the values are so small
+ that in practice they are not significant at all when the backend sees them
+ because a few hundred sp positive or negative are just noise there (so
+ adjustlevel 3 has hardly any consequence for the result but is more
+ efficient).
+
+*/
+
+scaled char_stretch(halfword p)
+{
+ internal_font_number f = font(p);
+ int m = font_max_stretch(f);
+ if (m > 0) {
+ int c = character(p);
+ int ef = get_ef_code(f, c);
+ if (ef > 0) {
+ scaled dw = calc_char_width(f, c, m) - char_width(f, c);
+ if (dw > 0) {
+ return round_xn_over_d(dw, ef, 1000);
+ }
+ }
+ }
+ return 0;
+}
+
+scaled char_shrink(halfword p)
+{
+ internal_font_number f = font(p);
+ int m = font_max_shrink(f);
+ if (m > 0) {
+ int c = character(p);
+ int ef = get_ef_code(f, c);
+ if (ef > 0) {
+ scaled dw = char_width(f, c) - calc_char_width(f, c, -m);
+ if (dw > 0) {
+ return round_xn_over_d(dw, ef, 1000);
+ }
+ }
+ }
+ return 0;
+}
+
+scaled kern_stretch(halfword p)
+{
+ int m;
+ scaled d, e, x;
+ scaled w = width(p) ;
+ halfword l;
+ halfword r;
+ if (w == 0) {
+ /*tex Why bother about zero kerns. */
+ return 0;
+ }
+ l = prev_char_p ;
+ if ((l == null) || (vlink(l) != p)) {
+ /*tex We only care about kerns following a char. */
+ return 0;
+ }
+ r = vlink(p);
+ if (r == null) {
+ /*tex We only care about kerns between a char and something else. */
+ }
+ if (!(is_char_node(l) && is_char_node(r))) {
+ /*tex We want two chars (but but don't care about the fonts). */
+ return 0;
+ }
+ /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */
+ m = (font_max_stretch(font(l)) + font_max_stretch(font(r)))/2;
+ if (m == 0) {
+ /*tex nothing to kern */
+ return 0;
+ }
+ d = round_xn_over_d(w, 1000 + m, 1000);
+ /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */
+ e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ;
+ if (e == 1000) {
+ x = d - w;
+ } else {
+ x = round_xn_over_d(d - w, e, 1000);
+ }
+ return x;
+}
+
+scaled kern_shrink(halfword p)
+{
+ int m;
+ scaled d, e, x;
+ scaled w = width(p) ;
+ halfword l;
+ halfword r;
+ if (w == 0) {
+ /*tex Why bother about zero kerns. */
+ return 0;
+ }
+ l = prev_char_p ;
+ if ((l == null) || (vlink(l) != p)) {
+ /*tex We only care about kerns following a char. */
+ return 0;
+ }
+ r = vlink(p);
+ if (r == null) {
+ /*tex We only care about kerns between a char and something else. */
+ }
+ if (!(is_char_node(l) && is_char_node(r))) {
+ /*tex We want two chars (but but don't care about the fonts). */
+ return 0;
+ }
+ /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */
+ m = (font_max_shrink(font(l)) + font_max_shrink(font(r)))/2;
+ if (m == 0) {
+ /*tex Nothing to kern. */
+ return 0;
+ }
+ d = round_xn_over_d(w, 1000 - m, 1000);
+ e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ;
+ if (e == 1000) {
+ x = w - d ;
+ } else {
+ x = round_xn_over_d(w - d, e, 1000);
+ }
+ return x;
+}
+
+void do_subst_font(halfword p, int ex_ratio)
+{
+ if (type(p) == disc_node) {
+ halfword r = vlink(pre_break(p));
+ while (r != null) {
+ if (is_char_node(r))
+ do_subst_font(r, ex_ratio);
+ r = vlink(r);
+ }
+ r = vlink(post_break(p));
+ while (r != null) {
+ if (is_char_node(r))
+ do_subst_font(r, ex_ratio);
+ r = vlink(r);
+ }
+ r = vlink(no_break(p));
+ while (r != null) {
+ if (is_char_node(r))
+ do_subst_font(r, ex_ratio);
+ r = vlink(r);
+ }
+ return;
+ }
+ if (! is_char_node(p)) {
+ normal_error("font expansion", "invalid node type");
+ return;
+ } else {
+ internal_font_number f = font(p);
+ int ef = get_ef_code(f, character(p));
+ if (ef == 0)
+ return;
+ if ((font_max_stretch(f) > 0) && (ex_ratio > 0)) {
+ int ex_stretch = ext_xn_over_d(ex_ratio * ef, font_max_stretch(f), 1000000);
+ ex_glyph(p) = fix_expand_value(f, ex_stretch)*1000;
+ } else if ((font_max_shrink(f) > 0) && (ex_ratio < 0)) {
+ int ex_shrink = ext_xn_over_d(ex_ratio * ef, font_max_shrink(f), 1000000);
+ ex_glyph(p) = fix_expand_value(f, ex_shrink)*1000;
+ }
+ }
+}
+
+scaled char_pw(halfword p, int side)
+{
+ internal_font_number f;
+ int c, w;
+ if (side == left_side)
+ last_leftmost_char = null;
+ else
+ last_rightmost_char = null;
+ if (p == null)
+ return 0;
+ if (!is_char_node(p))
+ return 0;
+ f = font(p);
+ if (side == left_side) {
+ c = get_lp_code(f, character(p));
+ last_leftmost_char = p;
+ } else {
+ c = get_rp_code(f, character(p));
+ last_rightmost_char = p;
+ }
+ if (c == 0)
+ return 0;
+ w = quad(f);
+ return round_xn_over_d(w, c, 1000);
+}
+
+halfword new_margin_kern(scaled w, halfword p, int side)
+{
+ halfword k, q;
+ k = new_node(margin_kern_node, side);
+ width(k) = w;
+ if (p == null)
+ normal_error("margin kerning", "invalid pointer to marginal char node");
+ q = new_char(font(p), character(p));
+ margin_char(k) = q;
+ return k;
+}
+
+/*tex
+
+ Here we prepare for |hpack|, which is place where we do font substituting
+ when font expansion is being used.
+
+*/
+
+/*tex The current expansion ratio, needed for recursive call. */
+
+int font_expand_ratio = 0;
+
+int ignore_math_skip(halfword p)
+{
+ if (math_skip_mode == 6) {
+ if (subtype(p) == after) {
+ if (math_skip_boundary(vlink(p))) {
+ return 0;
+ }
+ } else {
+ if (math_skip_boundary(alink(p))) {
+ return 0;
+ }
+ }
+ } else if (math_skip_mode == 7) {
+ if (subtype(p) == after) {
+ if (! math_skip_boundary(vlink(p))) {
+ return 0;
+ }
+ } else {
+ if (! math_skip_boundary(alink(p))) {
+ return 0;
+ }
+ }
+ } else {
+ return 0;
+ }
+ reset_glue_to_zero(p);
+ return 1;
+}
+
+halfword hpack(halfword p, scaled w, int m, int pack_direction)
+{
+ /*tex the box node that will be returned */
+ halfword r;
+ /*tex trails behind |p| */
+ halfword q;
+ /*tex height */
+ scaled h = 0;
+ /*tex depth */
+ scaled d = 0;
+ /*tex natural width */
+ scaled x = 0;
+ scaled_whd whd;
+ /*tex shift amount */
+ scaled s;
+ /*tex order of infinity */
+ int o;
+ /*tex for managing the direction stack */
+ halfword dir_ptr1 = null;
+ /*tex the current direction */
+ int hpack_dir;
+ int disc_level = 0;
+ halfword pack_interrupt[8];
+ scaled font_stretch = 0;
+ scaled font_shrink = 0;
+ int adjust_spacing = adjust_spacing_par;
+ last_badness = 0;
+ /*tex the box node that will be returned */
+ r = new_node(hlist_node, min_quarterword);
+ if (pack_direction == -1) {
+ hpack_dir = text_direction_par;
+ } else {
+ hpack_dir = pack_direction;
+ }
+ box_dir(r) = hpack_dir;
+ /*tex
+
+ A potential optimimization, save a little but neglectable in practice
+ (not so many empty boxes are used):
+
+ \starttyping
+ if (p == null) {
+ width(r) = w;
+ return r;
+ }
+ \stoptyping
+
+ */
+ /*tex push null */
+ push_dir(dir_ptr1,hpack_dir);
+ /*tex hm, adding something to a node address? */
+ q = r + list_offset;
+ vlink(q) = p;
+ if (m == cal_expand_ratio) {
+ /*tex Why not always: */
+ prev_char_p = null;
+ }
+ if (adjust_spacing > 2) {
+ adjust_spacing = 0;
+ }
+ total_stretch[normal] = 0;
+ total_shrink[normal] = 0;
+ total_stretch[sfi] = 0;
+ total_shrink[sfi] = 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;
+ RESWITCH:
+ while ((p != null) || (disc_level > 0)) {
+ if (p == null) {
+ decr(disc_level);
+ p = pack_interrupt[disc_level];
+ goto RESWITCH;
+ }
+ /*tex
+
+ 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.
+
+ */
+ while (is_char_node(p)) {
+ /*tex
+
+ Incorporate character dimensions into the dimensions of the hbox
+ that will contain~it, then move to the next node.
+
+ 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.
+
+ */
+ if (m >= cal_expand_ratio) {
+ prev_char_p = p;
+ if (m == cal_expand_ratio) {
+ font_stretch += char_stretch(p);
+ font_shrink += char_shrink(p);
+ } else if (m == subst_ex_font) {
+ do_subst_font(p, font_expand_ratio);
+ }
+ }
+ whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
+ x += whd.wd;
+ if (whd.ht > h)
+ h = whd.ht;
+ if (whd.dp > d)
+ d = whd.dp;
+ p = vlink(p);
+ }
+ if (p != null) {
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ /*tex
+
+ Incorporate box dimensions into the dimensions of the
+ hbox that will contain~it
+
+ 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.
+
+ */
+ s = shift_amount(p);
+ whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
+ x += whd.wd;
+ if (whd.ht - s > h)
+ h = whd.ht - s;
+ if (whd.dp + s > d)
+ d = whd.dp + s;
+ break;
+ case rule_node:
+ case unset_node:
+ x += width(p);
+ if (height(p) > h)
+ h = height(p);
+ if (depth(p) > d)
+ d = depth(p);
+ break;
+ case math_node:
+ /*tex Begin mathskip code. */
+ if (glue_is_zero(p) || ignore_math_skip(p)) {
+ x += surround(p);
+ break;
+ } else {
+ /*tex Fall through. */
+ }
+ /*tex End mathskip code. */
+ case glue_node:
+ /*tex Incorporate glue into the horizontal totals. */
+ x += width(p);
+ o = stretch_order(p);
+ total_stretch[o] = total_stretch[o] + stretch(p);
+ o = shrink_order(p);
+ total_shrink[o] = total_shrink[o] + shrink(p);
+ if (subtype(p) >= a_leaders) {
+ halfword g = leader_ptr(p);
+ if (height(g) > h)
+ h = height(g);
+ if (depth(g) > d)
+ d = depth(g);
+ }
+ break;
+ case kern_node:
+ x += width(p);
+ if (subtype(p) == font_kern && adjust_spacing) {
+ /*tex So only when 1 or 2. */
+ if (m == cal_expand_ratio) {
+ font_stretch = font_stretch + kern_stretch(p);
+ font_shrink = font_shrink + kern_shrink(p);
+ } else if (m == subst_ex_font) {
+ /*tex This is the finalizer. */
+ int k = 0;
+ if (font_expand_ratio > 0) {
+ k = kern_stretch(p);
+ } else if (font_expand_ratio < 0) {
+ k = kern_shrink(p);
+ }
+ ex_kern(p) = k;
+ x += k;
+ }
+ }
+ break;
+ case disc_node:
+ if (m == subst_ex_font)
+ do_subst_font(p, font_expand_ratio);
+ if ((subtype(p) != select_disc) && (vlink(no_break(p)) != null)) {
+ pack_interrupt[disc_level] = vlink(p);
+ incr(disc_level);
+ p = no_break(p);
+ }
+ break;
+ case dir_node:
+ /*tex Adjust the dir stack for the |hpack| routine. */
+ if (subtype(p) == normal_dir) {
+ hpack_dir = dir_dir(p);
+ push_dir_node(dir_ptr1,p);
+ } else {
+ pop_dir_node(dir_ptr1);
+ if (dir_ptr1 != null)
+ hpack_dir = dir_dir(dir_ptr1);
+ }
+ break;
+ case margin_kern_node:
+ if (m == cal_expand_ratio) {
+ int f = font(margin_char(p));
+ do_subst_font(margin_char(p), 1000);
+ if (f != font(margin_char(p)))
+ font_stretch = font_stretch - width(p) - char_pw(margin_char(p), subtype(p));
+ font(margin_char(p)) = f;
+ do_subst_font(margin_char(p), -1000);
+ if (f != font(margin_char(p)))
+ font_shrink = font_shrink - width(p) - char_pw(margin_char(p), subtype(p));
+ font(margin_char(p)) = f;
+ } else if (m == subst_ex_font) {
+ do_subst_font(margin_char(p), font_expand_ratio);
+ width(p) = -char_pw(margin_char(p), subtype(p));
+ }
+ x += width(p);
+ break;
+ case ins_node:
+ case mark_node:
+ case adjust_node:
+ /*tex
+
+ Transfer node |p| to the adjustment list.\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.
+
+ */
+ if (adjust_tail != null || pre_adjust_tail != null) {
+ while (vlink(q) != p)
+ q = vlink(q);
+ if (type(p) == adjust_node) {
+ if (adjust_pre(p) != 0)
+ update_adjust_list(pre_adjust_tail);
+ else
+ update_adjust_list(adjust_tail);
+ p = vlink(p);
+ adjust_ptr(vlink(q)) = null;
+ flush_node(vlink(q));
+ } else {
+ vlink(adjust_tail) = p;
+ adjust_tail = p;
+ p = vlink(p);
+ }
+ vlink(q) = p;
+ p = q;
+ }
+ break;
+ default:
+ break;
+ }
+ p = vlink(p);
+ }
+ }
+
+ if (adjust_tail != null)
+ vlink(adjust_tail) = null;
+ if (pre_adjust_tail != null)
+ vlink(pre_adjust_tail) = null;
+ height(r) = h;
+ depth(r) = d;
+ /*tex
+
+ Determine the value of |width(r)| and the appropriate glue setting; then
+ |return| or |goto common_ending|.
+
+ When we get to the present part of the program, |x| is the natural width
+ of the box being packaged.
+
+ */
+ if (m == additional)
+ w = x + w;
+ width(r) = w;
+ x = w - x;
+ /*tex 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 EXIT;
+ } else if (x > 0) {
+ /*tex
+
+ Determine horizontal glue stretch setting, then |return| or
+ |goto common_ending|.
+
+ If |hpack| is called with |m=cal_expand_ratio| we calculate
+ |font_expand_ratio| and return without checking for overfull or
+ underfull box.
+
+ */
+ if (total_stretch[filll] != 0)
+ o = filll;
+ else if (total_stretch[fill] != 0)
+ o = fill;
+ else if (total_stretch[fil] != 0)
+ o = fil;
+ else if (total_stretch[sfi] != 0)
+ o = sfi;
+ else
+ o = normal;
+ if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) {
+ font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0);
+ goto EXIT;
+ }
+ glue_order(r) = (quarterword) o;
+ glue_sign(r) = stretching;
+ if (total_stretch[o] != 0) {
+ glue_set(r) = unfloat((double) x / total_stretch[o]);
+ } else {
+ /*tex There's nothing to stretch. */
+ glue_sign(r) = normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+ if (o == normal) {
+ if (list_ptr(r) != null) {
+ /*tex
+
+ Report an underfull hbox and |goto common_ending|, if this
+ box is sufficiently bad.
+
+ */
+ last_badness = badness(x, total_stretch[normal]);
+ if (last_badness > hbadness_par) {
+ int callback_id = callback_defined(hpack_quality_callback);
+ if (callback_id > 0) {
+ halfword rule = null;
+ if (last_badness > 100) {
+ run_callback(callback_id, "SdNdd->N","underfull",last_badness,r,abs(pack_begin_line),line,&rule);
+ } else {
+ run_callback(callback_id, "SdNdd->N","loose",last_badness,r,abs(pack_begin_line),line,&rule);
+ }
+ if (rule != null) {
+ while (vlink(q) != null) {
+ q = vlink(q);
+ }
+ couple_nodes(q,rule);
+ }
+ } else {
+ print_ln();
+ if (last_badness > 100) {
+ tprint_nl("Underfull \\hbox (badness ");
+ } else {
+ tprint_nl("Loose \\hbox (badness ");
+ }
+ print_int(last_badness);
+ goto COMMON_ENDING;
+ }
+ }
+ }
+ }
+ goto EXIT;
+ } else {
+ /*tex
+
+ Determine horizontal glue shrink setting, then |return| or |goto
+ common_ending|,
+
+ */
+ if (total_shrink[filll] != 0)
+ o = filll;
+ else if (total_shrink[fill] != 0)
+ o = fill;
+ else if (total_shrink[fil] != 0)
+ o = fil;
+ else if (total_shrink[sfi] != 0)
+ o = sfi;
+ else
+ o = normal;
+ if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) {
+ font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0);
+ goto EXIT;
+ }
+ glue_order(r) = (quarterword) o;
+ glue_sign(r) = shrinking;
+ if (total_shrink[o] != 0) {
+ glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]);
+ } else {
+ /*tex There's nothing to shrink. */
+ glue_sign(r) = normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+ if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
+ int overshoot = -x - total_shrink[normal] ;
+ last_badness = 1000000;
+ /*tex Use the maximum shrinkage */
+ set_glue_ratio_one(glue_set(r));
+ /*tex
+
+ Report an overfull hbox and |goto common_ending|, if this box is
+ sufficiently bad.
+
+ */
+ if ((overshoot > hfuzz_par) || (hbadness_par < 100)) {
+ int callback_id = callback_defined(hpack_quality_callback);
+ halfword rule = null;
+ if (callback_id > 0) {
+ run_callback(callback_id, "SdNdd->N","overfull",overshoot,r,abs(pack_begin_line),line,&rule);
+ } else if (overfull_rule_par > 0) {
+ rule = new_rule(normal_rule);
+ rule_dir(rule) = box_dir(r);
+ width(rule) = overfull_rule_par;
+ }
+ if (rule != null) {
+ while (vlink(q) != null) {
+ q = vlink(q);
+ }
+ couple_nodes(q,rule);
+ }
+ if (callback_id == 0) {
+ print_ln();
+ tprint_nl("Overfull \\hbox (");
+ print_scaled(overshoot);
+ tprint("pt too wide");
+ goto COMMON_ENDING;
+ }
+ }
+ } else if (o == normal) {
+ if (list_ptr(r) != null) {
+ /*tex
+
+ Report a tight hbox and |goto common_ending|, if this box is
+ sufficiently bad.
+
+ */
+ last_badness = badness(-x, total_shrink[normal]);
+ if (last_badness > hbadness_par) {
+ int callback_id = callback_defined(hpack_quality_callback);
+ if (callback_id > 0) {
+ halfword rule = null;
+ run_callback(callback_id, "SdNdd->N","tight",last_badness,r,abs(pack_begin_line),line,&rule);
+ if (rule != null) {
+ while (vlink(q) != null) {
+ q = vlink(q);
+ }
+ couple_nodes(q,rule);
+ }
+ } else {
+ print_ln();
+ tprint_nl("Tight \\hbox (badness ");
+ print_int(last_badness);
+ goto COMMON_ENDING;
+ }
+ }
+ }
+ }
+ goto EXIT;
+ }
+
+ COMMON_ENDING:
+ /*tex Finish issuing a diagnostic message for an overfull or underfull hbox. */
+ if (output_active) {
+ tprint(") has occurred while \\output is active");
+ } else {
+ if (pack_begin_line != 0) {
+ if (pack_begin_line > 0) {
+ tprint(") in paragraph at lines ");
+ } else {
+ tprint(") in alignment at lines ");
+ }
+ print_int(abs(pack_begin_line));
+ tprint("--");
+ } else {
+ tprint(") 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);
+ EXIT:
+ if ((m == cal_expand_ratio) && (font_expand_ratio != 0)) {
+ font_expand_ratio = fix_int(font_expand_ratio, -1000, 1000);
+ q = list_ptr(r);
+ list_ptr(r) = null;
+ flush_node(r);
+ /*tex This nested call uses the more or less global font_expand_ratio. */
+ r = hpack(q, w, subst_ex_font, hpack_dir);
+ }
+ while (dir_ptr1 != null)
+ pop_dir_node(dir_ptr1);
+ /*tex Here we reset the |font_expand_ratio|. */
+ font_expand_ratio = 0;
+ return r;
+}
+
+halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int pac, int just_pack, halfword attr)
+{
+ halfword q;
+ if (just_pack) {
+ q = vlink(p);
+ } else if (type(p) == temp_node && vlink(p) == null) {
+ q = vlink(p);
+ } else {
+ new_hyphenation(p, qt);
+ /*tex We don't care about the tail in this case. */
+ (void) new_ligkern(p, qt);
+ q = vlink(p);
+ /*tex Maybe here: |alink(p) = null|. */
+ /*tex ignores empty anyway. Maybe also pass tail? */
+ q = lua_hpack_filter(q, w, m, grp, pac, attr);
+ }
+ return hpack(q, w, m, pac);
+}
+
+/*tex
+
+ Here is a function to calculate the natural whd of a (horizontal) node list.
+
+*/
+
+scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, int g_sign, int g_order, int pack_direction)
+{
+ /*tex shift amount */
+ scaled s;
+ /*tex points to a glue specification */
+ halfword g;
+ int hpack_dir;
+ /*tex For recursion */
+ scaled_whd xx;
+ scaled_whd whd, siz = { 0, 0, 0 };
+ scaled gp = 0;
+ scaled gm = 0;
+ if (pack_direction == -1) {
+ hpack_dir = text_direction_par;
+ } else {
+ hpack_dir = pack_direction;
+ }
+ while (p != pp && p != null) {
+ while (is_char_node(p) && p != pp) {
+ whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true);
+ siz.wd += whd.wd;
+ if (whd.ht > siz.ht)
+ siz.ht = whd.ht;
+ if (whd.dp > siz.dp)
+ siz.dp = whd.dp;
+ p = vlink(p);
+ }
+ if (p != pp && p != null) {
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ s = shift_amount(p);
+ whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false);
+ siz.wd += whd.wd;
+ if (whd.ht - s > siz.ht)
+ siz.ht = whd.ht - s;
+ if (whd.dp + s > siz.dp)
+ siz.dp = whd.dp + s;
+ break;
+ case rule_node:
+ case unset_node:
+ siz.wd += width(p);
+ if (height(p) > siz.ht)
+ siz.ht = height(p);
+ if (depth(p) > siz.dp)
+ siz.dp = depth(p);
+ break;
+ case math_node:
+ /*tex Begin mathskip code. */
+ if (glue_is_zero(p) || ignore_math_skip(p)) {
+ siz.wd += surround(p);
+ break;
+ } else {
+ /*tex Fall through. */
+ }
+ /*tex End mathskip code. */
+ case glue_node:
+ siz.wd += width(p);
+ if (g_sign != normal) {
+ if (g_sign == stretching) {
+ if (stretch_order(p) == g_order) {
+ /*tex
+ |siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p)))|
+ */
+ gp += stretch(p);
+ }
+ } else if (shrink_order(p) == g_order) {
+ /*tex
+ |siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));|
+ */
+ gm += shrink(p);
+ }
+ }
+ if (subtype(p) >= a_leaders) {
+ g = leader_ptr(p);
+ if (height(g) > siz.ht)
+ siz.ht = height(g);
+ if (depth(g) > siz.dp)
+ siz.dp = depth(g);
+ }
+ break;
+ case margin_kern_node:
+ siz.wd += width(p);
+ break;
+ case kern_node:
+ siz.wd += width(p) + ex_kern(p);
+ break;
+ case disc_node:
+ xx = natural_sizes(no_break(p), null, g_mult, g_sign, g_order, hpack_dir);
+ siz.wd += xx.wd;
+ if (xx.ht > siz.ht)
+ siz.ht = xx.ht;
+ if (xx.dp > siz.dp)
+ siz.dp = xx.dp;
+ break;
+ default:
+ break;
+ }
+ p = vlink(p);
+ }
+
+ }
+ if (g_sign != normal) {
+ if (g_sign == stretching) {
+ siz.wd += float_round(float_cast(g_mult) * float_cast(gp));
+ } else {
+ siz.wd -= float_round(float_cast(g_mult) * float_cast(gm));
+ }
+ }
+ return siz;
+}
+
+/*tex
+
+ 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.
+
+ The source file line where the current paragraph or alignment began; a
+ negative value denotes alignment:
+
+*/
+
+int pack_begin_line;
+
+/*tex
+
+ 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.
+
+*/
+
+halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
+{
+ /*tex the box node that will be returned */
+ halfword r;
+ /*tex width */
+ scaled w = 0;
+ /*tex depth */
+ scaled d = 0;
+ /*tex natural height */
+ scaled x = 0;
+ scaled_whd whd;
+ /*tex shift amount */
+ scaled s;
+ /*tex order of infinity */
+ int o;
+ last_badness = 0;
+ r = new_node(vlist_node, 0);
+ if (pack_direction == -1) {
+ box_dir(r) = body_direction_par;
+ } else {
+ box_dir(r) = pack_direction;
+ }
+ subtype(r) = min_quarterword;
+ shift_amount(r) = 0;
+ list_ptr(r) = p;
+ total_stretch[normal] = 0;
+ total_shrink[normal] = 0;
+ total_stretch[sfi] = 0;
+ total_shrink[sfi] = 0;
+ total_stretch[fil] = 0;
+ total_shrink[fil] = 0;
+ total_stretch[fill] = 0;
+ total_shrink[fill] = 0;
+ total_stretch[filll] = 0;
+ total_shrink[filll] = 0;
+ while (p != null) {
+ /*tex
+
+ 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.
+
+ */
+ if (is_char_node(p)) {
+ confusion("vpack");
+ } else {
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ /*tex
+
+ Incorporate box dimensions into the dimensions of the vbox
+ that will contain it.
+
+ */
+ s = shift_amount(p);
+ whd = pack_width_height_depth(box_dir(r), box_dir(p), p, false);
+ if (whd.wd + s > w)
+ w = whd.wd + s;
+ x += d + whd.ht;
+ d = whd.dp;
+ break;
+ case rule_node:
+ case unset_node:
+ x += d + height(p);
+ d = depth(p);
+ if (width(p) > w)
+ w = width(p);
+ break;
+ case glue_node:
+ /*tex Incorporate glue into the vertical totals. */
+ x += d;
+ d = 0;
+ x += width(p);
+ o = stretch_order(p);
+ total_stretch[o] = total_stretch[o] + stretch(p);
+ o = shrink_order(p);
+ total_shrink[o] = total_shrink[o] + shrink(p);
+ if (subtype(p) >= a_leaders) {
+ halfword g = leader_ptr(p);
+ if (width(g) > w)
+ w = width(g);
+ }
+ break;
+ case kern_node:
+ x += d + width(p);
+ d = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ p = vlink(p);
+ }
+ width(r) = w;
+ if (d > l) {
+ x += d - l;
+ depth(r) = l;
+ } else {
+ depth(r) = d;
+ }
+ /*tex
+
+ Determine the value of |height(r)| and the appropriate glue setting; then
+ |return| or |goto common_ending|.
+
+ When we get to the present part of the program, |x| is the natural height
+ of the box being packaged.
+ */
+ if (m == additional)
+ h = x + h;
+ height(r) = h;
+ x = h - x;
+ /*tex 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));
+ return r;
+ } else if (x > 0) {
+ /*tex
+
+ Determine vertical glue stretch setting, then |return| or |goto
+ common_ending|.
+
+ */
+ if (total_stretch[filll] != 0)
+ o = filll;
+ else if (total_stretch[fill] != 0)
+ o = fill;
+ else if (total_stretch[fil] != 0)
+ o = fil;
+ else if (total_stretch[sfi] != 0)
+ o = sfi;
+ else
+ o = normal;
+ glue_order(r) = (quarterword) o;
+ glue_sign(r) = stretching;
+ if (total_stretch[o] != 0) {
+ glue_set(r) = unfloat((double) x / total_stretch[o]);
+ } else {
+ /*tex There's nothing to stretch. */
+ glue_sign(r) = normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+ if (o == normal) {
+ if (list_ptr(r) != null) {
+ /*tex
+
+ Report an underfull vbox and |goto common_ending|, if this
+ box is sufficiently bad.
+
+ */
+ last_badness = badness(x, total_stretch[normal]);
+ if (last_badness > vbadness_par) {
+ int callback_id = callback_defined(vpack_quality_callback);
+ if (callback_id > 0) {
+ if (last_badness > 100) {
+ run_callback(callback_id, "SdNdd->","underfull",last_badness,r,abs(pack_begin_line),line);
+ } else {
+ run_callback(callback_id, "SdNdd->","loose",last_badness,r,abs(pack_begin_line),line);
+ }
+ goto EXIT;
+ } else {
+ print_ln();
+ if (last_badness > 100) {
+ tprint_nl("Underfull \\vbox (badness ");
+ } else {
+ tprint_nl("Loose \\vbox (badness ");
+ }
+ print_int(last_badness);
+ goto COMMON_ENDING;
+ }
+ }
+ }
+ }
+ return r;
+ } else {
+ /*tex
+
+ Determine vertical glue shrink setting, then |return| or |goto
+ common_ending|.
+
+ */
+ if (total_shrink[filll] != 0)
+ o = filll;
+ else if (total_shrink[fill] != 0)
+ o = fill;
+ else if (total_shrink[fil] != 0)
+ o = fil;
+ else if (total_shrink[sfi] != 0)
+ o = sfi;
+ else
+ o = normal;
+ glue_order(r) = (quarterword) o;
+ glue_sign(r) = shrinking;
+ if (total_shrink[o] != 0) {
+ glue_set(r) = unfloat((double) (-x) / total_shrink[o]);
+ } else {
+ /*tex There's nothing to shrink. */
+ glue_sign(r) = normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+ if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) {
+ int overshoot = -x - total_shrink[normal];
+ last_badness = 1000000;
+ /*tex Use the maximum shrinkage */
+ set_glue_ratio_one(glue_set(r));
+ /*tex
+
+ Report an overfull vbox and |goto common_ending|, if this box is
+ sufficiently bad.
+
+ */
+ if ((overshoot > vfuzz_par) || (vbadness_par < 100)) {
+ int callback_id = callback_defined(vpack_quality_callback);
+ if (callback_id > 0) {
+ run_callback(callback_id, "SdNdd->","overfull",overshoot,r,abs(pack_begin_line),line);
+ goto EXIT;
+ } else {
+ print_ln();
+ tprint_nl("Overfull \\vbox (");
+ print_scaled(-x - total_shrink[normal]);
+ tprint("pt too high");
+ goto COMMON_ENDING;
+ }
+ }
+ } else if (o == normal) {
+ if (list_ptr(r) != null) {
+ /*tex
+
+ Report a tight vbox and |goto common_ending|, if this box is
+ sufficiently bad.
+
+ */
+ last_badness = badness(-x, total_shrink[normal]);
+ if (last_badness > vbadness_par) {
+ int callback_id = callback_defined(vpack_quality_callback);
+ if (callback_id > 0) {
+ run_callback(callback_id, "SdNdd->","tight",last_badness,r,abs(pack_begin_line),line);
+ goto EXIT;
+ } else {
+ print_ln();
+ tprint_nl("Tight \\vbox (badness ");
+ print_int(last_badness);
+ goto COMMON_ENDING;
+ }
+ }
+ }
+ }
+ return r;
+ }
+
+ COMMON_ENDING:
+ /*tex Finish issuing a diagnostic message or an overfull or underfull vbox. */
+ if (output_active) {
+ tprint(") has occurred while \\output is active");
+ } else {
+ if (pack_begin_line != 0) {
+ /*tex It's actually negative. */
+ tprint(") in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ tprint("--");
+ } else {
+ tprint(") detected at line ");
+ }
+ print_int(line);
+ print_ln();
+ }
+ begin_diagnostic();
+ show_box(r);
+ end_diagnostic(true);
+ EXIT:
+ return r;
+}
+
+halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, int pack_direction, int just_pack, halfword attr)
+{
+ halfword q = p;
+ if (!just_pack)
+ q = lua_vpack_filter(q, h, m, l, grp, pack_direction, attr);
+ return vpackage(q, h, m, l, pack_direction);
+}
+
+void finish_vcenter(void)
+{
+ halfword p;
+ unsave();
+ save_ptr--;
+ p = vpack(vlink(cur_list.head_field), saved_value(0), saved_level(0), -1);
+ pop_nest();
+ p = math_vcenter_group(p);
+ tail_append(p);
+}
+
+void package(int c)
+{
+ halfword saved0, saved2, saved3, saved4;
+ int grp = cur_group;
+ scaled d = box_max_depth_par;
+ unsave();
+ save_ptr -= 5;
+ saved0 = saved_value(0);
+ saved2 = saved_value(2);
+ saved3 = saved_value(3);
+ saved4 = saved_value(4);
+ if (cur_list.mode_field == -hmode) {
+ cur_box = filtered_hpack(cur_list.head_field, cur_list.tail_field,
+ saved_value(1), saved_level(1), grp, saved_level(2), saved4, saved3);
+ subtype(cur_box) = hbox_list;
+ } else {
+ cur_box = filtered_vpackage(vlink(cur_list.head_field),
+ saved_value(1), saved_level(1), d, grp, saved_level(2), saved4, saved3);
+ if (c == vtop_code) {
+ /*tex
+
+ Read just the height and depth of |cur_box|, for \.{\\vtop}. 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.
+
+ */
+ scaled h = 0;
+ halfword p = list_ptr(cur_box);
+ if ((p != null) && (type(p) <= rule_node)) {
+ /* hlist, vlist, rule */
+ h = height(p);
+ }
+ depth(cur_box) = depth(cur_box) - h + height(cur_box);
+ height(cur_box) = h;
+ }
+ }
+ if (saved2 != null) {
+ /*tex Adjust back |text_dir_ptr| for |scan_spec| */
+ flush_node_list(text_dir_ptr);
+ text_dir_ptr = saved2;
+
+ }
+ replace_attribute_list(cur_box, saved3);
+ pop_nest();
+ box_end(saved0);
+}
+
+/*tex
+
+ When a box is being appended to the current vertical list, the baselineskip
+ calculation is handled by the |append_to_vlist| routine.
+
+*/
+
+void append_to_vlist(halfword b, int location)
+{
+ /*tex The deficiency of space between baselines: */
+ scaled d;
+ /*tex A new glue node. */
+ halfword p;
+ boolean mirrored = (type(b) == hlist_node) && is_mirrored(box_dir(b)) ;
+ halfword result = null;
+ halfword next_depth = ignore_depth;
+ boolean prev_set = false ;
+ if (lua_appendtovlist_callback(b,location,prev_depth_par,mirrored,&result,&next_depth,&prev_set)) {
+ while (result != null) {
+ couple_nodes(cur_list.tail_field, result);
+ cur_list.tail_field = result;
+ result = vlink(result);
+ }
+ if (prev_set) {
+ prev_depth_par = next_depth;
+ }
+ } else {
+ if (prev_depth_par > ignore_depth) {
+ if (mirrored) {
+ d = width(baseline_skip_par) - prev_depth_par - depth(b);
+ } else {
+ d = width(baseline_skip_par) - prev_depth_par - height(b);
+ }
+ if (d < line_skip_limit_par) {
+ p = new_param_glue(line_skip_code);
+ } else {
+ p = new_skip_param(baseline_skip_code);
+ width(p) = d;
+ }
+ couple_nodes(cur_list.tail_field, p);
+ cur_list.tail_field = p;
+ }
+ couple_nodes(cur_list.tail_field, b);
+ cur_list.tail_field = b;
+ if (mirrored) {
+ prev_depth_par = height(b);
+ } else {
+ prev_depth_par = depth(b);
+ }
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex last item removed by page builder */
+
+#define tail_page_disc disc_ptr[copy_code]
+
+/*tex first item removed by page builder */
+
+#define page_disc disc_ptr[last_box_code]
+
+/*tex first item removed by \.{\\vsplit} */
+
+#define split_disc disc_ptr[vsplit_code]
+
+/*tex List pointers. */
+
+halfword disc_ptr[(vsplit_code + 1)];
+
+/*tex
+
+ 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|.
+
+*/
+
+halfword prune_page_top(halfword p, boolean s)
+{
+ halfword q;
+ /*tex Lags one step behind |p|. */
+ halfword prev_p = temp_head;
+ halfword r = null;
+ vlink(temp_head) = p;
+ while (p != null) {
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ case rule_node:
+ /*tex Insert glue for |split_top_skip| and set |p:=null|. */
+ q = new_skip_param(split_top_skip_code);
+ vlink(prev_p) = q;
+ vlink(q) = p;
+ if (width(q) > height(p))
+ width(q) = width(q) - height(p);
+ else
+ width(q) = 0;
+ p = null;
+ break;
+ case boundary_node:
+ case whatsit_node:
+ case mark_node:
+ case ins_node:
+ prev_p = p;
+ p = vlink(prev_p);
+ break;
+ case glue_node:
+ case kern_node:
+ case penalty_node:
+ q = p;
+ p = vlink(q);
+ vlink(q) = null;
+ vlink(prev_p) = p;
+ if (s) {
+ if (split_disc == null)
+ split_disc = q;
+ else
+ vlink(r) = q;
+ r = q;
+ } else {
+ flush_node_list(q);
+ }
+ break;
+ default:
+ confusion("pruning");
+ break;
+ }
+ }
+ return vlink(temp_head);
+}
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex The distance from first active node to |cur_p|: */
+
+scaled active_height[10];
+
+/*tex
+
+ 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.
+
+*/
+
+#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7)
+#define set_height_zero(A) active_height[A]=0 /* initialize the height to zero */
+
+/*tex
+
+ 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.)
+
+*/
+
+/*tex The height of the best box, without stretching or shrinking: */
+
+scaled best_height_plus_depth;
+
+halfword vert_break(halfword p, scaled h, scaled d)
+{
+ /*tex
+ If |p| is a glue node, |type(prev_p)| determines whether |p| is a legal
+ breakpoint, an initial glue node is not a legal breakpoint.
+ */
+ halfword prev_p = p;
+ /*tex penalty value */
+ int pi = 0;
+ /*tex badness at a trial breakpoint */
+ int b;
+ /*tex |type| of the node following a kern */
+ int t;
+ /*tex the smallest badness plus penalties found so far */
+ int least_cost;
+ /*tex the most recent break that leads to |least_cost| */
+ halfword best_place = null;
+ /*tex depth of previous box in the list */
+ scaled prev_dp = 0;
+ least_cost = awful_bad;
+ do_all_six(set_height_zero);
+ while (1) {
+ /*tex
+
+ 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.
+
+ 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 (p == null) {
+ pi = eject_penalty;
+ } else {
+ /*tex
+
+ 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.
+
+ */
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ case rule_node:
+ cur_height = cur_height + prev_dp + height(p);
+ prev_dp = depth(p);
+ goto NOT_FOUND;
+ break;
+ case boundary_node:
+ case whatsit_node:
+ goto NOT_FOUND;
+ break;
+ case glue_node:
+ if (precedes_break(prev_p))
+ pi = 0;
+ else
+ goto UPDATE_HEIGHTS;
+ break;
+ case kern_node:
+ if (vlink(p) == null)
+ t = penalty_node;
+ else
+ t = type(vlink(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;
+ break;
+ default:
+ confusion("vertbreak");
+ break;
+ }
+ }
+ /*tex
+
+ Check if node |p| is a new champion breakpoint; then |goto done| if
+ |p| is a forced break or if the page-so-far is already too full.
+
+ */
+ if (pi < inf_penalty) {
+ /*tex 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) || (active_height[6] != 0))
+ b = 0;
+ else
+ b = badness(h - cur_height, active_height[2]);
+ } else if (cur_height - h > active_height[7]) {
+ b = awful_bad;
+ } else {
+ b = badness(cur_height - h, active_height[7]);
+ }
+ 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;
+ }
+ if ((type(p) < glue_node) || (type(p) > kern_node))
+ goto NOT_FOUND;
+ UPDATE_HEIGHTS:
+ /*tex
+
+ Update the current height and depth measurements with respect to a
+ glue or kern node~|p|. 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.
+
+ */
+ if (type(p) != kern_node) {
+ active_height[2 + stretch_order(p)] += stretch(p);
+ active_height[7] += shrink(p);
+ if ((shrink_order(p) != normal) && (shrink(p) != 0)) {
+ print_err("Infinite glue shrinkage found in box being split");
+ 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();
+ shrink_order(p) = normal;
+ }
+ }
+ cur_height = cur_height + prev_dp + width(p);
+ prev_dp = 0;
+ NOT_FOUND:
+ if (prev_dp > d) {
+ cur_height = cur_height + prev_dp - d;
+ prev_dp = d;
+ }
+ prev_p = p;
+ p = vlink(prev_p);
+ }
+ DONE:
+ return best_place;
+}
+
+/*tex
+
+ 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(x)=null| if and only if |split_bot_mark(x)=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).
+
+*/
+
+/*tex Extract a page of height |h| from box |n|: */
+
+halfword vsplit(halfword n, scaled h, int m)
+{
+ /*tex the box to be split */
+ halfword v;
+ /*tex the direction of the box to be split */
+ int vdir;
+ /*tex runs through the vlist */
+ halfword p;
+ /*tex points to where the break occurs */
+ halfword q;
+ /*tex for traversing marks lists */
+ halfword i;
+ v = box(n);
+ vdir = box_dir(v);
+ flush_node_list(split_disc);
+ split_disc = null;
+ for (i = 0; i <= biggest_used_mark; i++) {
+ delete_split_first_mark(i);
+ delete_split_bot_mark(i);
+ }
+ /*tex Dispense with trivial cases of void or bad boxes. */
+ if (v == null) {
+ return null;
+ }
+ if (type(v) != vlist_node) {
+ print_err("\\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;
+ }
+ q = vert_break(list_ptr(v), h, split_max_depth_par);
+ /*tex
+
+ Look at all the marks in nodes before the break, and set the final link
+ to |null| at the break. 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.
+
+ */
+ p = list_ptr(v);
+ if (p == q) {
+ list_ptr(v) = null;
+ } else {
+ while (1) {
+ if (type(p) == mark_node) {
+ if (split_first_mark(mark_class(p)) == null) {
+ set_split_first_mark(mark_class(p), mark_ptr(p));
+ set_split_bot_mark(mark_class(p), split_first_mark(mark_class(p)));
+ set_token_ref_count(split_first_mark(mark_class(p)),
+ token_ref_count(split_first_mark(mark_class(p))) + 2);
+ } else {
+ delete_token_ref(split_bot_mark(mark_class(p)));
+ set_split_bot_mark(mark_class(p), mark_ptr(p));
+ add_token_ref(split_bot_mark(mark_class(p)));
+ }
+ }
+ if (vlink(p) == q) {
+ vlink(p) = null;
+ break;
+ }
+ p = vlink(p);
+ }
+ }
+ q = prune_page_top(q, saving_vdiscards_par > 0);
+ p = list_ptr(v);
+ list_ptr(v) = null;
+ flush_node(v);
+ if (q == null) {
+ /*tex The |eq_level| of the box stays the same. */
+ box(n) = null;
+ } else {
+ box(n) = filtered_vpackage(q, 0, additional, max_depth_par, split_keep_group, vdir, 0, 0);
+ }
+ if (m == exactly) {
+ return filtered_vpackage(p, h, exactly, split_max_depth_par, split_off_group, vdir, 0, 0);
+ } else {
+ return filtered_vpackage(p, 0, additional, max_depth_par, split_off_group, vdir, 0, 0);
+ }
+}
+
+/*tex
+
+ 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|.
+
+*/
+
+void begin_box(int box_context)
+{
+ /*tex run through the current list */
+ halfword q;
+ /*tex 0 or |vmode| or |hmode| */
+ halfword k;
+ /*tex a box number */
+ int n;
+ int spec_direction = -1;
+ int just_pack = 0;
+ int split_mode = exactly ;
+ switch (cur_chr) {
+ case box_code:
+ scan_register_num();
+ cur_box = box(cur_val);
+ /*tex The box becomes void, at the same level. */
+ box(cur_val) = null;
+ break;
+ case copy_code:
+ scan_register_num();
+ cur_box = copy_node_list(box(cur_val));
+ break;
+ case last_box_code:
+ /*tex
+
+ 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|.
+
+ */
+ cur_box = null;
+ if (abs(cur_list.mode_field) == mmode) {
+ you_cant();
+ help1("Sorry; this \\lastbox will be void.");
+ error();
+ } else if ((cur_list.mode_field == vmode) && (cur_list.head_field == cur_list.tail_field)) {
+ you_cant();
+ help2(
+ "Sorry...I usually can't take things from the current page.",
+ "This \\lastbox will therefore be void."
+ );
+ error();
+ } else {
+ if (cur_list.head_field != cur_list.tail_field) {
+ if ((type(cur_list.tail_field) == hlist_node) || (type(cur_list.tail_field) == vlist_node)) {
+ /*tex Remove the last box */
+ q = alink(cur_list.tail_field);
+ if (q == null || vlink(q) != cur_list.tail_field) {
+ q = cur_list.head_field;
+ while (vlink(q) != cur_list.tail_field)
+ q = vlink(q);
+ }
+ uncouple_node(cur_list.tail_field);
+ cur_box = cur_list.tail_field;
+ shift_amount(cur_box) = 0;
+ cur_list.tail_field = q;
+ vlink(cur_list.tail_field) = null;
+ }
+ }
+ }
+ break;
+ case vsplit_code:
+ /*tex
+
+ Split off part of a vertical box, make |cur_box| point to it.
+ Here we deal with things like `\.{\\vsplit 13 to 100pt}'.
+
+ */
+ scan_register_num();
+ n = cur_val;
+ if (scan_keyword("upto")) {
+ split_mode = additional ;
+ } else if (!scan_keyword("to")) {
+ print_err("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, split_mode);
+ break;
+ default:
+ /*tex
+
+ Initiate the construction of an hbox or vbox, then |return|. Here
+ is where we enter restricted horizontal mode or internal vertical
+ mode, in order to make a box.
+
+ */
+ switch (cur_chr) {
+ case tpack_code:
+ cur_chr = vtop_code;
+ just_pack = 1;
+ break;
+ case vpack_code:
+ cur_chr = vtop_code + vmode;
+ just_pack = 1;
+ break;
+ case hpack_code:
+ cur_chr = vtop_code + hmode;
+ just_pack = 1;
+ break;
+ }
+ k = cur_chr - vtop_code;
+ set_saved_record(0, saved_boxcontext, 0, box_context);
+ switch (abs(cur_list.mode_field)) {
+ case vmode:
+ spec_direction = body_direction_par;
+ break;
+ case hmode:
+ spec_direction = text_direction_par;
+ break;
+ case mmode:
+ spec_direction = math_direction_par;
+ break;
+ }
+ if (k == hmode) {
+ if ((box_context < box_flag) && (abs(cur_list.mode_field) == vmode))
+ scan_full_spec(adjusted_hbox_group, spec_direction,just_pack);
+ else
+ scan_full_spec(hbox_group, spec_direction,just_pack);
+ } else {
+ if (k == vmode) {
+ scan_full_spec(vbox_group, spec_direction,just_pack);
+ } else {
+ scan_full_spec(vtop_group, spec_direction,just_pack);
+ k = vmode;
+ }
+ normal_paragraph();
+ }
+ push_nest();
+/* new */
+/* eq_word_define(int_base + no_local_whatsits_code, 0); */
+eq_word_define(int_base + no_local_dirs_code, 0);
+ cur_list.mode_field = -k;
+ if (k == vmode) {
+ prev_depth_par = ignore_depth;
+ if (every_vbox_par != null)
+ begin_token_list(every_vbox_par, every_vbox_text);
+ } else {
+ space_factor_par = 1000;
+ if (every_hbox_par != null)
+ begin_token_list(every_hbox_par, every_hbox_text);
+ }
+ return;
+ break;
+ }
+ /*tex In simple cases, we use the box immediately. */
+ box_end(box_context);
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/packaging.h b/Build/source/texk/web2c/luatexdir/tex/packaging.h
new file mode 100644
index 00000000000..9353afeaf20
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/packaging.h
@@ -0,0 +1,156 @@
+/* packaging.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PACKAGING_H
+# define PACKAGING_H
+
+/* We define some constants used when calling |hpack| to deal with font expansion. */
+
+typedef enum {
+ exactly = 0, /*a box dimension is pre-specified */
+ additional, /*a box dimension is increased from the natural one */
+ cal_expand_ratio, /* calculate amount for font expansion after breaking paragraph into lines */
+ subst_ex_font /* substitute fonts */
+} hpack_subtypes;
+
+# define substituted 3 /* |subtype| of kern nodes that should be substituted */
+
+extern void scan_spec(group_code c);
+extern void scan_full_spec(group_code c, int spec_direction, int justpack);
+
+extern scaled total_stretch[5];
+extern scaled total_shrink[5]; /* glue found by |hpack| or |vpack| */
+extern int last_badness; /* badness of the most recently packaged box */
+extern halfword adjust_tail; /* tail of adjustment list */
+extern halfword pre_adjust_tail;
+extern int font_expand_ratio; /* current expansion ratio */
+extern halfword last_leftmost_char;
+extern halfword last_rightmost_char;
+extern halfword next_char_p; /* pointer to the next char of an implicit kern */
+extern halfword prev_char_p; /* pointer to the previous char of an implicit kern */
+
+extern void set_prev_char_p(halfword p);
+extern scaled char_stretch(halfword p);
+extern scaled char_shrink(halfword p);
+extern scaled kern_stretch(halfword p);
+extern scaled kern_shrink(halfword p);
+extern void do_subst_font(halfword p, int ex_ratio);
+extern scaled char_pw(halfword p, int side);
+extern halfword new_margin_kern(scaled w, halfword p, int side);
+
+# define update_adjust_list(A) do { \
+ if (A == null) \
+ normal_error("pre vadjust", "adjust_tail or pre_adjust_tail is null"); \
+ vlink(A) = adjust_ptr(p); \
+ while (vlink(A) != null) \
+ A = vlink(A); \
+ } while (0)
+
+extern halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int d, int just_pack, halfword attr);
+extern scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, int g_sign, int g_order, int d);
+extern halfword hpack(halfword p, scaled w, int m, int d);
+
+extern int pack_begin_line;
+
+extern halfword vpackage(halfword p, scaled h, int m, scaled l, int d);
+extern halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, int d, int just_pack, halfword attr);
+extern void finish_vcenter(void);
+extern void package(int c);
+extern void append_to_vlist(halfword b, int location);
+
+# define vpack(A,B,C,D) vpackage(A,B,C,max_dimen,D) /* special case of unconstrained depth */
+
+extern halfword prune_page_top(halfword p, boolean s);
+extern scaled active_height[10]; /* distance from first active node to~|cur_p| */
+
+# define cur_height active_height[1] /* the natural height */
+# define awful_bad 07777777777 /* more than a billion demerits */
+# define deplorable 100000 /* more than |inf_bad|, but less than |awful_bad| */
+
+extern scaled best_height_plus_depth; /* height of the best box, without stretching or shrinking */
+
+extern halfword vert_break(halfword p, scaled h, scaled d);
+extern halfword vsplit(halfword n, scaled h, int m); /* extracts a page of height |h| from box |n| */
+
+# define box_code 0 /* |chr_code| for `\.{\\box}' */
+# define copy_code 1 /* |chr_code| for `\.{\\copy}' */
+# define last_box_code 2 /* |chr_code| for `\.{\\lastbox}' */
+# define vsplit_code 3 /* |chr_code| for `\.{\\vsplit}' */
+# define tpack_code 4
+# define vpack_code 5
+# define hpack_code 6
+# define vtop_code 7 /* |chr_code| for `\.{\\vtop}' */
+
+# define tail_page_disc disc_ptr[copy_code] /* last item removed by page builder */
+# define page_disc disc_ptr[last_box_code] /* first item removed by page builder */
+# define split_disc disc_ptr[vsplit_code] /* first item removed by \.{\\vsplit} */
+
+extern halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */
+
+/*
+
+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
+|box_flag+biggest_reg| represent `\.{\\setbox0}' through
+`\.{\\setbox}|biggest_reg|'; codes |box_flag+biggest_reg+1| through
+|box_flag+2*biggest_reg| represent `\.{\\global\\setbox0}' through
+`\.{\\global\\setbox}|biggest_reg|'; code |box_flag+2*number_regs| represents
+`\.{\\shipout}'; and codes |box_flag+2*number_regs+1| through
+|box_flag+2*number_regs+3| 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 denote \.{\\vbox} and \.{\\hbox}, respectively.
+
+*/
+
+# define box_flag 010000000000 /* context code for `\.{\\setbox0}' */
+# define global_box_flag (box_flag+number_regs) /* context code for `\.{\\global\\setbox0}' */
+# define max_global_box_flag (global_box_flag+number_regs)
+# define ship_out_flag (max_global_box_flag+1) /* context code for `\.{\\shipout}' */
+# define lua_scan_flag (max_global_box_flag+2) /* context code for |scan_list| */
+# define leader_flag (max_global_box_flag+3) /* context code for `\.{\\leaders}' */
+
+extern void begin_box(int box_context);
+
+#define math_skip_boundary(n) \
+(n && type(n) == glue_node && (subtype(n) == space_skip_subtype || subtype(n) == xspace_skip_subtype))
+
+extern int ignore_math_skip(halfword p) ;
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c
new file mode 100644
index 00000000000..c6f82d56052
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c
@@ -0,0 +1,603 @@
+/*
+
+postlinebreak.w
+
+Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+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
+|vlink(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 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.
+
+The |post_line_break| must also keep an dir stack, so that it can output end
+direction instructions at the ends of lines and begin direction instructions at
+the beginnings of lines.
+
+*/
+
+/*tex The new name for |prev_break| after links are reversed: */
+
+#define next_break prev_break
+
+/*tex The |int|s are actually halfwords. */
+
+void ext_post_line_break(int paragraph_dir,
+ int right_skip,
+ int left_skip,
+ int protrude_chars,
+ halfword par_shape_ptr,
+ int adjust_spacing,
+ halfword inter_line_penalties_ptr,
+ int inter_line_penalty,
+ int club_penalty,
+ halfword club_penalties_ptr,
+ halfword widow_penalties_ptr,
+ int widow_penalty,
+ int broken_penalty,
+ halfword final_par_glue,
+ halfword best_bet,
+ halfword last_special_line,
+ scaled second_width,
+ scaled second_indent,
+ scaled first_width,
+ scaled first_indent, halfword best_line)
+{
+
+ boolean have_directional = true;
+ /*tex temporary registers for list manipulation */
+ halfword q, r;
+ halfword k;
+ scaled w;
+ /*tex was a break at glue? */
+ boolean glue_break;
+ /*tex was the current break at a discretionary node? */
+ boolean disc_break;
+ /*tex and did it have a nonempty post-break part? */
+ boolean post_disc_break;
+ /*tex width of line number |cur_line| */
+ scaled cur_width;
+ /*tex left margin of line number |cur_line| */
+ scaled cur_indent;
+ /*tex use when calculating penalties between lines */
+ int pen;
+ /*tex |cur_p|, but localized */
+ halfword cur_p;
+ /*tex the current line number being justified */
+ halfword cur_line;
+ /*tex the current direction: */
+ dir_ptr = cur_list.dirs_field;
+ /*tex
+ Reverse the links of the relevant passive nodes, setting |cur_p| to the
+ first breakpoint. 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.
+ */
+ q = break_node(best_bet);
+ /*tex |cur_p| will become the first breakpoint; */
+ cur_p = null;
+ do {
+ r = q;
+ q = prev_break(q);
+ next_break(r) = cur_p;
+ cur_p = r;
+ } while (q != null);
+ /*tex prevgraf + 1 */
+ cur_line = cur_list.pg_field + 1;
+ do {
+ /*tex
+ Justify the line ending at breakpoint |cur_p|, and append it to the
+ current vertical list, together with associated penalties and other
+ insertions.
+
+ The current line to be justified appears in a horizontal list
+ starting at |vlink(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.
+ */
+ if (dir_ptr != null) {
+ /*tex Insert dir nodes at the beginning of the current line. */
+ for (q = dir_ptr; q != null; q = vlink(q)) {
+ halfword tmp = new_dir(dir_dir(q));
+ halfword nxt = vlink(temp_head);
+ delete_attribute_ref(node_attr(tmp));
+ node_attr(tmp) = node_attr(temp_head);
+ add_node_attr_ref(node_attr(tmp));
+ couple_nodes(temp_head, tmp);
+ /*tex \.{\\break}\.{\\par} */
+ try_couple_nodes(tmp, nxt);
+ }
+ flush_node_list(dir_ptr);
+ dir_ptr = null;
+ }
+ /*tex
+ 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|.
+ At the end of the following code, |q| will point to the final node on
+ the list about to be justified. In the meanwhile |r| will point to
+ the node we will use to insert end-of-line stuff after. |q==null|
+ means we use the final position of |r|.
+ */
+
+ if (temp_head != null) {
+ /*tex begin mathskip code */
+ q = temp_head;
+ while(q != null) {
+ if (type(q) == math_node) {
+ surround(q) = 0 ;
+ reset_glue_to_zero(q);
+ break;
+ } else if ((type(q) == hlist_node) && (subtype(q) == indent_list)) {
+ /* go on */
+ } else if (is_char_node(q)) {
+ break;
+ } else if (non_discardable(q)) {
+ break;
+ } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) {
+ break;
+ }
+ q = vlink(q);
+ }
+ /*tex end mathskip code */
+ }
+
+ r = cur_break(cur_p);
+ q = null;
+ disc_break = false;
+ post_disc_break = false;
+ glue_break = false;
+
+
+ if (r == null) {
+ for (r = temp_head; vlink(r) != null; r = vlink(r));
+ if (r == final_par_glue) {
+ /*tex This should almost always be true... */
+ q = r;
+ /*tex |q| refers to the last node of the line (and paragraph) */
+ r = alink(r);
+ }
+ /*tex |r| refers to the node after which the dir nodes should be closed */
+ } else if (type(r) == math_node) {
+ surround(r) = 0;
+ /*tex begin mathskip code */
+ reset_glue_to_zero(r);
+ /*tex end mathskip code */
+ } else if (type(r) == glue_node) {
+ copy_glue_values(r,right_skip);
+ subtype(r) = right_skip_code + 1;
+ glue_break = true;
+ /*tex |q| refers to the last node of the line */
+ q = r;
+ r = alink(r);
+ assert(vlink(r) == q);
+ /*tex |r| refers to the node after which the dir nodes should be closed */
+ } else if (type(r) == disc_node) {
+ halfword a = alink(r);
+ halfword v = vlink(r);
+ assert(a != null);
+ assert(v != null);
+ switch (subtype(r)) {
+ case select_disc:
+ if (vlink_pre_break(r) != null) {
+ flush_node_list(vlink_pre_break(r));
+ vlink_pre_break(r) = null;
+ tlink_pre_break(r) = null;
+ }
+ if (vlink_no_break(r) != null) {
+ couple_nodes(a, vlink_no_break(r));
+ couple_nodes(tlink_no_break(r), r);
+ vlink_no_break(r) = null;
+ tlink_no_break(r) = null;
+ }
+ assert(type(a) == disc_node && subtype(a) == init_disc);
+ flush_node_list(vlink_no_break(a));
+ vlink_no_break(a) = null;
+ tlink_no_break(a) = null;
+ flush_node_list(vlink_pre_break(a));
+ vlink_pre_break(a) = null;
+ tlink_pre_break(a) = null;
+ flush_node_list(vlink_post_break(a));
+ vlink_post_break(a) = null;
+ tlink_post_break(a) = null;
+ break;
+ case init_disc:
+ assert(type(v) == disc_node && subtype(v) == select_disc);
+ subtype(v) = syllable_disc;
+ flush_node_list(vlink_no_break(v));
+ vlink_no_break(v) = vlink_post_break(r);
+ tlink_no_break(v) = tlink_post_break(r);
+ vlink_post_break(r) = null;
+ tlink_post_break(r) = null;
+ default:
+ if (vlink_no_break(r) != null) {
+ flush_node_list(vlink_no_break(r));
+ vlink_no_break(r) = null;
+ tlink_no_break(r) = null;
+ }
+ if (vlink_pre_break(r) != null) {
+ couple_nodes(a, vlink_pre_break(r));
+ couple_nodes(tlink_pre_break(r), r);
+ vlink_pre_break(r) = null;
+ tlink_pre_break(r) = null;
+ }
+ }
+ if (vlink_post_break(r) != null) {
+ couple_nodes(r, vlink_post_break(r));
+ couple_nodes(tlink_post_break(r), v);
+ vlink_post_break(r) = null;
+ tlink_post_break(r) = null;
+ post_disc_break = true;
+ }
+ disc_break = true;
+ } else if (type(r) == kern_node) {
+ width(r) = 0;
+ }
+ /*tex Adjust the dir stack based on dir nodes in this line. */
+ if (have_directional) {
+ halfword e, p;
+ for (e = vlink(temp_head); e != null && e != cur_break(cur_p); e = vlink(e)) {
+ if (type(e) == dir_node) {
+ if (subtype(e) == normal_dir) {
+ dir_ptr = do_push_dir_node(dir_ptr, e);
+ } else if (dir_ptr != null && dir_dir(dir_ptr) == dir_dir(e)) {
+ dir_ptr = do_pop_dir_node(dir_ptr);
+ }
+ }
+ }
+ assert(e == cur_break(cur_p));
+ /*tex Insert dir nodes at the end of the current line. */
+ e = vlink(r);
+ for (p = dir_ptr; p != null; p = vlink(p)) {
+ halfword s = new_dir(dir_dir(p));
+ subtype(s) = cancel_dir;
+ delete_attribute_ref(node_attr(s));
+ node_attr(s) = node_attr(r);
+ add_node_attr_ref(node_attr(s));
+ couple_nodes(r, s);
+ try_couple_nodes(s, e);
+ r = s;
+ }
+ }
+ if (passive_right_box(cur_p) != null) {
+ halfword s = copy_node_list(passive_right_box(cur_p));
+ halfword e = vlink(r);
+ couple_nodes(r, s);
+ try_couple_nodes(s, e);
+ r = s;
+ }
+ if (q == null) {
+ q = r;
+ }
+ /*tex
+ Now [q] refers to the last node on the line and therefore the
+ rightmost breakpoint. The only exception is the case of a
+ discretionary break with non-empty |pre_break|, then |q| has been
+ changed to the last node of the |pre_break| list. If the par ends
+ with a \break command, the last line is utterly empty. That is the
+ case of |q==temp_head|.
+ */
+ if (q != temp_head && protrude_chars > 0) {
+ halfword p, ptmp;
+ if (disc_break && (is_char_node(q) || (type(q) != disc_node))) {
+ /*tex |q| is reset to the last node of |pre_break| */
+ p = q;
+ ptmp = p;
+ } else {
+ /*tex get |vlink(p) = q| */
+ p = alink(q);
+ assert(vlink(p) == q);
+ ptmp = p;
+ }
+ p = find_protchar_right(vlink(temp_head), p);
+ w = char_pw(p, right_side);
+ if (w != 0) {
+ /*tex we have found a marginal kern, append it after |ptmp| */
+ k = new_margin_kern(-w, last_rightmost_char, right_side);
+ delete_attribute_ref(node_attr(k));
+ node_attr(k) = node_attr(p);
+ add_node_attr_ref(node_attr(k));
+ try_couple_nodes(k, vlink(ptmp));
+ couple_nodes(ptmp,k);
+ if (ptmp == q)
+ q = vlink(q);
+ }
+ }
+ /*tex
+ If |q| was not a breakpoint at glue and has been reset to |rightskip|
+ then we append |rightskip| after |q| now.
+ */
+ if (!glue_break) {
+ /*tex Put the \.{\\rightskip} glue after node |q|. */
+ halfword r1 = new_glue((right_skip == null ? zero_glue : right_skip));
+ subtype(r1) = right_skip_code+1;
+ try_couple_nodes(r1,vlink(q));
+ delete_attribute_ref(node_attr(r1));
+ node_attr(r1) = node_attr(q);
+ add_node_attr_ref(node_attr(r1));
+ couple_nodes(q,r1);
+ q = r1;
+ }
+ /*tex
+ 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|;
+ Also put the \.{\\leftskip} glue at the left and detach this line.
+
+ 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
+ |vlink(temp_head)| pointing to the remainder of the paragraph, if
+ any.
+ */
+ r = vlink(q);
+ vlink(q) = null;
+
+ q = vlink(temp_head);
+ try_couple_nodes(temp_head, r);
+ if (passive_left_box(cur_p) != null && passive_left_box(cur_p) != 0) {
+ halfword s;
+ r = copy_node_list(passive_left_box(cur_p));
+ s = vlink(q);
+ couple_nodes(r,q);
+ q = r;
+ if ((cur_line == cur_list.pg_field + 1) && (s != null)) {
+ if (type(s) == hlist_node) {
+ if (list_ptr(s) == null) {
+ q = vlink(q);
+ try_couple_nodes(r,vlink(s));
+ try_couple_nodes(s, r);
+ }
+ }
+ }
+ }
+ /*tex
+ At this point |q| is the leftmost node; all discardable nodes have
+ been discarded
+ */
+ if (protrude_chars > 0) {
+ halfword p;
+ p = q;
+ p = find_protchar_left(p, false);
+ w = char_pw(p, left_side);
+ if (w != 0) {
+ k = new_margin_kern(-w, last_leftmost_char, left_side);
+ delete_attribute_ref(node_attr(k));
+ node_attr(k) = node_attr(q);
+ add_node_attr_ref(node_attr(k));
+ couple_nodes(k,q);
+ q = k;
+ }
+ }
+ if (! glue_is_zero(left_skip)) {
+ r = new_glue(left_skip);
+ subtype(r) = left_skip_code+1;
+ delete_attribute_ref(node_attr(r));
+ node_attr(r) = node_attr(q);
+ add_node_attr_ref(node_attr(r));
+ couple_nodes(r,q);
+ q = r;
+ }
+ /*tex
+ Put the \.{\\leftskip} glue at the left and detach this line. Call
+ the packaging subroutine, setting |just_box| to the justified box.
+ 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.
+ */
+ 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_indent = varmem[(par_shape_ptr + 2 * cur_line)].cint;
+ cur_width = varmem[(par_shape_ptr + 2 * cur_line + 1)].cint;
+ /* needs checking */
+ cur_indent = swap_parshape_indent(cur_indent,cur_width);
+ }
+ adjust_tail = adjust_head;
+ pre_adjust_tail = pre_adjust_head;
+ if (adjust_spacing > 0) {
+ just_box = hpack(q, cur_width, cal_expand_ratio, paragraph_dir);
+ } else {
+ just_box = hpack(q, cur_width, exactly, paragraph_dir);
+ }
+ shift_amount(just_box) = cur_indent;
+ subtype(just_box) = line_list;
+ /*tex
+ Call the packaging subroutine, setting |just_box| to the justified
+ box.
+ */
+ if ((vlink(contrib_head) != null))
+ checked_break_filter(pre_box);
+ if (pre_adjust_head != pre_adjust_tail) {
+ append_list(pre_adjust_head, pre_adjust_tail);
+ checked_break_filter(pre_adjust);
+ }
+ pre_adjust_tail = null;
+ append_to_vlist(just_box,lua_key_index(post_linebreak));
+ checked_break_filter(box);
+ if (adjust_head != adjust_tail) {
+ append_list(adjust_head, adjust_tail);
+ checked_break_filter(adjust);
+ }
+ adjust_tail = null;
+ /*tex
+ 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. 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.
+ */
+ if (cur_line + 1 != best_line) {
+ q = inter_line_penalties_ptr;
+ if (q != null) {
+ r = cur_line;
+ if (r > penalty(q))
+ r = penalty(q);
+ pen = penalty(q + r);
+ } else {
+ if (passive_pen_inter(cur_p) != 0) {
+ pen = passive_pen_inter(cur_p);
+ } else {
+ pen = inter_line_penalty;
+ }
+ }
+ q = club_penalties_ptr;
+ if (q != null) {
+ /*tex prevgraf */
+ r = cur_line - cur_list.pg_field;
+ if (r > penalty(q))
+ r = penalty(q);
+ pen += penalty(q + r);
+ } else if (cur_line == cur_list.pg_field + 1) {
+ /*tex prevgraf */
+ pen += club_penalty;
+ }
+ q = widow_penalties_ptr;
+ if (q != null) {
+ r = best_line - cur_line - 1;
+ if (r > penalty(q))
+ r = penalty(q);
+ pen += penalty(q + r);
+ } else if (cur_line + 2 == best_line) {
+ pen += widow_penalty;
+ }
+ if (disc_break) {
+ if (passive_pen_broken(cur_p) != 0) {
+ pen += passive_pen_broken(cur_p);
+ } else {
+ pen += broken_penalty;
+ }
+ }
+ if (pen != 0) {
+ r = new_penalty(pen,linebreak_penalty);
+ couple_nodes(cur_list.tail_field, r);
+ cur_list.tail_field = r;
+ }
+ }
+ /*tex
+ Append a penalty node, if a nonzero penalty is appropriate. 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 && !post_disc_break) {
+ /*tex
+ Prune unwanted nodes at the beginning of the next line. 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.
+ */
+ r = temp_head;
+ /*tex
+ Normally we have a matching math open and math close node but
+ when we cross a line the open node is removed, including any glue
+ or penalties following it. This is however not that nice for
+ callbacks that rely on symmetry. Of course this only counts for
+ one liners, as we can still have only a begin or end node on a
+ line. The end_of_math lua helper is made robust against this
+ although there you should be aware of the fact that one can end
+ up in the middle of math in callbacks that don't work on whole
+ paragraphs, but at least this branch makes sure that some proper
+ analysis is possible. (todo: check if math glyphs have the
+ subtype marked done).
+ */
+ while (1) {
+ q = vlink(r);
+ /*
+ if (q == cur_break(cur_p) || is_char_node(q))
+ break;
+ if (!((type(q) == local_par_node))) {
+ if (non_discardable(q) || (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern))
+ break;
+ }
+ */
+ if (type(q) == math_node) {
+ /*tex begin mathskip code */
+ surround(q) = 0 ;
+ reset_glue_to_zero(q);
+ /*tex end mathskip code */
+ }
+ if (q == cur_break(cur_p)) {
+ break;
+ } else if (is_char_node(q)) {
+ break;
+ } else if (type(q) == local_par_node) {
+ /*tex weird, in the middle somewhere */
+ } else if (non_discardable(q)) {
+ break;
+ } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) {
+ break;
+ }
+ r = q;
+ }
+ if (r != temp_head) {
+ vlink(r) = null;
+ flush_node_list(vlink(temp_head));
+ try_couple_nodes(temp_head, q);
+ }
+ }
+ } while (cur_p != null);
+ if ((cur_line != best_line) || (vlink(temp_head) != null))
+ confusion("line breaking");
+ /*tex prevgraf */
+ cur_list.pg_field = best_line - 1;
+ /*tex |dir_save| */
+ cur_list.dirs_field = dir_ptr;
+ dir_ptr = null;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h
new file mode 100644
index 00000000000..4aee1492c71
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h
@@ -0,0 +1,50 @@
+/* postlinebreak.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef POSTLINEBREAK_H
+# define POSTLINEBREAK_H
+
+# define append_list(A,B) do { \
+ couple_nodes(cur_list.tail_field,vlink((A))); \
+ cur_list.tail_field = (B); \
+} while (0)
+
+void ext_post_line_break(int paragraph_dir,
+ int right_skip,
+ int left_skip,
+ int protrude_chars,
+ halfword par_shape_ptr,
+ int adjust_spacing,
+ halfword inter_line_penalties_ptr,
+ int inter_line_penalty,
+ int club_penalty,
+ halfword club_penalties_ptr,
+ halfword widow_penalties_ptr,
+ int widow_penalty,
+ int broken_penalty,
+ halfword final_par_glue,
+ halfword best_bet,
+ halfword last_special_line,
+ scaled second_width,
+ scaled second_indent,
+ scaled first_width,
+ scaled first_indent, halfword best_line);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/primitive.c b/Build/source/texk/web2c/luatexdir/tex/primitive.c
new file mode 100644
index 00000000000..f16d69a333c
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/primitive.c
@@ -0,0 +1,785 @@
+/*
+
+primitive.w
+
+Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+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.
+
+*/
+
+/*tex The hash table: */
+
+two_halves *hash;
+
+/*tex Allocation pointer for |hash|: */
+
+halfword hash_used;
+
+/*tex |hash_extra=hash| above |eqtb_size|: */
+
+int hash_extra;
+
+/*tex Maximum of the hash array: */
+
+halfword hash_top;
+
+/*tex Pointer to next high hash location: */
+
+halfword hash_high;
+
+/*tex Are new identifiers legal? */
+
+boolean no_new_control_sequence;
+
+/*tex Total number of known identifiers: */
+
+int cs_count;
+
+/*tex Test if all positions are occupied: */
+
+#define hash_is_full (hash_used==hash_base)
+
+/*tex
+
+ \.{\\primitive} support needs a few extra variables and definitions,
+ like:
+
+*/
+
+#define prim_base 1
+
+/*tex
+
+The arrays |prim| and |prim_eqtb| are used for name -> cmd,chr lookups. The are
+modelled after |hash| and |eqtb|, except that primitives do not have an
+|eq_level|, that field is replaced by |origin|.
+
+*/
+
+/*tex Link for coalesced lists: */
+
+#define prim_next(a) prim[(a)].lhfield
+
+/*tex String number for control sequence name: */
+
+#define prim_text(a) prim[(a)].rh
+
+/*tex Test if all positions are occupied: */
+
+#define prim_is_full (prim_used==prim_base)
+
+#define prim_origin_field(a) (a).hh.b1
+
+#define prim_eq_type_field(a) (a).hh.b0
+
+#define prim_equiv_field(a) (a).hh.rh
+
+/*tex Level of definition: */
+
+#define prim_origin(a) prim_origin_field(prim_eqtb[(a)])
+
+/*tex Command code for equivalent: */
+
+#define prim_eq_type(a) prim_eq_type_field(prim_eqtb[(a)])
+
+/*tex Equivalent value: */
+
+#define prim_equiv(a) prim_equiv_field(prim_eqtb[(a)])
+
+/*tex Allocation pointer for |prim|: */
+
+static pointer prim_used;
+
+/*tex The primitives table: */
+
+static two_halves prim[(prim_size + 1)];
+
+static memory_word prim_eqtb[(prim_size + 1)];
+
+/*tex
+
+The array |prim_data| works the other way around, it is used for cmd,chr -> name
+lookups.
+
+*/
+
+typedef struct prim_info {
+ /*tex Number of name entries: */
+ halfword subids;
+ /*tex Offset to be used for |chr_code|s: */
+ halfword offset;
+ /*tex Array of names: */
+ str_number *names;
+} prim_info;
+
+static prim_info prim_data[(last_cmd + 1)];
+
+/*tex
+
+Initialize the memory arrays:
+
+*/
+
+void init_primitives(void)
+{
+ int k;
+ memset(prim_data, 0, (sizeof(prim_info) * (last_cmd + 1)));
+ memset(prim, 0, (sizeof(two_halves) * (prim_size + 1)));
+ memset(prim_eqtb, 0, (sizeof(memory_word) * (prim_size + 1)));
+ for (k = 0; k <= prim_size; k++) {
+ prim_eq_type(k) = undefined_cs_cmd;
+ }
+}
+
+/*tex Nothing is used (yet). */
+
+void ini_init_primitives(void)
+{
+ prim_used = prim_size;
+}
+
+
+/*tex
+
+The value of |hash_prime| should be roughly 85\%! 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@>
+
+*/
+
+static halfword compute_hash(const char *j, unsigned int l, halfword prime_number)
+{
+ int k;
+ halfword h = (unsigned char) *j;
+ for (k = 1; k <= (int)(l - 1); k++) {
+ h = h + h + (unsigned char) *(j + k);
+ while (h >= prime_number) {
+ h = h - prime_number;
+ }
+ }
+ return h;
+}
+
+/*tex
+
+Here is the subroutine that searches the primitive table for an identifier.
+
+*/
+
+pointer prim_lookup(str_number s)
+{
+ /*tex The hash code: */
+ int h;
+ /*tex The index in the |hash| array: */
+ pointer p;
+ unsigned char *j;
+ unsigned l;
+ if (s < STRING_OFFSET) {
+ p = s;
+ if ((p < 0) || (get_prim_eq_type(p) == undefined_cs_cmd)) {
+ p = undefined_primitive;
+ }
+ } else {
+ j = str_string(s);
+ l = (unsigned) str_length(s);
+ h = compute_hash((char *) j, l, prim_prime);
+ /*tex We start searching here; note that |0<=h<hash_prime|. */
+ p = h + prim_base;
+ while (1) {
+ if (prim_text(p) > 0)
+ if (str_length(prim_text(p)) == l)
+ if (str_eq_str(prim_text(p), s))
+ goto FOUND;
+ if (prim_next(p) == 0) {
+ if (no_new_control_sequence) {
+ p = undefined_primitive;
+ } else {
+ /*tex Insert a new primitive after |p|, then make |p| point to it. */
+ if (prim_text(p) > 0) {
+ do {
+ /*tex Search for an empty location in |prim| */
+ if (prim_is_full) {
+ overflow("primitive size", prim_size);
+ }
+ decr(prim_used);
+ } while (prim_text(prim_used) != 0);
+ prim_next(p) = prim_used;
+ p = prim_used;
+ }
+ prim_text(p) = s;
+ }
+ goto FOUND;
+ }
+ p = prim_next(p);
+ }
+ }
+ FOUND:
+ return p;
+}
+
+/*tex
+
+How to test a csname for primitive-ness?
+
+*/
+
+boolean is_primitive(str_number csname)
+{
+ int n, m;
+ char *ss;
+ m = prim_lookup(csname);
+ ss = makecstring(csname);
+ n = string_lookup(ss, str_length(csname));
+ free(ss);
+ return ((n != undefined_cs_cmd) && (m != undefined_primitive) &&
+ (eq_type(n) == prim_eq_type(m)) && (equiv(n) == prim_equiv(m)));
+}
+
+
+/*tex
+
+A few simple accessors.
+
+*/
+
+quarterword get_prim_eq_type(int p)
+{
+ return prim_eq_type(p);
+}
+
+quarterword get_prim_origin(int p)
+{
+ return prim_origin(p);
+}
+
+halfword get_prim_equiv(int p)
+{
+ return prim_equiv(p);
+}
+
+str_number get_prim_text(int p)
+{
+ return prim_text(p);
+}
+
+
+/*tex
+
+Dumping and undumping.
+
+*/
+
+void dump_primitives(void)
+{
+ int p, q;
+ for (p = 0; p <= prim_size; p++) {
+ dump_hh(prim[p]);
+ }
+ for (p = 0; p <= prim_size; p++) {
+ dump_wd(prim_eqtb[p]);
+ }
+ for (p = 0; p <= last_cmd; p++) {
+ dump_int(prim_data[p].offset);
+ dump_int(prim_data[p].subids);
+ for (q = 0; q < prim_data[p].subids; q++) {
+ dump_int(prim_data[p].names[q]);
+ }
+ }
+}
+
+void undump_primitives(void)
+{
+ int p, q;
+ for (p = 0; p <= prim_size; p++) {
+ undump_hh(prim[p]);
+ }
+ for (p = 0; p <= prim_size; p++) {
+ undump_wd(prim_eqtb[p]);
+ }
+ for (p = 0; p <= last_cmd; p++) {
+ undump_int(prim_data[p].offset);
+ undump_int(prim_data[p].subids);
+ if (prim_data[p].subids > 0) {
+ prim_data[p].names = (str_number *) xmalloc((unsigned) ((unsigned) prim_data[p].subids * sizeof(str_number *)));
+ for (q = 0; q < prim_data[p].subids; q++) {
+ undump_int(prim_data[p].names[q]);
+ }
+ }
+ }
+}
+
+/*tex
+
+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.
+
+Because the definitions of the actual user-accessible name of a primitive can be
+postponed until runtime, the function |primitive_def| is needed that does nothing
+except creating the control sequence name.
+
+*/
+
+void primitive_def(const char *s, size_t l, quarterword c, halfword o)
+{
+ int nncs = no_new_control_sequence;
+ no_new_control_sequence = false;
+ /*tex This creates the |text()| string: */
+ cur_val = string_lookup(s, l);
+ no_new_control_sequence = nncs;
+ eq_level(cur_val) = level_one;
+ eq_type(cur_val) = c;
+ equiv(cur_val) = o;
+}
+
+/*tex
+
+The function |store_primitive_name| sets up the bookkeeping for the reverse
+lookup. It is quite paranoid, because it is easy to mess this up accidentally.
+
+The |offset| is needed because sometimes character codes (in |o|) are indices
+into |eqtb| or are offset by a magical value to make sure they do not conflict
+with something else. We don't want the |prim_data[c].names| to have too many
+entries as it will just be wasted room, so |offset| is substracted from |o|
+because creating or accessing the array. The |assert(idx<=0xFFFF)| is not
+strictly needed, but it helps catch errors of this kind.
+
+*/
+
+static void store_primitive_name(str_number s, quarterword c, halfword o, halfword offset)
+{
+ int idx;
+ /*
+ if (prim_data[c].offset != 0 && prim_data[c].offset != offset) {
+ assert(false);
+ }
+ */
+ prim_data[c].offset = offset;
+ idx = ((int) o - offset);
+ /*
+ assert(idx >= 0);
+ assert(idx <= 0xFFFF);
+ */
+ if (prim_data[c].subids < (idx + 1)) {
+ str_number *new = (str_number *) xcalloc((unsigned) (idx + 1), sizeof(str_number *));
+ if (prim_data[c].names != NULL) {
+ /*
+ assert(prim_data[c].subids);
+ */
+ memcpy(new, (prim_data[c].names), (unsigned) (prim_data[c].subids) * sizeof(str_number));
+ free(prim_data[c].names);
+ }
+ prim_data[c].names = new;
+ prim_data[c].subids = idx + 1;
+ }
+ prim_data[c].names[idx] = s;
+}
+
+/*tex
+
+Compared to tex82, |primitive| has two extra parameters. The |off| is an offset
+that will be passed on to |store_primitive_name|, the |cmd_origin| is the bit
+that is used to group primitives by originator.
+
+*/
+
+void primitive(const char *thes, quarterword c, halfword o, halfword off, int cmd_origin)
+{
+ /*tex Needed to fill |prim_eqtb|: */
+ int prim_val;
+ str_number ss;
+ ss = maketexstring(thes);
+ if (cmd_origin == tex_command || cmd_origin == core_command) {
+ primitive_def(thes, strlen(thes), c, o);
+ }
+ prim_val = prim_lookup(ss);
+ prim_origin(prim_val) = (quarterword) cmd_origin;
+ prim_eq_type(prim_val) = c;
+ prim_equiv(prim_val) = o;
+ store_primitive_name(ss, c, o, off);
+}
+
+/*tex
+
+Here is a helper that does the actual hash insertion. This code far from ideal:
+the existance of |hash_extra| changes all the potential (short) coalesced lists
+into a single (long) one. This will create a slowdown.
+
+*/
+
+static halfword insert_id(halfword p, const unsigned char *j, unsigned int l)
+{
+ unsigned saved_cur_length;
+ unsigned saved_cur_string_size;
+ unsigned char *saved_cur_string;
+ const unsigned char *k;
+ if (cs_text(p) > 0) {
+ if (hash_high < hash_extra) {
+ incr(hash_high);
+ /*tex
+ Can't we use |eqtb_top| here (perhaps because that is not
+ finalized yet when called from |primitive|?
+ */
+ cs_next(p) = hash_high + eqtb_size;
+ p = cs_next(p);
+ } else {
+ /*tex
+ Search for an empty location in |hash|.
+ */
+ do {
+ if (hash_is_full)
+ overflow("hash size", (unsigned) (hash_size + hash_extra));
+ decr(hash_used);
+ } while (cs_text(hash_used) != 0);
+ cs_next(p) = hash_used;
+ p = hash_used;
+ }
+ }
+ saved_cur_length = cur_length;
+ saved_cur_string = cur_string;
+ saved_cur_string_size = cur_string_size;
+ reset_cur_string();
+ for (k = j; k <= j + l - 1; k++) {
+ append_char(*k);
+ }
+ cs_text(p) = make_string();
+ cur_length = saved_cur_length;
+ xfree(cur_string);
+ cur_string = saved_cur_string;
+ cur_string_size = saved_cur_string_size;
+ incr(cs_count);
+ return p;
+}
+
+
+/*tex
+
+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.. (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.
+
+*/
+
+pointer id_lookup(int j, int l)
+{
+ /*tex The hash code: */
+ int h;
+ /*tex The index in |hash| array: */
+ pointer p;
+ h = compute_hash((char *) (buffer + j), (unsigned) l, hash_prime);
+ /*tex We start searching here. Note that |0<=h<hash_prime|: */
+ p = h + hash_base;
+ while (1) {
+ if (cs_text(p) > 0)
+ if (str_length(cs_text(p)) == (unsigned) l)
+ if (str_eq_buf(cs_text(p), j))
+ goto FOUND;
+ if (cs_next(p) == 0) {
+ if (no_new_control_sequence) {
+ p = undefined_control_sequence;
+ } else {
+ p = insert_id(p, (buffer + j), (unsigned) l);
+ }
+ goto FOUND;
+ }
+ p = cs_next(p);
+ }
+ FOUND:
+ return p;
+}
+
+/*tex
+
+Here is a similar subroutine for finding a primitive in the hash.
+This one is based on a C string.
+
+*/
+
+pointer string_lookup(const char *s, size_t l)
+{
+ /*tex The hash code: */
+ int h;
+ /*tex The index in |hash| array: */
+ pointer p;
+ h = compute_hash(s, (unsigned) l, hash_prime);
+ /*tex We start searching here. Note that |0<=h<hash_prime|: */
+ p = h + hash_base;
+ while (1) {
+ if (cs_text(p) > 0)
+ if (str_eq_cstr(cs_text(p), s, l))
+ goto FOUND;
+ if (cs_next(p) == 0) {
+ if (no_new_control_sequence) {
+ p = undefined_control_sequence;
+ } else {
+ p = insert_id(p, (const unsigned char *) s, (unsigned) l);
+ }
+ goto FOUND;
+ }
+ p = cs_next(p);
+ }
+ FOUND:
+ return p;
+}
+
+/*tex
+
+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| use to be a rather tedious listing of print commands,
+and most of it was essentially an inverse to the |primitive| routine that enters
+a \TeX\ primitive into |eqtb|.
+
+Thanks to |prim_data|, there is no need for all that tediousness. What is left of
+|primt_cnd_chr| are just the exceptions to the general rule that the
+|cmd,chr_code| pair represents in a single primitive command.
+
+*/
+
+#define chr_cmd(A) do { tprint(A); print(chr_code); } while (0)
+
+static void prim_cmd_chr(quarterword cmd, halfword chr_code)
+{
+ int idx = chr_code - prim_data[cmd].offset;
+ if (cmd <= last_cmd &&
+ idx >= 0 && idx < prim_data[cmd].subids &&
+ prim_data[cmd].names != NULL && prim_data[cmd].names[idx] != 0) {
+ tprint_esc("");
+ print(prim_data[cmd].names[idx]);
+ } else {
+ /* \TEX82 didn't print the |cmd,idx| information, but it may be useful. */
+ tprint("[unknown command code! (");
+ print_int(cmd);
+ tprint(", ");
+ print_int(idx);
+ tprint(")]");
+ }
+}
+
+void print_cmd_chr(quarterword cmd, halfword chr_code)
+{
+ int n;
+ switch (cmd) {
+ case left_brace_cmd:
+ chr_cmd("begin-group character ");
+ break;
+ case right_brace_cmd:
+ chr_cmd("end-group character ");
+ break;
+ case math_shift_cmd:
+ chr_cmd("math shift character ");
+ break;
+ case mac_param_cmd:
+ if (chr_code == tab_mark_cmd_code)
+ tprint_esc("alignmark");
+ else
+ chr_cmd("macro parameter character ");
+ break;
+ case sup_mark_cmd:
+ chr_cmd("superscript character ");
+ break;
+ case sub_mark_cmd:
+ chr_cmd("subscript character ");
+ break;
+ case endv_cmd:
+ tprint("end of alignment template");
+ break;
+ case spacer_cmd:
+ chr_cmd("blank space ");
+ break;
+ case letter_cmd:
+ chr_cmd("the letter ");
+ break;
+ case other_char_cmd:
+ chr_cmd("the character ");
+ break;
+ case tab_mark_cmd:
+ if (chr_code == span_code)
+ tprint_esc("span");
+ else if (chr_code == tab_mark_cmd_code)
+ tprint_esc("aligntab");
+ else
+ chr_cmd("alignment tab character ");
+ break;
+ case if_test_cmd:
+ if (chr_code >= unless_code)
+ tprint_esc("unless");
+ prim_cmd_chr(cmd, (chr_code % unless_code));
+ break;
+ case char_given_cmd:
+ tprint_esc("char");
+ print_qhex(chr_code);
+ break;
+ case math_given_cmd:
+ /*tex
+ Okay, it's better for old macro packages that mess with meaning
+ to report a traditional value. A compromise.
+ */
+ tprint_esc("mathchar");
+ show_mathcode_value_old(chr_code);
+ break;
+ case xmath_given_cmd:
+ tprint_esc("Umathchar");
+ show_mathcode_value(mathchar_from_integer(chr_code, umath_mathcode));
+ break;
+ case lua_expandable_call_cmd:
+ tprint("expandable luacall ");
+ print_int(chr_code);
+ break;
+ case lua_local_call_cmd:
+ tprint("local luacall ");
+ print_int(chr_code);
+ break;
+ case lua_call_cmd:
+ tprint("luacall ");
+ print_int(chr_code);
+ break;
+ case set_font_cmd:
+ tprint("select font ");
+ tprint(font_name(chr_code));
+ if (font_size(chr_code) != font_dsize(chr_code)) {
+ tprint(" at ");
+ print_scaled(font_size(chr_code));
+ tprint("pt");
+ }
+ break;
+ case undefined_cs_cmd:
+ tprint("undefined");
+ break;
+ case call_cmd:
+ case long_call_cmd:
+ case outer_call_cmd:
+ case long_outer_call_cmd:
+ n = cmd - call_cmd;
+ if (token_info(token_link(chr_code)) == protected_token)
+ n = n + 4;
+ if (odd(n / 4))
+ tprint_esc("protected");
+ if (odd(n))
+ tprint_esc("long");
+ if (odd(n / 2))
+ tprint_esc("outer");
+ if (n > 0)
+ tprint(" ");
+ tprint("macro");
+ break;
+ case assign_glue_cmd:
+ case assign_mu_glue_cmd:
+ if (chr_code < skip_base) {
+ prim_cmd_chr(cmd, chr_code);
+ } else if (chr_code < mu_skip_base) {
+ tprint_esc("skip");
+ print_int(chr_code - skip_base);
+ } else {
+ tprint_esc("muskip");
+ print_int(chr_code - mu_skip_base);
+ }
+ break;
+ case assign_toks_cmd:
+ if (chr_code >= toks_base) {
+ tprint_esc("toks");
+ print_int(chr_code - toks_base);
+ } else {
+ prim_cmd_chr(cmd, chr_code);
+ }
+ break;
+ case assign_int_cmd:
+ if (chr_code < count_base) {
+ prim_cmd_chr(cmd, chr_code);
+ } else {
+ tprint_esc("count");
+ print_int(chr_code - count_base);
+ }
+ break;
+ case assign_attr_cmd:
+ tprint_esc("attribute");
+ print_int(chr_code - attribute_base);
+ break;
+ case assign_dimen_cmd:
+ if (chr_code < scaled_base) {
+ prim_cmd_chr(cmd, chr_code);
+ } else {
+ tprint_esc("dimen");
+ print_int(chr_code - scaled_base);
+ }
+ break;
+ case normal_cmd:
+ if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) {
+ prim_cmd_chr(cmd, chr_code);
+ } else {
+ tprint("[unknown command! (");
+ print_int(chr_code);
+ tprint(")]");
+ }
+ break;
+ case extension_cmd:
+ if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) {
+ prim_cmd_chr(cmd, chr_code);
+ } else {
+ tprint("[unknown extension! (");
+ print_int(chr_code);
+ tprint(")]");
+
+ }
+ break;
+ case node_cmd:
+ tprint("node ");
+ print_int(chr_code);
+ break;
+ default:
+ /*tex These are most commands, actually. */
+ prim_cmd_chr(cmd, chr_code);
+ break;
+ }
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/primitive.h b/Build/source/texk/web2c/luatexdir/tex/primitive.h
new file mode 100644
index 00000000000..b6b13c74f73
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/primitive.h
@@ -0,0 +1,89 @@
+/* primitive.h
+
+ Copyright 2008-2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef LUATEX_PRIMITIVE_H
+# define LUATEX_PRIMITIVE_H 1
+
+/* This enum is a list of origins for primitive commands */
+
+typedef enum {
+ /* Can be modified as suggested ?*/
+ tex_command = 1, /* 1 */
+ etex_command = 2, /* 2 */
+ pdftex_command = 16, /* 4 */ /* obsolete */
+ luatex_command = 32, /* 8 */
+ core_command = 64, /* 16 */
+ no_command = 128, /* 32 */
+ umath_command = 256, /* 64 */ /* obsolete */
+} command_origin;
+
+# define hash_size 65536 /* maximum number of control sequences; it should be at most about |(fix_mem_max-fix_mem_min)/10| */
+# define hash_prime 55711 /* a prime number equal to about 85\pct! of |hash_size| */
+
+extern two_halves *hash; /* the hash table */
+extern halfword hash_used; /* allocation pointer for |hash| */
+extern int hash_extra; /* |hash_extra=hash| above |eqtb_size| */
+extern halfword hash_top; /* maximum of the hash array */
+extern halfword hash_high; /* pointer to next high hash location */
+extern boolean no_new_control_sequence; /* are new identifiers legal? */
+extern int cs_count; /* total number of known identifiers */
+
+# define cs_next(a) hash[(a)].lhfield /* link for coalesced lists */
+# define cs_text(a) hash[(a)].rh
+ /* string number for control sequence name */
+
+# define undefined_primitive 0
+# define prim_size 2100 /* maximum number of primitives */
+# define prim_prime 1777 /* about 85\pct! of |primitive_size| */
+
+extern void init_primitives(void);
+extern void ini_init_primitives(void);
+
+extern halfword compute_pool_hash(pool_pointer j, pool_pointer l,
+ halfword prime_number);
+extern pointer prim_lookup(str_number s);
+
+extern boolean is_primitive(str_number csname);
+
+extern quarterword get_prim_eq_type(int p);
+extern halfword get_prim_equiv(int p);
+extern str_number get_prim_text(int p);
+extern quarterword get_prim_origin(int p);
+
+extern void dump_primitives(void);
+extern void undump_primitives(void);
+
+# define primitive_tex(a,b,c,d) primitive((a),(b),(c),(d),tex_command)
+# define primitive_etex(a,b,c,d) primitive((a),(b),(c),(d),etex_command)
+# define primitive_pdftex(a,b,c,d) primitive((a),(b),(c),(d),pdftex_command)
+# define primitive_luatex(a,b,c,d) primitive((a),(b),(c),(d),luatex_command)
+# define primitive_umath(a,b,c,d) primitive((a),(b),(c),(d),umath_command)
+# define primitive_core(a,b,c,d) primitive((a),(b),(c),(d),core_command)
+# define primitive_no(a,b,c,d) primitive((a),(b),(c),(d),no_command)
+
+extern void primitive(const char *ss, quarterword c, halfword o, halfword off,
+ int cmd_origin);
+extern void primitive_def(const char *s, size_t l, quarterword c, halfword o);
+extern void print_cmd_chr(quarterword cmd, halfword chr_code);
+
+extern pointer string_lookup(const char *s, size_t l);
+extern pointer id_lookup(int j, int l);
+
+#endif /* LUATEX_PRIMITIVE_H */
diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.c b/Build/source/texk/web2c/luatexdir/tex/printing.c
new file mode 100644
index 00000000000..31125d2ebca
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/printing.c
@@ -0,0 +1,1350 @@
+/*
+
+printing.w
+
+Copyright 2009-2013 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+#define wlog(A) fputc(A,log_file)
+#define wterm(A) fputc(A,term_out)
+
+int new_string_line = 0;
+int escape_controls = 1;
+
+/*tex
+
+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:
+
+\startitemize
+
+\startitem
+ |term_and_log|, the normal setting, prints on the terminal and on the
+ transcript file.
+\stopitem
+
+\startitem
+ |log_only|, prints only on the transcript file.
+\stopitem
+
+\startitem
+ |term_only|, prints only on the terminal.
+\stopitem
+
+\startitem
+ |no_print|, doesn't print at all. This is used only in rare cases before the
+ transcript file is open.
+\stopitem
+
+\startitem
+ |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.
+\stopitem
+
+\startitem
+ |new_string|, appends the output to the current string in the string pool.
+\stopitem
+
+\startitem
+ 0 to 15, prints on one of the sixteen files for \.{\\write} output.
+\stopitem
+
+\stopitemize
+
+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.
+
+*/
+
+/*tex transcript of \TeX\ session */
+
+alpha_file log_file;
+
+/*tex where to print a message */
+
+int selector = term_only;
+
+/*tex digits in a number being output */
+
+int dig[23];
+
+/*tex the number of characters recently printed */
+
+int tally = 0;
+
+/*tex the number of characters on the current terminal line */
+
+int term_offset = 0;
+
+/*tex the number of characters on the current file line */
+
+int file_offset = 0;
+
+/*tex circular buffer for pseudoprinting */
+
+packed_ASCII_code trick_buf[(ssup_error_line + 1)];
+
+/*tex threshold for pseudoprinting, explained later */
+
+int trick_count;
+
+/*tex another variable for pseudoprinting */
+
+int first_count;
+
+/*tex for minor adjustments to |show_token_list| */
+
+boolean inhibit_par_tokens = false;
+
+/*tex
+
+To end a line of text output, we call |print_ln|
+
+*/
+
+void print_ln(void)
+{
+ switch (selector) {
+ case no_print:
+ break;
+ case term_only:
+ wterm_cr();
+ term_offset = 0;
+ break;
+ case log_only:
+ wlog_cr();
+ file_offset = 0;
+ break;
+ case term_and_log:
+ wterm_cr();
+ wlog_cr();
+ term_offset = 0;
+ file_offset = 0;
+ break;
+ case pseudo:
+ break;
+ case new_string:
+ if (new_string_line > 0)
+ print_char(new_string_line);
+ break;
+ default:
+ fprintf(write_file[selector], "\n");
+ break;
+ }
+ /*tex |tally| is not affected */
+}
+
+/*tex
+
+The |print_char| procedure sends one byte to the desired destination. All
+printing comes through |print_ln| or |print_char|, except for the case of
+|tprint| (see below).
+
+The checking of the line length is an inheritance from previosu engines and we
+might drop it after release 1.0. We're not too picky about the exact match of
+that length because we have utf output so length is then a bit fuzzy anyway.
+
+*/
+
+#define needs_escaping(A) \
+ ((! escape_controls) || (A>=0x20) || (A==0x0A) || (A==0x0D) || (A==0x09))
+
+#define escaped_char(A) \
+ A+64
+
+#define wterm_char(A) \
+ if (needs_escaping(A)) { \
+ wterm(A); \
+ } else { \
+ if (term_offset+2>=max_print_line) { \
+ wterm_cr(); \
+ term_offset=0; \
+ } \
+ wterm('^'); \
+ wterm('^'); \
+ wterm(escaped_char(A)); \
+ term_offset += 2; \
+ }
+
+#define needs_wrapping(A,B) \
+ ( (A>=0xC0) && \
+ (((A>=0xF0) && (B+4>=max_print_line)) || \
+ ((A>=0xE0) && (B+3>=max_print_line)) || \
+ ( (B+2>=max_print_line))) \
+ )
+
+#define fix_term_offset(A) \
+ if (needs_wrapping(A,term_offset)){ \
+ wterm_cr(); \
+ term_offset=0; \
+ }
+
+#define fix_log_offset(A) \
+ if (needs_wrapping(A,file_offset)){ \
+ wlog_cr(); \
+ file_offset=0; \
+ }
+
+void print_char(int s)
+{
+ if (s < 0 || s > 255) {
+ formatted_warning("print","weird character %i",s);
+ return;
+ }
+ if (s == new_line_char_par) {
+ if (selector < pseudo) {
+ print_ln();
+ return;
+ }
+ }
+ switch (selector) {
+ case no_print:
+ break;
+ case term_only:
+ fix_term_offset(s);
+ wterm_char(s);
+ incr(term_offset);
+ if (term_offset == max_print_line) {
+ wterm_cr();
+ term_offset = 0;
+ }
+ break;
+ case log_only:
+ fix_log_offset(s);
+ wlog(s);
+ incr(file_offset);
+ if (file_offset == max_print_line) {
+ wlog_cr();
+ file_offset = 0;
+ }
+ break;
+ case term_and_log:
+ fix_term_offset(s);
+ fix_log_offset(s);
+ wterm_char(s);
+ wlog(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 pseudo:
+ if (tally < trick_count)
+ trick_buf[tally % error_line] = (packed_ASCII_code) s;
+ break;
+ case new_string:
+ append_char(s);
+ break;
+ default:
+ fprintf(write_file[selector], "%c", s);
+ }
+ incr(tally);
+}
+
+/*tex
+
+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.)
+
+The first 256 entries above the 17th unicode plane are used for a special trick:
+when \TeX\ has to print items in that range, it will instead print the character
+that results from substracting 0x110000 from that value. This allows
+byte-oriented output to things like \.{\\specials} and \.{\\pdfextension
+literals}. Todo: Perhaps it would be useful to do the same substraction while
+typesetting.
+
+
+*/
+
+void print(int s)
+{
+ if (s >= str_ptr) {
+ normal_warning("print","bad string pointer");
+ return;
+ } else if (s < STRING_OFFSET) {
+ if (s < 0) {
+ normal_warning("print","bad string offset");
+ } else {
+ /*tex We're not sure about this so it's disabled for now! */
+ if ((false) && (selector > pseudo)) {
+ /*tex internal strings are not expanded */
+ print_char(s);
+ return;
+ }
+ if (s == new_line_char_par) {
+ if (selector < pseudo) {
+ print_ln();
+ return;
+ }
+ }
+ if (s <= 0x7F) {
+ print_char(s);
+ } else if (s <= 0x7FF) {
+ print_char(0xC0 + (s / 0x40));
+ print_char(0x80 + (s % 0x40));
+ } else if (s <= 0xFFFF) {
+ print_char(0xE0 + (s / 0x1000));
+ print_char(0x80 + ((s % 0x1000) / 0x40));
+ print_char(0x80 + ((s % 0x1000) % 0x40));
+ } else if (s >= 0x110000) {
+ int c = s - 0x110000;
+ if (c >= 256) {
+ formatted_warning("print", "bad raw byte to print (c=%d), skipped",c);
+ } else {
+ print_char(c);
+ }
+ } else {
+ print_char(0xF0 + (s / 0x40000));
+ print_char(0x80 + ((s % 0x40000) / 0x1000));
+ print_char(0x80 + (((s % 0x40000) % 0x1000) / 0x40));
+ print_char(0x80 + (((s % 0x40000) % 0x1000) % 0x40));
+ }
+ }
+ return;
+ }
+ if (selector == new_string) {
+ append_string(str_string(s), (unsigned) str_length(s));
+ return;
+ }
+ lprint(&str_lstring(s));
+}
+
+void lprint (lstring *ss) {
+ /*tex current character code position */
+ unsigned char *j, *l;
+ j = ss->s;
+ l = j + ss->l;
+ while (j < l) {
+ /*tex I don't bother checking the last two bytes explicitly */
+ /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80 */
+ if ((j < l - 4) && (*j == 0xF4) && (*(j + 1) == 0x90)) {
+ int c = (*(j + 2) - 128) * 64 + (*(j + 3) - 128);
+ assert(c >= 0 && c < 256);
+ print_char(c);
+ j = j + 4;
+ } else {
+ print_char(*j);
+ incr(j);
+ }
+ }
+}
+
+/*tex
+
+The procedure |print_nl| is like |print|, but it makes sure that the string
+appears at the beginning of a new line.
+
+*/
+
+/*tex Moev to the beginning of the next line. */
+
+void print_nlp(void)
+{
+ if (new_string_line > 0) {
+ print_char(new_string_line);
+ } else if (((term_offset > 0) && (odd(selector))) ||
+ ((file_offset > 0) && (selector >= log_only))) {
+ print_ln();
+ }
+}
+
+/*tex Prints string |s| at beginning of the next line. */
+
+void print_nl(str_number s)
+{
+ print_nlp();
+ print(s);
+}
+
+/*tex
+
+|char *| versions of the same procedures. |tprint| is different because it uses
+buffering, which works well because most of the output actually comes through
+|tprint|.
+
+*/
+
+#define t_flush_buffer(target,offset) \
+ buffer[i++] = '\n'; \
+ buffer[i++] = '\0';\
+ fputs(buffer, target); \
+ i = 0; \
+ buffer[0] = '\0'; \
+ offset=0;
+
+void tprint(const char *sss)
+{
+ char *buffer = NULL;
+ int i = 0;
+ int newlinechar = (lua_only == 1) ? 10 : new_line_char_par;
+ int dolog = 0;
+ int doterm = 0;
+ switch (selector) {
+ case no_print:
+ return;
+ break;
+ case term_only:
+ doterm = 1;
+ break;
+ case log_only:
+ dolog = 1;
+ break;
+ case term_and_log:
+ dolog = 1;
+ doterm = 1;
+ break;
+ case pseudo:
+ while (*sss) {
+ if (tally < trick_count) {
+ trick_buf[tally % error_line] = (packed_ASCII_code) *sss++;
+ tally++;
+ } else {
+ return;
+ }
+ }
+ return;
+ break;
+ case new_string:
+ append_string((const unsigned char *)sss, (unsigned) strlen(sss));
+ return;
+ break;
+ default:
+ {
+ char *newstr = xstrdup(sss);
+ char *s;
+ for (s=newstr;*s;s++) {
+ if (*s == newlinechar) {
+ *s = '\n';
+ }
+ }
+ fputs(newstr, write_file[selector]);
+ free(newstr);
+ return;
+ }
+ break;
+ }
+ /*tex What is left is the 3 term/log settings. */
+ if (dolog || doterm) {
+ buffer = xmalloc(strlen(sss)*3);
+ /*tex The |wrapup_run| callback acts when the log file is already closed.*/
+ if (dolog && log_opened_global) {
+ const unsigned char *ss = (const unsigned char *) sss;
+ while (*ss) {
+ int s = *ss++;
+ if (needs_wrapping(s,file_offset) || s == newlinechar) {
+ t_flush_buffer(log_file,file_offset);
+ }
+ if (s != newlinechar) {
+ buffer[i++] = s;
+ if (file_offset++ == max_print_line) {
+ t_flush_buffer(log_file,file_offset);
+ }
+ }
+ }
+ if (*buffer) {
+ buffer[i++] = '\0';
+ fputs(buffer, log_file);
+ buffer[0] = '\0';
+ }
+ i = 0;
+ }
+ if (doterm) {
+ const unsigned char *ss = (const unsigned char *) sss;
+ while (*ss) {
+ int s = *ss++;
+ if (needs_wrapping(s,term_offset) || s == newlinechar) {
+ t_flush_buffer(term_out,term_offset);
+ }
+ if (s != newlinechar) {
+ if (needs_escaping(s)) {
+ buffer[i++] = s;
+ } else {
+ buffer[i++] = '^';
+ buffer[i++] = '^';
+ buffer[i++] = escaped_char(s);
+ term_offset += 2;
+ }
+ if (++term_offset == max_print_line) {
+ t_flush_buffer(term_out,term_offset);
+ }
+ }
+ }
+ if (*buffer) {
+ buffer[i++] = '\0';
+ fputs(buffer, term_out);
+ }
+ }
+ free(buffer);
+ }
+}
+
+void tprint_nl(const char *s)
+{
+ print_nlp();
+ tprint(s);
+}
+
+/*tex
+
+Here is the very first thing that \TeX\ prints: a headline that identifies the
+version number and format package. The |term_offset| variable is temporarily
+incorrect, but the discrepancy is not serious since we assume that the banner and
+format identifier together will occupy at most |max_print_line| character
+positions.
+
+*/
+
+void print_banner(const char *v)
+{
+ int callback_id = callback_defined(start_run_callback);
+ if (callback_id == 0) {
+ fprintf(term_out, "This is " MyName ", Version %s%s ", v, WEB2CVERSION);
+ if (format_ident > 0)
+ print(format_ident);
+ print_ln();
+ if (show_luahashchars) {
+ wterm(' ');
+ fprintf(term_out,"Number of bits used by the hash function (" my_name "): %d",LUAI_HASHLIMIT);
+ print_ln();
+ }
+ if (shellenabledp) {
+ wterm(' ');
+ if (restrictedshell) {
+ fprintf(term_out, "restricted ");
+ }
+ fprintf(term_out, "system commands enabled.\n");
+ }
+ } else if (callback_id > 0) {
+ run_callback(callback_id, "->");
+ }
+}
+
+void log_banner(const char *v)
+{
+ const char *months[] = { " ",
+ "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
+ "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
+ };
+ unsigned month = (unsigned) month_par;
+ if (month > 12)
+ month = 0;
+ fprintf(log_file, "This is " MyName ", Version %s%s ", v, WEB2CVERSION);
+ print(format_ident);
+ print_char(' ');
+ print_char(' ');
+ print_int(day_par);
+ print_char(' ');
+ fprintf(log_file, "%s", months[month]);
+ print_char(' ');
+ print_int(year_par);
+ print_char(' ');
+ print_two(time_par / 60);
+ print_char(':');
+ print_two(time_par % 60);
+ if (shellenabledp) {
+ wlog_cr();
+ wlog(' ');
+ if (restrictedshell)
+ fprintf(log_file, "restricted ");
+ fprintf(log_file, "system commands enabled.");
+ }
+ if (filelineerrorstylep) {
+ wlog_cr();
+ fprintf(log_file, " file:line:error style messages enabled.");
+ }
+}
+
+void print_version_banner(void)
+{
+ fprintf(term_out, "%s", luatex_banner);
+}
+
+/*tex
+
+The procedure |print_esc| prints a string that is preceded by the user's escape
+character (which is usually a backslash).
+
+*/
+
+void print_esc(str_number s)
+{
+ /*tex Set variable |c| to the current escape character: */
+ int c = escape_char_par;
+ if (c >= 0 && c < 0x110000)
+ print(c);
+ print(s);
+}
+
+/*tex
+
+This prints escape character, then |s|.
+
+*/
+
+void tprint_esc(const char *s)
+{
+ /*tex Set variable |c| to the current escape character: */
+ int c = escape_char_par;
+ if (c >= 0 && c < 0x110000)
+ print(c);
+ tprint(s);
+}
+
+/*tex
+
+An array of digits in the range |0..15| is printed by |print_the_digs|.
+
+*/
+
+void print_the_digs(eight_bits k)
+{
+ /*tex prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
+ while (k-- > 0) {
+ if (dig[k] < 10)
+ print_char('0' + dig[k]);
+ else
+ print_char('A' - 10 + dig[k]);
+ }
+}
+
+/*tex
+
+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 |mod| or |div| to negative
+arguments, since such operations are not implemented consistently by all PASCAL
+compilers.
+
+*/
+
+void print_int(longinteger n)
+{
+ /*tex index to current digit; we assume that $|n|<10^{23}$ */
+ int k = 0;
+ /*tex used to negate |n| in possibly dangerous cases */
+ longinteger m;
+ if (n < 0) {
+ print_char('-');
+ if (n > -100000000) {
+ n = -n;
+ } else {
+ m = -1 - n;
+ n = m / 10;
+ m = (m % 10) + 1;
+ k = 1;
+ if (m < 10)
+ dig[0] = (int) m;
+ else {
+ dig[0] = 0;
+ incr(n);
+ }
+ }
+ }
+ do {
+ dig[k] = (int) (n % 10);
+ n = n / 10;
+ incr(k);
+ } while (n != 0);
+ print_the_digs((eight_bits) k);
+}
+
+/*tex
+
+Here is a trivial procedure to print two digits; it is usually called with a
+parameter in the range |0<=n<=99|.
+
+*/
+
+void print_two(int n)
+{
+ n = abs(n) % 100;
+ print_char('0' + (n / 10));
+ print_char('0' + (n % 10));
+}
+
+/*tex
+
+Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
+
+*/
+
+void print_qhex(int n)
+{
+ /*tex index to current digit; we assume that $0\L n<16^{22}$ */
+ int k = 0 ;
+ print_char('"');
+ do {
+ dig[k] = n % 16;
+ n = n / 16;
+ incr(k);
+ } while (n != 0);
+ print_the_digs((eight_bits) k);
+}
+
+/*tex
+
+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}.
+
+*/
+
+void print_roman_int(int n)
+{
+ char *j, *k;
+ int u, v;
+ char mystery[] = "m2d5c2l5x2v5i";
+ j = (char *) mystery;
+ v = 1000;
+ while (1) {
+ while (n >= v) {
+ print_char(*j);
+ n = n - v;
+ }
+ if (n <= 0) {
+ /*tex nonpositive input produces no output */
+ return;
+ }
+ k = j + 2;
+ u = v / (*(k - 1) - '0');
+ if (*(k - 1) == '2') {
+ k = k + 2;
+ u = u / (*(k - 1) - '0');
+ }
+ if (n + u >= v) {
+ print_char(*k);
+ n = n + u;
+ } else {
+ j = j + 2;
+ v = v / (*(j - 1) - '0');
+ }
+ }
+}
+
+/*tex
+
+The |print| subroutine will not print a string that is still being created. The
+following procedure will.
+
+*/
+
+void print_current_string(void)
+{
+ /*tex points to current character code */
+ unsigned j = 0;
+ while (j < cur_length)
+ print_char(cur_string[j++]);
+}
+
+/*tex
+
+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.
+
+*/
+
+void print_cs(int p)
+{
+ str_number t = cs_text(p);
+ if (p < hash_base) {
+ /* nullcs */
+ if (p == null_cs) {
+ tprint_esc("csname");
+ tprint_esc("endcsname");
+ print_char(' ');
+ } else {
+ tprint_esc("IMPOSSIBLE.");
+ }
+ } else if ((p >= undefined_control_sequence) &&
+ ((p <= eqtb_size) || p > eqtb_size + hash_extra)) {
+ tprint_esc("IMPOSSIBLE.");
+ } else if (t >= str_ptr) {
+ tprint_esc("NONEXISTENT.");
+ } else {
+ if (is_active_cs(t)) {
+ print(active_cs_value(t));
+ } else {
+ print_esc(t);
+ if (single_letter(t)) {
+ if (get_cat_code(cat_code_table_par, pool_to_unichar(str_string(t))) == letter_cmd)
+ print_char(' ');
+ } else {
+ print_char(' ');
+ }
+ }
+ }
+}
+
+/*tex
+
+Here is a similar procedure; it avoids the error checks, and it never prints a
+space after the control sequence.
+
+*/
+void sprint_cs(pointer p)
+{
+ str_number t;
+ if (p == null_cs) {
+ tprint_esc("csname");
+ tprint_esc("endcsname");
+ } else {
+ t = cs_text(p);
+ if (is_active_cs(t))
+ print(active_cs_value(t));
+ else
+ print_esc(t);
+ }
+}
+
+void sprint_cs_name(pointer p)
+{
+ str_number t;
+ if (p != null_cs) {
+ t = cs_text(p);
+ if (is_active_cs(t))
+ print(active_cs_value(t));
+ else
+ print(t);
+ }
+}
+
+/*tex
+
+This procedure is never called when |interaction<scroll_mode|.
+
+*/
+
+void prompt_input(const char *s)
+{
+ wake_up_terminal();
+ tprint(s);
+ term_input();
+}
+
+/*tex
+
+Then there is a subroutine that prints glue stretch and shrink, possibly followed
+by the name of finite units:
+
+*/
+
+void print_glue(scaled d, int order, const char *s)
+{
+ print_scaled(d);
+ if ((order < normal) || (order > filll)) {
+ tprint("foul");
+ } else if (order > normal) {
+ tprint("fi");
+ while (order > sfi) {
+ print_char('l');
+ decr(order);
+ }
+ } else if (s != NULL) {
+ tprint(s);
+ }
+}
+
+/*tex
+
+The next subroutine prints a whole glue specification
+
+*/
+
+void print_spec(int p, const char *s)
+{
+ if (p < 0) {
+ print_char('*');
+ } else {
+ print_scaled(width(p));
+ if (s != NULL)
+ tprint(s);
+ if (stretch(p) != 0) {
+ tprint(" plus ");
+ print_glue(stretch(p), stretch_order(p), s);
+ }
+ if (shrink(p) != 0) {
+ tprint(" minus ");
+ print_glue(shrink(p), shrink_order(p), s);
+ }
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+/*tex An internal font number: */
+
+int font_in_short_display;
+
+/*tex
+
+Boxes, rules, inserts, whatsits, marks, and things in general that are sort of
+``complicated'' are indicated only by printing `\.{[]}'.
+
+We print a bit more than original \TEX. A value of 0 or 1 or any large value will
+behave the same as before. The reason for this extension is that a |name| not
+always makes sense.
+
+\starttyping
+0 \foo xyz
+1 \foo (bar)
+2 <bar> xyz
+3 <bar @ ..> xyz
+4 <id>
+5 <id: bar>
+6 <id: bar @ ..> xyz
+\stoptyping
+
+*/
+
+void print_font_identifier(internal_font_number f)
+{
+ str_number fonttext;
+ fonttext = font_id_text(f);
+ if (tracing_fonts_par >= 2 && tracing_fonts_par <= 6) {
+ /*tex < > is less likely to clash with text parenthesis */
+ tprint("<");
+ if (tracing_fonts_par >= 2 && tracing_fonts_par <= 3) {
+ print_font_name(f);
+ if (tracing_fonts_par >= 3 || font_size(f) != font_dsize(f)) {
+ tprint(" @ ");
+ print_scaled(font_size(f));
+ tprint("pt");
+ }
+ } else if (tracing_fonts_par >= 4 && tracing_fonts_par <= 6) {
+ print_int(f);
+ if (tracing_fonts_par >= 5) {
+ tprint(": ");
+ print_font_name(f);
+ if (tracing_fonts_par >= 6 || font_size(f) != font_dsize(f)) {
+ tprint(" @ ");
+ print_scaled(font_size(f));
+ tprint("pt");
+ }
+ }
+ }
+ print_char('>');
+ } else {
+ /*tex old method, inherited from pdftex */
+ if (fonttext > 0) {
+ print_esc(fonttext);
+ } else {
+ tprint_esc("FONT");
+ print_int(f);
+ }
+ if (tracing_fonts_par > 0) {
+ tprint(" (");
+ print_font_name(f);
+ if (font_size(f) != font_dsize(f)) {
+ tprint("@");
+ print_scaled(font_size(f));
+ tprint("pt");
+ }
+ print_char(')');
+ }
+ }
+}
+
+/*tex
+ We could do this much nicer but then we need to also adapt short_display a
+ bit and we have to be as compatible as possible in the log for some macro
+ packages.
+
+ The callback is also responsible for either or not reporting the character
+ number itself.
+*/
+
+void print_character_info(halfword p)
+{
+ int callback_id = callback_defined(glyph_info_callback);
+ if (callback_id) {
+ char* str = NULL;
+ run_callback(callback_id, "N->R", p, &str);
+ if (str == NULL) {
+ print_qhex(character(p));
+ } else {
+ tprint(str);
+ free(str);
+ }
+ } else {
+ print(character(p));
+ }
+}
+
+/*tex
+ This prints highlights of list |p|.
+*/
+
+void short_display(int p)
+{
+ while (p != null) {
+ if (is_char_node(p)) {
+ if (lig_ptr(p) != null) {
+ short_display(lig_ptr(p));
+ } else {
+ if (font(p) != font_in_short_display) {
+ if (!is_valid_font(font(p))) {
+ print_char('*');
+ } else {
+ print_font_identifier(font(p));
+ }
+ print_char(' ');
+ font_in_short_display = font(p);
+ }
+ print_character_info(p);
+ }
+ } else {
+ /*tex Print a short indication of the contents of node |p| */
+ print_short_node_contents(p);
+ }
+ p = vlink(p);
+ }
+}
+
+/*tex
+
+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.
+
+*/
+
+/*tex
+
+This prints |char_node| data.
+
+*/
+
+void print_font_and_char(int p)
+{
+ if (!is_valid_font(font(p)))
+ print_char('*');
+ else
+ print_font_identifier(font(p));
+ print_char(' ');
+ print_character_info(p);
+}
+
+/*tex
+
+This prints token list data in braces
+
+*/
+
+void print_mark(int p)
+{
+ print_char('{');
+ if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end))
+ tprint_esc("CLOBBERED.");
+ else
+ show_token_list(token_link(p), null, max_print_line - 10);
+ print_char('}');
+}
+
+/*tex
+
+This prints dimensions of a rule node.
+
+*/
+
+void print_rule_dimen(scaled d)
+{
+ if (is_running(d))
+ print_char('*');
+ else
+ print_scaled(d);
+}
+
+/*tex
+
+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.
+
+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.
+
+*/
+
+/*tex The maximum nesting depth in box displays: */
+
+int depth_threshold;
+
+/*tex The maximum number of items shown at the same list level: */
+
+int breadth_max;
+
+/*tex
+
+The recursive machinery is started by calling |show_box|. Assign the values
+|depth_threshold:=show_box_depth| and |breadth_max:=show_box_breadth|
+
+*/
+
+void show_box(halfword p)
+{
+ depth_threshold = show_box_depth_par;
+ breadth_max = show_box_breadth_par;
+ if (breadth_max <= 0)
+ breadth_max = 5;
+ /*tex the show starts at |p| */
+ show_node_list(p);
+ print_ln();
+}
+
+/*tex
+
+Helper for debugging purposes. It prints highlights of list |p|
+
+*/
+
+void short_display_n(int p, int m)
+{
+ int i = 0;
+ font_in_short_display = null_font;
+ if (p == null)
+ return;
+ while (p != null) {
+ if (is_char_node(p)) {
+ if (p <= max_halfword) {
+ if (font(p) != font_in_short_display) {
+ if (!is_valid_font(font(p)))
+ print_char('*');
+ else
+ print_font_identifier(font(p));
+ print_char(' ');
+ font_in_short_display = font(p);
+ }
+ print_character_info(p);
+ }
+ } else {
+ if ( (type(p) == glue_node) ||
+ (type(p) == disc_node) ||
+ (type(p) == penalty_node) ||
+ ((type(p) == kern_node) && (subtype(p) == explicit_kern ||
+ subtype(p) == italic_kern ))) {
+ incr(i);
+ }
+ if (i >= m)
+ return;
+ if (type(p) == disc_node) {
+ print_char('|');
+ short_display(vlink(pre_break(p)));
+ print_char('|');
+ short_display(vlink(post_break(p)));
+ print_char('|');
+ } else {
+ /*tex Print a short indication of the contents of node |p| */
+ print_short_node_contents(p);
+ }
+ }
+ p = vlink(p);
+ if (p == null)
+ return;
+ }
+ update_terminal();
+}
+
+/*tex
+
+When debugging a macro package, it can be useful to see the exact control
+sequence names in the format file. For example, if ten new csnames appear, it's
+nice to know what they are, to help pinpoint where they came from. (This isn't a
+truly ``basic'' printing procedure, but that's a convenient module in which to
+put it.)
+
+*/
+
+void print_csnames(int hstart, int hfinish)
+{
+ int h;
+ unsigned char *c, *l;
+ fprintf(stderr, "fmtdebug:csnames from %d to %d:", (int) hstart, (int) hfinish);
+ for (h = hstart; h <= hfinish; h++) {
+ if (cs_text(h) > 0) {
+ /*tex We have anything at this position. */
+ c = str_string(cs_text(h));
+ l = c + str_length(cs_text(h));
+ while (c < l) {
+ /*tex Print the characters. */
+ fputc(*c++, stderr);
+ }
+ fprintf(stderr, "|");
+ }
+ }
+}
+
+/*tex
+
+A helper for printing file:line:error style messages. Look for a filename in
+|full_source_filename_stack|, and if we fail to find one fall back on the
+non-file:line:error style.
+
+*/
+
+void print_file_line(void)
+{
+ int level = in_open;
+ while ((level > 0) && (full_source_filename_stack[level] == 0))
+ decr(level);
+ if (level == 0) {
+ tprint_nl("! ");
+ } else {
+ tprint_nl("");
+ tprint(full_source_filename_stack[level]);
+ print_char(':');
+ if (level == in_open)
+ print_int(line);
+ else
+ print_int(line_stack[level + 1]);
+ tprint(": ");
+ }
+}
+
+/*tex
+
+\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:
+
+*/
+
+void begin_diagnostic(void)
+{
+ global_old_setting = selector;
+ if ((tracing_online_par <= 0) && (selector == term_and_log)) {
+ decr(selector);
+ if (history == spotless)
+ history = warning_issued;
+ }
+}
+
+/*tex
+
+Restore proper conditions after tracing.
+
+*/
+
+void end_diagnostic(boolean blank_line)
+{
+ tprint_nl("");
+ if (blank_line)
+ print_ln();
+ selector = global_old_setting;
+}
+
+/*
+ Indentation (based on a suggestion by PO on tex-implementors but adapted to
+ out situation and with built-in limits and so.
+*/
+
+void print_input_level(void)
+{
+ int callback_id = callback_defined(input_level_string_callback);
+ if (callback_id>0) {
+ char *s = NULL;
+ if (run_callback(callback_id, "d->S", input_ptr, &s)) {
+ if (s && strlen(s) > 0) {
+ tprint_nl(s);
+ free(s);
+ } else {
+ print_ln();
+ }
+ } else {
+ print_ln();
+ }
+ } else {
+ int m = level_max;
+ if (m) {
+ int l = input_ptr;
+ int c = level_chr > 0 ? level_chr : '.';
+ if (l > m) {
+ tprint_nl("[");
+ print_int((l/m)*m);
+ print(']');
+ l = l % m;
+ } else {
+ print_ln();
+ }
+ while (l > 0) {
+ print(c);
+ l--;
+ }
+ } else {
+ print_ln();
+ }
+ }
+}
+
+/*tex
+
+Of course we had better declare another global variable, if the previous routines
+are going to work.
+
+*/
+
+int global_old_setting;
diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.h b/Build/source/texk/web2c/luatexdir/tex/printing.h
new file mode 100644
index 00000000000..0d87f3d4ae7
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/printing.h
@@ -0,0 +1,133 @@
+/* printing.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef PRINTING_H
+# define PRINTING_H
+
+#define last_file_selector 127 /* was 15 */
+
+/*
+ Nicer will be to start these with 0 and then use an offset for the write
+ registers internally.
+*/
+
+typedef enum {
+ no_print = last_file_selector + 1, /* 16 */ /* |selector| setting that makes data disappear */
+ term_only = last_file_selector + 2, /* 17 */ /* printing is destined for the terminal only */
+ log_only = last_file_selector + 3, /* 18 */ /* printing is destined for the transcript file only */
+ term_and_log = last_file_selector + 4, /* 19 */ /* normal |selector| setting */
+ pseudo = last_file_selector + 5, /* 20 */ /* special |selector| setting for |show_context| */
+ new_string = last_file_selector + 6, /* 21 */ /* printing is deflected to the string pool */
+} selector_settings;
+
+extern int escape_controls;
+extern int new_string_line;
+
+# define ssup_error_line 255
+# define max_selector new_string /* highest selector setting */
+
+extern alpha_file log_file;
+extern int selector;
+extern int dig[23];
+extern int tally;
+extern int term_offset;
+extern int file_offset;
+extern packed_ASCII_code trick_buf[(ssup_error_line + 1)];
+extern int trick_count;
+extern int first_count;
+extern boolean inhibit_par_tokens;
+
+/*
+ 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@>
+*/
+
+# define wterm_cr() fprintf(term_out,"\n")
+# define wlog_cr() fprintf(log_file,"\n")
+
+extern void print_ln(void);
+extern void print_char(int s);
+extern void print(int s);
+extern void lprint (lstring *ss);
+extern void print_nl(str_number s);
+extern void print_nlp(void);
+extern void print_banner(const char *);
+extern void log_banner(const char *);
+extern void print_version_banner(void);
+extern void print_esc(str_number s);
+extern void print_the_digs(eight_bits k);
+extern void print_int(longinteger n);
+extern void print_two(int n);
+extern void print_qhex(int n);
+extern void print_roman_int(int n);
+extern void print_current_string(void);
+
+# define print_font_name(A) tprint(font_name(A))
+
+extern void print_cs(int p);
+extern void sprint_cs(pointer p);
+extern void sprint_cs_name(pointer p);
+extern void tprint(const char *s);
+extern void tprint_nl(const char *s);
+extern void tprint_esc(const char *s);
+
+extern void prompt_input(const char *s);
+
+# define single_letter(A) \
+ ((str_length(A)==1)|| \
+ ((str_length(A)==4)&&*(str_string((A)))>=0xF0)|| \
+ ((str_length(A)==3)&&*(str_string((A)))>=0xE0)|| \
+ ((str_length(A)==2)&&*(str_string((A)))>=0xC0))
+
+# define is_active_cs(a) \
+ (a && str_length(a)>3 && \
+ ( *str_string(a) == 0xEF) && \
+ (*(str_string(a)+1) == 0xBF) && \
+ (*(str_string(a)+2) == 0xBF))
+
+# define active_cs_value(A) pool_to_unichar((str_string((A))+3))
+
+extern void print_glue(scaled d, int order, const char *s); /* prints a glue component */
+extern void print_spec(int p, const char *s); /* prints a glue specification */
+
+extern int font_in_short_display; /* an internal font number */
+extern void print_font_identifier(internal_font_number f);
+extern void short_display(int p); /* prints highlights of list |p| */
+extern void print_font_and_char(int p); /* prints |char_node| data */
+extern void print_mark(int p); /* prints token list data in braces */
+extern void print_rule_dimen(scaled d); /* prints dimension in rule node */
+extern int depth_threshold; /* maximum nesting depth in box displays */
+extern int breadth_max; /* maximum number of items shown at the same list level */
+extern void show_box(halfword p);
+extern void short_display_n(int p, int m); /* prints highlights of list |p| */
+
+extern void print_csnames(int hstart, int hfinish);
+extern void print_file_line(void);
+
+extern void begin_diagnostic(void);
+extern void print_input_level(void);
+extern void end_diagnostic(boolean blank_line);
+extern int global_old_setting;
+
+extern void print_character_info(halfword p);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/scanning.c b/Build/source/texk/web2c/luatexdir/tex/scanning.c
new file mode 100644
index 00000000000..deddbf5649b
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/scanning.c
@@ -0,0 +1,2751 @@
+/*
+
+Copyright 2009-2012 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation;
+either version 2 of the License, or (at your option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+static void scan_expr(void);
+
+/*tex
+
+ 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.
+
+ 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|.
+
+*/
+
+/* This reads a mandatory |left_brace|: */
+
+void scan_left_brace(void)
+{
+ /*tex Get the next non-blank non-relax non-call token */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (cur_cmd != left_brace_cmd) {
+ print_err("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_cmd;
+ cur_chr = '{';
+ incr(align_state);
+ }
+}
+
+/*tex
+
+ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded
+ by optional spaces; `\.{\\relax}' is not ignored here.
+
+*/
+
+void scan_optional_equals(void)
+{
+ /*tex Get the next non-blank non-call token */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_tok != other_token + '=')
+ back_input();
+}
+
+/*tex
+
+ Here is a procedure that sounds an alarm when mu and non-mu units are being
+ switched.
+
+*/
+
+static void mu_error(void)
+{
+ print_err("Incompatible glue units");
+ help1("I'm going to assume that 1mu=1pt when they're mixed.");
+ error();
+}
+
+/*tex
+
+ 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.
+
+ \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. Eight levels are
+ distinguished, namely |int_val|, |attr_val|, |dimen_val|, |glue_val|,
+ |mu_val|, |dir_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.
+
+*/
+
+/*tex The value returned by numeric scanners: */
+
+int cur_val;
+
+/*tex Delcodes are sometimes 51 digits: */
+
+int cur_val1;
+
+/*tex The level of this value: */
+
+int cur_val_level;
+
+#define scanned_result(A,B) do { \
+ cur_val=A; \
+ cur_val_level=B; \
+} while (0)
+
+/*tex
+
+ 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.
+
+*/
+
+static void downgrade_cur_val(boolean delete_glue)
+{
+ if (cur_val_level == glue_val_level) {
+ halfword m = cur_val;
+ cur_val = width(m);
+ if (delete_glue)
+ flush_node(m);
+ } else if (cur_val_level == mu_val_level) {
+ mu_error();
+ }
+ decr(cur_val_level);
+}
+
+void negate_cur_val(boolean modify_glue)
+{
+ if (cur_val_level >= glue_val_level) {
+ if (modify_glue) {
+ /*tex We modify in-place. */
+ } else {
+ cur_val = new_spec(cur_val);
+ }
+ negate(width(cur_val));
+ negate(stretch(cur_val));
+ negate(shrink(cur_val));
+ } else {
+ negate(cur_val);
+ }
+}
+
+/*tex
+
+ Some of the internal items can be fetched both routines, and these have been
+ split off into the next routine, that returns true if the command code was
+ understood.
+
+*/
+
+static boolean short_scan_something_internal(int cmd, int chr, int level, boolean negative)
+{
+ /*tex |chr_code| part of the operand token */
+ halfword m;
+ /*tex general purpose index */
+ halfword q;
+ /*tex index into |nest| */
+ int p;
+ int save_cur_chr;
+ boolean succeeded = true;
+ m = chr;
+ switch (cmd) {
+ case assign_toks_cmd:
+ scanned_result(equiv(m), tok_val_level);
+ break;
+ case assign_int_cmd:
+ scanned_result(eqtb[m].cint, int_val_level);
+ break;
+ case assign_attr_cmd:
+ scanned_result(eqtb[m].cint, int_val_level);
+ break;
+ case assign_dimen_cmd:
+ scanned_result(eqtb[m].cint, dimen_val_level);
+ break;
+ case assign_glue_cmd:
+ scanned_result(equiv(m), glue_val_level);
+ break;
+ case assign_mu_glue_cmd:
+ scanned_result(equiv(m), mu_val_level);
+ break;
+ case assign_direction_cmd:
+ if (m == (int_base + line_direction_code)) {
+ m = int_base + text_direction_code;
+ }
+ scanned_result(eqtb[m].cint, int_val_level);
+ break;
+ case assign_dir_cmd:
+ if (m == (int_base + line_direction_code)) {
+ m = int_base + text_direction_code;
+ }
+ scanned_result(eqtb[m].cint, dir_val_level);
+ break;
+ case math_style_cmd:
+ scanned_result(m, int_val_level);
+ break;
+ case set_aux_cmd:
+ /*tex Fetch the |space_factor| or the |prev_depth|. */
+ if (abs(cur_list.mode_field) != m) {
+ print_err("Improper ");
+ print_cmd_chr(set_aux_cmd, m);
+ 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_level)
+ scanned_result(0, dimen_val_level);
+ else
+ scanned_result(0, int_val_level);
+ } else if (m == vmode) {
+ scanned_result(prev_depth_par, dimen_val_level);
+ } else {
+ scanned_result(space_factor_par, int_val_level);
+ }
+ break;
+ case set_prev_graf_cmd:
+ /*tex Fetch the |prev_graf| */
+ if (cur_list.mode_field == 0) {
+ /*tex |prev_graf=0| within \.{\\write} */
+ scanned_result(0, int_val_level);
+ } else {
+ p = nest_ptr;
+ while (abs(nest[p].mode_field) != vmode)
+ decr(p);
+ scanned_result(nest[p].pg_field, int_val_level);
+ }
+ break;
+ case set_page_int_cmd:
+ /*tex Fetch the |dead_cycles| or the |insert_penalties| */
+ if (m == 0)
+ cur_val = dead_cycles;
+ else if (m == 2)
+ cur_val = interaction;
+ else
+ cur_val = insert_penalties;
+ cur_val_level = int_val_level;
+ break;
+ case set_page_dimen_cmd:
+ /*tex 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_level;
+ break;
+ case set_tex_shape_cmd:
+ /*tex Fetch the |par_shape| size. */
+ if (par_shape_par_ptr == null)
+ cur_val = 0;
+ else
+ cur_val = vinfo(par_shape_par_ptr + 1);
+ cur_val_level = int_val_level;
+ break;
+ case set_etex_shape_cmd:
+ /*tex 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);
+ }
+ cur_val_level = int_val_level;
+ break;
+ case char_given_cmd:
+ case math_given_cmd:
+ case xmath_given_cmd:
+ scanned_result(cur_chr, int_val_level);
+ break;
+ case last_item_cmd:
+ /*tex
+
+ Because the items in this case directly refer to |cur_chr|, it
+ needs to be saved and restored.
+
+ */
+ save_cur_chr = cur_chr;
+ cur_chr = chr;
+ /*tex
+
+ Fetch an item in the current node, if appropriate. Here is where
+ \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\ } 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.
+
+ */
+ if (m >= input_line_no_code) {
+ if (m >= eTeX_glue) {
+ /*tex Process an expression and |return|. */
+ if (m < eTeX_mu) {
+ if (m == mu_to_glue_code) {
+ scan_mu_glue();
+ };
+ cur_val_level = glue_val_level;
+ } else if (m < eTeX_expr) {
+ if (m == glue_to_mu_code) {
+ scan_normal_glue();
+ }
+ cur_val_level = mu_val_level;
+ } else {
+ cur_val_level = m - eTeX_expr + int_val_level;
+ scan_expr();
+ }
+ /*tex
+
+ 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|; we free a
+ glue_spec when needed.
+
+ */
+ while (cur_val_level > level) {
+ downgrade_cur_val(true);
+ }
+ if (negative) {
+ /*tex
+
+ We get a new glue spec node with negated values and
+ the old intermediate is deleted.
+
+ */
+ negate_cur_val(true);
+ }
+ return succeeded;
+ } else if (m >= eTeX_dim) {
+ switch (m) {
+ 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 (char_exists(q, cur_val)) {
+ switch (m) {
+ case font_char_wd_code:
+ cur_val = char_width(q, cur_val);
+ break;
+ case font_char_ht_code:
+ cur_val = char_height(q, cur_val);
+ break;
+ case font_char_dp_code:
+ cur_val = char_depth(q, cur_val);
+ break;
+ case font_char_ic_code:
+ cur_val = char_italic(q, cur_val);
+ break;
+ }
+ } else {
+ cur_val = 0;
+ }
+ break;
+ 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_par_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 > vinfo(par_shape_par_ptr + 1))
+ cur_val = vinfo(par_shape_par_ptr + 1);
+ cur_val =
+ varmem[par_shape_par_ptr + 2 * cur_val - q + 1].cint;
+ }
+ cur_val_level = dimen_val_level;
+ break;
+ 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);
+ flush_node(q);
+ break;
+ }
+ cur_val_level = dimen_val_level;
+ } else {
+ switch (m) {
+ case input_line_no_code:
+ cur_val = line;
+ break;
+ case badness_code:
+ cur_val = last_badness;
+ break;
+ case luatex_version_code:
+ cur_val = get_luatexversion();
+ break;
+ case last_saved_box_resource_index_code:
+ cur_val = last_saved_box_index;
+ break;
+ case last_saved_image_resource_index_code:
+ cur_val = last_saved_image_index;
+ break;
+ case last_saved_image_resource_pages_code:
+ cur_val = last_saved_image_pages;
+ break;
+ case last_x_pos_code:
+ cur_val = last_position.h;
+ break;
+ case last_y_pos_code:
+ cur_val = last_position.v;
+ break;
+ case random_seed_code:
+ cur_val = random_seed;
+ break;
+ case eTeX_version_code:
+ cur_val = eTeX_version;
+ break;
+ case eTeX_minor_version_code:
+ cur_val = eTeX_minor_version;
+ break;
+ case current_group_level_code:
+ cur_val = cur_level - level_one;
+ break;
+ case current_group_type_code:
+ cur_val = cur_group;
+ break;
+ case current_if_level_code:
+ q = cond_ptr;
+ cur_val = 0;
+ while (q != null) {
+ incr(cur_val);
+ q = vlink(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;
+ case glue_stretch_order_code:
+ case glue_shrink_order_code:
+ case eTeX_glue_stretch_order_code:
+ case eTeX_glue_shrink_order_code:
+ scan_normal_glue();
+ q = cur_val;
+ if (m == glue_stretch_order_code || m == eTeX_glue_stretch_order_code) {
+ cur_val = stretch_order(q);
+ } else {
+ cur_val = shrink_order(q);
+ }
+ if (cur_val && (m == eTeX_glue_stretch_order_code || m == eTeX_glue_shrink_order_code)) {
+ cur_val = (cur_val == 1) ? - 1 : cur_val - 1;
+ }
+ flush_node(q);
+ break;
+ }
+ cur_val_level = int_val_level;
+ }
+ } else {
+ if (cur_chr == glue_val_level)
+ cur_val = zero_glue;
+ else
+ cur_val = 0;
+ if (cur_chr == last_node_type_code) {
+ cur_val_level = int_val_level;
+ if ((cur_list.tail_field == cur_list.head_field)
+ || (cur_list.mode_field == 0))
+ cur_val = -1;
+ } else {
+ /*tex assumes identical values */
+ cur_val_level = cur_chr;
+ }
+ if ((cur_list.tail_field != contrib_head) && (cur_list.mode_field != 0)) {
+ switch (cur_chr) {
+ case lastpenalty_code:
+ if (type(cur_list.tail_field) == penalty_node)
+ cur_val = penalty(cur_list.tail_field);
+ break;
+ case lastkern_code:
+ if (type(cur_list.tail_field) == kern_node)
+ cur_val = width(cur_list.tail_field);
+ break;
+ case lastskip_code:
+ if (type(cur_list.tail_field) == glue_node)
+ cur_val = cur_list.tail_field;
+ if (subtype(cur_list.tail_field) == mu_glue)
+ cur_val_level = mu_val_level;
+ break;
+ case last_node_type_code:
+ cur_val = visible_last_node_type(cur_list.tail_field);
+ break;
+ }
+ } else if ((cur_list.mode_field == vmode) && (cur_list.tail_field == cur_list.head_field)) {
+ switch (cur_chr) {
+ case lastpenalty_code:
+ cur_val = last_penalty;
+ break;
+ case lastkern_code:
+ cur_val = last_kern;
+ break;
+ case lastskip_code:
+ if (last_glue != max_halfword)
+ cur_val = last_glue;
+ break;
+ case last_node_type_code:
+ cur_val = last_node_type;
+ break;
+ }
+ }
+ }
+ cur_chr = save_cur_chr;
+ break;
+ default:
+ succeeded = false;
+ }
+ if (succeeded) {
+ while (cur_val_level > level) {
+ /*tex Convert |cur_val| to a lower level. */
+ downgrade_cur_val(false);
+ }
+ /*tex
+
+ Fix the reference count, if any, and negate |cur_val| if |negative|.
+ 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|.
+
+ */
+ if (negative) {
+ /*tex We create a new (negated) glue spec and keep the old one. */
+ negate_cur_val(false);
+ } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) {
+ cur_val = new_spec(cur_val);
+ }
+ }
+ return succeeded;
+}
+
+/*tex
+
+ First, here is a short routine that is called from lua code. All the real
+ work is delegated to |short_scan_something_internal| that is shared between
+ this routine and |scan_something_internal|.
+
+*/
+
+void scan_something_simple(halfword cmd, halfword subitem)
+{
+ /*tex Negative is never true. */
+ if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) {
+ /*tex Complain that |texlib| can not do this; give zero result. */
+ print_err("You can't use `");
+ print_cmd_chr((quarterword) cmd, subitem);
+ tprint("' as tex library index");
+ help1("I'm forgetting what you said and using zero instead.");
+ error();
+ scanned_result(0, int_val_level);
+ }
+}
+
+/*tex
+
+ 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|.
+
+*/
+
+/*tex Fetch an internal parameter: */
+
+void scan_something_internal(int level, boolean negative)
+{
+ /*tex |chr_code| part of the operand token */
+ halfword m;
+ /*tex accumulators */
+ int n, k;
+ RESTART:
+ m = cur_chr;
+ if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) {
+ switch (cur_cmd) {
+ case def_char_code_cmd:
+ /*tex Fetch a character code from some table */
+ scan_char_num();
+ if (m == math_code_base) {
+ cur_val1 = get_math_code_num(cur_val);
+ scanned_result(cur_val1, int_val_level);
+ } else if (m == lc_code_base) {
+ cur_val1 = get_lc_code(cur_val);
+ scanned_result(cur_val1, int_val_level);
+ } else if (m == uc_code_base) {
+ cur_val1 = get_uc_code(cur_val);
+ scanned_result(cur_val1, int_val_level);
+ } else if (m == sf_code_base) {
+ cur_val1 = get_sf_code(cur_val);
+ scanned_result(cur_val1, int_val_level);
+ } else if (m == cat_code_base) {
+ cur_val1 = get_cat_code(cat_code_table_par, cur_val);
+ scanned_result(cur_val1, int_val_level);
+ } else {
+ confusion("def_char");
+ }
+ break;
+ case def_del_code_cmd:
+ case extdef_del_code_cmd:
+ /*tex Fetch a character code from some table. */
+ scan_char_num();
+ cur_val1 = get_del_code_num(cur_val);
+ scanned_result(cur_val1, int_val_level);
+ break;
+ case extdef_math_code_cmd:
+ /*tex Fetch an extended math code table value. */
+ scan_char_num();
+ cur_val1 = get_math_code_num(cur_val);
+ scanned_result(cur_val1, int_val_level);
+ break;
+ case toks_register_cmd:
+ case set_font_cmd:
+ case def_font_cmd:
+ case letterspace_font_cmd:
+ case copy_font_cmd:
+ /*tex Fetch a token list or font identifier, provided that |level=tok_val|. */
+ if (level != tok_val_level) {
+ print_err("Missing number, treated as zero");
+ 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.)"
+ );
+ back_error();
+ scanned_result(0, dimen_val_level);
+ } else if (cur_cmd == toks_register_cmd) {
+ scan_register_num();
+ m = toks_base + cur_val;
+ scanned_result(equiv(m), tok_val_level);
+ } else {
+ back_input();
+ scan_font_ident();
+ scanned_result(font_id_base + cur_val, ident_val_level);
+ }
+ break;
+ case set_font_id_cmd:
+ scan_int();
+ scanned_result(font_id_base + cur_val, ident_val_level);
+ break;
+ case def_family_cmd:
+ /*tex Fetch a math font identifier. */
+ scan_char_num();
+ cur_val1 = fam_fnt(cur_val, m);
+ scanned_result(font_id_base + cur_val1, ident_val_level);
+ break;
+ case set_math_param_cmd:
+ /*tex Fetch a math parameter. */
+ cur_val1 = cur_chr;
+ get_token();
+ if (cur_cmd != math_style_cmd) {
+ print_err("Missing math style, treated as \\displaystyle");
+ help1("A style should have been here; I inserted `\\displaystyle'.");
+ cur_val = display_style;
+ back_error();
+ } else {
+ cur_val = cur_chr;
+ }
+ if (cur_val1 < math_param_first_mu_glue) {
+ if (cur_val1 == math_param_radical_degree_raise) {
+ cur_val1 = get_math_param(cur_val1, cur_chr);
+ scanned_result(cur_val1, int_val_level);
+ } else {
+ cur_val1 = get_math_param(cur_val1, cur_chr);
+ scanned_result(cur_val1, dimen_val_level);
+ }
+ } else {
+ cur_val1 = get_math_param(cur_val1, cur_chr);
+ if (cur_val1 == thin_mu_skip_code)
+ cur_val1 = thin_mu_skip_par;
+ else if (cur_val1 == med_mu_skip_code)
+ cur_val1 = med_mu_skip_par;
+ else if (cur_val1 == thick_mu_skip_code)
+ cur_val1 = thick_mu_skip_par;
+ scanned_result(cur_val1, mu_val_level);
+ }
+ break;
+ case assign_box_dir_cmd:
+ scan_register_num();
+ m = cur_val;
+ if (box(m) != null)
+ cur_val = box_dir(box(m));
+ else
+ cur_val = 0;
+ cur_val_level = dir_val_level;
+ break;
+ case set_box_dimen_cmd:
+ /*tex Fetch a box dimension. */
+ scan_register_num();
+ if (box(cur_val) == null)
+ cur_val = 0;
+ else
+ cur_val = varmem[box(cur_val) + m].cint;
+ cur_val_level = dimen_val_level;
+ break;
+ case assign_font_dimen_cmd:
+ /*tex Fetch a font dimension. */
+ get_font_dimen();
+ break;
+ case assign_font_int_cmd:
+ /*tex Fetch a font integer. */
+ scan_font_ident();
+ if (m == 0) {
+ scanned_result(hyphen_char(cur_val), int_val_level);
+ } else if (m == 1) {
+ scanned_result(skew_char(cur_val), int_val_level);
+ } else if (m == no_lig_code) {
+ scanned_result(test_no_ligatures(cur_val), int_val_level);
+ } else {
+ n = cur_val;
+ scan_char_num();
+ k = cur_val;
+ switch (m) {
+ case lp_code_base:
+ scanned_result(get_lp_code(n, k), int_val_level);
+ break;
+ case rp_code_base:
+ scanned_result(get_rp_code(n, k), int_val_level);
+ break;
+ case ef_code_base:
+ scanned_result(get_ef_code(n, k), int_val_level);
+ break;
+ case tag_code:
+ scanned_result(get_tag_code(n, k), int_val_level);
+ break;
+ }
+ }
+ break;
+ case register_cmd:
+ /*tex Fetch a register */
+ scan_register_num();
+ switch (m) {
+ case int_val_level:
+ cur_val = count(cur_val);
+ break;
+ case attr_val_level:
+ cur_val = attribute(cur_val);
+ break;
+ case dimen_val_level:
+ cur_val = dimen(cur_val);
+ break;
+ case glue_val_level:
+ cur_val = skip(cur_val);
+ break;
+ case mu_val_level:
+ cur_val = mu_skip(cur_val);
+ break;
+ }
+ cur_val_level = m;
+ break;
+ case ignore_spaces_cmd:
+ /*tex Trap unexpandable primitives. */
+ if (cur_chr == 1) {
+ /*tex
+
+ Reset |cur_tok| for unexpandable primitives, goto
+ restart. This block deals with unexpandable
+ \.{\\primitive} appearing at a spot where an integer or
+ an internal values should have been found. It fetches the
+ next token then resets |cur_cmd|, |cur_cs|, and
+ |cur_tok|, based on the primitive value of that token. No
+ expansion takes place, because the next token may be all
+ sorts of things. This could trigger further expansion
+ creating new errors.
+
+ */
+ get_token();
+ cur_cs = prim_lookup(cs_text(cur_cs));
+ if (cur_cs != undefined_primitive) {
+ cur_cmd = get_prim_eq_type(cur_cs);
+ cur_chr = get_prim_equiv(cur_cs);
+ cur_tok = token_val(cur_cmd, cur_chr);
+ } else {
+ cur_cmd = relax_cmd;
+ cur_chr = 0;
+ cur_tok = cs_token_flag + frozen_relax;
+ cur_cs = frozen_relax;
+ }
+ goto RESTART;
+ }
+ break;
+ case hyph_data_cmd:
+ switch (cur_chr) {
+ case 0:
+ case 1:
+ goto DEFAULT;
+ break;
+ case 2:
+ cur_val = get_pre_hyphen_char(language_par);
+ cur_val_level = int_val_level;
+ break;
+ case 3:
+ cur_val = get_post_hyphen_char(language_par);
+ cur_val_level = int_val_level;
+ break;
+ case 4:
+ cur_val = get_pre_exhyphen_char(language_par);
+ cur_val_level = int_val_level;
+ break;
+ case 5:
+ cur_val = get_post_exhyphen_char(language_par);
+ cur_val_level = int_val_level;
+ break;
+ case 6:
+ cur_val = get_hyphenation_min(language_par);
+ cur_val_level = int_val_level;
+ break;
+ case 7:
+ scan_int();
+ cur_val = get_hj_code(language_par,cur_val);
+ cur_val_level = int_val_level;
+ break;
+ }
+ break;
+ default:
+ DEFAULT:
+ /*tex Complain that \.{\\the} can not do this; give zero result. */
+ print_err("You can't use `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ tprint("' after \\the");
+ help1("I'm forgetting what you said and using zero instead.");
+ error();
+ if (level != tok_val_level)
+ scanned_result(0, dimen_val_level);
+ else
+ scanned_result(0, int_val_level);
+ break;
+ }
+ while (cur_val_level > level) {
+ /*tex Convert |cur_val| to a lower level. */
+ downgrade_cur_val(false);
+ }
+ /*tex
+
+ Fix the reference count, if any, and negate |cur_val| if |negative|.
+ 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|.
+
+ */
+ if (negative) {
+ /*tex We create a new (negated) glue spec and keep the old one. */
+ negate_cur_val(false);
+ } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) {
+ cur_val = new_spec(cur_val);
+ }
+ }
+}
+
+/*tex
+
+ It is nice to have routines that say what they do, so the original
+ |scan_eight_bit_int| is superceded by |scan_register_num| and
+ |scan_mark_num|. It may become split up even further in the future.
+
+ Many of the |restricted classes| routines are the essentially the same except
+ for the upper limit and the error message, so it makes sense to combine these
+ all into one function.
+
+*/
+
+void scan_limited_int(int max, const char *name)
+{
+ char hlp[80];
+ scan_int();
+ if ((cur_val < 0) || (cur_val > max)) {
+ if (name == NULL) {
+ snprintf(hlp, 80, "Since I expected to read a number between 0 and %d,", max);
+ print_err("Bad number");
+ } else {
+ char msg[80];
+ snprintf(hlp, 80, "A %s must be between 0 and %d.", name, max);
+ snprintf(msg, 80, "Bad %s", name);
+ print_err(msg);
+ }
+ help2(hlp, "I changed this one to zero.");
+ int_error(cur_val);
+ cur_val = 0;
+ }
+}
+
+void scan_fifteen_bit_int(void)
+{
+ scan_real_fifteen_bit_int();
+ cur_val = ((cur_val / 0x1000) * 0x1000000) + (((cur_val % 0x1000) / 0x100) * 0x10000) + (cur_val % 0x100);
+}
+
+void scan_fifty_one_bit_int(void)
+{
+ int iiii;
+ scan_int();
+ if ((cur_val < 0) || (cur_val > 0777777777)) {
+ print_err("Bad delimiter code");
+ help2(
+ "A numeric delimiter (first part) must be between 0 and 2^{27}-1.",
+ "I changed this one to zero."
+ );
+ int_error(cur_val);
+ cur_val = 0;
+ }
+ iiii = cur_val;
+ scan_int();
+ if ((cur_val < 0) || (cur_val > 0xFFFFFF)) {
+ print_err("Bad delimiter code");
+ help2(
+ "A numeric delimiter (second part) must be between 0 and 2^{24}-1.",
+ "I changed this one to zero."
+ );
+ int_error(cur_val);
+ cur_val = 0;
+ }
+ cur_val1 = cur_val;
+ cur_val = iiii;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex |scan_int| sets this to 8, 10, 16, or zero */
+
+int radix;
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex Sets |cur_val| to an integer: */
+
+void scan_int(void)
+{
+ /*tex should the answer be negated? */
+ boolean negative;
+ /*tex |$2^{31}$ / radix|, the threshold of danger */
+ int m;
+ /*tex the digit just scanned */
+ int d;
+ /*tex have no digits appeared? */
+ boolean vacuous;
+ /*tex has an error message been issued? */
+ boolean OK_so_far;
+ radix = 0;
+ OK_so_far = true;
+ /*tex Get the next non-blank non-sign token; set |negative| appropriately. */
+ negative = false;
+ do {
+ /*tex Get the next non-blank non-call token. */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_tok == other_token + '-') {
+ negative = !negative;
+ cur_tok = other_token + '+';
+ }
+ } while (cur_tok == other_token + '+');
+
+ RESTART:
+ if (cur_tok == alpha_token) {
+ /*tex
+
+ Scan an alphabetic character code into |cur_val|. A space is ignored
+ after an alphabetic character constant, so that such constants behave
+ like numeric ones.
+
+ */
+ /*tex Suppress macro expansion: */
+ get_token();
+ if (cur_tok < cs_token_flag) {
+ cur_val = cur_chr;
+ if (cur_cmd <= right_brace_cmd) {
+ if (cur_cmd == right_brace_cmd)
+ incr(align_state);
+ else
+ decr(align_state);
+ }
+ } else {
+ /*tex The value of a csname in this context is its name. */
+ str_number txt = cs_text(cur_tok - cs_token_flag);
+ if (is_active_cs(txt))
+ cur_val = active_cs_value(txt);
+ else if (single_letter(txt))
+ cur_val = pool_to_unichar(str_string(txt));
+ else
+ cur_val = (biggest_char + 1);
+ }
+ if (cur_val > biggest_char) {
+ print_err("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 {
+ /*tex Scan an optional space. */
+ get_x_token();
+ if (cur_cmd != spacer_cmd)
+ back_input();
+ }
+
+ } else if (cur_tok == cs_token_flag + frozen_primitive) {
+ /*tex
+
+ Reset |cur_tok| for unexpandable primitives, goto restart This block
+ deals with unexpandable \.{\\primitive} appearing at a spot where an
+ integer or an internal values should have been found. It fetches the
+ next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on
+ the primitive value of that token. No expansion takes place, because
+ the next token may be all sorts of things. This could trigger further
+ expansion creating new errors.
+
+ */
+ get_token();
+ cur_cs = prim_lookup(cs_text(cur_cs));
+ if (cur_cs != undefined_primitive) {
+ cur_cmd = get_prim_eq_type(cur_cs);
+ cur_chr = get_prim_equiv(cur_cs);
+ cur_tok = token_val(cur_cmd, cur_chr);
+ } else {
+ cur_cmd = relax_cmd;
+ cur_chr = 0;
+ cur_tok = cs_token_flag + frozen_relax;
+ cur_cs = frozen_relax;
+ }
+ goto RESTART;
+ } else if (cur_cmd == math_style_cmd) {
+ cur_val = cur_chr;
+ } else if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
+ scan_something_internal(int_val_level, false);
+ } else {
+ /*tex 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;
+ /*tex Accumulate the constant until |cur_tok| is not a suitable digit. */
+ while (1) {
+ if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token) && (cur_tok <= nine_token)) {
+ 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 {
+ break;
+ }
+ } else {
+ break;
+ }
+ vacuous = false;
+ if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) {
+ if (OK_so_far) {
+ print_err("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();
+ }
+ if (vacuous) {
+ /*tex Express astonishment that no number was here */
+ print_err("Missing number, treated as zero");
+ 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.)"
+ );
+ back_error();
+ } else if (cur_cmd != spacer_cmd) {
+ back_input();
+ }
+ }
+ if (negative)
+ negate(cur_val);
+}
+
+/*tex
+
+ The following code is executed when |scan_something_internal| was called
+ asking for |mu_val|, when we really wanted a ``mudimen'' instead of
+ ``muglue.''
+
+*/
+
+static void coerce_glue(void)
+{
+ int v;
+ if (cur_val_level >= glue_val_level) {
+ v = width(cur_val);
+ flush_node(cur_val);
+ cur_val = v;
+ }
+}
+
+/*tex
+
+ 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|.
+
+*/
+
+/*tex The order of infinity found by |scan_dimen|: */
+
+int cur_order;
+
+/*tex
+
+ 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 |integer| to |scaled|.
+
+ The necessary conversion factors can all be specified exactly as fractions
+ whose numerator and denominator add 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). The Didot
+ point has been newly standardized in 1978; it's now exactly $\rm
+ 1\,nd=0.375\,mm$. Conversion uses the equation
+ $0.375=21681/20320/72.27\cdot25.4$. The new Cicero follows the new Didot
+ point; $\rm 1\,nc=12\,nd$. These would lead to the ratios $21681/20320$ and
+ $65043/5080$, respectively. The closest approximations supported by the
+ algorithm would be $11183/10481$ and $1370/107$. In order to maintain the
+ relation $\rm 1\,nc=12\,nd$, we pick the ratio $685/642$ for $\rm nd$,
+ however.
+
+*/
+
+static void scan_dimen_mu_error(void) {
+ print_err("Illegal unit of measure (mu inserted)");
+ 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.)"
+ );
+ error();
+}
+
+static void scan_dimen_unknown_unit_error(void) {
+ print_err("Illegal unit of measure (pt inserted)");
+ help6(
+ "Dimensions can be in units of em, ex, in, pt, pc,",
+ "cm, mm, dd, cc, nd, nc, 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.)"
+ );
+ error();
+}
+
+static void scan_dimen_out_of_range_error(void) {
+ print_err("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();
+}
+
+#define set_conversion(A,B) do { num=(A); denom=(B); } while(0)
+
+/*tex
+
+ This function sets |cur_val| to a dimension. It could be optimized a bit more
+ (but not now, something for luatex > 1).
+
+*/
+
+void scan_dimen(boolean mu, boolean inf, boolean shortcut)
+{
+ /*tex should the answer be negated? */
+ boolean negative = false;
+ boolean is_true = false;
+ /*tex numerator of a fraction whose denominator is $2^{16}$ */
+ int f = 0;
+ /*tex conversion ratio for the scanned units */
+ int num = 0;
+ int denom = 0;
+ /*tex top of decimal digit stack */
+ halfword q;
+ /*tex an internal dimension */
+ scaled v;
+ /*tex temporary storage of |cur_val| */
+ int save_cur_val;
+ arith_error = false;
+ cur_order = normal;
+ if (!shortcut) {
+ /*tex Get the next non-blank non-sign. */
+ do {
+ /*tex Get the next non-blank non-call token. */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_tok == minus_token) {
+ negative = !negative;
+ cur_tok = plus_token;
+ }
+ } while (cur_tok == plus_token);
+ if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
+ /*tex Fetch an internal dimension and |goto attach_sign|, or fetch an internal integer. */
+ if (mu) {
+ scan_something_internal(mu_val_level, false);
+ coerce_glue();
+ if (cur_val_level == mu_val_level) {
+ goto ATTACH_SIGN;
+ } else if (cur_val_level != int_val_level) {
+ mu_error();
+ }
+ } else {
+ scan_something_internal(dimen_val_level, false);
+ if (cur_val_level == dimen_val_level) {
+ goto ATTACH_SIGN;
+ }
+ }
+ } 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)) {
+ /*tex
+
+ Scan decimal fraction. 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.
+
+ */
+ int k = 0;
+ halfword p = null;
+ int kk;
+ /*tex The |point_token| is being re-scanned. */
+ get_token();
+ while (1) {
+ get_x_token();
+ if ((cur_tok > nine_token) || (cur_tok < zero_token))
+ break;
+ if (k < 17) {
+ /*tex Digits for |k>=17| cannot affect the result. */
+ q = get_avail();
+ set_token_link(q, p);
+ set_token_info(q, cur_tok - zero_token);
+ p = q;
+ incr(k);
+ }
+ }
+ for (kk = k; kk >= 1; kk--) {
+ dig[kk - 1] = token_info(p);
+ q = p;
+ p = token_link(p);
+ free_avail(q);
+ }
+ f = round_decimals(k);
+ if (cur_cmd != spacer_cmd) {
+ back_input();
+ }
+ }
+ }
+ }
+ if (cur_val < 0) {
+ /*tex In this case |f=0|. */
+ negative = !negative;
+ negate(cur_val);
+ }
+ /*tex
+
+ 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. 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.
+
+ */
+ if (inf) {
+ if (scan_keyword("fi")) {
+ cur_order = sfi;
+ if (scan_keyword("l")) {
+ cur_order = fil;
+ if (scan_keyword("l")) {
+ cur_order = fill;
+ if (scan_keyword("l")) {
+ cur_order = filll;
+ }
+ }
+ }
+ goto ATTACH_FRACTION;
+ }
+ }
+ /*tex
+
+ Scan for (u)units that are internal dimensions; |goto attach_sign| with
+ |cur_val| set if found.
+
+ */
+ save_cur_val = cur_val;
+ /*tex
+
+ Get the next non-blank non-call; a pitty if just backed up the input.
+
+ */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+
+ if ((cur_cmd < min_internal_cmd) || (cur_cmd > max_internal_cmd)) {
+ back_input();
+ } else {
+ /*tex |math_given_cmd|, |xmath_given_cmd| and |last_item_cmd} */
+ if (mu) {
+ scan_something_internal(mu_val_level, false);
+ coerce_glue();
+ if (cur_val_level != mu_val_level) {
+ mu_error();
+ }
+ } else {
+ scan_something_internal(dimen_val_level, false);
+ }
+ v = cur_val;
+ goto FOUND;
+ }
+ /*tex Bah |true| forces to split the unit scanner. */
+ if (mu) {
+ /*tex Scan for (m)\.{mu} units and |goto attach_fraction|. */
+ if (! scan_keyword("mu")) {
+ scan_dimen_mu_error();
+ }
+ goto ATTACH_FRACTION;
+ } else if (scan_keyword("em")) {
+ v = quad(get_cur_font());
+ } else if (scan_keyword("ex")) {
+ v = x_height(get_cur_font());
+ } else if (scan_keyword("px")) {
+ v = px_dimen_par;
+ } else {
+ goto PICKUP_UNIT;
+ }
+ /*tex Scan an optional space (after em, ex or px) */
+ get_x_token();
+ if (cur_cmd != spacer_cmd) {
+ back_input();
+ }
+ FOUND:
+ cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000));
+ goto ATTACH_SIGN;
+ /*tex
+
+ Scan for (a)all other units and adjust |cur_val| and |f| accordingly;
+ |goto done| in the case of scaled points.
+
+ */
+ PICKUP_UNIT:
+ if (scan_keyword("pt")) {
+ /*tex The easy case: */
+ goto SCALE_VALUE;
+ } else if (scan_keyword("mm")) {
+ set_conversion(7227, 2540);
+ goto SCALE_VALUE;
+ } else if (scan_keyword("cm")) {
+ set_conversion(7227, 254);
+ goto SCALE_VALUE;
+ } else if (scan_keyword("sp")) {
+ goto DONE;
+ } else if (scan_keyword("bp")) {
+ set_conversion(7227, 7200);
+ goto SCALE_VALUE;
+ } else if (scan_keyword("in")) {
+ set_conversion(7227, 100);
+ goto SCALE_VALUE;
+ } else if (scan_keyword("dd")) {
+ set_conversion(1238, 1157);
+ goto SCALE_VALUE;
+ } else if (scan_keyword("cc")) {
+ set_conversion(14856, 1157);
+ goto SCALE_VALUE;
+ } else if (scan_keyword("pc")) {
+ set_conversion(12, 1);
+ goto SCALE_VALUE;
+ } else if (scan_keyword("nd")) {
+ set_conversion(685, 642);
+ goto SCALE_VALUE;
+ } else if (scan_keyword("nc")) {
+ set_conversion(1370, 107);
+ goto SCALE_VALUE;
+ } else if (!is_true && scan_keyword("true")) {
+ is_true = true;
+ goto PICKUP_UNIT;
+ }
+ /*tex Complain about unknown unit and |goto done2|. */
+ scan_dimen_unknown_unit_error();
+ goto BAD_NEWS;
+ SCALE_VALUE:
+ /*tex Adjust |f| for the magnification ratio. */
+ if (is_true) {
+ /*tex Maybe at some point we will drop mag completely, even in \DVI\ mode. */
+ if (output_mode_used <= OMODE_DVI) {
+ prepare_mag();
+ if (mag_par != 1000) {
+ cur_val = xn_over_d(cur_val, 1000, mag_par);
+ f = (1000 * f + 0200000 * tex_remainder) / mag_par;
+ cur_val = cur_val + (f / 0200000);
+ f = f % 0200000;
+ }
+ } else {
+ /*tex in \PDF\ mode we're always |true|. */
+ one_true_inch = one_inch;
+ }
+ }
+ if (num) {
+ cur_val = xn_over_d(cur_val, num, denom);
+ f = (num * f + 0200000 * tex_remainder) / denom;
+ cur_val = cur_val + (f / 0200000);
+ f = f % 0200000;
+ }
+ BAD_NEWS:
+ ATTACH_FRACTION:
+ if (cur_val >= 040000) {
+ arith_error = true;
+ } else {
+ cur_val = cur_val * unity + f;
+ }
+ DONE:
+ /*tex Scan an optional space; this happens too often. */
+ get_x_token();
+ if (cur_cmd != spacer_cmd) {
+ back_input();
+ }
+ ATTACH_SIGN:
+ if (arith_error || (abs(cur_val) >= 010000000000)) {
+ /*tex Report that this dimension is out of range. */
+ scan_dimen_out_of_range_error();
+ cur_val = max_dimen;
+ arith_error = false;
+ }
+ if (negative) {
+ negate(cur_val);
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void scan_glue(int level)
+{
+ /*tex should the answer be negated? */
+ boolean negative = false;
+ /*tex new glue specification */
+ halfword q = null;
+ /*tex does |level=mu_val|? */
+ boolean mu = (level == mu_val_level);
+ /*tex Get the next non-blank non-sign. */
+ do {
+ /*tex Get the next non-blank non-call token. */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_tok == minus_token) {
+ negative = !negative;
+ cur_tok = plus_token;
+ }
+ } while (cur_tok == plus_token);
+ if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
+ scan_something_internal(level, negative);
+ if (cur_val_level >= glue_val_level) {
+ if (cur_val_level != level)
+ mu_error();
+ return;
+ }
+ if (cur_val_level == int_val_level)
+ scan_dimen(mu, false, true);
+ else if (level == mu_val_level)
+ mu_error();
+ } else {
+ back_input();
+ scan_dimen(mu, false, false);
+ if (negative)
+ negate(cur_val);
+ }
+ /*tex
+
+ Create a new glue specification whose width is |cur_val|; scan for its
+ stretch and shrink components.
+
+ */
+ q = new_spec(zero_glue);
+ width(q) = cur_val;
+ if (scan_keyword("plus")) {
+ scan_dimen(mu, true, false);
+ stretch(q) = cur_val;
+ stretch_order(q) = (quarterword) cur_order;
+ }
+ if (scan_keyword("minus")) {
+ scan_dimen(mu, true, false);
+ shrink(q) = cur_val;
+ shrink_order(q) = (quarterword) cur_order;
+ }
+ cur_val = q;
+}
+
+/*tex
+
+ 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}'.
+
+*/
+
+halfword the_toks(void)
+{
+ /*tex holds |selector| setting */
+ int old_setting;
+ /*tex used for copying a token list */
+ halfword p, q, r;
+ /*tex value of |cur_chr| */
+ int c;
+ str_number s;
+ halfword retval;
+ /*tex 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;
+ p = get_avail();
+ set_token_link(p, token_link(temp_token_head));
+ token_show(p);
+ flush_list(p);
+ selector = old_setting;
+ s = make_string();
+ retval = str_toks(str_lstring(s));
+ flush_str(s);
+ return retval;
+ }
+ }
+ get_x_token();
+ scan_something_internal(tok_val_level, false);
+ if (cur_val_level >= ident_val_level) {
+ /*tex Copy the token list */
+ p = temp_token_head;
+ set_token_link(p, null);
+ if (cur_val_level == ident_val_level) {
+ store_new_token(cs_token_flag + cur_val);
+ } else if (cur_val != null) {
+ /*tex Do not copy the reference count! */
+ r = token_link(cur_val);
+ while (r != null) {
+ fast_store_new_token(token_info(r));
+ r = token_link(r);
+ }
+ }
+ return p;
+ } else {
+ old_setting = selector;
+ selector = new_string;
+ switch (cur_val_level) {
+ case int_val_level:
+ print_int(cur_val);
+ break;
+ case attr_val_level:
+ print_int(cur_val);
+ break;
+ case dir_val_level:
+ print_dir_par(cur_val);
+ break;
+ case dimen_val_level:
+ print_scaled(cur_val);
+ tprint("pt");
+ break;
+ case glue_val_level:
+ print_spec(cur_val, "pt");
+ flush_node(cur_val);
+ break;
+ case mu_val_level:
+ print_spec(cur_val, "mu");
+ flush_node(cur_val);
+ break;
+ }
+ selector = old_setting;
+ s = make_string();
+ retval = str_toks(str_lstring(s));
+ flush_str(s);
+ return retval;
+ }
+}
+
+str_number the_scanned_result(void)
+{
+ /*tex holds |selector| setting */
+ int old_setting;
+ /*tex return value */
+ str_number r;
+ old_setting = selector;
+ selector = new_string;
+ if (cur_val_level >= ident_val_level) {
+ if (cur_val != null) {
+ show_token_list(token_link(cur_val), null, -1);
+ r = make_string();
+ } else {
+ r = get_nullstr();
+ }
+ } else {
+ switch (cur_val_level) {
+ case int_val_level:
+ print_int(cur_val);
+ break;
+ case attr_val_level:
+ print_int(cur_val);
+ break;
+ case dir_val_level:
+ print_dir_par(cur_val);
+ break;
+ case dimen_val_level:
+ print_scaled(cur_val);
+ tprint("pt");
+ break;
+ case glue_val_level:
+ print_spec(cur_val, "pt");
+ flush_node(cur_val);
+ break;
+ case mu_val_level:
+ print_spec(cur_val, "mu");
+ flush_node(cur_val);
+ break;
+ }
+ r = make_string();
+ }
+ selector = old_setting;
+ return r;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static void font_param_error(int f)
+{
+ print_err("Font ");
+ print_esc(font_id_text(f));
+ tprint(" has only ");
+ print_int(font_params(f));
+ tprint(" fontdimen parameters");
+ help2(
+ "To increase the number of font parameters, you must",
+ "use \\fontdimen immediately after the \\font is loaded."
+ );
+ error();
+}
+
+void set_font_dimen(void)
+{
+ internal_font_number f;
+ int n;
+ scan_int();
+ n = cur_val;
+ scan_font_ident();
+ f = cur_val;
+ if (n <= 0) {
+ font_param_error(f);
+ } else if (n > font_params(f)) {
+ if (font_used(f)) {
+ font_param_error(f);
+ } else {
+ /*tex Increase the number of parameters in the font. */
+ do {
+ set_font_param(f, (font_params(f) + 1), 0);
+ } while (n != font_params(f));
+ }
+ }
+ scan_optional_equals();
+ scan_normal_dimen();
+ set_font_param(f, n, cur_val);
+}
+
+void get_font_dimen(void)
+{
+ internal_font_number f;
+ /*tex The parameter number: */
+ int n;
+ scan_int();
+ n = cur_val;
+ scan_font_ident();
+ f = cur_val;
+ /*tex Initialize return value: */
+ cur_val = 0;
+ if (n <= 0) {
+ font_param_error(f);
+ goto EXIT;
+ } else if (n > font_params(f)) {
+ if (font_used(f)) {
+ font_param_error(f);
+ goto EXIT;
+ } else {
+ /*tex Increase the number of parameters in the font. */
+ do {
+ set_font_param(f, (font_params(f) + 1), 0);
+ } while (n != font_params(f));
+ }
+ }
+ cur_val = font_param(f, n);
+ EXIT:
+ scanned_result(cur_val, dimen_val_level);
+}
+
+/*tex
+
+ 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).
+
+*/
+
+halfword scan_rule_spec(void)
+{
+ /*tex |width|, |depth|, and |height| all equal |null_flag| now */
+ halfword q;
+ if (cur_cmd == no_vrule_cmd) {
+ q = new_rule(empty_rule);
+ cur_cmd = vrule_cmd;
+ } else if (cur_cmd == no_hrule_cmd) {
+ q = new_rule(empty_rule);
+ cur_cmd = hrule_cmd;
+ } else {
+ q = new_rule(normal_rule);
+ }
+ if (cur_cmd == vrule_cmd) {
+ width(q) = default_rule;
+ rule_dir(q) = body_direction_par;
+ } else {
+ height(q) = default_rule;
+ depth(q) = 0;
+ rule_dir(q) = text_direction_par;
+ }
+ RESWITCH:
+ if (scan_keyword("width")) {
+ scan_normal_dimen();
+ width(q) = cur_val;
+ goto RESWITCH;
+ }
+ if (scan_keyword("height")) {
+ scan_normal_dimen();
+ height(q) = cur_val;
+ goto RESWITCH;
+ }
+ if (scan_keyword("depth")) {
+ scan_normal_dimen();
+ depth(q) = cur_val;
+ goto RESWITCH;
+ }
+ return q;
+}
+
+/*tex Declare procedures that scan font-related stuff. */
+
+void scan_font_ident(void)
+{
+ internal_font_number f;
+ halfword m;
+ /*tex Get the next non-blank non-call. */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ if ((cur_cmd == def_font_cmd) || (cur_cmd == letterspace_font_cmd) || (cur_cmd == copy_font_cmd)) {
+ f = get_cur_font();
+ } else if (cur_cmd == set_font_cmd) {
+ f = cur_chr;
+ set_font_touched(f, 1);
+ } else if (cur_cmd == def_family_cmd) {
+ m = cur_chr;
+ scan_math_family_int();
+ f = fam_fnt(cur_val, m);
+ set_font_touched(f, 1);
+ } else {
+ print_err("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;
+}
+
+/*tex
+
+ The |scan_general_text| procedure is much like |scan_toks(false,false)|, but
+ will be invoked via |expand|, i.e., recursively.
+
+ The token list (balanced text) created by |scan_general_text| begins at
+ |link(temp_token_head)| and ends at |cur_val|. (If |cur_val=temp_token_head|,
+ the list is empty.)
+
+*/
+
+void scan_general_text(void)
+{
+ /*tex Here we save |scanner_status|: */
+ int s;
+ /*tex Here we save |warning_index|: */
+ halfword w;
+ /*tex Here we save |def_ref|: */
+ halfword d;
+ /*tex The tail of the token list being built: */
+ halfword p;
+ /*tex The new node being added to the token list via |store_new_token|: */
+ halfword q;
+ /*tex The number of unmatched left braces: */
+ halfword unbalance;
+ s = scanner_status;
+ w = warning_index;
+ d = def_ref;
+ scanner_status = absorbing;
+ warning_index = cur_cs;
+ p = get_avail();
+ def_ref = p;
+ set_token_ref_count(def_ref, 0);
+ p = def_ref;
+ /*tex Remove the compulsory left brace. */
+ scan_left_brace();
+ unbalance = 1;
+ while (1) {
+ get_token();
+ if (cur_tok < right_brace_limit) {
+ if (cur_cmd < right_brace_cmd) {
+ incr(unbalance);
+ } else {
+ decr(unbalance);
+ if (unbalance == 0)
+ break;
+ }
+ }
+ store_new_token(cur_tok);
+ }
+ q = token_link(def_ref);
+ /*tex Discard reference count. */
+ free_avail(def_ref);
+ if (q == null)
+ cur_val = temp_token_head;
+ else
+ cur_val = p;
+ set_token_link(temp_token_head, q);
+ scanner_status = s;
+ warning_index = w;
+ def_ref = d;
+}
+
+/*tex
+
+ The |get_x_or_protected| procedure is like |get_x_token| except that
+ protected macros are not expanded. It sets |cur_cmd|, |cur_chr|,
+ |cur_tok|,and expands non-protected macros.
+
+*/
+
+void get_x_or_protected(void)
+{
+
+ while (1) {
+ get_token();
+ if (cur_cmd <= max_command_cmd)
+ return;
+ if ((cur_cmd >= call_cmd) && (cur_cmd < end_template_cmd)) {
+ if (token_info(token_link(cur_chr)) == protected_token)
+ return;
+ }
+ expand();
+ }
+}
+
+/*tex
+
+ |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.
+
+*/
+
+halfword scan_toks(boolean macro_def, boolean xpand)
+{
+ /*tex token representing the highest parameter number */
+ halfword t;
+ /*tex saved token */
+ halfword s;
+ /*tex tail of the token list being built */
+ halfword p;
+ /*tex new node being added to the token list via |store_new_token| */
+ halfword q;
+ /*tex number of unmatched left braces */
+ halfword unbalance;
+ /*tex possible `\.{\#\{}' token */
+ halfword hash_brace;
+ if (macro_def)
+ scanner_status = defining;
+ else
+ scanner_status = absorbing;
+ warning_index = cur_cs;
+ p = get_avail();
+ def_ref = p;
+ set_token_ref_count(def_ref, 0);
+ p = def_ref;
+ hash_brace = 0;
+ t = zero_token;
+ if (macro_def) {
+ /*tex Scan and build the parameter part of the macro definition. */
+ while (1) {
+ /*tex Set |cur_cmd|, |cur_chr|, |cur_tok|: */
+ get_token();
+ if (cur_tok < right_brace_limit)
+ break;
+ if (cur_cmd == mac_param_cmd) {
+ /*tex
+
+ 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|.
+
+ */
+ s = match_token + cur_chr;
+ get_token();
+ /* if (cur_cmd == left_brace_cmd) { */
+ 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 == nine_token) {
+ print_err("You already have nine parameters");
+ help2(
+ "I'm going to ignore the # sign you just used,",
+ "as well as the token that followed it."
+ );
+ error();
+ } else {
+ incr(t);
+ if (cur_tok != t) {
+ print_err("Parameters must be numbered 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;
+ }
+ }
+ store_new_token(cur_tok);
+ }
+ store_new_token(end_match_token);
+ if (cur_cmd == right_brace_cmd) {
+ /*tex Express shock at the missing left brace; |goto found|. */
+ print_err("Missing { inserted");
+ incr(align_state);
+ help2(
+ "Where was the left brace? You said something like `\\def\\a}',",
+ "which I'm going to interpret as `\\def\\a{}'."
+ );
+ error();
+ goto FOUND;
+ }
+
+ } else {
+ /*tex Remove the compulsory left brace. */
+ scan_left_brace();
+ }
+ DONE:
+ /*tex Scan and build the body of the token list; |goto found| when finished. */
+ unbalance = 1;
+ while (1) {
+ if (xpand) {
+ /*tex
+
+ Expand the next part of the input. Here we insert an entire token
+ list created by |the_toks| without expanding it further.
+
+ */
+ while (1) {
+ get_next();
+ if (cur_cmd >= call_cmd) {
+ if (token_info(token_link(cur_chr)) == protected_token) {
+ cur_cmd = relax_cmd;
+ cur_chr = no_expand_flag;
+ }
+ }
+ if (cur_cmd <= max_command_cmd)
+ break;
+ if (cur_cmd != the_cmd) {
+ expand();
+ } else {
+ q = the_toks();
+ if (token_link(temp_token_head) != null) {
+ set_token_link(p, token_link(temp_token_head));
+ p = q;
+ }
+ }
+ }
+ x_token();
+ } else {
+ get_token();
+ }
+ if (cur_tok < right_brace_limit) {
+ if (cur_cmd < right_brace_cmd) {
+ incr(unbalance);
+ } else {
+ decr(unbalance);
+ if (unbalance == 0)
+ goto FOUND;
+ }
+ } else if (cur_cmd == mac_param_cmd) {
+ if (macro_def) {
+ /*tex Look for parameter number or \.{\#\#}. */
+ s = cur_tok;
+ if (xpand)
+ get_x_token();
+ else
+ get_token();
+ if (cur_cmd != mac_param_cmd) {
+ if ((cur_tok <= zero_token) || (cur_tok > t)) {
+ print_err("Illegal parameter number in definition of ");
+ 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;
+ }
+ }
+ }
+ }
+ store_new_token(cur_tok);
+ }
+ FOUND:
+ scanner_status = normal;
+ if (hash_brace != 0)
+ store_new_token(hash_brace);
+ return p;
+}
+
+/*tex
+
+ Here we declare two trivial procedures in order to avoid mutually recursive
+ procedures with parameters.
+
+*/
+
+void scan_normal_glue(void)
+{
+ scan_glue(glue_val_level);
+}
+
+void scan_mu_glue(void)
+{
+ scan_glue(mu_val_level);
+}
+
+/*tex
+
+ The |scan_expr| procedure scans and evaluates an expression. 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.
+
+*/
+
+typedef enum {
+ /*tex \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */
+ expr_none = 0,
+ /*tex \.( $\langle\it expr\rangle$ \.+ seen */
+ expr_add = 1,
+ /*tex \.( $\langle\it expr\rangle$ \.- seen */
+ expr_sub = 2,
+ /*tex $\langle\it term\rangle$ \.* seen */
+ expr_mult = 3,
+ /*tex $\langle\it term\rangle$ \./ seen */
+ expr_div = 4,
+ /*tex $\langle\it term\rangle$ \.* $\langle\it factor\rangle$ \./ seen */
+ expr_scale = 5,
+} expression_states;
+
+/*tex
+
+ 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.
+
+*/
+
+/* Clear a number or dimension and set |arith_error|: */
+
+#define num_error(A) do { \
+ arith_error=true; \
+ A=0; \
+} while (0)
+
+/*tex Clear a glue spec and set |arith_error|: */
+
+#define glue_error(A) do { \
+ arith_error=true; \
+ reset_glue_to_zero(A); \
+} while (0)
+
+#define normalize_glue(A) do { \
+ if (stretch(A)==0) stretch_order(A)=normal; \
+ if (shrink(A)==0) shrink_order(A)=normal; \
+} while (0)
+
+/*tex
+
+ 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.
+
+*/
+
+#define expr_add_sub(A,B,C) add_or_sub((A),(B),(C),(r==expr_sub))
+#define expr_a(A,B) expr_add_sub((A),(B),max_dimen)
+
+/*tex
+
+ 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|.
+
+*/
+
+inline static int add_or_sub(int x, int y, int max_answer, boolean negative)
+{
+ int a;
+ 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;
+}
+
+#define expr_m(A) A = nx_plus_y((A),f,0)
+#define expr_d(A) A=quotient((A),f)
+
+/*tex
+
+ The function |quotient(n,d)| computes the rounded quotient $q=\lfloor
+ n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive.
+
+*/
+
+inline static int quotient(int n, int d)
+{
+ /*tex Should the answer be negated? */
+ boolean negative;
+ /*tex The answer: */
+ int a;
+ 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;
+ /*tex Avoid certain compiler optimizations! */
+ d = n - d;
+ if (d + n >= 0)
+ incr(a);
+ if (negative)
+ negate(a);
+ }
+ return a;
+}
+
+#define expr_s(A) A=fract((A),n,f,max_dimen)
+
+/*tex
+
+ 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 Metafont's
+ |make_fraction| and |take_fraction| routines.
+
+*/
+
+int fract(int x, int n, int d, int max_answer)
+{
+ /*tex should the answer be negated? */
+ boolean negative;
+ /*tex the answer */
+ int a;
+ /*tex a proper fraction */
+ int f;
+ /*tex smallest integer such that |2*h>=d| */
+ int h;
+ /*tex intermediate remainder */
+ int r;
+ /*tex temp variable */
+ int t;
+ 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;
+ }
+ /*tex
+
+ Now |0<n<=x<d| and we compute $f=\lfloor xn/d+{1\over2}\rfloor$. 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.
+
+ */
+ f = 0;
+ r = (d / 2) - d;
+ h = -r;
+ while (1) {
+ if (odd(n)) {
+ r = r + x;
+ if (r >= 0) {
+ r = r - d;
+ incr(f);
+ }
+ }
+ n = n / 2;
+ if (n == 0)
+ break;
+ if (x < h) {
+ x = x + x;
+ } else {
+ t = x - d;
+ x = t + x;
+ f = f + n;
+ if (x < n) {
+ if (x == 0)
+ break;
+ t = x;
+ x = n;
+ n = t;
+ }
+ }
+ }
+ 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;
+}
+
+/*tex Scans and evaluates an expression: */
+
+static void scan_expr(void)
+{
+ /*tex saved values of |arith_error| */
+ boolean a, b;
+ /*tex type of expression */
+ int l;
+ /*tex state of expression so far */
+ int r;
+ /*tex state of term so far */
+ int s;
+ /*tex next operation or type of next factor */
+ int o;
+ /*tex expression so far */
+ int e;
+ /*tex term so far */
+ int t;
+ /*tex current factor */
+ int f;
+ /*tex numerator of combined multiplication and division */
+ int n;
+ /*tex top of expression stack */
+ halfword p;
+ /*tex for stack manipulations */
+ halfword q;
+ l = cur_val_level;
+ a = arith_error;
+ b = false;
+ p = null;
+ /*tex Scan and evaluate an expression |e| of type |l|. */
+ RESTART:
+ r = expr_none;
+ e = 0;
+ s = expr_none;
+ t = 0;
+ n = 0;
+ CONTINUE:
+ if (s == expr_none)
+ o = l;
+ else
+ o = int_val_level;
+ /*tex
+
+ Scan a factor |f| of type |o| or start a subexpression. Get the next
+ non-blank non-call token.
+
+ */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+
+ if (cur_tok == other_token + '(') {
+ /*tex Push the expression stack and |goto restart|. */
+ q = new_node(expr_node, 0);
+ vlink(q) = p;
+ expr_type(q) = (quarterword) l;
+ expr_state(q) = (quarterword) (4 * s + r);
+ expr_e_field(q) = e;
+ expr_t_field(q) = t;
+ expr_n_field(q) = n;
+ p = q;
+ l = o;
+ goto RESTART;
+ }
+ back_input();
+ if ((o == int_val_level) || (o == attr_val_level))
+ scan_int();
+ else if (o == dimen_val_level)
+ scan_normal_dimen();
+ else if (o == glue_val_level)
+ scan_normal_glue();
+ else
+ scan_mu_glue();
+ f = cur_val;
+
+ FOUND:
+ /*tex
+
+ Scan the next operator and set |o| and Get the next non-blank non-call
+ token.
+
+ */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+
+ 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_cmd)
+ back_input();
+ } else if (cur_tok != other_token + ')') {
+ print_err("Missing ) inserted for expression");
+ help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't.");
+ back_error();
+ }
+ }
+ arith_error = b;
+ /*tex Make sure that |f| is in the proper range. */
+ if (((l == int_val_level) || (l == attr_val_level)) || (s > expr_sub)) {
+ if ((f > infinity) || (f < -infinity))
+ num_error(f);
+ } else if (l == dimen_val_level) {
+ 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);
+ }
+ /*tex Cases for evaluation of the current term. */
+ switch (s) {
+ case expr_none:
+ /*tex
+
+ 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.
+
+ */
+ t = f;
+ if ((l >= glue_val_level) && (o != expr_none)) {
+ /*tex Do we really need to copy here? */
+ normalize_glue(t);
+ } else {
+ t = f;
+ }
+ break;
+ case expr_mult:
+ /*tex
+
+ 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|.
+
+ */
+ if (o == expr_div) {
+ n = f;
+ o = expr_scale;
+ } else if ((l == int_val_level) || (l == attr_val_level)) {
+ t = mult_integers(t, f);
+ } else if (l == dimen_val_level) {
+ expr_m(t);
+ } else {
+ expr_m(width(t));
+ expr_m(stretch(t));
+ expr_m(shrink(t));
+ }
+ break;
+ case expr_div:
+ /*tex Here we divide the term |t| by the factor |f|. */
+ if (l < glue_val_level) {
+ expr_d(t);
+ } else {
+ expr_d(width(t));
+ expr_d(stretch(t));
+ expr_d(shrink(t));
+ }
+ break;
+ case expr_scale:
+ /*tex Here the term |t| is multiplied by the quotient $n/f$. */
+ if ((l == int_val_level) || (l == attr_val_level)) {
+ t = fract(t, n, f, infinity);
+ } else if (l == dimen_val_level) {
+ expr_s(t);
+ } else {
+ expr_s(width(t));
+ expr_s(stretch(t));
+ expr_s(shrink(t));
+ }
+ break;
+ }
+ if (o > expr_sub) {
+ s = o;
+ } else {
+ /*tex
+
+ Evaluate the current expression. When a term |t| has been completed
+ it is copied to, added to, or subtracted from the expression |e|.
+
+ */
+ s = expr_none;
+ if (r == expr_none) {
+ e = t;
+ } else if ((l == int_val_level) || (l == attr_val_level)) {
+ e = expr_add_sub(e, t, infinity);
+ } else if (l == dimen_val_level) {
+ e = expr_a(e, t);
+ } else {
+ /*tex
+
+ Compute the sum or difference of two glue specs. We know that
+ |stretch_order(e)>normal| implies |stretch(e)<>0| and
+ |shrink_order(e)>normal| implies |shrink(e)<>0|.
+
+ */
+ 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);
+ }
+ flush_node(t);
+ normalize_glue(e);
+ }
+ r = o;
+ }
+ b = arith_error;
+ if (o != expr_none)
+ goto CONTINUE;
+ if (p != null) {
+ /*tex Pop the expression stack and |goto found|. */
+ f = e;
+ q = p;
+ e = expr_e_field(q);
+ t = expr_t_field(q);
+ n = expr_n_field(q);
+ s = expr_state(q) / 4;
+ r = expr_state(q) % 4;
+ l = expr_type(q);
+ p = vlink(q);
+ flush_node(q);
+ goto FOUND;
+ }
+
+ if (b) {
+ print_err("Arithmetic overflow");
+ help2(
+ "I can't evaluate this expression,",
+ "since the result is out of range."
+ );
+ error();
+ if (l >= glue_val_level) {
+ reset_glue_to_zero(e);
+ } else {
+ e = 0;
+ }
+ }
+ arith_error = a;
+ cur_val = e;
+ cur_val_level = l;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/scanning.h b/Build/source/texk/web2c/luatexdir/tex/scanning.h
new file mode 100644
index 00000000000..e4189a0f67a
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/scanning.h
@@ -0,0 +1,109 @@
+/* scanning.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef SCANNING_H
+# define SCANNING_H
+
+typedef enum {
+ int_val_level = 0, /* integer values */
+ attr_val_level, /* integer values */
+ dimen_val_level, /* dimension values */
+ glue_val_level, /* glue specifications */
+ mu_val_level, /* math glue specifications */
+ dir_val_level, /* directions */
+ ident_val_level, /* font identifier */
+ tok_val_level, /* token lists */
+} value_level_code;
+
+extern void scan_left_brace(void);
+extern void scan_optional_equals(void);
+
+extern int cur_val; /* value returned by numeric scanners */
+extern int cur_val1; /* delcodes are sometimes 51 digits */
+extern int cur_val_level; /* the ``level'' of this value */
+
+extern void scan_something_simple(halfword cmd, halfword subitem);
+extern void scan_something_internal(int level, boolean negative);
+
+extern void scan_limited_int(int max, const char *name);
+
+extern void negate_cur_val(boolean delete_glue);
+
+# define scan_register_num() scan_limited_int(65535,"register code")
+# define scan_mark_num() scan_limited_int(65535,"marks code")
+# define scan_char_num() scan_limited_int(biggest_char,"character code")
+# define scan_four_bit_int() scan_limited_int(15,NULL)
+# define scan_math_family_int() scan_limited_int(255,"math family")
+# define scan_real_fifteen_bit_int() scan_limited_int(32767,"mathchar")
+# define scan_big_fifteen_bit_int() scan_limited_int(0x7FFFFFF,"extended mathchar")
+# define scan_twenty_seven_bit_int() scan_limited_int(0777777777,"delimiter code")
+
+extern void scan_fifteen_bit_int(void);
+extern void scan_fifty_one_bit_int(void);
+
+# define octal_token (other_token+'\'') /* apostrophe, indicates an octal constant */
+# define hex_token (other_token+'"') /* double quote, indicates a hex constant */
+# define alpha_token (other_token+'`') /* reverse apostrophe, precedes alpha constants */
+# define point_token (other_token+'.') /* decimal point */
+# define comma_token (other_token+',') /* decimal comma */
+# define plus_token (other_token + '+')
+# define minus_token (other_token + '-')
+# define continental_point_token (other_token+',') /* decimal point, Eurostyle */
+# define infinity 017777777777 /* the largest positive value that \TeX\ knows */
+# define zero_token (other_token+'0') /* zero, the smallest digit */
+# define nine_token (other_token+'9') /* zero, the smallest digit */
+# define A_token (letter_token+'A') /* the smallest special hex digit */
+# define other_A_token (other_token+'A') /* special hex digit of type |other_char| */
+extern int radix;
+
+extern void scan_int(void);
+
+extern int cur_order;
+
+# define scan_normal_dimen() scan_dimen(false,false,false)
+
+extern void scan_dimen(boolean mu, boolean inf, boolean shortcut);
+extern void scan_glue(int level);
+
+extern halfword the_toks(void);
+extern str_number the_scanned_result(void);
+extern void set_font_dimen(void);
+extern void get_font_dimen(void);
+
+# define default_rule 26214 /* 0.4\thinspace pt */
+
+extern halfword scan_rule_spec(void);
+
+extern void scan_font_ident(void);
+extern void scan_general_text(void);
+extern void get_x_or_protected(void);
+extern halfword scan_toks(boolean macrodef, boolean xpand);
+
+extern void scan_normal_glue(void);
+extern void scan_mu_glue(void);
+
+/* extern int add_or_sub(int x, int y, int max_answer, boolean negative); */
+/* extern int quotient(int n, int d); */
+extern int fract(int x, int n, int d, int max_answer);
+
+/* extern void scan_expr(void); */
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/stringpool.c b/Build/source/texk/web2c/luatexdir/tex/stringpool.c
new file mode 100644
index 00000000000..bc09f814b00
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/stringpool.c
@@ -0,0 +1,375 @@
+/*
+
+stringpool.w
+
+Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+Control sequence names and diagnostic messages are variable-length strings of
+eight-bit characters. Since PASCAL did not have a well-developed string
+mechanism, \TeX\ did 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) bytes off 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_macro(s)<=j<str_start_macro(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_macro(str_ptr)| are ready for the next string
+to be allocated.
+
+String numbers 0 to |biggest_char| are reserved for strings that correspond to
+single UNICODE characters. This is in accordance with the conventions of \.{WEB}
+which converts single-character strings into the ASCII code number of the single
+character involved.
+
+*/
+
+/*tex The array of strings: */
+
+lstring *string_pool;
+
+/*tex
+ This variable lives |STRING_OFFSET| below |string_pool| (handy for debugging:
+ |_string_pool[str_ptr] == str_string(str_ptr)|:
+*/
+
+lstring *_string_pool;
+
+/*tex The number of the current string being created: */
+
+str_number str_ptr = (STRING_OFFSET + 1);
+
+/*tex The starting value of |str_ptr|: */
+
+str_number init_str_ptr;
+
+/*tex
+ The current string buffer, the current index in that buffer, the malloced
+ size of |cur_string| and the occupied byte count:
+*/
+
+unsigned char *cur_string;
+unsigned cur_length;
+unsigned cur_string_size;
+unsigned pool_size;
+
+/*tex
+
+Once a sequence of characters has been appended to |cur_string|, 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.
+
+*/
+
+void reset_cur_string(void)
+{
+ cur_length = 0;
+ cur_string_size = 255;
+ cur_string = (unsigned char *) xmalloc(256);
+ memset(cur_string, 0, 256);
+}
+
+str_number make_string(void)
+{
+ if (str_ptr == (max_strings + STRING_OFFSET)) {
+ overflow(
+ "number of strings",
+ (unsigned) (max_strings - init_str_ptr + STRING_OFFSET)
+ );
+ }
+ str_room(1);
+ cur_string[cur_length] = '\0'; /* now |lstring.s| is always a valid C string */
+ str_string(str_ptr) = (unsigned char *) cur_string;
+ str_length(str_ptr) = cur_length;
+ pool_size += cur_length;
+ reset_cur_string();
+ str_ptr++;
+ return (str_ptr - 1);
+}
+
+int pool_to_unichar(unsigned char *t)
+{
+ return (int) str2uni(t);
+}
+
+/*tex
+
+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.
+
+*/
+
+boolean str_eq_buf(str_number s, int k)
+{
+ int a;
+ if (s < STRING_OFFSET) {
+ a = buffer_to_unichar(k);
+ if (a != s)
+ return false;
+ } else {
+ unsigned char *j = str_string(s);
+ unsigned char *l = j + str_length(s);
+ while (j < l) {
+ if (*j++ != buffer[k++])
+ return false;
+ }
+ }
+ return true;
+}
+
+/*tex
+
+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.
+
+*/
+
+boolean str_eq_str(str_number s, str_number t)
+{
+ int a = 0;
+ unsigned char *j, *k, *l;
+ if (s < STRING_OFFSET) {
+ if (t >= STRING_OFFSET) {
+ k = str_string(t);
+ if (s <= 0x7F && (str_length(t) == 1) && *k == s)
+ return true;
+ a = pool_to_unichar(k);
+ if (a != s)
+ return false;
+ } else {
+ if (t != s)
+ return false;
+ }
+ } else if (t < STRING_OFFSET) {
+ j = str_string(s);
+ if (t <= 0x7F && (str_length(s) == 1) && *j == t)
+ return true;
+ a = pool_to_unichar(j);
+ if (a != t)
+ return false;
+ } else {
+ if (str_length(s) != str_length(t))
+ return false;
+ k = str_string(t);
+ j = str_string(s);
+ l = j + str_length(s);
+ while (j < l) {
+ if (*j++ != *k++)
+ return false;
+ }
+ }
+ return true;
+}
+
+/*tex A string compare helper: */
+
+boolean str_eq_cstr(str_number r, const char *s, size_t l)
+{
+ if (l != (size_t) str_length(r))
+ return false;
+ return (strncmp((const char *) (str_string(r)), s, l) == 0);
+}
+
+
+/*tex
+
+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.
+
+The first |string_offset| strings are single-characters strings matching Unicode.
+There is no point in generating all of these. But |str_ptr| has initialized
+properly, otherwise |print_char| cannot see the difference between characters and
+strings.
+
+*/
+
+boolean get_strings_started(void)
+{
+ reset_cur_string();
+ return true;
+}
+
+/*tex
+
+The string recycling routines. \TeX{} uses 2 upto 4 {\it new\/} strings when
+scanning a filename in an \.{\\input}, \.{\\openin}, or \.{\\openout} operation.
+These strings are normally lost because the reference to them are not saved after
+finishing the operation. |search_string| searches through the string pool for the
+given string and returns either 0 or the found string number.
+
+*/
+
+str_number search_string(str_number search)
+{
+ str_number s;
+ size_t len;
+ len = str_length(search);
+ if (len == 0) {
+ return get_nullstr();
+ } else {
+ /*tex We start the search with newest string below |s|; |search>1|! */
+ s = search - 1;
+ while (s >= STRING_OFFSET) {
+ /* The first |string_offset| of strings depend on the implementation! */
+ if (str_length(s) == len)
+ if (str_eq_str(s, search))
+ return s;
+ s--;
+ }
+ }
+ return 0;
+}
+
+str_number maketexstring(const char *s)
+{
+ if (s == NULL || *s == 0)
+ return get_nullstr();
+ return maketexlstring(s, strlen(s));
+}
+
+str_number maketexlstring(const char *s, size_t l)
+{
+ if (s == NULL || l == 0)
+ return get_nullstr();
+ str_string(str_ptr) = xmalloc((unsigned) (l + 1));
+ memcpy(str_string(str_ptr), s, (l + 1));
+ str_length(str_ptr) = (unsigned) l;
+ str_ptr++;
+ return (str_ptr - 1);
+}
+
+/*tex
+
+ This appends a C string to a \TEX\ string:
+
+*/
+
+void append_string(const unsigned char *s, unsigned l)
+{
+ if (s == NULL || *s == 0)
+ return;
+ l = (unsigned) strlen((const char *) s);
+ str_room(l);
+ memcpy(cur_string + cur_length, s, l);
+ cur_length += l;
+ return;
+}
+
+char *makecstring(int s)
+{
+ size_t l;
+ return makeclstring(s, &l);
+}
+
+char *makeclstring(int s, size_t * len)
+{
+ if (s < STRING_OFFSET) {
+ *len = (size_t) utf8_size(s);
+ return (char *) uni2str((unsigned) s);
+ } else {
+ unsigned l = (unsigned) str_length(s);
+ char *cstrbuf = xmalloc(l + 1);
+ memcpy(cstrbuf, str_string(s), l);
+ cstrbuf[l] = '\0';
+ *len = (size_t) l;
+ return cstrbuf;
+ }
+}
+
+int dump_string_pool(void)
+{
+ int j;
+ int l;
+ int k = str_ptr;
+ dump_int(k - STRING_OFFSET);
+ for (j = STRING_OFFSET + 1; j < k; j++) {
+ l = (int) str_length(j);
+ if (str_string(j) == NULL)
+ l = -1;
+ dump_int(l);
+ if (l > 0)
+ dump_things(*str_string(j), str_length(j));
+ }
+ return (k - STRING_OFFSET);
+}
+
+int undump_string_pool(void)
+{
+ int j;
+ int x;
+ undump_int(str_ptr);
+ if (max_strings < str_ptr + strings_free)
+ max_strings = str_ptr + strings_free;
+ str_ptr += STRING_OFFSET;
+ if (ini_version)
+ libcfree(string_pool);
+ init_string_pool_array((unsigned) max_strings);
+ for (j = STRING_OFFSET + 1; j < str_ptr; j++) {
+ undump_int(x);
+ if (x >= 0) {
+ str_length(j) = (unsigned) x;
+ pool_size += (unsigned) x;
+ str_string(j) = xmallocarray(unsigned char, (unsigned) (x + 1));
+ undump_things(*str_string(j), (unsigned) x);
+ *(str_string(j) + str_length(j)) = '\0';
+ } else {
+ str_length(j) = 0;
+ }
+ }
+ init_str_ptr = str_ptr;
+ return str_ptr;
+}
+
+void init_string_pool_array(unsigned s)
+{
+ string_pool = xmallocarray(lstring, s);
+ _string_pool = string_pool - STRING_OFFSET;
+ memset(string_pool, 0, s * sizeof(lstring));
+ /* seed the null string */
+ string_pool[0].s = xmalloc(1);
+ string_pool[0].s[0] = '\0';
+}
+
+/*tex
+
+ To destroy an already made string, we say |flush_str|.
+
+*/
+
+void flush_str(str_number s)
+{
+ if (s > STRING_OFFSET) {
+ /*tex Don't ever delete the null string! */
+ pool_size -= (unsigned) str_length(s);
+ str_length(s) = 0;
+ xfree(str_string(s));
+ }
+ while (str_string((str_ptr - 1)) == NULL)
+ str_ptr--;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/stringpool.h b/Build/source/texk/web2c/luatexdir/tex/stringpool.h
new file mode 100644
index 00000000000..2ba156046fd
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/stringpool.h
@@ -0,0 +1,142 @@
+/* stringpool.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef STRINGPOOL_H
+# define STRINGPOOL_H
+
+/* Both lua and tex strings can contains null, but C strings cannot, so: */
+
+typedef struct {
+ unsigned char *s;
+ size_t l;
+} lstring;
+
+typedef struct {
+ const char *s;
+ size_t l;
+} const_lstring;
+
+extern lstring *string_pool;
+
+extern str_number str_ptr;
+extern str_number init_str_ptr;
+
+# define STRING_OFFSET 0x200000
+# define STRING_OFFSET_BITS 21
+
+# define get_nullstr() STRING_OFFSET
+
+# define biggest_char 1114111 /* 0x10FFFF, the largest allowed character number; must be |< max_halfword| */
+# define too_big_char (biggest_char+1) /* 1114112, |biggest_char+1| */
+# define special_char (biggest_char+2) /* 1114113, |biggest_char+2| */
+# define number_chars (biggest_char+3) /* 1114112, |biggest_char+1| */
+
+/*
+ Several of the elementary string operations are performed using
+ macros instead of 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.
+*/
+
+# define str_length(a) string_pool[(a)-STRING_OFFSET].l
+# define str_string(a) string_pool[(a)-STRING_OFFSET].s
+# define str_lstring(a) string_pool[(a)-STRING_OFFSET]
+
+/* 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.
+*/
+
+/* The length of the current string is called |cur_length|: */
+
+extern unsigned char *cur_string;
+extern unsigned cur_length;
+extern unsigned cur_string_size;
+extern unsigned pool_size;
+
+# define EXTRA_STRING 500
+
+/* put |ASCII_code| \# at the end of |str_pool| */
+# define append_char(A) do { \
+ if (cur_string==NULL) reset_cur_string(); \
+ else str_room(1); \
+ cur_string[cur_length++]=(unsigned char)(A); \
+ } while (0)
+
+# define str_room(wsize) do { \
+ unsigned nsize; \
+ if ((cur_length+wsize) > cur_string_size) { \
+ nsize = cur_string_size + cur_string_size / 5 + EXTRA_STRING; \
+ if (nsize < (unsigned)(wsize)) { \
+ nsize = wsize + EXTRA_STRING; \
+ } \
+ cur_string = (unsigned char *) xreallocarray(cur_string, unsigned char, nsize); \
+ memset (cur_string+cur_length,0,nsize-cur_length); \
+ cur_string_size = nsize; \
+ } \
+ } while (0)
+
+# define flush_char() --cur_length /* forget the last character in the pool */
+
+extern str_number make_string(void);
+extern boolean str_eq_buf(str_number s, int k);
+extern boolean str_eq_str(str_number s, str_number t);
+extern boolean str_eq_cstr(str_number, const char *, size_t);
+extern boolean get_strings_started(void);
+extern void reset_cur_string(void);
+
+# define save_cur_string() (cur_length>0 ? make_string() : 0)
+
+# define restore_cur_string(u) if (u!=0) { \
+ unsigned l = (unsigned)str_length(u); \
+ xfree(cur_string); \
+ reset_cur_string(); \
+ str_room(l); \
+ memcpy(cur_string, str_string(u),l); \
+ cur_length = l; \
+ flush_str(u); \
+ u=0; \
+ }
+
+
+extern str_number search_string(str_number search);
+extern int pool_to_unichar(unsigned char *t);
+
+extern str_number maketexstring(const char *);
+extern str_number maketexlstring(const char *, size_t);
+extern void append_string(const unsigned char *s, unsigned l);
+
+extern char *makecstring(int);
+extern char *makeclstring(int, size_t *);
+
+extern int dump_string_pool(void);
+extern int undump_string_pool(void);
+
+extern void init_string_pool_array(unsigned s);
+extern void flush_str(str_number s);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.c b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c
new file mode 100644
index 00000000000..1ec4b12bf06
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c
@@ -0,0 +1,247 @@
+/*
+
+texdeffont.w
+
+Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+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.
+
+The variable |a| in the following code indicates the global nature of the value
+to be set. It's used in the |define| macro. Here we're never global.
+
+There's not much scanner code here because the other scanners are defined where
+they make most sense.
+
+*/
+
+int font_bytes;
+
+void set_cur_font(internal_font_number f)
+{
+ int a = 0;
+ define(cur_font_loc, data_cmd, f);
+}
+
+/*tex
+
+ This prints a scaled real, rounded to five digits.
+
+*/
+
+static char *scaled_to_string(scaled s)
+{
+ static char result[16];
+ int n, k;
+ /*tex The amount of allowable inaccuracy: */
+ scaled delta;
+ k = 0;
+ if (s < 0) {
+ /*tex Only print the sign, if negative */
+ result[k++] = '-';
+ s = -s;
+ }
+ {
+ int l = 0;
+ char digs[8] = { 0 };
+ n = s / unity;
+ /*tex Process the integer part: */
+ do {
+ digs[l++] = (char) (n % 10);
+ n = n / 10;;
+ } while (n > 0);
+ while (l > 0) {
+ result[k++] = (char) (digs[--l] + '0');
+ }
+ }
+ result[k++] = '.';
+ s = 10 * (s % unity) + 5;
+ delta = 10;
+ do {
+ if (delta > unity) {
+ /*tex Round the last digit: */
+ s = s + 0100000 - 050000;
+ }
+ result[k++] = (char) ('0' + (s / unity));
+ s = 10 * (s % unity);
+ delta = delta * 10;
+ } while (s > delta);
+ result[k] = 0;
+ return (char *) result;
+}
+
+void tex_def_font(small_number a)
+{
+ /*tex The user's font identifier. */
+ pointer u;
+ /*tex This runs through existing fonts. */
+ internal_font_number f;
+ /*tex The name for the frozen font identifier. */
+ str_number t, d;
+ /*tex Thos holds the |selector| setting. */
+ int old_setting;
+ /*tex Stated `at' size, or negative of scaled magnification. */
+ scaled s = -1000;
+ /*tex The natural direction of the font. */
+ int natural_dir = -1;
+ char *fn;
+ if (job_name == 0) {
+ /*tex Avoid confusing \.{texput} with the font name. */
+ open_log_file();
+ }
+ get_r_token();
+ u = cur_cs;
+ if (a >= 4) {
+ geq_define(u, set_font_cmd, null_font);
+ } else {
+ eq_define(u, set_font_cmd, null_font);
+ }
+ scan_optional_equals();
+ /*tex Get the next non-blank non-call token. */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+ if (cur_cmd != left_brace_cmd) {
+ back_input();
+ scan_file_name();
+ if (cur_area != get_nullstr() || cur_ext != get_nullstr()) {
+ /*tex
+ Have to do some rescue-ing here, fonts only have a name, no area
+ nor extension.
+ */
+ old_setting = selector;
+ selector = new_string;
+ if (cur_area != get_nullstr()) {
+ print(cur_area);
+ flush_str(cur_area);
+ }
+ if (cur_name != get_nullstr()) {
+ print(cur_name);
+ flush_str(cur_name);
+ }
+ if (cur_ext != get_nullstr()) {
+ print(cur_ext);
+ flush_str(cur_ext);
+ }
+ selector = old_setting;
+ cur_area = get_nullstr();
+ cur_name = make_string();
+ cur_ext = get_nullstr();
+ }
+ } else {
+ back_input();
+ (void) scan_toks(false, true);
+ old_setting = selector;
+ selector = new_string;
+ token_show(def_ref);
+ selector = old_setting;
+ flush_list(def_ref);
+ cur_area = get_nullstr();
+ cur_name = make_string();
+ cur_ext = get_nullstr();
+ }
+ /*tex
+ Scan the font size specification. The next variable keeps |cur_name| from
+ being changed
+ */
+ name_in_progress = true;
+ if (scan_keyword("at")) {
+ /*tex Put the positive `at' size into |s|. */
+ scan_normal_dimen();
+ s = cur_val;
+ if ((s <= 0) || (s >= 01000000000)) {
+ char err[256];
+ const char *errhelp[] = {
+ "I can only handle fonts at positive sizes that are",
+ "less than 2048pt, so I've changed what you said to 10pt.",
+ NULL
+ };
+ snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt", scaled_to_string(s));
+ tex_error(err, errhelp);
+ s = 10 * unity;
+ }
+ } else if (scan_keyword("scaled")) {
+ scan_int();
+ s = -cur_val;
+ if ((cur_val <= 0) || (cur_val > 32768)) {
+ char err[256];
+ const char *errhelp[] = {
+ "The magnification ratio must be between 1 and 32768.",
+ NULL
+ };
+ snprintf(err, 255, "Illegal magnification has been changed to 1000 (%d)", (int) cur_val);
+ tex_error(err, errhelp);
+ s = -1000;
+ }
+ }
+ /*tex
+ There is no real reason to support this obsolete key as there are no useful
+ fonts out there so let's get rid of this overhead. This also means that
+ |natural_dir| can go away.
+ */
+ /*
+ if (scan_keyword("naturaldir")) {
+ scan_direction();
+ natural_dir = cur_val;
+ }
+ */
+ name_in_progress = false;
+ fn = makecstring(cur_name);
+ f = read_font_info(u, fn, s, natural_dir);
+ xfree(fn);
+ equiv(u) = f;
+ eqtb[font_id_base + f] = eqtb[u];
+ /*tex
+
+ This is tricky: when we redefine a string we loose the old one. So this
+ will change as it's only used to display the |\fontname| so we can store
+ that with the font.
+
+ */
+ d = cs_text(font_id_base + f);
+ t = (u >= null_cs) ? cs_text(u) : maketexstring("FONT");
+ if (!d) {
+ /*tex We have a new string. */
+ cs_text(font_id_base + f) = t;
+ } else if ((d!=t) && str_eq_str(d,t)){
+ /*tex We have a duplicate string. */
+ flush_str(t);
+ } else if (d!=t){
+ d = search_string(t);
+ if (d) {
+ /*tex We have already such a string. */
+ cs_text(font_id_base + f) = d;
+ flush_str(t);
+ } else {
+ /*tex The old value is lost but still in the pool. */
+ cs_text(font_id_base + f) = t;
+ }
+ }
+ if (cur_name == str_ptr-1) {
+ flush_str(cur_name);
+ cur_name = get_nullstr();
+ }
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.h b/Build/source/texk/web2c/luatexdir/tex/texdeffont.h
new file mode 100644
index 00000000000..24782f5fbe4
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.h
@@ -0,0 +1,30 @@
+/* texdeffont.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef TEXDEFFONT_H
+# define TEXDEFFONT_H
+
+extern int font_bytes;
+
+extern void set_cur_font(internal_font_number f);
+
+extern void tex_def_font(small_number a);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.c b/Build/source/texk/web2c/luatexdir/tex/texfileio.c
new file mode 100644
index 00000000000..4e0677a564b
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texfileio.c
@@ -0,0 +1,1509 @@
+/*
+
+Copyright 2009-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+#include <string.h>
+#include <kpathsea/absolute.h>
+
+/*tex
+
+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.
+
+We finally did away with |nameoffile| and |namelength|, but the variables have to
+be kept otherwise there will be link errors from |openclose.c| in the web2c
+library
+
+*/
+
+char *nameoffile;
+int namelength;
+
+/*tex
+
+ When input files are opened via a callback, they will also be read using
+ callbacks. for that purpose, the |open_read_file_callback| returns an integer
+ to uniquely identify a callback table. This id replaces the file point |f| in
+ this case, because the input does not have to be a file in the traditional
+ sense.
+
+ Signalling this fact is achieved by having two arrays of integers.
+
+*/
+
+int *input_file_callback_id;
+int read_file_callback_id[17];
+
+/*tex
+
+ Here we handle |-output-directory|. We assume that it is OK to look here
+ first. Possibly it would be better to replace lookups in "." with lookups in
+ the |output_directory| followed by "." but to do this requires much more
+ invasive surgery in libkpathsea.
+
+*/
+
+static char *find_in_output_directory(const char *s)
+{
+ if (output_directory && !kpse_absolute_p(s, false)) {
+ FILE *f_ptr;
+ char *ftemp = concat3(output_directory, DIR_SEP_STRING, s);
+ /*tex This code is used for input files only. */
+ f_ptr = fopen(ftemp, "rb");
+ if (f_ptr) {
+ fclose(f_ptr);
+ return ftemp;
+ } else {
+ free(ftemp);
+
+ }
+ }
+ return NULL;
+}
+
+/*tex
+
+ Find an \.{\\input} or \.{\\read} file. |n| differentiates between those
+ case.
+
+*/
+
+int kpse_available(const char *m) {
+ if (!kpse_init) {
+ fprintf(stdout,"missing kpse replacement callback '%s', quitting\n",m);
+ exit(1);
+ }
+ return 1 ;
+}
+
+char *luatex_find_read_file(const char *s, int n, int callback_index)
+{
+ char *ftemp = NULL;
+ int callback_id = callback_defined(callback_index);
+ if (callback_id > 0) {
+ (void) run_callback(callback_id, "dS->R", n, s, &ftemp);
+ } else if (kpse_available("find_read_file")) {
+ /*tex Use kpathsea here. */
+ ftemp = find_in_output_directory(s);
+ if (!ftemp)
+ ftemp = kpse_find_file(s, kpse_tex_format, 1);
+ }
+ if (ftemp) {
+ if (fullnameoffile)
+ free(fullnameoffile);
+ fullnameoffile = xstrdup(ftemp);
+ }
+ return ftemp;
+}
+
+/*tex Find other files types. */
+
+char *luatex_find_file(const char *s, int callback_index)
+{
+ char *ftemp = NULL;
+ int callback_id = callback_defined(callback_index);
+ if (callback_id > 0) {
+ (void) run_callback(callback_id, "S->R", s, &ftemp);
+ } else if (kpse_available("find_read_file")) {
+ /*tex Use kpathsea here. */
+ switch (callback_index) {
+ case find_enc_file_callback:
+ ftemp = kpse_find_file(s, kpse_enc_format, 0);
+ break;
+ case find_map_file_callback:
+ ftemp = kpse_find_file(s, kpse_fontmap_format, 0);
+ break;
+ case find_type1_file_callback:
+ ftemp = kpse_find_file(s, kpse_type1_format, 0);
+ break;
+ case find_truetype_file_callback:
+ ftemp = kpse_find_file(s, kpse_truetype_format, 0);
+ break;
+ case find_opentype_file_callback:
+ ftemp = kpse_find_file(s, kpse_opentype_format, 0);
+ if (ftemp == NULL)
+ ftemp = kpse_find_file(s, kpse_truetype_format, 0);
+ break;
+ case find_data_file_callback:
+ ftemp = find_in_output_directory(s);
+ if (!ftemp)
+ ftemp = kpse_find_file(s, kpse_tex_format, 1);
+ break;
+ case find_font_file_callback:
+ ftemp = kpse_find_file(s, kpse_ofm_format, 1);
+ if (ftemp == NULL)
+ ftemp = kpse_find_file(s, kpse_tfm_format, 1);
+ break;
+ case find_vf_file_callback:
+ ftemp = kpse_find_file(s, kpse_ovf_format, 0);
+ if (ftemp == NULL)
+ ftemp = kpse_find_file(s, kpse_vf_format, 0);
+ break;
+ case find_cidmap_file_callback:
+ ftemp = kpse_find_file(s, kpse_cid_format, 0);
+ break;
+ default:
+ printf("luatex_find_file(): do not know how to handle file %s of type %d\n", s, callback_index);
+ break;
+ }
+ }
+ return ftemp;
+}
+
+/*tex
+
+ \LUATEX\ used to have private functions for these that did not use kpathsea,
+ but since the file paranoia tests have to come from kpathsea anyway, that is
+ no longer useful. The only downside to using luatex is that if one wants to
+ disable kpathsea via the Lua startup script, it is now an absolute
+ requirement that all file discovery callbacks are specified. Just using the
+ find_read_file, but not setting open_read_file, for example, does not work
+ any more if kpathsea is not to be used at all.
+
+*/
+
+#define openoutnameok(A) kpse_out_name_ok (A)
+#define openinnameok(A) kpse_in_name_ok (A)
+
+/*tex
+
+ Open an input file F, using the kpathsea format FILEFMT and passing
+ |FOPEN_MODE| to fopen. The filename is in `fn'. We return whether or not the
+ open succeeded.
+
+*/
+
+boolean luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, const_string fopen_mode, boolean must_exist)
+{
+ /*tex We haven't found anything yet. */
+ string fname = NULL;
+ *f_ptr = NULL;
+ if (fullnameoffile)
+ free(fullnameoffile);
+ fullnameoffile = NULL;
+ fname = kpse_find_file(fn, (kpse_file_format_type) filefmt, must_exist);
+ if (fname) {
+ fullnameoffile = xstrdup(fname);
+ /*tex
+
+ If we found the file in the current directory, don't leave the `./'
+ at the beginning of `fn', since it looks dumb when `tex foo' says
+ `(./foo.tex ... )'. On the other hand, if the user said `tex ./foo',
+ and that's what we opened, then keep it -- the user specified it, so
+ we shouldn't remove it.
+
+ */
+ if (fname[0] == '.' && IS_DIR_SEP(fname[1]) && (fn[0] != '.' || !IS_DIR_SEP(fn[1]))) {
+ unsigned i = 0;
+ while (fname[i + 2] != 0) {
+ fname[i] = fname[i + 2];
+ i++;
+ }
+ fname[i] = 0;
+ }
+ /*tex This fopen is not allowed to fail. */
+ *f_ptr = xfopen(fname, fopen_mode);
+ }
+ if (*f_ptr) {
+ recorder_record_input(fname);
+ }
+ return *f_ptr != NULL;
+}
+
+boolean luatex_open_output(FILE ** f_ptr, const char *fn, const_string fopen_mode)
+{
+ char *fname;
+ boolean absolute = kpse_absolute_p(fn, false);
+ /*tex If we have an explicit output directory, use it. */
+ if (output_directory && !absolute) {
+ fname = concat3(output_directory, DIR_SEP_STRING, fn);
+ } else {
+ fname = xstrdup(fn);
+ }
+ /*tex Is the filename openable as given? */
+ *f_ptr = fopen(fname, fopen_mode);
+ if (!*f_ptr) {
+ /*tex Can't open as given. Try the envvar. */
+ string texmfoutput = kpse_var_value("TEXMFOUTPUT");
+ if (texmfoutput && *texmfoutput && !absolute) {
+ fname = concat3(texmfoutput, DIR_SEP_STRING, fn);
+ *f_ptr = fopen(fname, fopen_mode);
+ }
+ }
+ if (*f_ptr) {
+ recorder_record_output(fname);
+ }
+ free(fname);
+ return *f_ptr != NULL;
+}
+
+boolean lua_a_open_in(alpha_file * f, char *fn, int n)
+{
+ int k;
+ char *fnam;
+ int callback_id;
+ boolean ret = true;
+ boolean file_ok = true;
+ if (n == 0) {
+ input_file_callback_id[iindex] = 0;
+ } else {
+ read_file_callback_id[n] = 0;
+ }
+ if (*fn == '|')
+ fnam = fn;
+ else
+ fnam = luatex_find_read_file(fn, n, find_read_file_callback);
+ if (!fnam)
+ return false;
+ callback_id = callback_defined(open_read_file_callback);
+ if (callback_id > 0) {
+ k = run_and_save_callback(callback_id, "S->", fnam);
+ if (k > 0) {
+ ret = true;
+ if (n == 0)
+ input_file_callback_id[iindex] = k;
+ else
+ read_file_callback_id[n] = k;
+ } else {
+ /*tex read failed */
+ file_ok = false;
+ }
+ } else {
+ /*tex no read callback */
+ if (openinnameok(fn)) {
+ ret = open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, (n == 0 ? true : false));
+ } else {
+ /*tex open failed */
+ file_ok = false;
+ }
+ }
+ if (!file_ok) {
+ ret = false;
+ }
+ return ret;
+}
+
+boolean lua_a_open_out(alpha_file * f, char *fn, int n)
+{
+ boolean test;
+ char *fnam = NULL;
+ int callback_id;
+ boolean ret = false;
+ callback_id = callback_defined(find_write_file_callback);
+ if (callback_id > 0) {
+ test = run_callback(callback_id, "dS->R", n, fn, &fnam);
+ if ((test) && (fnam != NULL) && (strlen(fnam) > 0)) {
+ /*tex
+
+ There is no message here because if that is needed the macro
+ package should do that in the callback code. As elsewhere,
+ messaging is left to \LUA\ then.
+
+ */
+ ret = open_outfile(f, fnam, FOPEN_W_MODE);
+ free(fnam);
+ }
+ } else {
+ if (openoutnameok(fn)) {
+ if (n > 0 && selector != term_only) {
+ /*tex
+
+ This message to the log is for downward compatibility with
+ other tex's as there are scripts out there that act on this
+ message. An alternative is to let a macro package write an
+ explicit message.
+
+ */
+ fprintf(log_file,"\n\\openout%i = %s\n",n-1,fn);
+ }
+ ret = open_out_or_pipe(f, fn, FOPEN_W_MODE);
+ }
+ }
+ return ret;
+}
+
+boolean lua_b_open_out(alpha_file * f, char *fn)
+{
+ boolean test;
+ char *fnam = NULL;
+ int callback_id;
+ boolean ret = false;
+ callback_id = callback_defined(find_output_file_callback);
+ if (callback_id > 0) {
+ test = run_callback(callback_id, "S->R", fn, &fnam);
+ if ((test) && (fnam != NULL) && (strlen(fnam) > 0)) {
+ ret = open_outfile(f, fnam, FOPEN_WBIN_MODE);
+ free(fnam);
+ }
+ } else {
+ if (openoutnameok(fn)) {
+ ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE);
+ }
+ }
+ return ret;
+}
+
+void lua_a_close_in(alpha_file f, int n)
+{
+ int callback_id;
+ if (n == 0)
+ callback_id = input_file_callback_id[iindex];
+ else
+ callback_id = read_file_callback_id[n];
+ if (callback_id > 0) {
+ run_saved_callback(callback_id, "close", "->");
+ destroy_saved_callback(callback_id);
+ if (n == 0)
+ input_file_callback_id[iindex] = 0;
+ else
+ read_file_callback_id[n] = 0;
+ } else {
+ close_file_or_pipe(f);
+ }
+}
+
+void lua_a_close_out(alpha_file f)
+{
+ close_file_or_pipe(f);
+}
+
+/*tex
+
+ Binary input and output are done with C's ordinary 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 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
+ |lua_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.
+
+*/
+
+/*tex lines of characters being read */
+
+packed_ASCII_code *buffer;
+
+/*tex the first unused position in |buffer| */
+
+int first;
+
+/*tex end of the line just input to |buffer| */
+
+int last;
+
+/*tex largest index used in |buffer| */
+
+int max_buf_stack;
+
+/*tex
+
+ The |lua_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 |lua_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 |lua_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 |lua_input_ln|.
+
+ If the |bypass_eoln| parameter is |true|, |lua_input_ln| will do a |get|
+ before looking at the first character of the line; this skips over an |eoln|
+ that was in |f^|. 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.
+
+ Since the inner loop of |lua_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.
+
+*/
+
+boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln)
+{
+ boolean lua_result;
+ int last_ptr;
+ int callback_id;
+ /*tex Todo: variable can be removed: */
+ (void) bypass_eoln;
+ if (n == 0)
+ callback_id = input_file_callback_id[iindex];
+ else
+ callback_id = read_file_callback_id[n];
+ if (callback_id > 0) {
+ last = first;
+ last_ptr = first;
+ lua_result =
+ run_saved_callback(callback_id, "reader", "->l", &last_ptr);
+ if ((lua_result == true) && (last_ptr != 0)) {
+ last = last_ptr;
+ if (last > max_buf_stack)
+ max_buf_stack = last;
+ } else {
+ lua_result = false;
+ }
+ } else {
+ lua_result = input_ln(f, bypass_eoln);
+ }
+ if (lua_result == true) {
+ /*tex Fix up the input buffer using callbacks */
+ if (last >= first) {
+ callback_id = callback_defined(process_input_buffer_callback);
+ if (callback_id > 0) {
+ last_ptr = first;
+ lua_result =
+ run_callback(callback_id, "l->l", (last - first),
+ &last_ptr);
+ if ((lua_result == true) && (last_ptr != 0)) {
+ last = last_ptr;
+ if (last > max_buf_stack)
+ max_buf_stack = last;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+/*tex
+
+ 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.)
+
+ 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.
+
+ 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:
+
+ \startitemize[n]
+
+ \startitem
+ It should open file |term_in| for input from the terminal. (The file
+ |term_out| will already be open for output to the terminal.)
+ \stopitem
+
+ \startitem
+ 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.
+ \stopitem
+
+ \startitem
+ 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.
+ \stopitem
+
+ \startitem
+ 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|.
+ \stopitem
+
+ \stopitemize
+
+ 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~`\.{**}'.)
+
+ The following program does the required initialization. Iff anything has been
+ specified on the command line, then |t_open_in| will return with |last >
+ first|.
+
+*/
+
+boolean init_terminal(void)
+{
+ /*tex This gets the terminal input started. */
+ t_open_in();
+ if (last > first) {
+ iloc = first;
+ while ((iloc < last) && (buffer[iloc] == ' '))
+ incr(iloc);
+ if (iloc < last) {
+ return true;
+ }
+ }
+ while (1) {
+ wake_up_terminal();
+ fputs("**", term_out);
+ update_terminal();
+ if (!input_ln(term_in, true)) {
+ /*tex This shouldn't happen. */
+ fputs("\n! End of file on the terminal... why?\n", term_out);
+ return false;
+ }
+ iloc = first;
+ while ((iloc < last) && (buffer[iloc] == ' ')) {
+ incr(iloc);
+ }
+ /*tex Return unless the line was all blank. */
+ if (iloc < last) {
+ return true;
+ }
+ fputs("Please type the name of your input file.\n", term_out);
+ }
+}
+
+
+/*tex
+
+ 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.
+
+*/
+
+void term_input(void)
+{
+ /*tex Index into |buffer|: */
+ int k;
+ /*tex Now the user sees the prompt for sure: */
+ update_terminal();
+ if (!input_ln(term_in, true))
+ fatal_error("End of file on the terminal!");
+ /*tex The user's line ended with \.{<return>}: */
+ term_offset = 0;
+ /*tex Prepare to echo the input. */
+ decr(selector);
+ if (last != first) {
+ for (k = first; k <= last - 1; k++)
+ print_char(buffer[k]);
+ }
+ print_ln();
+ /*tex Restore previous status. */
+ incr(selector);
+}
+
+/*tex
+
+ 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.
+
+ \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.
+
+ Here are the global values that file names will be scanned into.
+
+*/
+
+/*tex name of file just scanned */
+
+str_number cur_name;
+
+/*tex file area just scanned, or \.{""} */
+
+str_number cur_area;
+
+/*tex file extension just scanned, or \.{""} */
+
+str_number cur_ext;
+
+/*tex
+
+ The file names we shall deal with have the following structure: If the name
+ contains `\./' or `\.:' (for Amiga only), 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 last `\..' to the end, otherwise the
+ file extension is null.
+
+ We can scan such file names easily by using two global variables that keep
+ track of the occurrences of area and extension delimiters:
+
+*/
+
+/*tex the most recent `\./', if any */
+
+pool_pointer area_delimiter;
+
+/*tex the relevant `\..', if any */
+
+pool_pointer ext_delimiter;
+
+/*tex
+
+ 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|. $\Omega$'s compiled translation process files whose areas
+ are not given explicitly are assumed to appear in a standard system area.
+ These system area names will, of course, vary from place to place.
+
+*/
+
+#define append_to_fn(A) do { \
+ c=(A); \
+ if (c!='"') { \
+ if (k<file_name_size) fn[k++]=(unsigned char)(c); \
+ } \
+ } while (0)
+
+
+char *pack_file_name(str_number n, str_number a, str_number e)
+{
+ /*tex character being packed */
+ ASCII_code c;
+ /*tex index into |str_pool| */
+ unsigned char *j;
+ /*tex number of positions filled in |fn| */
+ int k = 0;
+ unsigned char *fn = xmallocarray(packed_ASCII_code, str_length(a) + str_length(n) + str_length(e) + 1);
+ for (j = str_string(a); j < str_string(a) + str_length(a); j++)
+ append_to_fn(*j);
+ for (j = str_string(n); j < str_string(n) + str_length(n); j++)
+ append_to_fn(*j);
+ for (j = str_string(e); j < str_string(e) + str_length(e); j++)
+ append_to_fn(*j);
+ fn[k] = 0;
+ return (char *) fn;
+}
+
+/*tex
+
+ A messier routine is also needed, since format file names must be scanned
+ before \TeX's string mechanism has been initialized. We shall use the global
+ variable |TEX_format_default| to supply the text for default system areas and
+ extensions related to format files.
+
+ Under \UNIX\ we don't give the area part, instead depending on the path
+ searching that will happen during file opening. Also, the length will be set
+ in the main program.
+
+*/
+
+char *TEX_format_default;
+
+
+/*tex
+
+ 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..(last-1)]|, where |loc<last| and |buffer[loc]<>" "|.
+
+*/
+
+char *open_fmt_file(void)
+{
+ /*tex The first space after the format file name: */
+ int j;
+ char *fmt = NULL;
+ int dist;
+ j = iloc;
+ if (buffer[iloc] == '&') {
+ incr(iloc);
+ j = iloc;
+ buffer[last] = ' ';
+ while (buffer[j] != ' ')
+ incr(j);
+ fmt = xmalloc((unsigned) (j - iloc + 1));
+ strncpy(fmt, (char *) (buffer + iloc), (size_t) (j - iloc));
+ fmt[j - iloc] = 0;
+ dist = (int) (strlen(fmt) - strlen(DUMP_EXT));
+ if (!(strstr(fmt, DUMP_EXT) == fmt + dist))
+ fmt = concat(fmt, DUMP_EXT);
+ if (zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE))
+ goto FOUND;
+ wake_up_terminal();
+ fprintf(stdout, "Sorry, I can't find the format `%s'; will try `%s'.\n",
+ fmt, TEX_format_default);
+ update_terminal();
+ }
+ /*tex Now pull out all the stops: try for the system \.{plain} file. */
+ fmt = TEX_format_default;
+ if (!zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) {
+ wake_up_terminal();
+ fprintf(stdout, "I can't find the format file `%s'!\n",
+ TEX_format_default);
+ return NULL;
+ }
+ FOUND:
+ iloc = j;
+ return fmt;
+}
+
+/*tex
+
+ 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} 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.
+
+*/
+
+/*tex is a file name being scanned? */
+
+boolean name_in_progress;
+
+/*tex principal file name */
+
+str_number job_name;
+
+/*tex has the transcript file been opened? */
+
+boolean log_opened_global;
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex full name of the log file */
+
+unsigned char *texmf_log_name;
+
+/*tex
+
+ 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.
+
+*/
+
+void open_log_file(void)
+{
+ /*tex previous |selector| setting */
+ int old_setting;
+ /*tex index into |buffer| */
+ int k;
+ /*tex end of first input line */
+ int l;
+ char *fn;
+ old_setting = selector;
+ if (job_name == 0)
+ job_name = getjobname(maketexstring("texput"));
+ fn = pack_job_name(".fls");
+ recorder_change_filename(fn);
+ fn = pack_job_name(".log");
+ while (!lua_a_open_out(&log_file, fn, 0)) {
+ /*tex
+
+ Try to get a different log file name. 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.
+ */
+ selector = term_only;
+ fn = prompt_file_name("transcript file name", ".log");
+ }
+ texmf_log_name = (unsigned char *) xstrdup(fn);
+ selector = log_only;
+ log_opened_global = true;
+ if (callback_defined(start_run_callback) == 0) {
+ /*tex Print the banner line, including current date and time. */
+ log_banner(luatex_version_string);
+ /*tex Make sure bottom level is in memory. */
+ input_stack[input_ptr] = cur_input;
+ tprint_nl("**");
+ /*tex The last position of first line. */
+ l = input_stack[0].limit_field;
+ if (buffer[l] == end_line_char_par) {
+ /*tex maybe also handle multichar endlinechar */
+ decr(l);
+ }
+ for (k = 1; k <= l; k++) {
+ print_char(buffer[k]);
+ }
+ /*tex now the transcript file contains the first line of input */
+ print_ln();
+ }
+ /*tex should be done always */
+ flush_loggable_info();
+ /*tex should be done always */
+ selector = old_setting + 2;
+}
+
+/*tex
+
+ This function is needed by synctex to make its log appear in the right spot
+ when |output_directory| is set.
+
+*/
+
+char *get_full_log_name (void)
+{
+ if (output_directory) {
+ char *ret = xmalloc(strlen((char *)texmf_log_name)+2+strlen(output_directory));
+ ret = strcpy(ret, output_directory);
+ strcat(ret, "/");
+ strcat(ret, (char *)texmf_log_name);
+ return ret;
+ } else {
+ return xstrdup((const char*)texmf_log_name);
+ }
+}
+
+/*tex Synctex uses this to get the anchored path of an input file. */
+
+char *luatex_synctex_get_current_name (void)
+{
+ char *pwdbuf = NULL, *ret;
+ if (kpse_absolute_p(fullnameoffile, false)) {
+ return xstrdup(fullnameoffile);
+ }
+ pwdbuf = xgetcwd();
+ ret = concat3(pwdbuf, DIR_SEP_STRING, fullnameoffile);
+ free(pwdbuf) ;
+ return ret;
+}
+
+/*tex
+
+ Let's turn now to the procedure that is used to initiate file reading when an
+ `\.{\\input}' command is being processed.
+
+*/
+
+void start_input(void)
+{
+ str_number temp_str;
+ char *fn;
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+ back_input();
+ if (cur_cmd != left_brace_cmd) {
+ /*tex Set |cur_name| to desired file name. */
+ scan_file_name();
+ } else {
+ scan_file_name_toks();
+ }
+ fn = pack_file_name(cur_name, cur_area, cur_ext);
+ while (1) {
+ /*tex Set up |cur_file| and new level of input. */
+ begin_file_reading();
+ if (lua_a_open_in(&cur_file, fn, 0)) {
+ break;
+ }
+ /*tex Remove the level that didn't work. */
+ end_file_reading();
+ fn = prompt_file_name("input file name", "");
+ }
+ iname = maketexstring(fullnameoffile);
+ /*tex
+
+ Now that we have |fullnameoffile|, it is time to post-adjust |cur_name|
+ and |cur_ext| for trailing |.tex|.
+
+ */
+ {
+ char *n, *p;
+ n = p = fullnameoffile + strlen(fullnameoffile);
+ while (p>fullnameoffile) {
+ p--;
+ if (IS_DIR_SEP(*p)) {
+ break;
+ }
+ }
+ if (IS_DIR_SEP(*p)) {
+ p++;
+ }
+ while (n>fullnameoffile) {
+ n--;
+ if (*n == '.') {
+ break;
+ }
+ }
+ if (n>p) {
+ int q = *n;
+ cur_ext = maketexstring(n);
+ *n = 0;
+ cur_name = maketexstring(p);
+ *n = q;
+ }
+ }
+ source_filename_stack[in_open] = iname;
+ full_source_filename_stack[in_open] = xstrdup(fullnameoffile);
+ /*tex We can try to conserve string pool space now. */
+ temp_str = search_string(iname);
+ if (temp_str > 0) {
+ flush_str(iname);
+ iname = temp_str;
+ }
+ if (job_name == 0) {
+ job_name = getjobname(cur_name);
+ open_log_file();
+ }
+ /*tex
+
+ |open_log_file| doesn't |show_context|, so |limit| and |loc| needn't be
+ set to meaningful values yet.
+
+ */
+ report_start_file(filetype_tex,fullnameoffile);
+ incr(open_parens);
+ update_terminal();
+ istate = new_line;
+ /*tex Prepare new file {\sl Sync\TeX} information. */
+ if (! synctex_get_no_files()) {
+ /*tex Give control to the {\sl Sync\TeX} controller. */
+ synctexstartinput();
+ }
+ /*tex
+
+ Read the first line of the new file. Here we have to remember to tell the
+ |lua_input_ln| routine not to start with a |get|. If the file is empty,
+ it is considered to contain a single blank line.
+
+ */
+ line = 1;
+ if (lua_input_ln(cur_file, 0, false)) {
+ ;
+ }
+ firm_up_the_line();
+ if (end_line_char_inactive)
+ decr(ilimit);
+ else
+ buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+ first = ilimit + 1;
+ iloc = istart;
+}
+
+/*tex
+
+ Because the format is zipped we read and write dump files through zlib.
+ Earlier versions recast |*f| from |FILE *| to |gzFile|, but there is no
+ guarantee that these have the same size, so a static variable is needed.
+
+*/
+
+static gzFile gz_fmtfile = NULL;
+
+/*tex
+
+ As distributed, the dump files are architecture dependent; specifically,
+ BigEndian and LittleEndian architectures produce different files. These
+ routines always output BigEndian files. This still does not guarantee them to
+ be architecture-independent, because it is possible to make a format that
+ dumps a glue ratio, i.e., a floating-point number. Fortunately, none of the
+ standard formats do that.
+
+*/
+
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+
+/*tex
+
+ This macro is always invoked as a statement. It assumes a variable `temp'.
+
+*/
+
+# define SWAP(x, y) do { temp = x; x = y; y = temp; } while (0)
+
+/*tex
+
+ Make the NITEMS items pointed at by P, each of size SIZE, be the
+ opposite-endianness of whatever they are now.
+
+*/
+
+static void swap_items(char *pp, int nitems, int size)
+{
+ char temp;
+ unsigned total = (unsigned) (nitems * size);
+ char *q = xmalloc(total);
+ char *p = q;
+ memcpy(p,pp,total);
+ /*tex
+
+ Since `size' does not change, we can write a while loop for each case,
+ and avoid testing `size' for each time.
+
+ */
+ switch (size) {
+ case 16:
+ /*tex
+
+ 16-byte items happen on the DEC Alpha machine when we are not doing
+ sharable memory dumps.
+
+ */
+ while (nitems--) {
+ SWAP(p[0], p[15]);
+ SWAP(p[1], p[14]);
+ SWAP(p[2], p[13]);
+ SWAP(p[3], p[12]);
+ SWAP(p[4], p[11]);
+ SWAP(p[5], p[10]);
+ SWAP(p[6], p[9]);
+ SWAP(p[7], p[8]);
+ p += size;
+ }
+ break;
+
+ case 12:
+ while (nitems--) {
+ SWAP(p[0], p[11]);
+ SWAP(p[1], p[10]);
+ SWAP(p[2], p[9]);
+ SWAP(p[3], p[8]);
+ SWAP(p[4], p[7]);
+ SWAP(p[5], p[6]);
+ p += size;
+ }
+ break;
+
+ case 8:
+ while (nitems--) {
+ SWAP(p[0], p[7]);
+ SWAP(p[1], p[6]);
+ SWAP(p[2], p[5]);
+ SWAP(p[3], p[4]);
+ p += size;
+ }
+ break;
+
+ case 4:
+ while (nitems--) {
+ SWAP(p[0], p[3]);
+ SWAP(p[1], p[2]);
+ p += size;
+ }
+ break;
+
+ case 2:
+ while (nitems--) {
+ SWAP(p[0], p[1]);
+ p += size;
+ }
+ break;
+ case 1:
+ /*tex Nothing to do. */
+ break;
+ default:
+ FATAL1("Can't swap a %d-byte item for (un)dumping", size);
+ }
+ memcpy(pp,q,total);
+ xfree(q);
+}
+#endif
+
+/*tex
+
+ That second swap is to make sure following calls don't get confused in the
+ case of |dump_things|.
+
+*/
+
+void do_zdump(char *p, int item_size, int nitems, FILE * out_file)
+{
+ int err;
+ (void) out_file;
+ if (nitems == 0)
+ return;
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+ swap_items(p, nitems, item_size);
+#endif
+ if (gzwrite(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) !=
+ item_size * nitems) {
+ fprintf(stderr, "! Could not write %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err));
+ uexit(1);
+ }
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+ swap_items(p, nitems, item_size);
+#endif
+}
+
+void do_zundump(char *p, int item_size, int nitems, FILE * in_file)
+{
+ int err;
+ (void) in_file;
+ if (nitems == 0)
+ return;
+ if (gzread(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) <= 0) {
+ fprintf(stderr, "Could not undump %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err));
+ uexit(1);
+ }
+#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
+ swap_items(p, nitems, item_size);
+#endif
+}
+
+/*tex
+
+ Tests has shown that a level 3 compression is the most optimal tradeoff
+ between file size and load time.
+
+*/
+
+#define COMPRESSION "R3"
+
+boolean zopen_w_input(FILE ** f, const char *fname, int format, const_string fopen_mode)
+{
+ int callbackid;
+ int res;
+ char *fnam;
+ callbackid = callback_defined(find_format_file_callback);
+ if (callbackid > 0) {
+ res = run_callback(callbackid, "S->R", fname, &fnam);
+ if (res && fnam && strlen(fnam) > 0) {
+ *f = fopen(fnam, fopen_mode);
+ if (*f == NULL) {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ } else {
+ res = luatex_open_input(f, fname, format, fopen_mode, true);
+ }
+ if (res) {
+ gz_fmtfile = gzdopen(fileno(*f), "rb" COMPRESSION);
+ }
+ return res;
+}
+
+boolean zopen_w_output(FILE ** f, const char *s, const_string fopen_mode)
+{
+ int res = 1;
+ if (luainit) {
+ *f = fopen(s, fopen_mode);
+ if (*f == NULL) {
+ return 0;
+ }
+ } else {
+ res = luatex_open_output(f, s, fopen_mode);
+ }
+ if (res) {
+ gz_fmtfile = gzdopen(fileno(*f), "wb" COMPRESSION);
+ }
+ return res;
+}
+
+void zwclose(FILE * f)
+{
+ (void) f;
+ gzclose(gz_fmtfile);
+}
+
+/*tex Create the \DVI\ or \PDF\ file. */
+
+int open_outfile(FILE ** f, const char *name, const char *mode)
+{
+ FILE *res;
+ res = fopen(name, mode);
+ if (res != NULL) {
+ *f = res;
+ return 1;
+ }
+ return 0;
+}
+
+/*tex The caller should set |tfm_buffer=NULL| and |tfm_size=0|. */
+
+int readbinfile(FILE * f, unsigned char **tfm_buffer, int *tfm_size)
+{
+ void *buf;
+ int size;
+ if (fseek(f, 0, SEEK_END) == 0) {
+ size = (int) ftell(f);
+ if (size > 0) {
+ buf = xmalloc((unsigned) size);
+ if (fseek(f, 0, SEEK_SET) == 0) {
+ if (fread((void *) buf, (size_t) size, 1, f) == 1) {
+ *tfm_buffer = (unsigned char *) buf;
+ *tfm_size = size;
+ return 1;
+ }
+ }
+ } else {
+ *tfm_buffer = NULL;
+ *tfm_size = 0;
+ return 1;
+ }
+ }
+ /*tex Either seek failed or we have a zero-sized file. */
+ return 0;
+}
+
+/*tex
+
+ Like |os.execute()|, the |runpopen()| function is called only when
+ |shellenabledp == 1|. Unlike |os.execute()| we write errors to stderr, since
+ we have nowhere better to use; and of course we return a file handle (or
+ NULL) instead of a status indicator.
+
+*/
+
+static FILE *runpopen(char *cmd, const char *mode)
+{
+ FILE *f = NULL;
+ char *safecmd = NULL;
+ char *cmdname = NULL;
+ int allow;
+#ifdef WIN32
+ char *pp;
+
+ for (pp = cmd; *pp; pp++) {
+ if (*pp == '\'') *pp = '"';
+ }
+#endif
+ /*tex If |restrictedshell| is zero, any command is allowed. */
+ if (restrictedshell == 0) {
+ allow = 1;
+ } else {
+ const char *thecmd = cmd;
+ allow = shell_cmd_is_allowed(thecmd, &safecmd, &cmdname);
+ }
+ if (allow == 1)
+ f = popen(cmd, mode);
+ else if (allow == 2)
+ f = popen(safecmd, mode);
+ else if (allow == -1)
+ fprintf(stderr, "\nrunpopen quotation error in command line: %s\n", cmd);
+ else
+ fprintf(stderr, "\nrunpopen command not allowed: %s\n", cmdname);
+ if (safecmd)
+ free(safecmd);
+ if (cmdname)
+ free(cmdname);
+ return f;
+}
+
+/*tex
+
+ The code that implements |popen()| needs an array for tracking possible pipe
+ file pointers, because these need to be closed using |pclose()|.
+
+*/
+
+#define NUM_PIPES 16
+static FILE *pipes[NUM_PIPES];
+
+#ifdef WIN32
+FILE *Poptr;
+#endif
+
+boolean open_in_or_pipe(FILE ** f_ptr, char *fn, int filefmt, const_string fopen_mode, boolean must_exist)
+{
+ string fname = NULL;
+ int i;
+ /*tex
+
+ Opening a read pipe is straightforward, only have to skip past the pipe
+ symbol in the file name. filename quoting is assumed to happen elsewhere
+ (it does :-))
+
+ */
+ if (shellenabledp && *fn == '|') {
+ /*tex The user requested a pipe. */
+ *f_ptr = NULL;
+ fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
+ strcpy(fname, fn);
+ if (fullnameoffile)
+ free(fullnameoffile);
+ fullnameoffile = xstrdup(fname);
+ recorder_record_input(fname + 1);
+ *f_ptr = runpopen(fname + 1, "r");
+ free(fname);
+ for (i = 0; i < NUM_PIPES; i++) {
+ if (pipes[i] == NULL) {
+ pipes[i] = *f_ptr;
+ break;
+ }
+ }
+ if (*f_ptr)
+ setvbuf(*f_ptr, (char *) NULL, _IONBF, 0);
+#ifdef WIN32
+ Poptr = *f_ptr;
+#endif
+ return *f_ptr != NULL;
+ }
+ return luatex_open_input(f_ptr, fn, filefmt, fopen_mode, must_exist);
+}
+
+
+boolean open_out_or_pipe(FILE ** f_ptr, char *fn, const_string fopen_mode)
+{
+ string fname;
+ int i;
+ /*tex
+
+ Opening a write pipe takes a little bit more work, because TeX will
+ perhaps have appended ".tex". To avoid user confusion as much as
+ possible, this extension is stripped only when the command is a bare
+ word. Some small string trickery is needed to make sure the correct
+ number of bytes is free()-d afterwards.
+ */
+ if (shellenabledp && *fn == '|') {
+ /*tex The user requested a pipe. */
+ fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
+ strcpy(fname, fn);
+ if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) {
+ /*tex
+
+ \METAPOST\ and \METAFIONT\ currently do not use this code, but it
+ is better to be prepared. Hm, what has this todo with \LUATEX ?
+
+ */
+ if (STREQ((fname + strlen(fname) - 3), "tex"))
+ *(fname + strlen(fname) - 4) = 0;
+ *f_ptr = runpopen(fname + 1, "w");
+ *(fname + strlen(fname)) = '.';
+ } else {
+ *f_ptr = runpopen(fname + 1, "w");
+ }
+ recorder_record_output(fname + 1);
+ free(fname);
+ for (i = 0; i < NUM_PIPES; i++) {
+ if (pipes[i] == NULL) {
+ pipes[i] = *f_ptr;
+ break;
+ }
+ }
+ if (*f_ptr)
+ setvbuf(*f_ptr, (char *) NULL, _IONBF, 0);
+ return *f_ptr != NULL;
+ }
+ return luatex_open_output(f_ptr, fn, fopen_mode);
+}
+
+
+void close_file_or_pipe(FILE * f)
+{
+ int i;
+ if (shellenabledp) {
+ for (i = 0; i <= 15; i++) {
+ /*tex If this file was a pipe, |pclose()| it and return. */
+ if (pipes[i] == f) {
+ if (f) {
+ pclose(f);
+#ifdef WIN32
+ Poptr = NULL;
+#endif
+ }
+ pipes[i] = NULL;
+ return;
+ }
+ }
+ }
+ close_file(f);
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.h b/Build/source/texk/web2c/luatexdir/tex/texfileio.h
new file mode 100644
index 00000000000..c6366500766
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texfileio.h
@@ -0,0 +1,145 @@
+/* texfileio.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef TEXFILEIO_H
+# define TEXFILEIO_H
+
+extern int *input_file_callback_id;
+extern int read_file_callback_id[17];
+
+extern char *luatex_find_read_file(const char *s, int n, int callback_index);
+extern boolean luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt,
+ const_string fopen_mode, boolean must_exist);
+extern boolean luatex_open_output(FILE ** f_ptr, const char *fn,
+ const_string fopen_mode);
+
+extern boolean lua_a_open_in(alpha_file * f, char *fn, int n);
+extern boolean lua_a_open_out(alpha_file * f, char *fn, int n);
+extern boolean lua_b_open_out(alpha_file * f, char *fn);
+extern void lua_a_close_in(alpha_file f, int n);
+extern void lua_a_close_out(alpha_file f);
+
+extern packed_ASCII_code *buffer;
+extern int first;
+extern int last;
+extern int max_buf_stack;
+
+extern boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln);
+
+/*
+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@>
+*/
+
+# define term_in stdin /* the terminal as an input file */
+# define term_out stdout /* the terminal as an output file */
+
+
+/*
+Here is how to open the terminal files. |t_open_out| does nothing.
+|t_open_in|, on the other hand, does the work of ``rescanning,'' or getting
+any command line arguments the user has provided. It's defined in C.
+*/
+
+# define t_open_out() /* output already open 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 with {\mc UNIX}. |update_terminal|
+does an |fflush|. |clear_terminal| is redefined
+to do nothing, since the user should control the terminal.
+@^system dependencies@>
+*/
+
+# define update_terminal() fflush (term_out)
+# define clear_terminal() do { ; } while (0)
+# define wake_up_terminal() do { ; } while (0) /* cancel the user's cancellation of output */
+
+extern boolean init_terminal(void);
+extern void term_input(void);
+
+extern str_number cur_name;
+extern str_number cur_area;
+extern str_number cur_ext;
+extern pool_pointer area_delimiter;
+extern pool_pointer ext_delimiter;
+
+extern char *pack_file_name(str_number n, str_number a, str_number e);
+
+# define file_name_size 512
+
+# define format_area_length 0 /* length of its area part */
+# define format_ext_length 4 /* length of its `\.{.fmt}' part */
+# define format_extension ".fmt"
+ /* the extension, as a constant */
+
+extern char *TEX_format_default;
+
+extern char *open_fmt_file(void);
+
+extern boolean name_in_progress; /* is a file name being scanned? */
+extern str_number job_name; /* principal file name */
+extern boolean log_opened_global; /* has the transcript file been opened? */
+
+extern unsigned char *texmf_log_name; /* full name of the log file */
+
+extern void open_log_file(void);
+extern char *get_full_log_name (void);
+extern void start_input(void);
+
+extern int open_outfile(FILE ** f, const char *name, const char *mode);
+
+extern boolean zopen_w_input(FILE **, const char *, int,
+ const_string fopen_mode);
+extern boolean zopen_w_output(FILE **, const char *, const_string fopen_mode);
+extern void zwclose(FILE *);
+
+# define read_tfm_file readbinfile
+# define read_vf_file readbinfile
+# define read_data_file readbinfile
+
+extern boolean openinnameok(const_string);
+extern boolean openoutnameok(const_string);
+
+# ifdef WIN32
+extern FILE *Poptr;
+# endif
+
+extern boolean open_in_or_pipe(FILE **, char *, int, const_string fopen_mode,
+ boolean must_exist);
+extern boolean open_out_or_pipe(FILE **, char *, const_string fopen_mode);
+extern void close_file_or_pipe(FILE *);
+
+extern char *luatex_synctex_get_current_name(void);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.c b/Build/source/texk/web2c/luatexdir/tex/texmath.c
new file mode 100644
index 00000000000..4d2bf80ecd6
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texmath.c
@@ -0,0 +1,2727 @@
+/*
+
+Copyright 2008-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation;
+either version 2 of the License, or (at your option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+#define mode mode_par
+#define tail tail_par
+#define head head_par
+#define dir_save dirs_par
+
+/*tex
+
+ Concerning display skips, \TEX\ normally always inserts before and only after
+ when larger than zero. THis can ow be controlled with |\mathdisplayskipmode|:
+
+ \starttabulate
+ \NC 0 \NC normal \TEX \NC \NR
+ \NC 1 \NC always \NC \NR
+ \NC 2 \NC non-zero \NC \NR
+ \NC 3 \NC ignore \NC \NR
+ \stoptabulate
+
+*/
+
+#define back_error(A,B) do { \
+ OK_to_interrupt=false; \
+ back_input(); \
+ OK_to_interrupt=true; \
+ tex_error(A,B); \
+ } while (0)
+
+int scan_math(pointer, int);
+int scan_math_style(pointer, int);
+pointer fin_mlist(pointer);
+
+/*tex
+
+ 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 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 five or more words long. The first word contains the |type| and
+ |subtype| and |link| fields that are already so familiar to us; the second
+ contains the attribute list pointer, and the third, fourth an fifth words are
+ called the noad's |nucleus|, |subscr|, and |supscr| fields. (This use of a
+ combined attribute list is temporary. Eventually, each of fields need their
+ own list)
+
+ 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)|), |q=null| indicates a field with no value (the
+ corresponding attribute of noad |p| is not present). Otherwise, there are
+ several possibilities for the subfields, depending on the |type| of |q|.
+
+ \startitemize
+
+ \startitem
+ |type(q)=math_char_node| means that |math_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.
+ \stopitem
+
+ \startitem
+ |type(q)=math_text_char_node| is similar, but the character is
+ unsubscripted and unsuperscripted and it is followed immediately by
+ another character from the same font. (This |type| setting appears
+ only briefly during the processing; it is used to suppress unwanted
+ italic corrections.)
+ \stopitem
+
+ \startitem
+ |type(q)=sub_box_node| means that |math_list(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.
+ \stopitem
+
+ \startitem
+ |type(q)=sub_mlist_node| means that |math_list(q)| points to an
+ mlist; the mlist must be converted to an hlist in order to obtain the
+ value of this field.
+ \stopitem
+
+ \startitem
+ In the latter case, we might have |math_list(q)=null|. This is not
+ the same as |q=null|; 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).
+ \stopitem
+
+ \startitemize
+
+*/
+
+static void unsave_math(void)
+{
+ unsave();
+ decr(save_ptr);
+ flush_node_list(text_dir_ptr);
+ assert(saved_type(0) == saved_textdir);
+ text_dir_ptr = saved_value(0);
+}
+
+/*tex
+
+ Sometimes it is necessary to destroy an mlist. The following subroutine
+ empties the current list, assuming that |abs(mode)=mmode|.
+
+*/
+
+void flush_math(void)
+{
+ flush_node_list(vlink(head));
+ flush_node_list(incompleat_noad_par);
+ vlink(head) = null;
+ tail = head;
+ incompleat_noad_par = null;
+}
+
+/*tex Before we can do anything in math mode, we need fonts. */
+
+#define MATHFONTSTACK 8
+#define MATHFONTDEFAULT 0
+
+static sa_tree math_fam_head = NULL;
+
+int fam_fnt(int fam_id, int size_id)
+{
+ int n = fam_id + (256 * size_id);
+ return (int) get_sa_item(math_fam_head, n).int_value;
+}
+
+void def_fam_fnt(int fam_id, int size_id, int f, int lvl)
+{
+ int n = fam_id + (256 * size_id);
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = f;
+ set_sa_item(math_fam_head, n, sa_value, lvl);
+ fixup_math_parameters(fam_id, size_id, f, lvl);
+ if (tracing_assigns_par > 1) {
+ begin_diagnostic();
+ tprint("{assigning");
+ print_char(' ');
+ print_cmd_chr(def_family_cmd, size_id);
+ print_int(fam_id);
+ print_char('=');
+ print_font_identifier(fam_fnt(fam_id, size_id));
+ print_char('}');
+ end_diagnostic(false);
+ }
+}
+
+static void unsave_math_fam_data(int gl)
+{
+ sa_stack_item st;
+ if (math_fam_head->stack == NULL)
+ return;
+ while (math_fam_head->stack_ptr > 0 &&
+ abs(math_fam_head->stack[math_fam_head->stack_ptr].level)
+ >= (int) gl) {
+ st = math_fam_head->stack[math_fam_head->stack_ptr];
+ if (st.level > 0) {
+ rawset_sa_item(math_fam_head, st.code, st.value);
+ /*tex Now do a trace message, if requested. */
+ if (tracing_restores_par > 1) {
+ int size_id = st.code / 256;
+ int fam_id = st.code % 256;
+ begin_diagnostic();
+ tprint("{restoring");
+ print_char(' ');
+ print_cmd_chr(def_family_cmd, size_id);
+ print_int(fam_id);
+ print_char('=');
+ print_font_identifier(fam_fnt(fam_id, size_id));
+ print_char('}');
+ end_diagnostic(false);
+ }
+ }
+ (math_fam_head->stack_ptr)--;
+ }
+}
+
+/*tex Parameters */
+
+#define MATHPARAMSTACK 8
+#define MATHPARAMDEFAULT undefined_math_parameter
+
+static sa_tree math_param_head = NULL;
+
+void def_math_param(int param_id, int style_id, scaled value, int lvl)
+{
+ int n = param_id + (256 * style_id);
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = (int) value;
+ set_sa_item(math_param_head, n, sa_value, lvl);
+ if (tracing_assigns_par > 1) {
+ begin_diagnostic();
+ tprint("{assigning");
+ print_char(' ');
+ print_cmd_chr(set_math_param_cmd, param_id);
+ print_cmd_chr(math_style_cmd, style_id);
+ print_char('=');
+ print_int(value);
+ print_char('}');
+ end_diagnostic(false);
+ }
+}
+
+scaled get_math_param(int param_id, int style_id)
+{
+ int n = param_id + (256 * style_id);
+ return (scaled) get_sa_item(math_param_head, n).int_value;
+}
+
+static void unsave_math_param_data(int gl)
+{
+ sa_stack_item st;
+ if (math_param_head->stack == NULL)
+ return;
+ while (math_param_head->stack_ptr > 0 &&
+ abs(math_param_head->stack[math_param_head->stack_ptr].level)
+ >= (int) gl) {
+ st = math_param_head->stack[math_param_head->stack_ptr];
+ if (st.level > 0) {
+ rawset_sa_item(math_param_head, st.code, st.value);
+ /*tex Do a trace message, if requested. */
+ if (tracing_restores_par > 1) {
+ int param_id = st.code % 256;
+ int style_id = st.code / 256;
+ begin_diagnostic();
+ tprint("{restoring");
+ print_char(' ');
+ print_cmd_chr(set_math_param_cmd, param_id);
+ print_cmd_chr(math_style_cmd, style_id);
+ print_char('=');
+ print_int(get_math_param(param_id, style_id));
+ print_char('}');
+ end_diagnostic(false);
+ }
+ }
+ (math_param_head->stack_ptr)--;
+ }
+}
+
+/*tex Saving and unsaving of both: */
+
+void unsave_math_data(int gl)
+{
+ unsave_math_fam_data(gl);
+ unsave_math_param_data(gl);
+}
+
+/*tex Dumping and undumping: */
+
+void dump_math_data(void)
+{
+ sa_tree_item sa_value = { 0 };
+ if (math_fam_head == NULL) {
+ sa_value.int_value = MATHFONTDEFAULT;
+ math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value);
+ }
+ dump_sa_tree(math_fam_head, "mathfonts");
+ if (math_param_head == NULL) {
+ sa_value.int_value = MATHPARAMDEFAULT;
+ math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value);
+ }
+ dump_sa_tree(math_param_head, "mathparameters");
+}
+
+void undump_math_data(void)
+{
+ math_fam_head = undump_sa_tree("mathfonts");
+ math_param_head = undump_sa_tree("mathparameters");
+}
+
+void initialize_math(void)
+{
+ sa_tree_item sa_value = { 0 };
+ if (math_fam_head == NULL) {
+ sa_value.int_value = MATHFONTDEFAULT;
+ math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value);
+ }
+ if (math_param_head == NULL) {
+ sa_value.int_value = MATHPARAMDEFAULT;
+ math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value);
+ initialize_math_spacing();
+ }
+ return;
+}
+
+/*tex
+
+ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, Clo, Pun,
+ or Inn, for purposes of spacing and line breaking. An |ord_noad|, |op_noad|,
+ |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, |punct_noad|, or
+ |inner_noad| is used to represent portions of the various types. For example,
+ an `\.=' sign in a formula leads to the creation of a |rel_noad| whose
+ |nucleus| field is a representation of an equals sign (usually |fam=0|,
+ |character=075|). A formula preceded by \.{\\mathrel} also results in a
+ |rel_noad|. When a |rel_noad| is followed by an |op_noad|, say, and possibly
+ separated by one or more ordinary nodes (not noads), \TeX\ will insert a
+ penalty node (with the current |rel_penalty|) just after the formula that
+ corresponds to the |rel_noad|, unless there already was a penalty immediately
+ following; and a ``thick space'' will be inserted just before the formula
+ that corresponds to the |op_noad|.
+
+ A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually has a
+ |subtype=normal|. The only exception is that an |op_noad| might have
+ |subtype=limits| or |no_limits|, if the normal positioning of limits has been
+ overridden for this operator.
+
+ A |radical_noad| also has a |left_delimiter| field, which usually represents
+ a square root sign.
+
+ A |fraction_noad| has a |right_delimiter| field as well as a
+ |left_delimiter|.
+
+ Delimiter fields 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}.
+
+ A |fraction_noad| is actually quite different from all other noads. 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{|type(numerator)=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}.
+
+ The |new_noad| function creates an |ord_noad| that is completely null
+
+*/
+
+pointer new_noad(void)
+{
+ pointer p;
+ p = new_node(simple_noad, ord_noad_type);
+ /*tex All noad fields are zero after this. */
+ return p;
+}
+
+pointer new_sub_box(pointer curbox)
+{
+ pointer p, q;
+ p = new_noad();
+ q = new_node(sub_box_node, 0);
+ nucleus(p) = q;
+ math_list(nucleus(p)) = curbox;
+ return p;
+}
+
+/*tex
+
+ 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
+ |math_fam(accent_chr(p))| and |math_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 |type(nucleus(p))=sub_box|.
+
+ And finally, we have the |fence_noad| type, 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 |fence_noad| such that |delimiter(p)| holds the family and character codes
+ for all left parentheses. A |fence_noad| of subtype |left_noad_side| never
+ appears in an mlist except as the first element, and a |fence_noad| with
+ subtype |right_noad_side| never appears in an mlist except as the last
+ element; furthermore, we either have both a |left_noad_side| and a
+ |right_noad_side|, or neither one is present.
+
+ 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.
+
+*/
+
+const char *math_style_names[] = {
+ "display", "crampeddisplay",
+ "text", "crampedtext",
+ "script", "crampedscript",
+ "scriptscript", "crampedscriptscript",
+ NULL
+};
+
+const char *math_param_names[] = {
+ "quad", "axis", "operatorsize",
+ "overbarkern", "overbarrule", "overbarvgap",
+ "underbarkern", "underbarrule", "underbarvgap",
+ "radicalkern", "radicalrule", "radicalvgap",
+ "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
+ "stackvgap", "stacknumup", "stackdenomdown",
+ "fractionrule", "fractionnumvgap", "fractionnumup",
+ "fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
+ "skewedfractionhgap", "skewedfractionvgap",
+ "limitabovevgap", "limitabovebgap", "limitabovekern",
+ "limitbelowvgap", "limitbelowbgap", "limitbelowkern",
+ "nolimitsubfactor", "nolimitsupfactor", /* bonus */
+ "underdelimitervgap", "underdelimiterbgap",
+ "overdelimitervgap", "overdelimiterbgap",
+ "subshiftdrop", "supshiftdrop", "subshiftdown",
+ "subsupshiftdown", "subtopmax", "supshiftup",
+ "supbottommin", "supsubbottommax", "subsupvgap",
+ "spaceafterscript", "connectoroverlapmin",
+ "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing",
+ "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing",
+ "opordspacing", "opopspacing", "opbinspacing", "oprelspacing",
+ "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing",
+ "binordspacing", "binopspacing", "binbinspacing", "binrelspacing",
+ "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing",
+ "relordspacing", "relopspacing", "relbinspacing", "relrelspacing",
+ "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing",
+ "openordspacing", "openopspacing", "openbinspacing", "openrelspacing",
+ "openopenspacing", "openclosespacing", "openpunctspacing",
+ "openinnerspacing",
+ "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
+ "closeopenspacing", "closeclosespacing", "closepunctspacing",
+ "closeinnerspacing",
+ "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
+ "punctopenspacing", "punctclosespacing", "punctpunctspacing",
+ "punctinnerspacing",
+ "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
+ "inneropenspacing", "innerclosespacing", "innerpunctspacing",
+ "innerinnerspacing",
+ NULL
+};
+
+pointer new_style(small_number s)
+{
+ m_style = s;
+ return new_node(style_node, s);
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static pointer new_choice(void)
+{
+ return new_node(choice_node, 0);
+}
+
+/*tex
+
+ 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\}\}\}\}\$}'.
+
+*/
+
+void display_normal_noad(pointer p);
+void display_fence_noad(pointer p);
+void display_fraction_noad(pointer p);
+
+void show_math_node(pointer p)
+{
+ switch (type(p)) {
+ case style_node:
+ print_cmd_chr(math_style_cmd, subtype(p));
+ break;
+ case choice_node:
+ tprint_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();
+ break;
+ case simple_noad:
+ case radical_noad:
+ case accent_noad:
+ display_normal_noad(p);
+ break;
+ case fence_noad:
+ display_fence_noad(p);
+ break;
+ case fraction_noad:
+ display_fraction_noad(p);
+ break;
+ default:
+ tprint("Unknown node type!");
+ break;
+ }
+}
+
+/*tex Here are some simple routines used in the display of noads. */
+
+static void print_fam_and_char(pointer p)
+{
+ tprint_esc("fam");
+ print_int(math_fam(p));
+ print_char(' ');
+ print(math_character(p));
+}
+
+static void print_delimiter(pointer p)
+{
+ int a;
+ if (delimiteroptionset(p)) {
+ tprint(" [ ");
+ if (delimiteraxis(p))
+ tprint("axis ");
+ if (delimiternoaxis(p))
+ tprint("noaxis ");
+ if (delimiterexact(p))
+ tprint("exact ");
+ tprint("]");
+ }
+ if (delimiterheight(p)) {
+ tprint("height=");
+ print_scaled(delimiterheight(p));
+ tprint(" ");
+ }
+ if (delimiterdepth(p)) {
+ tprint("depth=");
+ print_scaled(delimiterdepth(p));
+ tprint(" ");
+ }
+ if (delimiterclass(p)) {
+ tprint("class=");
+ print_int(delimiterclass(p));
+ tprint(" ");
+ }
+ if (small_fam(p) < 0) {
+ /*tex This should never happen. */
+ print_int(-1);
+ } else if (small_fam(p) < 16 && large_fam(p) < 16 && small_char(p) < 256 && large_char(p) < 256) {
+ /*tex Traditional tex style. */
+ a = small_fam(p) * 256 + small_char(p);
+ a = a * 0x1000 + large_fam(p) * 256 + large_char(p);
+ print_qhex(a);
+ } else if ((large_fam(p) == 0 && large_char(p) == 0) || small_char(p) > 65535 || large_char(p) > 65535) {
+ /*tex \LUATEX\ style. */
+ print_qhex(small_fam(p));
+ print_qhex(small_char(p));
+ }
+}
+
+/*tex
+
+ 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 missing field, because these are not equivalent (as
+ explained above).
+
+*/
+
+static void print_subsidiary_data(pointer p, ASCII_code c)
+{
+ if ((int) cur_length >= depth_threshold) {
+ if (p != null)
+ tprint(" []");
+ } else {
+ /*tex Include |c| in the recursion history. */
+ append_char(c);
+ if (p != null) {
+ switch (type(p)) {
+ case math_char_node:
+ print_ln();
+ print_current_string();
+ print_fam_and_char(p);
+ break;
+ case sub_box_node:
+ show_node_list(math_list(p));
+ break;
+ case sub_mlist_node:
+ if (math_list(p) == null) {
+ print_ln();
+ print_current_string();
+ tprint("{}");
+ } else {
+ show_node_list(math_list(p));
+ }
+ break;
+ }
+ }
+ /*tex Remove |c| from the recursion history. */
+ flush_char();
+ }
+}
+
+void display_normal_noad(pointer p)
+{
+ switch (type(p)) {
+ case simple_noad:
+ switch (subtype(p)) {
+ case ord_noad_type:
+ tprint_esc("mathord");
+ break;
+ case op_noad_type_normal:
+ case op_noad_type_limits:
+ case op_noad_type_no_limits:
+ tprint_esc("mathop");
+ if (subtype(p) == op_noad_type_limits)
+ tprint_esc("limits");
+ else if (subtype(p) == op_noad_type_no_limits)
+ tprint_esc("nolimits");
+ break;
+ case bin_noad_type:
+ tprint_esc("mathbin");
+ break;
+ case rel_noad_type:
+ tprint_esc("mathrel");
+ break;
+ case open_noad_type:
+ tprint_esc("mathopen");
+ break;
+ case close_noad_type:
+ tprint_esc("mathclose");
+ break;
+ case punct_noad_type:
+ tprint_esc("mathpunct");
+ break;
+ case inner_noad_type:
+ tprint_esc("mathinner");
+ break;
+ case over_noad_type:
+ tprint_esc("overline");
+ break;
+ case under_noad_type:
+ tprint_esc("underline");
+ break;
+ case vcenter_noad_type:
+ tprint_esc("vcenter");
+ break;
+ default:
+ tprint("<unknown noad type!>");
+ break;
+ }
+ break;
+ case radical_noad:
+ if (subtype(p) == 7)
+ tprint_esc("Uhextensible");
+ else if (subtype(p) == 6)
+ tprint_esc("Udelimiterover");
+ else if (subtype(p) == 5)
+ tprint_esc("Udelimiterunder");
+ else if (subtype(p) == 4)
+ tprint_esc("Uoverdelimiter");
+ else if (subtype(p) == 3)
+ tprint_esc("Uunderdelimiter");
+ else if (subtype(p) == 2)
+ tprint_esc("Uroot");
+ else
+ tprint_esc("radical");
+ print_delimiter(left_delimiter(p));
+ if (degree(p) != null) {
+ print_subsidiary_data(degree(p), '/');
+ }
+ if (radicalwidth(p)) {
+ tprint("width=");
+ print_scaled(radicalwidth(p));
+ tprint(" ");
+ }
+ if (radicaloptionset(p)) {
+ tprint(" [ ");
+ if (radicalexact(p))
+ tprint("exact ");
+ if (radicalleft(p))
+ tprint("left ");
+ if (radicalmiddle(p))
+ tprint("middle ");
+ if (radicalright(p))
+ tprint("right ");
+ tprint("]");
+ }
+ break;
+ case accent_noad:
+ if (top_accent_chr(p) != null) {
+ if (bot_accent_chr(p) != null) {
+ tprint_esc("Umathaccent both");
+ } else {
+ tprint_esc("Umathaccent");
+ }
+ } else if (bot_accent_chr(p) != null) {
+ tprint_esc("Umathaccent bottom");
+ } else {
+ tprint_esc("Umathaccent overlay");
+ }
+ if (accentfraction(p)) {
+ tprint(" fraction=");
+ print_int(accentfraction(p));
+ tprint(" ");
+ }
+ switch (subtype(p)) {
+ case 0:
+ if (top_accent_chr(p) != null) {
+ if (bot_accent_chr(p) != null) {
+ print_fam_and_char(top_accent_chr(p));
+ print_fam_and_char(bot_accent_chr(p));
+ } else {
+ print_fam_and_char(top_accent_chr(p));
+ }
+ } else if (bot_accent_chr(p) != null) {
+ print_fam_and_char(bot_accent_chr(p));
+ } else {
+ print_fam_and_char(overlay_accent_chr(p));
+ }
+ break;
+ case 1:
+ if (top_accent_chr(p) != null) {
+ tprint(" fixed ");
+ print_fam_and_char(top_accent_chr(p));
+ if (bot_accent_chr(p) != null) {
+ print_fam_and_char(bot_accent_chr(p));
+ }
+ } else {
+ confusion("display_accent_noad");
+ }
+ break;
+ case 2:
+ if (bot_accent_chr(p) != null) {
+ if (top_accent_chr(p) != null) {
+ print_fam_and_char(top_accent_chr(p));
+ }
+ tprint(" fixed ");
+ print_fam_and_char(bot_accent_chr(p));
+ } else{
+ confusion("display_accent_noad");
+ }
+ break;
+ case 3:
+ if (top_accent_chr(p) != null && bot_accent_chr(p) != null) {
+ tprint(" fixed ");
+ print_fam_and_char(top_accent_chr(p));
+ tprint(" fixed ");
+ print_fam_and_char(bot_accent_chr(p));
+ } else {
+ confusion("display_accent_noad");
+ }
+ break;
+ }
+ break;
+ }
+ print_subsidiary_data(nucleus(p), '.');
+ print_subsidiary_data(supscr(p), '^');
+ print_subsidiary_data(subscr(p), '_');
+}
+
+void display_fence_noad(pointer p)
+{
+ if (subtype(p) == right_noad_side)
+ tprint_esc("right");
+ else if (subtype(p) == left_noad_side)
+ tprint_esc("left");
+ else
+ tprint_esc("middle");
+ print_delimiter(delimiter(p));
+}
+
+void display_fraction_noad(pointer p)
+{
+ tprint_esc("fraction, thickness ");
+ if (thickness(p) == default_code)
+ tprint("= default");
+ else
+ print_scaled(thickness(p));
+ if ((left_delimiter(p) != null) &&
+ ((small_fam(left_delimiter(p)) != 0) || (small_char(left_delimiter(p)) != 0) ||
+ (large_fam(left_delimiter(p)) != 0) || (large_char(left_delimiter(p)) != 0))) {
+ tprint(", left-delimiter ");
+ print_delimiter(left_delimiter(p));
+ }
+ if ((right_delimiter(p) != null) &&
+ ((small_fam(right_delimiter(p)) != 0) || (small_char(right_delimiter(p)) != 0) ||
+ (large_fam(right_delimiter(p)) != 0) || (large_char(right_delimiter(p)) != 0))) {
+ tprint(", right-delimiter ");
+ print_delimiter(right_delimiter(p));
+ }
+ print_subsidiary_data(numerator(p), '\\');
+ print_subsidiary_data(denominator(p), '/');
+}
+
+/*tex
+
+ 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|.
+
+*/
+
+static void new_save_level_math(group_code c)
+{
+ set_saved_record(0, saved_textdir, 0, text_dir_ptr);
+ text_dir_ptr = new_dir(math_direction_par);
+ incr(save_ptr);
+ new_save_level(c);
+ eq_word_define(int_base + body_direction_code, math_direction_par);
+ eq_word_define(int_base + par_direction_code, math_direction_par);
+ eq_word_define(int_base + text_direction_code, math_direction_par);
+}
+
+static void push_math(group_code c, int mstyle)
+{
+ if (math_direction_par != text_direction_par)
+ dir_math_save = true;
+ push_nest();
+ mode = -mmode;
+ incompleat_noad_par = null;
+ m_style = mstyle;
+ new_save_level_math(c);
+}
+
+static void enter_ordinary_math(void)
+{
+ push_math(math_shift_group, text_style);
+ eq_word_define(int_base + cur_fam_code, -1);
+ if (every_math_par != null)
+ begin_token_list(every_math_par, every_math_text);
+}
+
+void enter_display_math(void);
+
+/*tex
+
+ 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.
+
+*/
+
+void init_math(void)
+{
+ if (cur_cmd == math_shift_cmd) {
+ /*tex |get_x_token| would fail on \.{\\ifmmode}\thinspace! */
+ get_token();
+ if ((cur_cmd == math_shift_cmd) && (mode > 0)) {
+ enter_display_math();
+ } else {
+ back_input();
+ enter_ordinary_math();
+ }
+ } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) {
+ enter_display_math();
+ } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) {
+ enter_ordinary_math();
+ } else {
+ you_cant();
+ }
+}
+
+/*tex
+
+ 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.
+
+ 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.
+
+*/
+
+void start_eq_no(void)
+{
+ set_saved_record(0, saved_eqno, 0, cur_chr);
+ incr(save_ptr);
+ enter_ordinary_math();
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void math_left_brace(void)
+{
+ pointer q;
+ tail_append(new_noad());
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ back_input();
+ (void) scan_math(nucleus(tail), m_style);
+}
+
+/*tex
+
+ If the inline directions of \.{\\pardir} and \.{\\mathdir} are opposite, then
+ this function will return true. Discovering that fact is somewhat odd because
+ it needs traversal of the |save_stack|. The occurance of displayed equations
+ is weird enough that this is probably still better than having yet another
+ field in the |input_stack| structures.
+
+ None of this makes much sense if the inline direction of either one of
+ \.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current math
+ machinery is ill suited anyway so I do not bother to test that.
+
+*/
+
+static boolean math_and_text_reversed_p(void)
+{
+ int i = save_ptr - 1;
+ while (save_type(i) != level_boundary)
+ i--;
+ while (i < save_ptr) {
+ if (save_type(i) == restore_old_value &&
+ save_value(i) == int_base + par_direction_code) {
+ if (textdir_opposite(math_direction_par, save_value(i - 1)))
+ return true;
+ }
+ i++;
+ }
+ return false;
+}
+
+/*tex
+
+ 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|.
+
+*/
+
+void enter_display_math(void)
+{
+ /*tex new or partial |pre_display_size| */
+ scaled w;
+ /*tex new |display_width| */
+ scaled l;
+ /*tex new |display_indent| */
+ scaled s;
+ pointer p;
+ /*tex scope of paragraph shape specification */
+ int n;
+ /*tex
+
+ `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' or the 2nd of \.{\$\${ }\$\$}
+ \.{\$\${ }\$\$}
+
+ */
+ if (head == tail ||
+ (vlink(head) == tail &&
+ type(tail) == local_par_node && vlink(tail) == null)) {
+ if (vlink(head) == tail) {
+ /*tex
+
+ |resume_after_display| inserts a |local_par_node|, but if there
+ is another display immediately following, we have to get rid of
+ that node.
+
+ */
+ flush_node(tail);
+ }
+ pop_nest();
+ w = -max_dimen;
+ } else {
+ line_break(true, math_shift_group);
+ w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par);
+ }
+ /*tex
+
+ Now we are in vertical mode, working on the list that will contain the
+ display. A displayed equation is considered to be three lines long, so we
+ calculate the length and offset of line number |prev_graf+2|.
+
+ */
+ if (par_shape_par_ptr == null) {
+ if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) {
+ halfword used_hang_indent = swap_hang_indent(hang_indent_par);
+ l = hsize_par - abs(used_hang_indent);
+ if (used_hang_indent > 0)
+ s = used_hang_indent;
+ else
+ s = 0;
+ } else {
+ l = hsize_par;
+ s = 0;
+ }
+ } else {
+ n = vinfo(par_shape_par_ptr + 1);
+ if (prev_graf_par + 2 >= n)
+ p = par_shape_par_ptr + 2 * n + 1;
+ else
+ p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1;
+ s = varmem[(p - 1)].cint;
+ l = varmem[p].cint;
+ s = swap_parshape_indent(s,l);
+ }
+ push_math(math_shift_group, display_style);
+ 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);
+ eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0));
+ if (every_display_par != null)
+ begin_token_list(every_display_par, every_display_text);
+ if (nest_ptr == 1) {
+ checked_page_filter(before_display);
+ build_page();
+ }
+}
+
+/*tex
+
+ The next routine parses all variations of a delimiter code. The |extcode|
+ tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the |doclass|
+ tells whether or not read a math class also (for \.{\\delimiter} c.s.). (the
+ class is passed on for conversion to \.{\\mathchar}).
+
+*/
+
+static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
+{
+ const char *hlp[] = {
+ "I'm going to use 0 instead of that illegal code value.",
+ NULL
+ };
+ delcodeval d;
+ int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0;
+ if (extcode == tex_mathcode) {
+ /*tex
+
+ \.{\\delcode}, this is the easiest
+
+ */
+ scan_int();
+ /*tex "MFCCFCC or "FCCFCC */
+ if (doclass) {
+ mcls = (cur_val / 0x1000000);
+ cur_val = (cur_val & 0xFFFFFF);
+ }
+ if (cur_val > 0xFFFFFF) {
+ tex_error("Invalid delimiter code", hlp);
+ cur_val = 0;
+ }
+ msfam = (cur_val / 0x100000);
+ mschr = (cur_val % 0x100000) / 0x1000;
+ mlfam = (cur_val & 0xFFF) / 0x100;
+ mlchr = (cur_val % 0x100);
+ } else if (extcode == umath_mathcode) {
+ /*tex
+
+ \.{\\Udelcode}: <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF>
+
+ */
+ if (doclass) {
+ scan_int();
+ mcls = cur_val;
+ }
+ scan_int();
+ msfam = cur_val;
+ scan_char_num();
+ mschr = cur_val;
+ if (msfam < 0 || msfam > 255) {
+ tex_error("Invalid delimiter code", hlp);
+ msfam = 0;
+ mschr = 0;
+ }
+ mlfam = 0;
+ mlchr = 0;
+ } else if (extcode == umathnum_mathcode) {
+ /*tex
+
+ \.{\\Udelcodenum}:"FF<21bits>; the largest numeric value is $2^29-1$,
+ but the top of bit 21 can't be used as it contains invalid USV's.
+
+ */
+ if (doclass) { /* such a primitive doesn't exist */
+ confusion("umathnum_mathcode");
+ }
+ scan_int();
+ msfam = (cur_val / 0x200000);
+ mschr = cur_val & 0x1FFFFF;
+ if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) {
+ tex_error("Invalid delimiter code", hlp);
+ msfam = 0;
+ mschr = 0;
+ }
+ mlfam = 0;
+ mlchr = 0;
+ } else {
+ /*tex Something's gone wrong! */
+ confusion("unknown_extcode");
+ }
+ d.class_value = mcls;
+ d.small_family_value = msfam;
+ d.small_character_value = mschr;
+ d.large_family_value = mlfam;
+ d.large_character_value = mlchr;
+ return d;
+}
+
+void scan_extdef_del_code(int level, int extcode)
+{
+ delcodeval d;
+ int p;
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ d = do_scan_extdef_del_code(extcode, false);
+ set_del_code(p, d.small_family_value, d.small_character_value,
+ d.large_family_value, d.large_character_value, (quarterword) (level));
+}
+
+mathcodeval scan_mathchar(int extcode)
+{
+ char errstr[255] = { 0 };
+ const char *hlp[] = {
+ "I'm going to use 0 instead of that illegal code value.",
+ NULL
+ };
+ mathcodeval d;
+ int mcls = 0, mfam = 0, mchr = 0;
+ if (extcode == tex_mathcode) {
+ /*tex \.{\\mathcode}: "TFCC */
+ scan_int();
+ if (cur_val > 0x8000) {
+ /*tex Needed for latex: fallback to umathnum_mathcode. */
+ mfam = (cur_val / 0x200000) & 0x7FF;
+ mcls = mfam % 0x08;
+ mfam = mfam / 0x08;
+ mchr = cur_val & 0x1FFFFF;
+ if (mchr > 0x10FFFF) {
+ tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp);
+ mcls = 0;
+ mfam = 0;
+ mchr = 0;
+ }
+ } else {
+ if (cur_val < 0) {
+ snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val);
+ tex_error(errstr, hlp);
+ cur_val = 0;
+ }
+ mcls = (cur_val / 0x1000);
+ mfam = ((cur_val % 0x1000) / 0x100);
+ mchr = (cur_val % 0x100);
+ }
+ } else if (extcode == umath_mathcode) {
+ /*tex <0-0x7> <0-0xFF> <0-0x10FFFF> */
+ scan_int();
+ mcls = cur_val;
+ scan_int();
+ mfam = cur_val;
+ scan_char_num();
+ mchr = cur_val;
+ if (mcls < 0 || mcls > 7 || mfam > 255) {
+ tex_error("Invalid math code", hlp);
+ mchr = 0;
+ mfam = 0;
+ mcls = 0;
+ }
+ } else if (extcode == umathnum_mathcode) {
+ /*tex
+
+ "FFT<21bits>: the largest numeric value is $2^32-1$, but the top of
+ bit 21 can't be used as it contains invalid USV's
+
+ Note: |scan_int| won't accept families 128-255 because these use bit 32
+
+ */
+ scan_int();
+ mfam = (cur_val / 0x200000) & 0x7FF;
+ mcls = mfam % 0x08;
+ mfam = mfam / 0x08;
+ mchr = cur_val & 0x1FFFFF;
+ if (mchr > 0x10FFFF) {
+ tex_error("Invalid math code", hlp);
+ mcls = 0;
+ mfam = 0;
+ mchr = 0;
+ }
+ } else {
+ /*tex Something's gone wrong. */
+ confusion("unknown_extcode");
+ }
+ d.class_value = mcls;
+ d.family_value = mfam;
+ d.character_value = mchr;
+ return d;
+}
+
+void scan_extdef_math_code(int level, int extcode)
+{
+ mathcodeval d;
+ int p;
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ d = scan_mathchar(extcode);
+ set_math_code(p, d.class_value, d.family_value, d.character_value, (quarterword) (level));
+}
+
+/*tex This reads in a delcode when actually a mathcode is needed. */
+
+mathcodeval scan_delimiter_as_mathchar(int extcode)
+{
+ delcodeval dval;
+ mathcodeval mval;
+ dval = do_scan_extdef_del_code(extcode, true);
+ mval.class_value = dval.class_value;
+ mval.family_value = dval.small_family_value;
+ mval.character_value = dval.small_character_value;
+ return mval;
+}
+
+/*tex
+
+ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are broken
+ down into subfields called |type| and either |math_list| or
+ |(math_fam,math_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|.
+
+*/
+
+#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd)
+
+int scan_math_style(pointer p, int mstyle)
+{
+ get_next_nb_nr();
+ back_input();
+ scan_left_brace();
+ set_saved_record(0, saved_math, 0, p);
+ incr(save_ptr);
+ push_math(math_group, mstyle);
+ return 1;
+}
+
+int scan_math(pointer p, int mstyle)
+{
+ mathcodeval mval = { 0, 0, 0 };
+ assert(p != null);
+ RESTART:
+ get_next_nb_nr();
+ RESWITCH:
+ switch (cur_cmd) {
+ case letter_cmd:
+ case other_char_cmd:
+ case char_given_cmd:
+ mval = get_math_code(cur_chr);
+ if (mval.class_value == 8) {
+ /*tex An active character that is an |outer_call| is allowed here. */
+ cur_cs = active_to_cs(cur_chr, true);
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ x_token();
+ back_input();
+ goto RESTART;
+ }
+ break;
+ case char_num_cmd:
+ scan_char_num();
+ cur_chr = cur_val;
+ cur_cmd = char_given_cmd;
+ goto RESWITCH;
+ break;
+ case math_char_num_cmd:
+ if (cur_chr == 0)
+ mval = scan_mathchar(tex_mathcode);
+ else if (cur_chr == 1)
+ mval = scan_mathchar(umath_mathcode);
+ else if (cur_chr == 2)
+ mval = scan_mathchar(umathnum_mathcode);
+ else
+ confusion("scan_math");
+ break;
+ case math_given_cmd:
+ mval = mathchar_from_integer(cur_chr, tex_mathcode);
+ break;
+ case xmath_given_cmd:
+ mval = mathchar_from_integer(cur_chr, umath_mathcode);
+ break;
+ case delim_num_cmd:
+ if (cur_chr == 0)
+ mval = scan_delimiter_as_mathchar(tex_mathcode);
+ else if (cur_chr == 1)
+ mval = scan_delimiter_as_mathchar(umath_mathcode);
+ else
+ confusion("scan_math");
+ break;
+ default:
+ /*tex
+
+ The pointer |p| is placed on |save_stack| while a complex subformula
+ is being scanned.
+
+ */
+ back_input();
+ scan_left_brace();
+ set_saved_record(0, saved_math, 0, p);
+ incr(save_ptr);
+ push_math(math_group, mstyle);
+ return 1;
+ }
+ type(p) = math_char_node;
+ math_character(p) = mval.character_value;
+ if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range)
+ math_fam(p) = cur_fam_par;
+ else
+ math_fam(p) = mval.family_value;
+ return 0;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+#define math_class_to_type(target,source) \
+ switch (source) { \
+ case 0: target = ord_noad_type; break; \
+ case 1: target = op_noad_type_normal; break; \
+ case 2: target = bin_noad_type; break; \
+ case 3: target = rel_noad_type; break; \
+ case 4: target = open_noad_type; break; \
+ case 5: target = close_noad_type; break; \
+ case 6: target = punct_noad_type; break; \
+ }
+
+void set_math_char(mathcodeval mval)
+{
+ pointer p;
+ if (mval.class_value == 8) {
+ /*tex An active character that is an |outer_call| is allowed here */
+ cur_cs = active_to_cs(cur_chr, true);
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ x_token();
+ back_input();
+ } else {
+ pointer q;
+ p = new_noad();
+ q = new_node(math_char_node, 0);
+ nucleus(p) = q;
+ math_character(nucleus(p)) = mval.character_value;
+ math_fam(nucleus(p)) = mval.family_value;
+ if (mval.class_value == math_use_current_family_code) {
+ if (cur_fam_par_in_range)
+ math_fam(nucleus(p)) = cur_fam_par;
+ subtype(p) = ord_noad_type;
+ } else {
+ math_class_to_type(subtype(p),mval.class_value);
+ }
+ tail_append(p);
+ }
+}
+
+/*tex
+
+ The |math_char_in_text| procedure creates a new node representing a math char
+ in text code, and appends it to the current list. However, if the math code
+ is sufficiently large, the |cur_chr| is treated as an active character and
+ nothing is appended.
+
+*/
+
+void math_char_in_text(mathcodeval mval)
+{
+ pointer p;
+ if (mval.class_value == 8) {
+ /*tex An active character that is an |outer_call| is allowed here */
+ cur_cs = active_to_cs(cur_chr, true);
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ x_token();
+ back_input();
+ } else {
+ p = new_char(fam_fnt(mval.family_value, text_size), mval.character_value);
+ tail_append(p);
+ }
+}
+
+void math_math_comp(void)
+{
+ pointer q;
+ tail_append(new_noad());
+ subtype(tail) = (quarterword) cur_chr;
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ if (cur_chr == over_noad_type)
+ (void) scan_math(nucleus(tail), cramped_style(m_style));
+ else
+ (void) scan_math(nucleus(tail), m_style);
+}
+
+void math_limit_switch(void)
+{
+ const char *hlp[] = {
+ "I'm ignoring this misplaced \\limits or \\nolimits command.",
+ NULL
+ };
+ if (head != tail) {
+ if (type(tail) == simple_noad &&
+ (subtype(tail) == op_noad_type_normal ||
+ subtype(tail) == op_noad_type_limits ||
+ subtype(tail) == op_noad_type_no_limits)) {
+ subtype(tail) = (quarterword) cur_chr;
+ return;
+ }
+ }
+ tex_error("Limit controls must follow a math operator", hlp);
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static void scan_delimiter(pointer p, int r)
+{
+ delcodeval dval = { 0, 0, 0, 0, 0 };
+ if (r == tex_mathcode) {
+ /*tex \.{\\radical} */
+ dval = do_scan_extdef_del_code(tex_mathcode, true);
+ } else if (r == umath_mathcode) {
+ /*tex \.{\\Uradical} */
+ dval = do_scan_extdef_del_code(umath_mathcode, false);
+ } else if (r == no_mathcode) {
+ get_next_nb_nr();
+ switch (cur_cmd) {
+ case letter_cmd:
+ case other_char_cmd:
+ dval = get_del_code(cur_chr);
+ break;
+ case delim_num_cmd:
+ if (cur_chr == 0) {
+ /*tex \.{\\delimiter} */
+ dval = do_scan_extdef_del_code(tex_mathcode, true);
+ } else if (cur_chr == 1) {
+ /*tex \.{\\Udelimiter} */
+ dval = do_scan_extdef_del_code(umath_mathcode, true);
+ } else {
+ confusion("scan_delimiter1");
+ }
+ break;
+ default:
+ dval.small_family_value = -1;
+ break;
+ }
+ } else {
+ confusion("scan_delimiter2");
+ }
+ if (p == null)
+ return;
+ if (dval.small_family_value < 0) {
+ const char *hlp[] = {
+ "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>'.",
+ NULL
+ };
+ back_error("Missing delimiter (. inserted)", hlp);
+ small_fam(p) = 0;
+ small_char(p) = 0;
+ large_fam(p) = 0;
+ large_char(p) = 0;
+ } else {
+ small_fam(p) = dval.small_family_value;
+ small_char(p) = dval.small_character_value;
+ large_fam(p) = dval.large_family_value;
+ large_char(p) = dval.large_character_value;
+ }
+ return;
+}
+
+void math_radical(void)
+{
+ halfword q;
+ int chr_code = cur_chr;
+ halfword options = 0;
+ tail_append(new_node(radical_noad, chr_code));
+ q = new_node(delim_node, 0);
+ left_delimiter(tail) = q;
+ while (1) {
+ if (scan_keyword("width")) {
+ scan_dimen(false,false,false);
+ radicalwidth(tail) = cur_val ;
+ } else if (scan_keyword("left")) {
+ options = options | noad_option_left ;
+ } else if (scan_keyword("middle")) {
+ options = options | noad_option_middle ;
+ } else if (scan_keyword("right")) {
+ options = options | noad_option_right ;
+ } else {
+ break;
+ }
+ }
+ radicaloptions(tail) = options;
+ if (chr_code == 0)
+ /*tex \.{\\radical} */
+ scan_delimiter(left_delimiter(tail), tex_mathcode);
+ else if (chr_code == 1)
+ /*tex \.{\\Uradical} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 2)
+ /*tex \.{\\Uroot} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 3)
+ /*tex \.{\\Uunderdelimiter} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 4)
+ /*tex \.{\\Uoverdelimiter} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 5)
+ /*tex \.{\\Udelimiterunder} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 6)
+ /*tex \.{\\Udelimiterover} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 7)
+ /*tex \.{\\Uhextensible} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else
+ confusion("math_radical");
+ if (chr_code == 7) {
+ /*tex type will change */
+ q = new_node(sub_box_node, 0);
+ nucleus(tail) = q;
+ return;
+ } else if (chr_code == 2) {
+ /*tex
+
+ The trick with the |vlink(q)| is used by |scan_math| to decide
+ whether it needs to go on.
+
+ */
+ q = new_node(math_char_node, 0);
+ vlink(q) = tail;
+ degree(tail) = q;
+ if (!scan_math(degree(tail), sup_sup_style(m_style))) {
+ vlink(degree(tail)) = null;
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ (void) scan_math(nucleus(tail), cramped_style(m_style));
+ }
+ } else {
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ (void) scan_math(nucleus(tail), cramped_style(m_style));
+ }
+}
+
+void math_ac(void)
+{
+ halfword q;
+ mathcodeval t = { 0, 0, 0 };
+ mathcodeval b = { 0, 0, 0 };
+ mathcodeval o = { 0, 0, 0 };
+ if (cur_cmd == accent_cmd) {
+ const char *hlp[] = {
+ "I'm changing \\accent to \\mathaccent here; wish me luck.",
+ "(Accents are not the same in formulas as they are in text.)",
+ NULL
+ };
+ tex_error("Please use \\mathaccent for accents in math mode", hlp);
+ }
+ tail_append(new_node(accent_noad, 0));
+ if (cur_chr == 0) {
+ /*tex \.{\\mathaccent} */
+ t = scan_mathchar(tex_mathcode);
+ } else if (cur_chr == 1) {
+ /*tex \.{\\Umathaccent} */
+ if (scan_keyword("fixed")) {
+ /*tex top */
+ subtype(tail) = 1;
+ t = scan_mathchar(umath_mathcode);
+ } else if (scan_keyword("both")) {
+ /*tex top bottom */
+ if (scan_keyword("fixed")) {
+ subtype(tail) = 1;
+ }
+ t = scan_mathchar(umath_mathcode);
+ if (scan_keyword("fixed")) {
+ subtype(tail) += 2;
+ }
+ b = scan_mathchar(umath_mathcode);
+ } else if (scan_keyword("bottom")) {
+ /*tex bottom */
+ if (scan_keyword("fixed")) {
+ subtype(tail) = 2;
+ }
+ b = scan_mathchar(umath_mathcode);
+ } else if (scan_keyword("top")) {
+ /*tex top */
+ if (scan_keyword("fixed")) {
+ subtype(tail) = 1;
+ }
+ t = scan_mathchar(umath_mathcode);
+ } else if (scan_keyword("overlay")) {
+ /* overlay */
+ if (scan_keyword("fixed")) {
+ subtype(tail) = 1;
+ }
+ o = scan_mathchar(umath_mathcode);
+ } else {
+ /*tex top */
+ t = scan_mathchar(umath_mathcode);
+ }
+ if (scan_keyword("fraction")) {
+ scan_int();
+ accentfraction(tail) = cur_val;
+ }
+ } else {
+ confusion("mathaccent");
+ }
+ if (!(t.character_value == 0 && t.family_value == 0)) {
+ q = new_node(math_char_node, 0);
+ top_accent_chr(tail) = q;
+ math_character(top_accent_chr(tail)) = t.character_value;
+ if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range)
+ math_fam(top_accent_chr(tail)) = cur_fam_par;
+ else
+ math_fam(top_accent_chr(tail)) = t.family_value;
+ }
+ if (!(b.character_value == 0 && b.family_value == 0)) {
+ q = new_node(math_char_node, 0);
+ bot_accent_chr(tail) = q;
+ math_character(bot_accent_chr(tail)) = b.character_value;
+ if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range)
+ math_fam(bot_accent_chr(tail)) = cur_fam_par;
+ else
+ math_fam(bot_accent_chr(tail)) = b.family_value;
+ }
+ if (!(o.character_value == 0 && o.family_value == 0)) {
+ q = new_node(math_char_node, 0);
+ overlay_accent_chr(tail) = q;
+ math_character(overlay_accent_chr(tail)) = o.character_value;
+ if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range)
+ math_fam(overlay_accent_chr(tail)) = cur_fam_par;
+ else
+ math_fam(overlay_accent_chr(tail)) = o.family_value;
+ }
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ (void) scan_math(nucleus(tail), cramped_style(m_style));
+}
+
+pointer math_vcenter_group(pointer p)
+{
+ pointer q, r;
+ q = new_noad();
+ subtype(q) = vcenter_noad_type;
+ r = new_node(sub_box_node, 0);
+ nucleus(q) = r;
+ math_list(nucleus(q)) = p;
+ return q;
+}
+
+/*tex
+
+ The routine that scans the four mlists of a \.{\\mathchoice} is very much
+ like the routine that builds discretionary nodes.
+
+*/
+
+void append_choices(void)
+{
+ tail_append(new_choice());
+ incr(save_ptr);
+ set_saved_record(-1, saved_choices, 0, 0);
+ push_math(math_choice_group, display_style);
+ scan_left_brace();
+}
+
+void build_choices(void)
+{
+ pointer p;
+ int prev_style;
+ prev_style = m_style;
+ unsave_math();
+ p = fin_mlist(null);
+ assert(saved_type(-1) == saved_choices);
+ switch (saved_value(-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;
+ break;
+ }
+ set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1));
+ push_math(math_choice_group, (prev_style + 2));
+ scan_left_brace();
+}
+
+/*tex
+
+ Subscripts and superscripts are attached to the previous nucleus by the
+ action procedure called |sub_sup|.
+
+*/
+
+static void do_sub_sup(int no)
+{
+ pointer q;
+ if (tail == head || (!scripts_allowed(tail))) {
+ tail_append(new_noad());
+ q = new_node(sub_mlist_node, 0);
+ nucleus(tail) = q;
+ }
+ if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) {
+ /*tex |super_sub_script| */
+ if (supscr(tail) != null) {
+ const char *hlp[] = {
+ "I treat `x^1^2' essentially like `x^1{}^2'.", NULL
+ };
+ tail_append(new_noad());
+ q = new_node(sub_mlist_node, 0);
+ nucleus(tail) = q;
+ tex_error("Double superscript", hlp);
+ }
+ if (no) {
+ noadoptions(tail) = noadoptions(tail) | noad_option_no_super_script ;
+ }
+ q = new_node(math_char_node, 0);
+ supscr(tail) = q;
+ (void) scan_math(supscr(tail), sup_style(m_style));
+ } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) {
+ if (subscr(tail) != null) {
+ const char *hlp[] = {
+ "I treat `x_1_2' essentially like `x_1{}_2'.", NULL
+ };
+ tail_append(new_noad());
+ q = new_node(sub_mlist_node, 0);
+ nucleus(tail) = q;
+ tex_error("Double subscript", hlp);
+ }
+ if (no) {
+ noadoptions(tail) = noadoptions(tail) | noad_option_no_sub_script ;
+ }
+ q = new_node(math_char_node, 0);
+ subscr(tail) = q;
+ (void) scan_math(subscr(tail), sub_style(m_style));
+ }
+}
+
+void sub_sup(void)
+{
+ do_sub_sup(0);
+}
+
+void no_sub_sup(void)
+{
+ do_sub_sup(1);
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void math_fraction(void)
+{
+ /*tex The type of generalized fraction we are scanning: */
+ halfword c;
+ pointer q;
+ halfword options = 0;
+ halfword temp_value;
+ c = cur_chr;
+ if (incompleat_noad_par != null) {
+ const char *hlp[] = {
+ "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}'.",
+ NULL
+ };
+ if (c >= delimited_code) {
+ scan_delimiter(null, no_mathcode);
+ scan_delimiter(null, no_mathcode);
+ }
+ if ((c % delimited_code) == above_code)
+ scan_normal_dimen();
+ tex_error("Ambiguous; you need another { and }", hlp);
+ } else {
+ incompleat_noad_par = new_node(fraction_noad, 0);
+ temp_value = new_node(sub_mlist_node, 0);
+ numerator(incompleat_noad_par) = temp_value;
+ math_list(numerator(incompleat_noad_par)) = vlink(head);
+ vlink(head) = null;
+ tail = head;
+ m_style = cramped_style(m_style);
+
+ if ((c % delimited_code) == skewed_code) {
+ q = new_node(delim_node, 0);
+ middle_delimiter(incompleat_noad_par) = q;
+ scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode);
+ }
+ if (c >= delimited_code) {
+ q = new_node(delim_node, 0);
+ left_delimiter(incompleat_noad_par) = q;
+ q = new_node(delim_node, 0);
+ right_delimiter(incompleat_noad_par) = q;
+ scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode);
+ scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode);
+ }
+ switch (c % delimited_code) {
+ case above_code:
+ while (1) {
+ if (scan_keyword("exact")) {
+ options = options | noad_option_exact ;
+ } else if (scan_keyword("norule")) {
+ options = options | noad_option_no_rule ;
+ } else {
+ break;
+ }
+ }
+ fractionoptions(incompleat_noad_par) = options;
+ scan_normal_dimen();
+ thickness(incompleat_noad_par) = cur_val;
+ break;
+ case over_code:
+ thickness(incompleat_noad_par) = default_code;
+ break;
+ case atop_code:
+ thickness(incompleat_noad_par) = 0;
+ break;
+ case skewed_code:
+ while (1) {
+ if (scan_keyword("exact")) {
+ options = options | noad_option_exact ;
+ } else if (scan_keyword("noaxis")) {
+ options = options | noad_option_no_axis ;
+ } else {
+ break;
+ }
+ }
+ fractionoptions(incompleat_noad_par) = options;
+ thickness(incompleat_noad_par) = 0;
+ break;
+ }
+ }
+}
+
+/*tex
+
+ 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 |fence_noad| that ends the current mlist; this |fence_noad|
+ has not yet been appended.
+
+*/
+
+pointer fin_mlist(pointer p)
+{
+ pointer q;
+ if (incompleat_noad_par != null) {
+ if (denominator(incompleat_noad_par) != null) {
+ type(denominator(incompleat_noad_par)) = sub_mlist_node;
+ } else {
+ q = new_node(sub_mlist_node, 0);
+ denominator(incompleat_noad_par) = q;
+ }
+ math_list(denominator(incompleat_noad_par)) = vlink(head);
+ if (p == null) {
+ q = incompleat_noad_par;
+ } else {
+ q = math_list(numerator(incompleat_noad_par));
+ if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) || (delim_par == null))
+ confusion("right");
+ math_list(numerator(incompleat_noad_par)) = vlink(delim_par);
+ vlink(delim_par) = incompleat_noad_par;
+ vlink(incompleat_noad_par) = p;
+ }
+ } else {
+ vlink(tail) = p;
+ q = vlink(head);
+ }
+ pop_nest();
+ return q;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void close_math_group(pointer p)
+{
+ int old_style = m_style;
+ unsave_math();
+ decr(save_ptr);
+ assert(saved_type(0) == saved_math);
+ type(saved_value(0)) = sub_mlist_node;
+ p = fin_mlist(null);
+ math_list(saved_value(0)) = p;
+ if (p != null && vlink(p) == null) {
+ if (type(p) == simple_noad) {
+ if (subscr(p) == null && supscr(p) == null) {
+ /*tex (subtype(p) == ord_noad_type) */
+ int flatten = 0;
+ int modepar = math_flatten_mode_par;
+ switch (subtype(p)) {
+ case ord_noad_type :
+ flatten = (modepar & 1) == 1;
+ break;
+ case bin_noad_type :
+ flatten = (modepar & 2) == 2;
+ break;
+ case rel_noad_type :
+ flatten = (modepar & 4) == 4;
+ break;
+ case punct_noad_type :
+ flatten = (modepar & 8) == 8;
+ break;
+ case inner_noad_type :
+ flatten = (modepar & 16) == 16;
+ break;
+ default:
+ break;
+ }
+ if (flatten) {
+ type(saved_value(0)) = type(nucleus(p));
+ if (type(nucleus(p)) == math_char_node) {
+ math_fam(saved_value(0)) = math_fam(nucleus(p));
+ math_character(saved_value(0)) =
+ math_character(nucleus(p));
+ } else {
+ math_list(saved_value(0)) = math_list(nucleus(p));
+ math_list(nucleus(p)) = null;
+ }
+ delete_attribute_ref(node_attr(saved_value(0)));
+ node_attr(saved_value(0)) = node_attr(nucleus(p));
+ node_attr(nucleus(p)) = null;
+ flush_node(p);
+ }
+ }
+ } else if (type(p) == accent_noad) {
+ if (saved_value(0) == nucleus(tail) && type(tail) == simple_noad && subtype(tail) == ord_noad_type) {
+ pointer q = head;
+ while (vlink(q) != tail)
+ q = vlink(q);
+ vlink(q) = p;
+ nucleus(tail) = null;
+ subscr(tail) = null;
+ supscr(tail) = null;
+ delete_attribute_ref(node_attr(p));
+ node_attr(p) = node_attr(tail);
+ node_attr(tail) = null;
+ flush_node(tail);
+ tail = p;
+ }
+ }
+ }
+ if (vlink(saved_value(0)) > 0) {
+ pointer q;
+ q = new_node(math_char_node, 0);
+ nucleus(vlink(saved_value(0))) = q;
+ vlink(saved_value(0)) = null;
+ saved_value(0) = q;
+ (void) scan_math(saved_value(0), old_style);
+ /*tex restart */
+ }
+}
+
+/*tex
+
+ 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. The |middle| feature of eTeX allows one ore several \.{\\middle}
+ delimiters to appear between \.{\\left} and \.{\\right}.
+
+*/
+
+void math_left_right(void)
+{
+ /*tex |left_noad_side| .. |right_noad_side| */
+ halfword t;
+ /*tex new noad */
+ pointer p;
+ /*tex resulting mlist */
+ pointer q;
+ /*tex temporary */
+ pointer r;
+ halfword ht = 0;
+ halfword dp = 0;
+ halfword options = 0;
+ halfword type = -1 ;
+ t = cur_chr;
+ if (t > 10) {
+ /*tex we have \Uleft \Uright \Umiddle */
+ t = t - 10;
+ while (1) {
+ if (scan_keyword("height")) {
+ scan_dimen(false,false,false);
+ ht = cur_val ;
+ } else if (scan_keyword("depth")) {
+ scan_dimen(false,false,false);
+ dp = cur_val ;
+ } else if (scan_keyword("axis")) {
+ options = options | noad_option_axis ;
+ } else if (scan_keyword("noaxis")) {
+ options = options | noad_option_no_axis ;
+ } else if (scan_keyword("exact")) {
+ options = options | noad_option_exact ;
+ } else if (scan_keyword("class")) {
+ scan_int();
+ math_class_to_type(type,cur_val);
+ } else {
+ break;
+ }
+ }
+ }
+ if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) {
+ if (cur_group == math_shift_group) {
+ scan_delimiter(null, no_mathcode);
+ if (t == middle_noad_side) {
+ const char *hlp[] = {
+ "I'm ignoring a \\middle that had no matching \\left.",
+ NULL
+ };
+ tex_error("Extra \\middle", hlp);
+ } else {
+ const char *hlp[] = {
+ "I'm ignoring a \\right that had no matching \\left.",
+ NULL
+ };
+ tex_error("Extra \\right", hlp);
+ }
+ } else {
+ off_save();
+ }
+ } else {
+ p = new_noad();
+ type(p) = fence_noad;
+ subtype(p) = (quarterword) t;
+ r = new_node(delim_node, 0);
+ delimiter(p) = r;
+ delimiterheight(p) = ht;
+ delimiterdepth(p) = dp;
+ delimiteroptions(p) = options;
+ delimiterclass(p) = type;
+ delimiteritalic(p) = 0;
+ delimitersamesize(p) = 0;
+ scan_delimiter(delimiter(p), no_mathcode);
+ if (t == no_noad_side) {
+ tail_append(new_noad());
+ subtype(tail) = inner_noad_type;
+ r = new_node(sub_mlist_node, 0);
+ nucleus(tail) = r;
+ math_list(nucleus(tail)) = p;
+ return ;
+ }
+ if (t == left_noad_side) {
+ q = p;
+ } else {
+ q = fin_mlist(p);
+ unsave_math();
+ }
+ if (t != right_noad_side) {
+ push_math(math_left_group, m_style);
+ vlink(head) = q;
+ tail = p;
+ delim_par = p;
+ } else {
+ tail_append(new_noad());
+ subtype(tail) = inner_noad_type;
+ r = new_node(sub_mlist_node, 0);
+ nucleus(tail) = r;
+ math_list(nucleus(tail)) = q;
+ }
+ }
+}
+
+/*tex
+
+ \TeX\ gets to the following part of the program when the first `\.\$' ending
+ a display has been scanned.
+
+*/
+
+static void check_second_math_shift(void)
+{
+ get_x_token();
+ if (cur_cmd != math_shift_cmd) {
+ const char *hlp[] = {
+ "The `$' that I just saw supposedly matches a previous `$$'.",
+ "So I shall assume that you typed `$$' both times.",
+ NULL
+ };
+ back_error("Display math should end with $$", hlp);
+ }
+}
+
+static void check_display_math_end(void)
+{
+ if (cur_chr != cramped_display_style) {
+ const char *hlp[] = {
+ "I shall assume that you typed that.",
+ NULL
+ };
+ tex_error("Display math should end with \\Ustopdisplaymath", hlp);
+ }
+}
+
+static void check_inline_math_end(void)
+{
+ if (cur_chr != cramped_text_style) {
+ const char *hlp[] = {
+ "I shall assume that you typed that.",
+ NULL
+ };
+ tex_error("Inline math should end with \\Ustopmath", hlp);
+ }
+}
+
+static void resume_after_display(void)
+{
+ if (cur_group != math_shift_group)
+ confusion("display");
+ unsave_math();
+ prev_graf_par = prev_graf_par + 3;
+ push_nest();
+ mode = hmode;
+ space_factor_par = 1000;
+ /*tex This needs to be intercepted in the display math start! */
+ tail_append(make_local_par_node(penalty_par_code));
+ get_x_token();
+ if (cur_cmd != spacer_cmd)
+ back_input();
+ if (nest_ptr == 1) {
+ normal_page_filter(after_display);
+ build_page();
+ }
+}
+
+/*tex
+
+ The fussiest part of math mode processing occurs when a displayed formula is
+ being centered and placed with an optional equation number.
+
+ At this time we are in vertical mode (or internal vertical mode).
+
+ \starttabulate
+ \NC \type {p} \NC points to the mlist for the formula \NC \NR
+ \NC \type {a} \NC is either |null| or it points to a box containing the equation number \NC \NR
+ \NC \type {l} \NC is true if there was an \.{\\leqno}/ (so |a| is a horizontal box) \NC \NR
+ \stoptabulate
+
+*/
+
+#define inject_display_skip_before(g) \
+ if (g > 0) { \
+ switch (display_skip_mode_par) { \
+ case 0 : \
+ /*tex normal tex | always */ \
+ case 1 : \
+ /*tex always */ \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 2 : \
+ /*tex non-zero */ \
+ if (! glue_is_zero(glue_par(g))) \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 3: \
+ /*tex ignore */ \
+ break; \
+ default: \
+ /*tex > 3 reserved for future use */ \
+ tail_append(new_param_glue(g)); \
+ break; \
+ } \
+ }
+
+#define inject_display_skip_after(g) \
+ if (g > 0) { \
+ switch (display_skip_mode_par) { \
+ case 0 : \
+ /*tex normal tex | always */ \
+ case 1 : \
+ /*tex always */ \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 2 : \
+ /*tex non-zero */ \
+ if (! glue_is_zero(glue_par(g))) \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 3: \
+ /*tex ignore */ \
+ break; \
+ default: \
+ /*tex > 3 reserved for future use */ \
+ tail_append(new_param_glue(g)); \
+ break; \
+ } \
+ }
+
+static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
+{
+ /*tex box containing the equation */
+ pointer eq_box;
+ /*tex width of the equation */
+ scaled eq_w;
+ /*tex width of the line */
+ scaled line_w;
+ /*tex width of equation number */
+ scaled eqno_w;
+ /*tex width of equation number plus space to separate from equation */
+ scaled eqno_w2;
+ /*tex move the line right this much */
+ scaled line_s;
+ /*tex displacement of equation in the line */
+ scaled d;
+ /*tex glue parameter codes for before and after */
+ small_number g1, g2;
+ /*tex kern nodes used to position the display */
+ pointer r,s;
+ /*tex tail of adjustment list */
+ pointer t;
+ /*tex tail of pre-adjustment list */
+ pointer pre_t;
+ /*tex true if the math and surrounding text dirs are opposed */
+ boolean swap_dir;
+ scaled eqno_width;
+ swap_dir = (pre_display_direction_par < 0 ? true : false );
+ if (eqno_box != null && swap_dir)
+ l = !l;
+ adjust_tail = adjust_head;
+ pre_adjust_tail = pre_adjust_head;
+ eq_box = hpack(p, 0, additional, -1);
+ subtype(eq_box) = equation_list;
+ build_attribute_list(eq_box);
+ p = list_ptr(eq_box);
+ t = adjust_tail;
+ adjust_tail = null;
+ pre_t = pre_adjust_tail;
+ pre_adjust_tail = null;
+ eq_w = width(eq_box);
+ line_w = display_width_par;
+ line_s = display_indent_par;
+ if (eqno_box == null) {
+ eqno_w = 0;
+ eqno_width = 0;
+ eqno_w2 = 0;
+ } else {
+ eqno_w = width(eqno_box);
+ eqno_width = eqno_w;
+ eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000);
+ subtype(eqno_box) = equation_number_list;
+ /*tex build_attribute_list(eqno_box); */
+ }
+ if (eq_w + eqno_w2 > line_w) {
+ /*tex
+
+ The user can force the equation number to go on a separate line by
+ causing its width to be zero.
+
+ */
+ if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w)
+ || (total_shrink[sfi] != 0) || (total_shrink[fil] != 0)
+ || (total_shrink[fill] != 0) || (total_shrink[filll] != 0))) {
+ list_ptr(eq_box) = null;
+ flush_node(eq_box);
+ eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
+ subtype(eq_box) = equation_list;
+ build_attribute_list(eq_box);
+ } else {
+ eqno_w = 0;
+ if (eq_w > line_w) {
+ list_ptr(eq_box) = null;
+ flush_node(eq_box);
+ eq_box = hpack(p, line_w, exactly, -1);
+ subtype(eq_box) = equation_list;
+ build_attribute_list(eq_box);
+ }
+ }
+ eq_w = width(eq_box);
+ }
+ /*tex
+
+ 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.
+
+ */
+ d = half(line_w - eq_w);
+ if ((eqno_w > 0) && (d < 2 * eqno_w)) {
+ /*tex too close */
+ d = half(line_w - eq_w - eqno_w);
+ if (p != null)
+ if (!is_char_node(p))
+ if (type(p) == glue_node)
+ d = 0;
+ }
+ tail_append(new_penalty(pre_display_penalty_par,after_display_penalty));
+ if ((d + line_s <= pre_display_size_par) || l) {
+ /*tex 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;
+ }
+ /*tex
+
+ 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.
+
+ \.{\\leqno} on a forced single line due to |width=0|; it follows that |type(a)=hlist_node|
+
+ */
+ if (eqno_box && l && (eqno_w == 0)) {
+ /* if (math_direction_par==dir_TLT) { */
+ shift_amount(eqno_box) = 0;
+ /* } else { */
+ /* } */
+ append_to_vlist(eqno_box,lua_key_index(equation_number));
+ tail_append(new_penalty(inf_penalty,equation_number_penalty));
+ } else {
+ inject_display_skip_before(g1);
+ }
+ if (eqno_w != 0) {
+ r = new_kern(line_w - eq_w - eqno_w - d);
+ if (l) {
+ if (swap_dir) {
+ if (math_direction_par==dir_TLT) {
+ /*tex TRT + TLT + \eqno: (swap_dir=true, math_direction_par=TLT, l=true) */
+ s = new_kern(width(r) + eqno_w);
+ try_couple_nodes(eqno_box,r);
+ try_couple_nodes(r,eq_box);
+ try_couple_nodes(eq_box,s);
+ } else {
+ /*tex TLT + TRT + \eqno: (swap_dir=true, math_direction_par=TRT, l=true) */
+ try_couple_nodes(eqno_box,r);
+ try_couple_nodes(r,eq_box);
+ }
+ } else {
+ if (math_direction_par==dir_TLT) {
+ /*tex TLT + TLT + \leqno: (swap_dir=false, math_direction_par=TLT, l=true) */
+ s = new_kern(width(r) + eqno_w);
+ } else {
+ /*tex TRT + TRT + \leqno: (swap_dir=false, math_direction_par=TRT, l=true) */
+ s = new_kern(width(r));
+ }
+ try_couple_nodes(eqno_box,r);
+ try_couple_nodes(r,eq_box);
+ try_couple_nodes(eq_box,s);
+ }
+ eq_box = eqno_box;
+ } else {
+ if (swap_dir) {
+ if (math_direction_par==dir_TLT) {
+ /*tex TRT + TLT + \leqno: (swap_dir=true, math_direction_par=TLT, l=false) */
+ } else {
+ /*tex TLT + TRT + \leqno: (swap_dir=true, math_direction_par=TRT, l=false) */
+ }
+ try_couple_nodes(eq_box,r);
+ try_couple_nodes(r,eqno_box);
+ } else {
+ if (math_direction_par==dir_TLT) {
+ /*tex TLT + TLT + \eqno: (swap_dir=false, math_direction_par=TLT, l=false) */
+ s = new_kern(d);
+ } else {
+ /*tex TRT + TRT + \eqno: (swap_dir=false, math_direction_par=TRT, l=false) */
+ s = new_kern(width(r) + eqno_w);
+ }
+ try_couple_nodes(s,eq_box);
+ try_couple_nodes(eq_box,r);
+ try_couple_nodes(r,eqno_box);
+ eq_box = s;
+ }
+ }
+ eq_box = hpack(eq_box, 0, additional, -1);
+ subtype(eq_box) = equation_list; /* new */
+ build_attribute_list(eq_box);
+ shift_amount(eq_box) = line_s;
+ } else {
+ shift_amount(eq_box) = line_s + d;
+ }
+ /*tex check for prev: */
+ append_to_vlist(eq_box,lua_key_index(equation));
+ if ((eqno_box != null) && (eqno_w == 0) && !l) {
+ tail_append(new_penalty(inf_penalty,equation_number_penalty));
+ /* if (math_direction_par==dir_TLT) { */
+ shift_amount(eqno_box) = line_s + line_w - eqno_width ;
+ /* } else { */
+ /* } */
+ append_to_vlist(eqno_box,lua_key_index(equation_number));
+ g2 = 0;
+ }
+ if (t != adjust_head) {
+ /*tex migrating material comes after equation number */
+ vlink(tail) = vlink(adjust_head);
+ alink(adjust_tail) = alink(tail);
+ tail = t;
+ }
+ if (pre_t != pre_adjust_head) {
+ vlink(tail) = vlink(pre_adjust_head);
+ alink(pre_adjust_tail) = alink(tail);
+ tail = pre_t;
+ }
+ tail_append(new_penalty(post_display_penalty_par,after_display_penalty));
+ inject_display_skip_after(g2);
+ resume_after_display();
+}
+
+void after_math(void)
+{
+ /*tex |mmode| or |-mmode| */
+ int m;
+ /*tex the formula */
+ pointer p;
+ /*tex box containing equation number */
+ pointer a = null;
+ /*tex `\.{\\leqno}' instead of `\.{\\eqno}' */
+ boolean l = false;
+ m = mode;
+ /*tex this pops the nest */
+ p = fin_mlist(null);
+ if (cur_cmd == math_shift_cs_cmd &&
+ (cur_chr == text_style || cur_chr == display_style)) {
+ you_cant();
+ }
+ if (mode == -m) {
+ /*tex end of equation number */
+ if (cur_cmd == math_shift_cmd) {
+ check_second_math_shift();
+ } else {
+ check_display_math_end();
+ }
+ run_mlist_to_hlist(p, false, text_style);
+ a = hpack(vlink(temp_head), 0, additional, -1);
+ build_attribute_list(a);
+ unsave_math();
+ /*tex now |cur_group=math_shift_group| */
+ decr(save_ptr);
+ assert(saved_type(0) == saved_eqno);
+ if (saved_value(0) == 1)
+ l = true;
+ m = mode;
+ p = fin_mlist(null);
+
+ }
+ if (m < 0) {
+ /*tex
+
+ 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.
+
+ */
+ if (cur_cmd == math_shift_cs_cmd) {
+ check_inline_math_end();
+ }
+ tail_append(new_math(math_surround_par, before));
+ /*tex begin mathskip code */
+ switch (math_skip_mode) {
+ case 0 :
+ /*tex obey mathsurround when zero glue */
+ if (! glue_is_zero(math_skip_par)) {
+ copy_glue_values(tail,math_skip_par);
+ surround(tail) = 0;
+ }
+ break ;
+ case 1 :
+ /*tex always left */
+ case 3 :
+ /*tex always both */
+ case 6 :
+ /*tex only when skip */
+ copy_glue_values(tail,math_skip_par);
+ surround(tail) = 0;
+ break ;
+ case 2 :
+ /*tex only right */
+ surround(tail) = 0;
+ break ;
+ case 4 :
+ /*tex ignore, obey marthsurround */
+ break ;
+ case 5:
+ /*tex all spacing disabled */
+ surround(tail) = 0;
+ break ;
+ }
+ /*tex end mathskip code */
+ if (dir_math_save) {
+ tail_append(new_dir(math_direction_par));
+ }
+ run_mlist_to_hlist(p, (mode > 0), text_style);
+ try_couple_nodes(tail,vlink(temp_head));
+
+ while (vlink(tail) != null) {
+ tail = vlink(tail);
+ }
+ if (dir_math_save) {
+ tail_append(new_dir(math_direction_par));
+ subtype(tail) = cancel_dir;
+ }
+ dir_math_save = false;
+ tail_append(new_math(math_surround_par, after));
+ /*tex begin mathskip code */
+ switch (math_skip_mode) {
+ case 0 :
+ /*tex obey mathsurround when zero glue */
+ if (! glue_is_zero(math_skip_par)) {
+ copy_glue_values(tail,math_skip_par);
+ surround(tail) = 0;
+ }
+ break ;
+ case 2 :
+ /*tex always right */
+ case 3 :
+ /*tex always both */
+ case 6 :
+ /*tex only when skip */
+ copy_glue_values(tail,math_skip_par);
+ surround(tail) = 0;
+ break ;
+ case 1 :
+ /*tex only left */
+ surround(tail) = 0;
+ break ;
+ case 4 :
+ /*tex ignore, obey marthsurround */
+ break ;
+ case 5:
+ /*tex all spacing disabled */
+ surround(tail) = 0;
+ break ;
+ }
+ /*tex end mathskip code */
+ space_factor_par = 1000;
+ unsave_math();
+ } else {
+ if (a == null) {
+ if (cur_cmd == math_shift_cmd) {
+ check_second_math_shift();
+ } else {
+ check_display_math_end();
+ }
+ }
+ run_mlist_to_hlist(p, false, display_style);
+ finish_displayed_math(l, a, vlink(temp_head));
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
+{
+ do_assignments();
+ if (cur_cmd == math_shift_cmd) {
+ check_second_math_shift();
+ } else {
+ check_display_math_end();
+ }
+ pop_nest();
+ tail_append(new_penalty(pre_display_penalty_par,before_display_penalty));
+ inject_display_skip_before(above_display_skip_code);
+ vlink(tail) = p;
+ if (p != null)
+ tail = q;
+ tail_append(new_penalty(post_display_penalty_par,after_display_penalty));
+ inject_display_skip_after(below_display_skip_code);
+ cur_list.prev_depth_field = saved_prevdepth;
+ resume_after_display();
+}
+
+/*tex Interface to \.{\\Umath} and \.{\\mathstyle}: */
+
+void setup_math_style(void)
+{
+ pointer q;
+ tail_append(new_noad());
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ (void) scan_math_style(nucleus(tail), num_style(m_style));
+}
+
+void print_math_style(void)
+{
+ if (abs(mode) == mmode)
+ print_int(m_style);
+ else
+ print_int(-1);
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.h b/Build/source/texk/web2c/luatexdir/tex/texmath.h
new file mode 100644
index 00000000000..23fc5a0e42e
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texmath.h
@@ -0,0 +1,306 @@
+/* texmath.h
+
+ Copyright 1996-2006 Han The Thanh <thanh@pdftex.org>
+ Copyright 2006-2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef TEXMATH_H
+# define TEXMATH_H 1
+
+# define empty 0
+
+extern pointer new_noad(void);
+
+extern void show_math_node(halfword);
+extern void flush_math(void);
+extern void math_left_brace(void);
+extern void finish_display_alignment(halfword, halfword, halfword);
+extern halfword new_sub_box(halfword);
+
+# define math_reset(p) do { if (p!=null) flush_node(p); p = null; } while (0)
+
+# define scripts_allowed(A) ((type((A))>=simple_noad)&&(type((A))<fence_noad))
+
+# define default_code 010000000000 /* denotes |default_rule_thickness| */
+
+extern void initialize_math(void);
+extern void initialize_math_spacing(void);
+extern halfword math_vcenter_group(halfword);
+extern void build_choices(void);
+extern void close_math_group(halfword);
+extern void init_math(void);
+extern void start_eq_no(void);
+extern void set_math_char(mathcodeval);
+extern void math_char_in_text(mathcodeval);
+extern void math_math_comp(void);
+extern void math_limit_switch(void);
+extern void math_radical(void);
+extern void math_ac(void);
+extern pointer new_style(small_number);
+extern void append_choices(void);
+extern void sub_sup(void);
+extern void no_sub_sup(void);
+extern void math_fraction(void);
+extern void math_left_right(void);
+extern void after_math(void);
+
+extern void scan_extdef_del_code(int level, int extcode);
+extern void scan_extdef_math_code(int level, int extcode);
+
+extern int fam_fnt(int fam_id, int size_id);
+extern void def_fam_fnt(int fam_id, int size_id, int f, int lvl);
+extern void dump_math_data(void);
+extern void undump_math_data(void);
+void unsave_math_data(int lvl);
+
+/*
+ @ 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|).
+*/
+
+extern const char *math_style_names[];
+
+# define cramped 1 /* add this to an uncramped style if you want to cramp it */
+
+# define cramped_style(A) 2*((A)/2)+cramped /* cramp the style */
+# define sub_style(A) 2*((A)/4)+script_style+cramped /* smaller and cramped */
+# define sup_style(A) 2*((A)/4)+script_style+((A)%2) /* smaller */
+# define num_style(A) (A)+2-2*((A)/6) /* smaller unless already script-script */
+# define denom_style(A) 2*((A)/2)+cramped+2-2*((A)/6) /* smaller, cramped */
+# define sup_sup_style(A) sup_style(sup_style((A))) /* smaller */
+
+void setup_math_style(void);
+void print_math_style(void);
+
+# define text_size 0
+# define script_size 1
+# define script_script_size 2
+
+# define dir_math_save cur_list.math_field
+# define m_style cur_list.math_style_field
+# define init_math_fields() do { \
+ dir_math_save=false; \
+ m_style=-1; \
+ } while (0)
+
+
+# define null_font 0
+# define min_quarterword 0
+
+# define undefined_math_parameter max_dimen
+
+typedef enum {
+ math_param_quad,
+ math_param_axis,
+ math_param_operator_size,
+ math_param_overbar_kern,
+ math_param_overbar_rule,
+ math_param_overbar_vgap,
+ math_param_underbar_kern,
+ math_param_underbar_rule,
+ math_param_underbar_vgap,
+ math_param_radical_kern,
+ math_param_radical_rule,
+ math_param_radical_vgap,
+ math_param_radical_degree_before,
+ math_param_radical_degree_after,
+ math_param_radical_degree_raise,
+ math_param_stack_vgap,
+ math_param_stack_num_up,
+ math_param_stack_denom_down,
+ math_param_fraction_rule,
+ math_param_fraction_num_vgap,
+ math_param_fraction_num_up,
+ math_param_fraction_denom_vgap,
+ math_param_fraction_denom_down,
+ math_param_fraction_del_size,
+ math_param_skewed_fraction_hgap,
+ math_param_skewed_fraction_vgap,
+ math_param_limit_above_vgap,
+ math_param_limit_above_bgap,
+ math_param_limit_above_kern,
+ math_param_limit_below_vgap,
+ math_param_limit_below_bgap,
+ math_param_limit_below_kern,
+ math_param_nolimit_sub_factor, /* bonus */
+ math_param_nolimit_sup_factor, /* bonus */
+ math_param_under_delimiter_vgap,
+ math_param_under_delimiter_bgap,
+ math_param_over_delimiter_vgap,
+ math_param_over_delimiter_bgap,
+ math_param_sub_shift_drop,
+ math_param_sup_shift_drop,
+ math_param_sub_shift_down,
+ math_param_sub_sup_shift_down,
+ math_param_sub_top_max,
+ math_param_sup_shift_up,
+ math_param_sup_bottom_min,
+ math_param_sup_sub_bottom_max,
+ math_param_subsup_vgap,
+ math_param_space_after_script,
+ math_param_connector_overlap_min,
+ math_param_ord_ord_spacing,
+ math_param_ord_op_spacing,
+ math_param_ord_bin_spacing,
+ math_param_ord_rel_spacing,
+ math_param_ord_open_spacing,
+ math_param_ord_close_spacing,
+ math_param_ord_punct_spacing,
+ math_param_ord_inner_spacing,
+ math_param_op_ord_spacing,
+ math_param_op_op_spacing,
+ math_param_op_bin_spacing,
+ math_param_op_rel_spacing,
+ math_param_op_open_spacing,
+ math_param_op_close_spacing,
+ math_param_op_punct_spacing,
+ math_param_op_inner_spacing,
+ math_param_bin_ord_spacing,
+ math_param_bin_op_spacing,
+ math_param_bin_bin_spacing,
+ math_param_bin_rel_spacing,
+ math_param_bin_open_spacing,
+ math_param_bin_close_spacing,
+ math_param_bin_punct_spacing,
+ math_param_bin_inner_spacing,
+ math_param_rel_ord_spacing,
+ math_param_rel_op_spacing,
+ math_param_rel_bin_spacing,
+ math_param_rel_rel_spacing,
+ math_param_rel_open_spacing,
+ math_param_rel_close_spacing,
+ math_param_rel_punct_spacing,
+ math_param_rel_inner_spacing,
+ math_param_open_ord_spacing,
+ math_param_open_op_spacing,
+ math_param_open_bin_spacing,
+ math_param_open_rel_spacing,
+ math_param_open_open_spacing,
+ math_param_open_close_spacing,
+ math_param_open_punct_spacing,
+ math_param_open_inner_spacing,
+ math_param_close_ord_spacing,
+ math_param_close_op_spacing,
+ math_param_close_bin_spacing,
+ math_param_close_rel_spacing,
+ math_param_close_open_spacing,
+ math_param_close_close_spacing,
+ math_param_close_punct_spacing,
+ math_param_close_inner_spacing,
+ math_param_punct_ord_spacing,
+ math_param_punct_op_spacing,
+ math_param_punct_bin_spacing,
+ math_param_punct_rel_spacing,
+ math_param_punct_open_spacing,
+ math_param_punct_close_spacing,
+ math_param_punct_punct_spacing,
+ math_param_punct_inner_spacing,
+ math_param_inner_ord_spacing,
+ math_param_inner_op_spacing,
+ math_param_inner_bin_spacing,
+ math_param_inner_rel_spacing,
+ math_param_inner_open_spacing,
+ math_param_inner_close_spacing,
+ math_param_inner_punct_spacing,
+ math_param_inner_inner_spacing,
+ math_param_last
+} math_parameters;
+
+extern const char *math_param_names[];
+
+# define math_param_first_mu_glue math_param_ord_ord_spacing
+
+extern void def_math_param(int param_code, int style_code, scaled value,
+ int lvl);
+extern scaled get_math_param(int param_code, int style_code);
+
+
+typedef enum {
+ ScriptPercentScaleDown = 1,
+ ScriptScriptPercentScaleDown,
+ DelimitedSubFormulaMinHeight,
+ DisplayOperatorMinHeight,
+ MathLeading,
+ AxisHeight,
+ AccentBaseHeight,
+ FlattenedAccentBaseHeight,
+ SubscriptShiftDown,
+ SubscriptTopMax,
+ SubscriptBaselineDropMin,
+ SuperscriptShiftUp,
+ SuperscriptShiftUpCramped,
+ SuperscriptBottomMin,
+ SuperscriptBaselineDropMax,
+ SubSuperscriptGapMin,
+ SuperscriptBottomMaxWithSubscript,
+ SpaceAfterScript,
+ UpperLimitGapMin,
+ UpperLimitBaselineRiseMin,
+ LowerLimitGapMin,
+ LowerLimitBaselineDropMin,
+ StackTopShiftUp,
+ StackTopDisplayStyleShiftUp,
+ StackBottomShiftDown,
+ StackBottomDisplayStyleShiftDown,
+ StackGapMin,
+ StackDisplayStyleGapMin,
+ StretchStackTopShiftUp,
+ StretchStackBottomShiftDown,
+ StretchStackGapAboveMin,
+ StretchStackGapBelowMin,
+ FractionNumeratorShiftUp,
+ FractionNumeratorDisplayStyleShiftUp,
+ FractionDenominatorShiftDown,
+ FractionDenominatorDisplayStyleShiftDown,
+ FractionNumeratorGapMin,
+ FractionNumeratorDisplayStyleGapMin,
+ FractionRuleThickness,
+ FractionDenominatorGapMin,
+ FractionDenominatorDisplayStyleGapMin,
+ SkewedFractionHorizontalGap,
+ SkewedFractionVerticalGap,
+ OverbarVerticalGap,
+ OverbarRuleThickness,
+ OverbarExtraAscender,
+ UnderbarVerticalGap,
+ UnderbarRuleThickness,
+ UnderbarExtraDescender,
+ RadicalVerticalGap,
+ RadicalDisplayStyleVerticalGap,
+ RadicalRuleThickness,
+ RadicalExtraAscender,
+ RadicalKernBeforeDegree,
+ RadicalKernAfterDegree,
+ RadicalDegreeBottomRaisePercent,
+ MinConnectorOverlap,
+ SubscriptShiftDownWithSuperscript,
+ FractionDelimiterSize,
+ FractionDelimiterDisplayStyleSize,
+ NoLimitSubFactor,
+ NoLimitSupFactor,
+ MATH_param_last,
+} MATH_param_codes;
+
+# define MATH_param_max MATH_param_last
+
+extern const char *MATH_param_names[];
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.c b/Build/source/texk/web2c/luatexdir/tex/texnodes.c
new file mode 100644
index 00000000000..02e117a876b
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.c
@@ -0,0 +1,4840 @@
+/*
+
+Copyright 2006-2010 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation;
+either version 2 of the License, or (at your option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+/*tex
+
+ This module started out using NDEBUG to trigger checking invalid node usage,
+ something that is needed because users can mess up nodes in Lua. At some
+ point that code was always enabled so it is now always on but still can be
+ recognized as additional code. And as the performance hit is close to zero so
+ disabling makes no sense, not even to make it configureable. There is a
+ little more memory used but that is neglectable compared to other memory
+ usage.
+
+*/
+
+#define MAX_CHAIN_SIZE 13 /* why not a bit larger */
+#define CHECK_NODE_USAGE 1 /* this triggers checking */
+
+memory_word *volatile varmem = NULL;
+
+#ifdef CHECK_NODE_USAGE
+ char *varmem_sizes = NULL;
+#endif
+
+halfword var_mem_max = 0;
+halfword rover = 0;
+
+halfword free_chain[MAX_CHAIN_SIZE] = { null };
+
+static int my_prealloc = 0;
+
+/*tex Used in font and lang: */
+
+int fix_node_lists = 1;
+
+/*tex Defined below. */
+
+halfword slow_get_node(int s);
+
+#define fake_node 100
+#define fake_node_size 2
+#define fake_node_name "fake"
+
+#define variable_node_size 2
+
+/*tex
+
+ The following definitions are used for keys at the \LUA\ end and
+ provide an efficient way to share hashed strings.
+
+*/
+
+# define init_node_key(target,n,key) \
+ target[n].lua = luaS_##key##_index; \
+ target[n].name = luaS_##key##_ptr;
+
+# define init_field_key(target,n,key) \
+ target[n].lua = luaS_##key##_index; \
+ target[n].name = luaS_##key##_ptr;
+
+# define init_field_nop(target,n) \
+ target[n].lua = 0; \
+ target[n].name = NULL;
+
+/*tex The fields of nodes. */
+
+field_info node_fields_accent[10];
+field_info node_fields_adjust[3];
+field_info node_fields_attribute[3];
+field_info node_fields_attribute_list[1];
+field_info node_fields_boundary[3];
+field_info node_fields_choice[6];
+field_info node_fields_delim[6];
+field_info node_fields_dir[4];
+field_info node_fields_disc[6];
+field_info node_fields_fence[8];
+field_info node_fields_fraction[10];
+field_info node_fields_glue[8];
+field_info node_fields_glue_spec[6];
+field_info node_fields_glyph[16];
+field_info node_fields_insert[7];
+field_info node_fields_inserting[9];
+field_info node_fields_kern[4];
+field_info node_fields_list[11];
+field_info node_fields_local_par[9];
+field_info node_fields_margin_kern[4];
+field_info node_fields_mark[4];
+field_info node_fields_math[8];
+field_info node_fields_math_char[4];
+field_info node_fields_math_text_char[4];
+field_info node_fields_noad[5];
+field_info node_fields_penalty[3];
+field_info node_fields_radical[9];
+field_info node_fields_rule[9];
+field_info node_fields_splitup[6];
+field_info node_fields_style[3];
+field_info node_fields_sub_box[3];
+field_info node_fields_sub_mlist[3];
+field_info node_fields_unset[12];
+
+/*tex The fields of whatsit nodes. */
+
+field_info node_fields_whatsit_close[3];
+field_info node_fields_whatsit_late_lua[6];
+field_info node_fields_whatsit_open[6];
+field_info node_fields_whatsit_save_pos[2];
+field_info node_fields_whatsit_special[3];
+field_info node_fields_whatsit_user_defined[5];
+field_info node_fields_whatsit_write[4];
+
+field_info node_fields_whatsit_pdf_action[7];
+field_info node_fields_whatsit_pdf_annot[7];
+field_info node_fields_whatsit_pdf_colorstack[5];
+field_info node_fields_whatsit_pdf_dest[10];
+field_info node_fields_whatsit_pdf_end_link[2];
+field_info node_fields_whatsit_pdf_end_thread[2];
+field_info node_fields_whatsit_pdf_literal[4];
+field_info node_fields_whatsit_pdf_refobj[3];
+field_info node_fields_whatsit_pdf_restore[2];
+field_info node_fields_whatsit_pdf_link_state[3];
+field_info node_fields_whatsit_pdf_save[2];
+field_info node_fields_whatsit_pdf_setmatrix[3];
+field_info node_fields_whatsit_pdf_start_link[8];
+field_info node_fields_whatsit_pdf_start_thread[8];
+field_info node_fields_whatsit_pdf_thread[8];
+
+
+/*tex The values of fields. */
+
+subtype_info node_values_dir[] = {
+ { 0, NULL, 0 },
+ { 1, NULL, 0 },
+ { 2, NULL, 0 },
+ { 3, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_values_pdf_destination[] = {
+ { 0, NULL, 0 },
+ { 1, NULL, 0 },
+ { 2, NULL, 0 },
+ { 3, NULL, 0 },
+ { 4, NULL, 0 },
+ { 5, NULL, 0 },
+ { 6, NULL, 0 },
+ { 7, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_values_pdf_action[] = {
+ { 0, NULL, 0 },
+ { 1, NULL, 0 },
+ { 2, NULL, 0 },
+ { 3, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_values_pdf_window[] = {
+ { 0, NULL, 0 },
+ { 1, NULL, 0 },
+ { 2, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_values_color_stack[] = {
+ { 0, NULL, 0 },
+ { 1, NULL, 0 },
+ { 2, NULL, 0 },
+ { 3, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_values_fill[] = {
+ { normal, NULL, 0 },
+ { sfi, NULL, 0 },
+ { fil, NULL, 0 },
+ { fill, NULL, 0 },
+ { filll, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_values_pdf_literal[] = {
+ { set_origin, NULL, 0 },
+ { direct_page, NULL, 0 },
+ { direct_always, NULL, 0 },
+ { direct_raw, NULL, 0 },
+ { direct_text, NULL, 0 },
+ { direct_font, NULL, 0 },
+ { scan_special, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info other_values_page_states[] = {
+ { 0, NULL, 0 },
+ { 1, NULL, 0 },
+ { 2, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+/*tex The subtypes of nodes (most have one). */
+
+subtype_info node_subtypes_dir[] = {
+ { normal_dir, NULL, 0 },
+ { cancel_dir, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_glue[] = {
+ { user_skip_glue, NULL, 0 },
+ { line_skip_glue, NULL, 0 },
+ { baseline_skip_glue, NULL, 0 },
+ { par_skip_glue, NULL, 0 },
+ { above_display_skip_glue, NULL, 0 },
+ { below_display_skip_glue, NULL, 0 },
+ { above_display_short_skip_glue, NULL, 0 },
+ { below_display_short_skip_glue, NULL, 0 },
+ { left_skip_glue, NULL, 0 },
+ { right_skip_glue, NULL, 0 },
+ { top_skip_glue, NULL, 0 },
+ { split_top_skip_glue, NULL, 0 },
+ { tab_skip_glue, NULL, 0 },
+ { space_skip_glue, NULL, 0 },
+ { xspace_skip_glue, NULL, 0 },
+ { par_fill_skip_glue, NULL, 0 },
+ { math_skip_glue, NULL, 0 },
+ { thin_mu_skip_glue, NULL, 0 },
+ { med_mu_skip_glue, NULL, 0 },
+ { thick_mu_skip_glue, NULL, 0 },
+ /* math */
+ { cond_math_glue, NULL, 0 },
+ { mu_glue, NULL, 0 },
+ /* leaders */
+ { a_leaders, NULL, 0 },
+ { c_leaders, NULL, 0 },
+ { x_leaders, NULL, 0 },
+ { g_leaders, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+/*tex
+
+ Math glue and leaders have special numbers. At some point we might decide to
+ move them down so best don't use hard coded numbers!
+
+*/
+
+subtype_info node_subtypes_mathglue[] = { /* 98+ */
+ { cond_math_glue, NULL, 0 },
+ { mu_glue, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_leader[] = { /* 100+ */
+ { a_leaders, NULL, 0 },
+ { c_leaders, NULL, 0 },
+ { x_leaders, NULL, 0 },
+ { g_leaders, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_boundary[] = {
+ { cancel_boundary, NULL, 0 },
+ { user_boundary, NULL, 0 },
+ { protrusion_boundary, NULL, 0 },
+ { word_boundary, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_penalty[] = {
+ { user_penalty, NULL, 0 },
+ { linebreak_penalty, NULL, 0 },
+ { line_penalty, NULL, 0 },
+ { word_penalty, NULL, 0 },
+ { final_penalty, NULL, 0 },
+ { noad_penalty, NULL, 0 },
+ { before_display_penalty, NULL, 0 },
+ { after_display_penalty, NULL, 0 },
+ { equation_number_penalty, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_kern[] = {
+ { font_kern, NULL, 0 },
+ { explicit_kern, NULL, 0 },
+ { accent_kern, NULL, 0 },
+ { italic_kern, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_rule[] = {
+ { normal_rule, NULL, 0 },
+ { box_rule, NULL, 0 },
+ { image_rule, NULL, 0 },
+ { empty_rule, NULL, 0 },
+ { user_rule, NULL, 0 },
+ { math_over_rule, NULL, 0 },
+ { math_under_rule, NULL, 0 },
+ { math_fraction_rule, NULL, 0 },
+ { math_radical_rule, NULL, 0 },
+ { outline_rule, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_glyph[] = {
+ { glyph_unset, NULL, 0 },
+ { glyph_character, NULL, 0 },
+ { glyph_ligature, NULL, 0 },
+ { glyph_ghost, NULL, 0 },
+ { glyph_left, NULL, 0 },
+ { glyph_right, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_disc[] = {
+ { discretionary_disc, NULL, 0 },
+ { explicit_disc, NULL, 0 },
+ { automatic_disc, NULL, 0 },
+ { syllable_disc, NULL, 0 },
+ { init_disc, NULL, 0 },
+ { select_disc, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_marginkern[] = {
+ { left_side, NULL, 0 },
+ { right_side, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_list[] = {
+ { unknown_list, NULL, 0 },
+ { line_list, NULL, 0 },
+ { hbox_list, NULL, 0 },
+ { indent_list, NULL, 0 },
+ { align_row_list, NULL, 0 },
+ { align_cell_list, NULL, 0 },
+ { equation_list, NULL, 0 },
+ { equation_number_list, NULL, 0 },
+ { math_list_list, NULL, 0 },
+ { math_char_list, NULL, 0 },
+ { math_h_extensible_list, NULL, 0 },
+ { math_v_extensible_list, NULL, 0 },
+ { math_h_delimiter_list, NULL, 0 },
+ { math_v_delimiter_list, NULL, 0 },
+ { math_over_delimiter_list, NULL, 0 },
+ { math_under_delimiter_list, NULL, 0 },
+ { math_numerator_list, NULL, 0 },
+ { math_denominator_list, NULL, 0 },
+ { math_limits_list, NULL, 0 },
+ { math_fraction_list, NULL, 0 },
+ { math_nucleus_list, NULL, 0 },
+ { math_sup_list, NULL, 0 },
+ { math_sub_list, NULL, 0 },
+ { math_degree_list, NULL, 0 },
+ { math_scripts_list, NULL, 0 },
+ { math_over_list, NULL, 0 },
+ { math_under_list, NULL, 0 },
+ { math_accent_list, NULL, 0 },
+ { math_radical_list, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_adjust[] = {
+ { 0, NULL, 0 },
+ { 1, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_math[] = {
+ { before, NULL, 0 },
+ { after, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_noad[] = {
+ { ord_noad_type, NULL, 0 },
+ { op_noad_type_normal, NULL, 0 },
+ { op_noad_type_limits, NULL, 0 },
+ { op_noad_type_no_limits, NULL, 0 },
+ { bin_noad_type, NULL, 0 },
+ { rel_noad_type, NULL, 0 },
+ { open_noad_type, NULL, 0 },
+ { close_noad_type, NULL, 0 },
+ { punct_noad_type, NULL, 0 },
+ { inner_noad_type, NULL, 0 },
+ { under_noad_type, NULL, 0 },
+ { over_noad_type, NULL, 0 },
+ { vcenter_noad_type, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_radical[] = {
+ { radical_noad_type, NULL, 0 },
+ { uradical_noad_type, NULL, 0 },
+ { uroot_noad_type, NULL, 0 },
+ { uunderdelimiter_noad_type, NULL, 0 },
+ { uoverdelimiter_noad_type, NULL, 0 },
+ { udelimiterunder_noad_type, NULL, 0 },
+ { udelimiterover_noad_type, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_accent[] = {
+ { bothflexible_accent, NULL, 0 },
+ { fixedtop_accent, NULL, 0 },
+ { fixedbottom_accent, NULL, 0 },
+ { fixedboth_accent, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+subtype_info node_subtypes_fence[] = {
+ { unset_noad_side, NULL, 0 },
+ { left_noad_side, NULL, 0 },
+ { middle_noad_side, NULL, 0 },
+ { right_noad_side, NULL, 0 },
+ { no_noad_side, NULL, 0 },
+ { -1, NULL, 0 },
+};
+
+/*tes This brings all together */
+
+node_info node_data[] = {
+ { hlist_node, box_node_size, node_subtypes_list, node_fields_list, NULL, 1, 0 },
+ { vlist_node, box_node_size, node_subtypes_list, node_fields_list, NULL, 2, 0 },
+ { rule_node, rule_node_size, node_subtypes_rule, node_fields_rule, NULL, 3, 0 },
+ { ins_node, ins_node_size, NULL, node_fields_insert, NULL, 4, 0 },
+ { mark_node, mark_node_size, NULL, node_fields_mark, NULL, 5, 0 },
+ { adjust_node, adjust_node_size, node_subtypes_adjust, node_fields_adjust, NULL, 6, 0 },
+ { boundary_node, boundary_node_size, node_subtypes_boundary, node_fields_boundary, NULL, -1, 0 },
+ { disc_node, disc_node_size, node_subtypes_disc, node_fields_disc, NULL, 8, 0 },
+ { whatsit_node, -1, NULL, NULL, NULL, 9, 0 },
+ { local_par_node, local_par_size, NULL, node_fields_local_par, NULL, -1, 0 },
+ { dir_node, dir_node_size, node_subtypes_dir, node_fields_dir, NULL, -1, 0 },
+ { math_node, math_node_size, node_subtypes_math, node_fields_math, NULL, 10, 0 },
+ { glue_node, glue_node_size, node_subtypes_glue, node_fields_glue, NULL, 11, 0 },
+ { kern_node, kern_node_size, node_subtypes_kern, node_fields_kern, NULL, 12, 0 },
+ { penalty_node, penalty_node_size, node_subtypes_penalty, node_fields_penalty, NULL, 13, 0 },
+ { unset_node, box_node_size, NULL, node_fields_unset, NULL, 14, 0 },
+ { style_node, style_node_size, NULL, node_fields_style, NULL, 15, 0 },
+ { choice_node, style_node_size, NULL, node_fields_choice, NULL, 15, 0 },
+ { simple_noad, noad_size, node_subtypes_noad, node_fields_noad, NULL, 15, 0 },
+ { radical_noad, radical_noad_size, node_subtypes_radical, node_fields_radical, NULL, 15, 0 },
+ { fraction_noad, fraction_noad_size, NULL, node_fields_fraction, NULL, 15, 0 },
+ { accent_noad, accent_noad_size, node_subtypes_accent, node_fields_accent, NULL, 15, 0 },
+ { fence_noad, fence_noad_size, node_subtypes_fence, node_fields_fence, NULL, 15, 0 },
+ { math_char_node, math_kernel_node_size, NULL, node_fields_math_char, NULL, 15, 0 },
+ { sub_box_node, math_kernel_node_size, NULL, node_fields_sub_box, NULL, 15, 0 },
+ { sub_mlist_node, math_kernel_node_size, NULL, node_fields_sub_mlist, NULL, 15, 0 },
+ { math_text_char_node, math_kernel_node_size, NULL, node_fields_math_text_char, NULL, 15, 0 },
+ { delim_node, math_shield_node_size, NULL, node_fields_delim, NULL, 15, 0 },
+ { margin_kern_node, margin_kern_node_size, node_subtypes_marginkern, node_fields_margin_kern, NULL, -1, 0 },
+ { glyph_node, glyph_node_size, node_subtypes_glyph, node_fields_glyph, NULL, 0, 0 },
+ { align_record_node, box_node_size, NULL, NULL, NULL, -1, 0 },
+ { pseudo_file_node, pseudo_file_node_size, NULL, NULL, NULL, -1, 0 },
+ { pseudo_line_node, variable_node_size, NULL, NULL, NULL, -1, 0 },
+ { inserting_node, page_ins_node_size, NULL, node_fields_inserting, NULL, -1, 0 },
+ { split_up_node, page_ins_node_size, NULL, node_fields_splitup, NULL, -1, 0 },
+ { expr_node, expr_node_size, NULL, NULL, NULL, -1, 0 },
+ { nesting_node, nesting_node_size, NULL, NULL, NULL, -1, 0 },
+ { span_node, span_node_size, NULL, NULL, NULL, -1, 0 },
+ { attribute_node, attribute_node_size, NULL, node_fields_attribute, NULL, -1, 0 },
+ { glue_spec_node, glue_spec_size, NULL, node_fields_glue_spec, NULL, -1, 0 },
+ { attribute_list_node, attribute_node_size, NULL, node_fields_attribute_list, NULL, -1, 0 },
+ { temp_node, temp_node_size, NULL, NULL, NULL, -1, 0 },
+ { align_stack_node, align_stack_node_size, NULL, NULL, NULL, -1, 0 },
+ { movement_node, movement_node_size, NULL, NULL, NULL, -1, 0 },
+ { if_node, if_node_size, NULL, NULL, NULL, -1, 0 },
+ { unhyphenated_node, active_node_size, NULL, NULL, NULL, -1, 0 },
+ { hyphenated_node, active_node_size, NULL, NULL, NULL, -1, 0 },
+ { delta_node, delta_node_size, NULL, NULL, NULL, -1, 0 },
+ { passive_node, passive_node_size, NULL, NULL, NULL, -1, 0 },
+ { shape_node, variable_node_size, NULL, NULL, NULL, -1, 0 },
+ { -1, -1, NULL, NULL, NULL, -1, 0 }
+};
+
+void l_set_node_data(void) {
+ init_node_key(node_data, hlist_node, hlist)
+ init_node_key(node_data, vlist_node, vlist)
+ init_node_key(node_data, rule_node, rule)
+ init_node_key(node_data, ins_node, ins)
+ init_node_key(node_data, mark_node, mark)
+ init_node_key(node_data, adjust_node, adjust)
+ init_node_key(node_data, boundary_node, boundary)
+ init_node_key(node_data, disc_node, disc)
+ init_node_key(node_data, whatsit_node, whatsit)
+ init_node_key(node_data, local_par_node, local_par)
+ init_node_key(node_data, dir_node, dir)
+ init_node_key(node_data, math_node, math)
+ init_node_key(node_data, glue_node, glue)
+ init_node_key(node_data, kern_node, kern)
+ init_node_key(node_data, penalty_node, penalty)
+ init_node_key(node_data, unset_node, unset)
+ init_node_key(node_data, style_node, style)
+ init_node_key(node_data, choice_node, choice)
+ init_node_key(node_data, simple_noad, noad)
+ init_node_key(node_data, radical_noad, radical)
+ init_node_key(node_data, fraction_noad, fraction)
+ init_node_key(node_data, accent_noad, accent)
+ init_node_key(node_data, fence_noad, fence)
+ init_node_key(node_data, math_char_node, math_char)
+ init_node_key(node_data, sub_box_node, sub_box)
+ init_node_key(node_data, sub_mlist_node, sub_mlist)
+ init_node_key(node_data, math_text_char_node, math_text_char)
+ init_node_key(node_data, delim_node, delim)
+ init_node_key(node_data, margin_kern_node, margin_kern)
+ init_node_key(node_data, glyph_node, glyph)
+ init_node_key(node_data, align_record_node, align_record)
+ init_node_key(node_data, pseudo_file_node, pseudo_file)
+ init_node_key(node_data, pseudo_line_node, pseudo_line)
+ init_node_key(node_data, inserting_node, page_insert)
+ init_node_key(node_data, split_up_node, split_insert)
+ init_node_key(node_data, expr_node, expr_stack)
+ init_node_key(node_data, nesting_node, nested_list)
+ init_node_key(node_data, span_node, span)
+ init_node_key(node_data, attribute_node, attribute)
+ init_node_key(node_data, glue_spec_node, glue_spec)
+ init_node_key(node_data, attribute_list_node, attribute_list)
+ init_node_key(node_data, temp_node, temp)
+ init_node_key(node_data, align_stack_node, align_stack)
+ init_node_key(node_data, movement_node, movement_stack)
+ init_node_key(node_data, if_node, if_stack)
+ init_node_key(node_data, unhyphenated_node, unhyphenated)
+ init_node_key(node_data, hyphenated_node, hyphenated)
+ init_node_key(node_data, delta_node, delta)
+ init_node_key(node_data, passive_node, passive)
+ init_node_key(node_data, shape_node, shape)
+
+ init_node_key(node_subtypes_dir, normal_dir, normal)
+ init_node_key(node_subtypes_dir, cancel_dir, cancel)
+
+ init_node_key(node_subtypes_glue, user_skip_glue, userskip)
+ init_node_key(node_subtypes_glue, line_skip_glue, lineskip)
+ init_node_key(node_subtypes_glue, baseline_skip_glue, baselineskip)
+ init_node_key(node_subtypes_glue, par_skip_glue, parskip)
+ init_node_key(node_subtypes_glue, above_display_skip_glue, abovedisplayskip)
+ init_node_key(node_subtypes_glue, below_display_skip_glue, belowdisplayskip)
+ init_node_key(node_subtypes_glue, above_display_short_skip_glue, abovedisplayshortskip)
+ init_node_key(node_subtypes_glue, below_display_short_skip_glue, belowdisplayshortskip)
+ init_node_key(node_subtypes_glue, left_skip_glue, leftskip)
+ init_node_key(node_subtypes_glue, right_skip_glue, rightskip)
+ init_node_key(node_subtypes_glue, top_skip_glue, topskip)
+ init_node_key(node_subtypes_glue, split_top_skip_glue, splittopskip)
+ init_node_key(node_subtypes_glue, tab_skip_glue, tabskip)
+ init_node_key(node_subtypes_glue, space_skip_glue, spaceskip)
+ init_node_key(node_subtypes_glue, xspace_skip_glue, xspaceskip)
+ init_node_key(node_subtypes_glue, par_fill_skip_glue, parfillskip)
+ init_node_key(node_subtypes_glue, math_skip_glue, mathskip)
+ init_node_key(node_subtypes_glue, thin_mu_skip_glue, thinmuskip)
+ init_node_key(node_subtypes_glue, med_mu_skip_glue, medmuskip)
+ init_node_key(node_subtypes_glue, thick_mu_skip_glue, thickmuskip)
+ init_node_key(node_subtypes_glue, thick_mu_skip_glue + 1, conditionalmathskip)
+ init_node_key(node_subtypes_glue, thick_mu_skip_glue + 2, muglue)
+ init_node_key(node_subtypes_glue, thick_mu_skip_glue + 3, leaders)
+ init_node_key(node_subtypes_glue, thick_mu_skip_glue + 4, cleaders)
+ init_node_key(node_subtypes_glue, thick_mu_skip_glue + 5, xleaders)
+ init_node_key(node_subtypes_glue, thick_mu_skip_glue + 6, gleaders)
+
+ init_node_key(node_subtypes_mathglue, 0, conditionalmathskip)
+ init_node_key(node_subtypes_mathglue, 1, muglue)
+
+ init_node_key(node_subtypes_leader, 0, leaders)
+ init_node_key(node_subtypes_leader, 1, cleaders)
+ init_node_key(node_subtypes_leader, 2, xleaders)
+ init_node_key(node_subtypes_leader, 3, gleaders)
+
+ init_node_key(node_subtypes_boundary, cancel_boundary, cancel)
+ init_node_key(node_subtypes_boundary, user_boundary, user)
+ init_node_key(node_subtypes_boundary, protrusion_boundary, protrusion)
+ init_node_key(node_subtypes_boundary, word_boundary, word)
+
+ init_node_key(node_subtypes_penalty, user_penalty, userpenalty)
+ init_node_key(node_subtypes_penalty, linebreak_penalty, linebreakpenalty)
+ init_node_key(node_subtypes_penalty, line_penalty, linepenalty)
+ init_node_key(node_subtypes_penalty, word_penalty, wordpenalty)
+ init_node_key(node_subtypes_penalty, final_penalty, finalpenalty)
+ init_node_key(node_subtypes_penalty, noad_penalty, noadpenalty)
+ init_node_key(node_subtypes_penalty, before_display_penalty, beforedisplaypenalty)
+ init_node_key(node_subtypes_penalty, after_display_penalty, afterdisplaypenalty)
+ init_node_key(node_subtypes_penalty, equation_number_penalty, equationnumberpenalty)
+
+ init_node_key(node_subtypes_kern, font_kern, fontkern)
+ init_node_key(node_subtypes_kern, explicit_kern, userkern)
+ init_node_key(node_subtypes_kern, accent_kern, accentkern)
+ init_node_key(node_subtypes_kern, italic_kern, italiccorrection)
+
+ init_node_key(node_subtypes_rule, normal_rule, normal)
+ init_node_key(node_subtypes_rule, box_rule, box)
+ init_node_key(node_subtypes_rule, image_rule, image)
+ init_node_key(node_subtypes_rule, empty_rule, empty)
+ init_node_key(node_subtypes_rule, user_rule, user)
+ init_node_key(node_subtypes_rule, math_over_rule, over)
+ init_node_key(node_subtypes_rule, math_under_rule, under)
+ init_node_key(node_subtypes_rule, math_fraction_rule, fraction)
+ init_node_key(node_subtypes_rule, math_radical_rule, radical)
+ init_node_key(node_subtypes_rule, outline_rule, outline)
+
+ init_node_key(node_subtypes_glyph, 0, unset)
+ init_node_key(node_subtypes_glyph, 1, character)
+ init_node_key(node_subtypes_glyph, 2, ligature)
+ init_node_key(node_subtypes_glyph, 3, ghost)
+ init_node_key(node_subtypes_glyph, 4, left)
+ init_node_key(node_subtypes_glyph, 5, right)
+
+ init_node_key(node_subtypes_disc, discretionary_disc, discretionary)
+ init_node_key(node_subtypes_disc, explicit_disc, explicit)
+ init_node_key(node_subtypes_disc, automatic_disc, automatic)
+ init_node_key(node_subtypes_disc, syllable_disc, regular)
+ init_node_key(node_subtypes_disc, init_disc, first)
+ init_node_key(node_subtypes_disc, select_disc, second)
+
+ init_node_key(node_subtypes_fence, unset_noad_side, unset)
+ init_node_key(node_subtypes_fence, left_noad_side, left)
+ init_node_key(node_subtypes_fence, middle_noad_side, middle)
+ init_node_key(node_subtypes_fence, right_noad_side, right)
+ init_node_key(node_subtypes_fence, no_noad_side, no)
+
+ init_node_key(node_subtypes_list, unknown_list, unknown)
+ init_node_key(node_subtypes_list, line_list, line)
+ init_node_key(node_subtypes_list, hbox_list, box)
+ init_node_key(node_subtypes_list, indent_list, indent)
+ init_node_key(node_subtypes_list, align_row_list, alignment)
+ init_node_key(node_subtypes_list, align_cell_list, cell)
+ init_node_key(node_subtypes_list, equation_list, equation)
+ init_node_key(node_subtypes_list, equation_number_list, equationnumber)
+ init_node_key(node_subtypes_list, math_list_list, math)
+ init_node_key(node_subtypes_list, math_char_list, mathchar)
+ init_node_key(node_subtypes_list, math_h_extensible_list, hextensible)
+ init_node_key(node_subtypes_list, math_v_extensible_list, vextensible)
+ init_node_key(node_subtypes_list, math_h_delimiter_list, hdelimiter)
+ init_node_key(node_subtypes_list, math_v_delimiter_list, vdelimiter)
+ init_node_key(node_subtypes_list, math_over_delimiter_list, overdelimiter)
+ init_node_key(node_subtypes_list, math_under_delimiter_list, underdelimiter)
+ init_node_key(node_subtypes_list, math_numerator_list, numerator)
+ init_node_key(node_subtypes_list, math_denominator_list, denominator)
+ init_node_key(node_subtypes_list, math_limits_list, limits)
+ init_node_key(node_subtypes_list, math_fraction_list, fraction)
+ init_node_key(node_subtypes_list, math_nucleus_list, nucleus)
+ init_node_key(node_subtypes_list, math_sup_list, sup)
+ init_node_key(node_subtypes_list, math_sub_list, sub)
+ init_node_key(node_subtypes_list, math_degree_list, degree)
+ init_node_key(node_subtypes_list, math_scripts_list, scripts)
+ init_node_key(node_subtypes_list, math_over_list, over)
+ init_node_key(node_subtypes_list, math_under_list, under)
+ init_node_key(node_subtypes_list, math_accent_list, accent)
+ init_node_key(node_subtypes_list, math_radical_list, radical)
+
+ init_node_key(node_subtypes_math, before, beginmath)
+ init_node_key(node_subtypes_math, after, endmath)
+
+ init_node_key(node_subtypes_marginkern, left_side, left)
+ init_node_key(node_subtypes_marginkern, right_side, right)
+
+ init_node_key(node_subtypes_adjust, 0, normal)
+ init_node_key(node_subtypes_adjust, 1, pre)
+
+ init_node_key(node_subtypes_noad, ord_noad_type, ord)
+ init_node_key(node_subtypes_noad, op_noad_type_normal, opdisplaylimits)
+ init_node_key(node_subtypes_noad, op_noad_type_limits, oplimits)
+ init_node_key(node_subtypes_noad, op_noad_type_no_limits, opnolimits)
+ init_node_key(node_subtypes_noad, bin_noad_type, bin)
+ init_node_key(node_subtypes_noad, rel_noad_type, rel)
+ init_node_key(node_subtypes_noad, open_noad_type, open)
+ init_node_key(node_subtypes_noad, close_noad_type, close)
+ init_node_key(node_subtypes_noad, punct_noad_type, punct)
+ init_node_key(node_subtypes_noad, inner_noad_type, inner)
+ init_node_key(node_subtypes_noad, under_noad_type, under)
+ init_node_key(node_subtypes_noad, over_noad_type, over)
+ init_node_key(node_subtypes_noad, vcenter_noad_type, vcenter)
+
+ init_node_key(node_subtypes_radical, radical_noad_type, radical)
+ init_node_key(node_subtypes_radical, uradical_noad_type, uradical)
+ init_node_key(node_subtypes_radical, uroot_noad_type, uroot)
+ init_node_key(node_subtypes_radical, uunderdelimiter_noad_type, uunderdelimiter)
+ init_node_key(node_subtypes_radical, uoverdelimiter_noad_type, uoverdelimiter)
+ init_node_key(node_subtypes_radical, udelimiterunder_noad_type, udelimiterunder)
+ init_node_key(node_subtypes_radical, udelimiterover_noad_type, udelimiterover)
+
+ init_node_key(node_subtypes_accent, bothflexible_accent, bothflexible)
+ init_node_key(node_subtypes_accent, fixedtop_accent, fixedtop)
+ init_node_key(node_subtypes_accent, fixedbottom_accent, fixedbottom)
+ init_node_key(node_subtypes_accent, fixedboth_accent, fixedboth)
+
+ init_node_key(node_values_fill, normal, normal)
+ init_node_key(node_values_fill, sfi, fi)
+ init_node_key(node_values_fill, fil, fil)
+ init_node_key(node_values_fill, fill, fill)
+ init_node_key(node_values_fill, filll, filll)
+
+ init_node_key(node_values_dir, 0, TLT)
+ init_node_key(node_values_dir, 1, TRT)
+ init_node_key(node_values_dir, 2, LTL)
+ init_node_key(node_values_dir, 3, RTT)
+
+ init_node_key(other_values_page_states, 0, empty)
+ init_node_key(other_values_page_states, 1, box_there)
+ init_node_key(other_values_page_states, 2, inserts_only)
+
+ init_field_key(node_fields_accent, 0, attr);
+ init_field_key(node_fields_accent, 1, nucleus);
+ init_field_key(node_fields_accent, 2, sub);
+ init_field_key(node_fields_accent, 3, sup);
+ init_field_key(node_fields_accent, 4, accent);
+ init_field_key(node_fields_accent, 5, bot_accent);
+ init_field_key(node_fields_accent, 6, top_accent);
+ init_field_key(node_fields_accent, 7, overlay_accent);
+ init_field_key(node_fields_accent, 8, fraction);
+ init_field_nop(node_fields_accent, 9);
+
+ init_field_key(node_fields_adjust, 0, attr);
+ init_field_key(node_fields_adjust, 1, head);
+ init_field_nop(node_fields_adjust, 2);
+
+ init_field_key(node_fields_attribute, 0, number);
+ init_field_key(node_fields_attribute, 1, value);
+ init_field_nop(node_fields_attribute, 2);
+
+ init_field_nop(node_fields_attribute_list,0);
+
+ init_field_key(node_fields_boundary, 0, attr);
+ init_field_key(node_fields_boundary, 1, value);
+ init_field_nop(node_fields_boundary, 2);
+
+ init_field_key(node_fields_choice, 0, attr);
+ init_field_key(node_fields_choice, 1, display);
+ init_field_key(node_fields_choice, 2, text);
+ init_field_key(node_fields_choice, 3, script);
+ init_field_key(node_fields_choice, 4, scriptscript);
+ init_field_nop(node_fields_choice, 5);
+
+ init_field_key(node_fields_delim, 0, attr);
+ init_field_key(node_fields_delim, 1, small_fam);
+ init_field_key(node_fields_delim, 2, small_char);
+ init_field_key(node_fields_delim, 3, large_fam);
+ init_field_key(node_fields_delim, 4, large_char);
+ init_field_nop(node_fields_delim, 5);
+
+ init_field_key(node_fields_dir, 0, attr);
+ init_field_key(node_fields_dir, 1, dir);
+ init_field_key(node_fields_dir, 2, level);
+ init_field_nop(node_fields_dir, 3);
+
+ init_field_key(node_fields_disc, 0, attr);
+ init_field_key(node_fields_disc, 1, pre);
+ init_field_key(node_fields_disc, 2, post);
+ init_field_key(node_fields_disc, 3, replace);
+ init_field_key(node_fields_disc, 4, penalty);
+ init_field_nop(node_fields_disc, 5);
+
+ init_field_key(node_fields_fence, 0, attr);
+ init_field_key(node_fields_fence, 1, delim);
+ init_field_key(node_fields_fence, 2, italic);
+ init_field_key(node_fields_fence, 3, height);
+ init_field_key(node_fields_fence, 4, depth);
+ init_field_key(node_fields_fence, 5, options);
+ init_field_key(node_fields_fence, 6, class);
+ init_field_nop(node_fields_fence, 7);
+
+ init_field_key(node_fields_fraction, 0, attr);
+ init_field_key(node_fields_fraction, 1, width);
+ init_field_key(node_fields_fraction, 2, num);
+ init_field_key(node_fields_fraction, 3, denom);
+ init_field_key(node_fields_fraction, 4, left);
+ init_field_key(node_fields_fraction, 5, right);
+ init_field_key(node_fields_fraction, 6, middle);
+ init_field_key(node_fields_fraction, 7, fam);
+ init_field_key(node_fields_fraction, 8, options);
+ init_field_nop(node_fields_fraction, 9);
+
+ init_field_key(node_fields_glue, 0, attr);
+ init_field_key(node_fields_glue, 1, leader);
+ init_field_key(node_fields_glue, 2, width);
+ init_field_key(node_fields_glue, 3, stretch);
+ init_field_key(node_fields_glue, 4, shrink);
+ init_field_key(node_fields_glue, 5, stretch_order);
+ init_field_key(node_fields_glue, 6, shrink_order);
+ init_field_nop(node_fields_glue, 7);
+
+ init_field_key(node_fields_glue_spec, 0, width);
+ init_field_key(node_fields_glue_spec, 1, stretch);
+ init_field_key(node_fields_glue_spec, 2, shrink);
+ init_field_key(node_fields_glue_spec, 3, stretch_order);
+ init_field_key(node_fields_glue_spec, 4, shrink_order);
+ init_field_nop(node_fields_glue_spec, 5);
+
+ init_field_key(node_fields_glyph, 0, attr);
+ init_field_key(node_fields_glyph, 1, char);
+ init_field_key(node_fields_glyph, 2, font);
+ init_field_key(node_fields_glyph, 3, lang);
+ init_field_key(node_fields_glyph, 4, left);
+ init_field_key(node_fields_glyph, 5, right);
+ init_field_key(node_fields_glyph, 6, uchyph);
+ init_field_key(node_fields_glyph, 7, components);
+ init_field_key(node_fields_glyph, 8, xoffset);
+ init_field_key(node_fields_glyph, 9, yoffset);
+ init_field_key(node_fields_glyph, 10, width);
+ init_field_key(node_fields_glyph, 11, height);
+ init_field_key(node_fields_glyph, 12, depth);
+ init_field_key(node_fields_glyph, 13, expansion_factor);
+ init_field_key(node_fields_glyph, 14, data);
+ init_field_nop(node_fields_glyph, 15);
+
+ init_field_key(node_fields_insert, 0, attr);
+ init_field_key(node_fields_insert, 1, cost);
+ init_field_key(node_fields_insert, 2, depth);
+ init_field_key(node_fields_insert, 3, height);
+ init_field_key(node_fields_insert, 4, spec);
+ init_field_key(node_fields_insert, 5, head);
+ init_field_nop(node_fields_insert, 6);
+
+ init_field_key(node_fields_inserting, 0, height);
+ init_field_key(node_fields_inserting, 1, last_ins_ptr);
+ init_field_key(node_fields_inserting, 2, best_ins_ptr);
+ init_field_key(node_fields_inserting, 3, width);
+ init_field_key(node_fields_inserting, 4, stretch);
+ init_field_key(node_fields_inserting, 5, shrink);
+ init_field_key(node_fields_inserting, 6, stretch_order);
+ init_field_key(node_fields_inserting, 7, shrink_order);
+ init_field_nop(node_fields_inserting, 8);
+
+ init_field_key(node_fields_kern, 0, attr);
+ init_field_key(node_fields_kern, 1, kern);
+ init_field_key(node_fields_kern, 2, expansion_factor);
+ init_field_nop(node_fields_kern, 3);
+
+ init_field_key(node_fields_list, 0, attr);
+ init_field_key(node_fields_list, 1, width);
+ init_field_key(node_fields_list, 2, depth);
+ init_field_key(node_fields_list, 3, height);
+ init_field_key(node_fields_list, 4, dir);
+ init_field_key(node_fields_list, 5, shift);
+ init_field_key(node_fields_list, 6, glue_order);
+ init_field_key(node_fields_list, 7, glue_sign);
+ init_field_key(node_fields_list, 8, glue_set);
+ init_field_key(node_fields_list, 9, head);
+ init_field_nop(node_fields_list, 10);
+
+ init_field_key(node_fields_local_par, 0, attr);
+ init_field_key(node_fields_local_par, 1, pen_inter);
+ init_field_key(node_fields_local_par, 2, pen_broken);
+ init_field_key(node_fields_local_par, 3, dir);
+ init_field_key(node_fields_local_par, 4, box_left);
+ init_field_key(node_fields_local_par, 5, box_left_width);
+ init_field_key(node_fields_local_par, 6, box_right);
+ init_field_key(node_fields_local_par, 7, box_right_width);
+ init_field_nop(node_fields_local_par, 8);
+
+ init_field_key(node_fields_margin_kern, 0, attr);
+ init_field_key(node_fields_margin_kern, 1, width);
+ init_field_key(node_fields_margin_kern, 2, glyph);
+ init_field_nop(node_fields_margin_kern, 3);
+
+ init_field_key(node_fields_mark, 0, attr);
+ init_field_key(node_fields_mark, 1, class);
+ init_field_key(node_fields_mark, 2, mark);
+ init_field_nop(node_fields_mark, 3);
+
+ init_field_key(node_fields_math, 0, attr);
+ init_field_key(node_fields_math, 1, surround);
+ init_field_key(node_fields_math, 2, width);
+ init_field_key(node_fields_math, 3, stretch);
+ init_field_key(node_fields_math, 4, shrink);
+ init_field_key(node_fields_math, 5, stretch_order);
+ init_field_key(node_fields_math, 6, shrink_order);
+ init_field_nop(node_fields_math, 7);
+
+ init_field_key(node_fields_math_char, 0, attr);
+ init_field_key(node_fields_math_char, 1, fam);
+ init_field_key(node_fields_math_char, 2, char);
+ init_field_nop(node_fields_math_char, 3);
+
+ init_field_key(node_fields_math_text_char, 0, attr);
+ init_field_key(node_fields_math_text_char, 1, fam);
+ init_field_key(node_fields_math_text_char, 2, char);
+ init_field_nop(node_fields_math_text_char, 3);
+
+ init_field_key(node_fields_noad, 0, attr);
+ init_field_key(node_fields_noad, 1, nucleus);
+ init_field_key(node_fields_noad, 2, sub);
+ init_field_key(node_fields_noad, 3, sup);
+ init_field_nop(node_fields_noad, 4);
+
+ init_field_key(node_fields_penalty, 0, attr);
+ init_field_key(node_fields_penalty, 1, penalty);
+ init_field_nop(node_fields_penalty, 2);
+
+ init_field_key(node_fields_radical, 0, attr);
+ init_field_key(node_fields_radical, 1, nucleus);
+ init_field_key(node_fields_radical, 2, sub);
+ init_field_key(node_fields_radical, 3, sup);
+ init_field_key(node_fields_radical, 4, left);
+ init_field_key(node_fields_radical, 5, degree);
+ init_field_key(node_fields_radical, 6, width);
+ init_field_key(node_fields_radical, 7, options);
+ init_field_nop(node_fields_radical, 8);
+
+ init_field_key(node_fields_rule, 0, attr);
+ init_field_key(node_fields_rule, 1, width);
+ init_field_key(node_fields_rule, 2, depth);
+ init_field_key(node_fields_rule, 3, height);
+ init_field_key(node_fields_rule, 4, dir);
+ init_field_key(node_fields_rule, 5, index);
+ init_field_key(node_fields_rule, 6, left);
+ init_field_key(node_fields_rule, 7, right);
+ init_field_nop(node_fields_rule, 8);
+
+ init_field_key(node_fields_splitup, 0, height);
+ init_field_key(node_fields_splitup, 1, last_ins_ptr);
+ init_field_key(node_fields_splitup, 2, best_ins_ptr);
+ init_field_key(node_fields_splitup, 3, broken_ptr);
+ init_field_key(node_fields_splitup, 4, broken_ins);
+ init_field_nop(node_fields_splitup, 5);
+
+ init_field_key(node_fields_style, 0, attr);
+ init_field_key(node_fields_style, 1, style);
+ init_field_nop(node_fields_style, 2);
+
+ init_field_key(node_fields_sub_box, 0, attr);
+ init_field_key(node_fields_sub_box, 1, head);
+ init_field_nop(node_fields_sub_box, 2);
+
+ init_field_key(node_fields_sub_mlist, 0, attr);
+ init_field_key(node_fields_sub_mlist, 1, head);
+ init_field_nop(node_fields_sub_mlist, 2);
+
+ init_field_key(node_fields_unset, 0, attr);
+ init_field_key(node_fields_unset, 1, width);
+ init_field_key(node_fields_unset, 2, depth);
+ init_field_key(node_fields_unset, 3, height);
+ init_field_key(node_fields_unset, 4, dir);
+ init_field_key(node_fields_unset, 5, shrink);
+ init_field_key(node_fields_unset, 6, glue_order);
+ init_field_key(node_fields_unset, 7, glue_sign);
+ init_field_key(node_fields_unset, 8, stretch);
+ init_field_key(node_fields_unset, 9, span);
+ init_field_key(node_fields_unset, 10, head);
+ init_field_nop(node_fields_unset, 11);
+
+}
+
+node_info whatsit_node_data[] = {
+
+ /*tex These are always there. The fake nodes are historical. */
+
+ { open_node, open_node_size, NULL, node_fields_whatsit_open, NULL, -1, 0 },
+ { write_node, write_node_size, NULL, node_fields_whatsit_write, NULL, -1, 0 },
+ { close_node, close_node_size, NULL, node_fields_whatsit_close, NULL, -1, 0 },
+ { special_node, special_node_size, NULL, node_fields_whatsit_special, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+ { save_pos_node, save_pos_node_size, NULL, node_fields_whatsit_save_pos, NULL, -1, 0 },
+ { late_lua_node, late_lua_node_size, NULL, node_fields_whatsit_late_lua, NULL, -1, 0 },
+ { user_defined_node, user_defined_node_size, NULL, node_fields_whatsit_user_defined, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+ { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
+
+ /*tex Here starts the \DVI\ backend section, todo: a separate list. */
+
+ /*tex {\em There is nothing here.} */
+
+ /*tex Here starts the \PDF\ backend section, todo: a separate list. */
+
+ { pdf_literal_node, write_node_size, NULL, node_fields_whatsit_pdf_literal, NULL, -1, 0 },
+ { pdf_refobj_node, pdf_refobj_node_size, NULL, node_fields_whatsit_pdf_refobj, NULL, -1, 0 },
+ { pdf_annot_node, pdf_annot_node_size, NULL, node_fields_whatsit_pdf_annot, NULL, -1, 0 },
+ { pdf_start_link_node, pdf_annot_node_size, NULL, node_fields_whatsit_pdf_start_link, NULL, -1, 0 },
+ { pdf_end_link_node, pdf_end_link_node_size, NULL, node_fields_whatsit_pdf_end_link, NULL, -1, 0 },
+ { pdf_dest_node, pdf_dest_node_size, NULL, node_fields_whatsit_pdf_dest, NULL, -1, 0 },
+ { pdf_action_node, pdf_action_size, NULL, node_fields_whatsit_pdf_action, NULL, -1, 0 },
+ { pdf_thread_node, pdf_thread_node_size, NULL, node_fields_whatsit_pdf_thread, NULL, -1, 0 },
+ { pdf_start_thread_node, pdf_thread_node_size, NULL, node_fields_whatsit_pdf_start_thread, NULL, -1, 0 },
+ { pdf_end_thread_node, pdf_end_thread_node_size, NULL, node_fields_whatsit_pdf_end_thread, NULL, -1, 0 },
+ { pdf_thread_data_node, pdf_thread_node_size, NULL, NULL, NULL, -1, 0 },
+ { pdf_link_data_node, pdf_annot_node_size, NULL, NULL, NULL, -1, 0 },
+ { pdf_colorstack_node, pdf_colorstack_node_size, NULL, node_fields_whatsit_pdf_colorstack, NULL, -1, 0 },
+ { pdf_setmatrix_node, pdf_setmatrix_node_size, NULL, node_fields_whatsit_pdf_setmatrix, NULL, -1, 0 },
+ { pdf_save_node, pdf_save_node_size, NULL, node_fields_whatsit_pdf_save, NULL, -1, 0 },
+ { pdf_restore_node, pdf_restore_node_size, NULL, node_fields_whatsit_pdf_restore, NULL, -1, 0 },
+ { pdf_link_state_node, pdf_link_state_node_size, NULL, node_fields_whatsit_pdf_link_state, NULL, -1, 0 },
+
+ /*tex That's it. */
+
+ { -1, -1, NULL, NULL, NULL, -1, 0 },
+
+};
+
+void l_set_whatsit_data(void) {
+ init_node_key(whatsit_node_data, open_node, open)
+ init_node_key(whatsit_node_data, write_node, write)
+ init_node_key(whatsit_node_data, close_node, close)
+ init_node_key(whatsit_node_data, special_node, special)
+ init_node_key(whatsit_node_data, save_pos_node, save_pos)
+ init_node_key(whatsit_node_data, late_lua_node, late_lua)
+ init_node_key(whatsit_node_data, user_defined_node, user_defined)
+
+ init_field_key(node_fields_whatsit_close, 0, attr);
+ init_field_key(node_fields_whatsit_close, 1, stream);
+ init_field_nop(node_fields_whatsit_close, 2);
+
+ init_field_key(node_fields_whatsit_late_lua, 0, attr);
+ init_field_key(node_fields_whatsit_late_lua, 1, reg);
+ init_field_key(node_fields_whatsit_late_lua, 2, data);
+ init_field_key(node_fields_whatsit_late_lua, 3, name);
+ init_field_key(node_fields_whatsit_late_lua, 4, string);
+ init_field_nop(node_fields_whatsit_late_lua, 5);
+
+ init_field_key(node_fields_whatsit_open, 0, attr);
+ init_field_key(node_fields_whatsit_open, 1, stream);
+ init_field_key(node_fields_whatsit_open, 2, name);
+ init_field_key(node_fields_whatsit_open, 3, area);
+ init_field_key(node_fields_whatsit_open, 4, ext);
+ init_field_nop(node_fields_whatsit_open, 5);
+
+ init_field_key(node_fields_whatsit_save_pos, 0, attr);
+ init_field_nop(node_fields_whatsit_save_pos, 1);
+
+ init_field_key(node_fields_whatsit_special, 0, attr);
+ init_field_key(node_fields_whatsit_special, 1, data);
+ init_field_nop(node_fields_whatsit_special, 2);
+
+ init_field_key(node_fields_whatsit_user_defined, 0, attr);
+ init_field_key(node_fields_whatsit_user_defined, 1, user_id);
+ init_field_key(node_fields_whatsit_user_defined, 2, type);
+ init_field_key(node_fields_whatsit_user_defined, 3, value);
+ init_field_nop(node_fields_whatsit_user_defined, 4);
+
+ init_field_key(node_fields_whatsit_write, 0, attr);
+ init_field_key(node_fields_whatsit_write, 1, stream);
+ init_field_key(node_fields_whatsit_write, 2, data);
+ init_field_nop(node_fields_whatsit_write, 3);
+
+ init_node_key(whatsit_node_data, pdf_literal_node, pdf_literal)
+ init_node_key(whatsit_node_data, pdf_refobj_node, pdf_refobj)
+ init_node_key(whatsit_node_data, pdf_annot_node, pdf_annot)
+ init_node_key(whatsit_node_data, pdf_start_link_node, pdf_start_link)
+ init_node_key(whatsit_node_data, pdf_end_link_node, pdf_end_link)
+ init_node_key(whatsit_node_data, pdf_dest_node, pdf_dest)
+ init_node_key(whatsit_node_data, pdf_action_node, pdf_action)
+ init_node_key(whatsit_node_data, pdf_thread_node, pdf_thread)
+ init_node_key(whatsit_node_data, pdf_start_thread_node,pdf_start_thread)
+ init_node_key(whatsit_node_data, pdf_end_thread_node, pdf_end_thread)
+ init_node_key(whatsit_node_data, pdf_thread_data_node, pdf_thread_data)
+ init_node_key(whatsit_node_data, pdf_link_data_node, pdf_link_data)
+ init_node_key(whatsit_node_data, pdf_colorstack_node, pdf_colorstack)
+ init_node_key(whatsit_node_data, pdf_setmatrix_node, pdf_setmatrix)
+ init_node_key(whatsit_node_data, pdf_save_node, pdf_save)
+ init_node_key(whatsit_node_data, pdf_restore_node, pdf_restore)
+ init_node_key(whatsit_node_data, pdf_link_state_node, pdf_link_state)
+
+ init_node_key(node_values_pdf_destination, 0, xyz)
+ init_node_key(node_values_pdf_destination, 1, fit)
+ init_node_key(node_values_pdf_destination, 2, fith)
+ init_node_key(node_values_pdf_destination, 3, fitv)
+ init_node_key(node_values_pdf_destination, 4, fitb)
+ init_node_key(node_values_pdf_destination, 5, fitbh)
+ init_node_key(node_values_pdf_destination, 6, fitbv)
+ init_node_key(node_values_pdf_destination, 7, fitr)
+
+ init_node_key(node_values_pdf_literal, set_origin, origin)
+ init_node_key(node_values_pdf_literal, direct_page, page)
+ init_node_key(node_values_pdf_literal, direct_always, always)
+ init_node_key(node_values_pdf_literal, direct_raw, raw)
+ init_node_key(node_values_pdf_literal, direct_text, text)
+ init_node_key(node_values_pdf_literal, direct_font, font)
+ init_node_key(node_values_pdf_literal, scan_special, special)
+
+ init_node_key(node_values_pdf_action, 0, page)
+ init_node_key(node_values_pdf_action, 1, goto)
+ init_node_key(node_values_pdf_action, 2, thread)
+ init_node_key(node_values_pdf_action, 3, user)
+
+ init_node_key(node_values_pdf_window, 0, unset)
+ init_node_key(node_values_pdf_window, 1, new)
+ init_node_key(node_values_pdf_window, 2, nonew)
+
+ init_node_key(node_values_color_stack, 0, set)
+ init_node_key(node_values_color_stack, 1, push)
+ init_node_key(node_values_color_stack, 2, pop)
+ init_node_key(node_values_color_stack, 3, current)
+
+ init_field_key(node_fields_whatsit_pdf_action, 0, action_type);
+ init_field_key(node_fields_whatsit_pdf_action, 1, named_id);
+ init_field_key(node_fields_whatsit_pdf_action, 2, action_id);
+ init_field_key(node_fields_whatsit_pdf_action, 3, file);
+ init_field_key(node_fields_whatsit_pdf_action, 4, new_window);
+ init_field_key(node_fields_whatsit_pdf_action, 5, data);
+ init_field_nop(node_fields_whatsit_pdf_action, 6);
+
+ init_field_key(node_fields_whatsit_pdf_annot, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_annot, 1, width);
+ init_field_key(node_fields_whatsit_pdf_annot, 2, depth);
+ init_field_key(node_fields_whatsit_pdf_annot, 3, height);
+ init_field_key(node_fields_whatsit_pdf_annot, 4, objnum);
+ init_field_key(node_fields_whatsit_pdf_annot, 5, data);
+ init_field_nop(node_fields_whatsit_pdf_annot, 6);
+
+ init_field_key(node_fields_whatsit_pdf_colorstack, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_colorstack, 1, stack);
+ init_field_key(node_fields_whatsit_pdf_colorstack, 2, cmd);
+ init_field_key(node_fields_whatsit_pdf_colorstack, 3, data);
+ init_field_nop(node_fields_whatsit_pdf_colorstack, 4);
+
+ init_field_key(node_fields_whatsit_pdf_dest, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_dest, 1, width);
+ init_field_key(node_fields_whatsit_pdf_dest, 2, depth);
+ init_field_key(node_fields_whatsit_pdf_dest, 3, height);
+ init_field_key(node_fields_whatsit_pdf_dest, 4, named_id);
+ init_field_key(node_fields_whatsit_pdf_dest, 5, dest_id);
+ init_field_key(node_fields_whatsit_pdf_dest, 6, dest_type);
+ init_field_key(node_fields_whatsit_pdf_dest, 7, xyz_zoom);
+ init_field_key(node_fields_whatsit_pdf_dest, 8, objnum);
+ init_field_nop(node_fields_whatsit_pdf_dest, 9);
+
+ init_field_key(node_fields_whatsit_pdf_end_link, 0, attr);
+ init_field_nop(node_fields_whatsit_pdf_end_link, 1);
+
+ init_field_key(node_fields_whatsit_pdf_link_state, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_link_state, 1, value);
+ init_field_nop(node_fields_whatsit_pdf_link_state, 2);
+
+ init_field_key(node_fields_whatsit_pdf_end_thread, 0, attr);
+ init_field_nop(node_fields_whatsit_pdf_end_thread, 1);
+
+ init_field_key(node_fields_whatsit_pdf_literal, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_literal, 1, mode);
+ init_field_key(node_fields_whatsit_pdf_literal, 2, data);
+ init_field_nop(node_fields_whatsit_pdf_literal, 3);
+
+ init_field_key(node_fields_whatsit_pdf_refobj, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_refobj, 1, objnum);
+ init_field_nop(node_fields_whatsit_pdf_refobj, 2);
+
+ init_field_key(node_fields_whatsit_pdf_restore, 0, attr);
+ init_field_nop(node_fields_whatsit_pdf_restore, 1);
+
+ init_field_key(node_fields_whatsit_pdf_save, 0, attr);
+ init_field_nop(node_fields_whatsit_pdf_save, 1);
+
+ init_field_key(node_fields_whatsit_pdf_setmatrix, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_setmatrix, 1, data);
+ init_field_nop(node_fields_whatsit_pdf_setmatrix, 2);
+
+ init_field_key(node_fields_whatsit_pdf_start_link, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_start_link, 1, width);
+ init_field_key(node_fields_whatsit_pdf_start_link, 2, depth);
+ init_field_key(node_fields_whatsit_pdf_start_link, 3, height);
+ init_field_key(node_fields_whatsit_pdf_start_link, 4, objnum);
+ init_field_key(node_fields_whatsit_pdf_start_link, 5, link_attr);
+ init_field_key(node_fields_whatsit_pdf_start_link, 6, action);
+ init_field_nop(node_fields_whatsit_pdf_start_link, 7);
+
+ init_field_key(node_fields_whatsit_pdf_start_thread, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_start_thread, 1, width);
+ init_field_key(node_fields_whatsit_pdf_start_thread, 2, depth);
+ init_field_key(node_fields_whatsit_pdf_start_thread, 3, height);
+ init_field_key(node_fields_whatsit_pdf_start_thread, 4, named_id);
+ init_field_key(node_fields_whatsit_pdf_start_thread, 5, thread_id);
+ init_field_key(node_fields_whatsit_pdf_start_thread, 6, thread_attr);
+ init_field_nop(node_fields_whatsit_pdf_start_thread, 7);
+
+ init_field_key(node_fields_whatsit_pdf_thread, 0, attr);
+ init_field_key(node_fields_whatsit_pdf_thread, 1, width);
+ init_field_key(node_fields_whatsit_pdf_thread, 2, depth);
+ init_field_key(node_fields_whatsit_pdf_thread, 3, height);
+ init_field_key(node_fields_whatsit_pdf_thread, 4, named_id);
+ init_field_key(node_fields_whatsit_pdf_thread, 5, thread_id);
+ init_field_key(node_fields_whatsit_pdf_thread, 6, thread_attr);
+ init_field_nop(node_fields_whatsit_pdf_thread, 7);
+
+}
+
+#define last_whatsit_node pdf_restore_node
+
+/*tex
+
+ When we copy a node list, there are several possibilities: we do the same as
+ a new node, we copy the entry to the table in properties (a reference), we do
+ a deep copy of a table in the properties, we create a new table and give it
+ the original one as a metatable. After some experiments (that also included
+ timing) with these scenarios I decided that a deep copy made no sense, nor
+ did nilling. In the end both the shallow copy and the metatable variant were
+ both ok, although the second ons is slower. The most important aspect to keep
+ in mind is that references to other nodes in properties no longer can be
+ valid for that copy. We could use two tables (one unique and one shared) or
+ metatables but that only complicates matters.
+
+ When defining a new node, we could already allocate a table but it is rather
+ easy to do that at the lua end e.g. using a metatable __index method. That
+ way it is under macro package control.
+
+ When deleting a node, we could keep the slot (e.g. setting it to false) but
+ it could make memory consumption raise unneeded when we have temporary large
+ node lists and after that only small lists.
+
+ So, in the end this is what we ended up with. For the record, I also
+ experimented with the following:
+
+ \startitemize
+
+ \startitem
+ Copy attributes to the properties so that we have fast access at the
+ lua end: in the end the overhead is not compensated by speed and
+ convenience, in fact, attributes are not that slow when it comes to
+ accessing them.
+ \stopitem
+
+ \startitem
+ A bitset in the node but again the gain compared to attributes is
+ neglectable and it also demands a pretty string agreement over what
+ bit represents what, and this is unlikely to succeed in the tex
+ community (I could use it for font handling, which is cross package,
+ but decided that it doesn't pay off.
+ \stopitem
+
+ \stopitemize
+
+ In case one wonders why properties make sense then, well, it is not so much
+ speed that we gain, but more convenience: storing all kind of (temporary)
+ data in attributes is no fun and this mechanism makes sure that properties
+ are cleaned up when a node is freed. Also, the advantage of a more or less
+ global properties table is that we stay at the lua end. An alternative is to
+ store a reference in the node itself but that is complicated by the fact that
+ the register has some limitations (no numeric keys) and we also don't want to
+ mess with it too much.
+
+*/
+
+int lua_properties_level = 0 ;
+int lua_properties_enabled = 0 ;
+int lua_properties_use_metatable = 0 ;
+
+/*tex
+
+ We keep track of nesting so that we don't oveflow the stack, and, what is
+ more important, don't keep resolving the registry index.
+
+*/
+
+#define lua_properties_push do { \
+ if (lua_properties_enabled) { \
+ lua_properties_level = lua_properties_level + 1 ; \
+ if (lua_properties_level == 1) { \
+ lua_get_metatablelua_l(Luas,node_properties); \
+ } \
+ } \
+} while(0)
+
+#define lua_properties_pop do { \
+ if (lua_properties_enabled) { \
+ if (lua_properties_level == 1) \
+ lua_pop(Luas,1); \
+ lua_properties_level = lua_properties_level - 1 ; \
+ } \
+} while(0)
+
+/*tex No setting is needed: */
+
+#define lua_properties_set(target) do { \
+} while(0)
+
+/*tex Resetting boils down to nilling. */
+
+#define lua_properties_reset(target) do { \
+ if (lua_properties_enabled) { \
+ if (lua_properties_level == 0) { \
+ lua_get_metatablelua_l(Luas,node_properties); \
+ lua_pushnil(Luas); \
+ lua_rawseti(Luas,-2,target); \
+ lua_pop(Luas,1); \
+ } else { \
+ lua_pushnil(Luas); \
+ lua_rawseti(Luas,-2,target); \
+ } \
+ } \
+} while(0)
+
+/*tex
+
+ For a moment I considered supporting all kind of data types but in practice
+ that makes no sense. So we stick to a cheap shallow copy with as option a
+ metatable. BTW, a deep copy would look like this:
+
+ \starttyping
+ static void copy_lua_table(lua_State* L, int index) {
+ lua_newtable(L);
+ lua_pushnil(L);
+ while(lua_next(L, index-1) != 0) {
+ lua_pushvalue(L, -2);
+ lua_insert(L, -2);
+ if (lua_type(L,-1)==LUA_TTABLE)
+ copy_lua_table(L,-1);
+ lua_settable(L, -4);
+ }
+ lua_pop(L,1);
+ }
+
+ #define lua_properties_copy(target, source) do { \
+ if (lua_properties_enabled) { \
+ lua_pushinteger(Luas,source); \
+ lua_rawget(Luas,-2); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ copy_lua_table(Luas,-1); \
+ lua_pushinteger(Luas,target); \
+ lua_insert(Luas,-2); \
+ lua_rawset(Luas,-3); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ } \
+ } while(0)
+ \stoptyping
+
+*/
+
+/*tex Isn't there a faster way to metatable? */
+
+/*tex
+
+ \starttyping
+ #define lua_properties_copy(target,source) do { \
+ if (lua_properties_enabled) { \
+ if (lua_properties_level == 0) { \
+ lua_get_metatablelua_l(Luas,node_properties); \
+ lua_rawgeti(Luas,-1,source); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ if (lua_properties_use_metatable) { \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setfield(Luas,-2,"__index"); \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setmetatable(Luas,-2); \
+ } \
+ lua_rawseti(Luas,-2,target); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ lua_pop(Luas,1); \
+ } else { \
+ lua_rawgeti(Luas,-1,source); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ if (lua_properties_use_metatable) { \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setfield(Luas,-2,"__index"); \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setmetatable(Luas,-2); \
+ } \
+ lua_rawseti(Luas,-2,target); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ } \
+ } \
+ } while(0)
+ \stoptyping
+
+*/
+
+/*tex
+
+ A simple testrun on many pages of dumb text shows 1% gain (of course it
+ depends on how properties are used but some other tests confirm it).
+
+*/
+
+#define lua_properties_copy(target,source) do { \
+ if (lua_properties_enabled) { \
+ if (lua_properties_level == 0) { \
+ lua_get_metatablelua_l(Luas,node_properties); \
+ lua_rawgeti(Luas,-1,source); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ if (lua_properties_use_metatable) { \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_push_string_by_name(Luas,__index); \
+ lua_insert(Luas,-2); \
+ lua_rawset(Luas, -3); \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setmetatable(Luas,-2); \
+ } \
+ lua_rawseti(Luas,-2,target); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ lua_pop(Luas,1); \
+ } else { \
+ lua_rawgeti(Luas,-1,source); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ if (lua_properties_use_metatable) { \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_push_string_by_name(Luas,__index); \
+ lua_insert(Luas,-2); \
+ lua_rawset(Luas, -3); \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setmetatable(Luas,-2); \
+ } \
+ lua_rawseti(Luas,-2,target); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ } \
+ } \
+} while(0)
+
+/*tex Here end the property handlers. */
+
+int valid_node(halfword p)
+{
+ if (p > my_prealloc && p < var_mem_max) {
+#ifdef CHECK_NODE_USAGE
+ if (varmem_sizes[p] > 0) {
+ return 1;
+ }
+#else
+ return 1;
+#endif
+ }
+ return 0;
+}
+
+static int test_count = 1;
+
+#define dorangetest(a,b,c) do { \
+ if (!(b>=0 && b<c)) { \
+ fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
+ (int)a, (int)b, (int)c, __LINE__,test_count); \
+ confusion("node range test failed"); \
+ } } while (0)
+
+#define dotest(a,b,c) do { \
+ if (b!=c) { \
+ fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
+ (int)a, (int)b, (int)c, __LINE__,test_count); \
+ confusion("node test failed"); \
+ } } while (0)
+
+#define check_action_ref(a) { dorangetest(p,a,var_mem_max); }
+#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); }
+
+/*tex hm, we can just pass |p| then. */
+
+#define check_token_ref(p) { \
+ if (type(p) == whatsit_node) { \
+ formatted_error("nodes","fuzzy token cleanup in whatsit node with type %s and subtype %i",node_data[type(p)].name,subtype(p)); \
+ } else { \
+ formatted_error("nodes","fuzzy token cleanup in node with type %s",node_data[type(p)].name); \
+ } \
+}
+
+#ifdef CHECK_NODE_USAGE
+
+static void check_static_node_mem(void)
+{
+ dotest(zero_glue, width(zero_glue), 0);
+ dotest(zero_glue, type(zero_glue), glue_spec_node);
+ dotest(zero_glue, vlink(zero_glue), null);
+ dotest(zero_glue, stretch(zero_glue), 0);
+ dotest(zero_glue, stretch_order(zero_glue), normal);
+ dotest(zero_glue, shrink(zero_glue), 0);
+ dotest(zero_glue, shrink_order(zero_glue), normal);
+
+ dotest(sfi_glue, width(sfi_glue), 0);
+ dotest(sfi_glue, type(sfi_glue), glue_spec_node);
+ dotest(sfi_glue, vlink(sfi_glue), null);
+ dotest(sfi_glue, stretch(sfi_glue), 0);
+ dotest(sfi_glue, stretch_order(sfi_glue), sfi);
+ dotest(sfi_glue, shrink(sfi_glue), 0);
+ dotest(sfi_glue, shrink_order(sfi_glue), normal);
+
+ dotest(fil_glue, width(fil_glue), 0);
+ dotest(fil_glue, type(fil_glue), glue_spec_node);
+ dotest(fil_glue, vlink(fil_glue), null);
+ dotest(fil_glue, stretch(fil_glue), unity);
+ dotest(fil_glue, stretch_order(fil_glue), fil);
+ dotest(fil_glue, shrink(fil_glue), 0);
+ dotest(fil_glue, shrink_order(fil_glue), normal);
+
+ dotest(fill_glue, width(fill_glue), 0);
+ dotest(fill_glue, type(fill_glue), glue_spec_node);
+ dotest(fill_glue, vlink(fill_glue), null);
+ dotest(fill_glue, stretch(fill_glue), unity);
+ dotest(fill_glue, stretch_order(fill_glue), fill);
+ dotest(fill_glue, shrink(fill_glue), 0);
+ dotest(fill_glue, shrink_order(fill_glue), normal);
+
+ dotest(ss_glue, width(ss_glue), 0);
+ dotest(ss_glue, type(ss_glue), glue_spec_node);
+ dotest(ss_glue, vlink(ss_glue), null);
+ dotest(ss_glue, stretch(ss_glue), unity);
+ dotest(ss_glue, stretch_order(ss_glue), fil);
+ dotest(ss_glue, shrink(ss_glue), unity);
+ dotest(ss_glue, shrink_order(ss_glue), fil);
+
+ dotest(fil_neg_glue, width(fil_neg_glue), 0);
+ dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node);
+ dotest(fil_neg_glue, vlink(fil_neg_glue), null);
+ dotest(fil_neg_glue, stretch(fil_neg_glue), -unity);
+ dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil);
+ dotest(fil_neg_glue, shrink(fil_neg_glue), 0);
+ dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal);
+}
+
+static void node_mem_dump(halfword p)
+{
+ halfword r;
+ for (r = my_prealloc + 1; r < var_mem_max; r++) {
+ if (vlink(r) == p) {
+ halfword s = r;
+ while (s > my_prealloc && varmem_sizes[s] == 0) {
+ s--;
+ }
+ if (s != null
+ && s != my_prealloc
+ && s != var_mem_max
+ && (r - s) < get_node_size(type(s), subtype(s))
+ && alink(s) != p) {
+ if (type(s) == disc_node) {
+ fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
+ get_node_name(type(s), subtype(s)), (int) s,
+ (int) vlink(s), (int) alink(s));
+ fprintf(stdout, "pre_break(%d,%d,%d), ",
+ (int) vlink_pre_break(s), (int) tlink(pre_break(s)),
+ (int) alink(pre_break(s)));
+ fprintf(stdout, "post_break(%d,%d,%d), ",
+ (int) vlink_post_break(s),
+ (int) tlink(post_break(s)),
+ (int) alink(post_break(s)));
+ fprintf(stdout, "no_break(%d,%d,%d)",
+ (int) vlink_no_break(s), (int) tlink(no_break(s)),
+ (int) alink(no_break(s)));
+ fprintf(stdout, "\n");
+ } else {
+ if (vlink(s) == p
+ || (type(s) == glyph_node && lig_ptr (s) == p)
+ || (type(s) == vlist_node && list_ptr(s) == p)
+ || (type(s) == hlist_node && list_ptr(s) == p)
+ || (type(s) == unset_node && list_ptr(s) == p)
+ || (type(s) == ins_node && ins_ptr (s) == p)
+ ) {
+ fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
+ get_node_name(type(s), subtype(s)), (int) s,
+ (int) vlink(s), (int) alink(s));
+ if (type(s) == glyph_node) {
+ fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s));
+ } else if (type(s) == vlist_node || type(s) == hlist_node) {
+ fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s));
+ }
+ fprintf(stdout, "\n");
+ } else {
+ if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) {
+ fprintf(stdout, " pointed to from %s node %d\n",
+ get_node_name(type(s), subtype(s)), (int) s);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+static int free_error(halfword p)
+{
+ if (p > my_prealloc && p < var_mem_max) {
+#ifdef CHECK_NODE_USAGE
+ int i;
+ if (varmem_sizes[p] == 0) {
+ check_static_node_mem();
+ for (i = (my_prealloc + 1); i < var_mem_max; i++) {
+ if (varmem_sizes[i] > 0) {
+ check_node(i);
+ }
+ }
+ test_count++;
+ if (type(p) == glyph_node) {
+ formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p);
+ } else {
+ formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
+ }
+ node_mem_dump(p);
+ return 1;
+ }
+#endif
+ } else {
+ formatted_error("nodes", "attempt to free an impossible node %d", (int) p);
+ return 1;
+ }
+ return 0;
+}
+
+static int copy_error(halfword p)
+{
+ if (p >= 0 && p < var_mem_max) {
+#ifdef CHECK_NODE_USAGE
+ if (p > my_prealloc && varmem_sizes[p] == 0) {
+ if (type(p) == glyph_node) {
+ formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p);
+ } else {
+ formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
+ }
+ return 1;
+ }
+#endif
+ } else {
+ formatted_error("nodes", "attempt to copy an impossible node %d", (int) p);
+ return 1;
+ }
+ return 0;
+}
+
+/*tex
+
+ Because of the 5-10\% overhead that \SYNTEX\ creates some options have been
+ implemented controlled by |synctex_anyway_mode|.
+
+ \startabulate
+ \NC \type {1} \NC all but glyphs \NC \NR
+ \NC \type {2} \NC also glyphs \NC \NR
+ \NC \type {3} \NC glyphs and glue \NC \NR
+ \NC \type {4} \NC only glyphs \NC \NR
+ \stoptabulate
+
+*/
+
+static halfword synctex_anyway_mode = 0;
+static halfword synctex_line_field = 0;
+static halfword synctex_no_files = 0;
+
+void synctex_set_mode(int m)
+{
+ synctex_anyway_mode = m;
+};
+
+int synctex_get_mode(void)
+{
+ return synctex_anyway_mode;
+};
+
+void synctex_set_no_files(int f)
+{
+ synctex_no_files = f;
+};
+
+int synctex_get_no_files(void)
+{
+ return (int) synctex_no_files ;
+};
+
+void synctex_set_tag(int t)
+{
+ cur_input.synctex_tag_field = t;
+};
+
+int synctex_get_tag(void)
+{
+ return (int) cur_input.synctex_tag_field;
+};
+
+int synctex_get_line(void)
+{
+ return (int) synctex_line_field;
+};
+
+static int forced_tag = 0;
+static int forced_line = 0;
+
+void synctex_force_tag(int t)
+{
+ forced_tag = t;
+};
+
+void synctex_force_line(int t)
+{
+ forced_line = t;
+};
+
+void synctex_set_line(int l)
+{
+ synctex_line_field = l;
+};
+
+/*tex |if_stack| is called a lot so maybe optimize that one. */
+
+halfword new_node(int i, int j)
+{
+ int s = get_node_size(i, j);
+ halfword n = get_node(s);
+ /*tex
+
+ It should be possible to do this memset at |free_node()|. Both type() and
+ subtype() will be set below, and vlink() is set to null by |get_node()|,
+ so we can do we clearing one word less than |s|.
+
+ */
+ (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1)));
+ switch (i) {
+ case glyph_node:
+ init_lang_data(n);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_dir(n) = -1;
+ break;
+ case disc_node:
+ pre_break(n) = pre_break_head(n);
+ type(pre_break(n)) = nesting_node;
+ subtype(pre_break(n)) = pre_break_head(0);
+ post_break(n) = post_break_head(n);
+ type(post_break(n)) = nesting_node;
+ subtype(post_break(n)) = post_break_head(0);
+ no_break(n) = no_break_head(n);
+ type(no_break(n)) = nesting_node;
+ subtype(no_break(n)) = no_break_head(0);
+ break;
+ case rule_node:
+ depth(n) = null_flag;
+ height(n) = null_flag;
+ width(n) = null_flag;
+ rule_dir(n) = -1;
+ rule_index(n) = 0;
+ rule_transform(n) = 0;
+ break;
+ case whatsit_node:
+ if (j == open_node) {
+ open_name(n) = get_nullstr();
+ open_area(n) = open_name(n);
+ open_ext(n) = open_name(n);
+ }
+ break;
+ case unset_node:
+ width(n) = null_flag;
+ break;
+ case pseudo_line_node:
+ case shape_node:
+ /*tex
+
+ This is a trick that makes |pseudo_files| slightly slower, but
+ the overall allocation faster then an explicit test at the top of
+ |new_node()|.
+
+ */
+ if (j>0) {
+ free_node(n, variable_node_size);
+ n = slow_get_node(j);
+ (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1)));
+ }
+ break;
+ case fraction_noad:
+ fraction_fam(n) = -1;
+ break;
+ case simple_noad:
+ noad_fam(n) = -1;
+ break;
+ default:
+ break;
+ }
+ if (synctex_anyway_mode) {
+ /*tex See table above. */
+ switch (i) {
+ case glyph_node:
+ if (synctex_anyway_mode > 1) {
+ synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ break;
+ case glue_node:
+ if (synctex_anyway_mode < 4) {
+ synctex_tag_glue(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_glue(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ break;
+ case kern_node:
+ if (synctex_anyway_mode < 3) {
+ synctex_tag_kern(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_kern(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ /*tex Rather useless: */
+ if (synctex_anyway_mode < 3) {
+ synctex_tag_box(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_box(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ break;
+ case rule_node:
+ if (synctex_anyway_mode < 3) {
+ synctex_tag_rule(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_rule(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ break;
+ case math_node:
+ /*tex Noads probably make more sense but let's not change that. */
+ if (synctex_anyway_mode < 3) {
+ synctex_tag_math(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_math(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ break;
+ }
+ } else if (synctex_par) {
+ /*tex Handle the \SYNTEX\ extension. */
+ switch (i) {
+ case glue_node:
+ synctex_tag_glue(n) = cur_input.synctex_tag_field;
+ synctex_line_glue(n) = line;
+ break;
+ case kern_node:
+ if (j != 0) {
+ synctex_tag_kern(n) = cur_input.synctex_tag_field;
+ synctex_line_kern(n) = line;
+ }
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ synctex_tag_box(n) = cur_input.synctex_tag_field;
+ synctex_line_box(n) = line;
+ break;
+ case rule_node:
+ synctex_tag_rule(n) = cur_input.synctex_tag_field;
+ synctex_line_rule(n) = line;
+ break;
+ case math_node:
+ synctex_tag_math(n) = cur_input.synctex_tag_field;
+ synctex_line_math(n) = line;
+ break;
+ }
+ }
+ /*tex Take care of attributes. */
+ if (nodetype_has_attributes(i)) {
+ build_attribute_list(n);
+ /*tex No need for |lua_properties_set|. */
+ }
+ type(n) = (quarterword) i;
+ subtype(n) = (quarterword) j;
+ return n;
+}
+
+halfword raw_glyph_node(void)
+{
+ register halfword n = get_node(glyph_node_size);
+ (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
+ if (synctex_anyway_mode > 1) {
+ synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ type(n) = glyph_node;
+ subtype(n) = 0;
+ return n;
+}
+
+halfword new_glyph_node(void)
+{
+ register halfword n = get_node(glyph_node_size);
+ (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
+ if (synctex_anyway_mode > 1) {
+ synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ type(n) = glyph_node;
+ subtype(n) = 0;
+ build_attribute_list(n);
+ /*tex No need for |lua_properties_set|. */
+ return n;
+}
+
+/*tex
+
+ This makes a duplicate of the node list that starts at |p| and returns a
+ pointer to the new list.
+
+*/
+
+halfword do_copy_node_list(halfword p, halfword end)
+{
+ /*tex previous position in new list */
+ halfword q = null;
+ /*tex head of the list */
+ halfword h = null;
+ register halfword s ;
+ /*tex saves stack and time */
+ lua_properties_push;
+ while (p != end) {
+ s = copy_node(p);
+ if (h == null) {
+ h = s;
+ } else {
+ couple_nodes(q, s);
+ }
+ q = s;
+ p = vlink(p);
+ }
+ /*tex saves stack and time */
+ lua_properties_pop;
+ return h;
+}
+
+halfword copy_node_list(halfword p)
+{
+ return do_copy_node_list(p, null);
+}
+
+#define copy_sub_list(target,source) do { \
+ if (source != null) { \
+ s = do_copy_node_list(source, null); \
+ target = s; \
+ } else { \
+ target = null; \
+ } \
+ } while (0)
+
+#define copy_sub_node(target,source) do { \
+ if (source != null) { \
+ s = copy_node(source); \
+ target = s ; \
+ } else { \
+ target = null; \
+ } \
+} while (0)
+
+/*tex Make a dupe of a single node. */
+
+static void copy_node_wrapup_core(halfword p, halfword r)
+{
+ halfword s ;
+ switch (subtype(p)) {
+ case write_node:
+ case special_node:
+ add_token_ref(write_tokens(p));
+ break;
+ case late_lua_node:
+ copy_late_lua(r, p);
+ break;
+ case user_defined_node:
+ switch (user_node_type(p)) {
+ case 'a':
+ add_node_attr_ref(user_node_value(p));
+ break;
+ case 'l':
+ copy_user_lua(r, p);
+ break;
+ case 'n':
+ s = copy_node_list(user_node_value(p));
+ user_node_value(r) = s;
+ break;
+ case 's':
+ /* |add_string_ref(user_node_value(p));| */
+ break;
+ case 't':
+ add_token_ref(user_node_value(p));
+ break;
+ }
+ break;
+ default:
+ break ;
+ }
+}
+
+void copy_node_wrapup_dvi(halfword p, halfword r)
+{
+}
+
+void copy_node_wrapup_pdf(halfword p, halfword r)
+{
+ switch(subtype(p)) {
+ case pdf_literal_node:
+ copy_pdf_literal(r, p);
+ break;
+ case pdf_colorstack_node:
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ add_token_ref(pdf_colorstack_data(p));
+ break;
+ case pdf_setmatrix_node:
+ add_token_ref(pdf_setmatrix_data(p));
+ break;
+ case pdf_annot_node:
+ add_token_ref(pdf_annot_data(p));
+ break;
+ case pdf_start_link_node:
+ if (pdf_link_attr(r) != null)
+ add_token_ref(pdf_link_attr(r));
+ add_action_ref(pdf_link_action(r));
+ break;
+ case pdf_dest_node:
+ if (pdf_dest_named_id(p) > 0)
+ add_token_ref(pdf_dest_id(p));
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (pdf_thread_named_id(p) > 0)
+ add_token_ref(pdf_thread_id(p));
+ if (pdf_thread_attr(p) != null)
+ add_token_ref(pdf_thread_attr(p));
+ break;
+ default:
+ break;
+ }
+}
+
+halfword copy_node(const halfword p)
+{
+ /*tex current node being fabricated for new list */
+ halfword r;
+ /*tex whatsit subtype */
+ halfword w;
+ /*tex type of node */
+ halfword t;
+ /*tex a helper variable for copying into variable mem */
+ register halfword s;
+ register int i;
+ if (copy_error(p)) {
+ r = new_node(temp_node, 0);
+ return r;
+ }
+ t = type(p);
+ i = get_node_size(t,subtype(p));
+ r = get_node(i);
+ (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i));
+ /*tex A possible speedup:
+
+ \starttyping
+ if t == glue_spec) {
+ return r;
+ }
+ \stoptyping
+
+ */
+ if (synctex_anyway_mode) {
+ /*tex Not:
+
+ \starttyping
+ if (t == glyph_node) {
+ if (synctex_anyway_mode > 1) {
+ synctex_tag_glyph(r) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
+ synctex_line_glyph(r) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
+ }
+ }
+ \stoptyping
+ */
+ } else if (synctex_par) {
+ /*tex Handle synctex extension. */
+ switch (t) {
+ case math_node:
+ synctex_tag_math(r) = cur_input.synctex_tag_field;
+ synctex_line_math(r) = line;
+ break;
+ case kern_node:
+ synctex_tag_kern(r) = cur_input.synctex_tag_field;
+ synctex_line_kern(r) = line;
+ break;
+ }
+ }
+ if (nodetype_has_attributes(t)) {
+ add_node_attr_ref(node_attr(p));
+ alink(r) = null;
+ lua_properties_copy(r,p);
+ }
+ vlink(r) = null;
+ switch (t) {
+ case glyph_node:
+ copy_sub_list(lig_ptr(r),lig_ptr(p)) ;
+ break;
+ case glue_node:
+ copy_sub_list(leader_ptr(r),leader_ptr(p)) ;
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ copy_sub_list(list_ptr(r),list_ptr(p)) ;
+ break;
+ case disc_node:
+ pre_break(r) = pre_break_head(r);
+ if (vlink_pre_break(p) != null) {
+ s = copy_node_list(vlink_pre_break(p));
+ alink(s) = pre_break(r);
+ tlink_pre_break(r) = tail_of_list(s);
+ vlink_pre_break(r) = s;
+ } else {
+ assert(tlink(pre_break(r)) == null);
+ }
+ post_break(r) = post_break_head(r);
+ if (vlink_post_break(p) != null) {
+ s = copy_node_list(vlink_post_break(p));
+ alink(s) = post_break(r);
+ tlink_post_break(r) = tail_of_list(s);
+ vlink_post_break(r) = s;
+ } else {
+ assert(tlink_post_break(r) == null);
+ }
+ no_break(r) = no_break_head(r);
+ if (vlink(no_break(p)) != null) {
+ s = copy_node_list(vlink_no_break(p));
+ alink(s) = no_break(r);
+ tlink_no_break(r) = tail_of_list(s);
+ vlink_no_break(r) = s;
+ } else {
+ assert(tlink_no_break(r) == null);
+ }
+ break;
+ case math_node:
+ break;
+ case ins_node:
+ copy_sub_list(ins_ptr(r),ins_ptr(p)) ;
+ break;
+ case margin_kern_node:
+ copy_sub_node(margin_char(r),margin_char(p));
+ break;
+ case mark_node:
+ add_token_ref(mark_ptr(p));
+ break;
+ case adjust_node:
+ copy_sub_list(adjust_ptr(r),adjust_ptr(p));
+ break;
+ case choice_node:
+ copy_sub_list(display_mlist(r),display_mlist(p)) ;
+ copy_sub_list(text_mlist(r),text_mlist(p)) ;
+ copy_sub_list(script_mlist(r),script_mlist(p)) ;
+ copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ;
+ break;
+ case simple_noad:
+ copy_sub_list(nucleus(r),nucleus(p)) ;
+ copy_sub_list(subscr(r),subscr(p)) ;
+ copy_sub_list(supscr(r),supscr(p)) ;
+ break;
+ case radical_noad:
+ copy_sub_list(nucleus(r),nucleus(p)) ;
+ copy_sub_list(subscr(r),subscr(p)) ;
+ copy_sub_list(supscr(r),supscr(p)) ;
+ copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
+ copy_sub_list(degree(r),degree(p)) ;
+ break;
+ case accent_noad:
+ copy_sub_list(nucleus(r),nucleus(p)) ;
+ copy_sub_list(subscr(r),subscr(p)) ;
+ copy_sub_list(supscr(r),supscr(p)) ;
+ copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ;
+ copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ;
+ copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ;
+ break;
+ case fence_noad:
+ copy_sub_node(delimiter(r),delimiter(p)) ;
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ copy_sub_list(math_list(r),math_list(p)) ;
+ break;
+ case fraction_noad:
+ copy_sub_list(numerator(r),numerator(p)) ;
+ copy_sub_list(denominator(r),denominator(p)) ;
+ copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
+ copy_sub_node(right_delimiter(r),right_delimiter(p)) ;
+ break;
+ case glue_spec_node:
+ break;
+ case dir_node:
+ break;
+ case local_par_node:
+ copy_sub_list(local_box_left(r),local_box_left(p));
+ copy_sub_list(local_box_right(r),local_box_right(p));
+ case boundary_node:
+ break;
+ case whatsit_node:
+ w = subtype(p) ;
+ if (w >= backend_first_pdf_whatsit) {
+ copy_node_wrapup_pdf(p,r);
+ } else if (w >= backend_first_dvi_whatsit) {
+ copy_node_wrapup_dvi(p,r);
+ } else {
+ copy_node_wrapup_core(p,r);
+ }
+ break;
+ }
+ return r;
+}
+
+#define free_sub_list(source) if (source != null) flush_node_list(source);
+#define free_sub_node(source) if (source != null) flush_node(source);
+
+static void flush_node_wrapup_core(halfword p)
+{
+ switch (subtype(p)) {
+ case open_node:
+ case write_node:
+ case close_node:
+ case save_pos_node:
+ break;
+ case special_node:
+ delete_token_ref(write_tokens(p));
+ break;
+ case late_lua_node:
+ free_late_lua(p);
+ break;
+ case user_defined_node:
+ switch (user_node_type(p)) {
+ case 'a':
+ delete_attribute_ref(user_node_value(p));
+ break;
+ case 'd':
+ break;
+ case 'l':
+ free_user_lua(p);
+ break;
+ case 'n':
+ flush_node_list(user_node_value(p));
+ break;
+ case 's':
+ /*tex |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */
+ break;
+ case 't':
+ delete_token_ref(user_node_value(p));
+ break;
+ default:
+ {
+ const char *hlp[] = {
+ "The type of the value in a user defined whatsit node should be one",
+ "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
+ "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
+ "know how to free the node's value. A memory leak may result.",
+ NULL
+ };
+ tex_error("Unidentified user defined whatsit", hlp);
+ }
+ break;
+ }
+ break;
+ }
+}
+
+void flush_node_wrapup_dvi(halfword p)
+{
+}
+
+void flush_node_wrapup_pdf(halfword p)
+{
+ switch(subtype(p)) {
+ case pdf_save_node:
+ case pdf_restore_node:
+ case pdf_link_state_node:
+ case pdf_refobj_node:
+ case pdf_end_link_node:
+ case pdf_end_thread_node:
+ break;
+ case pdf_literal_node:
+ free_pdf_literal(p);
+ break;
+ case pdf_colorstack_node:
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ delete_token_ref(pdf_colorstack_data(p));
+ break;
+ case pdf_setmatrix_node:
+ delete_token_ref(pdf_setmatrix_data(p));
+ break;
+ case pdf_annot_node:
+ delete_token_ref(pdf_annot_data(p));
+ break;
+ case pdf_link_data_node:
+ break;
+ case pdf_start_link_node:
+ if (pdf_link_attr(p) != null)
+ delete_token_ref(pdf_link_attr(p));
+ delete_action_ref(pdf_link_action(p));
+ break;
+ case pdf_dest_node:
+ if (pdf_dest_named_id(p) > 0)
+ delete_token_ref(pdf_dest_id(p));
+ break;
+ case pdf_action_node:
+ if (pdf_action_type(p) == pdf_action_user) {
+ delete_token_ref(pdf_action_tokens(p));
+ } else {
+ if (pdf_action_file(p) != null)
+ delete_token_ref(pdf_action_file(p));
+ if (pdf_action_type(p) == pdf_action_page)
+ delete_token_ref(pdf_action_tokens(p));
+ else if (pdf_action_named_id(p) > 0)
+ delete_token_ref(pdf_action_id(p));
+ }
+ break;
+ case pdf_thread_data_node:
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (pdf_thread_named_id(p) > 0)
+ delete_token_ref(pdf_thread_id(p));
+ if (pdf_thread_attr(p) != null)
+ delete_token_ref(pdf_thread_attr(p));
+ break;
+ }
+}
+
+void flush_node(halfword p)
+{
+ halfword w;
+ if (p == null){
+ /*tex legal, but no-op. */
+ return;
+ }
+ if (free_error(p))
+ return;
+ switch (type(p)) {
+ case glyph_node:
+ free_sub_list(lig_ptr(p));
+ break;
+ case glue_node:
+ free_sub_list(leader_ptr(p));
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ free_sub_list(list_ptr(p));
+ break;
+ case disc_node:
+ /*tex Watch the start at temp node hack! */
+ free_sub_list(vlink(pre_break(p)));
+ free_sub_list(vlink(post_break(p)));
+ free_sub_list(vlink(no_break(p)));
+ break;
+ case rule_node:
+ case kern_node:
+ case penalty_node:
+ case math_node:
+ break;
+ case glue_spec_node:
+ /*tex This allows free-ing of lua-allocated glue specs. */
+ break ;
+ case dir_node:
+ break;
+ case local_par_node:
+ free_sub_list(local_box_left(p));
+ free_sub_list(local_box_right(p));
+ break;
+ case boundary_node:
+ break;
+ case whatsit_node:
+ w = subtype(p) ;
+ if (w >= backend_first_pdf_whatsit) {
+ flush_node_wrapup_pdf(p);
+ } else if (w >= backend_first_dvi_whatsit) {
+ flush_node_wrapup_dvi(p);
+ } else {
+ flush_node_wrapup_core(p);
+ }
+ break;
+ case ins_node:
+ flush_node_list(ins_ptr(p));
+ break;
+ case margin_kern_node:
+ flush_node(margin_char(p));
+ break;
+ case mark_node:
+ delete_token_ref(mark_ptr(p));
+ break;
+ case adjust_node:
+ flush_node_list(adjust_ptr(p));
+ break;
+ case style_node:
+ /*tex Nothing to do. */
+ break;
+ case choice_node:
+ free_sub_list(display_mlist(p));
+ free_sub_list(text_mlist(p));
+ free_sub_list(script_mlist(p));
+ free_sub_list(script_script_mlist(p));
+ break;
+ case simple_noad:
+ free_sub_list(nucleus(p));
+ free_sub_list(subscr(p));
+ free_sub_list(supscr(p));
+ break;
+ case radical_noad:
+ free_sub_list(nucleus(p));
+ free_sub_list(subscr(p));
+ free_sub_list(supscr(p));
+ free_sub_node(left_delimiter(p));
+ free_sub_list(degree(p));
+ break;
+ case accent_noad:
+ free_sub_list(nucleus(p));
+ free_sub_list(subscr(p));
+ free_sub_list(supscr(p));
+ free_sub_list(top_accent_chr(p));
+ free_sub_list(bot_accent_chr(p));
+ free_sub_list(overlay_accent_chr(p));
+ break;
+ case fence_noad:
+ free_sub_list(delimiter(p));
+ break;
+ case delim_node:
+ case math_char_node:
+ case math_text_char_node:
+ /*tex Nothing to do. */
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ free_sub_list(math_list(p));
+ break;
+ case fraction_noad:
+ free_sub_list(numerator(p));
+ free_sub_list(denominator(p));
+ free_sub_node(left_delimiter(p));
+ free_sub_node(right_delimiter(p));
+ break;
+ case pseudo_file_node:
+ free_sub_list(pseudo_lines(p));
+ break;
+ case pseudo_line_node:
+ case shape_node:
+ free_node(p, subtype(p));
+ return;
+ break;
+ case align_stack_node:
+ case span_node:
+ case movement_node:
+ case if_node:
+ case nesting_node:
+ case unhyphenated_node:
+ case hyphenated_node:
+ case delta_node:
+ case passive_node:
+ case inserting_node:
+ case split_up_node:
+ case expr_node:
+ case attribute_node:
+ case attribute_list_node:
+ case temp_node:
+ break;
+ default:
+ formatted_error("nodes","flushing weird node type %d", type(p));
+ return;
+ }
+ if (nodetype_has_attributes(type(p))) {
+ delete_attribute_ref(node_attr(p));
+ lua_properties_reset(p);
+ }
+ free_node(p, get_node_size(type(p), subtype(p)));
+ return;
+}
+
+/*tex Erase the list of nodes starting at |pp|. */
+
+void flush_node_list(halfword pp)
+{
+ register halfword p = pp;
+ if (p == null) {
+ /*tex Legal, but no-op. */
+ return;
+ }
+ if (free_error(p))
+ return;
+ /*tex Saves stack and time. */
+ lua_properties_push;
+ while (p != null) {
+ register halfword q = vlink(p);
+ flush_node(p);
+ p = q;
+ }
+ /*tex Saves stack and time. */
+ lua_properties_pop;
+}
+
+static void check_node_wrapup_core(halfword p)
+{
+ switch (subtype(p)) {
+ /*tex Frontend code. */
+ case special_node:
+ check_token_ref(p);
+ break;
+ case user_defined_node:
+ switch (user_node_type(p)) {
+ case 'a':
+ check_attribute_ref(user_node_value(p));
+ break;
+ case 't':
+ check_token_ref(p);
+ break;
+ case 'n':
+ dorangetest(p, user_node_value(p), var_mem_max);
+ break;
+ case 's':
+ case 'd':
+ case 'l':
+ break;
+ default:
+ confusion("unknown user node type");
+ break;
+ }
+ break;
+ case open_node:
+ case write_node:
+ case close_node:
+ case save_pos_node:
+ break;
+ }
+}
+
+void check_node_wrapup_dvi(halfword p)
+{
+}
+
+void check_node_wrapup_pdf(halfword p)
+{
+ switch (subtype(p)) {
+ case pdf_literal_node:
+ if (pdf_literal_type(p) == normal)
+ check_token_ref(p);
+ break;
+ case pdf_colorstack_node:
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ check_token_ref(p);
+ break;
+ case pdf_setmatrix_node:
+ check_token_ref(p);
+ break;
+ case late_lua_node:
+ if (late_lua_name(p) > 0)
+ check_token_ref(p);
+ if (late_lua_type(p) == normal)
+ check_token_ref(p);
+ break;
+ case pdf_annot_node:
+ check_token_ref(p);
+ break;
+ case pdf_start_link_node:
+ if (pdf_link_attr(p) != null)
+ check_token_ref(p);
+ check_action_ref(pdf_link_action(p));
+ break;
+ case pdf_dest_node:
+ if (pdf_dest_named_id(p) > 0)
+ check_token_ref(p);
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (pdf_thread_named_id(p) > 0)
+ check_token_ref(p);
+ if (pdf_thread_attr(p) != null)
+ check_token_ref(p);
+ break;
+ case pdf_save_node:
+ case pdf_restore_node:
+ case pdf_link_state_node:
+ case pdf_refobj_node:
+ case pdf_end_link_node:
+ case pdf_end_thread_node:
+ break;
+ default:
+ confusion("wrapup pdf nodes");
+ break;
+ }
+}
+
+void check_node(halfword p)
+{
+ halfword w ;
+ switch (type(p)) {
+ case glyph_node:
+ dorangetest(p, lig_ptr(p), var_mem_max);
+ break;
+ case glue_node:
+ dorangetest(p, leader_ptr(p), var_mem_max);
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ case align_record_node:
+ dorangetest(p, list_ptr(p), var_mem_max);
+ break;
+ case ins_node:
+ dorangetest(p, ins_ptr(p), var_mem_max);
+ break;
+ case whatsit_node:
+ w = subtype(p) ;
+ if (w >= backend_first_pdf_whatsit) {
+ check_node_wrapup_pdf(p);
+ } else if (w >= backend_first_dvi_whatsit) {
+ check_node_wrapup_dvi(p);
+ } else {
+ check_node_wrapup_core(p);
+ }
+ break;
+ case margin_kern_node:
+ check_node(margin_char(p));
+ break;
+ case math_node:
+ break;
+ case disc_node:
+ dorangetest(p, vlink(pre_break(p)), var_mem_max);
+ dorangetest(p, vlink(post_break(p)), var_mem_max);
+ dorangetest(p, vlink(no_break(p)), var_mem_max);
+ break;
+ case adjust_node:
+ dorangetest(p, adjust_ptr(p), var_mem_max);
+ break;
+ case pseudo_file_node:
+ dorangetest(p, pseudo_lines(p), var_mem_max);
+ break;
+ case pseudo_line_node:
+ case shape_node:
+ break;
+ case choice_node:
+ dorangetest(p, display_mlist(p), var_mem_max);
+ dorangetest(p, text_mlist(p), var_mem_max);
+ dorangetest(p, script_mlist(p), var_mem_max);
+ dorangetest(p, script_script_mlist(p), var_mem_max);
+ break;
+ case fraction_noad:
+ dorangetest(p, numerator(p), var_mem_max);
+ dorangetest(p, denominator(p), var_mem_max);
+ dorangetest(p, left_delimiter(p), var_mem_max);
+ dorangetest(p, right_delimiter(p), var_mem_max);
+ break;
+ case simple_noad:
+ dorangetest(p, nucleus(p), var_mem_max);
+ dorangetest(p, subscr(p), var_mem_max);
+ dorangetest(p, supscr(p), var_mem_max);
+ break;
+ case radical_noad:
+ dorangetest(p, nucleus(p), var_mem_max);
+ dorangetest(p, subscr(p), var_mem_max);
+ dorangetest(p, supscr(p), var_mem_max);
+ dorangetest(p, degree(p), var_mem_max);
+ dorangetest(p, left_delimiter(p), var_mem_max);
+ break;
+ case accent_noad:
+ dorangetest(p, nucleus(p), var_mem_max);
+ dorangetest(p, subscr(p), var_mem_max);
+ dorangetest(p, supscr(p), var_mem_max);
+ dorangetest(p, top_accent_chr(p), var_mem_max);
+ dorangetest(p, bot_accent_chr(p), var_mem_max);
+ dorangetest(p, overlay_accent_chr(p), var_mem_max);
+ break;
+ case fence_noad:
+ dorangetest(p, delimiter(p), var_mem_max);
+ break;
+ case local_par_node:
+ dorangetest(p, local_box_left(p), var_mem_max);
+ dorangetest(p, local_box_right(p), var_mem_max);
+ break;
+ /*tex
+
+ There is no need for useless cases:
+
+ \starttyping
+ case rule_node:
+ case kern_node:
+ case penalty_node:
+ case mark_node:
+ case style_node:
+ case attribute_list_node:
+ case attribute_node:
+ case glue_spec_node:
+ case temp_node:
+ case align_stack_node:
+ case movement_node:
+ case if_node:
+ case nesting_node:
+ case span_node:
+ case unhyphenated_node:
+ case hyphenated_node:
+ case delta_node:
+ case passive_node:
+ case expr_node:
+ case dir_node:
+ case boundary_node:
+ break;
+ default:
+ fprintf(stdout, "check_node: type is %d\n", type(p));
+ \stoptyping
+
+ */
+ }
+}
+
+halfword fix_node_list(halfword head)
+{
+ halfword next, tail;
+ if (head == null)
+ return null;
+ tail = head;
+ next = vlink(head);
+ while (next != null) {
+ alink(next) = tail;
+ tail = next;
+ next = vlink(tail);
+ }
+ return tail;
+}
+
+halfword get_node(int s)
+{
+ register halfword r;
+
+ if (s < MAX_CHAIN_SIZE) {
+ r = free_chain[s];
+ if (r != null) {
+ free_chain[s] = vlink(r);
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[r] = (char) s;
+#endif
+ vlink(r) = null;
+ /*tex Maintain usage statistics. */
+ var_used += s;
+ return r;
+ }
+ /*tex This is the end of the \quote {inner loop}. */
+ return slow_get_node(s);
+ } else {
+ normal_error("nodes","there is a problem in getting a node, case 1");
+ return null;
+ }
+}
+
+void free_node(halfword p, int s)
+{
+ if (p <= my_prealloc) {
+ formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p));
+ return;
+ }
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[p] = 0;
+#endif
+ if (s < MAX_CHAIN_SIZE) {
+ vlink(p) = free_chain[s];
+ free_chain[s] = p;
+ } else {
+ /*tex Todo: it is perhaps possible to merge this node with an existing rover? */
+ node_size(p) = s;
+ vlink(p) = rover;
+ while (vlink(rover) != vlink(p)) {
+ rover = vlink(rover);
+ }
+ vlink(rover) = p;
+ }
+ /*tex Maintain statistics. */
+ var_used -= s;
+}
+
+static void free_node_chain(halfword q, int s)
+{
+ register halfword p = q;
+ while (vlink(p) != null) {
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[p] = 0;
+#endif
+ var_used -= s;
+ p = vlink(p);
+ }
+ var_used -= s;
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[p] = 0;
+#endif
+ vlink(p) = free_chain[s];
+ free_chain[s] = q;
+}
+
+/*tex
+
+ At the start of the node memory area we reserve some special nodes, for
+ instance frequently used glue specifications. We could as well just use
+ new_glue here but for the moment we stick to the traditional approach.
+
+*/
+
+#define initialize_glue(n,wi,st,sh,sto,sho) \
+ vlink(n) = null; \
+ type(n) = glue_spec_node; \
+ width(n) = wi; \
+ stretch(n) = st; \
+ shrink(n) = sh; \
+ stretch_order(n) = sto; \
+ shrink_order(n) = sho;
+
+#define initialize_whatever(n,t) \
+ vinfo(n) = 0; \
+ type(n) = t; \
+ vlink(n) = null; \
+ alink(n) = null;
+
+#define initialize_point(n) \
+ type(n) = glyph_node; \
+ subtype(n) = 0; \
+ vlink(n) = null; \
+ vinfo(n + 1) = null; \
+ alink(n) = null; \
+ font(n) = 0; \
+ character(n) = '.'; \
+ vinfo(n + 3) = 0; \
+ vlink(n + 3) = 0; \
+ vinfo(n + 4) = 0; \
+ vlink(n + 4) = 0;
+
+void init_node_mem(int t)
+{
+ my_prealloc = var_mem_stat_max;
+
+ varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t);
+ if (varmem == NULL) {
+ overflow("node memory size", (unsigned) var_mem_max);
+ }
+ memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word));
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t);
+ if (varmem_sizes == NULL) {
+ overflow("node memory size", (unsigned) var_mem_max);
+ }
+ memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t);
+#endif
+ var_mem_max = t;
+ rover = var_mem_stat_max + 1;
+ vlink(rover) = rover;
+ node_size(rover) = (t - rover);
+ var_used = 0;
+
+ /*tex Initialize static glue specs. */
+
+ initialize_glue(zero_glue,0,0,0,0,0);
+ initialize_glue(sfi_glue,0,0,0,sfi,0);
+ initialize_glue(fil_glue,0,unity,0,fil,0);
+ initialize_glue(fill_glue,0,unity,0,fill,0);
+ initialize_glue(ss_glue,0,unity,unity,fil,fil);
+ initialize_glue(fil_neg_glue,0,-unity,0,fil,0);
+
+ /*tex Initialize node list heads. */
+
+ initialize_whatever(page_ins_head,temp_node);
+ initialize_whatever(contrib_head,temp_node);
+ initialize_whatever(page_head,temp_node);
+ initialize_whatever(temp_head,temp_node);
+ initialize_whatever(hold_head,temp_node);
+ initialize_whatever(adjust_head,temp_node);
+ initialize_whatever(pre_adjust_head,temp_node);
+ initialize_whatever(align_head,temp_node);
+
+ initialize_whatever(active,unhyphenated_node);
+ initialize_whatever(end_span,span_node);
+
+ initialize_point(begin_point);
+ initialize_point(end_point);
+}
+
+void dump_node_mem(void)
+{
+ dump_int(var_mem_max);
+ dump_int(rover);
+ dump_things(varmem[0], var_mem_max);
+#ifdef CHECK_NODE_USAGE
+ dump_things(varmem_sizes[0], var_mem_max);
+#endif
+ dump_things(free_chain[0], MAX_CHAIN_SIZE);
+ dump_int(var_used);
+ dump_int(my_prealloc);
+}
+
+/*tex
+
+ It makes sense to enlarge the varmem array immediately.
+
+*/
+
+void undump_node_mem(void)
+{
+ int x;
+ undump_int(x);
+ undump_int(rover);
+ var_mem_max = (x < 100000 ? 100000 : x);
+ varmem = xmallocarray(memory_word, (unsigned) var_mem_max);
+ undump_things(varmem[0], x);
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
+ memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char));
+ undump_things(varmem_sizes[0], x);
+#endif
+ undump_things(free_chain[0], MAX_CHAIN_SIZE);
+ undump_int(var_used);
+ undump_int(my_prealloc);
+ if (var_mem_max > x) {
+ /*tex Todo: is it perhaps possible to merge the new node with an existing rover? */
+ vlink(x) = rover;
+ node_size(x) = (var_mem_max - x);
+ while (vlink(rover) != vlink(x)) {
+ rover = vlink(rover);
+ }
+ vlink(rover) = x;
+ }
+}
+
+halfword slow_get_node(int s)
+{
+ register int t;
+
+ RETRY:
+ t = node_size(rover);
+ if (vlink(rover) < var_mem_max && vlink(rover) != 0) {
+ if (t > s) {
+ /*tex Allocating from the bottom helps decrease page faults. */
+ register halfword r = rover;
+ rover += s;
+ vlink(rover) = vlink(r);
+ node_size(rover) = node_size(r) - s;
+ if (vlink(rover) != r) {
+ /*tex The list is longer than one. */
+ halfword q = r;
+ while (vlink(q) != r) {
+ q = vlink(q);
+ }
+ vlink(q) += s;
+ } else {
+ vlink(rover) += s;
+ }
+ if (vlink(rover) < var_mem_max) {
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[r] = (char) (s > 127 ? 127 : s);
+#endif
+ vlink(r) = null;
+ /*tex Maintain usage statistics. */
+ var_used += s;
+ /*tex This is the only exit. */
+ return r;
+ } else {
+ normal_error("nodes","there is a problem in getting a node, case 2");
+ return null;
+ }
+ } else {
+ /*tex Attempt to keep the free list small. */
+ int x;
+ if (vlink(rover) != rover) {
+ if (t < MAX_CHAIN_SIZE) {
+ halfword l = vlink(rover);
+ vlink(rover) = free_chain[t];
+ free_chain[t] = rover;
+ rover = l;
+ while (vlink(l) != free_chain[t]) {
+ l = vlink(l);
+ }
+ vlink(l) = rover;
+ goto RETRY;
+ } else {
+ halfword l = rover;
+ while (vlink(rover) != l) {
+ if (node_size(rover) > s) {
+ goto RETRY;
+ }
+ rover = vlink(rover);
+ }
+ }
+ }
+ /*tex If we are still here, it was apparently impossible to get a match. */
+ x = (var_mem_max >> 2) + s;
+ varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x));
+ if (varmem == NULL) {
+ overflow("node memory size", (unsigned) var_mem_max);
+ }
+ memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word));
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x));
+ if (varmem_sizes == NULL) {
+ overflow("node memory size", (unsigned) var_mem_max);
+ }
+ memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char));
+#endif
+ /*tex Todo: is it perhaps possible to merge the new memory with an existing rover? */
+ vlink(var_mem_max) = rover;
+ node_size(var_mem_max) = x;
+ while (vlink(rover) != vlink(var_mem_max)) {
+ rover = vlink(rover);
+ }
+ vlink(rover) = var_mem_max;
+ rover = var_mem_max;
+ var_mem_max += x;
+ goto RETRY;
+ }
+ } else {
+ normal_error("nodes","there is a problem in getting a node, case 3");
+ return null;
+ }
+}
+
+char *sprint_node_mem_usage(void)
+{
+ char *s;
+#ifdef CHECK_NODE_USAGE
+ char *ss;
+ int i;
+ int b = 0;
+ char msg[256];
+ int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 };
+ s = strdup("");
+ for (i = (var_mem_max - 1); i > my_prealloc; i--) {
+ if (varmem_sizes[i] > 0) {
+ if (type(i) > last_normal_node + last_whatsit_node) {
+ node_counts[last_normal_node + last_whatsit_node + 1]++;
+ } else if (type(i) == whatsit_node) {
+ node_counts[(subtype(i) + last_normal_node + 1)]++;
+ } else {
+ node_counts[type(i)]++;
+ }
+ }
+ }
+ for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) {
+ if (node_counts[i] > 0) {
+ int j =
+ (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0);
+ snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i],
+ get_node_name((i > last_normal_node ? whatsit_node : i), j));
+ ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1));
+ strcpy(ss, s);
+ strcat(ss, msg);
+ free(s);
+ s = ss;
+ b = 1;
+ }
+ }
+#else
+ s = strdup("");
+#endif
+ return s;
+}
+
+halfword list_node_mem_usage(void)
+{
+ halfword q = null;
+#ifdef CHECK_NODE_USAGE
+ halfword p = null;
+ halfword i, j;
+ char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
+ memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max);
+ for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) {
+ if (saved_varmem_sizes[i] > 0) {
+ j = copy_node(i);
+ if (p == null) {
+ q = j;
+ } else {
+ vlink(p) = j;
+ }
+ p = j;
+ }
+ }
+ free(saved_varmem_sizes);
+#endif
+ return q;
+}
+
+void print_node_mem_stats(void)
+{
+ int i, b;
+ halfword j;
+ char msg[256];
+ char *s;
+ int free_chain_counts[MAX_CHAIN_SIZE] = { 0 };
+ snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc));
+ tprint_nl(msg);
+ s = sprint_node_mem_usage();
+ tprint_nl(" ");
+ tprint(s);
+ free(s);
+ tprint(" nodes");
+ tprint_nl(" avail lists: ");
+ b = 0;
+ for (i = 1; i < MAX_CHAIN_SIZE; i++) {
+ for (j = free_chain[i]; j != null; j = vlink(j))
+ free_chain_counts[i]++;
+ if (free_chain_counts[i] > 0) {
+ snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]);
+ tprint(msg);
+ b = 1;
+ }
+ }
+ /*tex A newline, if needed: */
+ print_nlp();
+}
+
+halfword new_span_node(halfword n, int s, scaled w)
+{
+ halfword p = new_node(span_node, 0);
+ span_link(p) = n;
+ span_span(p) = s;
+ width(p) = w;
+ return p;
+}
+
+/* Now comes some attribute stuff. */
+
+static halfword new_attribute_node(unsigned int i, int v)
+{
+ register halfword r = get_node(attribute_node_size);
+ type(r) = attribute_node;
+ attribute_id(r) = (halfword) i;
+ attribute_value(r) = v;
+ /* not used but nicer in print */
+ subtype(r) = 0;
+ return r;
+}
+
+halfword copy_attribute_list(halfword n)
+{
+ halfword q = get_node(attribute_node_size);
+ register halfword p = q;
+ type(p) = attribute_list_node;
+ attr_list_ref(p) = 0;
+ n = vlink(n);
+ while (n != null) {
+ register halfword r = get_node(attribute_node_size);
+ /*tex The link will be fixed automatically in the next loop. */
+ (void) memcpy((void *) (varmem + r), (void *) (varmem + n), (sizeof(memory_word) * attribute_node_size));
+ vlink(p) = r;
+ p = r;
+ n = vlink(n);
+ }
+ return q;
+}
+
+void update_attribute_cache(void)
+{
+ halfword p;
+ register int i;
+ attr_list_cache = get_node(attribute_node_size);
+ type(attr_list_cache) = attribute_list_node;
+ attr_list_ref(attr_list_cache) = 0;
+ p = attr_list_cache;
+ for (i = 0; i <= max_used_attr; i++) {
+ register int v = attribute(i);
+ if (v > UNUSED_ATTRIBUTE) {
+ register halfword r = new_attribute_node((unsigned) i, v);
+ vlink(p) = r;
+ p = r;
+ }
+ }
+ if (vlink(attr_list_cache) == null) {
+ free_node(attr_list_cache, attribute_node_size);
+ attr_list_cache = null;
+ }
+ return;
+}
+
+void build_attribute_list(halfword b)
+{
+ if (max_used_attr >= 0) {
+ if (attr_list_cache == cache_disabled|| attr_list_cache == null) {
+ update_attribute_cache();
+ if (attr_list_cache == null)
+ return;
+ }
+ attr_list_ref(attr_list_cache)++;
+ node_attr(b) = attr_list_cache;
+ }
+}
+
+halfword current_attribute_list(void)
+{
+ if (max_used_attr >= 0) {
+ if (attr_list_cache == cache_disabled) {
+ update_attribute_cache();
+ }
+ return attr_list_cache ;
+ }
+ return null ;
+}
+
+void reassign_attribute(halfword n, halfword new)
+{
+ halfword old;
+ old = node_attr(n);
+ if (new == null) {
+ /*tex There is nothing to assign but we need to check for an old value. */
+ if (old != null) {
+ /*tex This also nulls |attr| field of |n|. */
+ delete_attribute_ref(old);
+ }
+ } else if (old == null) {
+ /*tex Nothing is assigned so we just do that now. */
+ assign_attribute_ref(n,new);
+ } else if (old != new) {
+ /*tex Something is assigned so we need to clean up and assign then. */
+ delete_attribute_ref(old);
+ assign_attribute_ref(n,new);
+ }
+ /*tex The same value so there is no need to assign and change the refcount. */
+ node_attr(n) = new ;
+}
+
+void delete_attribute_ref(halfword b)
+{
+ if (b != null) {
+ if (type(b) == attribute_list_node){
+ attr_list_ref(b)--;
+ if (attr_list_ref(b) == 0) {
+ if (b == attr_list_cache)
+ attr_list_cache = cache_disabled;
+ free_node_chain(b, attribute_node_size);
+ }
+ /*tex Maintain sanity. */
+ if (attr_list_ref(b) < 0) {
+ attr_list_ref(b) = 0;
+ }
+ } else {
+ normal_error("nodes","trying to delete an attribute reference of a non attribute node");
+ }
+ }
+}
+
+void reset_node_properties(halfword b)
+{
+ if (b != null) {
+ lua_properties_reset(b);
+ }
+}
+
+/*tex Here |p| is an attr list head, or zero. */
+
+halfword do_set_attribute(halfword p, int i, int val)
+{
+ register halfword q;
+ register int j = 0;
+ if (p == null) {
+ /*tex Add a new head \& node. */
+ q = get_node(attribute_node_size);
+ type(q) = attribute_list_node;
+ attr_list_ref(q) = 1;
+ p = new_attribute_node((unsigned) i, val);
+ vlink(q) = p;
+ return q;
+ }
+ q = p;
+ if (vlink(p) != null) {
+ while (vlink(p) != null) {
+ int t = attribute_id(vlink(p));
+ if (t == i && attribute_value(vlink(p)) == val) {
+ /*tex There is no need to do anything. */
+ return q;
+ }
+ if (t >= i)
+ break;
+ j++;
+ p = vlink(p);
+ }
+ p = q;
+ while (j-- > 0)
+ p = vlink(p);
+ if (attribute_id(vlink(p)) == i) {
+ attribute_value(vlink(p)) = val;
+ } else {
+ /*tex Add a new node. */
+ halfword r = new_attribute_node((unsigned) i, val);
+ vlink(r) = vlink(p);
+ vlink(p) = r;
+ }
+ return q;
+ } else {
+ normal_error("nodes","trying to set an attribute fails, case 1");
+ return null ;
+ }
+}
+
+void set_attribute(halfword n, int i, int val)
+{
+ register halfword p;
+ register int j = 0;
+ /*tex Not all nodes can have an attribute list. */
+ if (!nodetype_has_attributes(type(n)))
+ return;
+ /*tex If we have no list, we create one and quit. */
+ p = node_attr(n);
+ if (p == null) { /* add a new head \& node */
+ p = get_node(attribute_node_size);
+ type(p) = attribute_list_node;
+ attr_list_ref(p) = 1;
+ node_attr(n) = p;
+ p = new_attribute_node((unsigned) i, val);
+ vlink(node_attr(n)) = p;
+ return;
+ }
+ /*tex We check if we have this attribute already and quit if the value stays the same. */
+ if (vlink(p) != null) {
+ while (vlink(p) != null) {
+ int t = attribute_id(vlink(p));
+ if (t == i && attribute_value(vlink(p)) == val)
+ return;
+ if (t >= i)
+ break;
+ j++;
+ p = vlink(p);
+ }
+ /*tex If found |j| has now the position and we assume a sorted list ! */
+ p = node_attr(n);
+ if (attr_list_ref(p) == 0 ) {
+ /*tex The list is invalid i.e. freed already. */
+ formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n);
+ /*tex The still dangling list gets ref count 1. */
+ attr_list_ref(p) = 1;
+ } else if (attr_list_ref(p) == 1) {
+ /*tex This can really happen! */
+ if (p == attr_list_cache) {
+ /*tex
+
+ We can invalidate the cache setting with |attr_list_cache =
+ cache_disabled| or or save the list, as done below.
+
+ */
+ p = copy_attribute_list(p);
+ node_attr(n) = p;
+ /*tex The copied list gets ref count 1. */
+ attr_list_ref(p) = 1;
+ }
+ } else {
+ /*tex The list is used multiple times so we make a copy. */
+ p = copy_attribute_list(p);
+ /*tex We decrement the ref count or the original. */
+ delete_attribute_ref(node_attr(n));
+ node_attr(n) = p;
+ /*tex The copied list gets ref count 1. */
+ attr_list_ref(p) = 1;
+ }
+ /*tex We go to position |j| in the list. */
+ while (j-- > 0)
+ p = vlink(p);
+ /*tex If we have a hit we just set the value otherwise we add a new node. */
+ if (attribute_id(vlink(p)) == i) {
+ attribute_value(vlink(p)) = val;
+ } else {
+ /*tex Add a new node. */
+ halfword r = new_attribute_node((unsigned) i, val);
+ vlink(r) = vlink(p);
+ vlink(p) = r;
+ }
+ } else {
+ normal_error("nodes","trying to set an attribute fails, case 2");
+ }
+}
+
+int unset_attribute(halfword n, int i, int val)
+{
+ register halfword p;
+ register int t;
+ register int j = 0;
+ if (!nodetype_has_attributes(type(n)))
+ return null;
+ p = node_attr(n);
+ if (p == null)
+ return UNUSED_ATTRIBUTE;
+ if (attr_list_ref(p) == 0) {
+ formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n);
+ return UNUSED_ATTRIBUTE;
+ }
+ if (vlink(p) != null) {
+ while (vlink(p) != null) {
+ t = attribute_id(vlink(p));
+ if (t > i)
+ return UNUSED_ATTRIBUTE;
+ if (t == i) {
+ p = vlink(p);
+ break;
+ }
+ j++;
+ p = vlink(p);
+ }
+ if (attribute_id(p) != i)
+ return UNUSED_ATTRIBUTE;
+ /*tex If we are still here, the attribute exists. */
+ p = node_attr(n);
+ if (attr_list_ref(p) > 1 || p == attr_list_cache) {
+ halfword q = copy_attribute_list(p);
+ if (attr_list_ref(p) > 1) {
+ delete_attribute_ref(node_attr(n));
+ }
+ attr_list_ref(q) = 1;
+ node_attr(n) = q;
+ }
+ p = vlink(node_attr(n));
+ while (j-- > 0)
+ p = vlink(p);
+ t = attribute_value(p);
+ if (val == UNUSED_ATTRIBUTE || t == val) {
+ attribute_value(p) = UNUSED_ATTRIBUTE;
+ }
+ return t;
+ } else {
+ normal_error("nodes","trying to unset an attribute fails");
+ return null;
+ }
+}
+
+int has_attribute(halfword n, int i, int val)
+{
+ register halfword p;
+ if (!nodetype_has_attributes(type(n)))
+ return UNUSED_ATTRIBUTE;
+ p = node_attr(n);
+ if (p == null || vlink(p) == null)
+ return UNUSED_ATTRIBUTE;
+ p = vlink(p);
+ while (p != null) {
+ if (attribute_id(p) == i) {
+ int ret = attribute_value(p);
+ if (val == UNUSED_ATTRIBUTE || val == ret)
+ return ret;
+ return UNUSED_ATTRIBUTE;
+ } else if (attribute_id(p) > i) {
+ return UNUSED_ATTRIBUTE;
+ }
+ p = vlink(p);
+ }
+ return UNUSED_ATTRIBUTE;
+}
+
+void print_short_node_contents(halfword 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_char('[');
+ print_char(']');
+ break;
+ case rule_node:
+ print_char('|');
+ break;
+ case glue_node:
+ if (! glue_is_zero(p))
+ print_char(' ');
+ break;
+ case math_node:
+ print_char('$');
+ break;
+ case disc_node:
+ short_display(vlink(pre_break(p)));
+ short_display(vlink(post_break(p)));
+ break;
+ }
+}
+
+static void show_pdftex_whatsit_rule_spec(int p)
+{
+ tprint("(");
+ print_rule_dimen(height(p));
+ print_char('+');
+ print_rule_dimen(depth(p));
+ tprint(")x");
+ print_rule_dimen(width(p));
+}
+
+/*tex
+
+ 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.
+
+*/
+
+static void print_write_whatsit(const char *s, pointer p)
+{
+ tprint_esc(s);
+ if (write_stream(p) < 16)
+ print_int(write_stream(p));
+ else if (write_stream(p) == 16)
+ print_char('*');
+ else
+ print_char('-');
+}
+
+static void show_node_wrapup_core(int p)
+{
+ 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 special_node:
+ tprint_esc("special");
+ print_mark(write_tokens(p));
+ break;
+ case late_lua_node:
+ show_late_lua(p);
+ break;
+ case save_pos_node:
+ tprint_esc("savepos");
+ break;
+ case user_defined_node:
+ tprint_esc("whatsit");
+ print_int(user_node_id(p));
+ print_char('=');
+ switch (user_node_type(p)) {
+ case 'a':
+ tprint("<>");
+ break;
+ case 'n':
+ tprint("[");
+ show_node_list(user_node_value(p));
+ tprint("]");
+ break;
+ case 's':
+ print_char('"');
+ print(user_node_value(p));
+ print_char('"');
+ break;
+ case 't':
+ print_mark(user_node_value(p));
+ break;
+ default: /* only 'd' */
+ print_int(user_node_value(p));
+ break;
+ }
+ break;
+ }
+}
+
+void show_node_wrapup_dvi(int p)
+{
+}
+
+void show_node_wrapup_pdf(int p)
+{
+ switch (subtype(p)) {
+ case pdf_literal_node:
+ show_pdf_literal(p);
+ break;
+ case pdf_colorstack_node:
+ tprint_esc("pdfcolorstack ");
+ print_int(pdf_colorstack_stack(p));
+ switch (pdf_colorstack_cmd(p)) {
+ case colorstack_set:
+ tprint(" set ");
+ break;
+ case colorstack_push:
+ tprint(" push ");
+ break;
+ case colorstack_pop:
+ tprint(" pop");
+ break;
+ case colorstack_current:
+ tprint(" current");
+ break;
+ default:
+ confusion("colorstack");
+ break;
+ }
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ print_mark(pdf_colorstack_data(p));
+ break;
+ case pdf_setmatrix_node:
+ tprint_esc("pdfsetmatrix");
+ print_mark(pdf_setmatrix_data(p));
+ break;
+ case pdf_save_node:
+ tprint_esc("pdfsave");
+ break;
+ case pdf_restore_node:
+ tprint_esc("pdfrestore");
+ break;
+ case pdf_link_state_node:
+ tprint_esc("pdflinkstate ");
+ print_int(pdf_link_state(p));
+ break;
+ case pdf_refobj_node:
+ tprint_esc("pdfrefobj");
+ if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) {
+ if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
+ tprint(" attr");
+ lua_rawgeti(Luas, LUA_REGISTRYINDEX, obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)));
+ print_char(' ');
+ tprint((const char *) lua_tostring(Luas, -1));
+ lua_pop(Luas, 1);
+ }
+ tprint(" stream");
+ }
+ if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p)))
+ tprint(" file");
+ if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
+ lua_rawgeti(Luas, LUA_REGISTRYINDEX, obj_obj_data(static_pdf, pdf_obj_objnum(p)));
+ print_char(' ');
+ tprint((const char *) lua_tostring(Luas, -1));
+ lua_pop(Luas, 1);
+ }
+ break;
+ case pdf_annot_node:
+ tprint_esc("pdfannot");
+ show_pdftex_whatsit_rule_spec(p);
+ print_mark(pdf_annot_data(p));
+ break;
+ case pdf_start_link_node:
+ tprint_esc("pdfstartlink");
+ show_pdftex_whatsit_rule_spec(p);
+ if (pdf_link_attr(p) != null) {
+ tprint(" attr");
+ print_mark(pdf_link_attr(p));
+ }
+ tprint(" action");
+ if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) {
+ tprint(" user");
+ print_mark(pdf_action_tokens(pdf_link_action(p)));
+ return;
+ }
+ if (pdf_action_file(pdf_link_action(p)) != null) {
+ tprint(" file");
+ print_mark(pdf_action_file(pdf_link_action(p)));
+ }
+ switch (pdf_action_type(pdf_link_action(p))) {
+ case pdf_action_goto:
+ if (pdf_action_named_id(pdf_link_action(p)) > 0) {
+ tprint(" goto name");
+ print_mark(pdf_action_id(pdf_link_action(p)));
+ } else {
+ tprint(" goto num");
+ print_int(pdf_action_id(pdf_link_action(p)));
+ }
+ break;
+ case pdf_action_page:
+ tprint(" page");
+ print_int(pdf_action_id(pdf_link_action(p)));
+ print_mark(pdf_action_tokens(pdf_link_action(p)));
+ break;
+ case pdf_action_thread:
+ if (pdf_action_named_id(pdf_link_action(p)) > 0) {
+ tprint(" thread name");
+ print_mark(pdf_action_id(pdf_link_action(p)));
+ } else {
+ tprint(" thread num");
+ print_int(pdf_action_id(pdf_link_action(p)));
+ }
+ break;
+ default:
+ normal_error("pdf backend", "unknown action type for link");
+ break;
+ }
+ break;
+ case pdf_end_link_node:
+ tprint_esc("pdfendlink");
+ break;
+ case pdf_dest_node:
+ tprint_esc("pdfdest");
+ if (pdf_dest_named_id(p) > 0) {
+ tprint(" name");
+ print_mark(pdf_dest_id(p));
+ } else {
+ tprint(" num");
+ print_int(pdf_dest_id(p));
+ }
+ print_char(' ');
+ switch (pdf_dest_type(p)) {
+ case pdf_dest_xyz:
+ tprint("xyz");
+ if (pdf_dest_xyz_zoom(p) != null) {
+ tprint(" zoom");
+ print_int(pdf_dest_xyz_zoom(p));
+ }
+ break;
+ case pdf_dest_fitbh:
+ tprint("fitbh");
+ break;
+ case pdf_dest_fitbv:
+ tprint("fitbv");
+ break;
+ case pdf_dest_fitb:
+ tprint("fitb");
+ break;
+ case pdf_dest_fith:
+ tprint("fith");
+ break;
+ case pdf_dest_fitv:
+ tprint("fitv");
+ break;
+ case pdf_dest_fitr:
+ tprint("fitr");
+ show_pdftex_whatsit_rule_spec(p);
+ break;
+ case pdf_dest_fit:
+ tprint("fit");
+ break;
+ default:
+ tprint("unknown!");
+ break;
+ }
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (subtype(p) == pdf_thread_node)
+ tprint_esc("pdfthread");
+ else
+ tprint_esc("pdfstartthread");
+ tprint("(");
+ print_rule_dimen(height(p));
+ print_char('+');
+ print_rule_dimen(depth(p));
+ tprint(")x");
+ print_rule_dimen(width(p));
+ if (pdf_thread_attr(p) != null) {
+ tprint(" attr");
+ print_mark(pdf_thread_attr(p));
+ }
+ if (pdf_thread_named_id(p) > 0) {
+ tprint(" name");
+ print_mark(pdf_thread_id(p));
+ } else {
+ tprint(" num");
+ print_int(pdf_thread_id(p));
+ }
+ break;
+ case pdf_end_thread_node:
+ tprint_esc("pdfendthread");
+ break;
+ default:
+ break;
+ }
+}
+
+/*tex
+
+ 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 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.
+
+*/
+
+#define node_list_display(A) do { \
+ append_char('.'); \
+ show_node_list(A); \
+ flush_char(); \
+} while (0)
+
+#define node_list_display_x(A,B) do { \
+ if ((B) != null) { \
+ append_char('.'); \
+ append_char(A); \
+ append_char(' '); \
+ show_node_list(B); \
+ flush_char(); \
+ flush_char(); \
+ flush_char(); \
+ } \
+} while (0)
+
+/*tex Print a node list symbolically: */
+
+void show_node_list(int p)
+{
+ /*tex The number of items already printed at this level: */
+ int n = 0;
+ halfword w;
+ /*tex A glue ratio, as a floating point number: */
+ real g;
+ if ((int) cur_length > depth_threshold) {
+ if (p > null) {
+ /*tex Indicate that there's been some truncation. */
+ tprint(" []");
+ }
+ return;
+ }
+ while (p != null) {
+ print_ln();
+ print_current_string();
+ /*tex Display the nesting history. */
+ if (tracing_online_par < -2)
+ print_int(p);
+ incr(n);
+ if (n > breadth_max) {
+ /*tex Time to stop. */
+ tprint("etc.");
+ return;
+ }
+ /*tex Display node |p|. */
+ if (is_char_node(p)) {
+ print_font_and_char(p);
+ if (is_ligature(p)) {
+ /*tex Display ligature |p|. */
+ tprint(" (ligature ");
+ if (is_leftboundary(p))
+ print_char('|');
+ font_in_short_display = font(p);
+ short_display(lig_ptr(p));
+ if (is_rightboundary(p))
+ print_char('|');
+ print_char(')');
+ }
+ } else {
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ /*tex Display box |p|. */
+ if (type(p) == hlist_node)
+ tprint_esc("h");
+ else if (type(p) == vlist_node)
+ tprint_esc("v");
+ else
+ tprint_esc("unset");
+ tprint("box(");
+ print_scaled(height(p));
+ print_char('+');
+ print_scaled(depth(p));
+ tprint(")x");
+ print_scaled(width(p));
+ if (type(p) == unset_node) {
+ /*tex Display special fields of the unset node |p|. */
+ if (span_count(p) != min_quarterword) {
+ tprint(" (");
+ print_int(span_count(p) + 1);
+ tprint(" columns)");
+ }
+ if (glue_stretch(p) != 0) {
+ tprint(", stretch ");
+ print_glue(glue_stretch(p), glue_order(p), NULL);
+ }
+ if (glue_shrink(p) != 0) {
+ tprint(", shrink ");
+ print_glue(glue_shrink(p), glue_sign(p), NULL);
+ }
+ } else {
+ /*tex
+
+ Display the value of |glue_set(p)|. The code will
+ have to change in this place if |glue_ratio| is a
+ structured type instead of an ordinary |real|. 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 |real| 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.
+
+ */
+ g = (real) (glue_set(p));
+ if ((g != 0.0) && (glue_sign(p) != normal)) {
+ tprint(", glue set ");
+ if (glue_sign(p) == shrinking)
+ tprint("- ");
+ if (g > 20000.0 || g < -20000.0) {
+ if (g > 0.0)
+ print_char('>');
+ else
+ tprint("< -");
+ print_glue(20000 * unity, glue_order(p), NULL);
+ } else {
+ print_glue(round(unity * g), glue_order(p), NULL);
+ }
+ }
+ if (shift_amount(p) != 0) {
+ tprint(", shifted ");
+ print_scaled(shift_amount(p));
+ }
+ tprint(", direction ");
+ print_dir_par(box_dir(p));
+ }
+ /*tex Recursive call: */
+ node_list_display(list_ptr(p));
+ break;
+ case rule_node:
+ /*tex Display rule |p|. */
+ if (subtype(p) == normal_rule) {
+ tprint_esc("rule(");
+ } else if (subtype(p) == empty_rule) {
+ tprint_esc("norule(");
+ } else if (subtype(p) == user_rule) {
+ tprint_esc("userrule(");
+ } else if (subtype(p) == box_rule) {
+ tprint_esc("box(");
+ } else if (subtype(p) == image_rule) {
+ tprint_esc("image(");
+ }
+ print_rule_dimen(height(p));
+ print_char('+');
+ print_rule_dimen(depth(p));
+ tprint(")x");
+ print_rule_dimen(width(p));
+ break;
+ case ins_node:
+ /*tex Display insertion |p|. */
+ tprint_esc("insert");
+ print_int(subtype(p));
+ tprint(", natural size ");
+ print_scaled(height(p));
+ tprint("; split(");
+ print_spec(split_top_ptr(p), NULL);
+ print_char(',');
+ print_scaled(depth(p));
+ tprint("); float cost ");
+ print_int(float_cost(p));
+ /*tex Recursive call. */
+ node_list_display(ins_ptr(p));
+ break;
+ case dir_node:
+ if (subtype(p) == cancel_dir) {
+ tprint_esc("enddir");
+ } else {
+ tprint_esc("begindir");
+ }
+ print_char(' ');
+ print_dir_par(dir_dir(p));
+ break;
+ case local_par_node:
+ tprint_esc("localpar");
+ append_char('.');
+ print_ln();
+ print_current_string();
+ tprint_esc("localinterlinepenalty");
+ print_char('=');
+ print_int(local_pen_inter(p));
+ print_ln();
+ print_current_string();
+ tprint_esc("localbrokenpenalty");
+ print_char('=');
+ print_int(local_pen_broken(p));
+ print_ln();
+ print_current_string();
+ tprint_esc("localleftbox");
+ if (local_box_left(p) == null) {
+ tprint("=null");
+ } else {
+ append_char('.');
+ show_node_list(local_box_left(p));
+ decr(cur_length);
+ }
+ print_ln();
+ print_current_string();
+ tprint_esc("localrightbox");
+ if (local_box_right(p) == null) {
+ tprint("=null");
+ } else {
+ append_char('.');
+ show_node_list(local_box_right(p));
+ decr(cur_length);
+ }
+ decr(cur_length);
+ break;
+ case boundary_node:
+ if (subtype(p)==0) {
+ tprint_esc("noboundary");
+ } else {
+ switch (subtype(p)) {
+ case 1:
+ tprint_esc("boundary");
+ break;
+ case 2:
+ tprint_esc("protrusionboundary");
+ break;
+ case 3:
+ tprint_esc("wordboundary");
+ break;
+ default:
+ tprint_esc("boundary");
+ print_char(':');
+ print_int(subtype(p));
+ break;
+ }
+ print_char('=');
+ print_int(boundary_value(p));
+ }
+ break;
+ case whatsit_node:
+ w = subtype(p) ;
+ if (w >= backend_first_pdf_whatsit) {
+ show_node_wrapup_pdf(p);
+ } else if (w >= backend_first_dvi_whatsit) {
+ show_node_wrapup_dvi(p);
+ } else {
+ show_node_wrapup_core(p);
+ }
+ break;
+ case glue_node:
+ /*tex Display glue |p|. */
+ if (subtype(p) >= a_leaders) {
+ /*tex Display leaders |p|. */
+ tprint_esc("");
+ switch (subtype(p)) {
+ case a_leaders:
+ break;
+ case c_leaders:
+ print_char('c');
+ break;
+ case x_leaders:
+ print_char('x');
+ break;
+ case g_leaders:
+ print_char('g');
+ break;
+ default:
+ normal_warning("nodes","weird glue leader subtype ignored");
+ }
+ tprint("leaders ");
+ print_spec(p, NULL);
+ /*tex Recursive call: */
+ node_list_display(leader_ptr(p));
+ } else {
+ tprint_esc("glue");
+ if (subtype(p) != normal) {
+ print_char('(');
+ if ((subtype(p) - 1) < thin_mu_skip_code) {
+ print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1));
+ } else if (subtype(p) < cond_math_glue) {
+ print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1));
+ } else if (subtype(p) == cond_math_glue) {
+ tprint_esc("nonscript");
+ } else {
+ tprint_esc("mskip");
+ }
+ print_char(')');
+ }
+ if (subtype(p) != cond_math_glue) {
+ print_char(' ');
+ if (subtype(p) < cond_math_glue)
+ print_spec(p, NULL);
+ else
+ print_spec(p, "mu");
+ }
+ }
+ break;
+ case margin_kern_node:
+ tprint_esc("kern");
+ print_scaled(width(p));
+ if (subtype(p) == left_side)
+ tprint(" (left margin)");
+ else
+ tprint(" (right margin)");
+ break;
+ case kern_node:
+ /*tex Display kern |p| */
+ if (subtype(p) != mu_glue) {
+ tprint_esc("kern");
+ /*tex
+
+ \starttyping
+ if (subtype(p) != normal) {
+ print_char(' ');
+ }
+ \stoptyping
+
+ */
+ print_scaled(width(p));
+ if (subtype(p) == font_kern)
+ tprint(" (font)");
+ else if (subtype(p) == italic_kern)
+ tprint(" (italic)");
+ else if (subtype(p) == accent_kern)
+ tprint(" (accent)");
+ } else {
+ tprint_esc("mkern");
+ print_scaled(width(p));
+ tprint("mu");
+ }
+ break;
+ case math_node:
+ /*tex Display math node |p|. */
+ tprint_esc("math");
+ if (subtype(p) == before)
+ tprint("on");
+ else
+ tprint("off");
+ if (!glue_is_zero(p)) {
+ tprint(", glued ");
+ print_spec(p, NULL);
+ } else if (surround(p) != 0) {
+ tprint(", surrounded ");
+ print_scaled(surround(p));
+ }
+ break;
+ case penalty_node:
+ /*tex Display penalty |p|. */
+ tprint_esc("penalty ");
+ print_int(penalty(p));
+ break;
+ case disc_node:
+ /*tex
+
+ Display discretionary |p|. The |post_break| list of a
+ discretionary node is indicated by a prefixed
+ `\.{\char'174}' instead of the `\..' before the
+ |pre_break| list.
+
+ We're not compatible anyway so ...
+
+ \starttyping
+ tprint_esc("discretionary");
+ print_int(disc_penalty(p));
+ print_char('|');
+ if (vlink(no_break(p)) != null) {
+ tprint(" replacing ");
+ node_list_display(vlink(no_break(p)));
+ }
+ node_list_display(vlink(pre_break(p)));
+ append_char('|');
+ show_node_list(vlink(post_break(p)));
+ flush_char();
+ \stoptyping
+
+ has become:
+
+ */
+ tprint_esc("discretionary");
+ tprint(" (penalty ");
+ print_int(disc_penalty(p));
+ print_char(')');
+ node_list_display_x('<',vlink(pre_break(p)));
+ node_list_display_x('>',vlink(post_break(p)));
+ node_list_display_x('=',vlink(no_break(p)));
+ break;
+ case mark_node:
+ /*tex Display mark |p|. */
+ tprint_esc("mark");
+ if (mark_class(p) != 0) {
+ print_char('s');
+ print_int(mark_class(p));
+ }
+ print_mark(mark_ptr(p));
+ break;
+ case adjust_node:
+ /*tex Display adjustment |p|. */
+ tprint_esc("vadjust");
+ if (subtype(p) != 0)
+ tprint(" pre ");
+ /*tex Recursive call. */
+ node_list_display(adjust_ptr(p));
+ break;
+ case glue_spec_node:
+ tprint("<glue_spec ");
+ print_spec(p, NULL);
+ tprint(">");
+ break;
+ default:
+ show_math_node(p);
+ break;
+ }
+ }
+ p = vlink(p);
+ }
+}
+
+/*tex
+
+ This routine finds the 'base' width of a horizontal box, using the same logic
+ that \TeX82 used for \.{\\predisplaywidth}.
+
+*/
+
+static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width)
+{
+ /*tex increment to |v| */
+ scaled d;
+ /*tex calculated |size| */
+ scaled w = -max_dimen;
+ /*tex |w| plus possible glue amount */
+ scaled v = initial_width;
+ while (p != null) {
+ if (is_char_node(p)) {
+ d = glyph_width(p);
+ goto FOUND;
+ }
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ case rule_node:
+ d = width(p);
+ goto FOUND;
+ break;
+ case margin_kern_node:
+ d = width(p);
+ break;
+ case kern_node:
+ d = width(p);
+ break;
+ case disc_node:
+ /*tex At the end of the line we should actually take the |pre|. */
+ if (no_break(p) != null) {
+ d = get_actual_box_width(r,vlink_no_break(p),0);
+ if (d <= -max_dimen || d >= max_dimen) {
+ d = 0;
+ }
+ } else {
+ d = 0;
+ }
+ goto FOUND;
+ break;
+ case math_node:
+ /*tex Begin mathskip code. */
+ if (glue_is_zero(p)) {
+ d = surround(p);
+ break;
+ } else {
+ /*tex Fall through. */
+ }
+ /*tex End mathskip code. */
+ case glue_node:
+ /*tex
+
+ 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.
+
+ */
+ d = width(p);
+ if (glue_sign(r) == stretching) {
+ if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0))
+ v = max_dimen;
+ } else if (glue_sign(r) == shrinking) {
+ if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0))
+ v = max_dimen;
+ }
+ if (subtype(p) >= a_leaders)
+ goto FOUND;
+ break;
+ default:
+ d = 0;
+ break;
+ }
+ if (v < max_dimen)
+ v = v + d;
+ goto NOT_FOUND;
+ FOUND:
+ if (v < max_dimen) {
+ v = v + d;
+ w = v;
+ } else {
+ w = max_dimen;
+ break;
+ }
+ NOT_FOUND:
+ p = vlink(p);
+ }
+ return w;
+}
+
+pointer actual_box_width(pointer r, scaled base_width)
+{
+ /*tex
+
+ Often this is the same as:
+
+ \starttyping
+ return + shift_amount(r) + base_width +
+ natural_sizes(list_ptr(r),null,(glue_ratio) glue_set(r),glue_sign(r),glue_order(r),box_dir(r));
+ \stoptyping
+ */
+ return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width);
+}
+
+halfword tail_of_list(halfword p)
+{
+ halfword q = p;
+ while (vlink(q) != null)
+ q = vlink(q);
+ return q;
+}
+
+int var_used;
+
+/*tex
+
+ Attribute lists need two extra globals to increase processing efficiency.
+ |max_used_attr| limits the test loop that checks for set attributes, and
+ |attr_list_cache| contains a pointer to an already created attribute list. It
+ is set to the special value |cache_disabled| when the current value can no
+ longer be trusted: after an assignment to an attribute register, and after a
+ group has ended.
+
+*/
+
+/*tex The maximum assigned attribute id: */
+
+int max_used_attr;
+halfword attr_list_cache;
+
+/*tex
+
+ 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|'.
+
+ Character nodes appear only in horizontal lists, never in vertical lists.
+
+ 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|, |sfi|, |fil|, |fill|, or |filll|). The |subtype|
+ field is not used.
+
+ The |new_null_box| function returns a pointer to an |hlist_node| in which all
+ subfields have the values corresponding to `\.{\\hbox\{\}}'. The |subtype|
+ field is set to |min_quarterword|, since that's the desired |span_count|
+ value if this |hlist_node| is changed to an |unset_node|.
+
+*/
+
+/*tex Create a new box node. */
+
+halfword new_null_box(void)
+{
+ halfword p = new_node(hlist_node, min_quarterword);
+ box_dir(p) = text_direction_par;
+ return p;
+}
+
+/*tex
+
+ A |vlist_node| is like an |hlist_node| in all respects except that it
+ contains a vertical list.
+
+ 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.
+
+ 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.
+
+*/
+
+halfword new_rule(int s)
+{
+ halfword p = new_node(rule_node,s);
+ return p;
+}
+
+/*tex
+
+ 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.
+
+ 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.
+
+ 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.
+
+ A |glyph_node|, which occurs only in horizontal lists, specifies a glyph in a
+ particular font, along with its attribute list. Older versions of \TeX\ could
+ use token memory for characters, because the font,char combination would fit
+ in a single word (both values were required to be strictly less than
+ $2^{16}$). In LuaTeX, room is needed for characters that are larger than
+ that, as well as a pointer to a potential attribute list, and the two
+ displacement values.
+
+ In turn, that made the node so large that it made sense to merge ligature
+ glyphs as well, as that requires only one extra pointer. A few extra classes
+ of glyph nodes will be introduced later. The unification of all those types
+ makes it easier to manipulate lists of glyphs. The subtype differentiates
+ various glyph kinds.
+
+ First, here is a function that returns a pointer to a glyph node for a given
+ glyph in a given font. If that glyph doesn't exist, |null| is returned
+ instead. Nodes of this subtype are directly created only for accents and
+ their base (through |make_accent|), and math nucleus items (in the conversion
+ from |mlist| to |hlist|).
+
+*/
+
+halfword new_glyph(int f, int c)
+{
+ halfword p = null;
+ if ((f == 0) || (char_exists(f, c))) {
+ p = new_glyph_node();
+ set_to_glyph(p);
+ font(p) = f;
+ character(p) = c;
+ }
+ return p;
+}
+
+/*tex
+
+ A subset of the glyphs nodes represent ligatures: characters fabricated from
+ the interaction of two or more actual characters. The characters that
+ generated the ligature have not been forgotten, since they are needed for
+ diagnostic messages; 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 of these |glyph_node|s is 1, plus 2 and/or 1 if the
+ original source of the ligature included implicit left and/or right
+ boundaries. These nodes are created by the C function |new_ligkern|.
+
+ A third general type of glyphs could be called a character, as it only
+ appears in lists that are not yet processed by the ligaturing and kerning
+ steps of the program.
+
+ |main_control| inserts these, and they are later converted to
+ |subtype_normal| by |new_ligkern|.
+
+*/
+
+quarterword norm_min(int h)
+{
+ if (h <= 0)
+ return 1;
+ else if (h >= 255)
+ return 255;
+ else
+ return (quarterword) h;
+}
+
+halfword new_char(int f, int c)
+{
+ halfword p;
+ p = new_glyph_node();
+ set_to_character(p);
+ font(p) = f;
+ character(p) = c;
+ lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par);
+ return p;
+}
+
+/*tex
+
+ Left and right ghost glyph nodes are the result of \.{\\leftghost} and
+ \.{\\rightghost}, respectively. They are going to be removed by
+ |new_ligkern|, at the end of which they are no longer needed.
+
+ Here are a few handy helpers used by the list output routines. The yoffset
+ has some history but we now give some control over its treatment:
+
+ 0: what we had before
+ 1: compensate height and depth
+ 2: compensate height and depth, take max
+ 3: we keep height and depth
+
+ The modes are controlled by a variable because we need to retain downward
+ compatibility.
+
+*/
+
+scaled glyph_width(halfword p)
+{
+ scaled w = char_width(font(p), character(p));
+ return w;
+}
+
+scaled glyph_height(halfword p)
+{
+ scaled h = char_height(font(p), character(p));
+ scaled y = y_displace(p);
+ if ((glyph_dimensions_par == 0) || (glyph_dimensions_par == 1) || (glyph_dimensions_par == 2 && y > 0))
+ h += y;
+ if (h < 0)
+ h = 0;
+ return h;
+}
+
+scaled glyph_depth(halfword p) /* not used */
+{
+ scaled d = char_depth(font(p), character(p));
+ scaled y = y_displace(p);
+ if ((glyph_dimensions_par == 0 && y > 0) || (glyph_dimensions_par == 1) || (glyph_dimensions_par == 2 && y < 0))
+ d -= y;
+ if (d < 0)
+ d = 0;
+ return d;
+}
+
+/*tex
+
+ 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
+ |no_break(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
+ |no_break=null|.
+
+ If |subtype(p)=automatic_disc|, 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.
+
+*/
+
+halfword new_disc(void)
+{
+ halfword p = new_node(disc_node, 0);
+ disc_penalty(p) = hyphen_penalty_par;
+ 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, 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.
+
+ 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 |surround| field, which represents
+ the amount of surrounding space inserted by \.{\\mathsurround}.
+
+*/
+
+halfword new_math(scaled w, int s)
+{
+ halfword p = new_node(math_node, s);
+ surround(p) = w;
+ return p;
+}
+
+/*tex
+
+ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, |rule_node|,
+ |ins_node|, |mark_node|, |adjust_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.
+
+ 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.
+
+ A glue specification has a halfword reference count in its first word,
+ 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|, |sfi|, |fil|, |fill|, or |filll|) corresponding to the
+ stretch and shrink values.
+
+ 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.
+
+*/
+
+halfword new_spec(halfword q)
+{
+ if (q == null) {
+ return copy_node(zero_glue);
+ } else if (type(q) == glue_spec_node) {
+ return copy_node(q);
+ } else if (type(q) == glue_node) {
+ halfword p = copy_node(zero_glue);
+ width(p) = width(q);
+ stretch(p) = stretch(q);
+ shrink(p) = shrink(q);
+ stretch_order(p) = stretch_order(q);
+ shrink_order(p) = shrink_order(q);
+ return p;
+ } else {
+ /*tex Alternatively we can issue a warning. */
+ return copy_node(zero_glue);
+ }
+}
+
+/*tex
+
+ 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}.
+
+*/
+
+halfword new_param_glue(int n)
+{
+ halfword p = new_node(glue_node, n + 1);
+ halfword q = glue_par(n);
+ width(p) = width(q);
+ stretch(p) = stretch(q);
+ shrink(p) = shrink(q);
+ stretch_order(p) = stretch_order(q);
+ shrink_order(p) = shrink_order(q);
+ return p;
+}
+
+/*tex
+
+ Glue nodes that are more or less anonymous are created by |new_glue|, whose
+ argument points to a glue specification.
+
+*/
+
+halfword new_glue(halfword q)
+{
+ halfword p = new_node(glue_node, normal);
+ width(p) = width(q);
+ stretch(p) = stretch(q);
+ shrink(p) = shrink(q);
+ stretch_order(p) = stretch_order(q);
+ shrink_order(p) = shrink_order(q);
+ return p;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+halfword new_skip_param(int n)
+{
+ halfword p = new_node(glue_node, n + 1);
+ halfword q = glue_par(n);
+ width(p) = width(q);
+ stretch(p) = stretch(q);
+ shrink(p) = shrink(q);
+ stretch_order(p) = stretch_order(q);
+ shrink_order(p) = shrink_order(q);
+ return p;
+}
+
+/*tex
+
+ 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).
+
+ The |new_kern| function creates a kern node having a given width.
+
+*/
+
+halfword new_kern(scaled w)
+{
+ halfword p = new_node(kern_node, normal);
+ width(p) = w;
+ return p;
+}
+
+/*tex
+
+ 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.
+
+ Anyone who has been reading the last few sections of the program will be able
+ to guess what comes next.
+
+*/
+
+halfword new_penalty(int m, int s)
+{
+ halfword p = new_node(penalty_node, 0);
+ penalty(p) = m;
+ subtype(p) = s;
+ return p;
+}
+
+/*tex
+
+ 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.
+
+ 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'. 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.
+
+*/
+
+halfword make_local_par_node(int mode)
+{
+ int callback_id;
+ halfword q;
+ halfword p = new_node(local_par_node,mode);
+ local_pen_inter(p) = local_inter_line_penalty_par;
+ local_pen_broken(p) = local_broken_penalty_par;
+ if (local_left_box_par != null) {
+ q = copy_node_list(local_left_box_par);
+ local_box_left(p) = q;
+ local_box_left_width(p) = width(local_left_box_par);
+ }
+ if (local_right_box_par != null) {
+ q = copy_node_list(local_right_box_par);
+ local_box_right(p) = q;
+ local_box_right_width(p) = width(local_right_box_par);
+ }
+ local_par_dir(p) = par_direction_par;
+ /*tex Callback with node passed: */
+ callback_id = callback_defined(insert_local_par_callback);
+ if (callback_id > 0) {
+ /*tex Todo: use general mechanism/wrapper. */
+ int sfix = lua_gettop(Luas);
+ if (!get_callback(Luas, callback_id)) {
+ lua_settop(Luas, sfix);
+ } else {
+ int i;
+ nodelist_to_lua(Luas, p);
+ lua_push_local_par_mode(Luas,mode)
+ /*tex 2 arg, 0 result */
+ i = lua_pcall(Luas, 2, 0, 0);
+ if (i != 0) {
+ lua_gc(Luas, LUA_GCCOLLECT, 0);
+ Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
+ }
+ lua_settop(Luas, sfix);
+ }
+ }
+ return p;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.h b/Build/source/texk/web2c/luatexdir/tex/texnodes.h
new file mode 100644
index 00000000000..9742ca4966c
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.h
@@ -0,0 +1,1188 @@
+/* texnodes.h
+
+ Copyright 2006-2008 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#include <stdarg.h>
+
+#ifndef __NODES_H__
+# define __NODES_H__
+
+# define varmemcast(a) (memory_word *)(a)
+
+extern memory_word *volatile varmem;
+extern halfword var_mem_max;
+
+extern halfword get_node(int s);
+extern void free_node(halfword p, int s);
+extern void init_node_mem(int s);
+extern void dump_node_mem(void);
+extern void undump_node_mem(void);
+
+# define max_halfword 0x3FFFFFFF
+# define max_dimen 0x3FFFFFFF
+# ifndef null
+# define null 0
+# endif
+# define null_flag -0x40000000
+# define zero_glue 0
+# define normal 0
+
+# define vinfo(a) varmem[(a)].hh.v.LH
+# define vlink(a) varmem[(a)].hh.v.RH
+# define type(a) varmem[(a)].hh.u.B0
+# define subtype(a) varmem[(a)].hh.u.B1
+# define node_attr(a) vinfo((a)+1)
+# define alink(a) vlink((a)+1)
+
+# define node_size(a) varmem[(a)].hh.v.LH
+
+# define rlink(a) vlink((a)+1) /* aka alink() */
+# define tlink(a) vinfo((a)+1) /* overlaps with node_attr() */
+
+/* really special head node pointers that only need links */
+
+# define temp_node_size 2
+
+/* attribute lists */
+
+# define UNUSED_ATTRIBUTE -0x7FFFFFFF /* as low as it goes */
+
+/* it is convenient to have attribute list nodes and attribute node
+ * be the same size
+ */
+
+# define attribute_node_size 2
+# define cache_disabled max_halfword
+
+# define attr_list_ref(a) vinfo((a)+1) /* the reference count */
+# define attribute_id(a) vinfo((a)+1)
+# define attribute_value(a) vlink((a)+1)
+
+# define assign_attribute_ref(n,p) do { \
+ node_attr(n) = p;attr_list_ref(p)++; \
+} while (0)
+
+# define add_node_attr_ref(a) do { \
+ if (a!=null) attr_list_ref((a))++; \
+} while (0)
+
+# define replace_attribute_list(a,b) do { \
+ delete_attribute_ref(node_attr(a)); \
+ node_attr(a)=b; \
+} while (0)
+
+extern void update_attribute_cache(void);
+extern halfword copy_attribute_list(halfword n);
+extern halfword do_set_attribute(halfword p, int i, int val);
+
+# define width_offset 2
+# define depth_offset 3
+# define height_offset 4
+# define list_offset 6
+
+typedef enum {
+ user_skip_glue,
+ line_skip_glue,
+ baseline_skip_glue,
+ par_skip_glue,
+ above_display_skip_glue,
+ below_display_skip_glue,
+ above_display_short_skip_glue,
+ below_display_short_skip_glue,
+ left_skip_glue,
+ right_skip_glue,
+ top_skip_glue,
+ split_top_skip_glue,
+ tab_skip_glue,
+ space_skip_glue,
+ xspace_skip_glue,
+ par_fill_skip_glue,
+ math_skip_glue,
+ thin_mu_skip_glue,
+ med_mu_skip_glue,
+ thick_mu_skip_glue,
+ /* math */
+ cond_math_glue = 98, /* special |subtype| to suppress glue in the next node */
+ mu_glue, /* |subtype| for math glue */
+ /* leaders */
+ a_leaders, /* |subtype| for aligned leaders */
+ c_leaders, /* |subtype| for centered leaders */
+ x_leaders, /* |subtype| for expanded leaders */
+ g_leaders /* |subtype| for global (page) leaders */
+} glue_subtypes;
+
+/* normal nodes */
+
+# define inf_bad 10000 /* infinitely bad value */
+# define inf_penalty inf_bad /* ``infinite'' penalty value */
+# define eject_penalty -(inf_penalty) /* ``negatively infinite'' penalty value */
+
+# define penalty_node_size 3
+# define penalty(a) vlink((a)+2)
+
+typedef enum {
+ user_penalty,
+ linebreak_penalty, /* includes widow, club, broken ect */
+ line_penalty,
+ word_penalty,
+ final_penalty,
+ noad_penalty,
+ before_display_penalty,
+ after_display_penalty,
+ equation_number_penalty,
+} penalty_subtypes ;
+
+/*
+# define glue_node_size 4
+# define glue_ptr(a) vinfo((a)+2)
+# define leader_ptr(a) vlink((a)+2)
+# define synctex_tag_glue(a) vinfo((a)+3)
+# define synctex_line_glue(a) vlink((a)+3)
+*/
+
+# define glue_node_size 7
+# define glue_spec_size 5
+/* define width(a) vinfo((a)+2) */
+/* define leader_ptr(a) vlink((a)+2) */
+# define shrink(a) vinfo((a)+3)
+# define stretch(a) vlink((a)+3)
+# define stretch_order(a) vinfo((a)+4)
+# define shrink_order(a) vlink((a)+4)
+# define leader_ptr(a) vlink((a)+5) /* should be in vlink((a)+2) but fails */
+# define synctex_tag_glue(a) vinfo((a)+6)
+# define synctex_line_glue(a) vlink((a)+6)
+
+#define glue_is_zero(p) \
+ ((p == null) || (width(p) == 0 && stretch(p) == 0 && shrink(p) == 0))
+
+#define glue_is_positive(p) \
+ ((p == null) || (width(p) > 0))
+
+#define reset_glue_to_zero(p) \
+ if (p != null) { \
+ width(p) = 0; \
+ stretch(p) = 0; \
+ shrink(p) = 0; \
+ stretch_order(p) = 0; \
+ shrink_order(p) = 0; \
+ }
+
+#define copy_glue_values(p,q) \
+ if (q == null) { \
+ width(p) = 0; \
+ stretch(p) = 0; \
+ shrink(p) = 0; \
+ stretch_order(p) = 0; \
+ shrink_order(p) = 0; \
+ } else { \
+ width(p) = width(q); \
+ stretch(p) = stretch(q); \
+ shrink(p) = shrink(q); \
+ stretch_order(p) = stretch_order(q); \
+ shrink_order(p) = shrink_order(q); \
+ }
+
+
+/*
+ disc nodes could eventually be smaller, because the indirect
+ pointers are not really needed (8 instead of 10).
+*/
+
+typedef enum {
+ discretionary_disc = 0,
+ explicit_disc,
+ automatic_disc,
+ syllable_disc,
+ init_disc, /* first of a duo of syllable_discs */
+ select_disc, /* second of a duo of syllable_discs */
+} discretionary_subtypes;
+
+# define disc_node_size 11
+# define pre_break_head(a) ((a)+5)
+# define post_break_head(a) ((a)+7)
+# define no_break_head(a) ((a)+9)
+
+# define disc_penalty(a) vlink((a)+2)
+# define pre_break(a) vinfo((a)+3)
+# define post_break(a) vlink((a)+3)
+# define no_break(a) vlink((a)+4) /* we have vinfo((a)+4) for later usage */
+
+# define vlink_pre_break(a) vlink(pre_break_head(a))
+# define vlink_post_break(a) vlink(post_break_head(a))
+# define vlink_no_break(a) vlink(no_break_head(a))
+
+# define tlink_pre_break(a) tlink(pre_break_head(a))
+# define tlink_post_break(a) tlink(post_break_head(a))
+# define tlink_no_break(a) tlink(no_break_head(a))
+
+typedef enum {
+ font_kern = 0,
+ explicit_kern, /* |subtype| of kern nodes from \.{\\kern} and \.{\\/} */
+ accent_kern, /* |subtype| of kern nodes from accents */
+ italic_kern,
+} kern_subtypes;
+
+# define kern_node_size 5
+# define ex_kern(a) vinfo((a)+3) /* expansion factor (hz) */
+# define synctex_tag_kern(a) vinfo((a)+4)
+# define synctex_line_kern(a) vlink((a)+4)
+
+# define box_node_size 9
+
+/* todo, make an enum of this */
+
+typedef enum {
+ unknown_list = 0,
+ line_list = 1, /* paragraph lines */
+ hbox_list = 2, /* \.{\\hbox} */
+ indent_list = 3, /* indentation box */
+ align_row_list = 4, /* row from a \.{\\halign} or \.{\\valign} */
+ align_cell_list = 5, /* cell from a \.{\\halign} or \.{\\valign} */
+ equation_list = 6, /* display equation */
+ equation_number_list = 7, /* display equation number */
+ math_list_list = 8,
+ math_char_list = 9,
+ math_h_extensible_list = 10,
+ math_v_extensible_list = 11,
+ math_h_delimiter_list = 12,
+ math_v_delimiter_list = 13,
+ math_over_delimiter_list = 14,
+ math_under_delimiter_list = 15,
+ math_numerator_list = 16,
+ math_denominator_list = 17,
+ math_limits_list = 18,
+ math_fraction_list = 19,
+ math_nucleus_list = 20,
+ math_sup_list = 21,
+ math_sub_list = 22,
+ math_degree_list = 23,
+ math_scripts_list = 24,
+ math_over_list = 25,
+ math_under_list = 26,
+ math_accent_list = 27,
+ math_radical_list = 28,
+} list_subtypes ;
+
+# define width(a) varmem[(a)+2].cint
+# define depth(a) varmem[(a)+3].cint
+# define height(a) varmem[(a)+4].cint
+# define shift_amount(a) vlink((a)+5)
+# define box_dir(a) vinfo((a)+5)
+# define list_ptr(a) vlink((a)+6)
+# define glue_order(a) subtype((a)+6)
+# define glue_sign(a) type((a)+6)
+# define glue_set(a) varmem[(a)+7].gr
+# define synctex_tag_box(a) vinfo((a)+8)
+# define synctex_line_box(a) vlink((a)+8)
+
+/* unset nodes */
+
+# define glue_stretch(a) varmem[(a)+7].cint
+# define glue_shrink shift_amount
+# define span_count subtype
+
+typedef enum {
+ normal_rule = 0,
+ box_rule,
+ image_rule,
+ empty_rule,
+ user_rule,
+ math_over_rule,
+ math_under_rule,
+ math_fraction_rule,
+ math_radical_rule,
+ outline_rule,
+} rule_subtypes;
+
+# define rule_node_size 9
+# define rule_dir(a) vlink((a)+5)
+# define rule_index(a) vinfo((a)+6)
+# define rule_transform(a) vlink((a)+6)
+# define rule_left(a) vinfo((a)+7)
+# define rule_right(a) vlink((a)+7)
+# define synctex_tag_rule(a) vinfo((a)+8)
+# define synctex_line_rule(a) vlink((a)+8)
+
+# define rule_math_size rule_index
+# define rule_math_font rule_transform
+
+# define mark_node_size 3
+# define mark_ptr(a) vlink((a)+2)
+# define mark_class(a) vinfo((a)+2)
+
+# define adjust_node_size 3
+# define adjust_pre subtype
+# define adjust_ptr(a) vlink(a+2)
+
+# define glyph_node_size 7
+# define character(a) vinfo((a)+2)
+# define font(a) vlink((a)+2)
+# define lang_data(a) vinfo((a)+3)
+# define lig_ptr(a) vlink((a)+3)
+# define x_displace(a) vinfo((a)+4)
+# define y_displace(a) vlink((a)+4)
+# define ex_glyph(a) vinfo((a)+5) /* expansion factor (hz) */
+# define glyph_node_data(a) vlink((a)+5)
+# define synctex_tag_glyph(a) vinfo((a)+6)
+# define synctex_line_glyph(a) vlink((a)+6)
+
+# define is_char_node(a) (a!=null && type(a)==glyph_node)
+
+# define char_lang(a) ((const int)(signed short)(((signed int)((unsigned)lang_data(a)&0x7FFF0000)<<1)>>17))
+# define char_lhmin(a) ((const int)(((unsigned)lang_data(a) & 0x0000FF00)>>8))
+# define char_rhmin(a) ((const int)(((unsigned)lang_data(a) & 0x000000FF)))
+# define char_uchyph(a) ((const int)(((unsigned)lang_data(a) & 0x80000000)>>31))
+
+# define make_lang_data(a,b,c,d) (a>0 ? (1<<31): 0)+ \
+ (b<<16)+ (((c>0 && c<256) ? c : 255)<<8)+(((d>0 && d<256) ? d : 255))
+
+# define init_lang_data(a) lang_data(a)=256+1
+
+# define set_char_lang(a,b) lang_data(a)=make_lang_data(char_uchyph(a),b,char_lhmin(a),char_rhmin(a))
+# define set_char_lhmin(a,b) lang_data(a)=make_lang_data(char_uchyph(a),char_lang(a),b,char_rhmin(a))
+# define set_char_rhmin(a,b) lang_data(a)=make_lang_data(char_uchyph(a),char_lang(a),char_lhmin(a),b)
+# define set_char_uchyph(a,b) lang_data(a)=make_lang_data(b,char_lang(a),char_lhmin(a),char_rhmin(a))
+
+# define margin_kern_node_size 4
+# define margin_char(a) vlink((a)+3)
+
+/*@# {|subtype| of marginal kerns}*/
+
+typedef enum {
+ left_side = 0,
+ right_side
+} margin_kern_subtypes ;
+
+typedef enum {
+ before = 0,
+ after
+} math_subtypes ;
+
+# define math_node_size 7
+/* define width(a) vinfo((a)+2) */
+/* overlaps width */
+/* define shrink(a) vinfo((a)+3) */
+/* define stretch(a) vlink((a)+3) */
+/* define stretch_order(a) vinfo((a)+4) */
+/* define shrink_order(a) vlink((a)+4) */
+/* leader_ptr slot */
+# define surround(a) vinfo((a)+5)
+# define synctex_tag_math(a) vinfo((a)+6)
+# define synctex_line_math(a) vlink((a)+6)
+
+# define ins_node_size 6
+# define float_cost(a) varmem[(a)+2].cint
+# define ins_ptr(a) vinfo((a)+5)
+# define split_top_ptr(a) vlink((a)+5)
+
+# define page_ins_node_size 5
+
+/* height = 4 */
+
+typedef enum {
+ hlist_node = 0,
+ vlist_node,
+ rule_node,
+ ins_node,
+ mark_node,
+ adjust_node,
+ boundary_node,
+ disc_node,
+ whatsit_node,
+#define last_preceding_break_node whatsit_node
+ local_par_node,
+ dir_node,
+#define last_non_discardable_node dir_node
+ math_node,
+ glue_node,
+ kern_node,
+ penalty_node,
+ unset_node,
+ style_node,
+ choice_node,
+ simple_noad,
+ radical_noad,
+ fraction_noad,
+ accent_noad,
+ fence_noad,
+ math_char_node, /* kernel fields */
+ sub_box_node,
+ sub_mlist_node,
+ math_text_char_node,
+ delim_node, /* shield fields */
+ margin_kern_node,
+ glyph_node, /* this and below have attributes */
+ align_record_node,
+ pseudo_file_node,
+ pseudo_line_node,
+ inserting_node,
+ split_up_node,
+ expr_node,
+ nesting_node,
+ span_node,
+ attribute_node,
+ glue_spec_node,
+ attribute_list_node,
+ temp_node,
+ align_stack_node,
+ movement_node,
+ if_node,
+ unhyphenated_node,
+ hyphenated_node,
+ delta_node,
+ passive_node,
+ shape_node,
+} node_types;
+
+# define MAX_NODE_TYPE shape_node /* 60 */
+
+/*
+ TH: these two defines still need checking. The node ordering in luatex is not
+ quite the same as in tex82, HH: but it's probably ok
+*/
+
+/*
+
+ # define precedes_break(a) \
+ # (type(a)<math_node && \
+ # (type(a)!=whatsit_node || (subtype(a)!=dir_node && subtype(a)!=local_par_node)))
+
+*/
+
+/*
+ # define precedes_break(a) (type(a)<math_node)
+ # define non_discardable(a) (type(a)<math_node || type(a) == dir_node || type(a) == local_par_node)
+*/
+
+# define precedes_break(a) (type(a)<=last_preceding_break_node)
+# define precedes_kern(a) ((type(a) == kern_node) && (subtype(a) == font_kern || subtype(a) == accent_kern))
+# define precedes_dir(a) ((type(a) == dir_node) && (break_after_dir_mode_par == 1))
+# define non_discardable(a) (type(a)<=last_non_discardable_node)
+
+# define known_node_type(i) ( i >= 0 && i <= MAX_NODE_TYPE)
+
+# define last_known_node temp_node /* used by \lastnodetype */
+
+# define movement_node_size 3
+# define if_node_size 2
+# define align_stack_node_size 6
+# define nesting_node_size 2
+
+# define expr_node_size 3
+# define expr_type(A) type((A)+1)
+# define expr_state(A) subtype((A)+1) /* enum defined in scanning.w */
+# define expr_e_field(A) vlink((A)+1) /* saved expression so far */
+# define expr_t_field(A) vlink((A)+2) /* saved term so far */
+# define expr_n_field(A) vinfo((A)+2) /* saved numerator */
+
+# define span_node_size 3
+# define span_span(a) vlink((a)+1)
+# define span_link(a) vinfo((a)+1)
+
+# define pseudo_file_node_size 2
+# define pseudo_lines(a) vlink((a)+1)
+
+# define nodetype_has_attributes(t) (((t)<=glyph_node) && ((t)!=unset_node))
+
+# define nodetype_has_subtype(t) ((t)!=attribute_list_node && (t)!=attribute_node && (t)!=glue_spec_node)
+# define nodetype_has_prev(t) nodetype_has_subtype((t))
+
+/*
+ style and choice nodes; style nodes can be smaller, the information is encoded in
+ |subtype|, but choice nodes are on-the-spot converted to style nodes
+*/
+
+# define style_node_size 4
+# define display_mlist(a) vinfo((a)+2) /* mlist to be used in display style */
+# define text_mlist(a) vlink((a)+2) /* mlist to be used in text style */
+# define script_mlist(a) vinfo((a)+3) /* mlist to be used in script style */
+# define script_script_mlist(a) vlink((a)+3) /* mlist to be used in scriptscript style */
+
+/*
+ because noad types get changed when processing we need to make sure some if the node
+ sizes match and that we don't share slots with different properties
+
+ once it's sorted out we can go smaller and also adapt the flush etc code to it
+*/
+
+/* regular noads */
+
+# define noad_size 8
+# define new_hlist(a) vlink((a)+2) /* the translation of an mlist */
+# define nucleus(a) vinfo((a)+2) /* the |nucleus| field of a noad */
+# define supscr(a) vlink((a)+3) /* the |supscr| field of a noad */
+# define subscr(a) vinfo((a)+3) /* the |subscr| field of a noad */
+# define noaditalic(a) vlink((a)+4)
+# define noadwidth(a) vinfo((a)+4)
+# define noadheight(a) vlink((a)+5)
+# define noaddepth(a) vinfo((a)+5)
+# define noadextra1(a) vlink((a)+6) /* we need to match delimiter (saves copy) */
+# define noadoptions(a) vinfo((a)+6)
+# define noadextra3(a) vlink((a)+7) /* see (!) below */
+# define noadextra4(a) vinfo((a)+7) /* used to store samesize */
+
+# define noad_fam(a) vlink((a)+6) /* noadextra1 */
+
+typedef enum {
+ ord_noad_type = 0,
+ op_noad_type_normal,
+ op_noad_type_limits,
+ op_noad_type_no_limits,
+ bin_noad_type,
+ rel_noad_type,
+ open_noad_type,
+ close_noad_type,
+ punct_noad_type,
+ inner_noad_type,
+ under_noad_type,
+ over_noad_type,
+ vcenter_noad_type,
+} noad_types;
+
+/* accent noads */
+
+# define accent_noad_size 8
+# define top_accent_chr(a) vinfo((a)+6) /* the |top_accent_chr| field of an accent noad */
+# define bot_accent_chr(a) vlink((a)+6) /* the |bot_accent_chr| field of an accent noad */
+# define overlay_accent_chr(a) vinfo((a)+7) /* the |overlay_accent_chr| field of an accent noad */
+# define accentfraction(a) vlink((a)+7)
+
+typedef enum {
+ bothflexible_accent,
+ fixedtop_accent,
+ fixedbottom_accent,
+ fixedboth_accent,
+} math_accent_subtypes ;
+
+/* left and right noads */
+
+# define fence_noad_size 8 /* needs to match noad size */
+# define delimiteritalic(a) vlink((a)+4)
+/* define delimiterwidth(a) vinfo((a)+4) */
+# define delimiterheight(a) vlink((a)+5)
+# define delimiterdepth(a) vinfo((a)+5)
+# define delimiter(a) vlink((a)+6) /* |delimiter| field in left and right noads */
+# define delimiteroptions(a) vinfo((a)+6)
+# define delimiterclass(a) vlink((a)+7) /* (!) we could probably pack some more in 6 */
+# define delimitersamesize(a) vinfo((a)+7) /* set by engine */
+
+/* when dimensions then axis else noaxis */
+
+typedef enum {
+ noad_option_set = 0x08,
+ noad_option_unused_1 = 0x00 + 0x08,
+ noad_option_unused_2 = 0x01 + 0x08,
+ noad_option_axis = 0x02 + 0x08,
+ noad_option_no_axis = 0x04 + 0x08,
+ noad_option_exact = 0x10 + 0x08,
+ noad_option_left = 0x11 + 0x08,
+ noad_option_middle = 0x12 + 0x08,
+ noad_option_right = 0x14 + 0x08,
+ noad_option_no_sub_script = 0x21 + 0x08,
+ noad_option_no_super_script = 0x22 + 0x08,
+ noad_option_no_script = 0x23 + 0x08,
+ noad_option_no_rule = 0x24 + 0x08,
+} delimiter_options ;
+
+# define delimiteroptionset(a) ((delimiteroptions(a) & noad_option_set ) == noad_option_set )
+# define delimiteraxis(a) ((delimiteroptions(a) & noad_option_axis ) == noad_option_axis )
+# define delimiternoaxis(a) ((delimiteroptions(a) & noad_option_no_axis) == noad_option_no_axis)
+# define delimiterexact(a) ((delimiteroptions(a) & noad_option_exact ) == noad_option_exact )
+
+# define noadoptionnosubscript(a) ( (type(a) == simple_noad) && ( \
+ ((delimiteroptions(a) & noad_option_no_sub_script ) == noad_option_no_sub_script) || \
+ ((delimiteroptions(a) & noad_option_no_script ) == noad_option_no_script ) ))
+# define noadoptionnosupscript(a) ( (type(a) == simple_noad) && ( \
+ ((delimiteroptions(a) & noad_option_no_super_script) == noad_option_no_super_script) || \
+ ((delimiteroptions(a) & noad_option_no_script ) == noad_option_no_script ) ))
+
+typedef enum {
+ noad_delimiter_mode_noshift = 0x01,
+ noad_delimiter_mode_italics = 0x02,
+ noad_delimiter_mode_ordinal = 0x04,
+ noad_delimiter_mode_samenos = 0x08,
+ noad_delimiter_mode_charnos = 0x10,
+} delimiter_modes ;
+
+# define delimitermodenoshift ((math_delimiters_mode_par & noad_delimiter_mode_noshift) == noad_delimiter_mode_noshift)
+# define delimitermodeitalics ((math_delimiters_mode_par & noad_delimiter_mode_italics) == noad_delimiter_mode_italics)
+# define delimitermodeordinal ((math_delimiters_mode_par & noad_delimiter_mode_ordinal) == noad_delimiter_mode_ordinal)
+# define delimitermodesamenos ((math_delimiters_mode_par & noad_delimiter_mode_samenos) == noad_delimiter_mode_samenos)
+# define delimitermodecharnos ((math_delimiters_mode_par & noad_delimiter_mode_charnos) == noad_delimiter_mode_charnos)
+
+/* subtype of fence noads */
+
+/*
+# define left_noad_side 1
+# define middle_noad_side 2
+# define right_noad_side 3
+# define no_noad_side 4
+*/
+
+typedef enum {
+ unset_noad_side = 0,
+ left_noad_side = 1,
+ middle_noad_side = 2,
+ right_noad_side = 3,
+ no_noad_side = 4,
+} fence_subtypes ;
+
+/* fraction noads */
+
+# define fraction_noad_size 8
+# define thickness(a) vlink((a)+2) /* |thickness| field in a fraction noad */
+# define numerator(a) vlink((a)+3) /* |numerator| field in a fraction noad */
+# define denominator(a) vinfo((a)+3) /* |denominator| field in a fraction noad */
+# define left_delimiter(a) vlink((a)+5) /* first delimiter field of a noad */
+# define right_delimiter(a) vinfo((a)+5) /* second delimiter field of a fraction noad */
+# define middle_delimiter(a) vlink((a)+6)
+# define fractionoptions(a) vinfo((a)+6)
+# define fraction_fam(a) vlink((a)+7)
+
+# define fractionoptionset(a) ((fractionoptions(a) & noad_option_set ) == noad_option_set )
+# define fractionexact(a) ((fractionoptions(a) & noad_option_exact ) == noad_option_exact )
+# define fractionnoaxis(a) ((fractionoptions(a) & noad_option_no_axis) == noad_option_no_axis)
+# define fractionnorule(a) ((fractionoptions(a) & noad_option_no_rule) == noad_option_no_rule)
+
+/* radical noads */
+/* this is like a fraction, but it only stores a |left_delimiter| */
+
+# define radical_noad_size 7
+# define radicalwidth(a) vinfo((a)+4)
+# define degree(a) vlink((a)+6) /* the root degree in a radical noad */
+# define radicaloptions(a) vinfo((a)+6)
+
+# define radicaloptionset(a) ((radicaloptions(a) & noad_option_set ) == noad_option_set)
+# define radicalexact(a) ((radicaloptions(a) & noad_option_exact ) == noad_option_exact)
+# define radicalleft(a) ((radicaloptions(a) & noad_option_left ) == noad_option_left)
+# define radicalmiddle(a) ((radicaloptions(a) & noad_option_middle) == noad_option_middle)
+# define radicalright(a) ((radicaloptions(a) & noad_option_right ) == noad_option_right)
+
+typedef enum {
+ radical_noad_type,
+ uradical_noad_type,
+ uroot_noad_type,
+ uunderdelimiter_noad_type,
+ uoverdelimiter_noad_type,
+ udelimiterunder_noad_type,
+ udelimiterover_noad_type,
+} radical_subtypes;
+
+/* accessors for the |nucleus|-style node fields */
+
+# define math_kernel_node_size 3
+# define math_fam(a) vinfo((a)+2)
+# define math_character(a) vlink((a)+2)
+# define math_list(a) vlink((a)+2)
+
+/* accessors for the |delimiter|-style two-word subnode fields */
+
+# define math_shield_node_size 4 /* not used yet */
+
+# define small_fam(A) vinfo((A)+2) /* |fam| for ``small'' delimiter */
+# define small_char(A) vlink((A)+2) /* |character| for ``small'' delimiter */
+# define large_fam(A) vinfo((A)+3) /* |fam| for ``large'' delimiter */
+# define large_char(A) vlink((A)+3) /* |character| for ``large'' delimiter */
+
+/* we should have the codes in a separate enum: extension_codes */
+
+/* be careful: must be in sync with whatsit_node_data[] ! (at least) */
+
+typedef enum {
+ /* core */
+ open_node = 0,
+ write_node,
+ close_node,
+ special_node,
+ use_box_resource_node,
+ use_image_resource_node,
+ save_pos_node,
+ late_lua_node,
+ user_defined_node,
+ /* dvi backend */
+ dvi_literal_node = 15,
+ /* pdf backend */
+ pdf_literal_node = 16,
+ pdf_refobj_node,
+ pdf_annot_node,
+ pdf_start_link_node,
+ pdf_end_link_node,
+ pdf_dest_node,
+ pdf_action_node,
+ pdf_thread_node,
+ pdf_start_thread_node,
+ pdf_end_thread_node,
+ pdf_thread_data_node,
+ pdf_link_data_node,
+ pdf_colorstack_node,
+ pdf_setmatrix_node,
+ pdf_save_node,
+ pdf_restore_node,
+ pdf_link_state_node,
+} whatsit_types;
+
+# define first_common_whatsit 0
+# define last_common_whatsit user_defined_node
+# define backend_first_dvi_whatsit 15
+# define backend_last_dvi_whatsit 15
+# define backend_first_pdf_whatsit 16
+# define backend_last_pdf_whatsit 32
+
+/* add some slack for the future */
+
+# define MAX_WHATSIT_TYPE 34
+
+# define known_whatsit_type(i) ( \
+ (i >= first_common_whatsit && i <= last_common_whatsit) || \
+ (i >= backend_first_dvi_whatsit && i <= backend_last_dvi_whatsit) || \
+ (i >= backend_first_pdf_whatsit && i <= backend_last_pdf_whatsit) \
+)
+
+# define get_node_size(i,j) (i!=whatsit_node ? node_data[i].size : whatsit_node_data[j].size)
+# define get_node_name(i,j) (i!=whatsit_node ? node_data[i].name : whatsit_node_data[j].name)
+# define get_etex_code(i) (node_data[i].etex)
+
+# define GLYPH_CHARACTER (1 << 0)
+# define GLYPH_LIGATURE (1 << 1)
+# define GLYPH_GHOST (1 << 2)
+# define GLYPH_LEFT (1 << 3)
+# define GLYPH_RIGHT (1 << 4)
+
+typedef enum {
+ glyph_unset = 0,
+ glyph_character = GLYPH_CHARACTER,
+ glyph_ligature = GLYPH_LIGATURE,
+ glyph_ghost = GLYPH_GHOST,
+ glyph_left = GLYPH_LEFT,
+ glyph_right = GLYPH_RIGHT,
+} glyph_subtypes;
+
+# define is_character(p) ((subtype(p)) & GLYPH_CHARACTER)
+# define is_ligature(p) ((subtype(p)) & GLYPH_LIGATURE )
+# define is_ghost(p) ((subtype(p)) & GLYPH_GHOST )
+
+# define is_simple_character(p) (is_character(p) && !is_ligature(p) && !is_ghost(p))
+
+# define is_leftboundary(p) (is_ligature(p) && ((subtype(p)) & GLYPH_LEFT ))
+# define is_rightboundary(p) (is_ligature(p) && ((subtype(p)) & GLYPH_RIGHT ))
+# define is_leftghost(p) (is_ghost(p) && ((subtype(p)) & GLYPH_LEFT ))
+# define is_rightghost(p) (is_ghost(p) && ((subtype(p)) & GLYPH_RIGHT ))
+
+
+# define set_is_glyph(p) subtype(p) = (quarterword) (subtype(p) & ~GLYPH_CHARACTER)
+# define set_is_character(p) subtype(p) = (quarterword) (subtype(p) | GLYPH_CHARACTER)
+# define set_is_ligature(p) subtype(p) = (quarterword) (subtype(p) | GLYPH_LIGATURE)
+# define set_is_ghost(p) subtype(p) = (quarterword) (subtype(p) |GLYPH_GHOST)
+
+# define set_to_glyph(p) subtype(p) = (quarterword)(subtype(p) & 0xFF00)
+# define set_to_character(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_CHARACTER)
+# define set_to_ligature(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_LIGATURE)
+# define set_to_ghost(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_GHOST)
+
+# define set_is_leftboundary(p) { set_to_ligature(p); subtype(p) |= GLYPH_LEFT; }
+# define set_is_rightboundary(p) { set_to_ligature(p); subtype(p) |= GLYPH_RIGHT; }
+# define set_is_leftghost(p) { set_to_ghost(p); subtype(p) |= GLYPH_LEFT; }
+# define set_is_rightghost(p) { set_to_ghost(p); subtype(p) |= GLYPH_RIGHT; }
+
+typedef enum {
+ cancel_boundary = 0,
+ user_boundary,
+ protrusion_boundary,
+ word_boundary,
+} boundary_subtypes ;
+
+# define boundary_node_size 3
+# define boundary_value(a) vinfo((a)+2)
+
+# define special_node_size 3
+
+typedef enum {
+ normal_dir = 0,
+ cancel_dir,
+} dir_subtypes ;
+
+# define dir_node_size 5
+# define dir_dir(a) vinfo((a)+2)
+# define dir_level(a) vlink((a)+2)
+# define dir_refpos_h(a) vinfo((a)+3)
+# define dir_refpos_v(a) vlink((a)+3)
+# define dir_cur_h(a) vinfo((a)+4)
+# define dir_cur_v(a) vlink((a)+4)
+
+# define write_node_size 3
+# define close_node_size 3
+# define write_tokens(a) vlink(a+2)
+# define write_stream(a) vinfo(a+2)
+
+# define open_node_size 4
+# define open_name(a) vlink((a)+2)
+# define open_area(a) vinfo((a)+3)
+# define open_ext(a) vlink((a)+3)
+
+# define late_lua_node_size 4
+# define late_lua_data(a) vlink((a)+2)
+# define late_lua_reg(a) vinfo((a)+2)
+# define late_lua_name(a) vlink((a)+3)
+# define late_lua_type(a) subtype((a)+3)
+
+# define save_pos_node_size 3
+
+# define local_par_size 6
+
+# define local_pen_inter(a) vinfo((a)+2)
+# define local_pen_broken(a) vlink((a)+2)
+# define local_box_left(a) vlink((a)+3)
+# define local_box_left_width(a) vinfo((a)+3)
+# define local_box_right(a) vlink((a)+4)
+# define local_box_right_width(a) vinfo((a)+4)
+# define local_par_dir(a) vinfo((a)+5)
+
+/* type of literal data */
+
+# define lua_refid_literal 1 /* not a |normal| string */
+# define lua_refid_call 2 /* not a |normal| string */
+
+/* begin of pdf backend nodes */
+
+/* literal ctm types */
+
+typedef enum {
+ set_origin = 0,
+ direct_page,
+ direct_always,
+ direct_raw,
+ direct_text,
+ direct_font,
+ scan_special,
+} ctm_transform_modes;
+
+# define pdf_refobj_node_size 3
+
+# define pdf_obj_objnum(a) vinfo((a) + 2)
+
+# define pdf_annot_node_size 8
+# define pdf_dest_node_size 8
+# define pdf_thread_node_size 8
+
+/*
+ when a whatsit node representing annotation is created, words |1..3| are
+ width, height and depth of this annotation; after shipping out words |1..4|
+ are rectangle specification of annotation. For whatsit node representing
+ destination |pdf_ann_left| and |pdf_ann_top| are used for some types of destinations
+*/
+
+/*
+ coordinates of destinations/threads/annotations (in whatsit node)
+*/
+
+# define pdf_ann_left(a) varmem[(a) + 2].cint
+# define pdf_ann_top(a) varmem[(a) + 3].cint
+# define pdf_ann_right(a) varmem[(a) + 4].cint
+# define pdf_ann_bottom(a) varmem[(a) + 5].cint
+
+# define pdf_literal_data(a) vlink((a)+2)
+# define pdf_literal_mode(a) type((a)+2)
+# define pdf_literal_type(a) subtype((a)+2)
+
+# define pdf_annot_data(a) vinfo((a) + 6)
+# define pdf_link_attr(a) vinfo((a) + 6)
+# define pdf_link_action(a) vlink((a) + 6)
+# define pdf_annot_objnum(a) varmem[(a) + 7].cint
+# define pdf_link_objnum(a) varmem[(a) + 7].cint
+
+# define pdf_dest_type(a) type((a) + 6)
+# define pdf_dest_named_id(a) subtype((a) + 6)
+# define pdf_dest_id(a) vlink((a) + 6)
+# define pdf_dest_xyz_zoom(a) vinfo((a) + 7)
+# define pdf_dest_objnum(a) vlink((a) + 7)
+
+# define pdf_thread_named_id(a) subtype((a) + 6)
+# define pdf_thread_id(a) vlink((a) + 6)
+# define pdf_thread_attr(a) vinfo((a) + 7)
+
+# define pdf_end_link_node_size 3
+# define pdf_end_thread_node_size 3
+
+# define pdf_setmatrix_node_size 3
+# define pdf_save_node_size 3
+# define pdf_restore_node_size 3
+
+# define pdf_link_state_node_size 3
+
+# define pdf_link_state(a) vinfo((a) + 2)
+
+# define pdf_colorstack_node_size 4
+# define pdf_colorstack_stack(a) vlink((a)+2)
+# define pdf_colorstack_cmd(a) vinfo((a)+2)
+# define pdf_colorstack_data(a) vlink((a)+3)
+# define pdf_setmatrix_data(a) vlink((a)+2)
+
+typedef enum {
+ pdf_action_page = 0,
+ pdf_action_goto,
+ pdf_action_thread,
+ pdf_action_user
+} pdf_action_type;
+
+typedef enum {
+ pdf_window_notset,
+ pdf_window_new,
+ pdf_window_nonew,
+} pdf_window_type;
+
+# define pdf_action_size 6
+
+# define pdf_action_type(a) vlink((a)+2) /* enum pdf_action_type */
+# define pdf_action_named_id(a) vinfo((a)+2) /* boolean */
+# define pdf_action_id(a) vlink((a)+3) /* number or toks */
+# define pdf_action_file(a) vinfo((a)+3) /* toks */
+# define pdf_action_new_window(a) vlink((a)+4) /* enum pdf_window_type */
+# define pdf_action_tokens(a) vinfo((a)+4) /* toks */
+# define pdf_action_refcount(a) vlink((a)+5) /* number */
+
+typedef enum {
+ colorstack_set = 0,
+ colorstack_push,
+ colorstack_pop,
+ colorstack_current
+} colorstack_commands;
+
+# define colorstack_data colorstack_push /* last value where data field is set */
+
+/* end of pdf backend nodes */
+
+/* user defined nodes */
+
+# define user_defined_node_size 4
+# define user_node_type(a) vinfo((a)+2)
+# define user_node_id(a) vlink((a)+2)
+# define user_node_value(a) vinfo((a)+3)
+
+/* node sused in the parbuilder */
+
+# define active_node_size 4 /*number of words in extended active nodes */
+# define fitness subtype /*|very_loose_fit..tight_fit| on final line for this break */
+# define break_node(a) vlink((a)+1) /*pointer to the corresponding passive node */
+# define line_number(a) vinfo((a)+1) /*line that begins at this breakpoint */
+# define total_demerits(a) varmem[(a)+2].cint /* the quantity that \TeX\ minimizes */
+# define active_short(a) vinfo(a+3) /* |shortfall| of this line */
+# define active_glue(a) vlink(a+3) /*corresponding glue stretch or shrink */
+
+# define passive_node_size 7
+# define cur_break(a) vlink((a)+1) /*in passive node, points to position of this breakpoint */
+# define prev_break(a) vinfo((a)+1) /*points to passive node that should precede this one */
+# define passive_pen_inter(a) vinfo((a)+2)
+# define passive_pen_broken(a) vlink((a)+2)
+# define passive_left_box(a) vlink((a)+3)
+# define passive_left_box_width(a) vinfo((a)+3)
+# define passive_last_left_box(a) vlink((a)+4)
+# define passive_last_left_box_width(a) vinfo((a)+4)
+# define passive_right_box(a) vlink((a)+5)
+# define passive_right_box_width(a) vinfo((a)+5)
+# define serial(a) vlink((a)+6) /* serial number for symbolic identification */
+
+# define delta_node_size 10 /* 8 fields, stored in a+1..9 */
+
+/* helpers */
+
+# define couple_nodes(a,b) {assert(b!=null);vlink(a)=b;alink(b)=a;}
+# define try_couple_nodes(a,b) if (b==null) vlink(a)=b; else {couple_nodes(a,b);}
+# define uncouple_node(a) {assert(a!=null);vlink(a)=null;alink(a)=null;}
+
+# define cache_disabled max_halfword
+
+extern void delete_attribute_ref(halfword b);
+extern void reset_node_properties(halfword b);
+extern void reassign_attribute(halfword n,halfword new);
+extern void build_attribute_list(halfword b);
+extern halfword current_attribute_list(void);
+
+extern int unset_attribute(halfword n, int c, int w);
+extern void set_attribute(halfword n, int c, int w);
+extern int has_attribute(halfword n, int c, int w);
+
+extern halfword new_span_node(halfword n, int c, scaled w);
+
+extern void print_short_node_contents(halfword n);
+extern void show_node_list(int i);
+extern pointer actual_box_width(pointer r, scaled base_width);
+
+typedef struct _subtype_info {
+ int id;
+ const char *name;
+ int lua;
+} subtype_info;
+
+typedef struct _field_info {
+ const char *name;
+ int lua;
+} field_info;
+
+typedef struct _node_info {
+ int id;
+ int size;
+ subtype_info *subtypes;
+ field_info *fields;
+ const char *name;
+ int etex;
+ int lua;
+} node_info;
+
+extern node_info node_data[];
+extern node_info whatsit_node_data[];
+
+extern subtype_info node_subtypes_dir[];
+extern subtype_info node_subtypes_glue[];
+extern subtype_info node_subtypes_mathglue[];
+extern subtype_info node_subtypes_leader[];
+extern subtype_info node_subtypes_boundary[];
+extern subtype_info node_subtypes_penalty[];
+extern subtype_info node_subtypes_kern[];
+extern subtype_info node_subtypes_rule[];
+extern subtype_info node_subtypes_glyph[];
+extern subtype_info node_subtypes_disc[];
+extern subtype_info node_subtypes_marginkern[];
+extern subtype_info node_subtypes_list[];
+extern subtype_info node_subtypes_adjust[];
+extern subtype_info node_subtypes_math[];
+extern subtype_info node_subtypes_noad[];
+extern subtype_info node_subtypes_radical[];
+extern subtype_info node_subtypes_accent[];
+extern subtype_info node_subtypes_fence[];
+
+extern subtype_info node_values_pdf_destination[];
+extern subtype_info node_values_pdf_literal[];
+extern subtype_info node_values_pdf_literal[];
+extern subtype_info node_values_pdf_action[];
+extern subtype_info node_values_pdf_window[];
+
+extern subtype_info node_values_fill[];
+extern subtype_info node_values_dir[];
+extern subtype_info node_values_color_stack[];
+
+extern subtype_info other_values_page_states[];
+
+extern halfword new_node(int i, int j);
+extern void flush_node_list(halfword);
+extern void flush_node(halfword);
+extern halfword do_copy_node_list(halfword, halfword);
+extern halfword copy_node_list(halfword);
+extern halfword copy_node(const halfword);
+extern void check_node(halfword);
+extern halfword fix_node_list(halfword);
+extern int fix_node_lists;
+extern char *sprint_node_mem_usage(void);
+extern halfword raw_glyph_node(void);
+extern halfword new_glyph_node(void);
+extern int valid_node(halfword);
+
+extern void flush_node_wrapup_dvi(halfword);
+extern void flush_node_wrapup_pdf(halfword);
+extern void copy_node_wrapup_dvi(halfword, halfword); /* original target */
+extern void copy_node_wrapup_pdf(halfword, halfword); /* original target */
+extern void check_node_wrapup_dvi(halfword); /* DEBUG_NODES mode */
+extern void check_node_wrapup_pdf(halfword); /* DEBUG_NODES mode */
+extern void show_node_wrapup_dvi(halfword);
+extern void show_node_wrapup_pdf(halfword);
+
+typedef enum {
+ normal_g = 0, /* normal */
+ sfi,
+ fil,
+ fill,
+ filll
+} glue_orders;
+
+# define zero_glue 0
+# define sfi_glue zero_glue+glue_spec_size
+# define fil_glue sfi_glue+glue_spec_size
+# define fill_glue fil_glue+glue_spec_size
+# define ss_glue fill_glue+glue_spec_size
+# define fil_neg_glue ss_glue+glue_spec_size
+# define page_ins_head fil_neg_glue+glue_spec_size
+
+# define contrib_head page_ins_head+temp_node_size
+# define page_head contrib_head+temp_node_size
+# define temp_head page_head+temp_node_size
+# define hold_head temp_head+temp_node_size
+# define adjust_head hold_head+temp_node_size
+# define pre_adjust_head adjust_head+temp_node_size
+# define active pre_adjust_head+temp_node_size
+# define align_head active+active_node_size
+# define end_span align_head+temp_node_size
+# define begin_point end_span+span_node_size
+# define end_point begin_point+glyph_node_size
+# define var_mem_stat_max (end_point+glyph_node_size-1)
+
+# define stretching 1
+# define shrinking 2
+
+# define last_normal_node shape_node
+# define last_whatsit_node pdf_restore_node
+
+# define is_running(A) ((A)==null_flag) /* tests for a running dimension */
+
+extern halfword tail_of_list(halfword p);
+
+extern int var_used;
+
+# define cache_disabled max_halfword
+
+extern int max_used_attr;
+extern halfword attr_list_cache;
+
+extern halfword new_null_box(void);
+extern halfword new_rule(int s);
+extern halfword new_glyph(int f, int c);
+extern quarterword norm_min(int h);
+extern halfword new_char(int f, int c);
+extern scaled glyph_width(halfword p);
+extern scaled glyph_height(halfword p);
+extern scaled glyph_depth(halfword p);
+extern halfword new_disc(void);
+extern halfword new_math(scaled w, int s);
+extern halfword new_spec(halfword p);
+extern halfword new_param_glue(int n);
+extern halfword new_glue(halfword q);
+extern halfword new_skip_param(int n);
+extern halfword new_kern(scaled w);
+extern halfword new_penalty(int m, int s);
+
+extern int lua_properties_enabled ;
+extern int lua_properties_level ;
+extern int lua_properties_use_metatable ;
+
+extern halfword make_local_par_node(int mode);
+
+extern void synctex_set_mode(int mode);
+extern int synctex_get_mode(void);
+extern void synctex_set_tag(int tag);
+extern void synctex_set_line(int line);
+extern void synctex_force_tag(int tag);
+extern void synctex_force_line(int tag);
+extern int synctex_get_tag(void);
+extern void synctex_set_no_files(int flag);
+extern int synctex_get_no_files(void);
+extern int synctex_get_line(void);
+
+extern void l_set_node_data(void) ;
+extern void l_set_whatsit_data(void) ;
+
+#endif
+
diff --git a/Build/source/texk/web2c/luatexdir/tex/textcodes.c b/Build/source/texk/web2c/luatexdir/tex/textcodes.c
new file mode 100644
index 00000000000..a6a0b3a55e3
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/textcodes.c
@@ -0,0 +1,530 @@
+/*
+
+textcodes.w
+
+Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your
+option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU General Public License along
+with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+/*tex
+
+Contrary to traditional \TEX\ we have catcode tables so that we can switch
+catcode regimes very fast. We can have many such regimes and they're stored in
+trees.
+
+*/
+
+#define CATCODESTACK 8
+#define CATCODEDEFAULT 12
+#define CATCODE_MAX 32767
+
+static sa_tree *catcode_heads = NULL;
+static int catcode_max = 0;
+static unsigned char *catcode_valid = NULL;
+
+void set_cat_code(int h, int n, halfword v, quarterword gl)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_tree s = catcode_heads[h];
+ if (h > catcode_max)
+ catcode_max = h;
+ if (s == NULL) {
+ sa_value.int_value = CATCODEDEFAULT;
+ s = new_sa_tree(CATCODESTACK, 1, sa_value);
+ catcode_heads[h] = s;
+ }
+ sa_value.int_value = (int) v;
+ set_sa_item(s, n, sa_value, gl);
+}
+
+halfword get_cat_code(int h, int n)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_tree s = catcode_heads[h];
+ if (h > catcode_max)
+ catcode_max = h;
+ if (s == NULL) {
+ sa_value.int_value = CATCODEDEFAULT;
+ s = new_sa_tree(CATCODESTACK, 1, sa_value);
+ catcode_heads[h] = s;
+ }
+ return (halfword) get_sa_item(s, n).int_value;
+}
+
+void unsave_cat_codes(int h, quarterword gl)
+{
+ int k;
+ if (h > catcode_max)
+ catcode_max = h;
+ for (k = 0; k <= catcode_max; k++) {
+ if (catcode_heads[k] != NULL)
+ restore_sa_stack(catcode_heads[k], gl);
+ }
+}
+
+static void initializecatcodes(void)
+{
+ sa_tree_item sa_value = { 0 };
+ catcode_max = 0;
+ catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1));
+ catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1));
+ memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1));
+ memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1));
+ catcode_valid[0] = 1;
+ sa_value.int_value = CATCODEDEFAULT;
+ catcode_heads[0] = new_sa_tree(CATCODESTACK, 1, sa_value);
+}
+
+static void dumpcatcodes(void)
+{
+ int total = 0;
+ int k;
+ for (k = 0; k <= catcode_max; k++) {
+ if (catcode_valid[k]) {
+ total++;
+ }
+ }
+ dump_int(catcode_max);
+ dump_int(total);
+ for (k = 0; k <= catcode_max; k++) {
+ if (catcode_valid[k]) {
+ dump_int(k);
+ dump_sa_tree(catcode_heads[k],"catcodes");
+ }
+ }
+}
+
+static void undumpcatcodes(void)
+{
+ int total, k, x;
+ xfree(catcode_heads);
+ xfree(catcode_valid);
+ catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1));
+ catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1));
+ memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1));
+ memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1));
+ undump_int(catcode_max);
+ undump_int(total);
+ for (k = 0; k < total; k++) {
+ undump_int(x);
+ catcode_heads[x] = undump_sa_tree("catcodes");
+ catcode_valid[x] = 1;
+ }
+}
+
+int valid_catcode_table(int h)
+{
+ if (h <= CATCODE_MAX && h >= 0 && catcode_valid[h]) {
+ return 1;
+ }
+ return 0;
+}
+
+void copy_cat_codes(int from, int to)
+{
+ if (from < 0 || from > CATCODE_MAX || catcode_valid[from] == 0) {
+ uexit(1);
+ }
+ if (to > catcode_max)
+ catcode_max = to;
+ destroy_sa_tree(catcode_heads[to]);
+ catcode_heads[to] = copy_sa_tree(catcode_heads[from]);
+ catcode_valid[to] = 1;
+}
+
+void initex_cat_codes(int h)
+{
+ int k;
+ if (h > catcode_max)
+ catcode_max = h;
+ destroy_sa_tree(catcode_heads[h]);
+ catcode_heads[h] = NULL;
+ set_cat_code(h, '\r', car_ret_cmd, 1);
+ set_cat_code(h, ' ', spacer_cmd, 1);
+ set_cat_code(h, '\\', escape_cmd, 1);
+ set_cat_code(h, '%', comment_cmd, 1);
+ set_cat_code(h, 127, invalid_char_cmd, 1);
+ set_cat_code(h, 0, ignore_cmd, 1);
+ set_cat_code(h, 0xFEFF, ignore_cmd, 1);
+ for (k = 'A'; k <= 'Z'; k++) {
+ set_cat_code(h, k, letter_cmd, 1);
+ set_cat_code(h, k + 'a' - 'A', letter_cmd, 1);
+ }
+ catcode_valid[h] = 1;
+}
+
+static void freecatcodes(void)
+{
+ int k;
+ for (k = 0; k <= catcode_max; k++) {
+ if (catcode_valid[k]) {
+ destroy_sa_tree(catcode_heads[k]);
+ }
+ }
+ xfree(catcode_heads);
+ xfree(catcode_valid);
+}
+
+/*tex
+
+The lowercase mapping codes are also stored in a tree.
+
+*/
+
+#define LCCODESTACK 8
+#define LCCODEDEFAULT 0
+
+static sa_tree lccode_head = NULL;
+
+void set_lc_code(int n, halfword v, quarterword gl)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = (int) v;
+ set_sa_item(lccode_head, n, sa_value, gl);
+}
+
+halfword get_lc_code(int n)
+{
+ return (halfword) get_sa_item(lccode_head, n).int_value;
+}
+
+static void unsavelccodes(quarterword gl)
+{
+ restore_sa_stack(lccode_head, gl);
+}
+
+static void initializelccodes(void)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = LCCODEDEFAULT;
+ lccode_head = new_sa_tree(LCCODESTACK, 1, sa_value);
+}
+
+static void dumplccodes(void)
+{
+ dump_sa_tree(lccode_head,"lccodes");
+}
+
+static void undumplccodes(void)
+{
+ lccode_head = undump_sa_tree("lccodes");
+}
+
+static void freelccodes(void)
+{
+ destroy_sa_tree(lccode_head);
+}
+
+/*tex
+
+And the uppercase mapping codes are again stored in a tree.
+
+*/
+
+#define UCCODESTACK 8
+#define UCCODEDEFAULT 0
+
+static sa_tree uccode_head = NULL;
+
+void set_uc_code(int n, halfword v, quarterword gl)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = (int) v;
+ set_sa_item(uccode_head, n, sa_value, gl);
+}
+
+halfword get_uc_code(int n)
+{
+ return (halfword) get_sa_item(uccode_head, n).int_value;
+}
+
+static void unsaveuccodes(quarterword gl)
+{
+ restore_sa_stack(uccode_head, gl);
+}
+
+static void initializeuccodes(void)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = UCCODEDEFAULT;
+ uccode_head = new_sa_tree(UCCODESTACK, 1, sa_value);
+}
+
+static void dumpuccodes(void)
+{
+ dump_sa_tree(uccode_head,"uccodes");
+}
+
+static void undumpuccodes(void)
+{
+ uccode_head = undump_sa_tree("uccodes");
+}
+
+static void freeuccodes(void)
+{
+ destroy_sa_tree(uccode_head);
+}
+
+/*tex
+
+By now it will be no surprise that the space factors get stored in a tree.
+
+*/
+
+#define SFCODESTACK 8
+#define SFCODEDEFAULT 1000
+
+static sa_tree sfcode_head = NULL;
+
+void set_sf_code(int n, halfword v, quarterword gl)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = (int) v;
+ set_sa_item(sfcode_head, n, sa_value, gl);
+}
+
+halfword get_sf_code(int n)
+{
+ return (halfword) get_sa_item(sfcode_head, n).int_value;
+}
+
+static void unsavesfcodes(quarterword gl)
+{
+ restore_sa_stack(sfcode_head, gl);
+}
+
+static void initializesfcodes(void)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = SFCODEDEFAULT;
+ sfcode_head = new_sa_tree(SFCODESTACK, 1, sa_value);
+}
+
+static void dumpsfcodes(void)
+{
+ dump_sa_tree(sfcode_head,"sfcodes");
+}
+
+static void undumpsfcodes(void)
+{
+ sfcode_head = undump_sa_tree("sfcodes");
+}
+
+static void freesfcodes(void)
+{
+ destroy_sa_tree(sfcode_head);
+}
+
+/*tex
+
+The hyphenation codes are indeed stored in a tree and are used instead of
+lowercase codes when deciding what characters to take into acccount when
+hyphenating. They are bound to upto |HJCODE_MAX| languages.
+
+*/
+
+#define HJCODESTACK 8
+#define HJCODEDEFAULT 0
+#define HJCODE_MAX 16383
+
+static sa_tree *hjcode_heads = NULL;
+static int hjcode_max = 0;
+static unsigned char *hjcode_valid = NULL;
+
+/*tex
+
+Here we set codes but we don't initialize from lccodes.
+
+*/
+
+void set_hj_code(int h, int n, halfword v, quarterword gl)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_tree s = hjcode_heads[h];
+ if (h > hjcode_max)
+ hjcode_max = h;
+ if (s == NULL) {
+ sa_value.int_value = HJCODEDEFAULT;
+ s = new_sa_tree(HJCODESTACK, 1, sa_value);
+ hjcode_heads[h] = s;
+ }
+ sa_value.int_value = (int) v;
+ set_sa_item(s, n, sa_value, gl);
+}
+
+/*tex
+
+We just return the lccodes when nothing is set.
+
+*/
+
+halfword get_hj_code(int h, int n)
+{
+ sa_tree s = hjcode_heads[h];
+ if (s == NULL) {
+ s = lccode_head;
+ }
+ return (halfword) get_sa_item(s, n).int_value;
+}
+
+/*tex
+
+We don't restore as we can have more languages in a paragraph and hyphenation
+takes place at a later stage so we would get weird grouping side effects .. so,
+one can overload settings but management is then upto the used, so no:
+
+*/
+
+/*
+ static void unsavehjcodes(quarterword gl) { }
+*/
+
+static void initializehjcodes(void)
+{
+ /*
+ sa_tree_item sa_value = { 0 };
+ */
+ hjcode_max = 0;
+ hjcode_heads = Mxmalloc_array(sa_tree, (HJCODE_MAX + 1));
+ hjcode_valid = Mxmalloc_array(unsigned char, (HJCODE_MAX + 1));
+ memset(hjcode_heads, 0, sizeof(sa_tree) * (HJCODE_MAX + 1));
+ memset(hjcode_valid, 0, sizeof(unsigned char) * (HJCODE_MAX + 1));
+ /*
+ hjcode_valid[0] = 1;
+ sa_value.int_value = HJCODEDEFAULT;
+ hjcode_heads[0] = new_sa_tree(HJCODESTACK, 1, sa_value);
+ */
+}
+
+void hj_codes_from_lc_codes(int h)
+{
+ sa_tree_item sa_value = { 0 };
+ sa_tree s = hjcode_heads[h];
+ if (s != NULL) {
+ destroy_sa_tree(s);
+ } else if (h > hjcode_max) {
+ hjcode_max = h;
+ }
+ if (s == NULL) {
+ sa_value.int_value = HJCODEDEFAULT;
+ s = new_sa_tree(HJCODESTACK, 1, sa_value);
+ hjcode_heads[h] = s;
+ }
+ hjcode_heads[h] = copy_sa_tree(lccode_head);
+ hjcode_valid[h] = 1;
+}
+
+static void dumphjcodes(void)
+{
+ int total = 0;
+ int k;
+ for (k = 0; k <= hjcode_max; k++) {
+ if (hjcode_valid[k]) {
+ total++;
+ }
+ }
+ dump_int(hjcode_max);
+ dump_int(total);
+ for (k = 0; k <= hjcode_max; k++) {
+ if (hjcode_valid[k]) {
+ dump_int(k);
+ dump_sa_tree(hjcode_heads[k],"hjcodes");
+ }
+ }
+}
+
+static void undumphjcodes(void)
+{
+ int total, k, x;
+ xfree(hjcode_heads);
+ xfree(hjcode_valid);
+ hjcode_heads = Mxmalloc_array(sa_tree, (HJCODE_MAX + 1));
+ hjcode_valid = Mxmalloc_array(unsigned char, (HJCODE_MAX + 1));
+ memset(hjcode_heads, 0, sizeof(sa_tree) * (HJCODE_MAX + 1));
+ memset(hjcode_valid, 0, sizeof(unsigned char) * (HJCODE_MAX + 1));
+ undump_int(hjcode_max);
+ undump_int(total);
+ for (k = 0; k < total; k++) {
+ undump_int(x);
+ hjcode_heads[x] = undump_sa_tree("hjcodes");
+ hjcode_valid[x] = 1;
+ }
+}
+
+static void freehjcodes(void)
+{
+ int k;
+ for (k = 0; k <= hjcode_max; k++) {
+ if (hjcode_valid[k]) {
+ destroy_sa_tree(hjcode_heads[k]);
+ }
+ }
+ xfree(hjcode_heads);
+ xfree(hjcode_valid);
+}
+
+/*tex
+
+The public management functions.
+
+*/
+
+void unsave_text_codes(quarterword grouplevel)
+{
+ unsavelccodes(grouplevel);
+ unsaveuccodes(grouplevel);
+ unsavesfcodes(grouplevel);
+}
+
+void initialize_text_codes(void)
+{
+ initializecatcodes();
+ initializelccodes();
+ initializeuccodes();
+ initializesfcodes();
+ initializehjcodes();
+}
+
+void free_text_codes(void)
+{
+ freecatcodes();
+ freelccodes();
+ freeuccodes();
+ freesfcodes();
+ freehjcodes();
+}
+
+void dump_text_codes(void)
+{
+ dumpcatcodes();
+ dumplccodes();
+ dumpuccodes();
+ dumpsfcodes();
+ dumphjcodes();
+}
+
+void undump_text_codes(void)
+{
+ undumpcatcodes();
+ undumplccodes();
+ undumpuccodes();
+ undumpsfcodes();
+ undumphjcodes();
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/textcodes.h b/Build/source/texk/web2c/luatexdir/tex/textcodes.h
new file mode 100644
index 00000000000..759d9ee740e
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/textcodes.h
@@ -0,0 +1,54 @@
+/* textcodes.h
+
+ Copyright 2009 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef TEXTCODES_H
+# define TEXTCODES_H
+
+void set_cat_code(int h, int n, halfword v, quarterword gl);
+halfword get_cat_code(int h, int n);
+int valid_catcode_table(int h);
+void unsave_cat_codes(int h, quarterword gl);
+void copy_cat_codes(int from, int to);
+void initex_cat_codes(int h);
+
+void set_lc_code(int n, halfword v, quarterword gl);
+halfword get_lc_code(int n);
+
+void set_uc_code(int n, halfword v, quarterword gl);
+halfword get_uc_code(int n);
+
+void set_sf_code(int n, halfword v, quarterword gl);
+halfword get_sf_code(int n);
+
+void set_hj_code(int l, int n, halfword v, quarterword gl);
+halfword get_hj_code(int l, int n);
+void hj_codes_from_lc_codes(int h);
+
+void initialize_text_codes(void);
+void unsave_text_codes(quarterword grouplevel);
+
+void dump_text_codes(void);
+void undump_text_codes(void);
+
+void free_math_codes(void);
+void free_text_codes(void);
+
+#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.c b/Build/source/texk/web2c/luatexdir/tex/textoken.c
new file mode 100644
index 00000000000..e28626bbd26
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/textoken.c
@@ -0,0 +1,3920 @@
+/*
+
+Copyright 2006-2011 Taco Hoekwater <taco@luatex.org>
+
+This file is part of LuaTeX.
+
+LuaTeX is free software; you can redistribute it and/or modify it under the terms
+of the GNU General Public License as published by the Free Software Foundation;
+either version 2 of the License, or (at your option) any later version.
+
+LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "ptexlib.h"
+
+#define detokenized_line() (line_catcode_table==NO_CAT_TABLE)
+
+#define do_get_cat_code(a,b) do { \
+ if (line_catcode_table==DEFAULT_CAT_TABLE) \
+ a=get_cat_code(cat_code_table_par,b); \
+ else if (line_catcode_table>-0xFF) \
+ a=get_cat_code(line_catcode_table,b); \
+ else \
+ a= - line_catcode_table - 0xFF ; \
+ } while (0)
+
+
+/*tex
+
+ 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 two large arrays called
+ |fixmem| and |varmem| 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.
+
+ Locations in |fixmem| are used for storing one-word records; a conventional
+ \.{AVAIL} stack is used for allocation in this array.
+
+*/
+
+/*tex the big dynamic storage area */
+
+smemory_word *fixmem;
+
+/*tex the smallest location of one-word memory in use */
+
+unsigned fix_mem_min;
+
+/*tex the largest location of one-word memory in use */
+
+unsigned fix_mem_max;
+
+/*tex
+
+ 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 |stat| $\ldots$
+ |tats| is not commented out, \TeX\ will run a bit slower but it will report
+ these statistics when |tracing_stats| is sufficiently large.
+
+*/
+
+/*tex how much memory is in use */
+
+int dyn_used;
+
+/*tex head of the list of available one-word nodes */
+
+halfword avail;
+
+/*tex the last one-word node used in |mem| */
+
+unsigned fix_mem_end;
+
+/*tex head of a junk list, write only */
+
+halfword garbage;
+
+/*tex head of a temporary list of some kind */
+
+halfword temp_token_head;
+
+/*tex head of a temporary list of another kind */
+
+halfword hold_token_head;
+
+/*tex a constant token list */
+
+halfword omit_template;
+
+/*tex permanently empty list */
+
+halfword null_list;
+
+/*tex head of token list built by |scan_keyword| */
+
+halfword backup_head;
+
+void initialize_tokens(void)
+{
+ halfword p;
+ avail = null;
+ fix_mem_end = 0;
+ p = get_avail();
+ temp_token_head = p;
+ set_token_info(temp_token_head, 0);
+ p = get_avail();
+ hold_token_head = p;
+ set_token_info(hold_token_head, 0);
+ p = get_avail();
+ omit_template = p;
+ set_token_info(omit_template, 0);
+ p = get_avail();
+ null_list = p;
+ set_token_info(null_list, 0);
+ p = get_avail();
+ backup_head = p;
+ set_token_info(backup_head, 0);
+ p = get_avail();
+ garbage = p;
+ set_token_info(garbage, 0);
+ dyn_used = 0;
+}
+
+/*tex
+
+ 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.
+
+ If the available-space list is empty, i.e., if |avail=null|, we try first to
+ increase |fix_mem_end|. If that cannot be done, i.e., if
+ |fix_mem_end=fix_mem_max|, we try to reallocate array |fixmem|. If, that
+ doesn't work, we have to quit.
+
+ Single-word node allocation:
+*/
+
+halfword get_avail(void)
+{
+ /*tex The new node being got: */
+ unsigned p;
+ unsigned t;
+ /*tex Get top location in the |avail| stack. */
+ p = (unsigned) avail;
+ if (p != null) {
+ /*tex Pop it off. */
+ avail = token_link(avail);
+ } else if (fix_mem_end < fix_mem_max) {
+ /*tex Go into virgin territory. */
+ incr(fix_mem_end);
+ p = fix_mem_end;
+ } else {
+ /*tex The big dynamic storage area. */
+ smemory_word *new_fixmem;
+ t = (fix_mem_max / 5);
+ new_fixmem = fixmemcast(realloc(fixmem, sizeof(smemory_word) * (fix_mem_max + t + 1)));
+ if (new_fixmem == NULL) {
+ /*tex If memory is exhausted, display possible runaway text. */
+ runaway();
+ overflow("token memory size", fix_mem_max);
+ } else {
+ fixmem = new_fixmem;
+ }
+ memset(voidcast(fixmem + fix_mem_max + 1), 0, t * sizeof(smemory_word));
+ fix_mem_max += t;
+ p = ++fix_mem_end;
+ }
+ /*tex Provide an oft-desired initialization of the new node. */
+ token_link(p) = null;
+ /*tex Maintain statistics. */
+ incr(dyn_used);
+ return (halfword) p;
+}
+
+/*tex
+
+ The procedure |flush_list(p)| frees an entire linked list of one-word nodes
+ that starts at position |p|.
+
+ This makes list of single-word nodes available:
+
+*/
+
+void flush_list(halfword p)
+{
+ halfword q, r;
+ if (p != null) {
+ r = p;
+ do {
+ q = r;
+ r = token_link(r);
+ decr(dyn_used);
+ } while (r != null);
+ /*tex Now |q| is the last node on the list. */
+ token_link(q) = avail;
+ avail = p;
+ }
+}
+
+/*tex
+
+ A \TeX\ token is either a character or a control sequence, and it is
+ 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^{21}m+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^{25}-1$ is larger than
+ $2^{21}m+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.
+
+ A token list is a singly linked list of one-word nodes in |mem|, where each
+ word contains a token and a link. Macro definitions, output-routine
+ definitions, marks, \.{\\write} texts, and a few other things are remembered
+ by \TeX\ in the form of token lists, usually preceded by a node with a
+ reference count in its |token_ref_count| field. The token stored in location
+ |p| is called |info(p)|.
+
+ Three special commands appear in the token lists of macro definitions. When
+ |m=match|, it means that \TeX\ should scan a parameter for the current macro;
+ when |m=end_match|, it means that parameter matching should end and \TeX\
+ should start reading the macro text; and when |m=out_param|, it means that
+ \TeX\ should insert parameter number |c| into the text at this point.
+
+ The enclosing \.{\char'173} and \.{\char'175} characters of a macro
+ definition are omitted, but the final right brace of an output routine is
+ included at the end of its token list.
+
+ Here is an example macro definition that illustrates these conventions. After
+ \TeX\ processes the text
+
+ \starttyping
+ \def\mac a#1#2 \b {#1\-a ##1#2 \#2\}
+ \stoptyping
+
+ the definition of \.{\\mac} is represented as a token list containing
+
+ \starttyping
+ (reference count) |letter|a |match|# |match|# |spacer| \b |end_match|
+ |out_param|1 \- |letter|a |spacer|, |mac_param|# |other_char|1
+ |out_param|2 |spacer| |out_param|2
+ \stoptyping
+
+ The procedure |scan_toks| builds such token lists, and |macro_call| does the
+ parameter matching.
+
+ Examples such as \type{\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 |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in symbolic
+ form, including the expansion of a macro or mark.
+
+*/
+
+void print_meaning(void)
+{
+ /*tex
+
+ This would make sense but some macro packages don't like it:
+
+ \starttyping
+ if (cur_cmd == math_given_cmd) {
+ cur_cmd = xmath_given_cmd ;
+ }
+ \stoptyping
+
+ */
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ if (cur_cmd >= call_cmd) {
+ print_char(':');
+ print_ln();
+ token_show(cur_chr);
+ } else {
+ /*tex Show the meaning of a mark node. */
+ if ((cur_cmd == top_bot_mark_cmd) && (cur_chr < marks_code)) {
+ print_char(':');
+ print_ln();
+ switch (cur_chr) {
+ case first_mark_code:
+ token_show(first_mark(0));
+ break;
+ case bot_mark_code:
+ token_show(bot_mark(0));
+ break;
+ case split_first_mark_code:
+ token_show(split_first_mark(0));
+ break;
+ case split_bot_mark_code:
+ token_show(split_bot_mark(0));
+ break;
+ default:
+ token_show(top_mark(0));
+ break;
+ }
+ }
+ }
+}
+
+/*tex
+
+ 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\ }'.
+
+*/
+
+#define not_so_bad(p) \
+ switch (m) { \
+ case assign_int_cmd: \
+ if (c >= (backend_int_base) && c <= (backend_int_last)) \
+ p("[internal backend integer]"); \
+ break; \
+ case assign_dimen_cmd: \
+ if (c >= (backend_dimen_base) && c <= (backend_dimen_last)) \
+ p("[internal backend dimension]"); \
+ break; \
+ case assign_toks_cmd: \
+ if (c >= (backend_toks_base) && c <= (backend_toks_last)) \
+ p("[internal backend tokenlist]"); \
+ break; \
+ case node_cmd: \
+ p("[internal node pointer]"); \
+ break; \
+ case lua_call_cmd: \
+ p("[internal lua function call]"); \
+ break; \
+ case lua_expandable_call_cmd: \
+ p("[internal expandable lua function call]"); \
+ break; \
+ case lua_local_call_cmd: \
+ p("[internal local lua function call]"); \
+ break; \
+ default: \
+ p("BAD"); \
+ break; \
+ }
+
+void show_token_list(int p, int q, int l)
+{
+ /*tex pieces of a token */
+ int m, c;
+ /*tex character used in a `|match|' */
+ ASCII_code match_chr = '#';
+ /*tex the highest parameter number, as an ASCII digit */
+ ASCII_code n = '0';
+ tally = 0;
+ if (l < 0)
+ l = 0x3FFFFFFF;
+ while ((p != null) && (tally < l)) {
+ if (p == q) {
+ /*tex Do magic computation. */
+ set_trick_count();
+ }
+ /*tex Display token |p|, and |return| if there are problems. */
+ if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) {
+ tprint_esc("CLOBBERED.");
+ return;
+ }
+ if (token_info(p) >= cs_token_flag) {
+ if (!((inhibit_par_tokens) && (token_info(p) == par_token)))
+ print_cs(token_info(p) - cs_token_flag);
+ } else {
+ m = token_cmd(token_info(p));
+ c = token_chr(token_info(p));
+ if (token_info(p) < 0) {
+ tprint_esc("BAD");
+ } else {
+ /*
+ Display the token \type {(|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.
+
+ */
+ switch (m) {
+ case left_brace_cmd:
+ case right_brace_cmd:
+ case math_shift_cmd:
+ case tab_mark_cmd:
+ case sup_mark_cmd:
+ case sub_mark_cmd:
+ case spacer_cmd:
+ case letter_cmd:
+ case other_char_cmd:
+ print(c);
+ break;
+ case mac_param_cmd:
+ if (!in_lua_escape && (is_in_csname==0))
+ print(c);
+ print(c);
+ break;
+ case out_param_cmd:
+ print(match_chr);
+ if (c <= 9) {
+ print_char(c + '0');
+ } else {
+ print_char('!');
+ return;
+ }
+ break;
+ case match_cmd:
+ match_chr = c;
+ print(c);
+ incr(n);
+ print_char(n);
+ if (n > '9')
+ return;
+ break;
+ case end_match_cmd:
+ if (c == 0)
+ tprint("->");
+ break;
+ default:
+ not_so_bad(tprint);
+ break;
+ }
+ }
+ }
+ p = token_link(p);
+ }
+ if (p != null)
+ tprint_esc("ETC.");
+}
+
+#define do_buffer_to_unichar(a,b) do { \
+ a = (halfword)str2uni(buffer+b); \
+ b += utf8_size(a); \
+} while (0)
+
+/*tex
+
+ Here's the way we sometimes want to display a token list, given a pointer to
+ its reference count; the pointer may be null.
+
+*/
+
+void token_show(halfword p)
+{
+ if (p != null)
+ show_token_list(token_link(p), null, 10000000);
+}
+
+/*tex
+
+ |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.
+
+ |p| points to the reference count of a token list that is losing one
+ reference.
+
+*/
+
+void delete_token_ref(halfword p)
+{
+ if (token_ref_count(p) == 0)
+ flush_list(p);
+ else
+ decr(token_ref_count(p));
+}
+
+int get_char_cat_code(int curchr)
+{
+ int a;
+ do_get_cat_code(a,curchr);
+ return a;
+}
+
+static void invalid_character_error(void)
+{
+ const char *hlp[] = {
+ "A funny symbol that I can't read has just been input.",
+ "Continue, and I'll forget that it ever happened.",
+ NULL
+ };
+ deletions_allowed = false;
+ tex_error("Text line contains an invalid character", hlp);
+ deletions_allowed = true;
+}
+
+static boolean process_sup_mark(void);
+
+static int scan_control_sequence(void);
+
+typedef enum {
+ next_line_ok,
+ next_line_return,
+ next_line_restart
+} next_line_retval;
+
+static next_line_retval next_line(void);
+
+/*tex
+
+ 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.).
+
+*/
+
+boolean scan_keyword(const char *s)
+{
+ /*tex tail of the backup list */
+ halfword p;
+ /*tex new node being added to the token list via |store_new_token| */
+ halfword q;
+ /*tex index into |str_pool| */
+ const char *k;
+ halfword save_cur_cs = cur_cs;
+ if (strlen(s) == 0) {
+ /*tex but not with newtokenlib zero keyword simply doesn't match */
+ return false ;
+ }
+ p = backup_head;
+ token_link(p) = null;
+ k = s;
+ while (*k) {
+ /*tex Recursion is possible here! */
+ get_x_token();
+ if ((cur_cs == 0) && ((cur_chr == *k) || (cur_chr == *k - 'a' + 'A'))) {
+ store_new_token(cur_tok);
+ k++;
+ } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) {
+ back_input();
+ if (p != backup_head) {
+ begin_token_list(token_link(backup_head), backed_up);
+ }
+ cur_cs = save_cur_cs;
+ return false;
+ }
+ }
+ if (token_link(backup_head) != null)
+ flush_list(token_link(backup_head));
+ cur_cs = save_cur_cs;
+ return true;
+}
+
+boolean scan_keyword_case_sensitive(const char *s)
+{
+ halfword p;
+ halfword q;
+ const char *k;
+ halfword save_cur_cs = cur_cs;
+ if (strlen(s) == 0)
+ return false ;
+ p = backup_head;
+ token_link(p) = null;
+ k = s;
+ while (*k) {
+ get_x_token();
+ if ((cur_cs == 0) && (cur_chr == *k)) {
+ store_new_token(cur_tok);
+ k++;
+ } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) {
+ back_input();
+ if (p != backup_head) {
+ begin_token_list(token_link(backup_head), backed_up);
+ }
+ cur_cs = save_cur_cs;
+ return false;
+ }
+ }
+ if (token_link(backup_head) != null)
+ flush_list(token_link(backup_head));
+ cur_cs = save_cur_cs;
+ return true;
+}
+
+/*tex
+
+ We can not return |undefined_control_sequence| under some conditions (inside
+ |shift_case|, for example). This needs thinking.
+
+*/
+
+/*static char * FFFF = "\xEF\xBF\xBF";*/ /* 0xFFFF */
+
+halfword active_to_cs(int curchr, int force)
+{
+ halfword curcs;
+ int nncs = no_new_control_sequence;
+ if (force) {
+ no_new_control_sequence = false;
+ }
+ if (curchr > 0) {
+ char *b = (char *) uni2str((unsigned) curchr);
+ char *utfbytes = xmalloc(8);
+ utfbytes = strcpy(utfbytes, "\xEF\xBF\xBF");
+ utfbytes = strcat(utfbytes, b);
+ free(b);
+ curcs = string_lookup(utfbytes, utf8_size(curchr)+3);
+ free(utfbytes);
+ } else {
+ /*tex 0xFFFF ... why not 3 ? */
+ curcs = string_lookup("\xEF\xBF\xBF", 4);
+ }
+ no_new_control_sequence = nncs;
+ return curcs;
+}
+
+/*
+
+ static unsigned char *uni2csstr(unsigned unic)
+ {
+ unsigned char *buf = xmalloc(8);
+ unsigned char *pt = buf;
+ *pt++ = 239; *pt++ = 191; *pt++ = 191; // 0xFFFF
+ if (unic < 0x80)
+ *pt++ = (unsigned char) unic;
+ else if (unic < 0x800) {
+ *pt++ = (unsigned char) (0xc0 | (unic >> 6));
+ *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
+ } else if (unic >= 0x110000) {
+ *pt++ = (unsigned char) (unic - 0x110000);
+ } else if (unic < 0x10000) {
+ *pt++ = (unsigned char) (0xe0 | (unic >> 12));
+ *pt++ = (unsigned char) (0x80 | ((unic >> 6) & 0x3f));
+ *pt++ = (unsigned char) (0x80 | (unic & 0x3f));
+ } else {
+ int u, z, y, x;
+ unsigned val = unic - 0x10000;
+ u = (int) (((val & 0xf0000) >> 16) + 1);
+ z = (int) ((val & 0x0f000) >> 12);
+ y = (int) ((val & 0x00fc0) >> 6);
+ x = (int) (val & 0x0003f);
+ *pt++ = (unsigned char) (0xf0 | (u >> 2));
+ *pt++ = (unsigned char) (0x80 | ((u & 3) << 4) | z);
+ *pt++ = (unsigned char) (0x80 | y);
+ *pt++ = (unsigned char) (0x80 | x);
+ }
+ *pt = '\0';
+ return buf;
+ }
+
+ halfword active_to_cs(int curchr, int force)
+ {
+ halfword curcs;
+ int nncs = no_new_control_sequence;
+ if (force) {
+ no_new_control_sequence = false;
+ }
+ if (curchr > 0) {
+ char * utfbytes = (char *) uni2csstr((unsigned) curchr);
+ curcs = string_lookup(utfbytes, utf8_size(curchr)+3);
+ free(utfbytes);
+ } else {
+ curcs = string_lookup(FFFF, 4); // 0xFFFF ... why not 3 ?
+ }
+ no_new_control_sequence = nncs;
+ return curcs;
+ }
+
+*/
+
+/*tex
+
+ Maybe this function should listen to \.{\\escapechar} but we can do without.
+
+*/
+
+static char *cs_to_string(halfword p)
+{
+ const char *s;
+ char *sh;
+ int k = 0;
+ static char ret[256] = { 0 };
+ if (p == 0 || p == null_cs) {
+ ret[k++] = '\\';
+ s = "csname";
+ while (*s) {
+ ret[k++] = *s++;
+ }
+ ret[k++] = '\\';
+ s = "endcsname";
+ while (*s) {
+ ret[k++] = *s++;
+ }
+ ret[k] = 0;
+
+ } else {
+ str_number txt = cs_text(p);
+ sh = makecstring(txt);
+ s = sh;
+ if (is_active_cs(txt)) {
+ s = s + 3;
+ while (*s) {
+ ret[k++] = *s++;
+ }
+ ret[k] = 0;
+ } else {
+ ret[k++] = '\\';
+ while (*s) {
+ ret[k++] = *s++;
+ }
+ ret[k] = 0;
+ }
+ free(sh);
+ }
+ return (char *) ret;
+}
+
+/*tex This is sort of a hack. */
+
+static char *cmd_chr_to_string(int cmd, int chr)
+{
+ char *s;
+ str_number str;
+ int sel = selector;
+ selector = new_string;
+ print_cmd_chr((quarterword) cmd, chr);
+ str = make_string();
+ s = makecstring(str);
+ selector = sel;
+ flush_str(str);
+ return s;
+}
+
+/*tex
+
+ 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.
+
+ 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.
+
+*/
+
+/*tex
+
+ The location of `\.{\\par}' in |eqtb| adn the token representing `\.{\\par}.
+
+*/
+
+halfword par_loc;
+halfword par_token;
+
+/*tex
+
+ Parts |get_next| are executed more often than any other instructions of \TeX.
+
+ The global variable |force_eof| is normally |false|; it is set |true| by an
+ \.{\\endinput} command. |luacstrings| is the number of lua print statements
+ waiting to be input, it is changed by |luatokencall|.
+
+
+*/
+
+/*tex Should the next \.{\\input} be aborted early? */
+boolean force_eof;
+
+/*tex How many lua strings are waiting to be input? */
+
+int luacstrings;
+
+/*tex
+
+ 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.
+
+*/
+
+void firm_up_the_line(void)
+{
+ int k;
+ ilimit = last;
+ if (pausing_par > 0) {
+ if (interaction > nonstop_mode) {
+ wake_up_terminal();
+ print_ln();
+ if (istart < ilimit) {
+ for (k = istart; k <= ilimit - 1; k++)
+ print_char(buffer[k]);
+ }
+ first = ilimit;
+ prompt_input("=>");
+ if (last > first) {
+ /*tex Move line down in buffer. */
+ for (k = first; k < +last - 1; k++)
+ buffer[k + istart - first] = buffer[k];
+ ilimit = istart + last - first;
+ }
+ }
+ }
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void check_outer_validity(void)
+{
+ /*tex points to inserted token list */
+ halfword p;
+ /*tex auxiliary pointer */
+ halfword q;
+ if (suppress_outer_error_par)
+ return;
+ if (scanner_status != normal) {
+ deletions_allowed = false;
+ /*tex
+
+ Back up an outer control sequence so that it can be reread. An
+ outer control sequence that occurs in a \.{\\read} will not be
+ reread, since the error recovery for \.{\\read} is not very powerful.
+
+ */
+ if (cur_cs != 0) {
+ if ((istate == token_list) || (iname < 1) || (iname > 17)) {
+ p = get_avail();
+ token_info(p) = cs_token_flag + cur_cs;
+ /*tex prepare to read the control sequence again */
+ begin_token_list(p, backed_up);
+ }
+ /*tex replace it by a space */
+ cur_cmd = spacer_cmd;
+ cur_chr = ' ';
+ }
+ if (scanner_status > skipping) {
+ const char *errhlp[] = {
+ "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.",
+ NULL
+ };
+ char errmsg[318];
+ const char *startmsg;
+ const char *scannermsg;
+ /*tex
+
+ Tell the user what has run away and try to recover Print a
+ definition, argument, or preamble.
+
+ */
+ runaway();
+ if (cur_cs == 0) {
+ startmsg = "File ended";
+ } else {
+ cur_cs = 0;
+ startmsg = "Forbidden control sequence found";
+ }
+ /*tex
+
+ Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or
+ `\.{text}', and insert tokens that should lead to recovery. The
+ recovery procedure can't be fully understood without knowing more
+ about the \TeX\ routines that should be aborted, but we can
+ sketch the ideas here: For a runaway definition we will insert a
+ right brace; for a runaway preamble, we will insert a special
+ \.{\\cr} token and a right brace; and for a runaway argument, we
+ will set |long_state| to |outer_call| and insert \.{\\par}.
+
+ */
+ p = get_avail();
+ switch (scanner_status) {
+ case defining:
+ scannermsg = "definition";
+ token_info(p) = right_brace_token + '}';
+ break;
+ case matching:
+ scannermsg = "use";
+ token_info(p) = par_token;
+ long_state = outer_call_cmd;
+ break;
+ case aligning:
+ scannermsg = "preamble";
+ token_info(p) = right_brace_token + '}';
+ q = p;
+ p = get_avail();
+ token_link(p) = q;
+ token_info(p) = cs_token_flag + frozen_cr;
+ align_state = -1000000;
+ break;
+ case absorbing:
+ scannermsg = "text";
+ token_info(p) = right_brace_token + '}';
+ break;
+ default:
+ scannermsg = "unknown";
+ break;
+ }
+ begin_token_list(p, inserted);
+ snprintf(errmsg, 318, "%s while scanning %s of %s", startmsg, scannermsg, cs_to_string(warning_index));
+ tex_error(errmsg, errhlp);
+ } else {
+ char errmsg[256];
+ const char *errhlp_no[] = {
+ "The file ended while I was skipping conditional text.",
+ "This kind of error happens when you say `\\if...' and forget",
+ "the matching `\\fi'. I've inserted a `\\fi'; this might work.",
+ NULL
+ };
+ const char *errhlp_cs[] = {
+ "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.",
+ NULL
+ };
+ const char **errhlp = (const char **) errhlp_no;
+ char *ss;
+ if (cur_cs != 0) {
+ errhlp = errhlp_cs;
+ cur_cs = 0;
+ }
+ ss = cmd_chr_to_string(if_test_cmd, cur_if);
+ snprintf(errmsg, 255, "Incomplete %s; all text was ignored after line %d",
+ ss, (int) skip_line);
+ free(ss);
+ /*tex Incomplete |\if...| */
+ cur_tok = cs_token_flag + frozen_fi;
+ /*tex back up one inserted token and call |error|. */
+ {
+ OK_to_interrupt = false;
+ back_input();
+ token_type = inserted;
+ OK_to_interrupt = true;
+ tex_error(errmsg, errhlp);
+ }
+ }
+ deletions_allowed = true;
+ }
+}
+
+#if 0
+
+/*tex
+
+ The other variant gives less clutter in tracing cache usage when profiling
+ and for some files (like the manual) also a bit of a speedup.
+
+*/
+
+static boolean get_next_file(void)
+{
+ SWITCH:
+ if (iloc <= ilimit) {
+ /*tex current line not yet finished */
+ do_buffer_to_unichar(cur_chr, iloc);
+ RESWITCH:
+ if (detokenized_line()) {
+ cur_cmd = (cur_chr == ' ' ? 10 : 12);
+ } else {
+ do_get_cat_code(cur_cmd, cur_chr);
+ }
+ /*tex
+
+ Change state if necessary, and |goto switch| if the current
+ character should be ignored, or |goto reswitch| if the current
+ character changes to another;
+
+ The following 48-way switch accomplishes the scanning quickly,
+ assuming that a decent C 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.
+
+ Remark [ls/hh]: checking performance indicated that this switch was
+ the cause of many branch prediction errors but changing it to:
+
+ \starttyping
+ c = istate + cur_cmd;
+ if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) {
+ return true;
+ } else if (c >= new_line) {
+ switch (c) {
+ }
+ } else if (c >= skip_blanks) {
+ switch (c) {
+ }
+ } else if (c >= mid_line) {
+ switch (c) {
+ }
+ } else {
+ istate = mid_line;
+ return true;
+ }
+ \stoptyping
+
+ gives as many prediction errors. So, we can indeed assume that the
+ compiler does the right job, or that there is simply no other way.
+
+ */
+
+ switch (istate + cur_cmd) {
+ case mid_line + ignore_cmd:
+ case skip_blanks + ignore_cmd:
+ case new_line + ignore_cmd:
+ case skip_blanks + spacer_cmd:
+ case new_line + spacer_cmd:
+ /*tex Cases where character is ignored. */
+ goto SWITCH;
+ break;
+ case mid_line + escape_cmd:
+ case new_line + escape_cmd:
+ case skip_blanks + escape_cmd:
+ /*tex Scan a control sequence. */
+ istate = (unsigned char) scan_control_sequence();
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ break;
+ case mid_line + active_char_cmd:
+ case new_line + active_char_cmd:
+ case skip_blanks + active_char_cmd:
+ /*tex Process an active-character. */
+ cur_cs = active_to_cs(cur_chr, false);
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ istate = mid_line;
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ break;
+ case mid_line + sup_mark_cmd:
+ case new_line + sup_mark_cmd:
+ case skip_blanks + sup_mark_cmd:
+ /*tex If this |sup_mark| starts. */
+ if (process_sup_mark())
+ goto RESWITCH;
+ else
+ istate = mid_line;
+ break;
+ case mid_line + invalid_char_cmd:
+ case new_line + invalid_char_cmd:
+ case skip_blanks + invalid_char_cmd:
+ /*tex Decry the invalid character and |goto restart|. */
+ invalid_character_error();
+ /*tex Because state may be |token_list| now: */
+ return false;
+ break;
+ case mid_line + spacer_cmd:
+ /*tex Enter |skip_blanks| state, emit a space. */
+ istate = skip_blanks;
+ cur_chr = ' ';
+ break;
+ case mid_line + car_ret_cmd:
+ /*tex
+
+ Finish line, emit a space. 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.
+
+ */
+ iloc = ilimit + 1;
+ cur_cmd = spacer_cmd;
+ cur_chr = ' ';
+ break;
+ case skip_blanks + car_ret_cmd:
+ case mid_line + comment_cmd:
+ case new_line + comment_cmd:
+ case skip_blanks + comment_cmd:
+ /*tex Finish line, |goto switch|; */
+ iloc = ilimit + 1;
+ goto SWITCH;
+ break;
+ case new_line + car_ret_cmd:
+ /*tex Finish line, emit a \.{\\par}; */
+ iloc = ilimit + 1;
+ cur_cs = par_loc;
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ break;
+ case skip_blanks + left_brace_cmd:
+ case new_line + left_brace_cmd:
+ istate = mid_line;
+ /*tex Fall through. */
+ case mid_line + left_brace_cmd:
+ align_state++;
+ break;
+ case skip_blanks + right_brace_cmd:
+ case new_line + right_brace_cmd:
+ istate = mid_line;
+ /*tex Fall through. */
+ case mid_line + right_brace_cmd:
+ align_state--;
+ break;
+ case mid_line + math_shift_cmd:
+ case mid_line + tab_mark_cmd:
+ case mid_line + mac_param_cmd:
+ case mid_line + sub_mark_cmd:
+ case mid_line + letter_cmd:
+ case mid_line + other_char_cmd:
+ break;
+ /*
+ case skip_blanks + math_shift:
+ case skip_blanks + tab_mark:
+ case skip_blanks + mac_param:
+ case skip_blanks + sub_mark:
+ case skip_blanks + letter:
+ case skip_blanks + other_char:
+ case new_line + math_shift:
+ case new_line + tab_mark:
+ case new_line + mac_param:
+ case new_line + sub_mark:
+ case new_line + letter:
+ case new_line + other_char:
+ */
+ default:
+ istate = mid_line;
+ break;
+ }
+ } else {
+ if (iname != 21)
+ istate = new_line;
+ /*tex
+
+ Move to next line of file, or |goto restart| if there is no next line,
+ or |return| if a \.{\\read} line has finished;
+
+ */
+ do {
+ next_line_retval r = next_line();
+ if (r == next_line_return) {
+ return true;
+ } else if (r == next_line_restart) {
+ return false;
+ }
+ } while (0);
+ check_interrupt();
+ goto SWITCH;
+ }
+ return true;
+}
+
+#else
+
+/*tex
+
+ This variant gives 10 times less Bim in vallgrind!
+
+*/
+
+static boolean get_next_file(void)
+{
+ int c = 0;
+ SWITCH:
+ if (iloc <= ilimit) {
+ /*tex current line not yet finished */
+ do_buffer_to_unichar(cur_chr, iloc);
+ RESWITCH:
+ if (detokenized_line()) {
+ cur_cmd = (cur_chr == ' ' ? 10 : 12);
+ } else {
+ do_get_cat_code(cur_cmd, cur_chr);
+ }
+ /*tex
+
+ Change state if necessary, and |goto switch| if the current character
+ should be ignored, or |goto reswitch| if the current character changes
+ to another.
+
+ */
+ c = istate + cur_cmd;
+ if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) {
+ return true;
+ } else if (c >= new_line) {
+ switch (c-new_line) {
+ case escape_cmd:
+ istate = (unsigned char) scan_control_sequence();
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ return true;
+ case left_brace_cmd:
+ istate = mid_line;
+ align_state++;
+ return true;
+ case right_brace_cmd:
+ istate = mid_line;
+ align_state--;
+ return true;
+ case math_shift_cmd:
+ istate = mid_line;
+ return true;
+ case tab_mark_cmd:
+ istate = mid_line;
+ return true;
+ case car_ret_cmd:
+ /*tex Finish line, emit a \.{\\par}. */
+ iloc = ilimit + 1;
+ cur_cs = par_loc;
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ return true;
+ case mac_param_cmd:
+ istate = mid_line;
+ return true;
+ case sup_mark_cmd:
+ if (process_sup_mark())
+ goto RESWITCH;
+ else
+ istate = mid_line;
+ return true;
+ case sub_mark_cmd:
+ istate = mid_line;
+ return true;
+ case ignore_cmd:
+ goto SWITCH;
+ return true;
+ case spacer_cmd:
+ /*tex Cases where character is ignored. */
+ goto SWITCH;
+ case letter_cmd:
+ istate = mid_line;
+ return true;
+ case other_char_cmd:
+ istate = mid_line;
+ return true;
+ case active_char_cmd:
+ cur_cs = active_to_cs(cur_chr, false);
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ istate = mid_line;
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ return true;
+ case comment_cmd:
+ iloc = ilimit + 1;
+ goto SWITCH;
+ case invalid_char_cmd:
+ /*tex Because state may be |token_list| now. */
+ invalid_character_error();
+ return false;
+ default:
+ istate = mid_line;
+ return true;
+ }
+ } else if (c >= skip_blanks) {
+ switch (c-skip_blanks) {
+ case escape_cmd:
+ /*tex Scan a control sequence. */
+ istate = (unsigned char) scan_control_sequence();
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ return true;
+ case left_brace_cmd:
+ istate = mid_line;
+ align_state++;
+ return true;
+ case right_brace_cmd:
+ istate = mid_line;
+ align_state--;
+ return true;
+ case math_shift_cmd:
+ istate = mid_line;
+ return true;
+ case tab_mark_cmd:
+ istate = mid_line;
+ return true;
+ case car_ret_cmd:
+ iloc = ilimit + 1;
+ goto SWITCH;
+ case mac_param_cmd:
+ istate = mid_line;
+ return true;
+ case sup_mark_cmd:
+ if (process_sup_mark())
+ goto RESWITCH;
+ else
+ istate = mid_line;
+ return true;
+ case sub_mark_cmd:
+ istate = mid_line;
+ return true;
+ case ignore_cmd:
+ goto SWITCH;
+ case spacer_cmd:
+ goto SWITCH;
+ case letter_cmd:
+ istate = mid_line;
+ return true;
+ case other_char_cmd:
+ istate = mid_line;
+ return true;
+ case active_char_cmd:
+ cur_cs = active_to_cs(cur_chr, false);
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ istate = mid_line;
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ return true;
+ case comment_cmd:
+ /*tex Finish line, |goto switch|. */
+ iloc = ilimit + 1;
+ goto SWITCH;
+ case invalid_char_cmd:
+ /*tex Decry the invalid character and |goto restart|. */
+ invalid_character_error();
+ /*tex Because state may be |token_list| now. */
+ return false;
+ default:
+ istate = mid_line;
+ return true;
+ }
+ } else if (c >= mid_line) {
+ switch (c-mid_line) {
+ case escape_cmd:
+ istate = (unsigned char) scan_control_sequence();
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ return true;
+ case left_brace_cmd:
+ align_state++;
+ return true;
+ case right_brace_cmd:
+ align_state--;
+ return true;
+ case math_shift_cmd:
+ return true;
+ case tab_mark_cmd:
+ return true;
+ case car_ret_cmd:
+ /*tex
+
+ Finish line, emit a space. 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.
+
+ */
+ iloc = ilimit + 1;
+ cur_cmd = spacer_cmd;
+ cur_chr = ' ';
+ return true;
+ case mac_param_cmd:
+ return true;
+ case sup_mark_cmd:
+ if (process_sup_mark())
+ goto RESWITCH;
+ else
+ istate = mid_line;
+ return true;
+ case sub_mark_cmd:
+ return true;
+ case ignore_cmd:
+ goto SWITCH;
+ case spacer_cmd:
+ /*tex Enter |skip_blanks| state, emit a space. */
+ istate = skip_blanks;
+ cur_chr = ' ';
+ return true;
+ case letter_cmd:
+ istate = mid_line;
+ return true;
+ case other_char_cmd:
+ istate = mid_line;
+ return true;
+ case active_char_cmd:
+ cur_cs = active_to_cs(cur_chr, false);
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ istate = mid_line;
+ if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
+ check_outer_validity();
+ return true;
+ case comment_cmd:
+ iloc = ilimit + 1;
+ goto SWITCH;
+ case invalid_char_cmd:
+ /*tex Because state may be |token_list| now. */
+ invalid_character_error();
+ return false;
+ default:
+ istate = mid_line;
+ return true;
+ }
+ } else {
+ istate = mid_line;
+ return true;
+ }
+ } else {
+ if (iname != 21) {
+ istate = new_line;
+ }
+ /*tex
+
+ Move to next line of file, or |goto restart| if there is no next
+ line, or |return| if a \.{\\read} line has finished;
+
+ */
+ do {
+ next_line_retval r = next_line();
+ if (r == next_line_return) {
+ return true;
+ } else if (r == next_line_restart) {
+ return false;
+ }
+ } while (0);
+ check_interrupt();
+ goto SWITCH;
+ }
+ return true;
+}
+
+#endif
+
+/*tex
+
+ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
+ We only support a limited set:
+
+ \starttyping
+ ^^^^^^XXXXXX
+ ^^^^XXXXXX
+ ^^XX ^^<char>
+ \stoptyping
+
+*/
+
+#define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f'))
+
+#define add_nybble(c) \
+ if (c<='9') { \
+ cur_chr=(cur_chr<<4)+c-'0'; \
+ } else { \
+ cur_chr=(cur_chr<<4)+c-'a'+10; \
+ }
+
+#define set_nybble(c) \
+ if (c<='9') { \
+ cur_chr=c-'0'; \
+ } else { \
+ cur_chr=c-'a'+10; \
+ }
+
+#define one_hex_to_cur_chr(c1) \
+ set_nybble(c1);
+
+#define two_hex_to_cur_chr(c1,c2) \
+ set_nybble(c1); \
+ add_nybble(c2);
+
+#define four_hex_to_cur_chr(c1,c2,c3,c4) \
+ two_hex_to_cur_chr(c1,c2); \
+ add_nybble(c3); \
+ add_nybble(c4);
+
+#define six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6) \
+ four_hex_to_cur_chr(c1,c2,c3,c4); \
+ add_nybble(c5); \
+ add_nybble(c6);
+
+static boolean process_sup_mark(void)
+{
+ if (cur_chr == buffer[iloc]) {
+ if (iloc < ilimit) {
+ if ((cur_chr == buffer[iloc + 1]) && (cur_chr == buffer[iloc + 2])) {
+ if ((cur_chr == buffer[iloc + 3]) && (cur_chr == buffer[iloc + 4])) {
+ /*tex |^^^^^^XXXXXX| */
+ if ((iloc + 10) <= ilimit) {
+ int c1 = buffer[iloc + 5];
+ int c2 = buffer[iloc + 6];
+ int c3 = buffer[iloc + 7];
+ int c4 = buffer[iloc + 8];
+ int c5 = buffer[iloc + 9];
+ int c6 = buffer[iloc + 10];
+ if (is_hex(c1) && is_hex(c2) && is_hex(c3) &&
+ is_hex(c4) && is_hex(c5) && is_hex(c6)) {
+ iloc = iloc + 11;
+ six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6);
+ return true;
+ } else {
+ tex_error("^^^^^^ needs six hex digits", NULL);
+ }
+ } else {
+ tex_error("^^^^^^ needs six hex digits, end of input", NULL);
+ }
+ } else {
+ /*tex |^^^^XXXX| */
+ if ((iloc + 6) <= ilimit) {
+ int c1 = buffer[iloc + 3];
+ int c2 = buffer[iloc + 4];
+ int c3 = buffer[iloc + 5];
+ int c4 = buffer[iloc + 6];
+ if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4)) {
+ iloc = iloc + 7;
+ four_hex_to_cur_chr(c1,c2,c3,c4);
+ return true;
+ } else {
+ tex_error("^^^^ needs four hex digits", NULL);
+ }
+ } else {
+ tex_error("^^^^ needs four hex digits, end of input", NULL);
+ }
+ }
+ } else {
+ /*tex |^^XX| */
+ if ((iloc + 2) <= ilimit) {
+ int c1 = buffer[iloc + 1];
+ int c2 = buffer[iloc + 2];
+ if (is_hex(c1) && is_hex(c2)) {
+ iloc = iloc + 3;
+ two_hex_to_cur_chr(c1,c2);
+ return true;
+ }
+ }
+ /*tex Go on, no error, good old \TEX . */
+ }
+ }
+ /*tex The rest. */
+ {
+ int c1 = buffer[iloc + 1];
+ if (c1 < 0200) {
+ iloc = iloc + 2;
+ if (is_hex(c1) && (iloc <= ilimit)) {
+ int c2 = buffer[iloc];
+ if (is_hex(c2)) {
+ incr(iloc);
+ two_hex_to_cur_chr(c1,c2);
+ return true;
+ }
+ }
+ cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*tex
+
+ 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[ilimit+1]|, but it never looks at |buffer[ilimit+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.
+
+*/
+
+static boolean check_expanded_code(int *kk); /* below */
+
+static int scan_control_sequence(void)
+{
+ int retval = mid_line;
+ if (iloc > ilimit) {
+ /*tex |state| is irrelevant in this case. */
+ cur_cs = null_cs;
+ } else {
+ /*tex |cat_code(cur_chr)|, usually: */
+ register int cat;
+ while (1) {
+ int k = iloc;
+ do_buffer_to_unichar(cur_chr, k);
+ do_get_cat_code(cat, cur_chr);
+ if (cat != letter_cmd || k > ilimit) {
+ retval = (cat == spacer_cmd ? skip_blanks : mid_line);
+ /*tex If an expanded \unknown */
+ if (cat == sup_mark_cmd && check_expanded_code(&k))
+ continue;
+ } else {
+ retval = skip_blanks;
+ do {
+ do_buffer_to_unichar(cur_chr, k);
+ do_get_cat_code(cat, cur_chr);
+ } while (cat == letter_cmd && k <= ilimit);
+ /*tex If an expanded \unknown */
+ if (cat == sup_mark_cmd && check_expanded_code(&k))
+ continue;
+ if (cat != letter_cmd) {
+ /*tex Backtrack one character which can be \UTF. */
+ if (cur_chr <= 0x7F) {
+ k -= 1; /* in most cases */
+ } else if (cur_chr > 0xFFFF) {
+ k -= 4;
+ } else if (cur_chr > 0x7FF) {
+ k -= 3;
+ } else /* if (cur_chr > 0x7F) */ {
+ k -= 2;
+ }
+ /*tex Now |k| points to first nonletter. */
+ }
+ }
+ cur_cs = id_lookup(iloc, k - iloc);
+ iloc = k;
+ break;
+ }
+ }
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ return retval;
+}
+
+/*tex
+
+ Whenever we reach the following piece of code, we will have
+ |cur_chr=buffer[k-1]| and |k<=ilimit+1| and
+ |cat=get_cat_code(cat_code_table,cur_chr)|. If an expanded code like
+ \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)..(k+1)]| or
+ |buffer[(k-1)..(k+2)]|, we will store the corresponding code in |buffer[k-1]|
+ and shift the rest of the buffer left two or three places.
+
+*/
+
+static boolean check_expanded_code(int *kk)
+{
+ int l;
+ int k = *kk;
+ int d = 1;
+ if (buffer[k] == cur_chr && k < ilimit) {
+ if ((cur_chr == buffer[k + 1]) && (cur_chr == buffer[k + 2])) {
+ if ((cur_chr == buffer[k + 3]) && (cur_chr == buffer[k + 4])) {
+ if ((k + 10) <= ilimit) {
+ int c1 = buffer[k + 6 - 1];
+ int c2 = buffer[k + 6];
+ int c3 = buffer[k + 6 + 1];
+ int c4 = buffer[k + 6 + 2];
+ int c5 = buffer[k + 6 + 3];
+ int c6 = buffer[k + 6 + 4];
+ if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4) && is_hex(c5) && is_hex(c6)) {
+ d = 6;
+ six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6);
+ } else {
+ tex_error("^^^^^^ needs six hex digits", NULL);
+ }
+ } else {
+ tex_error("^^^^^^ needs six hex digits, end of input", NULL);
+ }
+ } else {
+ if ((k + 6) <= ilimit) {
+ int c1 = buffer[k + 4 - 1];
+ int c2 = buffer[k + 4];
+ int c3 = buffer[k + 4 + 1];
+ int c4 = buffer[k + 4 + 2];
+ if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4)) {
+ d = 4;
+ four_hex_to_cur_chr(c1,c2,c3,c4);
+ } else {
+ tex_error("^^^^ needs four hex digits", NULL);
+ }
+ } else {
+ tex_error("^^^^ needs four hex digits, end of input", NULL);
+ }
+ }
+ } else {
+ int c1 = buffer[k + 1];
+ if (c1 < 0200) {
+ d = 1;
+ if (is_hex(c1) && (k + 2) <= ilimit) {
+ int c2 = buffer[k + 2];
+ if (is_hex(c2)) {
+ d = 2;
+ two_hex_to_cur_chr(c1,c2);
+ } else {
+ cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
+ }
+ } else {
+ cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100);
+ }
+ }
+ }
+ if (d > 2)
+ d = 2 * d - 1;
+ else
+ d++;
+ if (cur_chr <= 0x7F) {
+ buffer[k - 1] = (packed_ASCII_code) cur_chr;
+ } else if (cur_chr <= 0x7FF) {
+ buffer[k - 1] = (packed_ASCII_code) (0xC0 + cur_chr / 0x40);
+ k++;
+ d--;
+ buffer[k - 1] = (packed_ASCII_code) (0x80 + cur_chr % 0x40);
+ } else if (cur_chr <= 0xFFFF) {
+ buffer[k - 1] = (packed_ASCII_code) (0xE0 + cur_chr / 0x1000);
+ k++;
+ d--;
+ buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) / 0x40);
+ k++;
+ d--;
+ buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) % 0x40);
+ } else {
+ buffer[k - 1] = (packed_ASCII_code) (0xF0 + cur_chr / 0x40000);
+ k++;
+ d--;
+ buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x40000) / 0x1000);
+ k++;
+ d--;
+ buffer[k - 1] = (packed_ASCII_code) (0x80 + ((cur_chr % 0x40000) % 0x1000) / 0x40);
+ k++;
+ d--;
+ buffer[k - 1] = (packed_ASCII_code) (0x80 + ((cur_chr % 0x40000) % 0x1000) % 0x40);
+ }
+ l = k;
+ ilimit = ilimit - d;
+ while (l <= ilimit) {
+ buffer[l] = buffer[l + d];
+ l++;
+ }
+ *kk = k;
+ return true;
+ }
+ return false;
+}
+
+/*tex
+
+ All of the easy branches of |get_next| have now been taken care of. There is
+ one more branch.
+
+*/
+
+static next_line_retval next_line(void)
+{
+ /*tex A way to end a pseudo file without trailing space: */
+ boolean inhibit_eol = false;
+ if (iname > 17) {
+ /*tex
+
+ Read next line of file into |buffer|, or |goto restart| if the file
+ has ended.
+
+ */
+ incr(line);
+ first = istart;
+ if (!force_eof) {
+ if (iname <= 20) {
+ if (pseudo_input()) {
+ /*tex Not end of file; set |ilimit|. */
+ firm_up_the_line();
+ line_catcode_table = DEFAULT_CAT_TABLE;
+ if ((iname == 19) && (pseudo_lines(pseudo_files) == null))
+ inhibit_eol = true;
+ } else if ((every_eof_par != null) && !eof_seen[iindex]) {
+ ilimit = first - 1;
+ /*tex Fake one empty line. */
+ eof_seen[iindex] = true;
+ if (iname != 19)
+ begin_token_list(every_eof_par, every_eof_text);
+ return next_line_restart;
+ } else {
+ force_eof = true;
+ }
+ } else {
+ if (iname == 21) {
+ halfword n = null;
+ int t = luacstring_input(&n);
+ switch (t) {
+ case 0:
+ force_eof = true;
+ break;
+ case 1:
+ /*tex string */
+ firm_up_the_line();
+ line_catcode_table = (short) luacstring_cattable();
+ line_partial = (signed char) luacstring_partial();
+ if (luacstring_final_line() || line_partial || line_catcode_table == NO_CAT_TABLE)
+ inhibit_eol = true;
+ if (!line_partial)
+ istate = new_line;
+ break;
+ case 2:
+ /*tex token */
+ cur_tok = n;
+ back_input();
+ /*tex Needs checking. */
+ return next_line_restart;
+ break;
+ case 3:
+ /*tex node */
+ if (n < biggest_char) {
+ /*tex |0x10FFFF == 1114111| */
+ cur_tok = token_val(node_cmd, n);
+ back_input();
+ /*tex Needs checking. */
+ return next_line_restart;
+ } else {
+ normal_warning("nodes","unable to store reference from lua in tex");
+ force_eof = true;
+ }
+ break;
+ default:
+ force_eof = true;
+ break;
+ }
+ } else if (lua_input_ln(cur_file, 0, true)) {
+ /*tex Not end of file, set |ilimit|. */
+ firm_up_the_line();
+ line_catcode_table = DEFAULT_CAT_TABLE;
+ } else if ((every_eof_par != null) && (!eof_seen[iindex])) {
+ ilimit = first - 1;
+ /* tex Fake one empty line. */
+ eof_seen[iindex] = true;
+ begin_token_list(every_eof_par, every_eof_text);
+ return next_line_restart;
+ } else {
+ force_eof = true;
+ }
+ }
+ }
+ if (force_eof) {
+ if (tracing_nesting_par > 0)
+ if ((grp_stack[in_open] != cur_boundary) || (if_stack[in_open] != cond_ptr))
+ if (!((iname == 19) || (iname == 21))) {
+ /*tex Give warning for some unfinished groups and/or conditionals. */
+ file_warning();
+ }
+ if ((iname > 21) || (iname == 20)) {
+ report_stop_file(filetype_tex);
+ decr(open_parens);
+ }
+ force_eof = false;
+ /*tex \LUA\ input or \.{\\scantextokens} */
+ if (iname == 21 || iname == 19) {
+ end_file_reading();
+ } else {
+ end_file_reading();
+ if (! suppress_outer_error_par)
+ check_outer_validity();
+ }
+ return next_line_restart;
+ }
+ if (inhibit_eol || end_line_char_inactive)
+ ilimit--;
+ else
+ buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+ first = ilimit + 1;
+ iloc = istart;
+ /*tex We're ready to read. */
+ } else {
+ if (!terminal_input) {
+ /*tex \.{\\read} line has ended */
+ cur_cmd = 0;
+ cur_chr = 0;
+ return next_line_return;
+ }
+ if (input_ptr > 0) {
+ /*tex Text was inserted during error recovery. */
+ end_file_reading();
+ /*tex Resume previous level. */
+ return next_line_restart;
+ }
+ if (selector < log_only)
+ open_log_file();
+ if (interaction > nonstop_mode) {
+ if (end_line_char_inactive)
+ ilimit++;
+ if (ilimit == istart) {
+ /*tex Previous line was empty. */
+ tprint_nl("(Please type a command or say `\\end')");
+ }
+ print_ln();
+ first = istart;
+ /*tex Input on-line into |buffer| */
+ prompt_input("*");
+ ilimit = last;
+ if (end_line_char_inactive)
+ ilimit--;
+ else
+ buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+ first = ilimit + 1;
+ iloc = istart;
+ } else {
+ /*tex
+
+ Nonstop mode, which is intended for overnight batch processing,
+ never waits for on-line input.
+
+ */
+ fatal_error("*** (job aborted, no legal \\end found)");
+ }
+ }
+ return next_line_ok;
+}
+
+/*tex
+
+ Let's consider now what happens when |get_next| is looking at a token list.
+
+*/
+
+static boolean get_next_tokenlist(void)
+{
+ register halfword t = token_info(iloc);
+ /*tex Move to next. */
+ iloc = token_link(iloc);
+ if (t >= cs_token_flag) {
+ /*tex A control sequence token */
+ cur_cs = t - cs_token_flag;
+ cur_cmd = eq_type(cur_cs);
+ if (cur_cmd >= outer_call_cmd) {
+ if (cur_cmd == dont_expand_cmd) {
+ /*tex
+
+ Get the next token, suppressing expansion. 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, |token_info(iloc)| is known to be a control sequence
+ token, and |token_link(iloc)=null|.
+
+ */
+ cur_cs = token_info(iloc) - cs_token_flag;
+ iloc = null;
+ cur_cmd = eq_type(cur_cs);
+ if (cur_cmd > max_command_cmd) {
+ cur_cmd = relax_cmd;
+ cur_chr = no_expand_flag;
+ return true;
+ }
+ } else if (! suppress_outer_error_par) {
+ check_outer_validity();
+ }
+ }
+ cur_chr = equiv(cur_cs);
+ } else {
+ cur_cmd = token_cmd(t);
+ cur_chr = token_chr(t);
+ switch (cur_cmd) {
+ case left_brace_cmd:
+ align_state++;
+ break;
+ case right_brace_cmd:
+ align_state--;
+ break;
+ case out_param_cmd:
+ /*tex Insert macro parameter and |goto restart|. */
+ begin_token_list(param_stack[param_start + cur_chr - 1], parameter);
+ return false;
+ break;
+ }
+ }
+ return true;
+}
+
+/*tex
+
+ 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.
+
+ This sets |cur_cmd|, |cur_chr|, |cur_cs| to next token:
+
+*/
+
+void get_next(void)
+{
+ RESTART:
+ cur_cs = 0;
+ if (istate != token_list) {
+ /*tex Input from external file, |goto restart| if no input found. */
+ if (!get_next_file())
+ goto RESTART;
+ } else {
+ if (iloc == null) {
+ end_token_list();
+ /*tex List exhausted, resume previous level. */
+ goto RESTART;
+ } else if (!get_next_tokenlist()) {
+ /*tex Parameter needs to be expanded. */
+ goto RESTART;
+ }
+ }
+ /*tex If an alignment entry has just ended, take appropriate action. */
+ if ((cur_cmd == tab_mark_cmd || cur_cmd == car_ret_cmd) && align_state == 0) {
+ insert_vj_template();
+ goto RESTART;
+ }
+}
+
+/*tex
+
+ Since |get_next| is used so frequently in \TeX, it is convenient to define
+ three related procedures that do a little more:
+
+ \startitemize
+ \startitem
+ |get_token| not only sets |cur_cmd| and |cur_chr|, it also sets
+ |cur_tok|, a packed halfword version of the current token.
+ \stopitem
+ \startitem
+ |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.
+ \stopitem
+ \startitem
+ |x_token| is like |get_x_token| except that it assumes that
+ |get_next| has already been called.
+ \stopitem
+ \stopitemize
+
+ 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.
+
+ This sets |cur_cmd|, |cur_chr|, |cur_tok|:
+
+*/
+
+void get_token(void)
+{
+ no_new_control_sequence = false;
+ get_next();
+ no_new_control_sequence = true;
+ if (cur_cs == 0)
+ cur_tok = token_val(cur_cmd, cur_chr);
+ else
+ cur_tok = cs_token_flag + cur_cs;
+}
+
+/*tex This changes the string |s| to a token list. */
+
+halfword string_to_toks(const char *ss)
+{
+ /*tex tail of the token list */
+ halfword p;
+ /*tex new node being added to the token list via |store_new_token| */
+ halfword q;
+ /*tex token being appended */
+ halfword t;
+ const char *s = ss;
+ const char *se = ss + strlen(s);
+ p = temp_token_head;
+ set_token_link(p, null);
+ while (s < se) {
+ t = (halfword) str2uni((const unsigned char *) s);
+ s += utf8_size(t);
+ if (t == ' ')
+ t = space_token;
+ else
+ t = other_token + t;
+ fast_store_new_token(t);
+ }
+ return token_link(temp_token_head);
+}
+
+/*tex
+
+ 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_token_head)| and
+ ends at the value |p| that is returned. (If |p=temp_token_head|, the list is
+ empty.)
+
+ |lua_str_toks| is almost identical, but it also escapes the three symbols
+ that |lua| considers special while scanning a literal string.
+
+ This changes the string |str_pool[b..pool_ptr]| to a token list:
+
+*/
+
+halfword lua_str_toks(lstring b)
+{
+ /*tex tail of the token list */
+ halfword p;
+ /*tex new node being added to the token list via |store_new_token| */
+ halfword q;
+ /*tex token being appended */
+ halfword t;
+ /*tex index into string */
+ unsigned char *k;
+ p = temp_token_head;
+ set_token_link(p, null);
+ k = (unsigned char *) b.s;
+ while (k < (unsigned char *) b.s + b.l) {
+ t = pool_to_unichar(k);
+ k += utf8_size(t);
+ if (t == ' ') {
+ t = space_token;
+ } else {
+ if ((t == '\\') || (t == '"') || (t == '\'') || (t == 10) || (t == 13))
+ fast_store_new_token(other_token + '\\');
+ if (t == 10)
+ t = 'n';
+ if (t == 13)
+ t = 'r';
+ t = other_token + t;
+ }
+ fast_store_new_token(t);
+ }
+ return p;
+}
+
+/*tex
+
+ Incidentally, the main reason for wanting |str_toks| is the function
+ |the_toks|, which has similar input/output characteristics.
+
+ This changes the string |str_pool[b..pool_ptr]| to a token list:
+
+*/
+
+halfword str_toks(lstring s)
+{
+ /*tex tail of the token list */
+ halfword p;
+ /*tex new node being added to the token list via |store_new_token| */
+ halfword q;
+ /*tex token being appended */
+ halfword t;
+ /*tex index into string */
+ unsigned char *k, *l;
+ p = temp_token_head;
+ set_token_link(p, null);
+ k = s.s;
+ l = k + s.l;
+ while (k < l) {
+ t = pool_to_unichar(k);
+ k += utf8_size(t);
+ if (t == ' ')
+ t = space_token;
+ else
+ t = other_token + t;
+ fast_store_new_token(t);
+ }
+ return p;
+}
+
+/*tex
+
+ Most of the converter is similar to the one i made for macro so at some point
+ I can make a helper; also todo: there is no need to go through the pool.
+
+*/
+
+/*tex Change the string |str_pool[b..pool_ptr]| to a token list. */
+
+halfword str_scan_toks(int ct, lstring s)
+{
+ /*tex tail of the token list */
+ halfword p;
+ /*tex new node being added to the token list via |store_new_token| */
+ halfword q;
+ /*tex token being appended */
+ halfword t;
+ /*tex index into string */
+ unsigned char *k, *l;
+ int cc;
+ p = temp_token_head;
+ set_token_link(p, null);
+ k = s.s;
+ l = k + s.l;
+ while (k < l) {
+ t = pool_to_unichar(k);
+ k += utf8_size(t);
+ cc = get_cat_code(ct,t);
+ if (cc == 0) {
+ /*tex We have a potential control sequence so we check for it. */
+ int _lname = 0 ;
+ int _s = 0 ;
+ int _c = 0 ;
+ halfword _cs = null ;
+ unsigned char *_name = k ;
+ while (k < l) {
+ t = (halfword) str2uni((const unsigned char *) k);
+ _s = utf8_size(t);
+ _c = get_cat_code(ct,t);
+ if (_c == 11) {
+ k += _s ;
+ _lname = _lname + _s ;
+ } else if (_c == 10) {
+ /*tex We ignore a trailing space like normal scanning does. */
+ k += _s ;
+ break ;
+ } else {
+ break ;
+ }
+ }
+ if (_s > 0) {
+ /*tex We have a potential |\cs|. */
+ _cs = string_lookup((const char *) _name, _lname);
+ if (_cs == undefined_control_sequence) {
+ /* let's play safe and backtrack */
+ t = cc * (1<<21) + t ;
+ k = _name ;
+ } else {
+ t = cs_token_flag + _cs;
+ }
+ } else {
+ /*tex
+
+ Just a character with some meaning, so |\unknown| becomes
+ effectively |\unknown| assuming that |\\| has some useful
+ meaning of course.
+
+ */
+ t = cc * (1<<21) + t ;
+ k = _name ;
+ }
+
+ } else {
+ /*tex
+
+ Whatever token, so for instance $x^2$ just works given a \TEX\
+ catcode regime.
+
+ */
+ t = cc * (1<<21) + t ;
+ }
+ fast_store_new_token(t);
+
+ }
+ return p;
+}
+
+/*tex
+
+ Here's part of the |expand| subroutine that we are now ready to complete:
+
+*/
+
+void ins_the_toks(void)
+{
+ (void) the_toks();
+ ins_list(token_link(temp_token_head));
+}
+
+#define set_toks_register(n,t,g) do { \
+ int a = (g>0) ? 4 : 0; \
+ halfword ref = get_avail(); \
+ set_token_ref_count(ref, 0); \
+ set_token_link(ref, token_link(t)); \
+ define(n + toks_base, call_cmd, ref); \
+} while (0)
+
+#define append_copied_toks_list(s,t) do { \
+ halfword p; \
+ halfword q; \
+ p = temp_token_head; \
+ set_token_link(p, null); \
+ while (s != null) { \
+ fast_store_new_token(token_info(s)); \
+ s = token_link(s); \
+ } \
+ while (t != null) { \
+ fast_store_new_token(token_info(t)); \
+ t = token_link(t); \
+ } \
+ } while (0)
+
+
+/*tex
+
+ \starttabulate[|T||T||]
+ \NC 0 \NC \type {toksapp} \NC 1 \NC \type {etoksapp} \NC \NR
+ \NC 2 \NC \type {tokspre} \NC 3 \NC \type {etokspre} \NC \NR
+ \NC 4 \NC \type {gtoksapp} \NC 5 \NC \type {xtoksapp} \NC \NR
+ \NC 6 \NC \type {gtokspre} \NC 7 \NC \type {xtokspre} \NC \NR
+ \stoptabulate
+
+*/
+
+# define immediate_permitted(n,target) ((eq_level(toks_base + n) == cur_level) && (token_ref_count(target) == 0))
+
+void combine_the_toks(void)
+{
+ halfword how = cur_chr;
+ halfword source = null;
+ halfword target = null;
+ halfword append = (how == 0) || (how == 1) || (how == 4) || (how == 5);
+ halfword expand = odd(how);
+ halfword global = how > 3;
+ halfword nt, ns, s, t, p, q, h;
+ get_x_token();
+ /*tex The target. */
+ if (cur_cmd == assign_toks_cmd) {
+ /*tex Check range. */
+ nt = equiv(cur_cs) - toks_base;
+ } else {
+ back_input();
+ scan_register_num();
+ nt = cur_val;
+ }
+ /*tex The source. */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_cmd == left_brace_cmd) {
+ back_input();
+ scan_toks(false,expand);
+ source = def_ref;
+ /*tex The action. */
+ if (source != null) {
+ target = toks(nt);
+ if (target == null) {
+ set_toks_register(nt,source,global);
+ token_link(source) = null;
+ } else {
+ s = token_link(source);
+ if (s != null) {
+ t = token_link(target);
+ if (t == null) {
+ /*tex Can this happen? */
+ set_token_link(target, s);
+ token_link(source) = null;
+ } else if (append) {
+ /*tex Append. */
+ if (immediate_permitted(nt,target)) {
+ p = t;
+ while (token_link(p) != null) {
+ p = token_link(p);
+ }
+ while (s != null) {
+ fast_store_new_token(token_info(s));
+ s = token_link(s);
+ }
+ } else {
+ append_copied_toks_list(t,s);
+ set_toks_register(nt,temp_token_head,global);
+ }
+ } else {
+ /* prepend */
+ if (immediate_permitted(nt,target)) {
+ h = null;
+ p = null ;
+ while (s != null) {
+ fast_store_new_token(token_info(s));
+ if (h == null) {
+ h = p;
+ }
+ s = token_link(s);
+ }
+ set_token_link(p,t);
+ set_token_link(target,h);
+ } else {
+ append_copied_toks_list(s,t);
+ set_toks_register(nt,temp_token_head,global);
+ }
+ }
+ }
+ }
+ flush_list(source);
+ }
+ } else {
+ if (cur_cmd == assign_toks_cmd) {
+ ns = equiv(cur_cs) - toks_base;
+ /*tex Check range. */
+ } else {
+ scan_register_num();
+ ns = cur_val;
+ }
+ /*tex The action. */
+ source = toks(ns);
+ if (source != null) {
+ target = toks(nt);
+ if (target == null) {
+ /*tex The assign. */
+ token_ref_count(source)++;
+ equiv(toks_base+nt) = source;
+ return;
+ }
+ s = token_link(source);
+ t = token_link(target);
+ if (append) {
+ /*tex Append. */
+ if (immediate_permitted(nt,target)) {
+ p = t;
+ while (token_link(p) != null) {
+ p = token_link(p);
+ }
+ while (s != null) {
+ fast_store_new_token(token_info(s));
+ s = token_link(s);
+ }
+ } else {
+ append_copied_toks_list(t,s);
+ set_toks_register(nt,temp_token_head,global);
+ }
+ } else {
+ /*tex Prepend. */
+ if (immediate_permitted(nt,target)) {
+ h = null;
+ p = null;
+ while (s != null) {
+ fast_store_new_token(token_info(s));
+ if (h == null) {
+ h = p;
+ }
+ s = token_link(s);
+ }
+ set_token_link(p,t);
+ set_token_link(target,h);
+ } else {
+ append_copied_toks_list(s,t);
+ set_toks_register(nt,temp_token_head,global);
+ }
+ }
+ }
+ }
+}
+
+/*tex
+
+ This routine, used in the next one, prints the job name, possibly modified by
+ the |process_jobname| callback.
+
+*/
+
+static void print_job_name(void)
+{
+ if (job_name) {
+ /*tex C strings for jobname before and after processing. */
+ char *s, *ss;
+ int callback_id, lua_retval;
+ s = (char*)str_string(job_name);
+ callback_id = callback_defined(process_jobname_callback);
+ if (callback_id > 0) {
+ lua_retval = run_callback(callback_id, "S->S", s, &ss);
+ if ((lua_retval == true) && (ss != NULL))
+ s = ss;
+ }
+ tprint(s);
+ } else {
+ print(job_name);
+ }
+}
+
+/*tex
+
+ Here is a routine that print the result of a convert command, using the
+ argument |i|. It returns |false | if it does not know to print the code |c|.
+ The function exists because lua code and tex code can both call it to convert
+ something.
+
+ Parse optional \LUA\ state integer, or an instance name to be stored in |sn|
+ and get the next non-blank non-relax non-call token.
+
+*/
+
+int scan_lua_state(void)
+{
+ int sn = 0;
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ back_input();
+ if (cur_cmd != left_brace_cmd) {
+ if (scan_keyword("name")) {
+ (void) scan_toks(false, true);
+ sn = def_ref;
+ } else {
+ scan_register_num();
+ if (get_lua_name(cur_val))
+ sn = (cur_val - 65536);
+ }
+ }
+ return sn;
+}
+
+/*tex
+
+ 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}'.
+
+ The extra temp string |u| is needed because |pdf_scan_ext_toks| incorporates
+ any pending string in its output. In order to save such a pending string, we
+ have to create a temporary string that is destroyed immediately after.
+
+*/
+
+#define push_selector { \
+ old_setting = selector; \
+ selector = new_string; \
+}
+
+#define pop_selector { \
+ selector = old_setting; \
+}
+
+static int do_variable_dvi(halfword c)
+{
+ return 0;
+}
+
+#define do_variable_backend_int(i) \
+ cur_cmd = assign_int_cmd; \
+ cur_val = backend_int_base + i; \
+ cur_tok = token_val(cur_cmd, cur_val); \
+ back_input();
+
+#define do_variable_backend_dimen(i) \
+ cur_cmd = assign_dimen_cmd; \
+ cur_val = backend_dimen_base + i; \
+ cur_tok = token_val(cur_cmd, cur_val); \
+ back_input();
+
+#define do_variable_backend_toks(i) \
+ cur_cmd = assign_toks_cmd; \
+ cur_val = backend_toks_base + i ; \
+ cur_tok = token_val(cur_cmd, cur_val); \
+ back_input();
+
+static int do_variable_pdf(halfword c)
+{
+ if (scan_keyword("compresslevel")) { do_variable_backend_int(c_pdf_compress_level); }
+ else if (scan_keyword("decimaldigits")) { do_variable_backend_int(c_pdf_decimal_digits); }
+ else if (scan_keyword("imageresolution")) { do_variable_backend_int(c_pdf_image_resolution); }
+ else if (scan_keyword("pkresolution")) { do_variable_backend_int(c_pdf_pk_resolution); }
+ else if (scan_keyword("uniqueresname")) { do_variable_backend_int(c_pdf_unique_resname); }
+ else if (scan_keyword("majorversion")) { do_variable_backend_int(c_pdf_major_version); }
+ else if (scan_keyword("minorversion")) { do_variable_backend_int(c_pdf_minor_version); }
+ else if (scan_keyword("pagebox")) { do_variable_backend_int(c_pdf_pagebox); }
+ else if (scan_keyword("inclusionerrorlevel")) { do_variable_backend_int(c_pdf_inclusion_errorlevel); }
+ else if (scan_keyword("ignoreunknownimages")) { do_variable_backend_int(c_pdf_ignore_unknown_images); }
+ else if (scan_keyword("gamma")) { do_variable_backend_int(c_pdf_gamma); }
+ else if (scan_keyword("imageapplygamma")) { do_variable_backend_int(c_pdf_image_apply_gamma); }
+ else if (scan_keyword("imagegamma")) { do_variable_backend_int(c_pdf_image_gamma); }
+ else if (scan_keyword("imagehicolor")) { do_variable_backend_int(c_pdf_image_hicolor); }
+ else if (scan_keyword("imageaddfilename")) { do_variable_backend_int(c_pdf_image_addfilename); }
+ else if (scan_keyword("objcompresslevel")) { do_variable_backend_int(c_pdf_obj_compress_level); }
+ else if (scan_keyword("inclusioncopyfonts")) { do_variable_backend_int(c_pdf_inclusion_copy_font); }
+ else if (scan_keyword("gentounicode")) { do_variable_backend_int(c_pdf_gen_tounicode); }
+ else if (scan_keyword("pkfixeddpi")) { do_variable_backend_int(c_pdf_pk_fixed_dpi); }
+ else if (scan_keyword("suppressoptionalinfo")) { do_variable_backend_int(c_pdf_suppress_optional_info); }
+ else if (scan_keyword("omitcidset")) { do_variable_backend_int(c_pdf_omit_cidset); }
+ else if (scan_keyword("omitcharset")) { do_variable_backend_int(c_pdf_omit_charset); }
+ else if (scan_keyword("recompress")) { do_variable_backend_int(c_pdf_recompress); }
+
+ else if (scan_keyword("horigin")) { do_variable_backend_dimen(d_pdf_h_origin); }
+ else if (scan_keyword("vorigin")) { do_variable_backend_dimen(d_pdf_v_origin); }
+ else if (scan_keyword("threadmargin")) { do_variable_backend_dimen(d_pdf_thread_margin); }
+ else if (scan_keyword("destmargin")) { do_variable_backend_dimen(d_pdf_dest_margin); }
+ else if (scan_keyword("linkmargin")) { do_variable_backend_dimen(d_pdf_link_margin); }
+ else if (scan_keyword("xformmargin")) { do_variable_backend_dimen(d_pdf_xform_margin); }
+
+ else if (scan_keyword("pageattr")) { do_variable_backend_toks(t_pdf_page_attr); }
+ else if (scan_keyword("pageresources")) { do_variable_backend_toks(t_pdf_page_resources); }
+ else if (scan_keyword("pagesattr")) { do_variable_backend_toks(t_pdf_pages_attr); }
+ else if (scan_keyword("xformattr")) { do_variable_backend_toks(t_pdf_xform_attr); }
+ else if (scan_keyword("xformresources")) { do_variable_backend_toks(t_pdf_xform_resources); }
+ else if (scan_keyword("pkmode")) { do_variable_backend_toks(t_pdf_pk_mode); }
+ else if (scan_keyword("trailerid")) { do_variable_backend_toks(t_pdf_trailer_id); }
+
+ else
+ return 0;
+ return 1;
+}
+
+static int do_feedback_dvi(halfword c)
+{
+ return 0;
+}
+
+/*tex Codes not really needed but cleaner when testing */
+
+#define pdftex_version 140 /* these values will not change any more */
+#define pdftex_revision "0" /* these values will not change any more */
+
+static int do_feedback_pdf(halfword c)
+{
+ /*tex holds |selector| setting */
+ int old_setting;
+ /*tex |scanner_status| upon entry */
+ int save_scanner_status;
+ /*tex |def_ref| upon entry, important if inside `\.{\\message}' */
+ halfword save_def_ref;
+ halfword save_warning_index;
+ /*tex temp boolean */
+ boolean bool;
+ /*tex first temp string */
+ str_number s;
+ /*tex for use with |set_ff| */
+ int ff;
+ /*tex third temp string, will become non-nil if a string is already being built */
+ str_number u = 0;
+ /*tex color stack init str */
+ char *str;
+ if (scan_keyword("lastlink")) {
+ push_selector;
+ print_int(pdf_last_link);
+ pop_selector;
+ } else if (scan_keyword("retval")) {
+ push_selector;
+ print_int(pdf_retval);
+ pop_selector;
+ } else if (scan_keyword("lastobj")) {
+ push_selector;
+ print_int(pdf_last_obj);
+ pop_selector;
+ } else if (scan_keyword("lastannot")) {
+ push_selector;
+ print_int(pdf_last_annot);
+ pop_selector;
+ } else if (scan_keyword("xformname")) {
+ scan_int();
+ check_obj_type(static_pdf, obj_type_xform, cur_val);
+ push_selector;
+ print_int(obj_info(static_pdf, cur_val));
+ pop_selector;
+ } else if (scan_keyword("creationdate")) {
+ ins_list(string_to_toks(getcreationdate(static_pdf)));
+ /*tex No further action. */
+ return 2;
+ } else if (scan_keyword("fontname")) {
+ scan_font_ident();
+ if (cur_val == null_font)
+ normal_error("pdf backend", "invalid font identifier when asking 'fontname'");
+ pdf_check_vf(cur_val);
+ if (!font_used(cur_val))
+ pdf_init_font(static_pdf, cur_val);
+ push_selector;
+ set_ff(cur_val);
+ print_int(obj_info(static_pdf, pdf_font_num(ff)));
+ pop_selector;
+ } else if (scan_keyword("fontobjnum")) {
+ scan_font_ident();
+ if (cur_val == null_font)
+ normal_error("pdf backend", "invalid font identifier when asking 'objnum'");
+ pdf_check_vf(cur_val);
+ if (!font_used(cur_val))
+ pdf_init_font(static_pdf, cur_val);
+ push_selector;
+ set_ff(cur_val);
+ print_int(pdf_font_num(ff));
+ pop_selector;
+ } else if (scan_keyword("fontsize")) {
+ scan_font_ident();
+ if (cur_val == null_font)
+ normal_error("pdf backend", "invalid font identifier when asking 'fontsize'");
+ push_selector;
+ print_scaled(font_size(cur_val));
+ tprint("pt");
+ pop_selector;
+ } else if (scan_keyword("pageref")) {
+ scan_int();
+ if (cur_val <= 0)
+ normal_error("pdf backend", "invalid page number when asking 'pageref'");
+ push_selector;
+ print_int(pdf_get_obj(static_pdf, obj_type_page, cur_val, false));
+ pop_selector;
+ } else if (scan_keyword("colorstackinit")) {
+ bool = scan_keyword("page");
+ if (scan_keyword("direct"))
+ cur_val = direct_always;
+ else if (scan_keyword("page"))
+ cur_val = direct_page;
+ else if (scan_keyword("text"))
+ cur_val = direct_text;
+ else if (scan_keyword("raw"))
+ cur_val = direct_raw;
+ else if (scan_keyword("origin"))
+ cur_val = set_origin;
+ else
+ cur_val = set_origin;
+ save_scanner_status = scanner_status;
+ save_warning_index = warning_index;
+ save_def_ref = def_ref;
+ u = save_cur_string();
+ scan_toks(false, true);
+ s = tokens_to_string(def_ref);
+ delete_token_ref(def_ref);
+ def_ref = save_def_ref;
+ warning_index = save_warning_index;
+ scanner_status = save_scanner_status;
+ str = makecstring(s);
+ cur_val = newcolorstack(str, cur_val, bool);
+ free(str);
+ flush_str(s);
+ cur_val_level = int_val_level;
+ if (cur_val < 0) {
+ print_err("Too many color stacks");
+ help2("The number of color stacks is limited to 32768.",
+ "I'll use the default color stack 0 here.");
+ error();
+ cur_val = 0;
+ restore_cur_string(u);
+ }
+ push_selector;
+ print_int(cur_val);
+ pop_selector;
+ } else if (scan_keyword("version")) {
+ push_selector;
+ print_int(pdftex_version);
+ pop_selector;
+ } else if (scan_keyword("revision")) {
+ ins_list(string_to_toks(pdftex_revision));
+ return 2;
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+void conv_toks(void)
+{
+ /*tex holds |selector| setting */
+ int old_setting;
+ halfword p, q;
+ /*tex |scanner_status| upon entry */
+ int save_scanner_status;
+ /*tex |def_ref| upon entry, important if inside `\.{\\message}' */
+ halfword save_def_ref;
+ halfword save_warning_index;
+ /*tex temp boolean */
+ boolean bool;
+ /*tex first temp string */
+ str_number s;
+ /*tex lua chunk name */
+ int sn;
+ /*tex third temp string, will become non-nil if a string is already being built */
+ str_number u = 0;
+ /*tex desired type of conversion */
+ int c = cur_chr;
+ str_number str;
+ int i = 0;
+ /*tex Scan the argument for command |c|. */
+ switch (c) {
+ case number_code:
+ scan_int();
+ push_selector;
+ print_int(cur_val);
+ pop_selector;
+ break;
+ case lua_function_code:
+ scan_int();
+ if (cur_val <= 0) {
+ normal_error("luafunction", "invalid number");
+ } else {
+ u = save_cur_string();
+ luacstrings = 0;
+ luafunctioncall(cur_val);
+ restore_cur_string(u);
+ if (luacstrings > 0)
+ lua_string_start();
+ }
+ return;
+ break;
+ case lua_bytecode_code:
+ scan_int();
+ if (cur_val < 0 || cur_val > 65535) {
+ normal_error("luabytecode", "invalid number");
+ } else {
+ u = save_cur_string();
+ luacstrings = 0;
+ luabytecodecall(cur_val);
+ restore_cur_string(u);
+ if (luacstrings > 0)
+ lua_string_start();
+ }
+ return;
+ break;
+ case lua_code:
+ u = save_cur_string();
+ save_scanner_status = scanner_status;
+ save_def_ref = def_ref;
+ save_warning_index = warning_index;
+ sn = scan_lua_state();
+ scan_toks(false, true);
+ s = def_ref;
+ warning_index = save_warning_index;
+ def_ref = save_def_ref;
+ scanner_status = save_scanner_status;
+ luacstrings = 0;
+ luatokencall(s, sn);
+ delete_token_ref(s);
+ restore_cur_string(u);
+ if (luacstrings > 0)
+ lua_string_start();
+ /*tex No further action. */
+ return;
+ break;
+ case expanded_code:
+ save_scanner_status = scanner_status;
+ save_warning_index = warning_index;
+ save_def_ref = def_ref;
+ u = save_cur_string();
+ scan_toks(false, true);
+ warning_index = save_warning_index;
+ scanner_status = save_scanner_status;
+ ins_list(token_link(def_ref));
+ token_link(def_ref) = null;
+ free_avail(def_ref);
+ def_ref = save_def_ref;
+ restore_cur_string(u);
+ /*tex No further action. */
+ return;
+ break;
+ case immediate_assignment_code:
+ case immediate_assigned_code:
+ /*tex
+
+ This is on-the-road-to-bachotex brain-wave but it needs a bit
+ more testing. A first variant did more in sequence till a relax
+ of spacer was seen (like do_assignments). It permits for instance
+ setting counters in full expansion.
+
+ */
+ save_scanner_status = scanner_status;
+ save_warning_index = warning_index;
+ save_def_ref = def_ref;
+ u = save_cur_string();
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (c == immediate_assignment_code) {
+ /*tex one-step do_assignment */
+ if (cur_cmd > max_non_prefixed_command) {
+ set_box_allowed = false;
+ prefixed_command();
+ set_box_allowed = true;
+ }
+ /*tex done */
+ } else {
+ /*tex pseudo token list do_assignment */
+ if (cur_cmd == left_brace_cmd) {
+ while (1) {
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (cur_cmd == right_brace_cmd) {
+ break;
+ } else {
+ set_box_allowed = false;
+ prefixed_command();
+ set_box_allowed = true;
+ }
+ }
+ }
+ /*tex done */
+ }
+ warning_index = save_warning_index;
+ scanner_status = save_scanner_status;
+ def_ref = save_def_ref;
+ restore_cur_string(u);
+ return;
+ break;
+ case math_style_code:
+ push_selector;
+ print_math_style();
+ pop_selector;
+ break;
+ case string_code:
+ save_scanner_status = scanner_status;
+ scanner_status = normal;
+ get_token();
+ scanner_status = save_scanner_status;
+ push_selector;
+ if (cur_cs != 0)
+ sprint_cs(cur_cs);
+ else
+ print(cur_chr);
+ pop_selector;
+ break;
+ case cs_string_code:
+ save_scanner_status = scanner_status;
+ scanner_status = normal;
+ get_token();
+ scanner_status = save_scanner_status;
+ push_selector;
+ if (cur_cs != 0)
+ sprint_cs_name(cur_cs);
+ else
+ print(cur_chr);
+ pop_selector;
+ break;
+ case roman_numeral_code:
+ scan_int();
+ push_selector;
+ print_roman_int(cur_val);
+ pop_selector;
+ break;
+ case meaning_code:
+ save_scanner_status = scanner_status;
+ scanner_status = normal;
+ get_token();
+ scanner_status = save_scanner_status;
+ push_selector;
+ print_meaning();
+ pop_selector;
+ break;
+ case uchar_code:
+ scan_char_num();
+ push_selector;
+ print(cur_val);
+ pop_selector;
+ break;
+ case lua_escape_string_code:
+ {
+ lstring escstr;
+ int l = 0;
+ save_scanner_status = scanner_status;
+ save_def_ref = def_ref;
+ save_warning_index = warning_index;
+ scan_toks(false, true);
+ bool = in_lua_escape;
+ in_lua_escape = true;
+ escstr.s = (unsigned char *) tokenlist_to_cstring(def_ref, false, &l);
+ escstr.l = (unsigned) l;
+ in_lua_escape = bool;
+ delete_token_ref(def_ref);
+ def_ref = save_def_ref;
+ warning_index = save_warning_index;
+ scanner_status = save_scanner_status;
+ (void) lua_str_toks(escstr);
+ ins_list(token_link(temp_token_head));
+ free(escstr.s);
+ return;
+ }
+ /*tex no further action */
+ break;
+ case font_id_code:
+ scan_font_ident();
+ push_selector;
+ print_int(cur_val);
+ pop_selector;
+ break;
+ case font_name_code:
+ scan_font_ident();
+ push_selector;
+ append_string((unsigned char *) font_name(cur_val),(unsigned) strlen(font_name(cur_val)));
+ if (font_size(cur_val) != font_dsize(cur_val)) {
+ tprint(" at ");
+ print_scaled(font_size(cur_val));
+ tprint("pt");
+ }
+ pop_selector;
+ break;
+ case left_margin_kern_code:
+ scan_int();
+ if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node))
+ normal_error("marginkern", "a non-empty hbox expected");
+ push_selector;
+ p = list_ptr(box(cur_val));
+ while ((p != null) && (type(p) == glue_node)) {
+ p = vlink(p);
+ }
+ if ((p != null) && (type(p) == margin_kern_node) && (subtype(p) == left_side))
+ print_scaled(width(p));
+ else
+ print_char('0');
+ tprint("pt");
+ pop_selector;
+ break;
+ case right_margin_kern_code:
+ scan_int();
+ if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node))
+ normal_error("marginkern", "a non-empty hbox expected");
+ push_selector;
+ p = list_ptr(box(cur_val));
+ if (p != null) {
+ p = tail_of_list(p);
+ /*tex
+
+ There can be a leftskip, rightskip, penalty and yes, also a
+ disc node with a nesting node that points to glue spec ...
+ and we don't want to analyze that messy lot.
+
+ */
+ while ((p != null) && (type(p) == glue_node)) {
+ p = alink(p);
+ }
+ if ((p != null) && ! ((type(p) == margin_kern_node) && (subtype(p) == right_side))) {
+ if (type(p) == disc_node) {
+ q = alink(p);
+ if ((q != null) && ((type(q) == margin_kern_node) && (subtype(q) == right_side))) {
+ p = q;
+ } else {
+ /*tex
+
+ Officially we should look in the replace but
+ currently protrusion doesn't work anyway with
+ "foo\discretionary{}{}{bar-} " (no following
+ char) so we don't need it now.
+ */
+ }
+ }
+ }
+ }
+ if ((p != null) && (type(p) == margin_kern_node) && (subtype(p) == right_side))
+ print_scaled(width(p));
+ else
+ print_char('0');
+ tprint("pt");
+ pop_selector;
+ break;
+ case uniform_deviate_code:
+ scan_int();
+ push_selector;
+ print_int(unif_rand(cur_val));
+ pop_selector;
+ break;
+ case normal_deviate_code:
+ push_selector;
+ print_int(norm_rand());
+ pop_selector;
+ break;
+ case math_char_class_code:
+ {
+ mathcodeval mval;
+ scan_int();
+ mval = get_math_code(cur_val);
+ push_selector;
+ print_int(mval.class_value);
+ pop_selector;
+ }
+ break;
+ case math_char_fam_code:
+ {
+ mathcodeval mval;
+ scan_int();
+ mval = get_math_code(cur_val);
+ push_selector;
+ print_int(mval.family_value);
+ pop_selector;
+ }
+ break;
+ case math_char_slot_code:
+ {
+ mathcodeval mval;
+ scan_int();
+ mval = get_math_code(cur_val);
+ push_selector;
+ print_int(mval.character_value);
+ pop_selector;
+ }
+ break;
+ case insert_ht_code:
+ scan_register_num();
+ push_selector;
+ i = cur_val;
+ p = page_ins_head;
+ while (i >= subtype(vlink(p)))
+ p = vlink(p);
+ if (subtype(p) == i)
+ print_scaled(height(p));
+ else
+ print_char('0');
+ tprint("pt");
+ pop_selector;
+ break;
+ case job_name_code:
+ if (job_name == 0)
+ open_log_file();
+ push_selector;
+ print_job_name();
+ pop_selector;
+ break;
+ case format_name_code:
+ if (job_name == 0)
+ open_log_file();
+ push_selector;
+ print(format_name);
+ pop_selector;
+ break;
+ case luatex_banner_code:
+ push_selector;
+ tprint(luatex_banner);
+ pop_selector;
+ break;
+ case luatex_revision_code:
+ push_selector;
+ print(get_luatexrevision());
+ pop_selector;
+ break;
+ case etex_code:
+ push_selector;
+ tprint(eTeX_version_string);
+ pop_selector;
+ break;
+ case eTeX_revision_code:
+ push_selector;
+ tprint(eTeX_revision);
+ pop_selector;
+ break;
+ case font_identifier_code:
+ confusion("convert");
+ break;
+ default:
+ confusion("convert");
+ break;
+ }
+ str = make_string();
+ (void) str_toks(str_lstring(str));
+ flush_str(str);
+ ins_list(token_link(temp_token_head));
+}
+
+void do_feedback(void)
+{
+ int c = cur_chr;
+ str_number str;
+ int done = 1;
+ switch (c) {
+ case dvi_feedback_code:
+ if (get_o_mode() == OMODE_DVI) {
+ done = do_feedback_dvi(c);
+ } else {
+ tex_error("unexpected use of \\dvifeedback",null);
+ return ;
+ }
+ if (done==0) {
+ /* we recover */
+ normal_warning("dvi backend","unexpected use of \\dvifeedback");
+ return;
+ } else if (done==2) {
+ return;
+ }
+ break;
+ case pdf_feedback_code:
+ if (get_o_mode() == OMODE_PDF) {
+ done = do_feedback_pdf(c);
+ } else {
+ tex_error("unexpected use of \\pdffeedback",null);
+ return ;
+ }
+ if (done==0) {
+ /*tex We recover. */
+ normal_warning("pdf backend","unexpected use of \\pdffeedback");
+ return;
+ } else if (done==2) {
+ return;
+ }
+ break;
+ default:
+ confusion("feedback");
+ break;
+ }
+ str = make_string();
+ (void) str_toks(str_lstring(str));
+ flush_str(str);
+ ins_list(token_link(temp_token_head));
+}
+
+void do_variable(void)
+{
+ int c = cur_chr;
+ int done = 1;
+ switch (c) {
+ case dvi_variable_code:
+ done = do_variable_dvi(c);
+ if (done==0) {
+ /*tex We recover. */
+ normal_warning("dvi backend","unexpected use of \\dvivariable");
+ }
+ return;
+ break;
+ case pdf_variable_code:
+ done = do_variable_pdf(c);
+ if (done==0) {
+ /*tex We recover. */
+ normal_warning("pdf backend","unexpected use of \\pdfvariable");
+ }
+ return;
+ break;
+ default:
+ confusion("variable");
+ break;
+ }
+}
+
+/*tex This boolean is keeping track of the lua string escape state */
+
+boolean in_lua_escape;
+
+static int the_convert_string_dvi(halfword c, int i)
+{
+ return 0 ;
+}
+
+static int the_convert_string_pdf(halfword c, int i)
+{
+ int ff;
+ if (get_o_mode() != OMODE_PDF) {
+ return 0;
+ } else if (scan_keyword("lastlink")) {
+ print_int(pdf_last_link);
+ } else if (scan_keyword("retval")) {
+ print_int(pdf_retval);
+ } else if (scan_keyword("lastobj")) {
+ print_int(pdf_last_obj);
+ } else if (scan_keyword("lastannot")) {
+ print_int(pdf_last_annot);
+ } else if (scan_keyword("xformname")) {
+ print_int(obj_info(static_pdf, i));
+ } else if (scan_keyword("creationdate")) {
+ return 0;
+ } else if (scan_keyword("fontname")) {
+ set_ff(i);
+ print_int(obj_info(static_pdf, pdf_font_num(ff)));
+ } else if (scan_keyword("fontobjnum")) {
+ set_ff(i);
+ print_int(pdf_font_num(ff));
+ } else if (scan_keyword("fontsize")) {
+ print_scaled(font_size(i));
+ tprint("pt");
+ } else if (scan_keyword("pageref")) {
+ print_int(pdf_get_obj(static_pdf, obj_type_page, i, false));
+ } else if (scan_keyword("colorstackinit")) {
+ return 0;
+ } else {
+ return 0;
+ }
+ return 1;
+}
+
+str_number the_convert_string(halfword c, int i)
+{
+ int old_setting;
+ str_number ret = 0;
+ boolean done = true ;
+ old_setting = selector;
+ selector = new_string;
+ switch (c) {
+ case number_code:
+ print_int(i);
+ break;
+ /* case lua_function_code: */
+ /* case lua_code: */
+ /* case expanded_code: */
+ case math_style_code:
+ print_math_style();
+ break;
+ /* case string_code: */
+ /* case cs_string_code: */
+ case roman_numeral_code:
+ print_roman_int(i);
+ break;
+ /* case meaning_code: */
+ case uchar_code:
+ print(i);
+ break;
+ /* lua_escape_string_code: */
+ case font_id_code:
+ print_int(i);
+ break;
+ case font_name_code:
+ append_string((unsigned char *) font_name(i),(unsigned) strlen(font_name(i)));
+ if (font_size(i) != font_dsize(i)) {
+ tprint(" at ");
+ print_scaled(font_size(i));
+ tprint("pt");
+ }
+ break;
+ /* left_margin_kern_code: */
+ /* right_margin_kern_code: */
+ case uniform_deviate_code:
+ print_int(unif_rand(i));
+ break;
+ case normal_deviate_code:
+ print_int(norm_rand());
+ break;
+ /* math_char_class_code: */
+ /* math_char_fam_code: */
+ /* math_char_slot_code: */
+ /* insert_ht_code: */
+ case job_name_code:
+ print_job_name();
+ break;
+ case format_name_code:
+ print(format_name);
+ break;
+ case luatex_banner_code:
+ tprint(luatex_banner);
+ break;
+ case luatex_revision_code:
+ print(get_luatexrevision());
+ break;
+ case etex_code:
+ tprint(eTeX_version_string);
+ break;
+ case eTeX_revision_code:
+ tprint(eTeX_revision);
+ break;
+ case font_identifier_code:
+ print_font_identifier(i);
+ break;
+ /*tex Backend: this might become obsolete! */
+ case dvi_feedback_code:
+ done = the_convert_string_dvi(c,i);
+ break;
+ case pdf_feedback_code:
+ done = the_convert_string_pdf(c,i);
+ break;
+ /*tex done */
+ default:
+ done = false;
+ break;
+ }
+ if (done) {
+ ret = make_string();
+ }
+ selector = old_setting;
+ return ret;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+/*tex used for \.{\\read} */
+
+FILE *read_file[16];
+
+/*tex state of |read_file[n]| */
+
+int read_open[17];
+
+void initialize_read(void)
+{
+ int k;
+ for (k = 0; k <= 16; k++)
+ read_open[k] = closed;
+}
+
+/*tex
+
+ 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.
+
+*/
+
+void read_toks(int n, halfword r, halfword j)
+{
+ /*tex tail of the token list */
+ halfword p;
+ /*tex new node being added to the token list via |store_new_token| */
+ halfword q;
+ /*tex saved value of |align_state| */
+ int s;
+ /*tex stream number */
+ int m;
+ scanner_status = defining;
+ warning_index = r;
+ p = get_avail();
+ def_ref = p;
+ set_token_ref_count(def_ref, 0);
+ /*tex the reference count */
+ p = def_ref;
+ store_new_token(end_match_token);
+ if ((n < 0) || (n > 15))
+ m = 16;
+ else
+ m = n;
+ s = align_state;
+ /*tex disable tab marks, etc. */
+ align_state = 1000000;
+ do {
+ /*tex Input and store tokens from the next line of the file. */
+ begin_file_reading();
+ iname = m + 1;
+ if (read_open[m] == closed) {
+ /*tex
+
+ Input for \.{\\read} from the terminal. 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.
+
+ */
+ 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)");
+ }
+
+ } else if (read_open[m] == just_open) {
+ /*tex
+
+ Input the first line of |read_file[m]|. The first line of a file
+ must be treated specially, since |lua_input_ln| must be told not
+ to start with |get|.
+
+ */
+ if (lua_input_ln(read_file[m], (m + 1), false)) {
+ read_open[m] = normal;
+ } else {
+ lua_a_close_in(read_file[m], (m + 1));
+ read_open[m] = closed;
+ }
+
+ } else {
+ /*tex
+
+ Input the next line of |read_file[m]|. An empty line is appended
+ at the end of a |read_file|.
+
+ */
+ if (!lua_input_ln(read_file[m], (m + 1), true)) {
+ lua_a_close_in(read_file[m], (m + 1));
+ read_open[m] = closed;
+ if (align_state != 1000000) {
+ runaway();
+ print_err("File ended within \\read");
+ help1("This \\read has unbalanced braces.");
+ align_state = 1000000;
+ error();
+ }
+ }
+
+ }
+ ilimit = last;
+ if (end_line_char_inactive)
+ decr(ilimit);
+ else
+ buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
+ first = ilimit + 1;
+ iloc = istart;
+ istate = new_line;
+ /*tex Handle \.{\\readline} and |goto done|. */
+ if (j == 1) {
+ while (iloc <= ilimit) {
+ /*tex Current line not yet finished. */
+ do_buffer_to_unichar(cur_chr, iloc);
+ if (cur_chr == ' ')
+ cur_tok = space_token;
+ else
+ cur_tok = cur_chr + other_token;
+ store_new_token(cur_tok);
+ }
+ } else {
+ while (1) {
+ get_token();
+ if (cur_tok == 0) {
+ /*tex |cur_cmd=cur_chr=0| will occur at the end of the line. */
+ break;
+ }
+ if (align_state < 1000000) {
+ /*tex Unmatched right brace aborts the line. */
+ do {
+ get_token();
+ } while (cur_tok != 0);
+ align_state = 1000000;
+ break;
+ }
+ store_new_token(cur_tok);
+ }
+ }
+ end_file_reading();
+
+ } while (align_state != 1000000);
+ cur_val = def_ref;
+ scanner_status = normal;
+ align_state = s;
+}
+
+/*tex Return a string from tokens list: */
+
+str_number tokens_to_string(halfword p)
+{
+ int old_setting;
+ if (selector == new_string)
+ normal_error("tokens","tokens_to_string() called while selector = new_string");
+ old_setting = selector;
+ selector = new_string;
+ show_token_list(token_link(p), null, -1);
+ selector = old_setting;
+ return make_string();
+}
+
+/*tex
+
+ Values like 512 and 128 also work ok. There is not much to gain in
+ optimization here.
+
+*/
+
+#define alloci_default 1024
+#define alloci_step 256
+
+#define make_room(a) \
+ if ((unsigned)i+a+1>alloci) { \
+ ret = xrealloc(ret,(alloci+alloci_step)); \
+ alloci = alloci + alloci_step; \
+ }
+
+#define append_i_byte(a) ret[i++] = (char)(a)
+
+#define Print_char(a) make_room(1); append_i_byte(a)
+
+#define Print_uchar(s) { \
+ make_room(4); \
+ if (s<=0x7F) { \
+ append_i_byte(s); \
+ } else if (s<=0x7FF) { \
+ append_i_byte(0xC0 + (s / 0x40)); \
+ append_i_byte(0x80 + (s % 0x40)); \
+ } else if (s<=0xFFFF) { \
+ append_i_byte(0xE0 + (s / 0x1000)); \
+ append_i_byte(0x80 + ((s % 0x1000) / 0x40)); \
+ append_i_byte(0x80 + ((s % 0x1000) % 0x40)); \
+ } else if (s>=0x110000) { \
+ append_i_byte(s-0x11000); \
+ } else { \
+ append_i_byte(0xF0 + (s / 0x40000)); \
+ append_i_byte(0x80 + ((s % 0x40000) / 0x1000)); \
+ append_i_byte(0x80 + (((s % 0x40000) % 0x1000) / 0x40)); \
+ append_i_byte(0x80 + (((s % 0x40000) % 0x1000) % 0x40)); \
+ } }
+
+#define Print_esc(b) { \
+ const char *v = b; \
+ if (e>0 && e<STRING_OFFSET) { \
+ Print_uchar (e); \
+ } \
+ make_room(strlen(v)); \
+ while (*v) { append_i_byte(*v); v++; } \
+ }
+
+#define Print_str(b) { \
+ const char *v = b; \
+ make_room(strlen(v)); \
+ while (*v) { append_i_byte(*v); v++; } \
+ }
+
+#define is_cat_letter(a) \
+ (get_char_cat_code(pool_to_unichar(str_string((a)))) == 11)
+
+/*tex
+
+ The actual token conversion in this function is now functionally equivalent
+ to |show_token_list|, except that it always prints the whole token list.
+ TODO: check whether this causes problems in the lua library.
+
+*/
+
+char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz)
+{
+ register int p, c, m;
+ int q;
+ int infop;
+ char *s, *sh;
+ int e = 0;
+ char *ret;
+ int match_chr = '#';
+ int n = '0';
+ unsigned alloci = alloci_default;
+ int i = 0;
+ p = pp;
+ if (p == null) {
+ if (siz != NULL)
+ *siz = 0;
+ return NULL;
+ }
+ ret = xmalloc(alloci);
+ /*tex Skip refcount. */
+ p = token_link(p);
+ if (p != null) {
+ e = escape_char_par;
+ }
+ while (p != null) {
+ if (p < (int) fix_mem_min || p > (int) fix_mem_end) {
+ Print_esc("CLOBBERED.");
+ break;
+ }
+ infop = token_info(p);
+ if (infop >= cs_token_flag) {
+ if (!(inhibit_par && infop == par_token)) {
+ q = infop - cs_token_flag;
+ if (q < hash_base) {
+ if (q == null_cs) {
+ Print_esc("csname");
+ Print_esc("endcsname");
+ } else {
+ Print_esc("IMPOSSIBLE.");
+ }
+ } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) {
+ Print_esc("IMPOSSIBLE.");
+ } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) {
+ Print_esc("NONEXISTENT.");
+ } else {
+ str_number txt = cs_text(q);
+ sh = makecstring(txt);
+ s = sh;
+ if (is_active_cs(txt)) {
+ s = s + 3;
+ while (*s) {
+ Print_char(*s);
+ s++;
+ }
+ } else {
+ if (e>=0 && e<0x110000) Print_uchar(e);
+ while (*s) {
+ Print_char(*s);
+ s++;
+ }
+ if ((!single_letter(txt)) || is_cat_letter(txt)) {
+ Print_char(' ');
+ }
+ }
+ free(sh);
+ }
+ }
+ } else {
+ if (infop < 0) {
+ Print_esc("BAD");
+ } else {
+ m = token_cmd(infop);
+ c = token_chr(infop);
+ switch (m) {
+ case left_brace_cmd:
+ case right_brace_cmd:
+ case math_shift_cmd:
+ case tab_mark_cmd:
+ case sup_mark_cmd:
+ case sub_mark_cmd:
+ case spacer_cmd:
+ case letter_cmd:
+ case other_char_cmd:
+ Print_uchar(c);
+ break;
+ case mac_param_cmd:
+ if (!in_lua_escape && (is_in_csname==0))
+ Print_uchar(c);
+ Print_uchar(c);
+ break;
+ case out_param_cmd:
+ Print_uchar(match_chr);
+ if (c <= 9) {
+ Print_char(c + '0');
+ } else {
+ Print_char('!');
+ goto EXIT;
+ }
+ break;
+ case match_cmd:
+ match_chr = c;
+ Print_uchar(c);
+ n++;
+ Print_char(n);
+ if (n > '9')
+ goto EXIT;
+ break;
+ case end_match_cmd:
+ if (c == 0) {
+ Print_char('-');
+ Print_char('>');
+ }
+ break;
+ default:
+ not_so_bad(Print_esc);
+ break;
+ }
+ }
+ }
+ p = token_link(p);
+ }
+ EXIT:
+ ret[i] = '\0';
+ if (siz != NULL)
+ *siz = i;
+ return ret;
+}
+
+char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz)
+{
+ register int p, c, m;
+ int q;
+ int infop;
+ char *s, *sh;
+ int e = 0;
+ char *ret;
+ int match_chr = '#';
+ int n = '0';
+ unsigned alloci = alloci_default;
+ int i = 0;
+ int skipping = 1;
+ p = pp;
+ if (p == null) {
+ if (siz != NULL)
+ *siz = 0;
+ return NULL;
+ }
+ ret = xmalloc(alloci);
+ /*tex Skip refcount. */
+ p = token_link(p);
+ if (p != null) {
+ e = escape_char_par;
+ }
+ while (p != null) {
+ if (p < (int) fix_mem_min || p > (int) fix_mem_end) {
+ /*tex Nothing done. */
+ break;
+ }
+ infop = token_info(p);
+ if (infop >= cs_token_flag) {
+ if (!(inhibit_par && infop == par_token)) {
+ q = infop - cs_token_flag;
+ if (q < hash_base) {
+ /*tex Nothing done. */
+ } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) {
+ /*tex Nothing done. */
+ } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) {
+ /*tex Nothing done. */
+ } else if (!skipping) {
+ str_number txt = cs_text(q);
+ sh = makecstring(txt);
+ s = sh;
+ if (is_active_cs(txt)) {
+ s = s + 3;
+ while (*s) {
+ Print_char(*s);
+ s++;
+ }
+ } else {
+ if (e>=0 && e<0x110000) Print_uchar(e);
+ while (*s) {
+ Print_char(*s);
+ s++;
+ }
+ if ((!single_letter(txt)) || is_cat_letter(txt)) {
+ Print_char(' ');
+ }
+ }
+ free(sh);
+ }
+ }
+ } else {
+ if (infop < 0) {
+ /*tex Nothing done. */
+ } else {
+ m = token_cmd(infop);
+ c = token_chr(infop);
+ switch (m) {
+ case left_brace_cmd:
+ case right_brace_cmd:
+ case math_shift_cmd:
+ case tab_mark_cmd:
+ case sup_mark_cmd:
+ case sub_mark_cmd:
+ case spacer_cmd:
+ case letter_cmd:
+ case other_char_cmd:
+ if (!skipping) {
+ Print_uchar(c);
+ }
+ break;
+ case mac_param_cmd:
+ if (!skipping) {
+ if (!in_lua_escape && (is_in_csname==0))
+ Print_uchar(c);
+ Print_uchar(c);
+ }
+ break;
+ case out_param_cmd:
+ if (!skipping) {
+ Print_uchar(match_chr);
+ }
+ if (c <= 9) {
+ if (!skipping) {
+ Print_char(c + '0');
+ }
+ } else {
+ /*tex Nothing done. */
+ goto EXIT;
+ }
+ break;
+ case match_cmd:
+ match_chr = c;
+ if (!skipping) {
+ Print_uchar(c);
+ }
+ n++;
+ if (!skipping) {
+ Print_char(n);
+ }
+ if (n > '9')
+ goto EXIT;
+ break;
+ case end_match_cmd:
+ if (c == 0) {
+ if (!skipping) {
+ Print_char('-');
+ Print_char('>');
+ }
+ i = 0;
+ skipping = 0 ;
+ }
+ break;
+ default:
+ /*tex Nothing done. */
+ break;
+ }
+ }
+ }
+ p = token_link(p);
+ }
+ EXIT:
+ ret[i] = '\0';
+ if (siz != NULL)
+ *siz = i;
+ return ret;
+}
+
+lstring *tokenlist_to_lstring(int pp, int inhibit_par)
+{
+ int siz;
+ lstring *ret = xmalloc(sizeof(lstring));
+ ret->s = (unsigned char *) tokenlist_to_cstring(pp, inhibit_par, &siz);
+ ret->l = (size_t) siz;
+ return ret;
+}
+
+void free_lstring(lstring * ls)
+{
+ if (ls == NULL)
+ return;
+ if (ls->s != NULL)
+ free(ls->s);
+ free(ls);
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.h b/Build/source/texk/web2c/luatexdir/tex/textoken.h
new file mode 100644
index 00000000000..248a12168cf
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/textoken.h
@@ -0,0 +1,189 @@
+/* textoken.h
+
+ Copyright 2006-2008 Taco Hoekwater <taco@luatex.org>
+
+ This file is part of LuaTeX.
+
+ LuaTeX is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
+
+
+#ifndef TEXTOKEN_H
+# define TEXTOKEN_H
+
+# define token_list 0
+# define null 0
+# define cs_token_flag 0x1FFFFFFF
+
+# define left_brace_token 0x0200000 /* $2^{21}\cdot|left_brace|$ */
+# define right_brace_token 0x0400000 /* $2^{21}\cdot|right_brace|$ */
+# define left_brace_limit 0x0400000 /* $2^{21}\cdot(|left_brace|+1)$ */
+# define right_brace_limit 0x0600000 /* $2^{21}\cdot(|right_brace|+1)$ */
+# define math_shift_token 0x0600000 /* $2^{21}\cdot|math_shift|$ */
+# define tab_token 0x0800000 /* $2^{21}\cdot|tab_mark|$ */
+# define out_param_token 0x0A00000 /* $2^{21}\cdot|out_param|$ */
+# define space_token 0x1400020 /* $2^{21}\cdot|spacer|+|" "|$ */
+# define letter_token 0x1600000 /* $2^{21}\cdot|letter|$ */
+# define other_token 0x1800000 /* $2^{21}\cdot|other_char|$ */
+# define match_token 0x1A00000 /* $2^{21}\cdot|match|$ */
+# define end_match_token 0x1C00000 /* $2^{21}\cdot|end_match|$ */
+# define protected_token 0x1C00001 /* $2^{21}\cdot|end_match|+1$ */
+
+# include "tex/stringpool.h"
+
+typedef struct smemory_word_ {
+# ifdef WORDS_BIGENDIAN
+ halfword hhrh;
+ halfword hhlh;
+# else
+ halfword hhlh;
+ halfword hhrh;
+# endif
+} smemory_word;
+
+# define fix_mem_init 10000
+
+extern smemory_word *fixmem;
+extern unsigned fix_mem_min;
+extern unsigned fix_mem_max;
+
+extern halfword garbage; /* head of a junk list, write only */
+extern halfword temp_token_head; /* head of a temporary list of some kind */
+extern halfword hold_token_head; /* head of a temporary list of another kind */
+extern halfword omit_template; /* a constant token list */
+extern halfword null_list; /* permanently empty list */
+extern halfword backup_head; /* head of token list built by |scan_keyword| */
+
+extern void initialize_tokens(void);
+
+extern int dyn_used;
+
+# define token_info(a) fixmem[(a)].hhlh
+# define token_link(a) fixmem[(a)].hhrh
+# define set_token_info(a,b) fixmem[(a)].hhlh=(b)
+# define set_token_link(a,b) fixmem[(a)].hhrh=(b)
+
+extern halfword avail; /* head of the list of available one-word nodes */
+extern unsigned fix_mem_end; /* the last one-word node used in |mem| */
+
+extern halfword get_avail(void);
+
+/* 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.
+*/
+
+# define free_avail(A) do { /* single-word node liberation */ \
+ token_link(A)=avail; avail=(A); decr(dyn_used); \
+ } while (0)
+
+/*
+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|.
+*/
+
+# define fast_get_avail(A) do { \
+ (A)=avail; /* avoid |get_avail| if possible, to save time */ \
+ if ((A)==null) { (A)=get_avail(); } \
+ else { avail=token_link((A)); token_link((A))=null; incr(dyn_used); } \
+ } while (0)
+
+extern void print_meaning(void);
+
+extern void flush_list(halfword p);
+extern void show_token_list(int p, int q, int l);
+extern void token_show(halfword p);
+
+# define token_ref_count(a) token_info((a)) /* reference count preceding a token list */
+# define set_token_ref_count(a,b) token_info((a))=b
+# define add_token_ref(a) token_ref_count(a)++ /* new reference to a token list */
+
+# define store_new_token(a) do { \
+ q=get_avail(); token_link(p)=q; token_info(q)=(a); p=q; \
+ } while (0)
+
+# define fast_store_new_token(a) do { \
+ fast_get_avail(q); token_link(p)=q; token_info(q)=(a); p=q; \
+ } while (0)
+
+extern void delete_token_ref(halfword p);
+
+extern void make_token_table(lua_State * L, int cmd, int chr, int cs);
+
+# define NO_CAT_TABLE -2
+# define DEFAULT_CAT_TABLE -1
+
+extern void get_next(void);
+extern void check_outer_validity(void);
+extern boolean scan_keyword(const char *);
+extern boolean scan_keyword_case_sensitive(const char *);
+extern halfword active_to_cs(int, int);
+extern void get_token_lua(void);
+halfword string_to_toks(const char *);
+extern int get_char_cat_code(int);
+
+/*
+|no_expand_flag| is a special character value that is inserted by
+|get_next| if it wants to suppress expansion.
+*/
+
+# define no_expand_flag special_char
+
+extern halfword par_loc;
+extern halfword par_token;
+extern boolean force_eof;
+extern int luacstrings;
+
+extern void firm_up_the_line(void);
+extern void get_token(void);
+
+extern halfword str_toks(lstring b);
+extern halfword str_scan_toks(int c, lstring b);
+extern void ins_the_toks(void);
+extern void combine_the_toks(void);
+
+extern int scan_lua_state(void);
+
+extern void conv_toks(void);
+extern str_number the_convert_string(halfword c, int i);
+extern void do_variable(void);
+extern void do_feedback(void);
+extern void do_mathoption(void);
+
+extern halfword lua_str_toks(lstring b);
+extern boolean in_lua_escape;
+
+# define closed 2 /* not open, or at end of file */
+# define just_open 1 /* newly opened, first line not yet read */
+
+extern FILE *read_file[16]; /* used for \.{\\read} */
+extern int read_open[17]; /* state of |read_file[n]| */
+
+extern void initialize_read(void);
+
+extern void read_toks(int n, halfword r, halfword j);
+
+extern str_number tokens_to_string(halfword p); /* return a string from tokens list */
+
+extern char *tokenlist_to_xstring(int p, int inhibit_par, int *siz);
+extern char *tokenlist_to_cstring(int p, int inhibit_par, int *siz);
+extern lstring *tokenlist_to_lstring(int pp, int inhibit_par);
+extern void free_lstring(lstring * ls);
+
+# define token_cmd(A) ((A) >> STRING_OFFSET_BITS)
+# define token_chr(A) ((A) & (STRING_OFFSET - 1))
+# define token_val(A,B) (((A)<<STRING_OFFSET_BITS)+(B))
+
+extern void l_set_token_data(void) ;
+
+#endif