summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/tex
diff options
context:
space:
mode:
authorLuigi Scarso <luigi.scarso@gmail.com>2018-09-05 21:57:30 +0000
committerLuigi Scarso <luigi.scarso@gmail.com>2018-09-05 21:57:30 +0000
commitecd1605a23ef54d2a4a69e1a361172d350e03b3b (patch)
tree8ea1d9745c24dbc5e755f13d801fa01e88944650 /Build/source/texk/web2c/luatexdir/tex
parent04563248956e7059aad4fc4f6bf7da18562f7b8c (diff)
luatex: correct automake file for pplib; replaced cweb files with c files.
git-svn-id: svn://tug.org/texlive/trunk@48594 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex')
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/align.c1297
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/align.w1144
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/arithmetic.c815
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/arithmetic.w735
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/backend.c123
-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.w1013
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/commands.c (renamed from Build/source/texk/web2c/luatexdir/tex/commands.w)194
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/conditional.c (renamed from Build/source/texk/web2c/luatexdir/tex/conditional.w)348
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/directions.c (renamed from Build/source/texk/web2c/luatexdir/tex/directions.w)113
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/dumpdata.c (renamed from Build/source/texk/web2c/luatexdir/tex/dumpdata.w)403
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/equivalents.c (renamed from Build/source/texk/web2c/luatexdir/tex/equivalents.w)766
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/errors.c1032
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/errors.w972
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/expand.c967
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/expand.w829
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/extensions.c1329
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/extensions.w1211
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/filename.c (renamed from Build/source/texk/web2c/luatexdir/tex/filename.w)230
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/inputstack.c (renamed from Build/source/texk/web2c/luatexdir/tex/inputstack.w)640
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/linebreak.c2520
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/linebreak.w2164
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mainbody.c769
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mainbody.w708
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/maincontrol.c (renamed from Build/source/texk/web2c/luatexdir/tex/maincontrol.w)2077
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mathcodes.c (renamed from Build/source/texk/web2c/luatexdir/tex/mathcodes.w)135
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/memoryword.c29
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/memoryword.w55
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/mlist.c (renamed from Build/source/texk/web2c/luatexdir/tex/mlist.w)2619
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/nesting.c429
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/nesting.w436
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/packaging.c (renamed from Build/source/texk/web2c/luatexdir/tex/packaging.w)1382
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/postlinebreak.c (renamed from Build/source/texk/web2c/luatexdir/tex/postlinebreak.w)466
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/primitive.c785
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/primitive.w664
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/printing.c (renamed from Build/source/texk/web2c/luatexdir/tex/printing.w)672
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/scanning.c2743
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/scanning.w2621
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/stringpool.c (renamed from Build/source/texk/web2c/luatexdir/tex/stringpool.w)248
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texdeffont.c (renamed from Build/source/texk/web2c/luatexdir/tex/texdeffont.w)151
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texfileio.c1509
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texfileio.w1389
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texmath.c (renamed from Build/source/texk/web2c/luatexdir/tex/texmath.w)1589
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texnodes.c4770
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texnodes.w3910
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/textcodes.c (renamed from Build/source/texk/web2c/luatexdir/tex/textcodes.w)112
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/textoken.c (renamed from Build/source/texk/web2c/luatexdir/tex/textoken.w)2151
48 files changed, 28433 insertions, 24049 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..b2384cc6290
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/align.c
@@ -0,0 +1,1297 @@
+/*
+
+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));
+ assert (type(r) == unset_node);
+ 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.w b/Build/source/texk/web2c/luatexdir/tex/align.w
deleted file mode 100644
index dd0ed10338e..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/align.w
+++ /dev/null
@@ -1,1144 +0,0 @@
-% align.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/>.
-
-\def\<#1>{$#1$}
-
-@ @c
-
-
-#include "ptexlib.h"
-
-@ @c
-void fin_align(void);
-void init_row(void);
-void init_col(void);
-
-#define noDEBUG
-
-@ It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because
-they cut across so many of the control structures of \TeX.
-
-Therefore the present page is probably not the best place for a beginner to
-start reading this program; it is better to master everything else first.
-
-Let us focus our thoughts on an example of what the input might be, in order
-to get some idea about how the alignment miracle happens. The example doesn't
-do anything useful, but it is sufficiently general to indicate all of the
-special cases that must be dealt with; please do not be disturbed by its
-apparent complexity and meaninglessness.
-$$\vbox{\halign{\.{#}\hfil\cr
-{}\\tabskip 2pt plus 3pt\cr
-{}\\halign to 300pt\{u1\#v1\&\cr
-\hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr
-\hskip 50pt u3\#v3\\cr\cr
-\hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr
-\hskip 25pt \\noalign\{\\vskip 3pt\}\cr
-\hskip 25pt b1\\span b2\\cr\cr
-\hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$
-Here's what happens:
-
-\yskip
-(0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine
-places the 300pt dimension onto the |save_stack|, and an |align_group|
-code is placed above it. This will make it possible to complete the alignment
-when the matching `\.\}' is found.
-
-(1) The preamble is scanned next. Macros in the preamble are not expanded,
-@^preamble@>
-except as part of a tabskip specification. For example, if \.{u2} had been
-a macro in the preamble above, it would have been expanded, since \TeX\
-must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list''
-is constructed based on the user's preamble; in our case it contains the
-following seven items:
-$$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr
-{}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr
-{}\\alignrecord, width $-\infty$&preamble info for column 1\cr
-{}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr
-{}\\alignrecord, width $-\infty$&preamble info for column 2\cr
-{}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr
-{}\\alignrecord, width $-\infty$&preamble info for column 3\cr
-{}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$
-These ``alignrecord'' entries have the same size as an |unset_node|,
-since they will later be converted into such nodes. 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.
-
-(2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble.
-It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to
-be read again, and the template `\.{u1}' is fed to the scanner. Just
-before reading `\.{u1}', \TeX\ goes into restricted horizontal mode.
-Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the
-{\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv|
-token, indicating the end of a column. At this point an |unset_node| is
-created, containing the contents of the current hlist (i.e., `\.{u1a1v1}').
-The natural width of this unset node replaces the |width| field of the
-alignrecord for column~1; in general, the alignrecords will record the
-maximum natural width that has occurred so far in a given column.
-
-(3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2
-are now bypassed. Again \TeX\ goes into restricted horizontal mode and
-makes an |unset_node| from the resulting hlist; but this time the
-hlist contains simply `\.{a2}'. The natural width of the new unset box
-is remembered in the |width| field of the alignrecord for column~2.
-
-(4) A third |unset_node| is created for column 3, using essentially the
-mechanism that worked for column~1; this unset box contains `\.{u3\\vrule
-v3}'. The vertical rule in this case has running dimensions that will later
-extend to the height and depth of the whole first row, since each |unset_node|
-in a row will eventually inherit the height and depth of its enclosing box.
-
-(5) The first row has now ended; it is made into a single unset box
-comprising the following seven items:
-$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
-{}\\glue 2pt plus 3pt\cr
-{}\\unsetbox for 1 column: u1a1v1\cr
-{}\\glue 2pt plus 3pt\cr
-{}\\unsetbox for 1 column: a2\cr
-{}\\glue 1pt plus 1fil\cr
-{}\\unsetbox for 1 column: u3\\vrule v3\cr
-{}\\glue 1pt plus 1fil\cr}}$$
-The width of this unset row is unimportant, but it has the correct height
-and depth, so the correct baselineskip glue will be computed as the row
-is inserted into a vertical list.
-
-(6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends
-additional material (in this case \.{\\vskip 3pt}) to the vertical list.
-While processing this material, \TeX\ will be in internal vertical
-mode, and |no_align_group| will be on |save_stack|.
-
-(7) The next row produces an unset box that looks like this:
-$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
-{}\\glue 2pt plus 3pt\cr
-{}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr
-{}\\glue 1pt plus 1fil\cr
-{}\\unsetbox for 1 column: {\rm(empty)}\cr
-{}\\glue 1pt plus 1fil\cr}}$$
-The natural width of the unset box that spans columns 1~and~2 is stored
-in a ``span node,'' which we will explain later; the |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|.
-
-(8) The final row produces the unset box
-$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
-{}\\glue 2pt plus 3pt\cr
-{}\\unsetbox for 1 column: {\rm(empty)}\cr
-{}\\glue 2pt plus 3pt\cr
-{}\\unsetbox for 2 columns: u2c2v2\cr
-{}\\glue 1pt plus 1fil\cr}}$$
-A new span node is attached to the alignrecord for column 2.
-
-(9) The last step is to compute the true column widths and to change all the
-unset boxes to hboxes, appending the whole works to the vertical list that
-encloses the \.{\\halign}. The rules for deciding on the final widths of
-each unset column box will be explained below.
-
-\yskip\noindent
-Note that as \.{\\halign} is being processed, we fearlessly give up control
-to the rest of \TeX. At critical junctures, an alignment routine is
-called upon to step in and do some little action, but most of the time
-these routines just lurk in the background. It's something like
-post-hypnotic suggestion.
-
-@ We have mentioned that alignrecords contain no |height| or |depth| fields.
-Their |glue_sign| and |glue_order| are pre-empted as well, since it
-is necessary to store information about what to do when a template ends.
-This information is called the |extra_info| field.
-
-@c
-/* could be in texnodes.h, but documented here*/
-
-#define u_part(A) vlink((A)+depth_offset) /* pointer to \<u_j> token list */
-#define v_part(A) vinfo((A)+depth_offset) /* pointer to \<v_j> token list */
-#define span_ptr(A) vinfo((A)+1) /* column spanning list */
-#define extra_info(A) vinfo((A)+list_offset) /* info to remember during template */
-
-@ Alignments can occur within alignments, so a small stack is used to access
-the alignrecord information. At each level we have a |preamble| pointer,
-indicating the beginning of the preamble list; a |cur_align| pointer,
-indicating the current position in the preamble list; a |cur_span| pointer,
-indicating the value of |cur_align| at the beginning of a sequence of
-spanned columns; a |cur_loop| pointer, indicating the tabskip glue before
-an alignrecord that should be copied next if the current list is extended;
-and the |align_state| variable, which indicates the nesting of braces so
-that \.{\\cr} and \.{\\span} and tab marks are properly intercepted.
-There also are pointers |cur_head| and |cur_tail| to the head and tail
-of a list of adjustments being moved out from horizontal mode to
-vertical~mode, 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.
-
-@c
-/* could be in texnodes.h but documented here*/
-
-#define preamble vlink(align_head) /* the current preamble list */
-
-pointer cur_align = null; /* current position in preamble list */
-pointer cur_span = null; /* start of currently spanned columns in preamble list */
-pointer cur_loop = null; /* place to copy when extending a periodic preamble */
-pointer align_ptr = null; /* most recently pushed-down alignment stack node */
-pointer cur_head = null, cur_tail = null; /* adjustment list pointers */
-pointer cur_pre_head = null, cur_pre_tail = null; /* pre-adjustment list pointers */
-
-/* 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.
-
-@c
-static void push_alignment(void)
-{
- pointer p; /* the new alignment stack node */
- 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)
-{
- pointer p; /* the top alignment stack node */
- 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\ 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).
-
-@c
-static void get_preamble_token(void)
-{
- RESTART:
- get_token();
- while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) {
- get_token(); /* this token will be expanded once */
- 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;
- }
-}
-
-
-
-@ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate
-mode, \TeX\ calls |init_align|, whose task is to get everything off to a
-good start. This mostly involves scanning the preamble and putting its
-information into the preamble list.
-@^preamble@>
-
-@c
-void init_align(void)
-{
- /* label done, done1, done2, continue; */
- pointer save_cs_ptr; /* |warning_index| value for error messages */
- pointer p, r; /* for short-term temporary use */
- save_cs_ptr = cur_cs; /* \.{\\halign} or \.{\\valign}, usually */
- push_alignment();
- align_state = -1000000; /* enter a new alignment level */
-
- /* 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();
- }
- push_nest(); /* enter a new semantic level */
- /* 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);
- /* Scan the preamble */
- preamble = null;
- cur_align = align_head;
- cur_loop = null;
- scanner_status = aligning;
- warning_index = save_cs_ptr;
- align_state = -1000000;
- /* at this point, |cur_cmd=left_brace| */
- while (true) {
- /* 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)
- break; /* \.{\\cr} ends the preamble */
-
- /* Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| */
- /* 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;
- cur_align = vlink(cur_align); /* a new alignrecord */
- span_ptr(cur_align) = end_span;
- width(cur_align) = null_flag;
- u_part(cur_align) = token_link(hold_token_head);
- /* 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);
- token_info(p) = end_template_token; /* put \.{\\endtemplate} at the end */
-
- 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);
- align_peek(); /* look for \.{\\noalign} or \.{\\omit} */
-}
-
-
-@ 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.
-
-@c
-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)) {
- goto RESTART; /* ignore \.{\\crcr} */
- } else {
- init_row(); /* start a new row */
- init_col(); /* start a new column and replace what we peeked at */
- }
-}
-
-
-@ 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.
-
-@c
-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;
-}
-
-
-@ 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.
-
-@c
-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);
-}
-
-
-@ 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.
-
-@c
-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);
- } /* now |align_state=1000000| */
-}
-
-
-@ The scanner sets |align_state| to zero when the \<u_j> template ends. When
-a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|,
-the scanner activates the following code, which fires up the \<v_j> template.
-We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|,
-|span_code|, or a character code, depending on how the column text has ended.
-
-This part of the program had better not be activated when the preamble
-to another alignment is being scanned, or when no alignment preamble is active.
-
-@c
-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;
-}
-
-/* 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)
-
-
-/* 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)
-
-
-
-@ 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.
-
-@c
-boolean fin_col(void)
-{
- pointer p; /* the alignrecord after the current one */
- pointer q, r; /* temporary pointers for list manipulation */
- pointer s; /* a new span node */
- pointer u; /* a new unset box */
- scaled w; /* natural width */
- unsigned char o; /* order of infinity */
- halfword n; /* span counter */
- 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);
- /* If the preamble list has been traversed, check that the row has ended */
- if ((p == null) && (extra_info(cur_align) < cr_code)) {
- if (cur_loop != null) {
- /* Lengthen the preamble periodically */
- r = new_node(align_record_node, 0);
- vlink(q) = r;
- p = vlink(q); /* a new alignrecord */
- span_ptr(p) = end_span;
- width(p) = null_flag;
- cur_loop = vlink(cur_loop);
-
- /* 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);
- /* 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);
- }
- n = min_quarterword; /* this represents a span count of 1 */
- if (cur_span != cur_align) {
- /* Update width entry for spanned columns */
- q = cur_span;
- do {
- incr(n);
- q = vlink(vlink(q));
- } while (q != cur_align);
- if (n > max_quarterword)
- confusion("too many spans"); /* this can happen, but won't */
- 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;
-
- /* 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;
-}
-
-
-
-@ 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|.
-@^data structure assumptions@>
-
-The |new_span_node| function is defined in |texnodes.c|.
-
-@c
-#ifndef span_span
-# define span_span(A) vlink((A)+1) /* that is normally |alink| */
-#endif
-
-
-@ 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.
-
-@c
-void fin_row(void)
-{
- pointer p; /* the new unset box */
- 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();
- /* note that |glue_shrink(p)=0| since |glue_shrink==shift_amount| */
-}
-
-
-@ Finally, we will reach the end of the alignment, and we can breathe a
-sigh of relief that memory hasn't overflowed. All the unset boxes will now be
-set so that the columns line up, taking due account of spanned columns.
-
-@c
-void fin_align(void)
-{
- pointer p, q, r, s, u, rr; /* registers for the list operations */
- scaled t, w; /* width of column */
- scaled o; /* shift offset for unset boxes */
- halfword n; /* matching span amount */
- scaled rule_save; /* temporary storage for |overfull_rule| */
- halfword pd; /* temporary storage for |prev_depth| */
- halfword ng; /* temporary storage for |new_glue| */
- if (cur_group != align_group)
- confusion("align1");
- unsave(); /* that |align_group| was for individual entries */
- if (cur_group != align_group)
- confusion("align0");
- unsave(); /* that |align_group| was for the whole alignment */
- if (nest[nest_ptr - 1].mode_field == mmode)
- o = display_indent_par;
- else
- o = 0;
- /* 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) {
- /* Nullify |width(q)| and the tabskip glue following this column */
- width(q) = 0;
- r = vlink(q);
- reset_glue_to_zero(r); /* is a lready copy */
- }
- if (span_ptr(q) != end_span) {
- /* 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);
-
- /* 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;
- overfull_rule_par = 0; /* prevent rule from being packaged */
- 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;
-
- /* 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) {
- /* 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));
- assert (type(r) == unset_node);
- s = vlink(list_ptr(p));
- do {
- /* 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);
- /* 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) {
- /* 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 {
- /* 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;
-
- }
- /* subtype(r) = 0; */
- shift_amount(r) = 0;
- if (u != hold_head) { /* 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) {
- /* 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();
- /* Insert the current list into its environment */
- /* 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; /* aux:=aux_save; */
- 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();
- }
- }
-}
-
-@ The token list |omit_template| just referred to is a constant token
-list that contains the special control sequence \.{\\endtemplate} only.
-
-@c
-void initialize_alignments(void)
-{
- token_info(omit_template) = end_template_token; /* |link(omit_template)=null| */
- span_span(end_span) = max_quarterword + 1;
- span_ptr(end_span) = null;
-}
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.w b/Build/source/texk/web2c/luatexdir/tex/arithmetic.w
deleted file mode 100644
index 56fb9568acb..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/arithmetic.w
+++ /dev/null
@@ -1,735 +0,0 @@
-% 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/>.
-
-\def\MP{MetaPost}
-
-@ @c
-
-
-#include "ptexlib.h"
-
-@ 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.
-
-@c
-int half(int x)
-{
- if (odd(x))
- return ((x + 1) / 2);
- else
- return (x / 2);
-}
-
-
-@ 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.
-
-@c
-scaled round_decimals(int k)
-{ /* converts a decimal fraction */
- int a; /* the accumulator */
- a = 0;
- while (k-- > 0) {
- a = (a + dig[k] * two) / 10;
- }
- return ((a + 1) / 2);
-}
-
-
-@ Conversely, here is a procedure analogous to |print_int|. If the output
-of this procedure is subsequently read by \TeX\ and converted by the
-|round_decimals| routine above, it turns out that the original value will
-be reproduced exactly; the ``simplest'' such decimal number is output,
-but there is always at least one digit following the decimal point.
-
-The invariant relation in the \&{repeat} loop is that a sequence of
-decimal digits yet to be printed will yield the original number if and only if
-they form a fraction~$f$ in the range $s-\delta\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.
-
-@c
-void print_scaled(scaled s)
-{ /* prints scaled real, rounded to five digits */
- scaled delta; /* amount of allowable inaccuracy */
- char buffer[20];
- int i = 0;
- if (s < 0) {
- print_char('-');
- negate(s); /* print the sign, if negative */
- }
- print_int(s / unity); /* print the integer part */
- buffer[i++] = '.';
- s = 10 * (s % unity) + 5;
- delta = 10;
- do {
- if (delta > unity)
- s = s + 0100000 - 50000; /* round the last digit */
- buffer[i++] = '0' + (s / unity);
- s = 10 * (s % unity);
- delta = delta * 10;
- } while (s > delta);
- buffer[i++] = '\0';
- tprint(buffer);
-}
-
-@ 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.
-
-@c
-boolean arith_error; /* has arithmetic overflow occurred recently? */
-scaled tex_remainder; /* amount subtracted to get an exact division */
-
-
-@ The first arithmetical subroutine we need computes $nx+y$, where |x|
-and~|y| are |scaled| and |n| is an integer. We will also use it to
-multiply integers.
-
-@c
-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;
- }
-}
-
-@ We also need to divide scaled dimensions by integers.
-@c
-scaled x_over_n(scaled x, int n)
-{
- boolean negative; /* should |tex_remainder| be negated? */
- 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));
- }
- }
-}
-
-
-@ 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.
-
-@c
-scaled xn_over_d(scaled x, int n, int d)
-{
- nonnegative_integer t, u, v, xx, dd; /* intermediate quantities */
- boolean positive = true; /* was |x>=0|? */
- 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 {
- /* casts are for ms cl */
- tex_remainder = -(int) (v % dd);
- return -(scaled) (u);
- }
-}
-
-
-@ 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.
-
-@c
-halfword badness(scaled t, scaled s)
-{ /* compute badness, given |t>=0| */
- int r; /* approximation to $\alpha t/s$, where $\alpha^3\approx
- 100\cdot2^{18}$ */
- if (t == 0) {
- return 0;
- } else if (s <= 0) {
- return inf_bad;
- } else {
- if (t <= 7230584)
- r = (t * 297) / s; /* $297^3=99.94\times2^{18}$ */
- else if (s >= 1663497)
- r = t / (s / 297);
- else
- r = t;
- if (r > 1290)
- return inf_bad; /* $1290^3<2^{31}<1291^3$ */
- else
- return ((r * r * r + 0400000) / 01000000);
- /* that was $r^3/2^{18}$, rounded to the nearest integer */
- }
-}
-
-
-@ When \TeX\ ``packages'' a list into a box, it needs to calculate the
-proportionality ratio by which the glue inside the box should stretch
-or shrink. This calculation does not affect \TeX's decision making,
-so the precise details of rounding, etc., in the glue calculation are not
-of critical importance for the consistency of results on different computers.
-
-We shall use the type |glue_ratio| for such proportionality ratios.
-A glue ratio should take the same amount of memory as an
-|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 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) -- Taco
-
-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.
-
-@c
-static int randoms[55]; /* the last 55 random values generated */
-static int j_random; /* the number of unused |randoms| */
-scaled random_seed; /* the default random seed */
-
-@ A small bit of metafont is needed.
-
-@c
-#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 */
-
-@ 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.
-
-@c
-static int make_frac(int p, int q)
-{
- int f; /* the fraction bits, with a leading 1 bit */
- int n; /* the integer part of $\vert p/q\vert$ */
- register int be_careful; /* disables certain compiler optimizations */
- boolean negative = false; /* should the result be negated? */
- if (p < 0) {
- negate(p);
- negative = true;
- }
- if (q <= 0) {
-#ifdef DEBUG
- if (q == 0)
- confusion("/");
-#endif
- 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;
- /* 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);
- }
-}
-
-@ @c
-static int take_frac(int q, int f)
-{
- int p; /* the fraction so far */
- int n; /* additional multiple of $q$ */
- register int be_careful; /* disables certain compiler optimizations */
- boolean negative = false; /* should the result be negated? */
- /* 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;
- /* 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}$.
- */
- p = fraction_half; /* that's $2^{27}$; the invariants hold now with $k=28$ */
- 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);
-}
-
-
-
-@ 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.
-
-@c
-static int two_to_the[31]; /* powers of two */
-static int spec_log[29]; /* special logarithms */
-
-@ @c
-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;
-}
-
-@ @c
-static int m_log(int x)
-{
- int y, z; /* auxiliary registers */
- int k; /* iteration counter */
- if (x <= 0) {
- /* 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 {
- y = 1302456956 + 4 - 100; /* $14\times2^{27}\ln2\approx1302456956.421063$ */
- z = 27595 + 6553600; /* and $2^{16}\times .421063\approx 27595$ */
- while (x < fraction_four) {
- x += x;
- y = y - 93032639;
- z = z - 48782;
- } /* $2^{27}\ln2\approx 93032639.74436163$
- and $2^{16}\times.74436163\approx 48782$ */
- y = y + (z / unity);
- k = 2;
- while (x > fraction_four + 4) {
- /* Increase |k| until |x| can be multiplied by a
- factor of $2^{-k}$, and adjust $y$ accordingly */
- z = ((x - 1) / two_to_the[k]) + 1; /* $z=\lceil x/2^k\rceil$ */
- while (x < fraction_four + z) {
- z = halfp(z + 1);
- k = k + 1;
- }
- y = y + spec_log[k];
- x = x - z;
- }
- return (y / 8);
- }
-}
-
-
-
-@ 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.
-
-@c
-static int ab_vs_cd(int a, int b, int c, int d)
-{
- int q, r; /* temporary registers */
- /* Reduce to the case that |a,c>=0|, |b,d>0| */
- 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; /* now |a>d>0| and |c>b>0| */
- }
-}
-
-
-
-@ To consume a random integer, the program below will say `|next_random|'
-and then it will fetch |randoms[j_random]|.
-
-@c
-#define next_random() do { \
- if (j_random==0) new_randoms(); else decr(j_random); \
- } while (0)
-
-static void new_randoms(void)
-{
- int k; /* index into |randoms| */
- int x; /* accumulator */
- for (k = 0; k <= 23; k++) {
- x = randoms[k] - randoms[k + 31];
- if (x < 0)
- x = x + 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;
-}
-
-
-@ To initialize the |randoms| table, we call the following routine.
-
-@c
-void init_randoms(int seed)
-{
- int j, jj, k; /* more or less random integers */
- int i; /* index into |randoms| */
- 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;
- }
- new_randoms();
- new_randoms();
- new_randoms(); /* ``warm up'' the array */
-}
-
-
-@ To produce a uniform random number in the range |0<=u<x| or |0>=u>x|
-or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here.
-
-Note that the call of |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.
-
-@c
-int unif_rand(int x)
-{
- int y; /* trial value */
- 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;
-}
-
-
-@ 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\/}).
-
-@c
-int norm_rand(void)
-{
- int x, u, l; /* what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$ */
- do {
- do {
- next_random();
- x = take_frac(112429, randoms[j_random] - fraction_half);
- /* $2^{16}\sqrt{8/e}\approx 112428.82793$ */
- next_random();
- u = randoms[j_random];
- } while (abs(x) >= u);
- x = make_frac(x, u);
- l = 139548960 - m_log(u); /* $2^{24}\cdot12\ln2\approx139548959.6165$ */
- } while (ab_vs_cd(1024, l, x, x) < 0);
- return x;
-}
-
-@ This function could also be expressed as a macro, but it is a useful
- breakpoint for debugging.
-
-@c
-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/backend.c b/Build/source/texk/web2c/luatexdir/tex/backend.c
new file mode 100644
index 00000000000..d8c7daed251
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/backend.c
@@ -0,0 +1,123 @@
+/* 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->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.w b/Build/source/texk/web2c/luatexdir/tex/buildpage.w
deleted file mode 100644
index cba07cbf6f6..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/buildpage.w
+++ /dev/null
@@ -1,1013 +0,0 @@
-% buildpage.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/>.
-
-@ @c
-
-#include "ptexlib.h"
-
-@ @c
-#define mode mode_par
-#define head head_par
-#define tail tail_par
-
-@ 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.
-@^current page@>
-
-Utter chaos would reign if the user kept changing page specifications
-while a page is being constructed, so the page builder keeps the pertinent
-specifications frozen as soon as the page receives its first box or
-insertion. The global variable |page_contents| is |empty| when the
-current page contains only mark nodes and content-less whatsit nodes; it
-is |inserts_only| if the page contains only insertion nodes in addition to
-marks and whatsits. Glue nodes, kern nodes, and penalty nodes are
-discarded until a box or rule node appears, at which time |page_contents|
-changes to |box_there|. As soon as |page_contents| becomes non-|empty|,
-the current |vsize| and |max_depth| are squirreled away into |page_goal|
-and |page_max_depth|; the latter values will be used until the page has
-been forwarded to the user's output routine. The \.{\\topskip} adjustment
-is made when |page_contents| changes to |box_there|.
-
-Although |page_goal| starts out equal to |vsize|, it is decreased by the
-scaled natural height-plus-depth of the insertions considered so far, and by
-the \.{\\skip} corrections for those insertions. Therefore it represents
-the size into which the non-inserted material should fit, assuming that
-all insertions in the current page have been made.
-
-The global variables |best_page_break| and |least_page_cost| correspond
-respectively to the local variables |best_place| and |least_cost| in the
-|vert_break| routine that we have already studied; i.e., they record the
-location and value of the best place currently known for breaking the
-current page. The value of |page_goal| at the time of the best break is
-stored in |best_size|.
-
-@c
-halfword page_tail; /* the final node on the current page */
-int page_contents; /* what is on the current page so far? */
-scaled page_max_depth; /* maximum box depth on page being built */
-halfword best_page_break; /* break here to get the best page known so far */
-int least_page_cost; /* the score for this currently best page */
-scaled best_size; /* its |page_goal| */
-
-@ The page builder has another data structure to keep track of insertions.
-This is a list of four-word nodes, starting and ending at |page_ins_head|.
-That is, the first element of the list is node |r@t$_1$@>=vlink(page_ins_head)|;
-node $r_j$ is followed by |r@t$_{j+1}$@>=vlink(r@t$_j$@>)|; and if there are
-|n| items we have |r@t$_{n+1}$@>=page_ins_head|. The |subtype| field of
-each node in this list refers to an insertion number; for example, `\.{\\insert
-250}' would correspond to a node whose |subtype| is |qi(250)|
-(the same as the |subtype| field of the relevant |ins_node|). These |subtype|
-fields are in increasing order, and |subtype(page_ins_head)=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.
-
-@c
-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;
-}
-
-
-@ 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.
-
-@c
-scaled page_so_far[8]; /* height and glue of the current page */
-halfword last_glue; /* used to implement \.{\\lastskip} */
-int last_penalty; /* used to implement \.{\\lastpenalty} */
-scaled last_kern; /* used to implement \.{\\lastkern} */
-int last_node_type; /* used to implement \.{\\lastnodetype} */
-int insert_penalties; /* sum of the penalties for held-over insertions */
-
-#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);
- }
-}
-
-@ Here is a procedure that is called when the |page_contents| is changing
-from |empty| to |inserts_only| or |box_there|.
-
-@c
-#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);
- }
-}
-
-@ The global variable |output_active| is true during the time the
-user's output routine is driving \TeX.
-
-@c
-boolean output_active; /* are we in the midst of an output routine? */
-
-@ 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.)
-
-@c
-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;
-}
-
-@ 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.
-
-@c
-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;
-}
-
-@ The following procedure guarantees that a given box register
-does not contain an \.{\\hbox}.
-
-@c
-static void ensure_vbox(int n)
-{
- halfword p; /* the box register contents */
- 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\ 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.
-
-@c
-void build_page(void)
-{ /* append contributions to the current page */
- halfword p; /* the node being appended */
- halfword q, r; /* nodes being examined */
- int b, c; /* badness and cost of current page */
- int pi = 0; /* penalty to be added to the badness */
- int n; /* insertion box number */
- scaled delta, h, w; /* sizes used for insertion calculations */
- int id, sk, i;
- if ((vlink(contrib_head) == null) || output_active)
- return;
- do {
- CONTINUE:
- p = vlink(contrib_head);
- /* 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);
- }
-
- /* 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) {
- /* 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 {
- /* 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:
- /* 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) {
- /* 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(n); */
-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;
- /* this much room is left if we shrink the maximum */
- if (count(n) == 1000)
- h = height(p);
- else
- h = x_over_n(height(p), 1000) * count(n); /* this much room is needed */
- if (((h <= 0) || (h <= delta))
- && (height(p) + height(r) <= dimen(n))) {
- page_goal = page_goal - h;
- height(r) = height(r) + height(p);
- } else {
- /* Find the best way to split the insertion, and change
- |type(r)| to |split_up_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) {
- /* 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:
- fprintf(stderr, "type(p)=%d\n", type(p));
- confusion("page");
- break;
- }
-
- /* 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) {
- /* 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) {
- /* 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)) {
- fire_up(p); /* output the current page at the best place */
- if (output_active)
- goto EXIT; /* user's output routine will act */
- goto DONE; /* the page has been shipped out by default output routine */
- }
- }
-
- if ((type(p) < glue_node) || (type(p) > kern_node))
- goto CONTRIBUTE;
-
- UPDATE_HEIGHTS: /* 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: /* 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;
- }
-
- /* 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:
- /* 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);
- /* Make the contribution list empty by setting its tail to |contrib_head| */
- contrib_tail = contrib_head;
- EXIT:
- ;
-}
-
-@ 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.
-
-@c
-void fire_up(halfword c)
-{
- halfword p, q, r, s; /* nodes being examined and/or changed */
- halfword prev_p; /* predecessor of |p| */
- int n; /* insertion box number */
- boolean wait; /* should the present insertion be held over? */
- int save_vbadness; /* saved value of |vbadness| */
- scaled save_vfuzz; /* saved value of |vfuzz| */
- halfword save_split_top_skip; /* saved value of |split_top_skip| */
- halfword i; /* for looping through the marks */
-
- /* 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);
- }
- }
- /* 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)
- best_page_break = null; /* |c| not yet linked in */
- /* 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);
- }
-
- insert_penalties = 0; /* this will count the number of insertions held over */
- save_split_top_skip = split_top_skip_par;
- if (holding_inserts_par <= 0) {
- /* 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) {
- /* 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; /* was a global temp_ptr */
- /* 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;
- }
- }
- /* 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) {
- /* 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;
- /* 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;
- vfuzz_par = max_dimen; /* inhibit error messages */
- 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);
- /* Start a new current page */
- start_new_page(); /* this sets |last_glue:=max_halfword| */
- if (q != hold_head) {
- vlink(page_head) = vlink(hold_head);
- page_tail = q;
- }
-
- /* Delete the page-insertion nodes */
- r = vlink(page_ins_head);
- while (r != page_ins_head) {
- /* 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) {
- /* 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 {
- /* 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;
-
- }
- }
- /* 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;
-}
-
-@ When the user's output routine finishes, it has constructed a vlist
-in internal vertical mode, and \TeX\ will do the following:
-
-@c
-void resume_after_output(void)
-{
- if ((iloc != null)
- || ((token_type != output_text) && (token_type != backed_up))) {
- /* 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();
- do {
- get_token();
- } while (iloc != null);
- /* loops forever if reading from a file, since |null=min_halfword<=0| */
-
- }
- end_token_list(); /* conserve stack space in case more outputs are triggered */
- end_graf(bottom_level);
- unsave();
- output_active = false;
- insert_penalties = 0;
- /* 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) { /* current list goes after heldover insertions */
- try_couple_nodes(page_tail, vlink(head));
- page_tail = tail;
- }
- if (vlink(page_head) != null) { /* and 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/commands.w b/Build/source/texk/web2c/luatexdir/tex/commands.c
index 38975eb1699..aceb0eb093d 100644
--- a/Build/source/texk/web2c/luatexdir/tex/commands.w
+++ b/Build/source/texk/web2c/luatexdir/tex/commands.c
@@ -1,34 +1,36 @@
-% 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/>.
-
-\def\eTeX{e-\TeX}
-
-@ @c
+/*
+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"
-@ 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.
+/*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.
+
+*/
-@c
void initialize_commands(void)
{
@@ -53,6 +55,9 @@ void initialize_commands(void)
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);
@@ -63,7 +68,7 @@ void initialize_commands(void)
primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base);
primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base);
- /* The integer parameter names must be entered into the hash table */
+ /*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);
@@ -166,11 +171,18 @@ void initialize_commands(void)
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);
+
+ /*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:
- /* 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);
@@ -240,29 +252,27 @@ void initialize_commands(void)
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("tokspre", combine_toks_cmd, 1, 0);
- primitive_luatex("etoksapp", combine_toks_cmd, 2, 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_tex("par", par_end_cmd, too_big_char, too_big_char); /* cf.\ |scan_file_name| */
+ 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;
- @<Create a bunch of primitives@>;
- @<Create the math param primitives@>;
- @<Create another bunch of primitives@>;
-}
-
-@ These are in a separate module due to a CWEAVE limitation.
-
-@<Create a bunch of primitives@>=
-
- /*
+ /*tex
The processing of \.{\\input} involves the |start_input| subroutine,
which will be declared later; the processing of \.{\\endinput} is trivial.
*/
@@ -318,7 +328,7 @@ void initialize_commands(void)
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_tex("csstring", convert_cmd, cs_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);
@@ -331,9 +341,12 @@ void initialize_commands(void)
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);
@@ -352,15 +365,15 @@ void initialize_commands(void)
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, ifx_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];
@@ -435,12 +448,13 @@ void initialize_commands(void)
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 - 1, 0); /* |ship_out_flag=leader_flag-1| */
+ 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);
@@ -514,9 +528,10 @@ void initialize_commands(void)
primitive_tex("gdef", def_cmd, 1, 0);
primitive_tex("edef", def_cmd, 2, 0);
primitive_tex("xdef", def_cmd, 3, 0);
- primitive_tex("let", let_cmd, normal, 0);
- primitive_tex("futurelet", let_cmd, normal + 1, 0);
- primitive_luatex("letcharcode", let_cmd, normal + 2, 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);
@@ -539,9 +554,6 @@ void initialize_commands(void)
primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0);
primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0);
-@ These are in a separate module due to a CWEAVE limitation.
-
-@<Create the math param primitives@>=
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);
@@ -654,9 +666,6 @@ void initialize_commands(void)
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);
-@ These are in a separate module due to a CWEAVE limitation.
-
-@<Create another bunch of primitives@>=
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);
@@ -695,6 +704,7 @@ void initialize_commands(void)
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];
@@ -720,6 +730,7 @@ void initialize_commands(void)
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);
@@ -729,6 +740,15 @@ void initialize_commands(void)
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.
@@ -737,8 +757,8 @@ void initialize_commands(void)
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);
+}
-@ @c
void initialize_etex_commands(void)
{
primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0);
@@ -746,8 +766,10 @@ void initialize_etex_commands(void)
primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, 0);
primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0);
- /*
- First we implement the additional \eTeX\ parameters in the table of equivalents.
+ /*tex
+
+ First we implement the additional \eTeX\ parameters in the table of
+ equivalents.
*/
primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base);
@@ -795,39 +817,48 @@ void initialize_etex_commands(void)
primitive_etex("showgroups", xray_cmd, show_groups, 0);
- /*
+ /*tex
+
The \.{\\showtokens} command displays a token list.
+
*/
primitive_etex("showtokens", xray_cmd, show_tokens, 0);
- /*
- 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.
+ /*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);
@@ -843,7 +874,8 @@ void initialize_etex_commands(void)
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}
@@ -852,8 +884,10 @@ void initialize_etex_commands(void)
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);
@@ -869,21 +903,25 @@ void initialize_etex_commands(void)
primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0);
primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0);
- /*
- The \.{\\pagediscards} and \.{\\splitdiscards} commands share the
- command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are
+ /*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
+ |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);
diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.w b/Build/source/texk/web2c/luatexdir/tex/conditional.c
index 318775141a8..948db580340 100644
--- a/Build/source/texk/web2c/luatexdir/tex/conditional.w
+++ b/Build/source/texk/web2c/luatexdir/tex/conditional.c
@@ -1,67 +1,73 @@
-% 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/>.
-
-@ @c
+/*
+
+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|.
-
-@c
-halfword cond_ptr; /* top of the condition stack */
-int if_limit; /* upper bound on |fi_or_else| codes */
-int cur_if; /* type of conditional being worked on */
-int if_line; /* line where that conditional began */
-
-@ When we skip conditional text, we keep track of the line number
-where skipping began, for use in error messages.
-
-@c
-int skip_line; /* skipping began here */
-
-@ Here is a procedure that ignores text until coming to an \.{\\or},
-\.{\\else}, or \.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$
-nesting. After it has acted, |cur_chr| will indicate the token that
-was found, but |cur_tok| will not be set (because this makes the
-procedure run faster).
-
-@c
+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; /* level of $\.{\\if}\ldots\.{\\fi}$ nesting */
- int save_scanner_status = scanner_status; /* |scanner_status| upon entry */
+ int l = 0;
+ int save_scanner_status = scanner_status;
scanner_status = skipping;
skip_line = line;
while (1) {
@@ -80,13 +86,16 @@ void pass_text(void)
show_cur_cmd_chr();
}
-@ When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then
-if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if}
-condition has been evaluated, \.{\\relax} will be inserted.
-For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}'
-would otherwise require something after the `\.1'.
+/*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'.
+
+*/
-@c
void push_condition_stack(void)
{
halfword p = new_node(if_node, 0);
@@ -103,9 +112,10 @@ void push_condition_stack(void)
void pop_condition_stack(void)
{
halfword p;
- if (if_stack[in_open] == cond_ptr)
+ if (if_stack[in_open] == cond_ptr) {
+ /*tex Conditionals are possibly not properly nested with files. */
if_warning();
- /* conditionals possibly not properly nested with files */
+ }
p = cond_ptr;
if_line = if_line_field(p);
cur_if = if_limit_subtype(p);
@@ -114,14 +124,16 @@ void pop_condition_stack(void)
flush_node(p);
}
-@ Here's a procedure that changes the |if_limit| code corresponding to
-a given value of |cond_ptr|.
+/*tex
+
+Here's a procedure that changes the |if_limit| code corresponding to a given
+value of |cond_ptr|.
+
+*/
-@c
void change_if_limit(int l, halfword p)
{
if (p == cond_ptr) {
- /* that's the easy case */
if_limit = l;
} else {
halfword q = cond_ptr;
@@ -137,22 +149,29 @@ void change_if_limit(int l, halfword p)
}
}
-@ 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).
+/*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).
+
+*/
-@c
static halfword last_tested_cs ;
static boolean test_for_cs(void)
{
- boolean b = false; /*is the condition true? */
- int m, s; /*to be tested against the second operand */
- halfword q; /*for traversing token lists in \.{\\ifx} tests */
+ /*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();
- halfword p = n; /*head of the list of characters */
-is_in_csname += 1;
+ /*tex Head of the list of characters: */
+ halfword p = n;
+ is_in_csname += 1;
while (1) {
get_x_token();
if (cur_cs != 0)
@@ -166,13 +185,13 @@ is_in_csname += 1;
get_x_token();
} while (cur_cmd != end_cs_name_cmd);
flush_list(n);
-is_in_csname -= 1;
+ is_in_csname -= 1;
return b;
} else {
complain_missing_csname();
}
}
- /* Look up the characters of list |n| in the hash table, and set |cur_cs| */
+ /*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) {
@@ -200,21 +219,26 @@ is_in_csname -= 1;
p = token_link(p);
}
if (m > first) {
- cur_cs = id_lookup(first, m - first); /* |no_new_control_sequence| is |true| */
+ /*tex |no_new_control_sequence| is |true| */
+ cur_cs = id_lookup(first, m - first);
} else if (m == first) {
- cur_cs = null_cs; /* the list is empty */
+ /*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;
+ is_in_csname -= 1;
return b;
}
-@ An active character will be treated as category 13 following
-\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}.
+/*tex
+
+An active character will be treated as category 13 following \.{\\if\\noexpand}
+or following \.{\\ifcat\\noexpand}.
+
+*/
-@c
#define get_x_token_or_active_char() do { \
get_x_token(); \
if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \
@@ -225,36 +249,47 @@ is_in_csname -= 1;
} \
} while (0)
-@ 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@>
+/*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@>
+
+*/
-@c
void conditional(void)
{
- boolean b = false; /*is the condition true? */
- int r; /*relation to be evaluated */
- int m, n; /*to be tested against the second operand */
- halfword p, q; /*for traversing token lists in \.{\\ifx} tests */
- int save_scanner_status; /*|scanner_status| upon entry */
- halfword save_cond_ptr; /*|cond_ptr| corresponding to this conditional */
- int this_if; /*type of this conditional */
- boolean is_unless; /*was this if preceded by `\.{\\unless}' ? */
- if ((tracing_ifs_par > 0) && (tracing_commands_par <= 1))
+ /*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;
- /* Either process \.{\\ifcase} or set |b| to the value of a boolean condition */
+ /*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:
- /* Test if two characters match */
+ /*tex Test if two characters match. */
get_x_token_or_active_char();
if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) {
- /*not a character */
+ /*tex It's not a character. */
m = relax_cmd;
n = too_big_char;
} else {
@@ -275,8 +310,10 @@ void conditional(void)
case if_dim_code:
case if_abs_dim_code:
case if_abs_num_code:
- /* Test relation between integers or dimensions */
- /* Here we use the fact that |"<"|, |"="|, and |">"| are consecutive ASCII codes. */
+ /*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
@@ -284,15 +321,10 @@ void conditional(void)
n = cur_val;
if ((n < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code))
negate(n);
- /* Get the next non-blank non-call... */
+ /*tex Get the next non-blank non-call... */
do {
get_x_token();
} while (cur_cmd == spacer_cmd);
- /*
- if ((cur_tok >= other_token + '<') && (cur_tok <= other_token + '>')) {
- r = cur_tok - other_token;
- } else {
- */
r = cur_tok - other_token;
if ((r < '<') || (r > '>')) {
print_err("Missing = inserted for ");
@@ -318,13 +350,13 @@ void conditional(void)
b = (n > cur_val);
break;
default:
- /* can't happen */
+ /*tex This can't happen. */
b = false;
break;
}
break;
case if_odd_code:
- /* Test if an integer is odd */
+ /*tex Test if an integer is odd. */
scan_int();
b = odd(cur_val);
break;
@@ -340,22 +372,6 @@ void conditional(void)
case if_inner_code:
b = (cur_list.mode_field < 0);
break;
- /*
- case if_void_code:
- case if_hbox_code:
- case if_vbox_code:
- scan_register_num();
- p = box(cur_val);
- if (this_if == if_void_code)
- b = (p == null);
- else if (p == null)
- b = false;
- else if (this_if == if_hbox_code)
- b = (type(p) == hlist_node);
- else
- b = (type(p) == vlist_node);
- break;
- */
case if_void_code:
scan_register_num();
p = box(cur_val);
@@ -371,16 +387,15 @@ void conditional(void)
p = box(cur_val);
b = (p != null) && (type(p) == vlist_node);
break;
- case ifx_code:
- /*
- 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.
+ 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.
+ 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;
@@ -394,11 +409,10 @@ void conditional(void)
} else if (cur_cmd < call_cmd) {
b = (cur_chr == q);
} else {
- /*
- Test if two macro texts match
-
- Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are
- different in examples like this:
+ /*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\}&
@@ -407,7 +421,7 @@ void conditional(void)
{}\\def\\d\{\}\cr}}$$
*/
p = token_link(cur_chr);
- /*omit reference counts */
+ /*tex Omit reference counts. */
q = token_link(equiv(n));
if (p == q) {
b = true;
@@ -437,9 +451,9 @@ void conditional(void)
b = false;
break;
case if_case_code:
- /* Select the appropriate case and |return| or |goto common_ending| */
+ /*tex Select the appropriate case and |return| or |goto common_ending|. */
scan_int();
- /* |n| is the number of cases to pass */
+ /*tex |n| is the number of cases to pass. */
n = cur_val;
if (tracing_commands_par > 1) {
begin_diagnostic();
@@ -460,7 +474,7 @@ void conditional(void)
}
}
change_if_limit(or_code, save_cond_ptr);
- /*wait for \.{\\or}, \.{\\else}, or \.{\\fi} */
+ /*tex Wait for \.{\\or}, \.{\\else}, or \.{\\fi}. */
return;
break;
case if_primitive_code:
@@ -475,10 +489,11 @@ void conditional(void)
(cur_chr == get_prim_equiv(m)));
break;
case if_def_code:
- /*
- 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.
+ /*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;
@@ -493,9 +508,9 @@ void conditional(void)
b = is_in_csname;
break;
case if_font_char_code:
- /*
- The conditional \.{\\iffontchar} tests the existence of a character in
- a font.
+ /*tex
+ The conditional \.{\\iffontchar} tests the existence of a
+ character in a font.
*/
scan_font_ident();
n = cur_val;
@@ -503,13 +518,13 @@ void conditional(void)
b = char_exists(n, cur_val);
break;
default:
- /* there are no other cases, but for -Wall: */
+ /*tex there are no other cases, but we need to please |-Wall|. */
b = false;
}
if (is_unless)
b = !b;
if (tracing_commands_par > 1) {
- /* Display the value of |b| */
+ /*tex Display the value of |b|. */
begin_diagnostic();
if (b)
tprint("{true}");
@@ -519,16 +534,15 @@ void conditional(void)
}
if (b) {
change_if_limit(else_code, save_cond_ptr);
- /*wait for \.{\\else} or \.{\\fi} */
+ /*tex Wait for \.{\\else} or \.{\\fi}. */
return;
}
- /*
- Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|
-
- In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first
+ /*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.
+ not the \.{\\else} we're looking for. Hence the following curious logic
+ is needed.
*/
while (1) {
pass_text();
@@ -536,7 +550,9 @@ void conditional(void)
if (cur_chr != or_code)
goto COMMON_ENDING;
print_err("Extra \\or");
- help1("I'm ignoring this; it doesn't match any \\if.");
+ help1(
+ "I'm ignoring this; it doesn't match any \\if."
+ );
error();
} else if (cur_chr == fi_code) {
pop_condition_stack();
@@ -546,7 +562,7 @@ void conditional(void)
if (cur_chr == fi_code) {
pop_condition_stack();
} else {
- /*wait for \.{\\fi} */
+ /*tex Wait for \.{\\fi}. */
if_limit = fi_code;
}
}
diff --git a/Build/source/texk/web2c/luatexdir/tex/directions.w b/Build/source/texk/web2c/luatexdir/tex/directions.c
index c844e934c0d..59b2f7dcd85 100644
--- a/Build/source/texk/web2c/luatexdir/tex/directions.w
+++ b/Build/source/texk/web2c/luatexdir/tex/directions.c
@@ -1,27 +1,28 @@
-% 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/>.
-
-@ @c
+/*
+
+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"
-@ @c
void scan_direction(void)
{
int save_cur_cmd = cur_cmd;
@@ -53,9 +54,12 @@ void scan_direction(void)
cur_chr = save_cur_chr;
}
-@ the next two are used by postlinebreak.c
+/*tex
+
+ The next two are used by |postlinebreak.c|:
+
+*/
-@c
halfword do_push_dir_node(halfword p, halfword a)
{
halfword n = copy_node(a);
@@ -70,18 +74,14 @@ halfword do_pop_dir_node(halfword p)
return n;
}
-@ @c
halfword dir_ptr;
-
halfword text_dir_ptr;
-@ There is no need to do anything here at the moment.
-@c
void initialize_directions(void)
{
+ /*tex There is no need to do anything here at the moment. */
}
-@ @c
halfword new_dir(int s)
{
halfword p = new_node(dir_node, 0);
@@ -90,55 +90,32 @@ halfword new_dir(int s)
return p;
}
-@ The global static array variable |dir_strings| is also used
-by the lua nodelib interface, so it cannot be static. Putting
-it here instead of there avoid the nodelib having to know
-about the actual values of |dir_TRT| etc.
-
-@c
-
-/*
-const char *dir_strings[128] = {
- "-TLT","???", "???", "???", "-TRT","???", "???", "???",
- "???", "-LTL","???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "-RTT","???", "???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "+TLT","???", "???", "???", "+TRT","???", "???", "???",
- "???", "+LTL","???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "+RTT","???", "???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???",
- "???", "???", "???", "???", "???", "???", "???", "???"
+const char *dir_strings_par[4] = { [0] =
+ "TLT","TRT","LTL","RTT",
};
-int dir_swap = 64;
-*/
-
-const char *dir_strings[8] = {
- "-TLT","-TRT","-LTL","-RTT",
+const char *dir_strings_text_normal[4] = { [0] =
"+TLT","+TRT","+LTL","+RTT",
};
-int dir_swap = 4;
+const char *dir_strings_text_cancel[4] = { [0] =
+ "-TLT","-TRT","-LTL","-RTT",
+};
-const char *string_dir(int d)
+void print_dir_par(int d)
{
- return (dir_strings[d+dir_swap]+1);
+ tprint(dir_strings_par[d]);
}
-@ @c
-void print_dir(int d)
+void print_dir_text(halfword d)
{
- tprint(string_dir(d));
+ if (subtype(d) == cancel_dir) {
+ tprint(dir_strings_text_cancel[dir_dir(d)]);
+ } else {
+ tprint(dir_strings_text_normal[dir_dir(d)]);
+ }
}
-@ @c
scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph)
{
scaled wd = 0;
@@ -153,9 +130,7 @@ scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph)
} else {
wd = glyph_depth(p) + glyph_height(p);
}
-/* experimental */
-wd += x_advance(p);
- } else { /* hlist, vlist, image, form, rule */
+ } else {
if (textdir_parallel(pdir, curdir))
wd = width(p);
else
@@ -164,7 +139,6 @@ wd += x_advance(p);
return wd;
}
-@ @c
scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph)
{
scaled_whd whd = { 0, 0, 0 };
@@ -208,7 +182,6 @@ scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isg
return whd;
}
-@ @c
void update_text_dir_ptr(int val)
{
if (dir_level(text_dir_ptr) == cur_level) {
diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w b/Build/source/texk/web2c/luatexdir/tex/dumpdata.c
index 9d06f1c8ed4..0091685fb74 100644
--- a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w
+++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.c
@@ -1,84 +1,97 @@
-% 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/>.
-
-@ @c
+/*
+
+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"
-/* we start with 907: the sum of the values of the bytes of "don knuth" */
+/*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+37)
+*/
+
+#define FORMAT_ID (907+48)
#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.
+
+*/
-@ 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.
-@c
str_number format_ident;
-str_number format_name; /* principal file name */
+str_number format_name;
-@ Format files consist of |memory_word| items, and we use the following
-macros to dump words of different types:
+/*tex
-@c
-FILE *fmt_file; /* for input or output of format information */
+Format files consist of |memory_word| items, and we use the following macros to
+dump words of different types:
+
+*/
+
+FILE *fmt_file;
-@ @c
void store_fmt_file(void)
{
- int j, k, l; /* all-purpose indices */
- halfword p; /* all-purpose pointer */
- int x; /* something to dump */
+ int j, k, l, x;
+ halfword p;
char *format_engine;
- int callback_id; /* |pre_dump| callback */
+ int callback_id;
char *fmtname = NULL;
- /* 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. */
+ /*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();
}
-
- /* Create the |format_ident|, open the format file, and inform the user
- that dumping has begun */
+ /*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, "->");
@@ -97,11 +110,11 @@ void store_fmt_file(void)
format_ident = make_string();
print(job_name);
format_name = make_string();
- if (interaction == batch_mode)
+ if (interaction == batch_mode) {
selector = log_only;
- else
+ } 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);
@@ -111,15 +124,16 @@ void store_fmt_file(void)
free(fmtname);
tprint_nl("");
print(format_ident);
-
- /* Dump constants for consistency check */
- /* The next few sections of the program should make it clear how we use the
- dump/undump macros. */
-
- dump_int(0x57325458); /* Web2C \TeX's magic constant: "W2TX" */
+ /*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);
-
- /* Align engine to 4 bytes with one or more trailing NUL */
+ /*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);
@@ -129,28 +143,26 @@ void store_fmt_file(void)
dump_int(x);
dump_things(format_engine[0], x);
xfree(format_engine);
- dump_int(0x57325458); /* TODO HM, what checksum would make sense? */
+ dump_int(0x57325458);
dump_int(max_halfword);
dump_int(hash_high);
dump_int(eqtb_size);
dump_int(hash_prime);
-
- /* Dump the string pool */
+ /*tex Dump the string pool. */
k = dump_string_pool();
print_ln();
print_int(k);
tprint(" strings using ");
print_int((longinteger) pool_size);
tprint(" bytes");
-
- /* 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.
- */
+ /*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);
@@ -180,14 +192,13 @@ void store_fmt_file(void)
print_int(var_used);
print_char('&');
print_int(dyn_used);
-
- /* Dump the table of equivalents */
- /* Dump regions 1 to 4 of |eqtb| */
- /*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)$.
- */
+ /*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;
@@ -198,7 +209,8 @@ void store_fmt_file(void)
incr(j);
}
l = int_base;
- goto DONE1; /* |j=int_base-1| */
+ /*tex |j=int_base-1| */
+ goto DONE1;
FOUND1:
incr(j);
l = j;
@@ -214,8 +226,7 @@ void store_fmt_file(void)
k = j + 1;
dump_int(k - l);
} while (k != int_base);
-
- /* Dump regions 5 and 6 of |eqtb| */
+ /*tex Dump regions 5 and 6 of |eqtb|. */
do {
j = k;
while (j < eqtb_size) {
@@ -224,7 +235,8 @@ void store_fmt_file(void)
incr(j);
}
l = eqtb_size + 1;
- goto DONE2; /* |j=eqtb_size| */
+ /*tex |j=eqtb_size| */
+ goto DONE2;
FOUND2:
incr(j);
l = j;
@@ -239,18 +251,20 @@ void store_fmt_file(void)
k = j + 1;
dump_int(k - l);
} while (k <= eqtb_size);
- if (hash_high > 0)
- dump_things(eqtb[eqtb_size + 1], hash_high); /* dump |hash_extra| part */
-
+ 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();
- /* 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.
+ /*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);
@@ -262,19 +276,18 @@ void store_fmt_file(void)
incr(cs_count);
}
}
- dump_things(hash[hash_used + 1],
- undefined_control_sequence - 1 - hash_used);
- if (hash_high > 0)
+ 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");
-
- /* Dump the font information */
+ /*tex Dump the font information. */
dump_int(max_font_id());
for (k = 0; k <= max_font_id(); k++) {
- /* Dump the array info for internal font number |k| */
+ /*tex Dump the array info for internal font number |k|. */
dump_font(k);
tprint_nl("\\font");
print_esc(font_id_text(k));
@@ -293,73 +306,78 @@ void store_fmt_file(void)
if (max_font_id() != 1)
print_char('s');
dump_math_data();
-
- /* Dump the hyphenation tables */
+ /*tex Dump the hyphenation tables. */
dump_language_data();
-
- /* Dump a couple more things and the closing check word */
+ /*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);
- /* We have already printed a lot of statistics, so we set |tracing_stats:=0|
- to prevent them from appearing again. */
+ /*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;
-
- /* Dump the lua bytecodes */
+ /*tex Dump the \LUA\ bytecodes. */
dump_luac_registers();
-
- /* Close the format file */
+ /*tex Close the format file. */
zwclose(fmt_file);
}
-@ 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.
-
-@c
-#define too_small(A) do { \
- wake_up_terminal(); \
- wterm_cr(); \
- fprintf(term_out,"---! Must increase the %s",(A)); \
- goto BAD_FMT; \
- } while (0)
-
-@ 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|.
-
-@c
-#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)
-
-
-@ @c
+/*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; /* all-purpose indices */
- halfword p; /* all-purpose pointer */
- int x; /* something undumped */
+ int j, k, x;
+ halfword p;
char *format_engine;
- /* Undump constants for consistency check */
+ /*tex Undump constants for consistency check .*/
if (ini_version) {
libcfree(hash);
libcfree(eqtb);
@@ -368,22 +386,25 @@ boolean load_fmt_file(const char *fmtname)
}
undump_int(x);
format_debug("format magic number", x);
- if (x != 0x57325458)
- goto BAD_FMT; /* not a format file */
-
+ 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)
- goto BAD_FMT; /* FORMAT_ID mismatch */
-
+ 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))
- goto BAD_FMT; /* corrupted format file */
-
+ 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; /* force string termination, just in case */
+ format_engine[x - 1] = 0;
if (strcmp(engine_name, format_engine)) {
wake_up_terminal();
wterm_cr();
@@ -394,7 +415,7 @@ boolean load_fmt_file(const char *fmtname)
xfree(format_engine);
undump_int(x);
format_debug("string pool checksum", x);
- if (x != 0x57325458) { /* todo: @@\$ *//* check that strings are the same */
+ if (x != 0x57325458) {
wake_up_terminal();
wterm_cr();
fprintf(term_out, "---! %s was written by a different version",
@@ -403,7 +424,7 @@ boolean load_fmt_file(const char *fmtname)
}
undump_int(x);
if (x != max_halfword)
- goto BAD_FMT; /* check |max_halfword| */
+ goto BAD_FMT;
undump_int(hash_high);
if ((hash_high < 0) || (hash_high > sup_hash_extra))
goto BAD_FMT;
@@ -428,10 +449,9 @@ boolean load_fmt_file(const char *fmtname)
undump_int(x);
if (x != hash_prime)
goto BAD_FMT;
-
- /* Undump the string pool */
+ /*tex Undump the string pool */
str_ptr = undump_string_pool();
- /* Undump the dynamic memory */
+ /*tex Undump the dynamic memory */
undump_node_mem();
undump_int(temp_token_head);
undump_int(hold_token_head);
@@ -447,9 +467,7 @@ boolean load_fmt_file(const char *fmtname)
undump_int(avail);
undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1);
undump_int(dyn_used);
-
- /* Undump the table of equivalents */
- /* Undump regions 1 to 6 of |eqtb| */
+ /*tex Undump regions 1 to 6 of the table of equivalents |eqtb|. */
k = null_cs;
do {
undump_int(x);
@@ -464,15 +482,16 @@ boolean load_fmt_file(const char *fmtname)
eqtb[j] = eqtb[k - 1];
k = k + x;
} while (k <= eqtb_size);
- if (hash_high > 0) /* undump |hash_extra| part */
+ 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();
- /* Undump the hash table */
+ /*tex Undump the hash table */
undump_primitives();
undump(hash_base, frozen_control_sequence, hash_used);
p = hash_base - 1;
@@ -480,8 +499,7 @@ boolean load_fmt_file(const char *fmtname)
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);
+ 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) {
@@ -490,20 +508,17 @@ boolean load_fmt_file(const char *fmtname)
print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1));
}
undump_int(cs_count);
-
- /* Undump the font information */
+ /*tex Undump the font information */
undump_int(x);
set_max_font_id(x);
for (k = 0; k <= max_font_id(); k++) {
- /* Undump the array info for internal font number |k| */
+ /*tex Undump the array info for internal font number |k| */
undump_font(k);
}
undump_math_data();
-
- /* Undump the hyphenation tables */
+ /*tex Undump the hyphenation tables */
undump_language_data();
-
- /* Undump a couple more things and the closing check word */
+ /*tex Undump a couple more things and the closing check word */
undump(batch_mode, error_stop_mode, interaction);
if (interactionoption != unspecified_mode)
interaction = interactionoption;
@@ -512,12 +527,10 @@ boolean load_fmt_file(const char *fmtname)
undump_int(x);
if (x != 69069)
goto BAD_FMT;
-
- /* Undump the lua bytecodes */
+ /*tex Undump the lua bytecodes. */
undump_luac_registers();
-
prev_depth_par = ignore_depth;
- return true; /* it worked! */
+ return true;
BAD_FMT:
wake_up_terminal();
wterm_cr();
diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.w b/Build/source/texk/web2c/luatexdir/tex/equivalents.c
index 271fc67ffbd..3d260d5c5ee 100644
--- a/Build/source/texk/web2c/luatexdir/tex/equivalents.w
+++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.c
@@ -1,29 +1,31 @@
-% 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/>.
-
-@ @c
+/*
+
+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;
-/* |eqtb[p]| has just been restored or retained */
+/*tex |eqtb[p]| has just been restored or retained. */
static void diagnostic_trace(halfword p, const char *s)
{
@@ -36,60 +38,54 @@ static void diagnostic_trace(halfword p, const char *s)
end_diagnostic(false);
}
-@ @c
-void show_eqtb_meaning(halfword n); /* forward */
+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.
-@ 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|:
-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.
-\yskip\hang 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.
-\yskip\hang 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.
-\yskip\hang 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.
-\yskip\hang 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.
-\yskip\hang 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.
-\yskip\hang 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.
-\yskip\noindent Note that, for example, the current amount of
-baselineskip glue is determined by the setting of a particular location
-in region~3 of |eqtb|, while the current meaning of the control sequence
-`\.{\\baselineskip}' (which might have been changed by \.{\\def} or
-\.{\\let}) appears in region~2.
+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|.
-@ 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|.
+*/
-@c
memory_word *eqtb;
-halfword eqtb_top; /* maximum of the |eqtb| */
+halfword eqtb_top;
quarterword xeq_level[(eqtb_size + 1)];
-@ @c
void initialize_equivalents(void)
{
int k;
@@ -97,48 +93,45 @@ void initialize_equivalents(void)
xeq_level[k] = level_one;
}
-@ 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:
-
-\yskip\hang 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)|).
-
-\yskip\hang 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]|.
-
-\yskip\hang 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.
-
-\yskip\hang 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:
+/*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,
@@ -154,32 +147,34 @@ an explicit |save_type|, and they are:
|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|.
+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|.
+
+*/
-@c
save_record *save_stack;
-int save_ptr; /* first unused entry on |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 */
-@ 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|.
+/*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.
-@ 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.
+*/
-@c
#define check_full_save_stack() do { \
if (save_ptr>max_save_stack) { \
max_save_stack=save_ptr; \
@@ -188,30 +183,32 @@ get by with testing for overflow in only a few places.
} \
} while (0)
-@ 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|.
+/*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.
+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.
+
+*/
-@c
void new_save_level(group_code c)
-{ /* begin a new level of grouping */
+{ /*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)
+ if (cur_level == max_quarterword) {
overflow("grouping levels", max_quarterword - min_quarterword);
- /* quit if |(cur_level+1)| is too big to be stored in |eqtb| */
+ }
+ /*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)
@@ -220,7 +217,6 @@ void new_save_level(group_code c)
incr(save_ptr);
}
-@ @c
static const char *save_stack_type(int v)
{
const char *s = "";
@@ -243,13 +239,13 @@ static const char *save_stack_type(int v)
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;
}
-@ @c
void print_save_stack(void)
{
int i;
@@ -274,7 +270,7 @@ void print_save_stack(void)
print_int(save_word(i - 1).cint);
} else {
print_int(eq_type_field(save_word(i - 1)));
- print_char(','); /* |print_int(eq_level_field(save_word(i-1)));| */
+ print_char(',');
print_int(equiv_field(save_word(i - 1)));
}
i--;
@@ -303,17 +299,21 @@ void print_save_stack(void)
break;
case saved_adjust:
tprint(", ");
- print_int(save_level(i)); /* vadjust vs vadjust pre */
+ /*tex vadjust vs vadjust pre */
+ print_int(save_level(i));
break;
case saved_insert:
tprint(", ");
- print_int(save_value(i)); /* insert number */
+ /*tex insert number */
+ print_int(save_value(i));
break;
- case saved_boxtype: /* \.{\\localleftbox} vs \.{\\localrightbox} */
+ case saved_boxtype:
+ /*tex \.{\\localleftbox} vs \.{\\localrightbox} */
tprint(", ");
print_int(save_value(i));
break;
- case saved_eqno: /* \.{\\eqno} vs \.{\\leqno} */
+ case saved_eqno:
+ /*tex \.{\\eqno} vs \.{\\leqno} */
tprint(", ");
print_int(save_value(i));
break;
@@ -339,10 +339,11 @@ void print_save_stack(void)
case saved_textdir:
case saved_boxdir:
tprint(", ");
- print_dir(dir_dir(save_value(i)));
+ print_dir_text(save_value(i));
break;
case saved_boxattr:
case saved_boxpack:
+ case saved_attrlist:
tprint(", ");
print_int(save_value(i));
break;
@@ -357,27 +358,28 @@ void print_save_stack(void)
end_diagnostic(true);
}
-@ The \.{\\showgroups} command displays all currently active grouping
- levels.
+/*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@>
-@ 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.
- 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.
+*/
-@c
void show_save_groups(void)
{
- int p = nest_ptr; /* index into |nest| */
- int m; /* mode */
- save_pointer v = save_ptr; /* saved value of |save_ptr| */
- quarterword l = cur_level; /* saved value of |cur_level| */
- group_code c = cur_group; /* saved value of |cur_group| */
- int a = 1; /* to keep track of alignments */
+ 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;
@@ -447,16 +449,21 @@ void show_save_groups(void)
break;
case disc_group:
tprint_esc("discretionary");
- for (i = 1; i < 3; i++)
- if (i <= saved_value(-2))
+ 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++)
- if (i <= saved_value(-3)) /* different offset because |-2==saved_textdir| */
+ 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:
@@ -500,7 +507,7 @@ void show_save_groups(void)
confusion("showgroups");
break;
}
- /* Show the box context */
+ /*tex Show the box context */
i = saved_value(-5);
if (i != 0) {
if (i < box_flag) {
@@ -528,9 +535,8 @@ void show_save_groups(void)
}
FOUND1:
tprint_esc(s);
- /* Show the box packaging info */
+ /*tex Show the box packaging info. The offsets may vary. */
{
- /* offsets may vary */
int ii = -1;
while (saved_type(ii) != saved_boxspec)
ii--;
@@ -558,18 +564,20 @@ void show_save_groups(void)
cur_group = c;
}
-@ 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@>
+/*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@>
-@c
-/* we don't need to destroy when an assignment has the same node */
+We don't need to destroy when an assignment has the same node:
+
+*/
void eq_destroy(memory_word w)
-{ /* gets ready to forget |w| */
- halfword q; /* |equiv| field of |w| */
+{
+ halfword q;
switch (eq_type_field(w)) {
case call_cmd:
case long_call_cmd:
@@ -581,10 +589,16 @@ void eq_destroy(memory_word w)
flush_node(equiv_field(w));
break;
case shape_ref_cmd:
- q = equiv_field(w); /* we need to free a \.{\\parshape} block */
- if (q != null)
+ 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; /* such a block is |2n+1| words long, where |n=vinfo(q)| */
+ }
+ break;
case box_ref_cmd:
flush_node_list(equiv_field(w));
break;
@@ -593,12 +607,14 @@ void eq_destroy(memory_word w)
}
}
-@ To save a value of |eqtb[p]| that was established at level |l|, we
-can use the following subroutine.
+/*tex
-@c
+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)
-{ /* saves |eqtb[p]| */
+{
check_full_save_stack();
if (l == level_zero) {
save_type(save_ptr) = restore_zero;
@@ -613,16 +629,17 @@ void eq_save(halfword p, quarterword l)
incr(save_ptr);
}
-@ The procedure |eq_define| defines an |eqtb| entry having specified
-|eq_type| and |equiv| fields, and saves the former value if appropriate.
-This procedure is used only for entries in the first four regions of |eqtb|,
-i.e., only for entries that have |eq_type| and |equiv| fields.
-After calling this routine, it is safe to put four more entries on
-|save_stack|, provided that there was room for four more entries before
-the call, since |eq_save| makes the necessary test.
+/*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.
+
+*/
-@ new data for |eqtb|
-@c
void eq_define(halfword p, quarterword t, halfword e)
{
boolean trace = tracing_assigns_par > 0;
@@ -645,11 +662,14 @@ void eq_define(halfword p, quarterword t, halfword e)
diagnostic_trace(p, "into");
}
-@ The counterpart of |eq_define| for the remaining (fullword) positions in
-|eqtb| is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all
-|p|, a `|restore_zero|' will never be used in this case.
+/*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.
+
+*/
-@c
void eq_word_define(halfword p, int w)
{
boolean trace = tracing_assigns_par > 0;
@@ -669,15 +689,17 @@ void eq_word_define(halfword p, int w)
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|.
+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|.
+
+*/
-@c
void geq_define(halfword p, quarterword t, halfword e)
-{ /* global |eq_define| */
+{
boolean trace = tracing_assigns_par > 0;
if (trace)
diagnostic_trace(p, "globally changing");
@@ -690,7 +712,7 @@ void geq_define(halfword p, quarterword t, halfword e)
}
void geq_word_define(halfword p, int w)
-{ /* global |eq_word_define| */
+{
boolean trace = tracing_assigns_par > 0;
if (trace)
diagnostic_trace(p, "globally changing");
@@ -700,9 +722,12 @@ void geq_word_define(halfword p, int w)
diagnostic_trace(p, "into");
}
-@ Subroutine |save_for_after| puts a token on the stack for save-keeping.
+/*tex
+
+Subroutine |save_for_after| puts a token on the stack for save-keeping.
+
+*/
-@c
void save_for_after(halfword t)
{
if (cur_level > level_one) {
@@ -714,16 +739,20 @@ void save_for_after(halfword t)
}
}
-@ 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.
+/*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.
+
+*/
-@c
void unsave(void)
-{ /* pops the top level off the save stack */
- halfword p; /* position to be restored */
- quarterword l = level_one; /* saved level, if in fullword regions of |eqtb| */
- boolean a = false; /* have we already processed an \.{\\aftergroup} ? */
+{
+ 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);
@@ -731,7 +760,7 @@ void unsave(void)
if (cur_level > level_one) {
boolean trace = tracing_restores_par > 0;
decr(cur_level);
- /* Clear off top level from |save_stack| */
+ /*tex Clear off top level from |save_stack|. */
while (true) {
decr(save_ptr);
if (save_type(save_ptr) == level_boundary)
@@ -739,7 +768,8 @@ void unsave(void)
p = save_value(save_ptr);
if (save_type(save_ptr) == insert_token) {
reinsert_token(a, p);
- a = true; /* always ... always etex now */
+ /*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);
@@ -747,21 +777,25 @@ void unsave(void)
} else {
save_word(save_ptr) = eqtb[undefined_control_sequence];
}
- /* 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.
- */
+ /*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) {
- eq_destroy(save_word(save_ptr)); /* destroy the saved value */
+ /*tex Destroy the saved value: */
+ eq_destroy(save_word(save_ptr));
if (trace)
diagnostic_trace(p, "retaining");
} else {
- eq_destroy(eqtb[p]); /* destroy the current value */
- eqtb[p] = save_word(save_ptr); /* restore the saved value */
+ /*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");
}
@@ -776,32 +810,40 @@ void unsave(void)
}
}
}
- if (tracing_groups_par > 0)
+
+ if (tracing_groups_par > 0) {
group_trace(true);
- if (grp_stack[in_open] == cur_boundary)
- group_warning(); /* groups possibly not properly nested with files */
+ }
+ 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 {
- confusion("curlevel"); /* |unsave| is not used when |cur_group=bottom_level| */
+ /*tex |unsave| is not used when |cur_group=bottom_level| */
+ confusion("curlevel");
}
attr_list_cache = cache_disabled;
}
-@ 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.
+/*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.
-@c
-int mag_set = 0; /* if nonzero, this magnification should be used henceforth */
+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.
-@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag|
-for magnification.
+*/
+
+int mag_set = 0;
-@c
void prepare_mag(void)
{
if ((mag_set > 0) && (mag_par != mag_set)) {
@@ -809,14 +851,18 @@ void prepare_mag(void)
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.");
+ 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.");
+ help1(
+ "The magnification ratio must be between 1 and 32768."
+ );
int_error(mag_par);
geq_word_define(int_base + mag_code, 1000);
}
@@ -829,65 +875,74 @@ void prepare_mag(void)
mag_set = mag_par;
}
-@ 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.
-
-@c
-int cur_cmd; /* current command set by |get_next| */
-halfword cur_chr; /* operand of current command */
-halfword cur_cs; /* control sequence found here, zero if none found */
-halfword cur_tok; /* packed representative of |cur_cmd| and |cur_chr| */
-
-@ Here is a procedure that displays the current command.
-
-@c
+/*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; /* level of \.{\\if...\\fi} nesting */
- int l; /* line where \.{\\if} started */
+ int n, l;
halfword p;
begin_diagnostic();
tprint_nl("{");
@@ -926,23 +981,26 @@ void show_cur_cmd_chr(void)
end_diagnostic(false);
}
-@ Here is a procedure that displays the contents of |eqtb[n]| symbolically.
+/*tex
+
+Here is a procedure that displays the contents of |eqtb[n]| symbolically.
+
+*/
-@c
void show_eqtb(halfword n)
{
if (n < null_cs) {
- /* this can't happen */
- print_char('?');
+ /*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))) {
- /*
- Show equivalent |n|, in region 1 or 2
-
- 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.)
+ /*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));
@@ -951,10 +1009,9 @@ void show_eqtb(halfword n)
show_token_list(token_link(equiv(n)), null, 32);
}
} else if (n < local_base) {
- /*
- Show equivalent |n|, in region 3
-
- All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'.
+ /*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)
@@ -979,18 +1036,17 @@ void show_eqtb(halfword n)
}
} else if (n < int_base) {
- /*
- 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.
+ /*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)
@@ -1032,13 +1088,16 @@ void show_eqtb(halfword n)
show_node_list(equiv(n));
}
} else if (n == cur_font_loc) {
- /* Show the font identifier in |eqtb[n]| */
+ /*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); /* that's |font_id_text(equiv(n))| */
+ print_esc(hash[font_id_base + equiv(n)].rh);
}
} else if (n < dimen_base) {
- /* Show equivalent |n|, in region 5 */
+ /*tex Show equivalent |n| in region 5: */
if (n < dir_base) {
print_cmd_chr(assign_int_cmd, n);
print_char('=');
@@ -1046,7 +1105,7 @@ void show_eqtb(halfword n)
} else if (n < count_base) {
print_cmd_chr(assign_dir_cmd, n);
print_char(' ');
- print_dir(eqtb[n].cint);
+ print_dir_par(eqtb[n].cint);
} else if (n < attribute_base) {
tprint_esc("count");
print_int(n - count_base);
@@ -1059,7 +1118,7 @@ void show_eqtb(halfword n)
print_int(eqtb[n].cint);
}
} else if (n <= eqtb_size) {
- /* Show equivalent |n|, in region 6 */
+ /*tex Show equivalent |n| in region 6: */
if (n < scaled_base) {
print_cmd_chr(assign_dimen_cmd, n);
} else {
@@ -1070,31 +1129,28 @@ void show_eqtb(halfword n)
print_scaled(eqtb[n].cint);
tprint("pt");
} else {
- /* this can't happen either */
- print_char('?');
+ /*tex This can't happen unless you messed up at the \LUA\ end. */
+ tprint("? bad token, case 2: ");
+ print_int(n);
}
}
-@ @c
void show_eqtb_meaning(halfword n)
{
if (n < null_cs) {
- /* this can't happen */
+ /*tex This can't happen. */
print_char('?');
} else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) {
- /*
- Show equivalent |n|, in region 1 or 2
-
- 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.)
+ /*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}'.
+ 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)
@@ -1108,20 +1164,16 @@ void show_eqtb_meaning(halfword n)
tprint_esc("muskip");
print_int(n - mu_skip_base);
}
-
} else if (n < int_base) {
- /*
- 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.
+ /*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)
@@ -1137,11 +1189,11 @@ void show_eqtb_meaning(halfword n)
tprint_esc("box");
print_int(n - box_base);
} else if (n == cur_font_loc) {
- /* Show the font identifier in |eqtb[n]| */
+ /*tex Show the font identifier in |eqtb[n]|. */
tprint("current font");
}
} else if (n < dimen_base) {
- /* Show equivalent |n|, in region 5 */
+ /*tex Show equivalent |n| in region 5. */
if (n < dir_base) {
print_cmd_chr(assign_int_cmd, n);
} else if (n < count_base) {
@@ -1154,7 +1206,7 @@ void show_eqtb_meaning(halfword n)
print_int(n - attribute_base);
}
} else if (n <= eqtb_size) {
- /* Show equivalent |n|, in region 6 */
+ /*tex Show equivalent |n| in region 6. */
if (n < scaled_base) {
print_cmd_chr(assign_dimen_cmd, n);
} else {
@@ -1162,7 +1214,7 @@ void show_eqtb_meaning(halfword n)
print_int(n - scaled_base);
}
} else {
- /* this can't happen either */
+ /*tex This can't happen either. */
print_char('?');
}
}
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..b630422ee98
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/errors.c
@@ -0,0 +1,1032 @@
+/*
+
+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, *fullcmd;
+ 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);
+ }
+#endif
+ fullcmd = command;
+ /*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:
+ 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.
+
+*/
+
+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;
+ }
+}
+
+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) {
+ 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.w b/Build/source/texk/web2c/luatexdir/tex/errors.w
deleted file mode 100644
index 93dbe141b91..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/errors.w
+++ /dev/null
@@ -1,972 +0,0 @@
-% 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/>.
-
-@ TODO: we need to use a formatted normal_error in:
-
-hyphen.w
-luafflib.c
-
-@ @c
-#include "ptexlib.h"
-#define edit_var "TEXEDIT"
-
-@ 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@>
-
-@c
-int interaction; /* current level of interaction */
-int interactionoption; /* set from command line */
-
-/* ls-hh: so, new code only kicks in when we have a callback defined */
-
-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\ is careful not to call |error| when the print |selector| setting
-might be unusual. The only possible values of |selector| at the time of
-error messages are
-
-\yskip
-\hang|no_print| (when |interaction=batch_mode| and |log_file| not yet open);
-
-\hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
-
-\hang|log_only| (when |interaction=batch_mode| and |log_file| is open);
-
-\hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
-
-@c
-void fixup_selector(boolean logopened)
-{
- if (interaction == batch_mode)
- selector = no_print;
- else
- selector = term_only;
- if (logopened)
- selector = selector + 2;
-}
-
-@ 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.
-
-@c
-boolean deletions_allowed; /* is it safe for |error| to call |get_token|? */
-boolean set_box_allowed; /* is it safe to do a \.{\\setbox} assignment? */
-int history; /* has the source input been clean so far? */
-int error_count; /* the number of scrolled errors since the last paragraph ended */
-int interrupt; /* should \TeX\ pause for instructions? */
-boolean OK_to_interrupt; /* should interrupts be observed? */
-
-@ The value of |history| is initially |fatal_error_stop|, but it will
-be changed to |spotless| if \TeX\ survives the initialization process.
-
-@c
-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;
- /* |history| is initialized elsewhere */
-}
-
-@ 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|.
-
-@c
-const char *help_line[7]; /* helps for the next |error| */
-boolean use_err_help; /* should the |err_help| list be shown? */
-
-@ 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.
-
-@c
-int defaultexitcode = 0; /* the exit code can be overloaded */
-
-__attribute__ ((noreturn))
-void do_final_end(void)
-{
- update_terminal();
- ready_already = 0;
- lua_close(Luas); /* new per 0.99 */
- if ((history != spotless) && (history != warning_issued))
- uexit(1);
- else
- uexit(defaultexitcode);
-}
-
-__attribute__ ((noreturn))
-void jump_out(void)
-{
- close_files_and_terminate();
- do_final_end();
-}
-@ 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.
-
-@c
-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, *fullcmd;
- 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;
-
- /* Close any open input files, since we're going to kill the job. */
- close_files_and_terminate();
-
- /* 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;
-
- /* Construct the command string. The `11' is the maximum length an
- integer might be. */
- command = xmalloc (strlen (edit_value) + fnlength + 11);
-
- /* 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++ = '%';
- /* 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
-#endif
- fullcmd = command;
-
- /* Execute the command. */
- if (system (fullcmd) != 0)
- fprintf (stderr, "! Trouble executing `%s'.\n", command);
-
- /* Quit, since we found an error. */
- do_final_end ();
-}
-
-
-@ @c
-void error(void)
-{ /* completes the job of error reporting */
- ASCII_code c; /* what the user types */
- int callback_id;
- int s1, s2, s3, s4; /* used to save global variables when deleting tokens */
- int i;
- flush_err(); /* hh-ls */
- 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) {
- /* Get user's advice and |return| */
- while (1) {
- CONTINUE:
- clear_for_error_prompt();
- prompt_input("? ");
- if (last == first)
- return;
- c = buffer[first];
- if (c >= 'a')
- c = c + 'A' - 'a'; /* convert to uppercase */
- /* Interpret code |c| and |return| if done */
-
- /* It is desirable to provide an `\.E' option here that gives the user
- an easy way to return from \TeX\ to the system editor, with the offending
- line ready to be edited. But such an extension requires some system
- wizardry, so the present implementation simply types out the name of the
- file that should be edited and the relevant line number.
-
- There is a secret `\.D' option available when the debugging routines haven't
- been commented~out. */
-
- switch (c) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- if (deletions_allowed) {
- /* Delete |c-"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) {
- get_token(); /* one-level recursive call of |error| is possible */
- decr(c);
- }
- cur_tok = s1;
- cur_cmd = s2;
- cur_chr = s3;
- align_state = s4;
- OK_to_interrupt = true;
- help2("I have just deleted some text, as you asked.",
- "You can now delete more, or insert, or whatever.");
- show_context();
- goto CONTINUE;
- }
- break;
-#ifdef DEBUG
- case 'D':
- debug_help();
- goto CONTINUE;
- break;
-#endif
- 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);
- jump_out(); /* should not be reached */
- } 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 {
- tprint_nl("There is no valid callback defined.");
- jump_out(); /* should not be reached */
- }
- }
- }
- break;
- case 'H':
- /* 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':
- /* 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. */
-
- begin_file_reading(); /* enter a new syntactic level for terminal input */
- /* now |state=mid_line|, so an initial blank space will count as a blank */
- if (last > first + 1) {
- iloc = first + 1;
- buffer[first] = ' ';
- } else {
- prompt_input("insert>");
- iloc = first;
- }
- first = last;
- ilimit = last - 1; /* no |end_line_char| ends this line */
- return;
- break;
- case 'Q':
- case 'R':
- case 'S':
- /* 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();
- }
- /* Put help message on the transcript file */
- if (interaction > batch_mode)
- decr(selector); /* avoid terminal output */
- if (use_err_help) {
- print_ln();
- give_err_help();
- } else {
- int i1 = 0;
- while (help_line[i1] != NULL)
- tprint_nl(help_line[i1++]);
- }
- print_ln();
- if (interaction > batch_mode)
- incr(selector); /* re-enable terminal output */
- print_ln();
-}
-
-@ A dozen or so error messages end with a parenthesized integer, so we
-save a teeny bit of program space by declaring the following procedure:
-
-@c
-void int_error(int n)
-{
- tprint(" (");
- print_int(n);
- print_char(')');
- error();
-}
-
-@ In anomalous cases, the print selector might be in an unknown state;
-the following subroutine is called to fix things just enough to keep
-running a bit longer.
-
-@c
-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);
-}
-
-@ The following procedure prints \TeX's last words before dying
-@c
-void succumb(void)
-{
- if (interaction == error_stop_mode)
- interaction = scroll_mode; /* no more interaction */
- if (log_opened_global)
- error();
-#ifdef DEBUG
- if (interaction > batch_mode)
- debug_help();
-#endif
- history = fatal_error_stop;
- jump_out(); /* irrecoverable error */
-}
-
-@ @c
-void fatal_error(const char *s)
-{ /* prints |s|, and that's it */
- normalize_selector();
- print_err("Emergency stop");
- help1(s);
- succumb();
-}
-
-@ Here is the most dreaded error message
-@c
-void overflow(const char *s, unsigned int n)
-{ /* stop due to finiteness */
- 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();
-}
-
-@ 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@>
-
-@c
-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 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();
-}
-
-@ 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@>
-
-@c
-void check_interrupt(void)
-{
- if (interrupt != 0)
- pause_for_instructions();
-}
-
-@ 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.
-
-@c
-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;
- }
-}
-
-@ @c
-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();
-}
-
-@ 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.
-
-@c
-void back_error(void)
-{ /* back up one token and call |error| */
- OK_to_interrupt = false;
- back_input();
- OK_to_interrupt = true;
- error();
-}
-
-@ @c
-void ins_error(void)
-{ /* back up one inserted token and call |error| */
- OK_to_interrupt = false;
- back_input();
- token_type = inserted;
- OK_to_interrupt = true;
- error();
-}
-
-@ 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.
-
-@c
-void char_warning(internal_font_number f, int c)
-{
- int old_setting; /* saved value of |tracing_online| */
- int k; /* index to current digit; we assume that $0\L n<16^{22}$ */
- 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;
- }
-}
-
-@ @c
-
-void wrapup_backend(void) {
- ensure_output_state(static_pdf, ST_OMODE_FIX);
- switch (output_mode_used) {
- case OMODE_NONE:
- print_err(" ==> Fatal error occurred, no FMT file produced!");
- break;
- case OMODE_PDF:
- if (history == fatal_error_stop) {
- remove_pdffile(static_pdf); /* will become remove_output_file */
- print_err(" ==> Fatal error occurred, no output PDF file produced!");
- } else {
- finish_pdf_file(static_pdf, luatex_version, get_luatexrevision());
- }
- break;
- case OMODE_DVI:
- if (history == fatal_error_stop) {
- print_err(" ==> Fatal error occurred, bad output DVI file produced!");
- finish_dvi_file(static_pdf, luatex_version, get_luatexrevision());
- } else {
- finish_dvi_file(static_pdf, luatex_version, get_luatexrevision());
- }
- break;
- }
-}
-
-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_error(const char *t, const char *p)
-{
- normalize_selector();
- if (interaction == error_stop_mode) {
- wake_up_terminal();
- }
- tprint("error : ");
- if (p != NULL)
- tprint(p);
- history = fatal_error_stop;
- wrapup_backend();
- exit(EXIT_FAILURE);
-}
-*/
-
-@ @c
-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) {
- 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) {
- /* free 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;
- }
-}
-
-@ @c
-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/expand.c b/Build/source/texk/web2c/luatexdir/tex/expand.c
new file mode 100644
index 00000000000..a0a317db238
--- /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 combine_toks_cmd:
+ combine_the_toks(cur_chr);
+ 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_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_nl(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.w b/Build/source/texk/web2c/luatexdir/tex/expand.w
deleted file mode 100644
index c912863b2a1..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/expand.w
+++ /dev/null
@@ -1,829 +0,0 @@
-% expand.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/>.
-
-@ @c
-
-
-#include "ptexlib.h"
-
-@ Only a dozen or so command codes |>max_command| can possibly be returned by
-|get_next|; in increasing order, they are |undefined_cs|, |expand_after|,
-|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|,
-|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and
-|end_template|.{\emergencystretch=40pt\par}
-
-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.
-@^system dependencies@>
-
-@c
-static int expand_depth_count = 0;
-
-
-@ The |expand| subroutine is used when |cur_cmd>max_command|. It removes a
-``call'' or a conditional or one of the other special operations just
-listed. It follows that |expand| might invoke itself recursively. In all
-cases, |expand| destroys the current token, but it sets things up so that
-the next |get_next| will deliver the appropriate next token. The value of
-|cur_tok| need not be known when |expand| is called.
-
-Since several of the basic scanning routines communicate via global variables,
-their values are saved as local variables of |expand| so that
-recursive calls don't invalidate them.
-@^recursion@>
-
-@c
-int is_in_csname = 0;
-
-@ @c
-void expand(void)
-{
- halfword t; /* token that is being ``expanded after'' */
- halfword p; /* for list manipulation */
- halfword cur_ptr; /* for a local token list pointer */
- int cv_backup; /* to save the global quantity |cur_val| */
- int cvl_backup, radix_backup, co_backup; /* to save |cur_val_level|, etc. */
- halfword backup_backup; /* to save |link(backup_head)| */
- int save_scanner_status; /* temporary storage of |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) {
- /* Expand a nonmacro */
- if (tracing_commands_par > 1)
- show_cur_cmd_chr();
- switch (cur_cmd) {
- case top_bot_mark_cmd:
- /* 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) {
- /* 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 { /* \\unless */
- /* 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) {
- /* 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(); /* 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 {
- /* Implement \.{\\primitive} */
- /*
- 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.
-
- In a next version, 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(); /* 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:
- /* 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(); /* this procedure is discussed in Part 27 below */
- break;
- case the_cmd:
- ins_the_toks(); /* this procedure is discussed in Part 27 below */
- break;
- case combine_toks_cmd:
- combine_the_toks(cur_chr);
- break;
- case if_test_cmd:
- conditional(); /* this procedure is discussed in Part 28 below */
- break;
- case fi_or_else_cmd:
- /* 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) {
- insert_relax(); /* condition not yet evaluated */
- } 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)
- pass_text(); /* skip to \.{\\fi} */
- pop_condition_stack();
- }
-
- break;
- case input_cmd:
- /* 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 variable_cmd:
- do_variable();
- break;
- case feedback_cmd:
- do_feedback();
- break;
- default:
- /* 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 {
- /* 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);
-}
-
-@ @c
-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();
-}
-
-@ @c
-void manufacture_csname(boolean use)
-{
- halfword p, q, r;
- lstring *ss;
- r = get_avail();
- p = r; /* head of the list of characters */
- 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) {
- /* Complain about missing \.{\\endcsname} */
- complain_missing_csname();
- }
- /* 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) {
- /* skip */
- } else if (eq_type(cur_cs) == undefined_cs_cmd) {
- /* 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 {
- cur_cs = null_cs; /* the list is empty */
- }
- last_cs_name = cur_cs ;
- free_lstring(ss);
- flush_list(r);
- if (eq_type(cur_cs) == undefined_cs_cmd) {
- eq_define(cur_cs, relax_cmd, too_big_char); /* N.B.: The |save_stack| might change */
- }; /* 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();
- }
-}
-
-@ Sometimes the expansion looks too far ahead, so we want to insert
-a harmless \.{\\relax} into the user's input.
-
-@c
-void insert_relax(void)
-{
- cur_tok = cs_token_flag + cur_cs;
- back_input();
- cur_tok = cs_token_flag + frozen_relax;
- back_input();
- token_type = inserted;
-}
-
-
-@ Here is a recursive procedure that is \TeX's usual way to get the
-next token of input. It has been slightly optimized to take account of
-common cases.
-
-@c
-void get_x_token(void)
-{ /* 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;
- goto DONE; /* |cur_chr=null_list| */
- }
- } 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;
-}
-
-
-@ The |get_x_token| procedure is equivalent to two consecutive
-procedure calls: |get_next; x_token|.
-
-@c
-void x_token(void)
-{ /* |get_x_token| without the initial |get_next| */
- 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;
-}
-
-
-@ 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|.
-
-@c
-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;
-
-@ @c
-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;
- }
-}
-
-
-@ 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.
-
-@c
-int long_state; /* governs the acceptance of \.{\\par} */
-
-@ The parameters, if any, must be scanned before the macro is expanded.
-Parameters are token lists without reference counts. They are placed on
-an auxiliary stack called |pstack| while they are being scanned, since
-the |param_stack| may be losing entries during the matching process.
-(Note that |param_stack| can't be gaining entries, since |macro_call| is
-the only routine that puts anything onto |param_stack|, and it
-is not recursive.)
-
-@c
-halfword pstack[9]; /* arguments supplied to a macro */
-
-
-@ After parameter scanning is complete, the parameters are moved to the
-|param_stack|. Then the macro body is fed to the scanner; in other words,
-|macro_call| places the defined text of the control sequence at the
-top of\/ \TeX's input stack, so that |get_next| will proceed to read it
-next.
-
-The global variable |cur_cs| contains the |eqtb| address of the control sequence
-being expanded, when |macro_call| begins. If this control sequence has not been
-declared \.{\\long}, i.e., if its command code in the |eq_type| field is
-not |long_call| or |long_outer_call|, its parameters are not allowed to contain
-the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro
-call is aborted, and the \.{\\par} will be rescanned.
-
-@c
-void macro_call(void)
-{ /* invokes a user-defined control sequence */
- halfword r; /* current node in the macro's token list */
- halfword p = null; /* current node in parameter token list being built */
- halfword q; /* new node being put into the token list */
- halfword s; /* backup pointer for parameter matching */
- halfword t; /* cycle pointer for backup recovery */
- halfword u, v; /* auxiliary pointers for backup recovery */
- halfword rbrace_ptr = null; /* one step before the last |right_brace| token */
- int n = 0; /* the number of parameters scanned */
- halfword unbalance; /* unmatched left braces in current parameter */
- halfword m = 0; /* the number of tokens or groups (usually) */
- halfword ref_count; /* start of the token list */
- int save_scanner_status = scanner_status; /* |scanner_status| upon entry */
- halfword save_warning_index = warning_index; /* |warning_index| upon entry */
- int match_chr = 0; /* character used in parameter */
- warning_index = cur_cs;
- ref_count = cur_chr;
- r = token_link(ref_count);
- if (tracing_macros_par > 0) {
- /* Show the text of the macro being expanded */
- begin_diagnostic();
- 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) {
- /* 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;
- }
- /* 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:
- get_token(); /* set |cur_tok| to the next token of input */
- if (cur_tok == token_info(r)) {
- /* 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;
- }
-
- }
- /* 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) {
- /* 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; /* 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) {
- /* 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 {
- /* 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;
- /* a white lie; the \.{\\par} won't always trigger a runaway */
- }
- } else {
- /* Store the current token, but |goto continue| if it is
- a blank space that would become an undelimited parameter */
- if (cur_tok == space_token)
- if (token_info(r) <= end_match_token)
- if (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_nl(match_chr);
- print_int(n);
- tprint("<-");
- show_token_list(pstack[n - 1], null, 1000);
- end_diagnostic(false);
- }
-
- }
-
- /* now |info(r)| is a token whose command code is either |match| or |end_match| */
- } while (token_info(r) != end_match_token);
-
- }
- /* 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)) {
- /* 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:
- /* 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/extensions.c b/Build/source/texk/web2c/luatexdir/tex/extensions.c
new file mode 100644
index 00000000000..d5914db8a67
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/extensions.c
@@ -0,0 +1,1329 @@
+/*
+
+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("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();
+ 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 C string */
+ char *s, *ss;
+ 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_cstring(def_ref, false, NULL);
+ 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, "S->S", s, &ss);
+ if ((lua_retval == true) && (ss != NULL)) {
+ xfree(s);
+ s = ss;
+ }
+ }
+ }
+ tprint(s);
+ xfree(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.w b/Build/source/texk/web2c/luatexdir/tex/extensions.w
deleted file mode 100644
index 73dc3ba4514..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/extensions.w
+++ /dev/null
@@ -1,1211 +0,0 @@
-% extensions.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/>.
-
-\def\eTeX{e-\TeX}
-\def\pdfTeX{pdf\TeX}
-
-@ @c
-#include "ptexlib.h"
-
-@ @c
-#define mode mode_par
-#define tail tail_par
-#define head head_par
-#define dir_save dirs_par
-
-@ 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.
-@^system dependencies@>
-@^extensions to \TeX@>
-
-First let's consider the format of whatsit nodes that are used to represent
-the data associated with \.{\\write} and its relatives. Recall that a whatsit
-has |type=whatsit_node|, and the |subtype| is supposed to distinguish
-different kinds of whatsits. Each node occupies two or more words; the
-exact number is immaterial, as long as it is readily determined from the
-|subtype| or other data.
-
-We shall introduce five |subtype| values here, corresponding to the
-control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, 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|.
-
-@c
-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;
-
-@ The variable |write_loc| just introduced is used to provide an
-appropriate error message in case of ``runaway'' write texts.
-
-@c
-halfword write_loc; /* |eqtb| address of \.{\\write} */
-
-/*
- hh: eventually i'll make \pdfextension a lua token parsed function;
- a complication is that sometimes token lists are delayed
-*/
-
-@ When an |extension| command occurs in |main_control|, in any mode,
-the |do_extension| routine is called.
-
-@c
-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("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) /* 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; /* error */
- 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")) {
- /*
- 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) {
- flush_str((str_ptr - 1)); /* from |tokens_to_string| */
- 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)
-{
-}
-
-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;
- }
-}
-
-/*
-
- 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}'.
-
-*/
-
-/* 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.
-
-@c
-static void new_write_whatsit(int w, int check)
-{
- new_whatsit(cur_chr);
- if (check) {
- /* so we check with open and close */
- scan_limited_int(last_file_selector,NULL);
- } else {
- /* 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)
-{
- halfword p; /* all-purpose pointer */
- if (cur_cmd == extension_cmd) {
- /* 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();
- open_name(tail) = cur_name;
- open_area(tail) = cur_area;
- open_ext(tail) = cur_ext;
- if (immediate) {
- out_what(static_pdf, tail);
- flush_node_list(tail);
- tail = p;
- vlink(p) = null;
- }
- break;
- case write_code:
- /*
- 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) {
- out_what(static_pdf, 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) {
- out_what(static_pdf, tail);
- flush_node_list(tail);
- tail = p;
- vlink(p) = null;
- }
- break;
- case special_code:
- /*
- 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 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;
- /* 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;
- /* done */
- default:
- if (immediate) {
- back_input();
- } else {
- confusion("invalid extension");
- }
- break;
- }
- } else {
- /* no extension command, quite certainly following \immediate */
- back_input();
- }
-}
-
-@ 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.
-
-@c
-void new_whatsit(int s)
-{
- halfword p = new_node(whatsit_node, s);
- couple_nodes(tail, p);
- tail = p;
-}
-
-@ The final line of this routine is slightly subtle; at least, the author
-didn't think about it until getting burnt! There is a used-up token list
-@^Knuth, Donald Ervin@>
-on the stack, namely the one that contained |end_write_token|. (We
-insert this artificial `\.{\\endwrite}' to prevent runaways, as explained
-above.) If it were not removed, and if there were numerous writes on a
-single page, the stack would overflow.
-
-@c
-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);
- /* now we're ready to scan
- `\.\{$\langle\,$token list$\,\rangle$\.{\} \\endwrite}' */
- old_mode = mode;
- mode = 0;
- /* disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, \.{\\prevgraf} */
- cur_cs = write_loc;
- q = scan_toks(false, true); /* expand macros, etc. */
- get_token();
- if (cur_tok != end_write_token) {
- /* Recover from an unbalanced write command */
- 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;
- end_token_list(); /* conserve stack space */
-}
-
-void write_out(halfword p)
-{
- int old_setting; /* holds print |selector| */
- int j; /* write stream number */
- char *s, *ss; /* line to be written, as a C string */
- 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)) {
- /* write to the terminal if file isn't open */
- selector = log_only;
- tprint_nl("");
- } else {
- tprint_nl("");
- }
- s = tokenlist_to_cstring(def_ref, false, NULL);
- if (selector < no_print) {
- /* selector is a file */
- callback_id = callback_defined(process_output_buffer_callback);
- if (callback_id > 0) {
- /* fix up the output buffer using callbacks */
- lua_retval = run_callback(callback_id, "S->S", s, &ss);
- if ((lua_retval == true) && (ss != NULL)) {
- xfree(s);
- s = ss;
- }
- }
- }
- tprint(s);
- xfree(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;
- }
-}
-
-
-@ To implement primitives as \.{\\pdfextension info}, \.{\\pdfextension catalog} or
-\.{\\pdfextension names} we need to concatenate tokens lists.
-
-@c
-halfword concat_tokens(halfword q, halfword r)
-{ /* 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;
-}
-
-@ 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.
-
-@c
-boolean *eof_seen; /* has eof been seen? */
-
-@ The |print_group| procedure prints the current level of grouping and
-the name corresponding to |cur_group|.
-
-@c
-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;
- } /* there are no other cases */
- tprint(" group (level ");
- print_int(cur_level);
- print_char(')');
- if (saved_value(-1) != 0) { /* |saved_line| */
- if (e)
- tprint(" entered at line ");
- else
- tprint(" at line ");
- print_int(saved_value(-1)); /* |saved_line| */
- }
-}
-
-@ 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.
-
-@c
-void group_trace(boolean e)
-{
- begin_diagnostic();
- print_char('{');
- if (e)
- tprint("leaving ");
- else
- tprint("entering ");
- print_group(e);
- print_char('}');
- end_diagnostic(false);
-}
-
-@ 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.
-
-@c
-save_pointer *grp_stack; /* initial |cur_boundary| */
-halfword *if_stack; /* initial |cond_ptr| */
-
-@ When a group ends that was apparently entered in a different input
-file, the |group_warning| procedure is invoked in order to update the
-|grp_stack|. If moreover \.{\\tracingnesting} is positive we want to
-give a warning message. The situation is, however, somewhat complicated
-by two facts: (1)~There may be |grp_stack| elements without a
-corresponding \.{\\input} file or \.{\\scantokens} pseudo file (e.g.,
-error insertions from the terminal); and (2)~the relevant information is
-recorded in the |name_field| of the |input_stack| only loosely
-synchronized with the |in_open| variable indexing |grp_stack|.
-
-@c
-void group_warning(void)
-{
- boolean w = false; /* do we need a warning? */
- int i = in_open; /* index into |grp_stack| */
- base_ptr = input_ptr;
- input_stack[base_ptr] = cur_input; /* store current state */
- while ((grp_stack[i] == cur_boundary) && (i > 0)) {
- /* 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;
- }
-}
-
-@ 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).
-
-@c
-void if_warning(void)
-{
- boolean w = false; /* do we need a warning? */
- int i = in_open;
- base_ptr = input_ptr;
- input_stack[base_ptr] = cur_input; /* store current state */
- while (if_stack[i] == cond_ptr) {
- /* 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;
- }
-}
-
-
-@ 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.
-
-@c
-void file_warning(void)
-{
- halfword p = save_ptr; /* saved value of |save_ptr| or |cond_ptr| */
- int l = cur_level; /* saved value of |cur_level| or |if_limit| */
- int c = cur_group; /* saved value of |cur_group| or |cur_if| */
- int i; /* saved value of |if_line| */
- 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;
- cur_group = (group_code) c; /* restore old values */
- 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);
- }
- cond_ptr = p;
- if_limit = l;
- cur_if = c;
- if_line = i; /* restore old values */
- print_ln();
- if (tracing_nesting_par > 1)
- show_context();
- if (history == spotless)
- history = warning_issued;
-}
-
-@ @c
-halfword last_line_fill; /* the |par_fill_skip| glue node of the new paragraph */
-
-@ 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.
-
-@c
-#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)
-
-/* 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; /* return non-nil for error */
- 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; /* return non-nil for error */
- 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; /* return non-nil for error */
- 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; /* return non-nil for error */
- 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; /* return non-nil for error */
- 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; /* return non-nil for error */
- 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;
-}
-
-@ 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.
-
-@c
-int synctexoption;
-
-@ 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.
-
-@c
-int synctexoffset; /* holds the true value of |synctex_code| */
-
-@ 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.
-
-{\sl 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.
-@^synctex@>
-@^synchronization@>
-
-@ Here are extra variables for Web2c. (This numbering of the
-system-dependent section allows easy integration of Web2c and e-\TeX, etc.)
-@^<system dependencies@>
-
-@c
-pool_pointer edit_name_start; /* where the filename to switch to starts */
-int edit_name_length, edit_line; /* what line to start editing at */
-boolean stop_at_space; /* whether |more_name| returns false for space */
-
-@ 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.
-
-@c
-int shellenabledp;
-int restrictedshell;
-char *output_comment;
-
-@ Are we printing extra info as we read the format file?
-
-@c
-boolean debug_format_file;
diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.w b/Build/source/texk/web2c/luatexdir/tex/filename.c
index 9e5d9c5cc1c..fdc6641efca 100644
--- a/Build/source/texk/web2c/luatexdir/tex/filename.w
+++ b/Build/source/texk/web2c/luatexdir/tex/filename.c
@@ -1,59 +1,62 @@
-% 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/>.
-
-@ @c
+/*
+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"
-@ 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@>
-
-@c
+/*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;
@@ -61,13 +64,16 @@ static void begin_name(void)
quoted_filename = false;
}
-@ And here's the second. The string pool might change as the file name is
- being scanned, since a new \.{\\csname} might be entered; therefore we keep
- |area_delimiter| and |ext_delimiter| relative to the beginning of the current
- string, instead of assigning an absolute address like |pool_ptr| to them.
- @^system dependencies@>
+/*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@>
+
+*/
-@c
static boolean more_name(ASCII_code c)
{
if (c == ' ' && stop_at_space && (!quoted_filename)) {
@@ -77,7 +83,7 @@ static boolean more_name(ASCII_code c)
return true;
} else {
str_room(1);
- append_char(c); /* contribute |c| to the current string */
+ append_char(c);
if (IS_DIR_SEP(c)) {
area_delimiter = (pool_pointer) cur_length;
ext_delimiter = 0;
@@ -87,17 +93,21 @@ static boolean more_name(ASCII_code c)
}
}
-@ The third.
-@^system dependencies@>
+/*tex
+
+ The third. @^system dependencies@>
+
+*/
-@c
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));
- /* at this point, the full string lives in |cur_string| */
+ 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 {
@@ -105,7 +115,7 @@ static void end_name(void)
cur_string[area_delimiter] = '\0';
cur_length = (unsigned) strlen((char *) cur_string);
cur_area = make_string();
- xfree(cur_string);
+ xfree(cur_string);
cur_length = (unsigned) strlen((char *) s);
cur_string = s;
}
@@ -118,24 +128,27 @@ static void end_name(void)
cur_string[l] = '\0';
cur_length = (unsigned) strlen((char *) cur_string);
cur_name = make_string();
- xfree(cur_string);
+ xfree(cur_string);
cur_length = (unsigned) strlen((char *) s);
cur_string = s;
cur_ext = make_string();
}
}
-@ 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
+/*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.
-@c
+*/
+
void scan_file_name(void)
{
str_number u = 0;
name_in_progress = true;
begin_name();
- /* Get the next non-blank non-call token; */
+ /*tex Get the next non-blank non-call token: */
do {
get_x_token();
} while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
@@ -145,9 +158,11 @@ void scan_file_name(void)
back_input();
break;
}
- /* 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. */
+ /*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;
@@ -174,8 +189,10 @@ void scan_file_name(void)
name_in_progress = false;
}
-@ This function constructs a the three file name strings from a token list
-@c
+/*
+ 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;
@@ -192,12 +209,14 @@ void scan_file_name_toks(void)
e = s + i;
}
}
- if (n != s) { /* explicit area */
+ if (n != s) {
+ /*tex explicit area */
cur_area = maketexlstring(a, (size_t) (n - a));
} else {
cur_area = get_nullstr();
}
- if (e != NULL) { /* explicit extension */
+ if (e != NULL) {
+ /*tex explicit extension */
cur_name = maketexlstring(n, (size_t) (e - n));
cur_ext = maketexstring(e);
} else {
@@ -205,33 +224,34 @@ void scan_file_name_toks(void)
cur_ext = get_nullstr();
}
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|.
+ 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|
+*/
-@c
char *pack_job_name(const char *s)
-{ /* |s = ".log"|, |".dvi"|, or |format_extension| */
+{
cur_area = get_nullstr();
cur_ext = maketexstring(s);
cur_name = job_name;
return pack_file_name(cur_name, cur_area, cur_ext);
}
-@ 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.
+/*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.
+
+*/
-@c
char *prompt_file_name(const char *s, const char *e)
{
int k; /* index into |buffer| */
@@ -290,12 +310,10 @@ char *prompt_file_name(const char *s, const char *e)
return pack_file_name(cur_name, cur_area, cur_ext);
}
-
-@ @c
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 */
+ boolean must_quote; /* whether to quote the filename */
+ unsigned char *j; /* index into string */
must_quote = false;
if (a != NULL) {
j = a;
@@ -318,13 +336,11 @@ void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e)
j++;
}
}
- /* FIXME: 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.
- |must_quote|:=((|a|<>0)and(|str_pool|[|str_start|[|a|]]=""""))or
- ((|n|<>0)and(|str_pool|[|str_start|[|n|]]=""""))or
- ((|e|<>0)and(|str_pool|[|str_start|[|e|]]="""")); */
-
+ /*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) {
@@ -346,15 +362,17 @@ void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e)
print_char('"');
}
-@ @c
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);
+ 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/inputstack.w b/Build/source/texk/web2c/luatexdir/tex/inputstack.c
index 68b22e1a334..c64111edc89 100644
--- a/Build/source/texk/web2c/luatexdir/tex/inputstack.w
+++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.c
@@ -1,51 +1,85 @@
-% 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/>.
-
-@ @c
+/*
+
+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"
-@ @c
in_state_record *input_stack = NULL;
-int input_ptr = 0; /* first unused location of |input_stack| */
-int max_in_stack = 0; /* largest value of |input_ptr| when pushing */
-in_state_record cur_input; /* the ``top'' input state */
-int in_open = 0; /* the number of lines in the buffer, less one */
-int open_parens = 0; /* the number of open text files */
+/*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;
-int line = 0; /* current line number in the current source file */
+
+/*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;
-int scanner_status = 0; /* can a subfile end now? */
-pointer warning_index = null; /* identifier relevant to non-|normal| scanner status */
-pointer def_ref = null; /* reference count of token list being defined */
+/*tex Can a subfile end now? */
-@ Here is a procedure that uses |scanner_status| to print a warning message
-when a subfile has ended, and at certain other crucial times:
+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:
+
+*/
-@c
void runaway(void)
{
- pointer p = null; /* head of runaway list */
+ /*tex The head of the runaway list: */
+ pointer p = null;
if (scanner_status > skipping) {
switch (scanner_status) {
case defining:
@@ -65,7 +99,7 @@ void runaway(void)
p = def_ref;
break;
default:
- /* there are no other cases */
+ /*tex There are no other cases. */
break;
}
print_char('?');
@@ -74,48 +108,69 @@ void runaway(void)
}
}
-@ 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.
-
-@c
-pointer *param_stack = NULL; /* token list pointers for parameters */
-int param_ptr = 0; /* first unused entry in |param_stack| */
-int max_param_stack = 0; /* largest value of |param_ptr|, will be |<=param_size+9| */
-
-@ The input routines must also interact with the processing of
-\.{\\halign} and \.{\\valign}, since the appearance of tab marks and
-\.{\\cr} in certain places is supposed to trigger the beginning of special
-$v_j$ template text in the scanner. This magic is accomplished by an
-|align_state| variable that is increased by~1 when a `\.{\char'173}' is
-scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state|
-is nonzero during the $u_j$ template, after which it is set to zero; the
-$v_j$ template begins when a tab mark or \.{\\cr} occurs at a time that
-|align_state=0|.
-
-@c
-int align_state = 0; /* group level with respect to current alignment */
-
-@ Thus, the ``current input state'' can be very complicated indeed; there
-can be many levels and each level can arise in a variety of ways. The
-|show_context| procedure, which is used by \TeX's error-reporting routine to
-print out the current input state on all levels down to the most recent
-line of characters from an input file, illustrates most of these conventions.
-The global variable |base_ptr| contains the lowest level that was
-displayed by this procedure.
-
-@c
-int base_ptr = 0; /* shallowest level shown by |show_context| */
-
-@ The status at each level is indicated by printing two lines, where the first
-line indicates what was read so far and the second line shows what remains
-to be read. The context is cropped, if necessary, so that the first line
-contains at most |half_error_line| characters, and the second contains
-at most |error_line|. Non-current input levels whose |token_type| is
-`|backed_up|' are shown only if they have not been fully read.
-
-@c
+/*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) {
@@ -172,47 +227,49 @@ static void print_token_list_type(int t)
case write_text:
tprint_nl("<write> ");
break;
+ case local_text:
+ tprint_nl("<local> ");
+ break;
default:
tprint_nl("?");
- /* this should never happen */
+ /*tex This should never happen. */
break;
}
}
-@ 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.
-
-@c
+/*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;
@@ -230,10 +287,12 @@ void set_trick_count(void)
#define PSEUDO_PRINT_THE_LINE() do { \
begin_pseudoprint(); \
- if (buffer[ilimit]==end_line_char_par) \
+ if (buffer[ilimit]==end_line_char_par) { \
j=ilimit; \
- else \
- j=ilimit+1; /* determine the effective end of the line */ \
+ } 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) \
@@ -243,10 +302,12 @@ void set_trick_count(void)
} \
} while (0)
-/*
- 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.
+/*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 { \
@@ -268,46 +329,66 @@ void set_trick_count(void)
print_char(trick_buf[(q+2) % error_line]); \
print_char(trick_buf[(q+3) % error_line]); \
} else { \
- /* invalid */ \
+ /*tex Invalid character! */ \
} \
} while (0)
-@ @c
+/*tex
+
+This one prints where the scanner is.
+
+*/
+
void show_context(void)
-{ /* prints where the scanner is */
- int old_setting; /* saved |selector| setting */
- int nn = -1; /* number of contexts shown so far, less one */
- boolean bottom_line = false; /* have we reached the final context to be shown? */
- int i; /* index into |buffer| */
- int j; /* end of current line in |buffer| */
- int l; /* length of descriptive information on line 1 */
- int m; /* context information gathered for line 2 */
- int n; /* length of line 1 */
- int p; /* starting or ending place in |trick_buf| */
- int q; /* temporary index */
- int c; /* used in sanitizer */
+{
+ /*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;
- /* store current state */
+ /*tex Store the current state. */
while (true) {
- cur_input = input_stack[base_ptr]; /* enter into the context */
+ /*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)) {
- /* Display the current context */
+ /*tex Display the current context. */
if ((base_ptr == input_ptr) || (istate != token_list) || (token_type != backed_up) || (iloc != null)) {
- /* we omit backed-up token lists that have already been read */
- tally = 0; /* get ready to count characters */
+ /*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) {
- /*
- Print location of 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.
+ /*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) {
@@ -327,7 +408,8 @@ void show_context(void)
tprint_nl("l.");
if (iindex == in_open) {
print_int(line);
- } else { /* input from a pseudo file */
+ } else {
+ /*tex Input from a pseudo file. */
print_int(line_stack[iindex + 1]);
}
}
@@ -337,21 +419,26 @@ void show_context(void)
print_token_list_type(token_type);
begin_pseudoprint();
- if (token_type < macro)
+ if (token_type < macro) {
show_token_list(istart, iloc, 100000);
- else
- show_token_list(token_link(istart), iloc, 100000); /* avoid reference count */
+ } else {
+ /*tex Avoid reference count. */
+ show_token_list(token_link(istart), iloc, 100000);
+ }
}
- /* stop pseudoprinting */
+ /*tex Stop pseudoprinting. */
selector = old_setting;
- /* Print two lines using the tricky pseudoprinted information */
- if (trick_count == 1000000)
+ /*tex Print two lines using the tricky pseudoprinted information. */
+ if (trick_count == 1000000) {
set_trick_count();
- /* |set_trick_count| must be performed */
- if (tally < trick_count)
+ }
+ /*tex The |set_trick_count| must be performed. */
+ if (tally < trick_count) {
m = tally - first_count;
- else
- m = trick_count - first_count; /* context on line 2 */
+ } else {
+ /* The context on line 2: */
+ m = trick_count - first_count;
+ }
if (l + first_count <= half_error_line) {
p = 0;
n = l + first_count;
@@ -360,12 +447,14 @@ void show_context(void)
p = l + first_count - half_error_line + 3;
n = half_error_line;
}
- for (q = p; q <= first_count - 1; q++)
+ for (q = p; q <= first_count - 1; q++) {
print_valid_utf8(q);
- print_ln();
- /* print |n| spaces to begin line~2 */
- for (q = 1; q <= n; 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
@@ -379,24 +468,26 @@ void show_context(void)
} else if (nn == error_context_lines_par) {
tprint_nl("...");
incr(nn);
- /* omitted if |error_context_lines_par<0| */
+ /*tex Omitted if |error_context_lines_par<0|. */
}
if (bottom_line)
break;
decr(base_ptr);
}
- /* restore original state */
+ /*tex Restore the original state. */
cur_input = input_stack[input_ptr];
}
-@ The following subroutines change the input status in commonly needed ways.
+/*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).
-@c
+Enter a new input level, save the old:
-/* enter a new input level, save the old */
+*/
# define pop_input() \
cur_input=input_stack[--input_ptr]
@@ -411,12 +502,14 @@ new level (having, initially, the same properties as the old).
nofilter = false; \
incr(input_ptr);
-@
-Here is a procedure that starts a new level of token-list input, given
-a token list |p| and its type |t|. If |t=macro|, the calling routine should
-set |name| and |loc|.
+/*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|.
+
+*/
-@c
void begin_token_list(halfword p, quarterword t)
{
push_input();
@@ -424,7 +517,7 @@ void begin_token_list(halfword p, quarterword t)
istart = p;
token_type = (unsigned char) t;
if (t >= macro) {
- /* the token list starts with a reference count */
+ /*tex The token list starts with a reference count. */
add_token_ref(p);
if (t == macro) {
param_start = param_ptr;
@@ -438,8 +531,7 @@ void begin_token_list(halfword p, quarterword t)
else if (t == write_text)
tprint_esc("write");
else
- print_cmd_chr(assign_toks_cmd,
- t - output_text + output_routine_loc);
+ print_cmd_chr(assign_toks_cmd, t - output_text + output_routine_loc);
tprint("->");
token_show(p);
end_diagnostic(false);
@@ -450,24 +542,26 @@ void begin_token_list(halfword p, quarterword t)
}
}
-@ 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@>
+/*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@>
+
+*/
-@c
void end_token_list(void)
{
- /* leave a token-list input level */
+ /*tex Leave a token-list input level: */
if (token_type >= backed_up) {
- /* token list to be deleted */
+ /*tex The token list to be deleted: */
if (token_type <= inserted) {
flush_list(istart);
} else {
- /* update reference count */
+ /*tex Update the reference count: */
delete_token_ref(istart);
if (token_type == macro) {
- /* parameters must be flushed */
+ /*tex Parameters must be flushed: */
while (param_ptr > param_start) {
decr(param_ptr);
flush_list(param_stack[param_ptr]);
@@ -484,23 +578,24 @@ void end_token_list(void)
check_interrupt();
}
-@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has
-seen. The |back_input| procedure takes care of this by putting the token
-just scanned back into the input stream, ready to be read again. This
-procedure can be used only if |cur_tok| represents the token to be
-replaced. Some applications of \TeX\ use this procedure a lot,
-so it has been slightly optimized for speed.
-@^inner loop@>
+/*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@>
-@c
+*/
/* undoes one token of input */
void back_input(void)
{
- halfword p; /* a token list of length one */
+ /*tex A token list of length one: */
+ halfword p;
while ((istate == token_list) && (iloc == null) && (token_type != v_template)) {
- /* conserve stack space */
+ /*tex Conserve stack space. */
end_token_list();
}
p = get_avail();
@@ -512,14 +607,18 @@ void back_input(void)
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; /* that was |back_list(p)|, without procedure overhead */
+ iloc = p;
}
-@ Insert token |p| into \TeX's input
-@c
+/*tex
+
+Insert token |p| into \TeX's input
+
+*/
void reinsert_token(boolean a, halfword pp)
{
@@ -545,13 +644,15 @@ void reinsert_token(boolean a, halfword pp)
cur_tok = t;
}
-@ 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|.
+/*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@>
-@c
+*/
+
void begin_file_reading(void)
{
if (in_open == max_in_open)
@@ -569,48 +670,59 @@ void begin_file_reading(void)
line_stack[iindex] = line;
istart = first;
istate = mid_line;
- iname = 0; /* |terminal_input| is now |true| */
+ iname = 0;
+ /*tex Variable |terminal_input| is now |true|. */
line_catcode_table = DEFAULT_CAT_TABLE;
line_partial = false;
- /* Prepare terminal input {\sl Sync\TeX} information */
+ /*tex Prepare terminal input \SYNCTEX\ information. */
synctex_tag = 0;
}
-@ Conversely, the variables must be downdated when such a level of input
-is finished:
+/*tex
+
+Conversely, the variables must be downdated when such a level of input is
+finished:
+
+*/
-@c
void end_file_reading(void)
{
first = istart;
line = line_stack[iindex];
- if ((iname >= 18) && (iname <= 20))
+ if ((iname >= 18) && (iname <= 20)) {
pseudo_close();
- else if (iname == 21)
+ } else if (iname == 21) {
luacstring_close(iindex);
- else if (iname > 17)
- lua_a_close_in(cur_file, 0); /* forget it */
+ } else if (iname > 17) {
+ /*tex Forget it. */
+ lua_a_close_in(cur_file, 0);
+ }
pop_input();
decr(in_open);
}
-@ In order to keep the stack from overflowing during a long sequence of
+/*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.
-@c
+*/
void clear_for_error_prompt(void)
{
- while ((istate != token_list) && terminal_input
- && (input_ptr > 0) && (iloc > ilimit))
+ while ((istate != token_list) && terminal_input && (input_ptr > 0) && (iloc > ilimit)) {
end_file_reading();
+ }
print_ln();
clear_terminal();
}
-@ To get \TeX's whole input mechanism going, we perform the following actions.
+/*tex
+
+To get \TeX's whole input mechanism going, we perform the following actions.
+
+*/
-@c
void initialize_inputstack(void)
{
input_ptr = 0;
@@ -645,29 +757,29 @@ void initialize_inputstack(void)
line_catcode_table = DEFAULT_CAT_TABLE;
line_partial = false;
align_state = 1000000;
- if (!init_terminal())
- exit(EXIT_FAILURE); /* |goto final_end|; */
+ if (!init_terminal()) {
+ /*tex |goto final_end|; */
+ exit(EXIT_FAILURE);
+ }
+ /* |init_terminal| has set |loc| and |last| */
ilimit = last;
- first = last + 1; /* |init_terminal| has set |loc| and |last| */
+ first = last + 1;
}
-@ 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.
+/*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.
- hh: todo: 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.
+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.
*/
-
-@c
-halfword pseudo_files; /* stack of pseudo files */
+halfword pseudo_files;
static halfword string_to_pseudo(str_number str, int nl)
{
@@ -680,7 +792,8 @@ static halfword string_to_pseudo(str_number str, int nl)
len = (unsigned) str_length(str);
l = 0;
while (l < len) {
- unsigned m = l; /* start of current line */
+ /*tex start of current line */
+ unsigned m = l;
while ((l < len) && (s[l] != nl))
l++;
sz = (int) (l - m + 7) / 4;
@@ -703,7 +816,8 @@ static halfword string_to_pseudo(str_number str, int nl)
if (q == null) {
pseudo_lines(h) = r;
} else {
- vlink(q) = r ; /* no prev node here so no couple_nodes !*/
+ /*tex There is no |prev| node here so no need to couple_nodes! */
+ vlink(q) = r ;
}
q = r ;
if (s[l] == nl)
@@ -712,24 +826,33 @@ static halfword string_to_pseudo(str_number str, int nl)
return h;
}
-@ The |pseudo_start| procedure initiates reading from a pseudo file.
+/*tex
+
+The |pseudo_start| procedure initiates reading from a pseudo file.
+
+*/
-@c
void pseudo_from_string(void)
{
- str_number s; /* string to be converted into a pseudo file */
- halfword p; /* for list construction */
+ /*tex The string to be converted into a pseudo file: */
+ str_number s;
+ /*tex A helper for list construction: */
+ halfword p;
s = make_string();
- /* Convert string |s| into a new pseudo file */
+ /*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);
- /* Initiate input from new pseudo file */
- begin_file_reading(); /* set up |cur_file| and new level of input */
+ /*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;
- iloc = ilimit + 1; /* force line read */
+ /*tex force line read */
+ iloc = ilimit + 1;
if (tracing_scan_tokens_par > 0) {
if (term_offset > max_print_line - 3)
print_ln();
@@ -742,7 +865,7 @@ void pseudo_from_string(void)
} else {
iname = 18;
}
- /* Prepare pseudo file {\sl Sync\TeX} information */
+ /*tex Prepare pseudo file \SYNCTEX\ information. */
synctex_tag = 0;
}
@@ -759,29 +882,37 @@ void pseudo_start(void)
pseudo_from_string();
}
-@ @c
void lua_string_start(void)
{
- begin_file_reading(); /* set up |cur_file| and new level of input */
+ /*tex Set up |cur_file| and a new level of input: */
+ begin_file_reading();
line = 0;
ilimit = istart;
- iloc = ilimit + 1; /* force line read */
+ /*tex Force line read: */
+ iloc = ilimit + 1;
iname = 21;
luacstring_start(iindex);
}
-@ Here we read a line from the current pseudo file into |buffer|.
+/*tex
-@c
-/* inputs the next line or returns |false| */
+Here we read a line from the current pseudo file into |buffer|.
+It inputs the next line or returns |false|.
+
+*/
boolean pseudo_input(void)
{
- halfword p; /* current line from pseudo file */
- int sz; /* size of node |p| */
- four_quarters w; /* four ASCII codes */
- halfword r; /* loop index */
- last = first; /* cf.\ Matthew 19\thinspace:\thinspace30 */
+ /*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;
@@ -808,10 +939,11 @@ boolean pseudo_input(void)
return true;
}
-@ When we are done with a pseudo file we `close' it.
+/*tex
+
+When we are done with a pseudo file we `close' it.
-@c
-/* close the top level pseudo file */
+*/
void pseudo_close(void)
{
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..df13931e14c
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.c
@@ -0,0 +1,2520 @@
+/*
+
+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;
+ }
+ if (abs(fit_class - fitness(r)) > 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.w b/Build/source/texk/web2c/luatexdir/tex/linebreak.w
deleted file mode 100644
index 345a1a42456..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/linebreak.w
+++ /dev/null
@@ -1,2164 +0,0 @@
-% linebreak.w
-%
-% 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/>.
-
-@ @c
-
-#include "ptexlib.h"
-
-@ We come now to what is probably the most interesting algorithm of \TeX:
-the mechanism for choosing the ``best possible'' breakpoints that yield
-the individual lines of a paragraph. \TeX's line-breaking algorithm takes
-a given horizontal list and converts it to a sequence of boxes that are
-appended to the current vertical list. In the course of doing this, it
-creates a special data structure containing three kinds of records that are
-not used elsewhere in \TeX. Such nodes are created while a paragraph is
-being processed, and they are destroyed afterwards; thus, the other parts
-of \TeX\ do not need to know anything about how line-breaking is done.
-
-The method used here is based on an approach devised by Michael F. Plass and
-@^Plass, Michael Frederick@>
-@^Knuth, Donald Ervin@>
-the author in 1977, subsequently generalized and improved by the same two
-people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice
-\AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the
-line-breaking problem can be regarded as a special case of the problem of
-computing the shortest path in an acyclic network. The cited paper includes
-numerous examples and describes the history of line breaking as it has been
-practiced by printers through the ages. The present implementation adds two
-new ideas to the algorithm of 1980: Memory space requirements are considerably
-reduced by using smaller records for inactive nodes than for active ones,
-and arithmetic overflow is avoided by using ``delta distances'' instead of
-keeping track of the total distance from the beginning of the paragraph to the
-current point.
-
-The |line_break| procedure should be invoked only in horizontal mode; it
-leaves that mode and places its output into the current vlist of the
-enclosing vertical mode (or internal vertical mode).
-There is one explicit parameter: |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.
-
-@c
-halfword just_box; /* the |hlist_node| for the last line of the new paragraph */
-
-@ 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.
-@^data structure assumptions@>
-
-Most other processing is delegated to external functions.
-
-@c
-void line_break(boolean d, int line_break_context)
-{
- int paragraph_dir = 0; /* main direction of paragraph */
- halfword final_par_glue;
- halfword start_of_par;
- int callback_id;
- pack_begin_line = cur_list.ml_field; /* this is for over/underfull box messages */
- alink(temp_head) = null; /* hh-ls */
- 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) {
- /* 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"); /* assert(0); */ /* |paragraph_dir = 0|; */
- }
- 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;
-}
-
-@ 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.
-
-@c
-#define check_shrinkage(a) \
- if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \
- a=finite_shrink((a))
-
-static boolean no_shrink_error_yet; /*have we complained about infinite shrinkage? */
-
-static halfword finite_shrink(halfword p)
-{ /* recovers from infinite shrinkage */
- 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;
-}
-
-@ 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|.
-
-@c
-static boolean second_pass; /* is this our second attempt to break this paragraph? */
-static boolean final_pass; /*is this our final attempt to break this paragraph? */
-static int threshold; /* maximum badness on feasible lines */
-
-/* maximum fill level for |hlist_stack|*/
-#define max_hlist_stack 512 /* maybe good if larger than |2 *
- max_quarterword|, so that box nesting
- level would overflow first */
-
-/* stack for |find_protchar_left()| and |find_protchar_right()| */
-static halfword hlist_stack[max_hlist_stack];
-
-/* fill level for |hlist_stack| */
-static short hlist_stack_level = 0;
-
-@ @c
-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) /* would point to some bug */
- normal_error("pop_node","stack underflow (internal error)");
- return hlist_stack[--hlist_stack_level];
-}
-
-@ @c
-static int max_stretch_ratio = 0; /*maximal stretch ratio of expanded fonts */
-static int max_shrink_ratio = 0; /*maximal shrink ratio of expanded fonts */
-static int cur_font_step = 0; /*the current step of expanded fonts */
-
-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;
-}
-
-@ searches left to right from list head |l|, returns 1st non-skipable item
-
-@c
-/*public*/ 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)) {
- /*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)))) {
- /* std.\ 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)) {
- l = pop_node(); /* don't visit this node again */
- run = false;
- }
- if ((vlink(l) != null) && (type(l) == boundary_node) && (subtype(l) == protrusion_boundary) &&
- ((boundary_value(l) == 1) || (boundary_value(l) == 3))) {
- /* 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;
-}
-
-@ searches right to left from list tail |r| to head |l|, returns 1st non-skipable item
-
-@c
-/*public*/ 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)) {
- r = pop_node(); /* don't visit this node again */
- 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))) {
- /* skip next node */
- r = alink(r);
- }
- if (alink(r) != null) {
- r = alink(r);
- } else { /* 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;
-}
-
-@ @c
-#define left_pw(a) char_pw((a), left_side)
-#define right_pw(a) char_pw((a), right_side)
-
-@ 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|.
-
-@c
-typedef enum {
- very_loose_fit = 0, /* fitness classification for lines stretching more than
- their stretchability */
- loose_fit, /* fitness classification for lines stretching 0.5 to 1.0 of their
- stretchability */
- decent_fit, /* fitness classification for all other lines */
- tight_fit /* fitness classification for lines shrinking 0.5 to 1.0 of their
- shrinkability */
-} fitness_value;
-
-
-@ 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:
-
-|vlink| points to the next node in the list of active nodes; the
-last active node has |vlink=active|.
-
-|break_node| points to the passive node associated with this
-breakpoint.
-
-|line_number| is the number of the line that follows this
-breakpoint.
-
-|fitness| is the fitness classification of the line ending at this
-breakpoint.
-
-|type| is either |hyphenated_node| or |unhyphenated_node|, depending on
-whether this breakpoint is a |disc_node|.
-
-|total_demerits| is the minimum possible sum of demerits over all
-lines leading from the beginning of the paragraph to this breakpoint.
-
-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.
-
-@c
-void initialize_active(void)
-{
- type(active) = hyphenated_node;
- line_number(active) = max_halfword;
- subtype(active) = 0; /* the |subtype| is never examined */
-}
-
-@ The passive node for a given breakpoint contains EIGHT fields:
-
-|vlink| points to the passive node created just before this one,
-if any, otherwise it is |null|.
-
-|cur_break| points to the position of this breakpoint in the
-horizontal list for the paragraph being broken.
-
-|prev_break| points to the passive node that should precede this
-one in an optimal path to this breakpoint.
-
-|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.)
-
-|passive_pen_inter| holds the current \.{\\localinterlinepenalty}
-
-|passive_pen_broken| holds the current \.{\\localbrokenpenalty}
-
-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.
-
-@c
-static halfword passive; /* most recent node on passive list */
-static halfword printed_node; /*most recent node that has been printed */
-static halfword pass_number; /*the number of passive nodes allocated on this pass */
-
-@ The active list also contains ``delta'' nodes that help the algorithm
-compute the badness of individual lines. Such nodes appear only between two
-active nodes, and they have |type=delta_node|. If |p| and |r| are active nodes
-and if |q| is a delta node between them, so that |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
-$$\hbox{\ignorespaces|for
-k:=1 to 7 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
-want to do this without the overhead of |for| loops. The |do_all_six|
-macro makes such six-tuples convenient.
-
-@c
-static scaled active_width[10] = { 0 }; /*distance from first active node to~|cur_p| */
-static scaled background[10] = { 0 }; /*length of an ``empty'' line */
-static scaled break_width[10] = { 0 }; /*length being computed after current break */
-
-static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_break| */
-
-@ Let's state the principles of the delta nodes more precisely and concisely,
- so that the following programs will be less obscure. For each legal
- breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and
- $\beta(p)$ such that the length of material in a line from breakpoint~|p|
- to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
- Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from
- the beginning of the paragraph to a point ``after'' a break at |p| and to a
- point ``before'' a break at |q|; and $\gamma$ is the width of an empty line,
- namely the length contributed by \.{\\leftskip} and \.{\\rightskip}.
-
- Suppose, for example, that the paragraph consists entirely of alternating
- boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and
- let the skips have widths $y_1\ldots y_n$, so that the paragraph can be
- represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint
- at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)=
- x_1+y_1+\cdots+x_i$. To check this, note that the length of material from
- $p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5)
- -\alpha(p_2)$.
-
- The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and
- shrinkability as well as a natural width. If we were to compute $\alpha(p)$
- and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and
- the multiprecise numbers would have to be kept in the active nodes.
- \TeX\ avoids this problem by working entirely with relative differences
- or ``deltas.'' Suppose, for example, that the active list contains
- $a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints
- and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$
- and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is
- currently positioned at some other breakpoint |p|, the |active_width| array
- contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through
- the list of active nodes and considering a tentative line that runs from
- $a_2$ to~|p|, say, the |cur_active_width| array will contain the value
- $\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$,
- we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this
- is just $\delta_2$, which appears in the active list between $a_2$ and
- $a_3$. The |background| array contains $\gamma$. The |break_width| array
- will be used to calculate values of new delta nodes when the active
- list is being updated.
-
-@ 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|.
-
-@c
-static int internal_pen_inter; /* running \.{\\localinterlinepenalty} */
-static int internal_pen_broken; /* running \.{\\localbrokenpenalty} */
-static halfword internal_left_box; /* running \.{\\localleftbox} */
-static int internal_left_box_width; /* running \.{\\localleftbox} width */
-static halfword init_internal_left_box; /* running \.{\\localleftbox} */
-static int init_internal_left_box_width; /* running \.{\\localleftbox} width */
-static halfword internal_right_box; /* running \.{\\localrightbox} */
-static int internal_right_box_width; /* running \.{\\localrightbox} width */
-
-static scaled disc_width[10] = { 0 }; /* the length of discretionary material preceding a break */
-
-@ 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.
-
-@c
-static int minimal_demerits[4]; /* best total demerits known for current
- line class and position, given the fitness */
-static int minimum_demerits; /* best total demerits known for current line class
- and position */
-static halfword best_place[4]; /* how to achieve |minimal_demerits| */
-static halfword best_pl_line[4]; /*corresponding line number */
-
-@ 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.
-@^length of lines@> @^hanging indentation@>
-
-@c
-static halfword easy_line; /*line numbers |>easy_line| are equivalent in break nodes */
-static halfword last_special_line; /*line numbers |>last_special_line| all have the same width */
-static scaled first_width; /*the width of all lines |<=last_special_line|, if
- no \.{\\parshape} has been specified */
-static scaled second_width; /*the width of all lines |>last_special_line| */
-static scaled first_indent; /*left margin to go with |first_width| */
-static scaled second_indent; /*left margin to go with |second_width| */
-
-static halfword best_bet; /*use this passive node and its predecessors */
-static int fewest_demerits; /*the demerits associated with |best_bet| */
-static halfword best_line; /*line number following the last line of the new paragraph */
-static int actual_looseness; /*the difference between |line_number(best_bet)|
- and the optimum |best_line| */
-static int line_diff; /*the difference between the current line number and
- the optimum |best_line| */
-
-@ \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.
-
-@c
-#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; \
- } \
-}
-
-static boolean do_last_line_fit; /* special algorithm for last line of paragraph? */
-static scaled fill_width[4]; /* infinite stretch components of |par_fill_skip| */
-static scaled best_pl_short[4]; /* |shortfall| corresponding to |minimal_demerits| */
-static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */
-
-#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))
-
-@ 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.
-
-@c
-#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);
- }
- /* fall through */
- case rule_node:
- widths[1] += width(s);
- break;
- case disc_node: /* TH temp */
- break;
- default:
- bad_node_in_disc_error(s);
- break;
- }
- }
- s = vlink(s);
- }
-}
-
-@ 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.
-
-@c
-static void sub_from_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths)
-{
- while (s != null) {
- /* 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);
- }
- /* fall through */
- case rule_node:
- widths[1] -= width(s);
- break;
- case disc_node: /* TH temp */
- break;
- default:
- bad_node_in_disc_error(s);
- break;
- }
- }
- s = vlink(s);
- }
-}
-
-@ 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|.
-
-@c
-static void compute_break_width(int break_type, int line_break_dir, int adjust_spacing, halfword p)
-{
- halfword s = p; /* glue and other 'whitespace' to be skipped after a break;
- used if unhyphenated, or |post_break==empty| */
- if (break_type > unhyphenated_node && p != null) {
- /*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.
-
- TH: I don't quite understand the above remarks.
-
- 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) {
- s = vlink(p); /* no |post_break|: 'skip' any 'whitespace' following */
- } else {
- s = null;
- }
- }
- while (s != null) {
- switch (type(s)) {
- case math_node:
- /* begin mathskip code */
- if (glue_is_zero(s)) {
- break_width[1] -= surround(s);
- break;
- } else {
- /* fall through */
- }
- /* end mathskip code */
- case glue_node:
- /*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);
- }
-}
-
-@ @c
-static void print_break_node(halfword q, fitness_value fit_class,
- quarterword break_type, halfword cur_p)
-{
- /* 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) {
- /*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)));
-}
-
-@ @c
-static void print_feasible_break(halfword cur_p, pointer r, halfword b, int pi,
- int d, boolean artificial_demerits)
-{
- /* Print a symbolic description of this feasible break; */
- if (printed_node != cur_p) {
- /* Print the list between |printed_node| and |cur_p|, then
- set |printed_node:=cur_p|; */
- 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);
-}
-
-@ @c
-#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)
-{
- /* labels: |CONTINUE,DEACTIVATE,FOUND,NOT_FOUND|; */
- pointer r; /* runs through the active list */
- scaled margin_kern_stretch;
- scaled margin_kern_shrink;
- halfword lp, rp, cp;
- halfword prev_r = active; /* stays a step behind |r| */
- halfword prev_prev_r = null; /*a step behind |prev_r|, if |type(prev_r)=delta_node| */
- halfword old_l = 0; /* maximum line number in current equivalence class of lines */
- boolean no_break_yet = true; /* have we found a feasible break at |cur_p|? */
- halfword q; /*points to a new node being created */
- halfword l; /*line number of current active node */
- boolean node_r_stays_active; /*should node |r| remain in the active list? */
- scaled line_width = 0; /*the current line will be justified to this width */
- fitness_value fit_class; /*possible fitness class of test line */
- halfword b; /*badness of test line */
- int d; /*demerits of test line */
- boolean artificial_demerits; /*has |d| been forced to zero? */
-
- scaled shortfall; /*used in badness calculations */
- scaled g = 0; /*glue stretch or shrink of test line, adjustment for last line */
- scaled cur_active_width[10] = { 0 }; /*distance from current active node */
-
- /*Make sure that |pi| is in the proper range; */
- if (pi >= inf_penalty) {
- return; /* this breakpoint is inhibited by infinite penalty */
- } else if (pi <= -inf_penalty) {
- pi = eject_penalty; /*this breakpoint will be forced */
- }
-
- do_all_eight(copy_to_cur_active);
-
- while (1) {
- r = vlink(prev_r);
- /* 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) {
- do_all_eight(update_width); /* IMPLICIT ,r */
- prev_prev_r = prev_r;
- prev_r = r;
- continue;
- }
- /* 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) { /* now we are no longer in the inner loop */
- if ((minimum_demerits < awful_bad)
- && ((old_l != easy_line) || (r == active))) {
- /*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);
- }
- /* 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) { /* modify an existing delta node */
- do_all_eight(convert_to_break_width); /* IMPLICIT |prev_r| */
- } else if (prev_r == active) { /* 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); /* IMPLICIT q */
- 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) {
- /* 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];
- /*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) {
- /*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;
- /* 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); /* IMPLICIT q */
- vlink(prev_r) = q;
- prev_prev_r = prev_r;
- prev_r = q;
- }
- }
- if (r == active)
- return;
- /*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;
- }
- }
- }
- /* /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 { /* TODO |if (is_character_node(alink(cur_p)))| */
- o = alink(cur_p);
- assert(vlink(o) == cur_p);
- }
- /* MAGIC: 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)
-
- BUT:
- 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)) {
- /* 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);
- }
- /* now the left margin */
- if ((l1 != null) && (type(l1) == disc_node) && (vlink_post_break(l1) != null)) {
- /* FIXME: first 'char' could be a disc! */
- l1 = vlink_post_break(l1); /* protrude the first char */
- } 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) {
- /* 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) {
- /* 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) { /* 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))
- /* 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]))
- /* 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)
- /*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) {
- /*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) {
- /*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;
- fit_class = decent_fit; /* infinite stretch */
- } 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 {
- /* 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) {
- /* 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)) {
- /* 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)) {
- artificial_demerits = true; /* set demerits zero, this break is forced */
- } else if (b > threshold) {
- goto DEACTIVATE;
- }
- node_r_stays_active = false;
- } else {
- prev_r = r;
- if (b > threshold)
- continue;
- node_r_stays_active = true;
- }
- /* 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 {
- /* 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;
- }
- if (abs(fit_class - fitness(r)) > 1)
- d = d + adj_demerits;
- }
- if (tracing_paragraphs > 0)
- print_feasible_break(cur_p, r, b, pi, d, artificial_demerits);
- d += total_demerits(r); /*this is the minimum total demerits
- from the beginning to |cur_p| via |r| */
- if (d <= minimal_demerits[fit_class]) {
- minimal_demerits[fit_class] = d;
- best_place[fit_class] = break_node(r);
- best_pl_line[fit_class] = l;
- if (do_last_line_fit) {
- /* 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;
- }
- /* /Record a new feasible break */
- if (node_r_stays_active)
- continue; /*|prev_r| has been set to |r| */
- DEACTIVATE:
- /* 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) {
- /*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); /* IMPLICIT r */
- 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); /* IMPLICIT |prev_r| */
- 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); /* IMPLICIT ,r */
- do_all_eight(combine_two_deltas); /* IMPLICIT r |prev_r| */
- vlink(prev_r) = vlink(r);
- flush_node(r);
- }
- }
- }
-}
-
-@ @c
-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)
-{
- /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE; */
- halfword cur_p, q, r, s; /* miscellaneous nodes of temporary interest */
- int line_break_dir = paragraph_dir;
-
- /* 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;
-
- /* 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);
- /* 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);
- }
- /* 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);
- }
- }
- }
- /* DIR: Initialize |dir_ptr| for |line_break| */
- if (dir_ptr != null) {
- flush_node_list(dir_ptr);
- dir_ptr = null;
- }
-#if 0
- push_dir(dir_ptr,paragraph_dir); /* TODO what was the point of this? */
-#endif
-
- /* 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;
- /* 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;
- /* /Create an active breakpoint representing the beginning of the paragraph */
- auto_breaking = true;
- cur_p = vlink(temp_head);
- /* LOCAL: Initialize with first |local_paragraph| node */
- if ((cur_p != null) && (type(cur_p) == local_par_node)) {
- alink(cur_p) = temp_head; /* this used to be an assert, but may as well force it */
- 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;
- }
- /* /LOCAL: Initialize with first |local_paragraph| node */
- set_prev_char_p(null);
- first_p = cur_p;
- /* to access the first node of paragraph as the first active node
- has |break_node=null| */
- while ((cur_p != null) && (vlink(active) != active)) {
- /* |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)) {
- /* 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");
- }
- /* 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: /* DIR: Adjust the dir stack for the |line_break| routine; */
- if (dir_dir(cur_p) >= 0) {
- line_break_dir = dir_dir(cur_p);
- push_dir_node(dir_ptr,cur_p); /* adds to |dir_ptr| */
- } else {
- pop_dir_node(dir_ptr);
- if (dir_ptr != null) {
- line_break_dir = dir_dir(dir_ptr);
- }
- }
- break;
- case local_par_node: /* LOCAL: 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);
- /* begin mathskip code */
- if (glue_is_zero(cur_p) || ignore_math_skip(cur_p)) {
- kern_break();
- break;
- } else {
- /* fall through */
- }
- /* end mathskip code */
- case glue_node:
- /*
- If node |cur_p| is a legal breakpoint, call |try_break|;
- then update the active widths by including the glue in
- |glue_ptr(cur_p)|;
-
- 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);
- }
- }
- /* *INDENT-ON* */
- 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:
- /* |select_disc|s are handled by the leading |init_disc| */
- if (subtype(cur_p) == select_disc)
- break;
- /* 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 (HH). */
- if (second_pass || subtype(cur_p) <= automatic_disc) {
- /*
- int actual_penalty = hyphen_penalty;
- if (disc_penalty(cur_p) != 0) {
- actual_penalty = (int) disc_penalty(cur_p);
- } else if (subtype(cur_p) == automatic_disc) {
- actual_penalty = ex_hyphen_penalty;
- }
- */
- int actual_penalty = (int) disc_penalty(cur_p);
- s = vlink_pre_break(cur_p);
- do_one_seven_eight(reset_disc_width);
- if (s == null) { /* 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) {
- /* we should at two break points after the one we
- added above:
- \item1 which does a possible break in INIT's |post_break|
- \item2 which means the |no_break| actually was broken
- just a character later */
- /* 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));
-#if 0
- /* TODO this does not work */
- /* go back to the starting situation */
- do_one_seven_eight(sub_disc_width_from_active_width);
- do_one_seven_eight(reset_disc_width);
- /* 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:
- /* / 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) {
- /*
- 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) {
- /* 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);
- /*
- Find an active node with fewest demerits;
- */
- if (looseness == 0)
- goto DONE;
- /*
- 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);
- /*
- Find the best active node for the desired looseness;
- */
- if ((actual_looseness == looseness) || final_pass)
- goto DONE;
- }
- }
- /* Clean up the memory by removing the break nodes; */
- clean_up_the_memory();
- /* /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 {
- /* if at first you do not succeed, \dots */
- 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) {
- /*
- 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;
- }
- }
-
- /*
- 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.
- */
-
- /* 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);
- /*
- Break the paragraph at the chosen ...Clean up the memory by removing
- the break nodes.
- */
- clean_up_the_memory();
-}
-
-@ @c
-void get_linebreak_info (int *f, int *a)
-{
- *f = fewest_demerits;
- *a = actual_looseness;
-}
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..ca9a79dbb02
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/mainbody.c
@@ -0,0 +1,769 @@
+/*
+
+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
+ 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);
+ /*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);
+ }
+ 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.w b/Build/source/texk/web2c/luatexdir/tex/mainbody.w
deleted file mode 100644
index 4d38b9d4a47..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/mainbody.w
+++ /dev/null
@@ -1,708 +0,0 @@
-% 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/>.
-
-\def\eTeX{e-\TeX}
-\def\Aleph{Aleph}
-\def\pdfTeX{pdf\TeX}
-
-@ @c
-
-
-#include "ptexlib.h"
-
-@
-pdfTeX is copyright (C) 1996-2006 Han The Thanh, <thanh@@pdftex.org>.
-
-e-TeX is copyright (C) 1994,98 by Peter Breitenlohner.
-
-This is LuaTeX, a continuation of $\pdfTeX$ and $\Aleph$. 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.
-
-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.
-
-% 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 e-TeX, although the change history is relevant in that it
-% demonstrates the evolutionary path followed. This program is not TeX;
-% that name is reserved strictly for the program which is the creation
-% and sole responsibility of Professor Knuth.
-
-% Version 0 was released in September 1982 after it passed a variety of tests.
-% Version 1 was released in November 1983 after thorough testing.
-% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984).
-% Version 1.2 allowed `0' in response to an error, et alia (October 1984).
-% Version 1.3 made memory allocation more flexible and local (November 1984).
-% Version 1.4 fixed accents right after line breaks, et alia (April 1985).
-% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985).
-% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986).
-% Version 2.1 corrected anomalies in discretionary breaks (January 1987).
-% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987).
-% Version 2.3 avoided incomplete page in premature termination (August 1987).
-% Version 2.4 fixed \noaligned rules in indented displays (August 1987).
-% Version 2.5 saved cur_order when expanding tokens (September 1987).
-% Version 2.6 added 10sp slop when shipping leaders (November 1987).
-% Version 2.7 improved rounding of negative-width characters (November 1987).
-% Version 2.8 fixed weird bug if no \patterns are used (December 1987).
-% Version 2.9 made \csname\endcsname's "relax" local (December 1987).
-% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988).
-% Version 2.92 fixed \patterns, also file names with complex macros (May 1988).
-% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988).
-% Version 2.94 kept open_log_file from calling fatal_error (November 1988).
-% Version 2.95 solved that problem a better way (December 1988).
-% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989).
-% Version 2.97 corrected blunder in creating 2.95 (February 1989).
-% Version 2.98 omitted save_for_after at outer level (March 1989).
-% Version 2.99 caught $$\begingroup\halign..$$ (June 1989).
-% Version 2.991 caught .5\ifdim.6... (June 1989).
-% Version 2.992 introduced major changes for 8-bit extensions (September 1989).
-% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989).
-% Version 3.0 fixed unusual displays; was more \output robust (March 1990).
-% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990).
-% Version 3.14 fixed unprintable font names and corrected typos (March 1991).
-% Version 3.141 more of same; reconstituted ligatures better (March 1992).
-% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993).
-% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995).
-% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002).
-% Version 3.1415926 was a general cleanup with minor fixes (February 2008).
-
-
-% 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.
-
-A large piece of software like \TeX\ has inherent complexity that cannot
-be reduced below a certain level of difficulty, although each individual
-part is fairly simple by itself. The \.{WEB} language is intended to make
-the algorithms as readable as possible, by reflecting the way the
-individual program pieces fit together and by providing the
-cross-references that connect different parts. Detailed comments about
-what is going on, and about why things were done in certain ways, have
-been liberally sprinkled throughout the program. These comments explain
-features of the implementation, but they rarely attempt to explain the
-\TeX\ language itself, since the reader is supposed to be familiar with
-{\sl The \TeX book}.
-@.WEB@>
-@:TeXbook}{\sl The \TeX book@>
-
-The present implementation has a long ancestry, beginning in the summer
-of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded
-a prototype
-@^Plass, Michael Frederick@>
-@^Liang, Franklin Mark@>
-@^Knuth, Donald Ervin@>
-based on some specifications that the author had made in May of that year.
-This original proto\TeX\ included macro definitions and elementary
-manipulations on boxes and glue, but it did not have line-breaking,
-page-breaking, mathematical formulas, alignment routines, error recovery,
-or the present semantic nest; furthermore,
-it used character lists instead of token lists, so that a control sequence
-like \.{\\halign} was represented by a list of seven characters. A
-complete version of \TeX\ was designed and coded by the author in late
-1977 and early 1978; that program, like its prototype, was written in the
-{\mc SAIL} language, for which an excellent debugging system was
-available. Preliminary plans to convert the {\mc SAIL} code into a form
-somewhat like the present ``web'' were developed by Luis Trabb~Pardo and
-@^Trabb Pardo, Luis Isidoro@>
-the author at the beginning of 1979, and a complete implementation was
-created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which
-@^Zabala Salelles, Ignacio Andr\'es@>
-was written by the author during the latter part of 1981 and the early
-part of 1982, also incorporates ideas from the 1979 implementation of
-@^Guibas, Leonidas Ioannis@>
-@^Sedgewick, Robert@>
-@^Wyatt, Douglas Kirk@>
-\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick,
-and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred
-refinements were introduced into \TeX82 based on the experiences gained with
-the original implementations, so that essentially every part of the system
-has been substantially improved. After the appearance of ``Version 0'' in
-September 1982, this program benefited greatly from the comments of
-many other people, notably David~R. Fuchs and Howard~W. Trickey.
-A final revision in September 1989 extended the input character set to
-eight-bit codes and introduced the ability to hyphenate words from
-different languages, based on some ideas of Michael~J. Ferguson.
-@^Fuchs, David Raymond@>
-@^Trickey, Howard Wellington@>
-@^Ferguson, Michael John@>
-
-No doubt there still is plenty of room for improvement, but the author
-is firmly committed to keeping \TeX82 ``frozen'' from now on; stability
-and reliability are to be its main virtues.
-
-On the other hand, the \.{WEB} description can be extended without changing
-the core of \TeX82 itself, and the program has been designed so that such
-extensions are not extremely difficult to make.
-The |banner| string defined here should be changed whenever \TeX\
-undergoes any modifications, so that it will be clear which version of
-\TeX\ might be the guilty party when a problem arises.
-@^extensions to \TeX@>
-@^system dependencies@>
-
-This program contains code for various features extending \TeX,
-therefore this program is called `\eTeX' and not
-`\TeX'; the official name `\TeX' by itself is reserved
-for software systems that are fully compatible with each other.
-A special test suite called the ``\.{TRIP} test'' is available for
-helping to determine whether a particular implementation deserves to be
-known as `\TeX' [cf.~Stanford Computer Science report CS1027,
-November 1984].
-
-A similar test suite called the ``\.{e-TRIP} test'' is available for
-helping to determine whether a particular implementation deserves to be
-known as `\eTeX'.
-
-@ This is the first of many sections of \TeX\ where global variables are
-defined.
-
-@c
-boolean luainit; /* are we using lua for initializations */
-boolean tracefilenames; /* print file open-close info? */
-
-
-@ 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.
-
-@c
-boolean ini_version; /* are we \.{INITEX}? */
-boolean dump_option; /* was the dump name option used? */
-boolean dump_line; /* was a \.{\%\AM format} line seen? */
-int bound_default; /* temporary for setup */
-char *bound_name; /* temporary for setup */
-int error_line; /* width of context lines on terminal error messages */
-int half_error_line; /* width of first lines of contexts in terminal
- error messages; should be between 30 and |error_line-15| */
-int max_print_line; /* width of longest text lines output; should be at least 60 */
-int max_strings; /* maximum number of strings; must not exceed |max_halfword| */
-int strings_free; /* strings available after format loaded */
-int font_k; /* loop variable for initialization */
-int buf_size; /* 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 stack_size; /* maximum number of simultaneous input sources */
-int max_in_open; /* maximum number of input files and error insertions that
- can be going on simultaneously */
-int param_size; /* maximum number of simultaneous macro parameters */
-int nest_size; /* maximum number of semantic levels simultaneously active */
-int save_size; /* space for saving values outside of current group; must be
- at most |max_halfword| */
-int expand_depth; /* limits recursive calls of the |expand| procedure */
-int parsefirstlinep; /* parse the first line for options */
-int filelineerrorstylep; /* format messages as file:line:error */
-int haltonerrorp; /* stop at first error */
-boolean quoted_filename; /* current filename is quoted */
-
-@ @c
-int get_luatexversion(void)
-{
- return luatex_version;
-}
-
-str_number get_luatexrevision(void)
-{
- return luatex_revision;
-}
-
-@ 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.
-
-@c
-#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;
- /* 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. */
-
- 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);
-
- /* 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;
-
- /* 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);
-
- 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();
- }
- /* 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;
- /* 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 {
- initialize(); /* set global variables to their starting values */
- if (ini_version) {
- /* 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;
-}
-
-@ @c
-void main_body(void)
-{
- static char pdftex_map[] = "pdftex.map";
- int bad = main_initialize();
- history = fatal_error_stop; /* in case we quit during initialization */
- t_open_out(); /* open the terminal for output */
- if (!luainit)
- tracefilenames = true;
- if (bad > 0) {
- goto FINAL_END;
- }
- print_banner(luatex_version_string);
-
- /* 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.
- */
- initialize_inputstack(); /* this copies the command-line */
- if (buffer[iloc] == '*')
- incr(iloc);
- if ((format_ident == 0) || (buffer[iloc] == '&') || dump_line) {
- char *fname = NULL;
- if (format_ident != 0 && !ini_version)
- initialize(); /* erase preloaded format */
- 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;
- }
- /* 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))
- start_input(); /* \.{\\input} assumed */
- /* DIR: Initialize |text_dir_ptr| */
- text_dir_ptr = new_dir(0);
-
- history = spotless; /* ready to go! */
- /* Initialize synctex primitive */
- synctexinitcommand();
- main_control(); /* come to life */
- flush_node(text_dir_ptr);
- final_cleanup(); /* prepare for death */
- close_files_and_terminate();
- FINAL_END:
- do_final_end();
-}
-
-
-@ 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.
-
-@c
-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) {
- /* 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();
- /* rubish, these pdf arguments, passed, needs to be fixed, e.g. with a dummy in dvi */
- wrapup_backend();
- /* Close {\sl Sync\TeX} file and write status */
- synctexterminate(log_opened_global);
- /*
- 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);
- }
-}
-
-
-@ We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has
-been scanned and |its_all_over|\kern-2pt.
-
-@c
-void final_cleanup(void)
-{
- int c; /* 0 for \.{\\end}, 1 for \.{\\dump} */
- halfword i; /* for looping marks */
- halfword t; /* was a global temp_ptr */
- 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);
- }
- while (pseudo_files != null)
- pseudo_close(); /* flush pseudo files */
- store_fmt_file();
- return;
- }
- tprint_nl("(\\dump is performed only by INITEX)");
- return;
- }
-}
-
-@ Once \TeX\ is working, you should be able to diagnose most errors with
-the \.{\\show} commands and other diagnostic features.
-An additional routine called |debug_help|
-will come into play when you type `\.D' after an error message;
-|debug_help| also occurs just before a fatal error causes \TeX\ to succumb.
-@^debugging@>
-@^system dependencies@>
-
-The interface to |debug_help| is primitive, but it is good enough when used
-with a debugger that allows you to set breakpoints and to read
-variables and change their values. After getting the prompt `\.{debug \#}', you
-type either a negative number (this exits |debug_help|), or zero (this
-goes to a location where you can set a breakpoint, thereby entering into
-dialog with the debugger), or a positive number |m| followed by
-an argument |n|. The meaning of |m| and |n| will be clear from the
-program below. (If |m=13|, there is an additional argument, |l|.)
-@.debug \#@>
-
-@c
-#ifdef DEBUG
-void debug_help(void)
-{ /* routine to display various things */
- int k;
- int m = 0, n = 0, l = 0;
- while (1) {
- wake_up_terminal();
- tprint_nl("debug # (-1 to exit):");
- update_terminal();
- (void) fscanf(term_in, "%d", &m);
- if (m < 0)
- return;
- else if (m == 0)
- abort(); /* go to every label at least once */
- else {
- (void) fscanf(term_in, "%d", &n);
- switch (m) {
- case 1:
- print_word(varmem[n]); /* display |varmem[n]| in all forms */
- break;
- case 2:
- print_int(info(n));
- break;
- case 3:
- print_int(link(n));
- break;
- case 4:
- print_word(eqtb[n]);
- break;
- case 6:
- print_int(save_type(n));
- print_int(save_level(n));
- print_word(save_word(n));
- break;
- case 7:
- show_box(n); /* show a box, abbreviated by |show_box_depth| and |show_box_breadth| */
- break;
- case 8:
- breadth_max = 10000;
- depth_threshold = 0x7FFFFFFF;
- show_node_list(n); /* show a box in its entirety */
- break;
- case 9:
- show_token_list(n, null, 1000);
- break;
- case 10:
- print(n);
- break;
- case 13:
- (void) fscanf(term_in, "%d", &l);
- print_cmd_chr(n, l);
- break;
- case 14:
- for (k = 0; k <= n; k++)
- print(buffer[k]);
- break;
- case 15:
- font_in_short_display = null_font;
- short_display(n);
- break;
- default:
- tprint("?");
- break;
- }
- }
- }
-}
-#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w b/Build/source/texk/web2c/luatexdir/tex/maincontrol.c
index 7b4cf9065ca..295e2c6ccae 100644
--- a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w
+++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.c
@@ -1,80 +1,79 @@
-% 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/>.
-
-@ @c
+/*
+
+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"
-@ @c
#define mode mode_par
#define tail tail_par
#define head head_par
#define dir_save dirs_par
-@ 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.
+/*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.
-
-@c
+|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);
@@ -90,21 +89,21 @@ void adjust_space_factor(void)
}
}
-@ From Knuth: ``Having |font_glue| allocated for each text font saves
-both time and memory.'' That may be true, but it also punches through
-the API wall for fonts, so I removed that -- Taco. But a bit of caching
-is very welcome, which is why I need to have the next two globals:
+/*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.
+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.
+
+*/
-@c
#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.
@@ -112,8 +111,8 @@ 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.
-@
-@c
+*/
+
static void run_char_num (void) {
scan_char_num();
cur_chr = cur_val;
@@ -126,23 +125,49 @@ static void run_char (void) {
tail_append(new_char(cur_font_par, cur_chr));
}
-@
-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.
+static void run_node (void) {
+ halfword n = cur_chr;
+ if (copy_lua_input_nodes_par) {
+ n = copy_node_list(n);
+ }
+ tail_append(n);
+ while (vlink(n) != null) {
+ n = vlink(n);
+ tail_append(n);
+ }
+}
-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_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.
+
+*/
-@c
static void run_app_space (void) {
halfword p; /* was a global temp_ptr */
int method = disable_space_par ;
if (method == 1) {
- /* don't inject anything, not even zero skip */
+ /*tex Don't inject anything, not even zero skip. */
} else if (method == 2) {
p = new_glue(zero_glue);
couple_nodes(tail,p);
@@ -150,9 +175,9 @@ static void run_app_space (void) {
} else if ((abs(mode) + cur_cmd == hmode + spacer_cmd) && (!(space_factor_par == 1000))) {
app_space();
} else {
- /* Append a normal inter-word space to the current list */
+ /*tex Append a normal inter-word space to the current list. */
if (glue_is_zero(space_skip_par)) {
- /* Find the glue specification for text spaces in the current font */
+ /*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);
@@ -161,20 +186,24 @@ static void run_app_space (void) {
} else {
p = new_param_glue(space_skip_code);
}
- /* so from now we have a subtype with spaces: */
+ /*tex So from now we have a subtype with spaces: */
subtype(p) = space_skip_code + 1 ;
couple_nodes(tail,p);
tail = p;
}
}
-@ Append a |boundary_node|
-@c
+/*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) ) {
- /* user boundary or protrusion boundary */
+ /*tex We expect a user boundary or protrusion boundary. */
scan_int();
boundary_value(n) = cur_val;
}
@@ -182,7 +211,6 @@ static void run_boundary (void) {
tail = n;
}
-@ @c
static void run_char_ghost (void) {
int t;
t = cur_chr;
@@ -199,19 +227,21 @@ static void run_char_ghost (void) {
}
}
-@ @c
static void run_relax (void) {
return;
}
-@ |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.
+/*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.
+
+*/
-@c
static void run_ignore_spaces (void) {
if (cur_chr == 0) {
- /* Get the next non-blank non-call... */
+ /*tex Get the next non-blank non-call... */
do {
get_x_token();
} while (cur_cmd == spacer_cmd);
@@ -231,24 +261,33 @@ static void run_ignore_spaces (void) {
}
}
-@ |stop| is the second special case. We want |main_control| to return to its caller
+/*tex
+
+|stop| is the second special case. We want |main_control| to return to its caller
if there is nothing left to do.
-@c
+*/
+
static void run_stop (void) {
- if (its_all_over())
- main_control_state= goto_return; /* this is the only way out */
+ if (its_all_over()) {
+ /*tex this is the only way out */
+ main_control_state= goto_return;
+ }
}
-@ @c
static void run_non_math_math (void) {
back_input();
new_graf(true);
}
-@ @c
+/*tex
+
+ We build up an argument to |set_math_char|:
+
+*/
+
static void run_math_char_num (void) {
- mathcodeval mval; /* to build up an argument to |set_math_char| */
+ mathcodeval mval;
if (cur_chr == 0)
mval = scan_mathchar(tex_mathcode);
else if (cur_chr == 1)
@@ -258,35 +297,47 @@ static void run_math_char_num (void) {
math_char_in_text(mval);
}
-@ @c
+/*tex
+
+ We build up an argument to |set_math_char|:
+*/
+
static void run_math_given (void) {
- mathcodeval mval; /* to build up an argument to |set_math_char| */
+ 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; /* to build up an argument to |set_math_char| */
+ mathcodeval mval;
mval = mathchar_from_integer(cur_chr, umath_mathcode);
math_char_in_text(mval);
}
-@ 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.
+/*tex
-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.
+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.
-Note that baselineskip calculations are disabled after a rule in vertical
-mode, by setting |prev_depth:=ignore_depth|.
+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|.
+
+*/
-@c
static void run_rule (void) {
tail_append(scan_rule_spec());
if (abs(mode) == vmode)
@@ -295,29 +346,30 @@ static void run_rule (void) {
space_factor_par = 1000;
}
-@
-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.
-
-@c
+/*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);
@@ -338,11 +390,14 @@ static void run_end_group (void) {
}
}
-@ 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|.
+/*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|.
+
+*/
-@c
static void run_move (void) {
int t = cur_chr;
scan_normal_dimen();
@@ -352,17 +407,14 @@ static void run_move (void) {
scan_box(-cur_val);
}
-@ @c
static void run_leader_ship (void) {
scan_box(leader_flag - a_leaders + cur_chr);
}
-@ @c
static void run_make_box (void) {
begin_box(0);
}
-@ @c
static void run_box_dir (void) {
scan_register_num();
cur_box = box(cur_val);
@@ -372,37 +424,51 @@ static void run_box_dir (void) {
box_dir(cur_box) = cur_val;
}
-@ 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).
+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}'.
+A paragraph begins when horizontal-mode material occurs in vertical mode, or when
+the paragraph is explicitly started by `\.{\\quitvmode}', `\.{\\indent}' or
+`\.{\\noindent}'.
+
+*/
-@c
static void run_start_par_vmode (void) {
new_graf((cur_chr > 0));
}
-@ @c
static void run_start_par (void) {
if (cur_chr != 2)
indent_in_hmode();
}
-@ @c
static void run_new_graf (void) {
back_input();
new_graf(true);
}
-@ 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}.
+/*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}.
+
+*/
-@c
static void run_par_end_vmode (void) {
normal_paragraph();
if (mode > 0) {
@@ -411,28 +477,27 @@ static void run_par_end_vmode (void) {
}
}
-@ @c
static void run_par_end_hmode (void) {
- if (align_state < 0)
- off_save(); /* this tries to recover from an alignment that didn't end properly */
- end_graf(bottom_level); /* this takes us to the enclosing mode, if |mode>0| */
+ 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();
}
}
-@ @c
static void append_italic_correction_mmode (void) {
- tail_append(new_kern(0)); /* what subtype to use */
+ tail_append(new_kern(0));
}
-@ @c
static void run_local_box (void) {
append_local_box(cur_chr);
}
-@ @c
static void run_halign_mmode (void) {
if (privileged()) {
if (cur_group == math_shift_group)
@@ -442,7 +507,6 @@ static void run_halign_mmode (void) {
}
}
-@ @c
static void run_eq_no (void) {
if (privileged()) {
if (cur_group == math_shift_group)
@@ -452,21 +516,18 @@ static void run_eq_no (void) {
}
}
-@ @c
static void run_letter_mmode (void) {
set_math_char(get_math_code(cur_chr));
}
-@ @c
static void run_char_num_mmode (void) {
scan_char_num();
cur_chr = cur_val;
set_math_char(get_math_code(cur_chr));
}
-@ @c
static void run_math_char_num_mmode (void) {
- mathcodeval mval; /* to build up an argument to |set_math_char| */
+ mathcodeval mval;
if (cur_chr == 0)
mval = scan_mathchar(tex_mathcode);
else if (cur_chr == 1)
@@ -476,31 +537,27 @@ static void run_math_char_num_mmode (void) {
set_math_char(mval);
}
-@ @c
static void run_math_given_mmode (void) {
- mathcodeval mval; /* to build up an argument to |set_math_char| */
+ mathcodeval mval;
mval = mathchar_from_integer(cur_chr, tex_mathcode);
set_math_char(mval);
}
static void run_xmath_given_mmode (void) {
- mathcodeval mval; /* to build up an argument to |set_math_char| */
+ mathcodeval mval;
mval = mathchar_from_integer(cur_chr, umath_mathcode);
set_math_char(mval);
}
-@ @c
static void run_delim_num (void) {
- mathcodeval mval; /* to build up an argument to |set_math_char| */
+ 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);
-
}
-@ @c
static void run_vcenter (void) {
scan_spec(vcenter_group);
normal_paragraph();
@@ -511,18 +568,15 @@ static void run_vcenter (void) {
begin_token_list(every_vbox_par, every_vbox_text);
}
-@ @c
static void run_math_style (void) {
tail_append(new_style((small_number) cur_chr));
}
-@ @c
static void run_non_script (void) {
tail_append(new_glue(zero_glue));
subtype(tail) = cond_math_glue;
}
-@ @c
static void run_math_choice (void) {
if (cur_chr == 0)
append_choices();
@@ -530,7 +584,6 @@ static void run_math_choice (void) {
setup_math_style();
}
-@ @c
static void run_math_shift (void) {
if (cur_group == math_shift_group)
after_math();
@@ -538,19 +591,16 @@ static void run_math_shift (void) {
off_save();
}
-@ @c
static void run_after_assignment (void) {
get_token();
after_token = cur_tok;
}
-@ @c
static void run_after_group (void) {
get_token();
save_for_after(cur_tok);
}
-@ @c
static void run_extension (void) {
do_extension(0);
}
@@ -565,12 +615,16 @@ static void run_normal (void) {
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");
+ 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");
+ help1(
+ "You cannot overwrite the current \\catcode table"
+ );
error();
} else {
copy_cat_codes(cat_code_table_par, cur_val);
@@ -581,12 +635,16 @@ static void run_normal (void) {
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");
+ 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");
+ help1(
+ "You cannot overwrite the current \\catcode table"
+ );
error();
} else {
initex_cat_codes(cur_val);
@@ -607,6 +665,12 @@ static void run_normal (void) {
(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;
@@ -617,9 +681,10 @@ static void run_normal (void) {
}
}
-/*
- this is experimental and not used for production, only for testing and writing
- macros (some options stay)
+/*tex
+
+This is experimental and not used for production, only for testing and writing
+macros (some options stay).
*/
@@ -633,14 +698,10 @@ static void run_option(void) {
case math_option_code:
if (scan_keyword("old")) {
mathoption_set_int(c_mathoption_old_code);
- } else if (scan_keyword("noitaliccompensation")) {
- mathoption_set_int(c_mathoption_no_italic_compensation_code);
- } else if (scan_keyword("nocharitalic")) {
- mathoption_set_int(c_mathoption_no_char_italic_code);
- } else if (scan_keyword("useoldfractionscaling")) {
- mathoption_set_int(c_mathoption_use_old_fraction_scaling_code);
+ /*
} else if (scan_keyword("umathcodemeaning")) {
mathoption_set_int(c_mathoption_umathcode_meaning_code);
+ */
} else {
normal_warning("mathoption","unknown key");
}
@@ -651,20 +712,55 @@ static void run_option(void) {
}
}
-@ For mode-independent commands, the following macro is useful.
+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.
+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.
+
+*/
-@c
#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.
+
+*/
-@ The |main_control| uses a jump table, and |init_main_control| sets that table up.
-@c
typedef void (*main_control_function) (void);
+
main_control_function *jump_table;
static void init_main_control (void) {
@@ -749,6 +845,7 @@ static void init_main_control (void) {
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;
@@ -835,6 +932,7 @@ static void init_main_control (void) {
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);
@@ -879,46 +977,181 @@ static void init_main_control (void) {
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);
+
}
-@ And here is |main_control| itself. It is quite short nowadays.
+/*tex
+
+ And here is |main_control| itself. It is quite short nowadays.
+
+*/
-@c
void main_control(void)
{
main_control_state = goto_next;
- init_main_control () ;
-
- if (equiv(every_job_loc) != null)
+ 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; /* reset */
- else
+ if (main_control_state == goto_skip_token) {
+ main_control_state = goto_next;
+ } else {
get_x_token();
-
- /* 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. */
+ }
+ /*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)
+ 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;
+}
- (jump_table[(abs(mode) + cur_cmd)])(); /* run the command */
+/*tex
- if (main_control_state == goto_return) {
+We assume a trailing relax: |{...}\relax|, so we don't need a |back_input()| here.
+
+*/
+
+/*int local_level = 0; */
+
+extern void local_control_message(const char *s)
+{
+ tprint("local control level ");
+ print_int(local_level);
+ tprint(": ");
+ tprint(s);
+ tprint_nl("");
+}
+
+void local_control(void)
+{
+ int ll = local_level;
+ main_control_state = goto_next;
+ local_level += 1;
+ 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 due to level change");
+ }
+ return ;
+ } else if (main_control_state == goto_return) {
+ if (tracing_nesting_par > 2) {
+ local_control_message("leaving due to triggering");
+ }
return;
}
}
- return; /* not reached */
+ return;
+}
+
+void end_local_control(void )
+{
+ local_level -= 1;
+}
+
+/*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;
}
-@ @c
void app_space(void)
{ /* handle spaces when |space_factor<>1000| */
halfword q; /* glue node */
@@ -948,20 +1181,24 @@ void app_space(void)
tail = q;
}
-@ @c
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.");
+ help2(
+ "I've inserted a begin-math/end-math symbol since I think",
+ "you left one out. Proceed, with fingers crossed."
+ );
ins_error();
}
-@ We can silently ignore \.{\\par}s in a math formula.
+/*tex
+
+ We can silently ignore \.{\\par}s in a math formula.
+
+*/
-@c
void insert_dollar_sign_par_end(void)
{
if (!suppress_mathpar_error_par) {
@@ -969,10 +1206,13 @@ void insert_dollar_sign_par_end(void)
}
}
-@ The `|you_cant|' procedure prints a line saying that the current command
-is illegal in the current mode; it identifies these things symbolically.
+/*tex
+
+The `|you_cant|' procedure prints a line saying that the current command is
+illegal in the current mode; it identifies these things symbolically.
+
+*/
-@c
void you_cant(void)
{
print_err("You can't use `");
@@ -980,34 +1220,40 @@ void you_cant(void)
print_in_mode(mode);
}
-@
-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.'
+/*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.'
+
+*/
-@c
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'.");
+ help4(
+ "Sorry, but I'm not programmed to handle this case;",
+ "I'll just pretend that you didn''t ask for it.",
+ "If you're in the wrong mode, you might be able to",
+ "return to the right one by typing `I}' or `I$' or `I\\par'."
+ );
error();
}
-@ Some operations are allowed only in privileged modes, i.e., in cases
-that |mode>0|. The |privileged| function is used to detect violations
-of this rule; it issues an error message and returns |false| if the
-current |mode| is negative.
+/*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.
+
+*/
-@c
boolean privileged(void)
{
if (mode > 0) {
@@ -1018,15 +1264,18 @@ boolean privileged(void)
}
}
-@ 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.''
+/*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.''
+
+*/
+
-@c
boolean its_all_over(void)
{ /* do this when \.{\\end} or \.{\\dump} occurs */
if (privileged()) {
@@ -1044,16 +1293,18 @@ boolean its_all_over(void)
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|.
-@ 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.
-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.
+*/
-@c
void append_glue(void)
{
int s = cur_chr;
@@ -1085,35 +1336,37 @@ void append_glue(void)
}
}
-@ @c
void append_kern(void)
{
- int s; /* |subtype| of the kern node */
- s = cur_chr;
+ 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;
}
-@ 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.
+/*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.
+to insert something into the user's input that will help clean off the top level.
+
+*/
-@c
void off_save(void)
{
- halfword p, q; /* inserted token */
+ halfword p, q;
if (cur_group == bottom_level) {
- /* Drop current token and complain that it was unmatched */
+ /*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.");
+ help1(
+ "Things are pretty mixed up, but I think the worst is over."
+ );
error();
} else {
@@ -1121,8 +1374,11 @@ void off_save(void)
p = get_avail();
set_token_link(temp_token_head, p);
print_err("Missing ");
- /* 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. */
+ /*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);
@@ -1147,23 +1403,27 @@ void off_save(void)
}
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.");
+ 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.
-@ 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.
+*/
-@c
void handle_right_brace(void)
{
halfword p, q; /* for short-term use */
@@ -1176,8 +1436,10 @@ void handle_right_brace(void)
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.");
+ help2(
+ "You've closed more groups than you opened.",
+ "Such booboos are generally harmless, so keep going."
+ );
error();
break;
case semi_simple_group:
@@ -1185,10 +1447,20 @@ void handle_right_brace(void)
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:
- /* 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. */
+ 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:
@@ -1211,7 +1483,9 @@ void handle_right_brace(void)
f = floating_penalty_par;
unsave();
save_ptr--;
- /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */
+ /*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) {
@@ -1236,9 +1510,11 @@ void handle_right_brace(void)
}
break;
case output_group:
- /* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */
+ /*tex
+ this is needed in case the \.{\\output} executes a \.{\\textdir} command.
+ */
if (dir_level(text_dir_ptr) == cur_level) {
- /* DIR: Remove from |text_dir_ptr| */
+ /*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;
@@ -1255,7 +1531,9 @@ void handle_right_brace(void)
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.");
+ help1(
+ "I'm guessing that you meant to end an alignment here."
+ );
ins_error();
break;
case no_align_group:
@@ -1279,7 +1557,6 @@ void handle_right_brace(void)
}
}
-@ @c
void extra_right_brace(void)
{
print_err("Extra }, or forgotten ");
@@ -1294,19 +1571,24 @@ void extra_right_brace(void)
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$}'.");
+ help5(
+ "I've deleted a group-closing symbol because it seems to be",
+ "spurious, as in `$x}$'. But perhaps the } is legitimate and",
+ "you forgot something else, as in `\\hbox{$x}'. In such cases",
+ "the way to recover is to insert both the forgotten and the",
+ "deleted material, e.g., by typing `I$}'."
+ );
error();
incr(align_state);
}
-@ Here is where we clear the parameters that are supposed to revert to their
+/*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.
-@c
+*/
+
void normal_paragraph(void)
{
if (looseness_par != 0)
@@ -1323,27 +1605,35 @@ void normal_paragraph(void)
eq_word_define(dimen_base + shape_mode_code, 0);
}
-@ 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.
+/*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.
+
+*/
-@c
halfword cur_box; /* box to be placed into its context */
-@ The |box_end| procedure does the right thing with |cur_box|, if
-|box_context| represents the context as explained above.
+/*tex
+
+The |box_end| procedure does the right thing with |cur_box|, if |box_context|
+represents the context as explained above.
+
+*/
-@c
void box_end(int box_context)
{
if (box_context < box_flag) {
- /* 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.
- */
+ /*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) {
@@ -1372,15 +1662,28 @@ void box_end(int box_context)
}
}
} else if (box_context < ship_out_flag) {
- /* Store |cur_box| in a box register */
+ /*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) {
- if (box_context > ship_out_flag) {
- /* Append a new leader node that uses |cur_box| */
- /* Get the next non-blank non-relax... */
+ /*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));
@@ -1391,25 +1694,32 @@ void box_end(int box_context)
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.");
+ 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);
}
}
}
-@ the next input should specify a box or perhaps a rule
+/*tex
+
+the next input should specify a box or perhaps a rule
+
+*/
-@c
void scan_box(int box_context)
{
- /* Get the next non-blank non-relax... */
+ /*tex Get the next non-blank non-relax... */
do {
get_x_token();
} while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
@@ -1422,26 +1732,36 @@ void scan_box(int box_context)
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.");
+ 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);
+ }
}
}
-@ @c
void new_graf(boolean indented)
{
halfword p, q, dir_graf_tmp;
halfword dir_rover;
- prev_graf_par = 0;
+ 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;
- /* LOCAL: Add local paragraph node */
+ /*tex Add local paragraph node */
tail_append(make_local_par_node(new_graf_par_code));
if (indented) {
p = new_null_box();
@@ -1470,15 +1790,16 @@ void new_graf(boolean indented)
begin_token_list(every_par_par, every_par_text);
if (nest_ptr == 1) {
checked_page_filter(new_graf);
- build_page(); /* put |par_skip| glue on current page */
+ /*tex put |par_skip| glue on current page */
+ build_page();
}
}
-@ @c
void indent_in_hmode(void)
{
halfword p;
- if (cur_chr > 0) { /* \.{\\indent} */
+ if (cur_chr > 0) {
+ /*tex \.{\\indent} */
p = new_null_box();
width(p) = par_indent_par;
if (abs(mode) == hmode)
@@ -1489,7 +1810,6 @@ void indent_in_hmode(void)
}
}
-@ @c
void head_for_vmode(void)
{
if (mode < 0) {
@@ -1497,8 +1817,10 @@ void head_for_vmode(void)
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).");
+ help2(
+ "To put a horizontal rule in an hbox or an alignment,",
+ "you should use \\leaders or \\hrulefill (see The TeXbook)."
+ );
error();
}
} else {
@@ -1509,19 +1831,23 @@ void head_for_vmode(void)
}
}
-@ TODO (BUG?): |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 is probably
-wrong in some way that relates to in-paragraph displays.
+/*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.
+
+*/
-@c
void end_graf(int line_break_context)
{
if (mode == hmode) {
if ((head == tail) || (vlink(head) == tail)) {
if (vlink(head) == tail)
flush_node(vlink(head));
- pop_nest(); /* null paragraphs are ignored, all contain a |local_paragraph| node */
+ /*tex |null| paragraphs are ignored, all contain a |local_paragraph| node */
+ pop_nest();
} else {
line_break(false, line_break_context);
}
@@ -1534,7 +1860,6 @@ void end_graf(int line_break_context)
}
}
-@ @c
void begin_insert_or_adjust(void)
{
if (cur_cmd != vadjust_cmd) {
@@ -1542,7 +1867,9 @@ void begin_insert_or_adjust(void)
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.");
+ help1(
+ "I'm changing to \\insert0; box \\outputbox is special."
+ );
error();
cur_val = 0;
}
@@ -1561,12 +1888,14 @@ void begin_insert_or_adjust(void)
prev_depth_par = ignore_depth;
}
-@ 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.
+/*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.
+
+*/
-@c
void handle_mark(void)
{
halfword p; /* new node */
@@ -1597,7 +1926,6 @@ void handle_mark(void)
}
}
-@ @c
void append_penalty(void)
{
scan_int();
@@ -1608,29 +1936,34 @@ void append_penalty(void)
}
}
-@ When |delete_last| is called, |cur_chr| is the |type| of node that
-will be deleted, if present.
+/*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.
+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.
+
+*/
-@c
void delete_last(void)
{
halfword p, q; /* run through the current list */
if ((mode == vmode) && (tail == head)) {
- /* Apologize for inability to do the operation now,
- unless \.{\\unskip} follows non-glue */
+ /*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.");
+ 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.",
@@ -1643,7 +1976,7 @@ void delete_last(void)
error();
}
} else {
- /* todo: clean this up */
+ /*tex Todo: clean this up! */
if (!is_char_node(tail)) {
if (type(tail) == cur_chr) {
q = head;
@@ -1665,7 +1998,6 @@ void delete_last(void)
}
}
-@ @c
void unpackage(void)
{
halfword p; /* the box */
@@ -1673,7 +2005,7 @@ void unpackage(void)
int c; /* should we copy? */
halfword s; /* for varmem assignment */
if (cur_chr > copy_code) {
- /* Handle saved items and |goto done| */
+ /*tex Handle saved items and |goto done| */
try_couple_nodes(tail, disc_ptr[cur_chr]);
disc_ptr[cur_chr] = null;
goto DONE;
@@ -1687,9 +2019,11 @@ void unpackage(void)
|| ((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.");
+ 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;
}
@@ -1713,12 +2047,14 @@ void unpackage(void)
}
}
-@
+/*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.
+follows a character. In math mode the same effect is achieved by appending a kern
+of zero here, since italic corrections are supplied later.
+
+*/
-@c
void append_italic_correction(void)
{
halfword p; /* |char_node| at the tail of the current list */
@@ -1734,7 +2070,6 @@ void append_italic_correction(void)
}
}
-@ @c
void append_local_box(int kind)
{
incr(save_ptr);
@@ -1746,20 +2081,23 @@ void append_local_box(int kind)
space_factor_par = 1000;
}
-@ Discretionary nodes are easy in the common case `\.{\\-}', but in the
-general case we must process three braces full of items.
+/*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.
-The space factor does not change when we append a discretionary node,
-but it starts out as 1000 in the subsidiary lists.
+*/
-@c
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);
@@ -1774,7 +2112,7 @@ void append_discretionary(void)
}
set_explicit_disc_penalty(tail);
} else if (cur_chr == automatic_disc) {
- /* - as done in hyphenator */
+ /*tex As done in hyphenator: */
c = get_pre_exhyphen_char(cur_lang_par);
if (c <= 0) {
c = ex_hyphen_char_par;
@@ -1798,7 +2136,7 @@ void append_discretionary(void)
}
set_automatic_disc_penalty(tail);
} else {
- /* \discretionary */
+ /*tex |\discretionary| */
if (scan_keyword("penalty")) {
scan_int();
disc_penalty(tail) = cur_val;
@@ -1810,14 +2148,17 @@ void append_discretionary(void)
push_nest();
mode = -hmode;
space_factor_par = 1000;
- /* already preset: disc_penalty(tail) = hyphen_penalty_par; */
+ /*tex Already preset: |disc_penalty(tail) = hyphen_penalty_par;| */
}
}
-@ The test for |p != null| ensures that empty \.{\\localleftbox} and
- \.{\\localrightbox} commands are not applied.
+/*tex
+
+The test for |p != null| ensures that empty \.{\\localleftbox} and
+\.{\\localrightbox} commands are not applied.
+
+*/
-@c
void build_local_box(void)
{
halfword p;
@@ -1828,41 +2169,51 @@ void build_local_box(void)
decr(save_ptr);
p = vlink(head);
pop_nest();
- if (p != null)
- p = hpack(p, 0, additional, -1);
+ if (p != null) {
+ /*tex Somehow |filtered_hpack| goes beyond the first node so we loose it. */
+ new_hyphenation(p, null);
+ (void) new_ligkern(p, null);
+ p = lua_hpack_filter(p, 0, additional, local_box_group, -1, null);
+ }
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) {
- /* LOCAL: Add local paragraph node */
+ /*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);
}
-@ 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.)
+/*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.)
+
+*/
-@c
void build_discretionary(void)
{
halfword p, q; /* for link manipulation */
int n; /* length of discretionary list */
unsave();
- /* Prune the current list, if necessary, until it contains only
- |char_node|, |kern_node|, |hlist_node|, |vlist_node| 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|. */
+ /*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.");
+ help1(
+ "Discretionary lists must contain only boxes and kerns."
+ );
error();
begin_diagnostic();
tprint_nl("The following discretionary sublist has been deleted:");
@@ -1897,12 +2248,16 @@ void build_discretionary(void)
}
break;
case 2:
- /* Attach list |p| to the current list, and record its length;
- then finish up and |return| */
+ /*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.");
+ 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 {
@@ -1913,9 +2268,10 @@ void build_discretionary(void)
}
}
decr(save_ptr);
+ /*tex There are no other cases. */
return;
break;
- } /* there are no other cases */
+ }
set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1));
new_save_level(disc_group);
scan_left_brace();
@@ -1924,15 +2280,18 @@ void build_discretionary(void)
space_factor_par = 1000;
}
-@ 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.
+/*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.
+
+*/
-@c
void make_accent(void)
{
double s, t; /* amount of slant */
@@ -1944,11 +2303,14 @@ void make_accent(void)
p = new_glyph(f, cur_val);
if (p != null) {
x = x_height(f);
- s = float_cast(slant(f)) / float_constant(65536); /* real division */
+ /*tex real division */
+ s = float_cast(slant(f)) / float_constant(65536);
a = glyph_width(p);
do_assignments();
- /* Create a character node |q| for the next character,
- but set |q:=null| if problems arise */
+ /*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) ||
@@ -1962,22 +2324,26 @@ void make_accent(void)
}
if (q != null) {
- /* 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}.
+ /*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) { /* the accent must be shifted up or down */
+ if (h != x) {
+ /*tex the accent must be shifted up or down */
p = hpack(p, 0, additional, -1);
shift_amount(p) = x - h;
}
- delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); /* real multiplication */
+ /*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);
@@ -1994,34 +2360,43 @@ void make_accent(void)
}
}
-@ 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.
+/*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.
+
+*/
-@c
void align_error(void)
{
if (abs(align_state) > 2) {
- /* Express consternation over the fact that no alignment is in progress */
+ /*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.");
+ 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.");
+ 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();
@@ -2036,43 +2411,55 @@ void align_error(void)
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.");
+ 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();
}
}
-@ The help messages here contain a little white lie, since \.{\\noalign}
-and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
+/*tex
+
+The help messages here contain a little white lie, since \.{\\noalign} and
+\.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
+
+*/
-@c
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.");
+ 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.");
+ help2(
+ "I expect to see \\omit only after tab marks or the \\cr of",
+ "an alignment. Proceed, and I'll ignore this case."
+ );
error();
}
-@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}.
-Let's take a look at what happens when they are used correctly.
+/*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.
+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.
+A devious user might force an |endv| command to occur just about anywhere; we
+must defeat such hacks.
+
+*/
-@c
void do_endv(void)
{
base_ptr = input_ptr;
@@ -2085,7 +2472,7 @@ void do_endv(void)
(input_stack[base_ptr].loc_field != null) ||
(input_stack[base_ptr].state_field != token_list))
fatal_error("(interwoven alignment preambles are not allowed)");
- /*.interwoven alignment preambles... */
+ /*tex interwoven alignment preambles... */
if (cur_group == align_group) {
end_graf(align_group);
if (fin_col())
@@ -2095,55 +2482,62 @@ void do_endv(void)
}
}
-@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
+/*tex
+
+Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
+
+*/
-@c
void cs_error(void)
{
print_err("Extra \\endcsname");
- help1("I'm ignoring this, since I wasn't doing a \\csname.");
+ help1(
+ "I'm ignoring this, since I wasn't doing a \\csname."
+ );
error();
}
-@
- 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.)
+/*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.
+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.
-@ If the user says, e.g., `\.{\\global\\global}', the redundancy is
-silently accepted.
+*/
+/*tex
-@ The different types of code values have different legal ranges; the
+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.
-@c
-#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; \
- } \
+*/
+
+#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)
-@ @c
/*
halfword swap_hang_indent(halfword indentation, halfword shape_mode) {
if (shape_mode == 1 || shape_mode == 3 || shape_mode == -1 || shape_mode == -3) {
@@ -2176,40 +2570,51 @@ void prefixed_command(void)
while (cur_cmd == prefix_cmd) {
if (!odd(a / cur_chr))
a = a + cur_chr;
- /* Get the next non-blank non-relax... */
+ /*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) {
- /* Discard erroneous prefixes and |return| */
+ /*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.");
+ 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();
}
- /* Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant */
+ /*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) && ((a % 4 != 0) || (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.");
+ help1(
+ "I'll pretend you didn't say \\long or \\outer or \\protected here."
+ );
error();
}
- /* Adjust for the setting of \.{\\globaldefs} */
+ /*tex
+ Adjust for the setting of \.{\\globaldefs}
+ */
if (global_defs_par != 0) {
if (global_defs_par < 0) {
if (is_global(a))
@@ -2221,15 +2626,18 @@ void prefixed_command(void)
}
switch (cur_cmd) {
case set_font_cmd:
- /* Here's an example of the way many of the following routines operate.
- (Unfortunately, they aren't all as simple as this.) */
+ /*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:
- /* 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. */
-
+ /*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);
@@ -2246,35 +2654,15 @@ void prefixed_command(void)
break;
case let_cmd:
n = cur_chr;
- if (n == normal) {
- 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();
- }
- } else if (n == normal + 1) {
- /* futurelet */
- get_r_token();
- p = cur_cs;
- get_token();
- q = cur_tok;
- get_token();
- back_input();
- cur_tok = q;
- /* look ahead, then back up */
- /* note that |back_input| doesn't affect |cur_cmd|, |cur_chr| */
- back_input();
- } else {
- /* letcharcode */
- scan_int();
- if (cur_val > 0) {
- cur_cs = active_to_cs(cur_val, true);
- set_token_info(cur_cs, cur_cs + cs_token_flag);
+ 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();
@@ -2284,21 +2672,60 @@ void prefixed_command(void)
if (cur_cmd == spacer_cmd)
get_token();
}
- } else {
+ 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) {
+ cur_cs = active_to_cs(cur_val, true);
+ set_token_info(cur_cs, cur_cs + cs_token_flag);
+ 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();
+ }
+ } else {
+ p = null;
+ tex_error("invalid number for \\letcharcode",NULL);
+ }
+ break;
+ default:
+ /*tex We please the compiler. */
p = null;
- tex_error("invalid number for \\letcharcode",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:
- /* 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}'.
- */
+ /*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;
@@ -2311,13 +2738,13 @@ void prefixed_command(void)
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 {
+ /* 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);
@@ -2363,8 +2790,10 @@ void prefixed_command(void)
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.");
+ help2(
+ "You should have said `\\read<number> to \\cs'.",
+ "I'm going to look for the \\cs now."
+ );
error();
}
get_r_token();
@@ -2374,25 +2803,30 @@ void prefixed_command(void)
break;
case toks_register_cmd:
case assign_toks_cmd:
- /* 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.) */
+ /*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 {
- p = cur_chr; /* |p=every_par_loc| or |output_routine_loc| or \dots */
+ /*tex |p=every_par_loc| or |output_routine_loc| or \dots */
+ p = cur_chr;
}
scan_optional_equals();
- /* Get the next non-blank non-relax non-call token */
+ /*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) {
- /* If the right-hand side is a token parameter
- or token register, finish the assignment and |goto done| */
+ /*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;
@@ -2431,7 +2865,7 @@ void prefixed_command(void)
}
break;
case assign_int_cmd:
- /* Similar routines are used to assign values to the numeric parameters. */
+ /*tex Similar routines are used to assign values to the numeric parameters. */
p = cur_chr;
scan_optional_equals();
scan_int();
@@ -2446,9 +2880,18 @@ void prefixed_command(void)
attr_list_cache = cache_disabled;
word_define(p, cur_val);
break;
+ case assign_direction_cmd:
case assign_dir_cmd:
- /* DIR: Assign direction codes */
- scan_direction();
+ /*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);
@@ -2487,17 +2930,19 @@ void prefixed_command(void)
}
if (abs(mode) == hmode) {
if (no_local_dirs_par > 0) {
- /* tail is non zero but we test anyway */
+ /*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 - dir_swap);
+ 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 - dir_swap));
+ tail_append(new_dir(text_direction_par));
+ subtype(tail) = cancel_dir;
}
} else {
- /* what is the use of nolocaldirs .. maybe we should get rid of it */
+ /*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));
@@ -2505,19 +2950,6 @@ void prefixed_command(void)
} else {
update_text_dir_ptr(cur_val);
}
- /* original:
-
- // if ((no_local_dirs_par > 0) && (abs(mode) == hmode)) {
- // // tail_append(new_dir(text_direction_par) // kind of wrong
- // tail_append(new_dir(text_direction_par - dir_swap)); // better
- // }
-
- update_text_dir_ptr(cur_val);
- if (abs(mode) == hmode) {
- tail_append(new_dir(cur_val));
- dir_level(tail) = cur_level;
- }
- */
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;
@@ -2542,14 +2974,13 @@ void prefixed_command(void)
break;
case def_char_code_cmd:
case def_del_code_cmd:
- /* Let |n| be the largest legal code value, based on |cur_chr| */
+ /*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))
@@ -2621,8 +3052,9 @@ void prefixed_command(void)
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'.");
+ help1(
+ "A style should have been here; I inserted `\\displaystyle'."
+ );
cur_val1 = display_style;
back_error();
} else {
@@ -2652,9 +3084,11 @@ void prefixed_command(void)
do_register_command(a);
break;
case set_box_cmd:
- /* 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. */
+ /*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;
@@ -2665,16 +3099,22 @@ void prefixed_command(void)
scan_box(n);
} else {
print_err("Improper \\setbox");
- help2("Sorry, \\setbox is not allowed after \\halign in a display,",
- "or between \\accent and an accented character.");
+ 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:
- /* 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. */
+ /*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:
@@ -2687,8 +3127,11 @@ void prefixed_command(void)
alter_integer();
break;
case set_box_dimen_cmd:
- /* When some dimension of a box register is changed, the change isn't exactly
- global; but \TeX\ does not look at the \.{\\global} switch. */
+ /*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:
@@ -2722,7 +3165,7 @@ void prefixed_command(void)
p = new_node(shape_node, 2 * n + 1 + 1);
vinfo(p + 1) = n;
n = cur_val;
- varmem[p + 2].cint = n; /* number of penalties */
+ 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 */
@@ -2733,8 +3176,10 @@ void prefixed_command(void)
define(q, shape_ref_cmd, p);
break;
case hyph_data_cmd:
- /* All of \TeX's parameters are kept in |eqtb| except the font information,
- the interaction mode, and the hyphenation tables; these are strictly global.
+ /*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:
@@ -2801,9 +3246,20 @@ void prefixed_command(void)
}
break;
case def_font_cmd:
- /* Here is where the information for a new font gets loaded. */
+ /*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;
@@ -2821,9 +3277,10 @@ void prefixed_command(void)
default:
confusion("prefix");
break;
- } /* end of Assignments cases */
+ }
+ /*tex End of assignments cases. */
DONE:
- /* Insert a token saved by \.{\\afterassignment}, if any */
+ /*tex Insert a token saved by \.{\\afterassignment}, if any. */
if (after_token != 0) {
cur_tok = after_token;
back_input();
@@ -2831,14 +3288,13 @@ void prefixed_command(void)
}
}
-@ @c
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) {
- /* DIR: Remove from |text_dir_ptr| */
+ /* Remove from |text_dir_ptr|. */
halfword text_dir_tmp = vlink(text_dir_ptr);
flush_node(text_dir_ptr);
text_dir_ptr = text_dir_tmp;
@@ -2846,22 +3302,50 @@ void fixup_directions(void)
unsave();
if (abs(mode) == hmode) {
if (temp_no_dirs != 0) {
- /* DIR: Add local dir node */
+ /* Add local dir node. */
tail_append(new_dir(text_direction_par));
- dir_dir(tail) = temporary_dir - dir_swap;
+ dir_dir(tail) = temporary_dir;
+ subtype(tail) = cancel_dir;
}
if (temp_no_whatsits != 0) {
- /* LOCAL: Add local paragraph node */
+ /*tex Add local paragraph node. */
tail_append(make_local_par_node(hmode_par_par_code));
}
}
}
-@ 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.
+/*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.
+
+*/
-@c
void get_r_token(void)
{
RESTART:
@@ -2871,11 +3355,13 @@ void get_r_token(void)
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.");
+ 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;
@@ -2884,7 +3370,6 @@ void get_r_token(void)
}
}
-@ @c
void assign_internal_value(int a, halfword p, int val)
{
halfword n;
@@ -2896,17 +3381,19 @@ void assign_internal_value(int a, halfword p, int val)
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");
+ 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.");
+ help1(
+ "The value for \\outputbox has to be between 0 and 65535."
+ );
error();
} else {
word_define(p, val);
@@ -2915,9 +3402,10 @@ void assign_internal_value(int a, halfword p, int val)
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.");
+ help2(
+ "The value for \\newlinechar has to be no higher than 127.",
+ "Your invalid assignment will be ignored."
+ );
error();
} else {
word_define(p, val);
@@ -2926,9 +3414,10 @@ void assign_internal_value(int a, halfword p, int val)
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.");
+ help2(
+ "The value for \\endlinechar has to be no higher than 127.",
+ "Your invalid assignment will be ignored."
+ );
error();
} else {
word_define(p, val);
@@ -2940,9 +3429,10 @@ void assign_internal_value(int a, halfword p, int val)
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.");
+ 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);
@@ -2953,15 +3443,17 @@ void assign_internal_value(int a, halfword p, int val)
word_define(p, val);
break;
}
- /* 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. */
+ /*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)))) {
- /* LOCAL: Add local paragraph node */
+ /*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);
}
@@ -2980,20 +3472,23 @@ void assign_internal_value(int a, halfword p, int val)
word_define(dimen_base + page_top_offset_code, n);
}
word_define(p, val);
- } else if ((p >= local_base) && (p < toks_base)) { /* internal locals */
+ } else if ((p >= local_base) && (p < toks_base)) {
+ /*tex internal locals */
define(p, call_cmd, val);
} else {
confusion("assign internal value");
}
}
-@ We use the fact that |register<advance<multiply<divide|
+/*tex
-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
+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.
-@c
+*/
+
void do_register_command(int a)
{
int p;
@@ -3011,7 +3506,9 @@ void do_register_command(int a)
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.");
+ help1(
+ "I'm forgetting what you said and not changing anything."
+ );
error();
return;
}
@@ -3032,11 +3529,11 @@ void do_register_command(int a)
if (q == register_cmd) {
scan_optional_equals();
} else if (scan_keyword("by")) {
- /* optional `\.{by}' */
+ /*tex optional `\.{by}' */
}
arith_error = false;
if (q < multiply_cmd) {
- /* Compute result of |register| or |advance|, put it in |cur_val| */
+ /*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();
@@ -3045,7 +3542,6 @@ void do_register_command(int a)
if (q == advance_cmd)
cur_val = cur_val + eqtb[l].cint;
} else {
- /* we can probably save a copy */
scan_glue(p);
if (q == advance_cmd) {
/* Compute the sum of two glue specs */
@@ -3075,7 +3571,7 @@ void do_register_command(int a)
}
}
} else {
- /* Compute result of |multiply| or |divide|, put it in |cur_val| */
+ /*tex Compute result of |multiply| or |divide|, put it in |cur_val| */
scan_int();
if (p < glue_val_level) {
if (q == multiply_cmd) {
@@ -3104,8 +3600,10 @@ void do_register_command(int a)
}
if (arith_error) {
print_err("Arithmetic overflow");
- help2("I can't carry out that multiplication or division,",
- "since the result is out of range.");
+ 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();
@@ -3126,10 +3624,9 @@ void do_register_command(int a)
}
}
-@ @c
void alter_aux(void)
{
- halfword c; /* |hmode| or |vmode| */
+ halfword c; /* |hmode| or |vmode| */
if (cur_chr != abs(mode)) {
report_illegal_case();
} else {
@@ -3142,7 +3639,9 @@ void alter_aux(void)
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.");
+ help1(
+ "I allow only values in the range 1..32767 here."
+ );
int_error(cur_val);
} else {
space_factor_par = cur_val;
@@ -3151,39 +3650,39 @@ void alter_aux(void)
}
}
-@ @c
void alter_prev_graf(void)
{
- int p; /* index into |nest| */
- p = nest_ptr;
+ 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.");
+ help1(
+ "I allow only nonnegative values here."
+ );
int_error(cur_val);
} else {
nest[p].pg_field = cur_val;
}
}
-@ @c
void alter_page_so_far(void)
{
- int c; /* index into |page_so_far| */
- c = cur_chr;
+ int c = cur_chr; /* index into |page_so_far| */
scan_optional_equals();
scan_normal_dimen();
page_so_far[c] = cur_val;
}
-@ @c
+/*tex
+ The value of |c| is 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc.
+*/
+
void alter_integer(void)
{
- int c; /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */
- c = cur_chr;
+ int c = cur_chr;
scan_optional_equals();
scan_int();
if (c == 0) {
@@ -3191,8 +3690,10 @@ void alter_integer(void)
} 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.");
+ 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;
@@ -3203,11 +3704,10 @@ void alter_integer(void)
}
}
-@ @c
void alter_box_dimen(void)
{
- int c; /* |width_offset| or |height_offset| or |depth_offset| */
- int b; /* box number */
+ int c; /* |width_offset| or |height_offset| or |depth_offset| */
+ int b; /* box number */
c = cur_chr;
scan_register_num();
b = cur_val;
@@ -3217,7 +3717,6 @@ void alter_box_dimen(void)
varmem[box(b) + c].cint = cur_val;
}
-@ @c
void new_interaction(void)
{
print_ln();
@@ -3229,21 +3728,27 @@ void new_interaction(void)
fixup_selector(log_opened_global);
}
-@ The \.{\\afterassignment} command puts a token into the global
-variable |after_token|. This global variable is examined just after
-every assignment has been performed.
+/*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
-@c
-halfword after_token; /* zero, or a saved token */
+ Here is a procedure that might be called `Get the next non-blank non-relax
+ non-call non-assignment token'.
-@ Here is a procedure that might be called `Get the next non-blank non-relax
-non-call non-assignment token'.
+*/
-@c
void do_assignments(void)
{
while (true) {
- /* Get the next non-blank non-relax... */
+ /*tex Get the next non-blank non-relax... */
do {
get_x_token();
} while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
@@ -3255,13 +3760,13 @@ void do_assignments(void)
}
}
-@ @c
+/*tex |cur_chr| is 1 for \.{\\openin}, 0 for \.{\\closein}: */
+
void open_or_close_in(void)
{
- int c; /* 1 for \.{\\openin}, 0 for \.{\\closein} */
- int n; /* stream number */
+ int c = cur_chr;
+ int n;
char *fn;
- c = cur_chr;
scan_four_bit_int();
n = cur_val;
if (read_open[n] != closed) {
@@ -3275,7 +3780,8 @@ void open_or_close_in(void)
} while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
back_input();
if (cur_cmd != left_brace_cmd) {
- scan_file_name(); /* set |cur_name| to desired file name */
+ /*tex Set |cur_name| to desired file name. */
+ scan_file_name();
if (cur_ext == get_nullstr())
cur_ext = maketexstring(".tex");
} else {
@@ -3288,14 +3794,17 @@ void open_or_close_in(void)
}
}
-@ @c
-boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */
+/*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 */
+ 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;
@@ -3306,7 +3815,7 @@ void issue_message(void)
str_room(1);
s = make_string();
if (c == 0) {
- /* Print string |s| on the terminal */
+ /*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))
@@ -3315,23 +3824,29 @@ void issue_message(void)
update_terminal();
} else {
- /* 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. */
+ /*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.)");
+ 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.");
+ 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;
@@ -3340,34 +3855,40 @@ void issue_message(void)
flush_str(s);
}
-@ The |error| routine calls on |give_err_help| if help is requested from
-the |err_help| parameter.
+/*tex
+
+The |error| routine calls on |give_err_help| if help is requested from the
+|err_help| parameter.
+
+*/
-@c
void give_err_help(void)
{
token_show(err_help_par);
}
-@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by
-building a token list and then changing the cases of the letters in it.
+/*tex
+
+The \.{\\uppercase} and \.{\\lowercase} commands are implemented by building a
+token list and then changing the cases of the letters in it.
+
+*/
-@c
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 */
+ 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) {
- /* 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.
+ /*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) {
@@ -3390,27 +3911,30 @@ void shift_case(void)
p = token_link(p);
}
back_list(token_link(def_ref));
- free_avail(def_ref); /* omit reference count */
+ free_avail(def_ref);
}
-@ We come finally to the last pieces missing from |main_control|, namely the
+/*tex
+
+We come finally to the last pieces missing from |main_control|, namely the
`\.{\\show}' commands that are useful when debugging.
-@c
+*/
+
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 */
+ 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:
- /* Show the current contents of a box */
+ /*tex Show the current contents of a box. */
scan_register_num();
begin_diagnostic();
tprint_nl("> \\box");
@@ -3422,7 +3946,7 @@ void show_whatever(void)
show_box(box(cur_val));
break;
case show_code:
- /* Show the current meaning of a token, then |goto common_ending| */
+ /*tex Show the current meaning of a token, then |goto common_ending|. */
get_token();
if (interaction == error_stop_mode)
wake_up_terminal();
@@ -3434,7 +3958,7 @@ void show_whatever(void)
print_meaning();
goto COMMON_ENDING;
break;
- /* Cases for |show_whatever| */
+ /*tex Cases for |show_whatever| */
case show_groups:
begin_diagnostic();
show_save_groups();
@@ -3474,8 +3998,10 @@ void show_whatever(void)
}
break;
default:
- /* Show the current value of some parameter or register,
- then |goto common_ending| */
+ /*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();
@@ -3485,7 +4011,7 @@ void show_whatever(void)
goto COMMON_ENDING;
break;
}
- /* Complete a potentially long \.{\\show} command */
+ /*tex Complete a potentially long \.{\\show} command: */
end_diagnostic(true);
print_err("OK");
if (selector == term_and_log) {
@@ -3500,32 +4026,40 @@ void show_whatever(void)
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).");
+ 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.");
+ 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();
}
-@ @c
+/*tex
+ This procedure gets things started properly:
+*/
+
void initialize(void)
-{ /* this procedure gets things started properly */
- int k; /* index into |mem|, |eqtb|, etc. */
- /* Initialize whatever \TeX\ might access */
- /* Set initial values of key variables */
+{
+ 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();
-
- /* Start a new current page */
+ /*tex Start a new current page: */
page_contents = empty;
page_tail = page_head;
#if 0
@@ -3539,7 +4073,7 @@ void initialize(void)
page_max_depth = 0;
initialize_equivalents();
- no_new_control_sequence = true; /* new identifiers are usually forbidden */
+ no_new_control_sequence = true; /* new identifiers are usually forbidden */
init_primitives();
mag_set = 0;
@@ -3559,11 +4093,10 @@ void initialize(void)
stop_at_space = true;
if (ini_version) {
- /* Initialize table entries (done by \.{INITEX} only) */
-
+ /*tex Initialize table entries (done by \.{INITEX} only). */
init_node_mem(500);
initialize_tokens();
- /* Initialize the special list heads and constant nodes */
+ /*tex Initialize the special list heads and constant nodes. */
initialize_alignments();
initialize_buildpage();
@@ -3629,7 +4162,9 @@ void initialize(void)
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 */
@@ -3642,7 +4177,7 @@ void initialize(void)
page_right_offset_par = one_inch;
page_bottom_offset_par = one_inch;
ini_init_primitives();
- hash_used = frozen_control_sequence; /* nothing is used */
+ hash_used = frozen_control_sequence;
hash_high = 0;
cs_count = 0;
set_eq_type(frozen_dont_expand, dont_expand_cmd);
@@ -3655,16 +4190,16 @@ void initialize(void)
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);
- /* bah */
+ /*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/mathcodes.w b/Build/source/texk/web2c/luatexdir/tex/mathcodes.c
index a0a357e5319..42e8b0b15b9 100644
--- a/Build/source/texk/web2c/luatexdir/tex/mathcodes.w
+++ b/Build/source/texk/web2c/luatexdir/tex/mathcodes.c
@@ -1,81 +1,88 @@
-% mathnodes.w
-%
-% Copyright 2006-2012 Taco Hoekwater <taco@@luatex.org>
-% Copyright 2012 Khaled Hosny <khaledhosny@@eglug.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/>.
-
-@ @c
+/*
+
+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"
-@ math codes
-@c
+/*tex
+
+We support tre traditional math codes as well as larger ones suitable for
+\UNICODE\ input and fonts.
+
+*/
+
static sa_tree mathcode_head = NULL;
-/* the 0xFFFFFFFF is a flag value */
+/*tex the |0xFFFFFFFF| is a flag value. */
#define MATHCODESTACK 8
#define MATHCODEDEFAULT 0xFFFFFFFF
#define MATHCODEACTIVE 0xFFFFFFFE
-@ delcodes
-@c
+/*tex
+
+Delcodes are also went larger.
+
+*/
+
static sa_tree delcode_head = NULL;
#define DELCODESTACK 4
#define DELCODEDEFAULT 0xFFFFFFFF
-@ some helpers for mathcode printing
+/*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.
+
+*/
+
-@c
-#define print_hex_digit(A) do { \
- if ((A)>=10) print_char('A'+(A)-10); \
- else print_char('0'+(A)); \
+#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); \
+#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); \
+#define four_hex(A) do { \
+ two_hex((A)/256); \
+ two_hex((A)%256); \
} while (0)
-#define six_hex(A) do { \
- two_hex((A)/65536); \
+#define six_hex(A) do { \
+ two_hex((A)/65536); \
two_hex(((A)%65536)/256); \
- two_hex((A)%256); \
+ two_hex((A)%256); \
} while (0)
-/*
- At some point we will drop the mathchardef 8 bit storage (c_mathoption_umathcode_meaning_code => 1)
- and then some of the conversion can go away. Like mathchar_from_integer: only wide characters are
- possible then.
-*/
-
-
-@ @c
mathcodeval mathchar_from_integer(int value, int extcode)
{
mathcodeval mval;
if (extcode == tex_mathcode) {
- /* printf("can't happen: tex_mathcode\n"); */
mval.class_value = (value / 0x1000);
mval.family_value = ((value % 0x1000) / 0x100);
mval.character_value = (value % 0x100);
@@ -88,12 +95,12 @@ mathcodeval mathchar_from_integer(int value, int extcode)
return mval;
}
-@ @c
void show_mathcode_value_old(int value)
{
print_char('"');
four_hex(value);
}
+
void show_mathcode_value(mathcodeval c)
{
print_char('"');
@@ -104,7 +111,6 @@ void show_mathcode_value(mathcodeval c)
six_hex(c.character_value);
}
-@ @c
static void show_mathcode(int n)
{
mathcodeval c = get_math_code(n);
@@ -114,7 +120,6 @@ static void show_mathcode(int n)
show_mathcode_value(c);
}
-@ @c
static void unsavemathcode(quarterword gl)
{
sa_stack_item st;
@@ -138,7 +143,6 @@ static void unsavemathcode(quarterword gl)
}
}
-@ @c
void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword level)
{
sa_tree_item v;
@@ -161,9 +165,6 @@ void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quar
}
}
-@ @c
-/* we could use two structs ... tex and umath */
-
mathcodeval get_math_code(int n)
{
mathcodeval d;
@@ -189,14 +190,12 @@ mathcodeval get_math_code(int n)
return d;
}
-@ @c
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;
}
-@ @c
static void initializemathcode(void)
{
sa_tree_item sa_value = { 0 };
@@ -214,7 +213,6 @@ static void undumpmathcode(void)
mathcode_head = undump_sa_tree("mathcodes");
}
-@ @c
static void show_delcode(int n)
{
delcodeval c;
@@ -232,7 +230,6 @@ static void show_delcode(int n)
}
}
-@ @c
static void unsavedelcode(quarterword gl)
{
sa_stack_item st;
@@ -256,7 +253,6 @@ static void unsavedelcode(quarterword gl)
}
}
-@ @c
void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl)
{
sa_tree_item v;
@@ -266,7 +262,8 @@ void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, i
v.del_code_value.dummy_value = 0;
v.del_code_value.large_family_value = lmathfamily;
v.del_code_value.large_character_value = lmathcharacter;
- set_sa_item(delcode_head, n, v, gl); /* always global */
+ /*tex Always global! */
+ set_sa_item(delcode_head, n, v, gl);
if (tracing_assigns_par > 1) {
begin_diagnostic();
print_char('{');
@@ -278,7 +275,6 @@ void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, i
}
}
-@ @c
delcodeval get_del_code(int n)
{
delcodeval d;
@@ -299,9 +295,12 @@ delcodeval get_del_code(int n)
return d;
}
-@ this really only works for old-style delcodes!
+/*tex
+
+This really only works for old-style delcodes!
+
+*/
-@c
int get_del_code_num(int n)
{
delcodeval d = get_del_code(n);
@@ -313,7 +312,6 @@ int get_del_code_num(int n)
}
}
-@ @c
static void initializedelcode(void)
{
sa_tree_item sa_value = { 0 };
@@ -321,7 +319,6 @@ static void initializedelcode(void)
delcode_head = new_sa_tree(DELCODESTACK, 2, sa_value);
}
-@ @c
static void dumpdelcode(void)
{
dump_sa_tree(delcode_head,"delcodes");
@@ -332,28 +329,24 @@ static void undumpdelcode(void)
delcode_head = undump_sa_tree("delcodes");
}
-@ @c
void unsave_math_codes(quarterword grouplevel)
{
unsavemathcode(grouplevel);
unsavedelcode(grouplevel);
}
-@ @c
void initialize_math_codes(void)
{
initializemathcode();
initializedelcode();
}
-@ @c
void free_math_codes(void)
{
destroy_sa_tree(mathcode_head);
destroy_sa_tree(delcode_head);
}
-@ @c
void dump_math_codes(void)
{
dumpmathcode();
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.w b/Build/source/texk/web2c/luatexdir/tex/memoryword.w
deleted file mode 100644
index b596abb37ec..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/memoryword.w
+++ /dev/null
@@ -1,55 +0,0 @@
-% 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/>.
-
-@ @c
-
-
-#include "ptexlib.h"
-
-@ When debugging, we may want to print a |memory_word| without knowing
-what type it is; so we print it in all modes.
-
-@c
-#ifdef DEBUG
-void print_word(memory_word w)
-{
- /* prints |w| in all ways */
- print_int(w.cint);
- print_char(' ');
- print_scaled(w.cint);
- print_char(' ');
- print_scaled(round(unity * float_cast(w.gr)));
- print_ln();
- print_int(w.hh.lhfield);
- print_char('=');
- print_int(w.hh.b0);
- print_char(':');
- print_int(w.hh.b1);
- print_char(';');
- print_int(w.hh.rh);
- print_char(' ');
- print_int(w.qqqq.b0);
- print_char(':');
- print_int(w.qqqq.b1);
- print_char(':');
- print_int(w.qqqq.b2);
- print_char(':');
- print_int(w.qqqq.b3);
-}
-#endif
diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.w b/Build/source/texk/web2c/luatexdir/tex/mlist.c
index 291ce8c4868..8f6d71eb294 100644
--- a/Build/source/texk/web2c/luatexdir/tex/mlist.w
+++ b/Build/source/texk/web2c/luatexdir/tex/mlist.c
@@ -1,71 +1,72 @@
-% mlist.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/>.
-
-% (HH / 0.82+):
-
-@ 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:
-
-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.
-
-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.
-
-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.
-
-@ @c
+/*
+
+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))
-@
-\def\LuaTeX{Lua\TeX}
-
-@ @c
-
#include "ptexlib.h"
#include "lua/luatex-api.h"
-@ @c
-#define nDEBUG
-
#define reset_attributes(p,newatt) do { \
delete_attribute_ref(node_attr(p)); \
node_attr(p) = newatt; \
@@ -97,83 +98,160 @@ already apply that shift.
#define font_MATH_par(a,b) \
(font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter)
-@ here are the math parameters that are font-dependant
+/*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.
-@^parameters for symbols@>
-@^font parameters@>
+ 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.
+
+*/
-@c
#define total_mathsy_params 22
#define total_mathex_params 13
#define mathsy(A,B) font_param(fam_fnt(2,A),B)
-#define math_x_height(A) mathsy(A,5) /* height of `\.x' */
-#define math_quad(A) mathsy(A,6) /* \.{18mu} */
-#define num1(A) mathsy(A,8) /* numerator shift-up in display styles */
-#define num2(A) mathsy(A,9) /* numerator shift-up in non-display, non-\.{\\atop} */
-#define num3(A) mathsy(A,10) /* numerator shift-up in non-display \.{\\atop} */
-#define denom1(A) mathsy(A,11) /* denominator shift-down in display styles */
-#define denom2(A) mathsy(A,12) /* denominator shift-down in non-display styles */
-#define sup1(A) mathsy(A,13) /* superscript shift-up in uncramped display style */
-#define sup2(A) mathsy(A,14) /* superscript shift-up in uncramped non-display */
-#define sup3(A) mathsy(A,15) /* superscript shift-up in cramped styles */
-#define sub1(A) mathsy(A,16) /* subscript shift-down if superscript is absent */
-#define sub2(A) mathsy(A,17) /* subscript shift-down if superscript is present */
-#define sup_drop(A) mathsy(A,18) /* superscript baseline below top of large box */
-#define sub_drop(A) mathsy(A,19) /* subscript baseline below bottom of large box */
-#define delim1(A) mathsy(A,20) /* size of \.{\\atopwithdelims} delimiters in display styles */
-#define delim2(A) mathsy(A,21) /* size of \.{\\atopwithdelims} delimiters in non-displays */
-#define axis_height(A) mathsy(A,22) /* height of fraction lines above the baseline */
-
-@ The math-extension parameters have similar macros, but the size code is
-omitted (since it is always |cur_size| when we refer to such parameters).
-@^parameters for symbols@>
-@^font parameters@>
-
-@c
+/*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)
-#define default_rule_thickness(A) mathex(A,8) /* thickness of \.{\\over} bars */
-#define big_op_spacing1(A) mathex(A,9) /* minimum clearance above a displayed op */
-#define big_op_spacing2(A) mathex(A,10) /* minimum clearance below a displayed op */
-#define big_op_spacing3(A) mathex(A,11) /* minimum baselineskip above displayed op */
-#define big_op_spacing4(A) mathex(A,12) /* minimum baselineskip below displayed op */
-#define big_op_spacing5(A) mathex(A,13) /* padding above and below displayed limits */
-@ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of
-the MathConstants values have no matching usage in \LuaTeX\ right now.
+/*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 */
-ScriptPercentScaleDown,
-ScriptScriptPercentScaleDown:
- These should be handled by the macro package, on the engine
- side there are three separate fonts
+#define big_op_spacing4(A) mathex(A,12)
-DelimitedSubFormulaMinHeight:
- This is perhaps related to word's natural math input? I have
- no idea what to do about it
+/*tex padding above and below displayed limits */
-MathLeading:
- LuaTeX does not currently handle multi-line displays, and
- the parameter does not seem to make much sense elsewhere
+#define big_op_spacing5(A) mathex(A,13)
-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
+/*tex
-Also still TODO for OpenType Math:
- * prescripts
+ \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.
-@ this is not really a math parameter at all
+ \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
+
+*/
-@c
static void math_param_error(const char *param, int style)
{
char s[256];
@@ -184,16 +262,11 @@ static void math_param_error(const char *param, int style)
"the parameter mentioned earlier.",
NULL
};
- snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set",
- param, math_style_names[style]);
+ snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", param, math_style_names[style]);
tex_error(s, hlp);
-#if 0
- flush_math();
-#endif
return;
}
-@ @c
static scaled accent_base_height(int f)
{
scaled a;
@@ -207,11 +280,14 @@ static scaled accent_base_height(int f)
return a;
}
-@ 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.
+/*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.
+
+*/
-@c
scaled get_math_quad_style(int var)
{
scaled a = get_math_param(math_param_quad, var);
@@ -223,10 +299,13 @@ scaled get_math_quad_style(int var)
}
}
-@ For this reason the next one is different because it is called with a size
-specifier instead of a style specifier.
+/*tex
+
+ For this reason the next one is different because it is called with a size
+ specifier instead of a style specifier.
+
+*/
-@c
static scaled math_axis_size(int b)
{
scaled a;
@@ -246,7 +325,6 @@ static scaled math_axis_size(int b)
}
}
-@ @c
scaled get_math_quad_size(int b)
{
int var;
@@ -259,26 +337,32 @@ scaled get_math_quad_size(int b)
return get_math_param(math_param_quad, var);
}
-@ @c
static scaled minimum_operator_size(int var)
{
scaled a = get_math_param(math_param_operator_size, var);
return a;
}
-@ 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.
+/*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.
+
+*/
-@c
static scaled radical_rule_par(int var)
{
scaled a = get_math_param(math_param_radical_rule, var);
return a;
}
-@ now follow all the trivial math parameters
+/*tex
+
+ Now follow all the trivial math parameters.
+
+*/
-@c
#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)
@@ -301,9 +385,12 @@ static scaled do_get_math_param_or_zero(int var, int param, const char *name)
return a;
}
-@ A variant on a suggestion on the list based on analysis by UV.
+/*tex
+
+ A variant on a suggestion on the list based on analysis by UV.
+
+*/
-@c
static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
scaled delta, delta1, delta2;
if (axis) {
@@ -313,7 +400,7 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
}
delta1 = max_h + max_d - delta2;
if (delta2 > delta1) {
- /* |delta1| is max distance from axis */
+ /*tex |delta1| is max distance from axis */
delta1 = delta2;
}
delta = (delta1 / 500) * delimiter_factor_par;
@@ -325,7 +412,6 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
}
}
-@ @c
#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)
@@ -359,9 +445,7 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
#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)
-/*
-#define fraction_del_size_old(a) get_math_param(a, math_param_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)
@@ -390,11 +474,11 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) {
#define space_after_script(a) get_math_param_or_error(a, space_after_script)
-@ @c
void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
{
+ if (is_new_mathfont(f)) {
- if (is_new_mathfont(f)) { /* fix all known parameters */
+ /*tex Fix all known parameters. */
DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
font_size(f), lvl);
@@ -632,7 +716,7 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
} else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) {
- /* fix old-style |sy| parameters */
+ /*tex Fix old-style |sy| parameters. */
DEFINE_MATH_PARAMETERS(math_param_quad, size_id,
math_quad(size_id), lvl);
@@ -746,9 +830,11 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id,
(abs(math_x_height(size_id) * 4) / 5), lvl);
- /*
- The display-size |radical_vgap| is done twice because it needs
- values from both the sy and the ex font.
+ /*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,
@@ -768,7 +854,7 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
} else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) {
- /* fix old-style |ex| parameters */
+ /*tex Fix old-style |ex| parameters. */
DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id,
default_rule_thickness(size_id), lvl);
@@ -853,9 +939,12 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id,
4 * default_rule_thickness(size_id), lvl);
- /*
- All of the |space_after_script|s are done in |finalize_math_parameters|
- because the \.{\\scriptspace} may have been altered by the user
+ /*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,
@@ -880,9 +969,11 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id,
big_op_spacing3(size_id), lvl);
- /*
- The display-size |radical_vgap| is done twice because it needs
- values from both the sy and the ex font.
+ /*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,
@@ -891,10 +982,13 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl)
}
}
-@ This needs to be called just at the start of |mlist_to_hlist|, for
-backward compatibility with \.{\\scriptspace}.
+/*tex
+
+ This needs to be called just at the start of |mlist_to_hlist|, for backward
+ compatibility with \.{\\scriptspace}.
+
+*/
-@c
static void finalize_math_parameters(void)
{
int saved_trace = tracing_assigns_par;
@@ -920,16 +1014,19 @@ static void finalize_math_parameters(void)
tracing_assigns_par = saved_trace;
}
-@ In order to convert mlists to hlists, i.e., noads to nodes, we need several
-subroutines that are conveniently dealt with now.
+/*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.)
-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.)
+*/
-@c
static const char *math_size_string(int s)
{
if (s == text_size)
@@ -940,10 +1037,13 @@ static const char *math_size_string(int s)
return "scriptscriptfont";
}
-@ When the style changes, the following piece of program computes associated
-information:
+/*tex
+
+ When the style changes, the following piece of program computes associated
+ information:
+
+*/
-@c
#define setup_cur_size(a) do { \
if (a==script_style || a==cramped_script_style) \
cur_size = script_size; \
@@ -954,8 +1054,12 @@ information:
} while (0)
-@ a simple routine that creates a flat copy of a nucleus
-@c
+/*tex
+
+ A simple routine that creates a flat copy of a nucleus.
+
+*/
+
static pointer math_clone(pointer q)
{
pointer x;
@@ -972,14 +1076,17 @@ static pointer math_clone(pointer q)
return x;
}
-@ 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.
+/*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.
+
+*/
-@c
static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfword cur_size, halfword cur_fam)
{
- pointer p; /* the new node */
+ pointer p;
if (math_rules_mode_par) {
p = new_rule(some_rule);
rule_math_size(p) = cur_size;
@@ -994,14 +1101,17 @@ static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfw
return p;
}
-@ The |overbar| function returns a pointer to a vlist box that consists of
- a given box |b|, above which has been placed a kern of height |k| under a
- fraction rule of thickness |t| under additional space of height |ht|.
+/*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|.
+
+*/
-@c
static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, halfword index, halfword cur_size, halfword cur_fam)
{
- pointer p, q; /* nodes being constructed */
+ pointer p, q;
p = new_kern(k);
reset_attributes(p, att);
couple_nodes(p,b);
@@ -1015,16 +1125,20 @@ static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, ha
return q;
}
-@ 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.
+/*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.
+
+*/
-@c
static pointer char_box(internal_font_number f, int c, pointer bb)
{
- pointer b, p; /* the new box and its character node */
+ /*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);
@@ -1039,24 +1153,31 @@ static pointer char_box(internal_font_number f, int c, pointer bb)
return b;
}
-@ Another handy subroutine computes the height plus depth of
- a given character:
+/*tex
+
+ Another handy subroutine computes the height plus depth of a given character:
+
+*/
-@c
static scaled height_plus_depth(internal_font_number f, int c)
{
return (char_height(f, c) + char_depth(f, c));
}
-@ When we build an extensible character, it's handy to have the
- following subroutine, which puts a given character on top
- of the characters already in box |b|:
+/*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|:
+
+*/
-@c
static scaled stack_into_box(pointer b, internal_font_number f, int c)
{
- pointer p, q; /* new node placed into |b| */
- p = char_box(f, c, node_attr(b)); /* italic gets added to width */
+ /*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;
@@ -1085,7 +1206,9 @@ 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;
- reset_attributes(p, node_attr(b));
+ 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;
@@ -1094,59 +1217,96 @@ static void stack_glue_into_box(pointer b, scaled min, scaled max) {
if (q == null) {
list_ptr(b) = p;
} else {
- while (vlink(q) != null)
+ while (vlink(q) != null) {
q = vlink(q);
+ }
couple_nodes(q,p);
}
}
}
-@ \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.
-@^recursion@>
-
-@c
-int cur_size; /* size code corresponding to |cur_style| */
-
-@ @c
-static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, scaled v,
- pointer att, int cur_style, int boxtype)
+/*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;
+
+static pointer get_delim_box(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att)
{
- pointer b; /* new box */
- scaled b_max; /* natural (maximum) size of the stack */
- scaled s_max; /* amount of possible shrink in the stack */
+ 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;
- scaled min_overlap, prev_overlap;
- int i; /* a temporary counter number of extensible pieces */
- int with_extenders; /* number of times to repeat each repeatable item in |ext| */
+ 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;
-
- assert(ext != NULL);
b = new_null_box();
- type(b) = (quarterword) boxtype;
- reset_attributes(b, att);
- min_overlap = connector_overlap_min(cur_style);
- assert(min_overlap >= 0);
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(f, cur->glyph)) {
+ 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!",
@@ -1160,7 +1320,7 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s
num_extenders++;
else
num_normal++;
- /* no negative overlaps or advances are allowed */
+ /*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.",
@@ -1190,16 +1350,16 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s
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.
+ 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;
@@ -1217,12 +1377,15 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s
c = prev_overlap;
a = cur->advance;
if (a == 0) {
- /* for tfm fonts */
- if (boxtype == vlist_node)
- a = height_plus_depth(f, cur->glyph);
- else
- a = char_width(f, cur->glyph);
- assert(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;
@@ -1236,12 +1399,15 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s
c = prev_overlap;
a = cur->advance;
if (a == 0) {
- /* for tfm fonts */
- if (boxtype == vlist_node)
- a = height_plus_depth(f, cur->glyph);
- else
- a = char_width(f, cur->glyph);
- assert(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;
@@ -1250,10 +1416,11 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s
}
}
}
+ /*tex
+
+ Assemble box using |with_extenders| copies of each extender, with
+ appropriate glue wherever an overlap occurs.
- /*
- assemble box using |with_extenders| copies of each extender, with
- appropriate glue wherever an overlap occurs
*/
prev_overlap = 0;
b_max = 0;
@@ -1271,7 +1438,7 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s
s_max += (-c) - (-d);
b_max -= d;
}
- b_max += stack_into_box(b, f, cur->glyph);
+ b_max += stack_into_box(b, fnt, cur->glyph);
prev_overlap = cur->end_overlap;
i--;
} else {
@@ -1288,19 +1455,17 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s
s_max += (-c) - (-d);
b_max -= d;
}
- b_max += stack_into_box(b, f, cur->glyph);
+ b_max += stack_into_box(b, fnt, cur->glyph);
prev_overlap = cur->end_overlap;
i--;
}
}
}
-
- /* set glue so as to stretch the connections if needed */
-
+ /*tex Set glue so as to stretch the connections if needed. */
d = 0;
if (v > b_max && s_max > 0) {
d = v-b_max;
- /* don't stretch more than |s_max| */
+ /*tex Don't stretch more than |s_max|. */
if (d > s_max)
d = s_max;
glue_order(b) = normal;
@@ -1308,32 +1473,33 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s
glue_set(b) = unfloat(d/(float) s_max);
b_max += d;
}
-
- if (boxtype == vlist_node) {
- height(b) = b_max;
- } else {
+ if (horizontal) {
width(b) = b_max;
+ } else {
+ height(b) = b_max;
}
-
return b;
}
-@ 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.
-
-@c
+/*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];
@@ -1343,23 +1509,31 @@ static void endless_loop_error(internal_font_number g, int y)
"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));
+ 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)
{
- pointer b; /* the box that will be constructed */
- internal_font_number f, g; /* best-so-far and tentative font codes */
- int c, i, x, y; /* best-so-far and tentative character codes */
- scaled u; /* height-plus-depth of a tentative character */
- scaled w = 0; /* largest height-plus-depth so far */
- int z; /* runs through font family members */
- boolean large_attempt = false; /* are we trying the ``large'' variant? */
- pointer att = null; /* to save the current attribute list */
+ /*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;
f = null_font;
c = 0;
@@ -1373,10 +1547,12 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat,
same = 0;
}
while (true) {
- /*
- 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.
+ /*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);
@@ -1403,7 +1579,6 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat,
goto FOUND;
}
if (i > 10000) {
- /* endless loop */
endless_loop_error(g, y);
goto FOUND;
}
@@ -1414,8 +1589,10 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat,
}
}
}
- if (large_attempt)
- goto FOUND; /* there were none large enough */
+ if (large_attempt) {
+ /*tex There were none large enough. */
+ goto FOUND;
+ }
large_attempt = true;
z = large_fam(d);
x = large_char(d);
@@ -1427,17 +1604,20 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat,
flush_node(d);
}
if (f != null_font) {
- /*
- When the following code is executed, |do_parts| will be true
- if a built-up symbol is supposed to be returned.
+ /*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(d, ext, f, v, att, cur_style, hlist_node);
+ b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 1, att);
} else {
- b = get_delim_box(d, ext, f, v, att, cur_style, vlist_node);
+ b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 0, att);
}
if (delta != NULL) {
if (do_new_math(f)) {
@@ -1449,16 +1629,18 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat,
if (stack != NULL)
*stack = true ;
} else {
+ parts_done = false;
if (same != NULL && x == c) {
*same = emas;
}
b = char_box(f, c, att);
if (!do_new_math(f)) {
- /* italic gets added to width */
+ /*tex Italic gets added to width. */
width(b) += char_italic(f, c);
}
if (delta != NULL) {
- *delta = char_italic(f, c); /* was historically (f, x) */
+ /*tex This used to be (f, x). */
+ *delta = char_italic(f, c);
}
if (stack != NULL)
*stack = false ;
@@ -1469,7 +1651,7 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat,
if (flat) {
width(b) = 0;
} else {
- /* use this width if no delimiter was found */
+ /*tex Use this width if no delimiter was found. */
width(b) = null_delimiter_space_par;
}
if (delta != NULL) {
@@ -1479,38 +1661,54 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat,
*stack = false ;
}
if (!flat) {
- if (emas == 0 || ! delimitermodenoshift) {
- /* vertical variant */
- shift_amount(b) = half(height(b) - depth(b));
- if (shift) {
- shift_amount(b) -= math_axis_size(s);
- }
+ /*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;
}
-@ 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.
+/*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.
-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.
+*/
-@c
static pointer rebox(pointer b, scaled w)
{
- pointer p, q, r, att; /* temporary registers for list manipulation */
- internal_font_number f; /* font in a one-character box */
- scaled v; /* width of a character without italic correction */
-
+ /*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);
@@ -1549,23 +1747,30 @@ static pointer rebox(pointer b, scaled w)
}
}
-@ 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.
+/*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.
+
+*/
-@c
#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)
{
- int n = x_over_n(m, unity); /* integer part of |m| */
- scaled f = tex_remainder; /* fraction part of |m| */
- pointer p; /* the new glue specification */
+ /*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);
- width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */
+ /* 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));
@@ -1581,13 +1786,16 @@ static pointer math_glue(pointer g, scaled m)
static void math_glue_to_glue(pointer p, scaled m)
{
- int n = x_over_n(m, unity); /* integer part of |m| */
- scaled f = tex_remainder; /* fraction part of |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;
}
- width(p) = mu_mult(width(p)); /* convert \.{mu} to \.{pt} */
+ /* 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)
@@ -1595,14 +1803,18 @@ static void math_glue_to_glue(pointer p, scaled m)
subtype(p) = normal;
}
-@ The |math_kern| subroutine removes |mu_glue| from a kern node, given
-the value of the math unit.
+/*tex
+
+ The |math_kern| subroutine removes |mu_glue| from a kern node, given the
+ value of the math unit.
-@c
+*/
static void math_kern(pointer p, scaled m)
{
- int n; /* integer part of |m| */
- scaled f; /* fraction part of |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;
@@ -1611,15 +1823,15 @@ static void math_kern(pointer p, scaled m)
f = f + unity;
}
width(p) = mu_mult(width(p));
- subtype(p) = italic_kern; /* this is weird, it's not a italic but explicit_kern */
+ /* this is weird, it's not a italic but explicit_kern */
+ subtype(p) = italic_kern;
}
}
-@ @c
void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
{
int callback_id;
- int a, sfix;
+ int a, sfix, i;
if (p == null) {
vlink(temp_head) = null;
return;
@@ -1636,18 +1848,16 @@ void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
nodelist_to_lua(Luas, p);
lua_push_math_style_name(Luas, mstyle);
lua_pushboolean(Luas, penalties);
- if (lua_pcall(Luas, 3, 1, 0) != 0) { /* 3 args, 1 result */
- char errmsg[256]; /* temp hack ... we will have a formatted error */
- snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1));
- errmsg[255]='\0';
+ if ((i=lua_pcall(Luas, 3, 1, 0)) != 0) {
+ formatted_warning("mlist to hlist","error: %s",lua_tostring(Luas, -1));
lua_settop(Luas, sfix);
- normal_error("mlist to hlist",errmsg); /* to be done */
+ luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1));
return;
}
- a = nodelist_from_lua(Luas);
+ a = nodelist_from_lua(Luas,-1);
/* alink(vlink(a)) = null; */
- lua_settop(Luas, sfix);
vlink(temp_head) = a;
+ lua_settop(Luas, sfix);
} else if (callback_id == 0) {
mlist_to_hlist(p, penalties, mstyle);
} else {
@@ -1655,22 +1865,27 @@ void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle)
}
}
-@ The recursion in |mlist_to_hlist| is due primarily to a subroutine
-called |clean_box| that puts a given noad field into a box using a given
-math style; |mlist_to_hlist| can call |clean_box|, which can call
-|mlist_to_hlist|.
-@^recursion@>
+/*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.
+ The box returned by |clean_box| is ``clean'' in the sense that its
+ |shift_amount| is zero.
+
+*/
-@c
static pointer clean_box(pointer p, int s, int cur_style)
{
- pointer q; /* beginning of a list to be boxed */
- pointer x; /* box to be returned */
- pointer r; /* temporary pointer */
- pointer mlist = null; /* beginning of mlist to be translated */
+ /*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();
@@ -1689,18 +1904,20 @@ static pointer clean_box(pointer p, int s, int cur_style)
goto FOUND;
}
mlist_to_hlist(mlist, false, s);
- q = vlink(temp_head); /* recursive call */
+ /*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))
- x = q; /* it's already clean */
+ /*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));
- /* Here we save memory space in a common case. */
+ /*tex Here we save memory space in a common case. */
q = list_ptr(x);
if (is_char_node(q)) {
r = vlink(q);
@@ -1708,7 +1925,7 @@ static pointer clean_box(pointer p, int s, int cur_style)
if (vlink(r) == null) {
if (!is_char_node(r)) {
if (type(r) == kern_node) {
- /* unneeded italic correction */
+ /*tex Unneeded italic correction. */
flush_node(r);
vlink(q) = null;
}
@@ -1719,22 +1936,30 @@ static pointer clean_box(pointer p, int s, int cur_style)
return x;
}
-@ 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|.
+/*tex
-The outputs of |fetch| are placed in global variables.
+ 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|.
-@c
-internal_font_number cur_f; /* the |font| field of a |math_char| */
-int cur_c; /* the |character| field of a |math_char| */
+ The outputs of |fetch| are placed in global variables.
-@ Here we unpack the |math_char| field |a|.
+*/
+
+/*tex the |font| field of a |math_char| */
+
+internal_font_number cur_f;
+
+/*tex the |character| field of a |math_char| */
-@c static void fetch(pointer a)
+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);
@@ -1757,75 +1982,125 @@ int cur_c; /* the |character| field of a |math_char| */
}
}
-@ We need to do a lot of different things, so |mlist_to_hlist| makes two
-passes over the given mlist.
+/*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|.
-@^recursion@>
+ 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.
+ The second pass eliminates all noads and inserts the correct glue and
+ penalties between nodes.
+
+*/
-@c
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;
+ 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;
}
-@ @c
#define choose_mlist(A) do { p=A(q); A(q)=null; } while (0)
-@ 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.
+/*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.
+
+*/
-@c
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),
- overbar_vgap(cur_style), overbar_rule(cur_style),
- overbar_kern(cur_style), node_attr(nucleus(q)), math_over_rule, cur_size, cur_fam);
+ overbar_vgap(cur_style), used_thickness, overbar_kern(cur_style),
+ node_attr(nucleus(q)), math_over_rule, cur_size, used_fam);
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)
{
- pointer p, x, y, r; /* temporary registers for box construction */
- scaled delta; /* overall height plus depth */
+ /*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);
p = new_kern(underbar_vgap(cur_style));
reset_attributes(p, node_attr(q));
couple_nodes(x,p);
- r = do_fraction_rule(underbar_rule(cur_style), node_attr(q), math_under_rule, cur_size, 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, 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));
@@ -1838,8 +2113,10 @@ static void make_under(pointer q, int cur_style, int cur_size, int cur_fam)
static void make_vcenter(pointer q)
{
- pointer v; /* the box that should be centered vertically */
- scaled delta; /* its height plus depth */
+ /*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");
@@ -1848,16 +2125,19 @@ static void make_vcenter(pointer q)
depth(v) = delta - height(v);
}
-@ According to the rules in the \.{DVI} file specifications, we ensure alignment
-@^square roots@>
-between a square root sign and the rule above its nucleus by assuming that the
-baseline of the square-root symbol is the same as the bottom of the rule. The
-height of the square-root symbol will be the thickness of the rule, and the
-depth of the square-root symbol should exceed or equal the height-plus-depth
-of the nucleus plus a certain minimum clearance~|psi|. The symbol will be
-placed so that the actual clearance is |psi| plus half the excess.
+/*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.
+
+*/
-@c
static void make_hextension(pointer q, int cur_style)
{
pointer e, p;
@@ -1885,25 +2165,50 @@ static void make_hextension(pointer q, int cur_style)
static void make_radical(pointer q, int cur_style)
{
- pointer x, y, p, l1, l2; /* temporary registers for box construction */
- scaled delta, clr, theta, h; /* dimensions involved in the calculation */
+ /*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);
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) {
- /* a real radical */
+ /*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)) {
- /* possible composite */
+ /*tex possible composite */
l2 = list_ptr(l1);
if ((l2 != null) && (type(l2) == glyph_node)) {
- /* top character */
+ /*tex top character */
theta = char_height(font(l2), character(l2));
} else {
theta = height(y);
@@ -1912,18 +2217,29 @@ static void make_radical(pointer q, int cur_style)
theta = height(y);
}
} else {
- /* not really a radical but we use its node, historical sharing (like in mathml) */
+ /*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) {
- /* increase the actual clearance */
+ /*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, small_fam(left_delimiter(q)));
+ p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y), math_radical_rule, cur_size, used_fam);
couple_nodes(y,p);
if (degree(q) != null) {
scaled wr, br, ar;
@@ -1949,7 +2265,7 @@ static void make_radical(pointer q, int cur_style)
couple_nodes(x,r);
y = x;
}
- /* for \.{\\Uroot ..{<list>}{}} : */
+ /*tex for \.{\\Uroot ..{<list>}{}} : */
math_list(degree(q)) = null;
flush_node(degree(q));
}
@@ -1959,12 +2275,11 @@ static void make_radical(pointer q, int cur_style)
type(nucleus(q)) = sub_box_node;
}
-@ Construct a vlist box
+/*tex Construct a vlist box: */
-@c
static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down)
{
- pointer p; /* temporary register for box construction */
+ pointer p;
pointer v = new_null_box();
type(v) = vlist_node;
height(v) = shift_up + height(x);
@@ -1978,9 +2293,7 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal
return v;
}
-/* when exact use radicalwidth (y is delimiter) */
-
-@ @c
+/*tex When |exact| use radicalwidth (|y| is delimiter). */
#define fixup_widths(q,x,y) do { \
if (width(y) >= width(x)) { \
@@ -2014,7 +2327,7 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal
width(r) = radicalwidth(q); \
reset_attributes(r, node_attr(q)); \
} else if (radicalright(q)) { \
- /* also kind of exact compared to vertical */ \
+ /*tex also kind of exact compared to vertical */ \
r = hpack(r, 0, additional, -1); \
width(r) = radicalwidth(q); \
reset_attributes(r, node_attr(q)); \
@@ -2030,12 +2343,15 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal
} \
} while (0)
-@ this has the |nucleus| box |x| as a limit above an extensible delimiter |y|
+/*tex
+
+ This has the |nucleus| box |x| as a limit above an extensible delimiter |y|.
+
+*/
-@c
static void make_over_delimiter(pointer q, int cur_style)
{
- pointer x, y, v; /* temporary registers for box construction */
+ 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);
@@ -2052,17 +2368,21 @@ static void make_over_delimiter(pointer q, int cur_style)
shift_up = shift_up + delta;
}
v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
- width(v) = width(x); /* this also equals |width(y)| */
+ /*tex This also equals |width(y)|: */
+ width(v) = width(x);
math_list(nucleus(q)) = v;
type(nucleus(q)) = sub_box_node;
}
-@ this has the extensible delimiter |x| as a limit below |nucleus| box |y|
+/*tex
+
+ This has the extensible delimiter |x| as a limit below |nucleus| box |y|.
+
+*/
-@c
static void make_under_delimiter(pointer q, int cur_style)
{
- pointer x, y, v; /* temporary registers for box construction */
+ 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);
@@ -2079,17 +2399,21 @@ static void make_under_delimiter(pointer q, int cur_style)
shift_down = shift_down + delta;
}
v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
- width(v) = width(y); /* this also equals |width(y)| */
+ /*tex This also equals |width(y)|: */
+ width(v) = width(y);
math_list(nucleus(q)) = v;
type(nucleus(q)) = sub_box_node;
}
-@ this has the extensible delimiter |x| as a limit above |nucleus| box |y|
+/*tex
+
+ This has the extensible delimiter |x| as a limit above |nucleus| box |y|.
+
+*/
-@c
static void make_delimiter_over(pointer q, int cur_style)
{
- pointer x, y, v; /* temporary registers for box construction */
+ pointer x, y, v;
scaled shift_up, shift_down, clr, actual, wd;
boolean stack;
y = clean_box(nucleus(q), cur_style, cur_style);
@@ -2106,17 +2430,21 @@ static void make_delimiter_over(pointer q, int cur_style)
shift_up = shift_up + (clr-actual);
}
v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
- width(v) = width(x); /* this also equals |width(y)| */
+ /*tex This also equals |width(y)|: */
+ width(v) = width(x);
math_list(nucleus(q)) = v;
type(nucleus(q)) = sub_box_node;
}
-@ this has the extensible delimiter |y| as a limit below a |nucleus| box |x|
+/*tex
+
+ This has the extensible delimiter |y| as a limit below a |nucleus| box |x|.
+
+*/
-@c
static void make_delimiter_under(pointer q, int cur_style)
{
- pointer x, y, v; /* temporary registers for box construction */
+ pointer x, y, v;
scaled shift_up, shift_down, clr, actual, wd;
boolean stack;
x = clean_box(nucleus(q), cur_style, cur_style);
@@ -2133,16 +2461,20 @@ static void make_delimiter_under(pointer q, int cur_style)
shift_down += (clr-actual);
}
v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down);
- width(v) = width(y); /* this also equals |width(y)| */
+ /*tex This also equals |width(y)|: */
+ width(v) = width(y);
math_list(nucleus(q)) = v;
type(nucleus(q)) = sub_box_node;
}
-@ 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.
+/*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.
+
+*/
-@c
#define TOP_CODE 1
#define BOT_CODE 2
#define OVERLAY_CODE 4
@@ -2150,14 +2482,17 @@ respect to the size of the final box.
static boolean compute_accent_skew(pointer q, int flags, scaled *s)
{
- pointer p; /* temporary register for box construction */
- boolean s_is_absolute = false; /* will be true if a top-accent is placed in |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)) {
- /*
- there is no bot_accent so let's assume similarity
+ /*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) {
@@ -2169,6 +2504,7 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s)
s_is_absolute = true;
}
}
+ \stoptyping
*/
*s = char_top_accent(cur_f, cur_c);
if (*s != INT_MIN) {
@@ -2182,15 +2518,24 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s)
}
}
} else if (type(nucleus(q)) == sub_mlist_node) {
- /*
- if |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we
-
- * use the positioning of the nucleus of that noad, recursing until
- * the inner most |accent_noad|. This way multiple stacked accents are
- * aligned to the inner most one.
-
- the vlink test was added in version 1.06, so that we only consider a lone
- noad:
+ /*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
@@ -2213,12 +2558,18 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s)
static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style)
{
- pointer p, r, x, y; /* temporary registers for box construction */
- scaled s; /* amount to skew the accent to the right */
- scaled h; /* height of character being accented */
- scaled delta; /* space to remove between accent and accentee */
- scaled w; /* width of the accentee, not including sub/superscripts */
- boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */
+ /*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 ;
@@ -2232,7 +2583,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl
if (fraction == 0) {
fraction = 1000;
}
- /* Compute the amount of skew, or set |s| to an alignment point */
+ /*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);
w = width(x);
@@ -2241,7 +2592,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl
s = half(w);
s_is_absolute = true;
}
- /* Switch to a larger accent if available and appropriate */
+ /*tex Switch to a larger accent if available and appropriate */
y = null;
ext = NULL;
if (flags & OVERLAY_CODE) {
@@ -2258,31 +2609,32 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl
}
}
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)) {
- /* a bit weird for an overlay but anyway, here we don't need a factor as we don't step */
- y = get_delim_box(q, ext, f, w, node_attr(attr_p), cur_style, hlist_node);
- break;
- } else if (char_tag(f, c) != list_tag) {
- break;
- } else {
- int yy = char_remainder(f, c);
- if (!char_exists(f, yy)) {
+ 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 if (flags & OVERLAY_CODE) {
- if (char_height(f, yy) > target) {
- break;
- }
} else {
- if (char_width(f, yy) > target)
- break;
+ 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;
}
- c = yy;
}
- }
}
if (y == null) {
- y = char_box(f, c, node_attr(attr_p)); /* italic gets added to width */
+ /*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)) {
@@ -2291,13 +2643,14 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl
delta = accent_base_height(f);
}
} else if (flags & OVERLAY_CODE) {
- delta = half(height(y) + depth(y) + height(x) + depth(x)); /* center the accent vertically around the accentee */
+ /*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) {
- /* swap the subscript and superscript into box |x| */
+ /*tex swap the subscript and superscript into box |x| */
flush_node_list(x);
x = new_noad();
r = math_clone(nucleus(q));
@@ -2313,32 +2666,34 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl
h = height(x);
}
} else if ((vlink(q) != null) && (type(nucleus(q)) == math_char_node)) {
- /* only pure math char nodes */
+ /*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)));
}
}
- /* the top accents of both characters are aligned */
+ /*tex the top accents of both characters are aligned */
if (s_is_absolute) {
scaled sa;
if (ext != NULL) {
- /* if the accent is extensible just take the center */
+ /*tex if the accent is extensible just take the center */
sa = half(width(y));
} else {
- /*
- there is no bot_accent so let's assume similarity
+ /*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) {
- /* just take the center */
+ /*tex just take the center */
sa = half(width(y));
}
if (math_direction_par == dir_TRT) {
@@ -2371,7 +2726,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl
y = r;
if (flags & (TOP_CODE | OVERLAY_CODE)) {
if (height(y) < h) {
- /* make the height of box |y| equal to |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));
@@ -2382,7 +2737,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl
shift_amount(y) = -(h - height(y));
}
if (ic != 0) {
- /* old font codepath has ic built in, new font code doesn't */
+ /*tex old font codepath has ic built in, new font code doesn't */
width(r) += ic ;
}
math_list(nucleus(q)) = y;
@@ -2420,26 +2775,48 @@ static void make_math_accent(pointer q, int cur_style)
}
}
-@ The |make_fraction| procedure is a bit different because it sets
-|new_hlist(q)| directly rather than making a sub-box.
+/*tex
+
+ The |make_fraction| procedure is a bit different because it sets
+ |new_hlist(q)| directly rather than making a sub-box.
+
+*/
-@c
static void make_fraction(pointer q, int cur_style)
{
- pointer p, p1, p2, v, x, y, z, l, r, m; /* temporary registers for box construction */
- scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2;
- /* dimensions for box calculations */
+ 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
- */
+ are displaced from the baseline.
+ */
x = clean_box(numerator(q), num_style(cur_style), cur_style);
z = clean_box(denominator(q), denom_style(cur_style), cur_style);
-
if (middle_delimiter(q) != null) {
delta = 0;
m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL);
@@ -2452,17 +2829,18 @@ static void make_fraction(pointer q, int cur_style)
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);
- /*
- 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|.
+ /*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)));
@@ -2473,9 +2851,11 @@ static void make_fraction(pointer q, int cur_style)
} else {
shift_up = fraction_num_up(cur_style);
shift_down = fraction_denom_down(cur_style);
- /*
- in the case of a fraction line, the minimum clearance depends on the actual
- thickness of the line.
+ /*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);
@@ -2497,15 +2877,17 @@ static void make_fraction(pointer q, int cur_style)
}
}
if (m != null) {
- /*
- construct a hlist box for the fraction, according to |hgap| and |vgap|
+ /*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));
@@ -2516,7 +2898,6 @@ static void make_fraction(pointer q, int cur_style)
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;
@@ -2526,7 +2907,6 @@ static void make_fraction(pointer q, int cur_style)
depth(v) = depth(z) + shift_down;
shift_amount(v) = shift_down;
z = v;
-
v = new_null_box();
reset_attributes(v, node_attr(q));
type(v) = hlist_node;
@@ -2546,7 +2926,6 @@ static void make_fraction(pointer q, int cur_style)
if (depth(m) > depth(v)) {
depth(v) = depth(m);
}
-
if (fractionexact(q)) {
delta1 = -half(skewed_fraction_hgap(cur_style));
delta2 = delta1;
@@ -2557,33 +2936,34 @@ static void make_fraction(pointer q, int cur_style)
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 {
- /*
- construct a vlist box for the fraction, according to |shift_up| and |shift_down|
+ /*tex
+
+ Construct a vlist box for the fraction, according to |shift_up| and
+ |shift_down|.
+
*/
v = new_null_box();
type(v) = vlist_node;
height(v) = shift_up + height(x);
depth(v) = depth(z) + shift_down;
- width(v) = width(x); /* this also equals |width(z)| */
+ /*tex This also equals |width(z)|. */
+ width(v) = width(x);
reset_attributes(v, node_attr(q));
if (thickness(q) == 0) {
p = new_kern((shift_up - depth(x)) - (height(z) - shift_down));
couple_nodes(p,z);
} else {
- y = do_fraction_rule(thickness(q), node_attr(q), math_fraction_rule, cur_size, math_rules_fam_par);
+ 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);
@@ -2595,16 +2975,14 @@ static void make_fraction(pointer q, int cur_style)
couple_nodes(x,p);
list_ptr(v) = x;
}
- /*
- put the fraction into a box with its delimiters, and make |new_hlist(q)|
- point to it
+ /*tex
+
+ Put the fraction into a box with its delimiters, and make |new_hlist(q)|
+ point to it.
+
*/
if (do_new_math(cur_f)) {
- if (math_use_old_fraction_scaling_par) {
- delta = fraction_del_size_old(cur_style);
- } else {
- delta = fraction_del_size_new(cur_style);
- }
+ delta = fraction_del_size_new(cur_style);
if (delta == undefined_math_parameter) {
delta = get_delimiter_height(depth(v), height(v), true);
}
@@ -2622,31 +3000,38 @@ static void make_fraction(pointer q, int cur_style)
assign_new_hlist(q, y);
}
-@ 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.
+/*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.
-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.
-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.
+*/
-@c
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)
{
- scaled delta = 0; /* offset between subscript and superscript */
+ /*tex offset between subscript and superscript */
+ scaled delta = 0;
scaled dummy = 0;
- pointer p, v, x, y, z, n; /* temporary registers for box construction */
- int c; /* register for character examination */
- scaled shift_up, shift_down; /* dimensions for box calculation */
+ /*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)) {
@@ -2655,10 +3040,10 @@ static scaled make_op(pointer q, int cur_style)
if (type(nucleus(q)) == math_char_node) {
fetch(nucleus(q));
if (cur_style < text_style) {
- /* try to make it larger */
+ /*tex try to make it larger */
ok_size = minimum_operator_size(cur_style);
if (ok_size != undefined_math_parameter) {
- /* creating a temporary delimiter is the cleanest way */
+ /*tex creating a temporary delimiter is the cleanest way */
y = new_node(delim_node, 0);
reset_attributes(y, node_attr(q));
small_fam(y) = math_fam(nucleus(q));
@@ -2666,9 +3051,9 @@ static scaled make_op(pointer q, int cur_style)
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)) {
- /* we never added italic correction */
+ /*tex we never added italic correction */
} else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
- /* remove italic correction */
+ /*tex remove italic correction */
width(x) -= delta;
}
}
@@ -2685,82 +3070,91 @@ static scaled make_op(pointer q, int cur_style)
x = clean_box(nucleus(q), cur_style, cur_style);
if (delta != 0) {
if (do_new_math(cur_f)) {
- /* we never added italic correction */
+ /*tex we never added italic correction */
} else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
- /* remove italic correction */
+ /*tex remove italic correction */
width(x) -= delta;
}
}
axis_shift = true;
}
} else {
- /* normal size */
+ /*tex normal size */
delta = char_italic(cur_f, cur_c);
x = clean_box(nucleus(q), cur_style, cur_style);
if (delta != 0) {
if (do_new_math(cur_f)) {
- /* we never added italic correction */
+ /*tex we never added italic correction */
} else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) {
- /* remove italic correction */
+ /*tex remove italic correction */
width(x) -= delta;
}
}
axis_shift = true;
}
if (axis_shift) {
- /* center vertically */
+ /*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;
}
-
- /* we now handle op_nod_type_no_limits here too */
-
+ /*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 */
- /*
- for easy configuration ... fonts are somewhat inconsistent and the
- values for italic correction run from 30 to 60% of the width
+ /*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 :
- /* full bottom correction */
+ /*tex full bottom correction */
make_scripts(q, p, 0, cur_style, 0, -delta);
break;
case 1 :
- /* MathConstants driven */
+ /*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));
case 2 :
- /* no correction */
+ /*tex no correction */
make_scripts(q, p, 0, cur_style, 0, 0);
break ;
case 3 :
- /* half bottom correction */
+ /*tex half bottom correction */
make_scripts(q, p, 0, cur_style, 0, -half(delta));
break;
case 4 :
- /* half bottom and top correction */
+ /*tex half bottom and top correction */
make_scripts(q, p, 0, cur_style, half(delta), -half(delta));
break;
default :
if (mode > 15) {
- /* for quickly testing values */
+ /*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);
@@ -2770,7 +3164,7 @@ static scaled make_op(pointer q, int cur_style)
}
delta = 0;
} else {
- /* similar code then the caller (before CHECK_DIMENSIONS) */
+ /*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);
@@ -2779,8 +3173,13 @@ static scaled make_op(pointer q, int cur_style)
}
}
} else if (subtype(q) == op_noad_type_limits) {
- /* 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. */
+ /*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);
y = clean_box(nucleus(q), cur_style, cur_style);
z = clean_box(subscr(q), sub_style(cur_style), cur_style);
@@ -2788,38 +3187,36 @@ static scaled make_op(pointer q, int cur_style)
reset_attributes(v, node_attr(q));
type(v) = vlist_node;
if (do_new_math(cur_f)) {
- n = null;
- if (! math_no_italic_compensation_par) {
- 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) {
- n = list_ptr(n); /* just a not scaled char */
- while (n != null) {
- if (type(n) == glyph_node) {
- delta = char_italic(font(n),character(n));
- }
- n = vlink(n);
+ 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));
}
- } else {
- while (n != null) {
- if (type(n) == fence_noad) {
- if (delimiteritalic(n) > delta) {
- /* we can have dummies, the period ones */
- delta = delimiteritalic(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);
}
+ 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));
- }
+ }
+ } else {
+ n = nucleus(q);
+ if (type(n) == math_char_node) {
+ delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n));
}
}
}
@@ -2834,23 +3231,23 @@ static scaled make_op(pointer q, int cur_style)
z = rebox(z, width(v));
shift_amount(x) = half(delta);
shift_amount(z) = -shift_amount(x);
- /* v is the still empty target */
+ /*tex v is the still empty target */
height(v) = height(y);
depth(v) = depth(y);
- /*
- attach the limits to |y| and adjust |height(v)|, |depth(v)| to
- account for their presence
+ /*tex
- 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|
+ Attach the limits to |y| and adjust |height(v)|, |depth(v)| to
+ account for their presence.
- 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
+ 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|.
- b: baseline v: minumum gap
- */
+ 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);
@@ -2905,17 +3302,20 @@ static scaled make_op(pointer q, int cur_style)
return delta;
}
-@ 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|.
+/*tex
-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|).
+ 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|.
-No boundary characters enter into these ligatures.
+ 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.
+
+*/
-@c
#define simple_char_noad(p) (\
(p != null) && \
(type(p) == simple_noad) && \
@@ -2928,10 +3328,14 @@ No boundary characters enter into these ligatures.
static void make_ord(pointer q)
{
- int a; /* the left-side character for lig/kern testing */
- pointer p, r, s; /* temporary registers for list manipulation */
- scaled k; /* a kern */
- liginfo lig; /* a ligature */
+ /*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);
@@ -2939,7 +3343,7 @@ static void make_ord(pointer q)
type(nucleus(q)) = math_text_char_node;
fetch(nucleus(q));
a = cur_c;
- /* add italic correction */
+ /*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;
@@ -2948,45 +3352,49 @@ static void make_ord(pointer q)
couple_nodes(q,p);
return;
}
- /* construct ligatures, quite unlikely in new math fonts */
+ /*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));
- /*
- 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|
+ /*tex
- 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
+ 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|.
- 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
- */
+ 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)) {
- check_interrupt(); /* allow a way out of infinite ligature loop */
+ /*tex allow a way out of infinite ligature loop */
+ check_interrupt();
switch (lig_type(lig)) {
case 1:
- /* \.{=:\char`\|} */
+ /*tex \.{=:\char`\|} */
case 5:
- /* \.{=:\char`\|>} */
+ /*tex \.{=:\char`\|>} */
math_character(nucleus(q)) = lig_replacement(lig);
break;
case 2:
- /* \.{\char`\|=:} */
+ /*tex \.{\char`\|=:} */
case 6:
- /* \.{\char`\|=:>} */
+ /*tex \.{\char`\|=:>} */
math_character(nucleus(p)) = lig_replacement(lig);
break;
case 3:
- /* \.{\char`\|=:\char`\|} */
+ /*tex \.{\char`\|=:\char`\|} */
case 7:
- /* \.{\char`\|=:\char`\|>} */
+ /*tex \.{\char`\|=:\char`\|>} */
case 11:
- /* \.{\char`\|=:\char`\|>>} */
+ /*tex \.{\char`\|=:\char`\|>>} */
r = new_noad();
reset_attributes(r, node_attr(q));
s = new_node(math_char_node, 0);
@@ -2996,21 +3404,20 @@ static void make_ord(pointer q)
math_fam(nucleus(r)) = math_fam(nucleus(q));
couple_nodes(q,r);
couple_nodes(r,p);
- if (lig_type(lig) < 11)
+ if (lig_type(lig) < 11) {
type(nucleus(r)) = math_char_node;
- else
- /* prevent combination */
+ } 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); /* \.{=:} */
- s = math_clone(subscr(p));
- subscr(q) = s;
- s = math_clone(supscr(p));
- supscr(q) = s;
- math_reset(subscr(p)); /* just in case */
- math_reset(supscr(p));
+ subscr(q) = subscr(p);
+ supscr(q) = supscr(p);
+ subscr(p) = null ;
+ supscr(p) = null ;
flush_node(p);
break;
}
@@ -3021,7 +3428,7 @@ static void make_ord(pointer q)
}
}
if (disable_kern_par == 0 && has_kern(cur_f, a)) {
- /* todo: should this use mathkerns? */
+ /*tex todo: should this use mathkerns? */
k = get_kern(cur_f, a, cur_c);
if (k != 0) {
p = new_kern(k);
@@ -3036,32 +3443,35 @@ static void make_ord(pointer q)
}
}
-@ 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
+/*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
+
+*/
-@c
#define MATH_KERN_NOT_FOUND 0x7FFFFFFF
-@ 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.
+/*tex
-The microsoft spec says that there are four quadrants, but the
-actual images say
+ 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.
+
+*/
-@c
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;
- charinfo *co = char_info(f, c); /* known to exist */
+ /*tex Known to exist: */
+ charinfo *co = char_info(f, c);
numkerns = get_charinfo_math_kerns(co, side);
-#ifdef DEBUG
- fprintf(stderr, " entries = %d, height = %d\n", numkerns, v);
-#endif
if (numkerns == 0)
return kern;
if (side == top_left_kern) {
@@ -3073,21 +3483,15 @@ static scaled math_kern_at(internal_font_number f, int c, int side, int v)
} 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; /* not reached */
+ kerns_heights = NULL;
}
-#ifdef DEBUG
- fprintf(stderr, " entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]);
-#endif
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];
-#ifdef DEBUG
- if (k > 0)
- fprintf(stderr, " entry %d: %d,%d\n", k, h, kern);
-#endif
if (h > v) {
return kern;
}
@@ -3095,68 +3499,55 @@ static scaled math_kern_at(internal_font_number f, int c, int side, int v)
return kern;
}
-@ @c
-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)
+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)))
- if ((!(do_new_math(l_f) || do_new_math(r_f))) || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c)))
+ 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);
- corr_height_bot = -char_depth(r_f, r_c) + shift; /* bottom of superscript */
+ /*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);
-#ifdef DEBUG
- fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r, shift);
-#endif
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);
-#ifdef DEBUG
- fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r);
-#endif
if ((krn_l + krn_r) < krn)
krn = (krn_l + krn_r);
return (krn);
-
} else if (cmd == sub_mark_cmd) {
- corr_height_top = char_height(r_f, r_c) - shift; /* top of subscript */
+ /*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);
-#ifdef DEBUG
- fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r);
-#endif
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);
-#ifdef DEBUG
- fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r);
-#endif
if ((krn_l + krn_r) < krn)
krn = (krn_l + krn_r);
return (krn);
-
} else {
confusion("find_math_kern");
}
- return 0; /* not reached */
+ /*tex Not reached: */
+ return 0;
}
-@ just a small helper
-@c
-static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2)
+/*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) {
- /* this is somewhat weird */
+ /*tex this is somewhat weird */
new_hlist(q) = z;
} else {
y = new_hlist(q);
@@ -3167,67 +3558,36 @@ static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2)
return new_hlist(q);
}
-@
-@c
-#ifdef DEBUG
-void dump_simple_field(pointer q)
-{
- pointer p;
- printf(" [%d, type=%d, vlink=%d] ", q, type(q), vlink(q));
- switch (type(q)) {
- case math_char_node:
- printf("mathchar ");
- break;
- case math_text_char_node:
- printf("texchar ");
- break;
- case sub_box_node:
- printf("box ");
- break;
- case sub_mlist_node:
- printf("mlist ");
- p = math_list(q);
- while (p != null) {
- dump_simple_field(p);
- p = vlink(p);
- }
- break;
- }
-}
+/*tex
-void dump_simple_node(pointer q)
-{
- printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q));
- printf("nucleus: ");
- dump_simple_field(nucleus(q));
- printf("\n");
- printf("sub: ");
- dump_simple_field(subscr(q));
- printf("\n");
- printf("sup: ");
- dump_simple_field(supscr(q));
- printf("\n\n");
-}
-#endif
+ 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|.
-@ 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.
-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.
-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.
+*/
-@ @c
#define analyze_script(init,su_n,su_f,su_c) do { \
su_n = init; \
if (su_n != null) { \
- if (math_script_box_mode_par > 0 && type(su_n) == sub_mlist_node) { \
+ 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)) { \
@@ -3312,10 +3672,10 @@ the new fonts so eventualy there will be an option to ignore such corrections.
static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift)
{
- pointer x, y, z; /* temporary registers for box construction */
- scaled shift_up, shift_down, clr; /* dimensions in the calculation */
+ pointer x, y, z;
+ scaled shift_up, shift_down, clr;
scaled delta1, delta2;
- halfword sub_n, sup_n;
+ halfword sub_n, sup_n, subtyp;
internal_font_number sub_f, sup_f;
int sub_c, sup_c;
sub_n = null;
@@ -3326,18 +3686,13 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled
sup_c = 0;
delta1 = it;
delta2 = 0;
-
-#ifdef DEBUG
- printf("it: %d\n", it);
- dump_simple_node(q);
- printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p));
-#endif
+ subtyp = 0;
switch (type(nucleus(q))) {
case math_char_node:
case math_text_char_node:
if ((subscr(q) == null) && (delta1 != 0)) {
- /* todo: selective */
- x = new_kern(delta1); /* italic correction */
+ /*tex todo: selective italic correction */
+ x = new_kern(delta1);
subtype(x) = italic_kern;
reset_attributes(x, node_attr(nucleus(q)));
couple_nodes(p,x);
@@ -3355,22 +3710,20 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled
list_ptr(z) = null;
flush_node(z);
}
-
if (is_char_node(p)) {
- /* we look at the subscript character (_i) or first character in a list (_{ij}) */
+ /*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);
- /* we look at the superscript character (^i) or first character in a list (^{ij}) */
+ /*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) {
- /*
- construct a subscript box |x| when there is no superscript
+ /*tex
- when there is a subscript without a superscript, the top of the subscript
+ 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), sub_style(cur_style), cur_style); */
x = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style);
width(x) = width(x) + space_after_script(cur_style);
switch (math_scripts_mode_par) {
@@ -3398,30 +3751,29 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled
break;
}
shift_amount(x) = shift_down;
-
- /* now find and correct for horizontal shift */
+ /*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);
+ p = attach_hkern_to_new_hlist(q, delta2, subtyp);
}
-
} else {
- /*
- construct a superscript box |x|
+ /*tex
+
+ Construct a superscript box |x|. The bottom of a superscript should
+ never descend below the baseline plus one-fourth of the x-height.
- the bottom of a superscript should never descend below the baseline plus
- one-fourth of the x-height.
*/
- /* x = clean_box(supscr(q), sup_style(cur_style), cur_style); */
x = clean_box(supscr(q), (noadoptionnosupscript(q) ? cur_style : sup_style(cur_style)), cur_style);
width(x) = width(x) + space_after_script(cur_style);
switch (math_scripts_mode_par) {
@@ -3451,33 +3803,35 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled
}
if (subscr(q) == null) {
shift_amount(x) = -shift_up;
- /* now find and correct for horizontal shift */
+ /*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);
+ p = attach_hkern_to_new_hlist(q, clr, subtyp);
}
} else {
- /*
- construct a sub/superscript combination box |x|, with the superscript offset
- by |delta|
+ /*tex
- when both subscript and superscript are present, the subscript must be
- separated from the superscript by at least four times |default_rule_thickness|
+ 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.
- 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) sub_style(cur_style), cur_style); */
y = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style);
width(y) = width(y) + space_after_script(cur_style);
switch (math_scripts_mode_par) {
@@ -3510,30 +3864,37 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled
}
break;
}
- /* now find and correct for horizontal shift */
+ /*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);
+ p = attach_hkern_to_new_hlist(q, delta2, subtyp);
}
- /*
- now the horizontal shift for the superscript; the superscript is also to be shifted
- by |delta1| (the italic correction)
+ /*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
- /* delta can already have been applied and now be 0 */
+ The delta can already have been applied and now be 0.
+
+ */
if (delta2 == MATH_KERN_NOT_FOUND)
delta2 = - supshift ;
else
@@ -3543,18 +3904,17 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled
} else {
shift_amount(x) = delta1 - delta2;
}
- /* todo: only if kern != 0 */
+ /*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);
- /* we end up with funny dimensions */
+ /*tex We end up with funny dimensions. */
x = vpackage(x, 0, additional, max_dimen, math_direction_par);
reset_attributes(x, node_attr(q));
shift_amount(x) = shift_down;
}
}
-
if (new_hlist(q) == null) {
new_hlist(q) = x;
} else {
@@ -3575,12 +3935,15 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled
}
}
-@ 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.
+/*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.
+
+*/
-@c
static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h)
{
scaled delta;
@@ -3589,21 +3952,21 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m
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) */
+ 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);
@@ -3629,7 +3992,7 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m
}
delimiter(q) = null;
assign_new_hlist(q, tmp);
- delimitersamesize(q) = same; /* new */
+ delimitersamesize(q) = same;
if (delimiterclass(q) >= ord_noad_type) {
if (delimiterclass(q) <= inner_noad_type) {
return delimiterclass(q);
@@ -3643,35 +4006,32 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m
}
}
-@ @c
-#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); \
+#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); \
+#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)); \
+#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)); \
+#define SPLIT_STYLES(A,B,C) do { \
+ TEXT_STYLES(A,(B)); \
+ SCRIPT_STYLES(A,(C)); \
} while (0)
-
void initialize_math_spacing(void)
{
- /* *INDENT-OFF* */
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);
@@ -3743,10 +4103,8 @@ void initialize_math_spacing(void)
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);
- /* *INDENT-ON* */
}
-@ @c
#define both_types(A,B) ((A)*16+(B))
static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
@@ -3758,102 +4116,99 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu)
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)) {
- /* *INDENT-OFF* */
- 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;
-#if 0
- case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;
-#endif
- 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;
-#if 0
- 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;
-#endif
- case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break;
-#if 0
- 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;
-#endif
- 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;
-#if 0
- case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;
-#endif
- 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;
-#if 0
- case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;
-#endif
- 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;
-#if 0
- case both_types(punct_noad_type, bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;
-#endif
- 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;
- /* *INDENT-ON* */
+ 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) {
- /* trap thin/med/thick settings cf. old TeX */
- z = math_glue(glue_par(x), mmu); /* allocates a glue */
- /* store a symbolic subtype */
+ /*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); /* allocates a glue */
+ z = math_glue(x, mmu);
}
}
return z;
}
-@ @c
static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same)
{
pointer p = null;
@@ -3866,37 +4221,36 @@ static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_styl
case math_text_char_node:
fetch(nucleus(q));
if (char_exists(cur_f, cur_c)) {
- /* we could look at neighbours */
+ /*tex we could look at neighbours */
if (do_new_math(cur_f)) {
- *delta = 0 ; /* cf spec only the last one */
+ /*tex cf spec only the last one */
+ *delta = 0 ;
} else {
*delta = char_italic(cur_f, cur_c);
}
p = new_glyph(cur_f, cur_c);
reset_attributes(p, node_attr(nucleus(q)));
if (do_new_math(cur_f)) {
- if (! math_no_char_italic_par) {
- /* keep italic, but bad with two successive letters */
- } else if (get_char_cat_code(cur_c) == 11) {
- /* no italic correction in mid-word of text font */
+ if (get_char_cat_code(cur_c) == 11) {
+ /*tex no italic correction in mid-word of text font */
*delta = 0;
}
} else {
- /* no italic correction in mid-word of text font */
+ /*tex no italic correction in mid-word of text font */
if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) {
*delta = 0;
}
}
- /* so we only add italic correction when we have no scripts */
+ /*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 /* needs checking but looks ok */
- if (do_new_math(cur_f)) {
- *delta = char_italic(cur_f, cur_c); /* must be more selective */
+ } 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;
@@ -3905,60 +4259,80 @@ static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_styl
break;
case sub_mlist_node:
t = math_list(nucleus(q));
- mlist_to_hlist(t, false, cur_style); /* recursive call */
-if (same != NULL && type(t) == fence_noad && delimitersamesize(t)) {
- *same = delimitersamesize(t) ;
-}
+ /*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"); /* this can't happen mlist2 */
+ confusion("mlist2");
}
return p;
}
-@ Here is the overall plan of |mlist_to_hlist|, and the list of its
- local variables.
+/*tex
+
+ Here is the overall plan of |mlist_to_hlist|, and the list of its local
+ variables.
+
+*/
-@c
void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
{
- pointer q = mlist; /* runs through the mlist */
- pointer r = null; /* the most recent noad preceding |q| */
- int style = cur_style; /* tuck global parameter away as local variable */
- int r_type = simple_noad; /* the |type| of noad |r|, or |op_noad| if |r=null| */
- int r_subtype = op_noad_type_normal; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */
- int t; /* the effective |type| of noad |q| during the second pass */
- int t_subtype; /* the effective |subtype| of noad |q| during the second pass */
+ /*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;
- int pen; /* a penalty to be inserted */
- int prepen; /* a penalty to be inserted */
- scaled max_hl = 0; /* maximum height of the list translated so far */
- scaled max_d = 0; /* maximum depth of the list translated so far */
- scaled delta; /* italic correction offset for subscript and superscript */
- scaled cur_mu; /* the math unit width corresponding to |cur_size| */
+ /*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) {
- /* we could do this via the callback but it's nice to have it as primitive too */
+ /*tex
+ We could do this via the callback but it's nice to have it as
+ primitive too.
+ */
penalties = 1;
}
while (q != null) {
- /*
- we use the fact that no character nodes appear in an mlist, hence
- the field |type(q)| is always present.
+ /*tex
- 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
+ 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.
- the values of |r| and |r_type| make this fairly easy
*/
RESWITCH:
delta = 0;
@@ -3966,66 +4340,69 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
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;
+ 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 fence_noad:
- if (r_subtype == left_noad_side) {
- subtype(q) = ord_noad_type; /* so these can best be the same size */
- goto RESWITCH;
+ 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;
- }
- 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) {
- type(r) = simple_noad; /* assumes the same size .. can't this go */
- 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;
+ 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) {
- type(r) = simple_noad; /* assumes the same size */
+ /*tex Assumes the same size. */
+ type(r) = simple_noad;
subtype(r) = ord_noad_type;
}
}
@@ -4060,16 +4437,20 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
break;
case choice_node:
switch (cur_style / 2) {
- case 0: /* |display_style=0| */
+ case 0:
+ /*tex |display_style=0| */
choose_mlist(display_mlist);
break;
- case 1: /* |text_style=2| */
+ case 1:
+ /*tex |text_style=2| */
choose_mlist(text_mlist);
break;
- case 2: /* |script_style=4| */
+ case 2:
+ /*tex |script_style=4| */
choose_mlist(script_mlist);
break;
- case 3: /* |script_script_style=6| */
+ case 3:
+ /*tex |script_script_style=6| */
choose_mlist(script_script_mlist);
break;
}
@@ -4105,14 +4486,16 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
goto DONE_WITH_NODE;
break;
case glue_node:
- /*
- conditional math glue (`\.{\\nonscript}') results in a |glue_node|
- pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case
- the node following will be eliminated if it is a glue or kern node and if the
- current size is different from |text_size|
+ /*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|
+ Unconditional math glue (`\.{\\muskip}') is converted to
+ normal glue by multiplying the dimensions by |cur_mu|.
*/
if (subtype(q) == mu_glue) {
@@ -4139,29 +4522,31 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style)
default:
confusion("mlist1");
}
- /*
- 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.
+ /*tex
- 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.
+ 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 ;
+ same = 0 ;
p = check_nucleus_complexity(q, &delta, cur_style, &same);
-if (same) {
- noadextra4(q) = 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.
+ 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) {
@@ -4199,7 +4584,7 @@ if (same) {
}
assign_new_hlist(q, p);
} else {
- /* top, bottom */
+ /*tex top, bottom */
make_scripts(q, p, delta, cur_style, 0, 0);
}
CHECK_DIMENSIONS:
@@ -4209,7 +4594,7 @@ if (same) {
if (depth(z) > max_d)
max_d = depth(z);
list_ptr(z) = null;
- /* only drop the \.{\\hbox} */
+ /*tex only drop the \.{\\hbox} */
flush_node(z);
DONE_WITH_NOAD:
r = q;
@@ -4219,7 +4604,8 @@ if (same) {
r_subtype = left_noad_side;
cur_style = style;
setup_cur_size(cur_style);
- cur_mu = x_over_n(get_math_quad_size(cur_size), 18); /* style */
+ /*tex style */
+ cur_mu = x_over_n(get_math_quad_size(cur_size), 18);
}
DONE_WITH_NODE:
q = vlink(q);
@@ -4228,15 +4614,18 @@ if (same) {
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.
+ 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;
@@ -4248,15 +4637,18 @@ if (same) {
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
+ 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.
- 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;
@@ -4292,7 +4684,7 @@ if (same) {
t_subtype = make_left_right(q, style, max_d, max_hl);
break;
case style_node:
- /* Change the current style and |goto delete_q| */
+ /*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);
@@ -4316,34 +4708,36 @@ if (same) {
default:
confusion("mlist3");
}
- /* Append inter-element spacing based on |r_type| and |t| */
+ /*tex Append inter-element spacing based on |r_type| and |t| */
if (r_type > 0) {
- /* not the first noad */
+ /*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 (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) {
- /* no checking of prev node type */
+ /*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;
}
}
- /*
- Append any |new_hlist| entries for |q|, and any appropriate penalties
+ /*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.
- 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));
@@ -4370,12 +4764,13 @@ if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1)
DELETE_Q:
r = q;
q = vlink(q);
- /*
- 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).
+ /*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.
- 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));
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..0e50c1031dc
--- /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);
+ }
+ flush_node(cur_list.tail_field);
+ cur_list.tail_field = n;
+ 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.w b/Build/source/texk/web2c/luatexdir/tex/nesting.w
deleted file mode 100644
index d9e7cf744ee..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/nesting.w
+++ /dev/null
@@ -1,436 +0,0 @@
-% 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/>.
-
-@ @c
-
-#include "ptexlib.h"
-
-@ these are for |show_activities|
-@c
-#define page_goal page_so_far[0]
-
-@ \TeX\ is typically in the midst of building many lists at once. For example,
-when a math formula is being processed, \TeX\ is in math mode and
-working on an mlist; this formula has temporarily interrupted \TeX\ from
-being in horizontal mode and building the hlist of a paragraph; and this
-paragraph has temporarily interrupted \TeX\ from being in vertical mode
-and building the vlist for the next page of a document. Similarly, when a
-\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily
-interrupted from working in restricted horizontal mode, and it enters
-internal vertical mode. The ``semantic nest'' is a stack that
-keeps track of what lists and modes are currently suspended.
-
-At each level of processing we are in one of six modes:
-
-\yskip\hang|vmode| stands for vertical mode (the page builder);
-
-\hang|hmode| stands for horizontal mode (the paragraph builder);
-
-\hang|mmode| stands for displayed formula mode;
-
-\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox});
-
-\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox});
-
-\hang|-mmode| stands for math formula mode (not displayed).
-
-\yskip\noindent The mode is temporarily set to zero while processing \.{\\write}
-texts in the |ship_out| routine.
-
-Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that
-\TeX's ``big semantic switch'' can select the appropriate thing to
-do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current
-mode and |cur_cmd| is the current command code.
-
-@c
-static const char *string_mode(int m)
-{ /* prints the mode represented by |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";
-}
-
-@ @c
-void print_mode(int m)
-{ /* prints the mode represented by |m| */
- tprint(string_mode(m));
-}
-
-@ @c
-void print_in_mode(int m)
-{ /* prints the mode represented by |m| */
- tprint("' in ");
- tprint(string_mode(m));
-}
-
-@ @c
-int get_mode_id(void)
-{ /* returns the mode represented by |m| */
- 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;
- }
- }
-}
-
-
-@ The state of affairs at any semantic level can be represented by
-five values:
-
-\yskip\hang|mode| is the number representing the semantic mode, as
-just explained.
-
-\yskip\hang|head| is a |pointer| to a list head for the list being built;
-|link(head)| therefore points to the first element of the list, or
-to |null| if the list is empty.
-
-\yskip\hang|tail| is a |pointer| to the final node of the list being
-built; thus, |tail=head| if and only if the list is empty.
-
-\yskip\hang|prev_graf| is the number of lines of the current paragraph that
-have already been put into the present vertical list.
-
-\yskip\hang|aux| is an auxiliary |memory_word| that gives further information
-that is needed to characterize the situation.
-
-\yskip\noindent
-In vertical mode, |aux| is also known as |prev_depth|; it is the scaled
-value representing the depth of the previous box, for use in baseline
-calculations, or it is |<=-1000|pt if the next box on the vertical list is to
-be exempt from baseline calculations. In horizontal mode, |aux| is also
-known as |space_factor|; 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@>
-
-\yskip\hang 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.
-
-\yskip\hang 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.
-
-@c
-list_state_record *nest;
-int nest_ptr; /* first unused location of |nest| */
-int max_nest_stack; /* maximum of |nest_ptr| when pushing */
-int shown_mode; /* most recent mode shown by \.{\\tracingcommands} */
-halfword save_tail; /* save |tail| so we can examine whether we have an auto
- kern before a glue */
-
-@ 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.
-
-@c
-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();
-}
-
-@ Here is a common way to make the current list grow:
-
-@c
-void tail_append(halfword p)
-{
- couple_nodes(cur_list.tail_field, p);
- cur_list.tail_field = vlink(cur_list.tail_field);
-}
-
-@ @c
-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);
- }
- flush_node(cur_list.tail_field);
- cur_list.tail_field = n;
- vlink(n) = null;
- return r;
- } else {
- return null;
- }
-}
-
-@ 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|.
-
-@c
-void push_nest(void)
-{ /* enter a new semantic level, save the old */
- if (nest_ptr > max_nest_stack) {
- max_nest_stack = nest_ptr;
- if (nest_ptr == nest_size)
- overflow("semantic nest size", (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();
-}
-
-@ 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.
-
-@c
-void pop_nest(void)
-{ /* leave a semantic level, re-enter the old */
- flush_node(cur_list.head_field);
- decr(nest_ptr);
-}
-
-@ Here is a procedure that displays what \TeX\ is working on, at all levels.
-
-@c
-void show_activities(void)
-{
- int p; /* index into |nest| */
- int m; /* mode */
- halfword q, r; /* for showing the current page */
- int t; /* ditto */
- 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));
- /* we dont do this any more */
-#if 0
-
- if (m == hmode)
- if (nest[p].pg_field != 040600000) {
- tprint(" (language");
- print_int(nest[p].pg_field % 0200000);
- tprint(":hyphenmin");
- print_int(nest[p].pg_field / 020000000);
- print_char(',');
- print_int((nest[p].pg_field / 0200000) % 0100);
- print_char(')');
- }
-#endif
- if (nest[p].ml_field < 0)
- tprint(" (\\output routine)");
- if (p == 0) {
- /* 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));
- /* 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);
- /* we dont do this any more, this was aux.rh originally */
-#if 0
- if (m > 0) {
- if (nest[p].current_language_field > 0) {
- tprint(", current language ");
- print_int(nest[p].current_language_field);
- }
- }
-#endif
- break;
- case 2:
- if (nest[p].incompleat_noad_field != null) {
- tprint("this will be denominator of:");
- show_box(nest[p].incompleat_noad_field);
- }
- } /* there are no other cases */
-
- }
-}
diff --git a/Build/source/texk/web2c/luatexdir/tex/packaging.w b/Build/source/texk/web2c/luatexdir/tex/packaging.c
index f094a0d9665..e2b007f11ef 100644
--- a/Build/source/texk/web2c/luatexdir/tex/packaging.w
+++ b/Build/source/texk/web2c/luatexdir/tex/packaging.c
@@ -1,84 +1,68 @@
-% packaging.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/>.
-
-@ @c
+/*
+
+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"
-@ 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}}$$
-
-@c
-/*
- void scan_spec(group_code c)
- {
- int spec_code;
- 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);
- scan_left_brace();
- }
+/*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)
-{ /* scans a box specification and left brace */
+{
int spec_code;
boolean done = false ;
do {
@@ -89,7 +73,7 @@ void scan_spec(group_code c)
cur_val = 0;
done = true;
} else {
- /* todo: attr */
+ /*tex todo: attr */
back_input();
if (scan_keyword("to")) {
spec_code = exactly;
@@ -110,84 +94,21 @@ void scan_spec(group_code c)
}
}
-@ 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.
+/*tex
-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}).
+ 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}).
-@c
-/*
- void scan_full_spec(group_code c, int spec_direction)
- {
- int s;
- int i;
- int v;
- int spec_code;
- halfword attr_list;
- if (attr_list_cache == cache_disabled)
- update_attribute_cache();
- attr_list = attr_list_cache;
- s = saved_value(0);
- CONTINUE:
- while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) {
- get_x_token();
- if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd)
- back_input();
- }
- if (scan_keyword("attr")) {
- scan_register_num();
- i = cur_val;
- scan_optional_equals();
- scan_int();
- v = cur_val;
- if ((attr_list != null) && (attr_list == attr_list_cache)) {
- attr_list = copy_attribute_list(attr_list_cache);
- add_node_attr_ref(attr_list);
- }
- attr_list = do_set_attribute(attr_list, i, v);
- goto CONTINUE;
- }
- if (scan_keyword("dir")) {
- scan_direction();
- spec_direction = cur_val;
- goto CONTINUE;
- }
- if (attr_list == attr_list_cache) {
- add_node_attr_ref(attr_list);
- }
- 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();
- FOUND:
- set_saved_record(0, saved_boxcontext, 0, s);
- set_saved_record(1, saved_boxspec, spec_code, cur_val);
- 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);
- save_ptr += 4;
- new_save_level(c);
- scan_left_brace();
- 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);
- }
*/
-/* scans a box specification and left brace */
+/*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)
{
@@ -195,10 +116,12 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack)
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;
- s = saved_value(0); /* the box context */
+ /*tex The box context: */
+ s = saved_value(0);
do {
get_x_token();
} while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
@@ -219,6 +142,7 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack)
}
}
KEYWORDS:
+ /*tex Multiple |attr| keys possible (before or after dir). */
if (scan_keyword("attr")) {
scan_register_num();
i = cur_val;
@@ -232,11 +156,23 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack)
attr_list = do_set_attribute(attr_list, i, v);
goto CONTINUE;
}
- if (scan_keyword("dir")) {
- scan_direction();
- spec_direction = cur_val;
- 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")) {
@@ -256,7 +192,7 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack)
add_node_attr_ref(attr_list);
set_saved_record(0, saved_boxcontext, 0, s);
set_saved_record(1, saved_boxspec, spec_code, cur_val);
- /* DIR: Adjust |text_dir_ptr| for |scan_spec| */
+ /*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);
@@ -270,73 +206,91 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack)
if (! done) {
scan_left_brace();
}
- /* no gain: if (body_direction_par != spec_direction) etc */
+ /*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.
+ 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.)
+ 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}.
+ 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|. */
-@c
scaled total_stretch[5];
-scaled total_shrink[5]; /* glue found by |hpack| or |vpack| */
-int last_badness; /* badness of the most recently packaged box */
+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|.
+ 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|.
-@c
-halfword adjust_tail; /* tail of adjustment list */
+*/
+
+/*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|.
+ Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to
+ |pre_adjust_tail| instead of |adjust_tail|.
+
+*/
-@c
halfword pre_adjust_tail;
halfword last_leftmost_char;
halfword last_rightmost_char;
-halfword next_char_p; /* pointer to the next char of an implicit kern */
-halfword prev_char_p; /* pointer to the previous char of an implicit kern */
+/*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. */
-@ This procedure is called repeatedly from inside the line break algorithm.
-@c
void set_prev_char_p(halfword p)
{
prev_char_p = p;
}
-/*
- 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)
-*/
+/*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).
+*/
-@ @c
scaled char_stretch(halfword p)
{
internal_font_number f = font(p);
@@ -345,7 +299,7 @@ scaled char_stretch(halfword p)
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) - x_advance(p);
+ scaled dw = calc_char_width(f, c, m) - char_width(f, c);
if (dw > 0) {
return round_xn_over_d(dw, ef, 1000);
}
@@ -354,7 +308,6 @@ scaled char_stretch(halfword p)
return 0;
}
-@ @c
scaled char_shrink(halfword p)
{
internal_font_number f = font(p);
@@ -363,7 +316,7 @@ scaled char_shrink(halfword p)
int c = character(p);
int ef = get_ef_code(f, c);
if (ef > 0) {
- scaled dw = char_width(f, c) + x_advance(p) - calc_char_width(f, c, -m);
+ scaled dw = char_width(f, c) - calc_char_width(f, c, -m);
if (dw > 0) {
return round_xn_over_d(dw, ef, 1000);
}
@@ -372,33 +325,6 @@ scaled char_shrink(halfword p)
return 0;
}
-@ @c
-/*
-scaled kern_stretch(halfword p)
-{
- halfword l, r;
- scaled d;
- int m;
- if ((prev_char_p == null) || (vlink(prev_char_p) != p) || (vlink(p) == null))
- return 0;
- l = prev_char_p;
- // we need a left char
- if (!is_char_node(l))
- return 0;
- r = vlink(p);
- // and a right char
- if (!is_char_node(r))
- return 0;
- // and a reason to kern
- if ((font(l) != font(r)) || (font_max_stretch(font(l)) == 0))
- return 0;
- m = font_max_stretch(font(l));
- d = get_kern(font(l), character(l), character(r)); // real kern, so what is width(p) then; the messed up one
- d = round_xn_over_d(d, 1000 + m, 1000);
- return round_xn_over_d(d - width(p), get_ef_code(font(l), character(l)), 1000);
-}
-*/
-
scaled kern_stretch(halfword p)
{
int m;
@@ -407,69 +333,39 @@ scaled kern_stretch(halfword p)
halfword l;
halfword r;
if (w == 0) {
- /* why bother about zero kerns */
+ /*tex Why bother about zero kerns. */
return 0;
}
l = prev_char_p ;
if ((l == null) || (vlink(l) != p)) {
- /* we only care about kerns following a char*/
+ /*tex We only care about kerns following a char. */
return 0;
}
r = vlink(p);
if (r == null) {
- /* we only care about kerns between a char and something else */
+ /*tex We only care about kerns between a char and something else. */
}
if (!(is_char_node(l) && is_char_node(r))) {
- /* we want two chars (but but don't care about the fonts) */
+ /*tex We want two chars (but but don't care about the fonts). */
return 0;
}
- /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
+ /*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) {
- /* nothing to kern */
+ /*tex nothing to kern */
return 0;
}
d = round_xn_over_d(w, 1000 + m, 1000);
- /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
+ /*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);
}
- /*
- printf("STRETCH w=%i s=%i x=%i\n",w,e+m,x);
- */
return x;
}
-@ @c
-/*
-scaled kern_shrink(halfword p)
-{
- halfword l, r;
- scaled d;
- int m;
- if ((prev_char_p == null) || (vlink(prev_char_p) != p) || (vlink(p) == null))
- return 0;
- l = prev_char_p;
- // we need a left char
- if (!is_char_node(l))
- return 0;
- r = vlink(p);
- // and a right char
- if (!is_char_node(r))
- return 0;
- // and a reason to kern
- if ((font(l) != font(r)) || (font_max_shrink(font(l)) == 0))
- return 0;
- m = font_max_shrink(font(l));
- d = get_kern(font(l), character(l), character(r)); // real kern, so what is width(p) then; the messed up one
- d = round_xn_over_d(d, 1000 - m, 1000);
- return round_xn_over_d(width(p) - d, get_ef_code(font(l), character(l)), 1000);
-}
-*/
-
scaled kern_shrink(halfword p)
{
int m;
@@ -478,26 +374,26 @@ scaled kern_shrink(halfword p)
halfword l;
halfword r;
if (w == 0) {
- /* why bother about zero kerns */
+ /*tex Why bother about zero kerns. */
return 0;
}
l = prev_char_p ;
if ((l == null) || (vlink(l) != p)) {
- /* we only care about kerns following a char*/
+ /*tex We only care about kerns following a char. */
return 0;
}
r = vlink(p);
if (r == null) {
- /* we only care about kerns between a char and something else */
+ /*tex We only care about kerns between a char and something else. */
}
if (!(is_char_node(l) && is_char_node(r))) {
- /* we want two chars (but but don't care about the fonts) */
+ /*tex We want two chars (but but don't care about the fonts). */
return 0;
}
- /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */
+ /*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) {
- /* nothing to kern */
+ /*tex Nothing to kern. */
return 0;
}
d = round_xn_over_d(w, 1000 - m, 1000);
@@ -507,13 +403,9 @@ scaled kern_shrink(halfword p)
} else {
x = round_xn_over_d(w - d, e, 1000);
}
- /*
- printf("SHRINK w=%i s=%i x=%i\n",w,e+m,x);
- */
return x;
}
-@ @c
void do_subst_font(halfword p, int ex_ratio)
{
if (type(p) == disc_node) {
@@ -555,7 +447,6 @@ void do_subst_font(halfword p, int ex_ratio)
}
}
-@ @c
scaled char_pw(halfword p, int side)
{
internal_font_number f;
@@ -582,7 +473,6 @@ scaled char_pw(halfword p, int side)
return round_xn_over_d(w, c, 1000);
}
-@ @c
halfword new_margin_kern(scaled w, halfword p, int side)
{
halfword k, q;
@@ -595,11 +485,16 @@ halfword new_margin_kern(scaled w, halfword p, int side)
return k;
}
-@ Here is |hpack|, which is place where we do font substituting when font
-expansion is being used.
+/*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. */
-@c
-int font_expand_ratio = 0; /* current expansion ratio, needed for recursive call */
+int font_expand_ratio = 0;
int ignore_math_skip(halfword p)
{
@@ -632,47 +527,60 @@ int ignore_math_skip(halfword p)
halfword hpack(halfword p, scaled w, int m, int pack_direction)
{
- halfword r; /* the box node that will be returned */
- halfword q; /* trails behind |p| */
- scaled h = 0; /* height */
- scaled d = 0; /* depth */
- scaled x = 0; /* natural width */
+ /*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;
- scaled s; /* shift amount */
- int o; /* order of infinity */
- halfword dir_ptr1 = null; /* for managing the direction stack */
- int hpack_dir; /* the current direction */
+ /*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;
-
-/*
- int font_expand_ratio = 0;
-*/
last_badness = 0;
- r = new_node(hlist_node, min_quarterword); /* the box node that will be returned */
+ /*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;
- /*
- potential optimimization, save a little but neglectable in practice (not so
- many empty boxes are used)
+ /*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
+
*/
- push_dir(dir_ptr1,hpack_dir); /* push null */
- q = r + list_offset; /* hm, adding something to a node address? */
+ /*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) {
- prev_char_p = null; /* why not always */
+ /*tex Why not always: */
+ prev_char_p = null;
}
if (adjust_spacing > 2) {
adjust_spacing = 0;
@@ -687,7 +595,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
total_shrink[fill] = 0;
total_stretch[filll] = 0;
total_shrink[filll] = 0;
-
RESWITCH:
while ((p != null) || (disc_level > 0)) {
if (p == null) {
@@ -695,20 +602,24 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
p = pack_interrupt[disc_level];
goto RESWITCH;
}
- /*
- 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
+ /*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) {
@@ -730,13 +641,16 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
switch (type(p)) {
case hlist_node:
case vlist_node:
- /*
- Incorporate box dimensions into the dimensions of the hbox
- that will contain~it
+ /*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.
- 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);
@@ -746,20 +660,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
if (whd.dp + s > d)
d = whd.dp + s;
break;
- /*
- case rule_node:
- case unset_node:
- x += width(p);
- if (type(p) >= rule_node) // always
- s = 0;
- else
- s = shift_amount(p);
- if (height(p) - s > h)
- h = height(p) - s;
- if (depth(p) + s > d)
- d = depth(p) + s;
- break;
- */
case rule_node:
case unset_node:
x += width(p);
@@ -768,18 +668,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
if (depth(p) > d)
d = depth(p);
break;
- /* */
case math_node:
- /* begin mathskip code */
+ /*tex Begin mathskip code. */
if (glue_is_zero(p) || ignore_math_skip(p)) {
x += surround(p);
break;
} else {
- /* fall through */
+ /*tex Fall through. */
}
- /* end mathskip code */
+ /*tex End mathskip code. */
case glue_node:
- /* Incorporate glue into the horizontal totals */
+ /*tex Incorporate glue into the horizontal totals. */
x += width(p);
o = stretch_order(p);
total_stretch[o] = total_stretch[o] + stretch(p);
@@ -796,12 +695,12 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
case kern_node:
x += width(p);
if (subtype(p) == font_kern && adjust_spacing) {
- /* so only when 1 or 2 */
+ /*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) {
- /* this is the finalizer */
+ /*tex This is the finalizer. */
int k = 0;
if (font_expand_ratio > 0) {
k = kern_stretch(p);
@@ -810,9 +709,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
}
ex_kern(p) = k;
x += k;
- /*
- if (x!=0) printf("SET %i %i %i\n",font_expand_ratio,k,x);
- */
}
}
break;
@@ -826,8 +722,8 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
}
break;
case dir_node:
- /* Adjust the dir stack for the |hpack| routine */
- if (dir_dir(p) >= 0) {
+ /*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 {
@@ -856,15 +752,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
case ins_node:
case mark_node:
case adjust_node:
- /*
- 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.
- */
+ /*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);
@@ -885,13 +783,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
p = q;
}
break;
- /* */
default:
break;
}
p = vlink(p);
}
-
}
if (adjust_tail != null)
@@ -900,31 +796,35 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
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;
- /* now |x| is the excess to be made up */
+ /*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) {
- /*
- Determine horizontal glue stretch setting, then |return|
- or \hbox{|goto common_ending|}.
+ /*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;
@@ -936,7 +836,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
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;
@@ -946,15 +845,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
if (total_stretch[o] != 0) {
glue_set(r) = unfloat((double) x / total_stretch[o]);
} else {
- /* there's nothing to stretch */
+ /*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) {
- /*
- Report an underfull hbox and |goto common_ending|, if this box
- is sufficiently bad.
+ /*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) {
@@ -987,9 +888,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
}
goto EXIT;
} else {
- /*
- Determine horizontal glue shrink setting, then |return|
- or \hbox{|goto common_ending|},
+ /*tex
+
+ Determine horizontal glue shrink setting, then |return| or |goto
+ common_ending|,
+
*/
if (total_shrink[filll] != 0)
o = filll;
@@ -1001,7 +904,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
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;
@@ -1011,18 +913,20 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
if (total_shrink[o] != 0) {
glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]);
} else {
- /* there's nothing to shrink */
+ /*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;
- /* use the maximum shrinkage */
+ /*tex Use the maximum shrinkage */
set_glue_ratio_one(glue_set(r));
- /*
- Report an overfull hbox and |goto common_ending|, if this box
- is sufficiently bad.
+ /*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);
@@ -1050,9 +954,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
}
} 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) {
@@ -1079,10 +985,7 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
}
COMMON_ENDING:
- /*
- Finish issuing a diagnostic message for an overfull or underfull
- hbox.
- */
+ /*tex Finish issuing a diagnostic message for an overfull or underfull hbox. */
if (output_active) {
tprint(") has occurred while \\output is active");
} else {
@@ -1113,17 +1016,16 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction)
q = list_ptr(r);
list_ptr(r) = null;
flush_node(r);
- /* this nested call uses the more or less global font_expand_ratio */
+ /*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);
- /* here we reset the font_expan_ratio */
+ /*tex Here we reset the |font_expand_ratio|. */
font_expand_ratio = 0;
return r;
}
-@ @c
halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int pac, int just_pack, halfword attr)
{
halfword q;
@@ -1131,32 +1033,33 @@ halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int p
q = vlink(p);
} else if (type(p) == temp_node && vlink(p) == null) {
q = vlink(p);
- /*
- q = new_node(hlist_node, min_quarterword);
- box_dir(q) = (pac == -1) ? text_direction_par : pac;
- width(q) = w;
- return q;
- */
} else {
new_hyphenation(p, qt);
- (void) new_ligkern(p, qt); /* we don't care about the tail in this case */
+ /*tex We don't care about the tail in this case. */
+ (void) new_ligkern(p, qt);
q = vlink(p);
- /* maybe here: alink(p) = null */
- q = lua_hpack_filter(q, w, m, grp, pac, attr); /* ignores empty anyway */ /* maybe also pass tail */
+ /*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);
}
-@ here is a function to calculate the natural whd of a (horizontal) node list
+/*tex
-@c
-scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult,
- int g_sign, int g_order, int pack_direction)
+ 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)
{
- scaled s; /* shift amount */
- halfword g; /* points to a glue specification */
+ /*tex shift amount */
+ scaled s;
+ /*tex points to a glue specification */
+ halfword g;
int hpack_dir;
- scaled_whd xx; /* for recursion */
+ /*tex For recursion */
+ scaled_whd xx;
scaled_whd whd, siz = { 0, 0, 0 };
scaled gp = 0;
scaled gm = 0;
@@ -1187,20 +1090,6 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult,
if (whd.dp + s > siz.dp)
siz.dp = whd.dp + s;
break;
- /*
- case rule_node:
- case unset_node:
- siz.wd += width(p);
- if (type(p) >= rule_node) // always true
- s = 0;
- else
- s = shift_amount(p);
- if (height(p) - s > siz.ht)
- siz.ht = height(p) - s;
- if (depth(p) + s > siz.dp)
- siz.dp = depth(p) + s;
- break;
- */
case rule_node:
case unset_node:
siz.wd += width(p);
@@ -1209,29 +1098,28 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult,
if (depth(p) > siz.dp)
siz.dp = depth(p);
break;
- /* */
case math_node:
- /* begin mathskip code */
+ /*tex Begin mathskip code. */
if (glue_is_zero(p) || ignore_math_skip(p)) {
siz.wd += surround(p);
break;
} else {
- /* fall through */
+ /*tex Fall through. */
}
- /* end mathskip code */
+ /*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) {
- /*
- siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p)));
+ /*tex
+ |siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p)))|
*/
gp += stretch(p);
}
} else if (shrink_order(p) == g_order) {
- /*
- siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));
+ /*tex
+ |siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));|
*/
gm += shrink(p);
}
@@ -1275,34 +1163,46 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult,
return siz;
}
-@ 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.
+/*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:
+ The source file line where the current paragraph or alignment began; a
+ negative value denotes alignment:
+
+*/
-@c
int pack_begin_line;
-@ 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.
+/*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.
+
+*/
-@c
halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
{
- halfword r; /* the box node that will be returned */
- scaled w = 0; /* width */
- scaled d = 0; /* depth */
- scaled x = 0; /* natural height */
+ /*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;
- scaled s; /* shift amount */
- int o; /* order of infinity */
+ /*tex shift amount */
+ scaled s;
+ /*tex order of infinity */
+ int o;
last_badness = 0;
r = new_node(vlist_node, 0);
if (pack_direction == -1) {
@@ -1323,12 +1223,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
total_shrink[fill] = 0;
total_stretch[filll] = 0;
total_shrink[filll] = 0;
-
while (p != null) {
- /*
- Examine node |p| in the vlist, taking account of its effect
- on the dimensions of the new box; then advance |p| to the next
- node.
+ /*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");
@@ -1336,9 +1236,11 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
switch (type(p)) {
case hlist_node:
case vlist_node:
- /*
- Incorporate box dimensions into the dimensions of
- the vbox that will contain it.
+ /*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);
@@ -1347,19 +1249,6 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
x += d + whd.ht;
d = whd.dp;
break;
- /*
- case rule_node:
- case unset_node:
- x += d + height(p);
- d = depth(p);
- if (type(p) >= rule_node) // always
- s = 0;
- else
- s = shift_amount(p);
- if (width(p) + s > w)
- w = width(p) + s;
- break;
- */
case rule_node:
case unset_node:
x += d + height(p);
@@ -1367,9 +1256,8 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
if (width(p) > w)
w = width(p);
break;
- /* */
case glue_node:
- /* Incorporate glue into the vertical totals */
+ /*tex Incorporate glue into the vertical totals. */
x += d;
d = 0;
x += width(p);
@@ -1400,27 +1288,30 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
} else {
depth(r) = d;
}
- /*
- Determine the value of |height(r)| and the appropriate glue setting;
- then |return| or |goto common_ending|.
+ /*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.
+ 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;
- /* now |x| is the excess to be made up */
+ /*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) {
- /*
- Determine vertical glue stretch setting, then |return|
- or \hbox{|goto common_ending|}.
+ /*tex
+
+ Determine vertical glue stretch setting, then |return| or |goto
+ common_ending|.
+
*/
if (total_stretch[filll] != 0)
o = filll;
@@ -1432,20 +1323,22 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
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)); /* there's nothing to stretch */
+ set_glue_ratio_zero(glue_set(r));
}
if (o == normal) {
if (list_ptr(r) != null) {
- /*
- Report an underfull vbox and |goto common_ending|, if this box
- is sufficiently bad.
+ /*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) {
@@ -1471,11 +1364,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
}
}
return r;
-
} else {
- /*
- Determine vertical glue shrink setting, then |return|
- or \hbox{|goto common_ending|}.
+ /*tex
+
+ Determine vertical glue shrink setting, then |return| or |goto
+ common_ending|.
+
*/
if (total_shrink[filll] != 0)
o = filll;
@@ -1487,24 +1381,25 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
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 {
- /* there's nothing to shrink */
+ /*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;
- /* use the maximum shrinkage */
+ /*tex Use the maximum shrinkage */
set_glue_ratio_one(glue_set(r));
- /*
- Report an overfull vbox and |goto common_ending|, if this box
- is sufficiently bad.
+ /*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);
@@ -1521,9 +1416,11 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
}
} 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) {
@@ -1544,12 +1441,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
}
COMMON_ENDING:
- /* Finish issuing a diagnostic message or an overfull or underfull vbox */
+ /*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) {
- /* it's actually negative */
+ /*tex It's actually negative. */
tprint(") in alignment at lines ");
print_int(abs(pack_begin_line));
tprint("--");
@@ -1566,17 +1463,14 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction)
return r;
}
-@ @c
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)
- /* if (q != null) */
q = lua_vpack_filter(q, h, m, l, grp, pack_direction, attr);
return vpackage(q, h, m, l, pack_direction);
}
-@ @c
void finish_vcenter(void)
{
halfword p;
@@ -1588,12 +1482,11 @@ void finish_vcenter(void)
tail_append(p);
}
-@ @c
void package(int c)
{
halfword saved0, saved2, saved3, saved4;
int grp = cur_group;
- scaled d = box_max_depth_par; /* max depth */
+ scaled d = box_max_depth_par;
unsave();
save_ptr -= 5;
saved0 = saved_value(0);
@@ -1608,11 +1501,13 @@ void package(int c)
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);
@@ -1625,7 +1520,7 @@ void package(int c)
}
}
if (saved2 != null) {
- /* DIR: Adjust back |text_dir_ptr| for |scan_spec| */
+ /*tex Adjust back |text_dir_ptr| for |scan_spec| */
flush_node_list(text_dir_ptr);
text_dir_ptr = saved2;
@@ -1635,14 +1530,19 @@ void package(int c)
box_end(saved0);
}
-@ When a box is being appended to the current vertical list, the baselineskip
-calculation is handled by the |append_to_vlist| routine.
+/*tex
+
+ When a box is being appended to the current vertical list, the baselineskip
+ calculation is handled by the |append_to_vlist| routine.
+
+*/
-@c
void append_to_vlist(halfword b, int location)
{
- scaled d; /* deficiency of space between baselines */
- halfword p; /* a new glue node */
+ /*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;
@@ -1682,188 +1582,235 @@ void append_to_vlist(halfword b, int location)
}
}
-@ 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
+
+ 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} */
-@c
-#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} */
+#define split_disc disc_ptr[vsplit_code]
-halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */
+/*tex List pointers. */
-@ 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.
+halfword disc_ptr[(vsplit_code + 1)];
-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.
+/*tex
-When the second argument |s| is |false| the deleted nodes are destroyed,
-otherwise they are collected in a list starting at |split_disc|.
+ 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|.
+
+*/
-@c
halfword prune_page_top(halfword p, boolean s)
{
halfword q;
- halfword prev_p = temp_head; /* lags one step behind |p| */
+ /*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:
- /* 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;
+ 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
- vlink(r) = q;
- r = q;
- } else {
- flush_node_list(q);
- }
- break;
- default:
- confusion("pruning");
- break;
+ 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);
}
-@ 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 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.
-@c
-scaled active_height[10]; /* distance from first active node to~|cur_p| */
+*/
+
+/*tex The distance from first active node to |cur_p|: */
+
+scaled active_height[10];
-@ 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.
+/*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.
+
+*/
-@c
#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 */
-@ 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
-@ height of the best box, without stretching or shrinking
+ 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.)
-@c
-scaled best_height_plus_depth;
+*/
-/* finds optimum page break */
+/*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)
{
- halfword prev_p = p; /* 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 */
- int pi = 0; /* penalty value */
- int b; /* badness at a trial breakpoint */
- int t; /* |type| of the node following a kern */
- int least_cost; /* the smallest badness plus penalties found so far */
- halfword best_place = null; /* the most recent break that leads to |least_cost| */
- scaled prev_dp = 0; /* depth of previous box in the list */
+ /*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) {
- /* 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. */
+ /*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 {
- /* 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 */
+ /*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;
+ 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;
}
}
- /* 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 */
+ /*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) {
- /* Compute the badness, |b|, using |awful_bad| if the box is too full */
+ /*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))
@@ -1875,7 +1822,6 @@ halfword vert_break(halfword p, scaled h, scaled d)
} else {
b = badness(cur_height - h, active_height[7]);
}
-
if (b < awful_bad) {
if (pi <= eject_penalty)
b = pi;
@@ -1892,25 +1838,29 @@ halfword vert_break(halfword p, scaled h, scaled d)
if ((b == awful_bad) || (pi <= eject_penalty))
goto DONE;
}
-
if ((type(p) < glue_node) || (type(p) > kern_node))
goto NOT_FOUND;
UPDATE_HEIGHTS:
- /* Update the current height and depth measurements with
- respect to a glue or kern node~|p| */
- /* 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. */
+ /*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.");
+ 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;
}
@@ -1929,31 +1879,39 @@ halfword vert_break(halfword p, scaled h, scaled d)
return best_place;
}
-@ Now we are ready to consider |vsplit| itself. Most of its work is accomplished
-by the two subroutines that we have just considered.
+/*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|.
+ 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).
+ 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).
-@c
-/* extracts a page of height |h| from box |n| */
+*/
+
+/*tex Extract a page of height |h| from box |n|: */
halfword vsplit(halfword n, scaled h, int m)
{
- halfword v; /* the box to be split */
- int vdir; /* the direction of the box to be split */
- halfword p; /* runs through the vlist */
- halfword q; /* points to where the break occurs */
- halfword i; /* for traversing marks lists */
+ /*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);
@@ -1962,23 +1920,27 @@ halfword vsplit(halfword n, scaled h, int m)
delete_split_first_mark(i);
delete_split_bot_mark(i);
}
- /* Dispense with trivial cases of void or bad boxes */
+ /*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.");
+ 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);
- /*
- 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.
+ /*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) {
@@ -2009,7 +1971,7 @@ halfword vsplit(halfword n, scaled h, int m)
list_ptr(v) = null;
flush_node(v);
if (q == null) {
- /* the |eq_level| of the box stays the same */
+ /*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);
@@ -2021,17 +1983,23 @@ halfword vsplit(halfword n, scaled h, int m)
}
}
-@ 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|.
+/*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|.
+
+*/
-@c
void begin_box(int box_context)
{
- halfword q; /* run through the current list */
- halfword k; /* 0 or |vmode| or |hmode| */
- int n; /* a box number */
+ /*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 ;
@@ -2039,7 +2007,7 @@ void begin_box(int box_context)
case box_code:
scan_register_num();
cur_box = box(cur_val);
- /* the box becomes void, at the same level */
+ /*tex The box becomes void, at the same level. */
box(cur_val) = null;
break;
case copy_code:
@@ -2047,10 +2015,11 @@ void begin_box(int box_context)
cur_box = copy_node_list(box(cur_val));
break;
case last_box_code:
- /*
- If the current list ends with a box node, delete it from
- the list and make |cur_box| point to it; otherwise set
- |cur_box:=null|.
+ /*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) {
@@ -2059,17 +2028,15 @@ void begin_box(int box_context)
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.");
+ 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) {
- /* todo: new code, needs testing */
-
- /* maybe: ((type(cur_list.tail_field) == hlist_node) < rule_node) */
-
if ((type(cur_list.tail_field) == hlist_node) || (type(cur_list.tail_field) == vlist_node)) {
- /* Remove the last box ... */
+ /*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;
@@ -2086,9 +2053,11 @@ void begin_box(int box_context)
}
break;
case vsplit_code:
- /*
- Split off part of a vertical box, make |cur_box| point to it. Here we
- deal with things like `\.{\\vsplit 13 to 100pt}'.
+ /*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;
@@ -2096,18 +2065,22 @@ void begin_box(int box_context)
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.");
+ 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:
- /*
- 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.
+ /*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:
@@ -2123,7 +2096,6 @@ void begin_box(int box_context)
just_pack = 1;
break;
}
- /* */
k = cur_chr - vtop_code;
set_saved_record(0, saved_boxcontext, 0, box_context);
switch (abs(cur_list.mode_field)) {
@@ -2165,6 +2137,6 @@ void begin_box(int box_context)
return;
break;
}
- /* in simple cases, we use the box immediately */
+ /*tex In simple cases, we use the box immediately. */
box_end(box_context);
}
diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c
index dd85cf01b3e..cbc77e556fa 100644
--- a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w
+++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c
@@ -1,60 +1,65 @@
-% 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/>.
+/*
-@ @c
+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"
-@ 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.
+/*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 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.
+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 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.
-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.
+*/
-@c
-#define next_break prev_break /*new name for |prev_break| after links are reversed */
+/*tex The new name for |prev_break| after links are reversed: */
+
+#define next_break prev_break
+
+/*tex The |int|s are actually halfwords. */
-/* the ints are actually halfwords */
void ext_post_line_break(int paragraph_dir,
int right_skip,
int left_skip,
@@ -78,32 +83,39 @@ void ext_post_line_break(int paragraph_dir,
{
boolean have_directional = true;
- halfword q, r; /* temporary registers for list manipulation */
+ /*tex temporary registers for list manipulation */
+ halfword q, r;
halfword k;
scaled w;
- boolean glue_break; /* was a break at glue? */
- boolean disc_break; /*was the current break at a discretionary node? */
- boolean post_disc_break; /*and did it have a nonempty post-break part? */
- scaled cur_width; /*width of line number |cur_line| */
- scaled cur_indent; /*left margin of line number |cur_line| */
- int pen; /*use when calculating penalties between lines */
- halfword cur_p; /* |cur_p|, but localized */
- halfword cur_line; /*the current line number being justified */
-
+ /*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;
- /* 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.
- */
+ /*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);
-#if 0
- used_discs = used_disc(best_bet);
-#endif
- /* |has_direction| */
+ /*tex |cur_p| will become the first breakpoint; */
cur_p = null;
do {
r = q;
@@ -111,73 +123,68 @@ void ext_post_line_break(int paragraph_dir,
next_break(r) = cur_p;
cur_p = r;
} while (q != null);
- /* |cur_p| is now the first breakpoint; */
-
- cur_line = cur_list.pg_field + 1; /* prevgraf+1 */
-
+ /*tex prevgraf + 1 */
+ cur_line = cur_list.pg_field + 1;
do {
- /* Justify the line ending at breakpoint |cur_p|, and append it to the
- current vertical list, together with associated penalties and other
- insertions; */
- /* 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 0
- tprint("BEGIN OF LINE ");
- print_int(cur_break(cur_p));
- breadth_max = 100000;
- depth_threshold = 100000;
- show_node_list(temp_head);
-#endif
-
- /* DIR: 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);
- try_couple_nodes(tmp, nxt); /* \.{\\break}\.{\\par} */
- }
+ /*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|.
+ */
- /* 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| */
-
- /* begin mathskip code */
if (temp_head != null) {
- 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 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);
}
- /* end mathskip code */
+ /*tex end mathskip code */
+ }
r = cur_break(cur_p);
q = null;
@@ -189,27 +196,26 @@ void ext_post_line_break(int paragraph_dir,
if (r == null) {
for (r = temp_head; vlink(r) != null; r = vlink(r));
if (r == final_par_glue) {
- /* This should almost always be true... */
- /* TODO assert ? */
+ /*tex This should almost always be true... */
q = r;
- /* |q| refers to the last node of the line (and paragraph) */
+ /*tex |q| refers to the last node of the line (and paragraph) */
r = alink(r);
}
- /* |r| refers to the node after which the dir nodes should be closed */
+ /*tex |r| refers to the node after which the dir nodes should be closed */
} else if (type(r) == math_node) {
surround(r) = 0;
- /* begin mathskip code */
+ /*tex begin mathskip code */
reset_glue_to_zero(r);
- /* end mathskip code */
+ /*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;
- /* |q| refers to the last node of the line */
+ /*tex |q| refers to the last node of the line */
q = r;
r = alink(r);
assert(vlink(r) == q);
- /* |r| refers to the node after which the dir nodes should be closed */
+ /*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);
@@ -228,7 +234,6 @@ void ext_post_line_break(int paragraph_dir,
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;
@@ -239,11 +244,10 @@ void ext_post_line_break(int paragraph_dir,
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; /* not special any more */
+ 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);
@@ -273,27 +277,24 @@ void ext_post_line_break(int paragraph_dir,
} else if (type(r) == kern_node) {
width(r) = 0;
}
-
- /* DIR: Adjust the dir stack based on dir nodes in this line; */
- /* TODO what about the previousparagraph ??? */
+ /*tex Adjust the dir stack based on dir nodes in this line. */
if (have_directional) {
- halfword e;
- halfword p;
+ halfword e, p;
for (e = vlink(temp_head); e != null && e != cur_break(cur_p); e = vlink(e)) {
if (type(e) == dir_node) {
- if (dir_dir(e) >= 0) {
+ 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_swap)) {
+ } 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));
-
- /* DIR: Insert dir nodes at the end of the current line; */
+ /*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) - dir_swap);
+ 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));
@@ -303,7 +304,6 @@ void ext_post_line_break(int paragraph_dir,
}
}
if (passive_right_box(cur_p) != null) {
- /* TODO: CHECK has |s| below a |alink| ? */
halfword s = copy_node_list(passive_right_box(cur_p));
halfword e = vlink(r);
couple_nodes(r, s);
@@ -313,28 +313,30 @@ void ext_post_line_break(int paragraph_dir,
if (q == null) {
q = r;
}
- /* Now [q] refers to the last node on the line */
-
- /* FIXME from this point on we no longer keep alink() valid */
-
- /* at this point |q| is 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| */
+ /*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))) {
- p = q; /* |q| has been reset to the last node of |pre_break| */
+ /*tex |q| is reset to the last node of |pre_break| */
+ p = q;
ptmp = p;
} else {
- p = alink(q); /* get |vlink(p) = q| */
+ /*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) { /* we have found a marginal kern, append it after |ptmp| */
+ 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);
@@ -345,10 +347,12 @@ void ext_post_line_break(int paragraph_dir,
q = vlink(q);
}
}
- /* if |q| was not a breakpoint at glue and has been reset to |rightskip|
- then we append |rightskip| after |q| now */
+ /*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) {
- /* Put the \.{\\rightskip} glue after node |q|; */
+ /*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));
@@ -358,21 +362,22 @@ void ext_post_line_break(int paragraph_dir,
couple_nodes(q,r1);
q = r1;
}
-
- /* /Modify the end of the line to reflect the nature of the break and to
- include \.{\\rightskip}; also set the proper value of |disc_break|; */
-
- /* Put the \.{\\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. */
+ /*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) {
- /* omega bits: */
halfword s;
r = copy_node_list(passive_left_box(cur_p));
s = vlink(q);
@@ -388,11 +393,14 @@ void ext_post_line_break(int paragraph_dir,
}
}
}
- /*at this point |q| is the leftmost node; all discardable nodes have been discarded */
+ /*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); /* no more discardables */
+ 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);
@@ -412,13 +420,14 @@ void ext_post_line_break(int paragraph_dir,
couple_nodes(r,q);
q = r;
}
- /* /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. */
+ /*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;
@@ -438,8 +447,10 @@ void ext_post_line_break(int paragraph_dir,
}
shift_amount(just_box) = cur_indent;
subtype(just_box) = line_list;
- /* /Call the packaging subroutine, setting |just_box| to the justified box; */
-
+ /*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) {
@@ -454,17 +465,17 @@ void ext_post_line_break(int paragraph_dir,
checked_break_filter(adjust);
}
adjust_tail = null;
-
- /* /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. */
+ /*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) {
@@ -481,11 +492,13 @@ void ext_post_line_break(int paragraph_dir,
}
q = club_penalties_ptr;
if (q != null) {
- r = cur_line - cur_list.pg_field; /* prevgraf */
+ /*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) { /* prevgraf */
+ } else if (cur_line == cur_list.pg_field + 1) {
+ /*tex prevgraf */
pen += club_penalty;
}
q = widow_penalties_ptr;
@@ -510,34 +523,37 @@ void ext_post_line_break(int paragraph_dir,
cur_list.tail_field = r;
}
}
- /* /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; */
+ /*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) {
- /* 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. */
+ /*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;
- /*
- 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).
-
- Todo: turn math nodes into glues when mathskip otherwise remove them.
+ /*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);
@@ -550,17 +566,17 @@ void ext_post_line_break(int paragraph_dir,
}
*/
if (type(q) == math_node) {
- /* begin mathskip code */
+ /*tex begin mathskip code */
surround(q) = 0 ;
reset_glue_to_zero(q);
- /* end mathskip code */
+ /*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) {
- /* weird, in the middle somewhere */
+ /*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) {
@@ -577,7 +593,9 @@ void ext_post_line_break(int paragraph_dir,
} while (cur_p != null);
if ((cur_line != best_line) || (vlink(temp_head) != null))
confusion("line breaking");
- cur_list.pg_field = best_line - 1; /* prevgraf */
- cur_list.dirs_field = dir_ptr; /* |dir_save| */
+ /*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/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.w b/Build/source/texk/web2c/luatexdir/tex/primitive.w
deleted file mode 100644
index 8c6592c9edf..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/primitive.w
+++ /dev/null
@@ -1,664 +0,0 @@
-% 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/>.
-
-@ @c
-
-
-#include "ptexlib.h"
-
-@ 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.
-
-@c
-two_halves *hash; /* the hash table */
-halfword hash_used; /* allocation pointer for |hash| */
-int hash_extra; /* |hash_extra=hash| above |eqtb_size| */
-halfword hash_top; /* maximum of the hash array */
-halfword hash_high; /* pointer to next high hash location */
-boolean no_new_control_sequence; /* are new identifiers legal? */
-int cs_count; /* total number of known identifiers */
-
-#define hash_is_full (hash_used==hash_base) /* test if all positions are occupied */
-
-@ \.{\\primitive} support needs a few extra variables and definitions
-
-@c
-#define prim_base 1
-
-@ 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|.
-
-@c
-#define prim_next(a) prim[(a)].lhfield /* link for coalesced lists */
-#define prim_text(a) prim[(a)].rh /* string number for control sequence name */
-#define prim_is_full (prim_used==prim_base) /* test if all positions are occupied */
-
-#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
-#define prim_origin(a) prim_origin_field(prim_eqtb[(a)]) /* level of definition */
-#define prim_eq_type(a) prim_eq_type_field(prim_eqtb[(a)]) /* command code for equivalent */
-#define prim_equiv(a) prim_equiv_field(prim_eqtb[(a)]) /* equivalent value */
-
-static pointer prim_used; /* allocation pointer for |prim| */
-static two_halves prim[(prim_size + 1)]; /* the primitives table */
-static memory_word prim_eqtb[(prim_size + 1)];
-
-@ The array |prim_data| works the other way around, it is used for
- cmd,chr -> name lookups.
-
-@c
-typedef struct prim_info {
- halfword subids; /* number of name entries */
- halfword offset; /* offset to be used for |chr_code|s */
- str_number *names; /* array of names */
-} prim_info;
-
-static prim_info prim_data[(last_cmd + 1)];
-
-@ initialize the memory arrays
-@c
-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;
-}
-
-void ini_init_primitives(void)
-{
- prim_used = prim_size; /* nothing is used */
-}
-
-
-@ 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@>
-
-@c
-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;
-}
-
-
-@ Here is the subroutine that searches the primitive table for an identifier
-@c
-pointer prim_lookup(str_number s)
-{
- int h; /* hash code */
- pointer p; /* index in |hash| array */
- 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);
- p = h + prim_base; /* we start searching here; note that |0<=h<hash_prime| */
- 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 {
- /* Insert a new primitive after |p|, then make |p| point to it */
- if (prim_text(p) > 0) {
- do { /* 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;
-}
-
-@ how to test a csname for primitive-ness
-@c
-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)));
-}
-
-
-@ a few simple accessors
-@c
-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);
-}
-
-
-@ dumping and undumping
-@c
-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]);
- }
- }
-}
-
-@ 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.
-
-@c
-void primitive_def(const char *s, size_t l, quarterword c, halfword o)
-{
- int nncs = no_new_control_sequence;
- no_new_control_sequence = false;
- cur_val = string_lookup(s, l); /* this creates the |text()| string */
- no_new_control_sequence = nncs;
- eq_level(cur_val) = level_one;
- eq_type(cur_val) = c;
- equiv(cur_val) = o;
-}
-
-@ 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.
-
-@c
-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;
-}
-
-@ 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.
-
-@c
-void
-primitive(const char *thes, quarterword c, halfword o, halfword off,
- int cmd_origin)
-{
- int prim_val; /* needed to fill |prim_eqtb| */
- str_number ss;
- assert(o >= off);
- 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);
-}
-
-
-
-@ Here is a helper that does the actual hash insertion.
-
-@c
-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;
- /* 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. */
- if (cs_text(p) > 0) {
- if (hash_high < hash_extra) {
- incr(hash_high);
- /* can't 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 {
- do {
- if (hash_is_full)
- overflow("hash size", (unsigned) (hash_size + hash_extra));
- decr(hash_used);
- } while (cs_text(hash_used) != 0); /* search for an empty location in |hash| */
- 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;
-}
-
-
-@ 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.
-
-@c
-pointer id_lookup(int j, int l)
-{ /* search the hash table */
- int h; /* hash code */
- pointer p; /* index in |hash| array */
-
- h = compute_hash((char *) (buffer + j), (unsigned) l, hash_prime);
-#ifdef VERBOSE
- {
- unsigned char *todo = xmalloc(l + 2);
- strncpy(todo, (buffer + j), l);
- todo[l] = '\0';
- todo[l + 1] = '\0';
- fprintf(stdout, "id_lookup(%s)\n", todo);
- free(todo);
- }
-#endif
- p = h + hash_base; /* we start searching here; note that |0<=h<hash_prime| */
- 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;
-}
-
-@ Here is a similar subroutine for finding a primitive in the hash.
-This one is based on a C string.
-
-@c
-pointer string_lookup(const char *s, size_t l)
-{ /* search the hash table */
- int h; /* hash code */
- pointer p; /* index in |hash| array */
- h = compute_hash(s, (unsigned) l, hash_prime);
- p = h + hash_base; /* we start searching here; note that |0<=h<hash_prime| */
- 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;
-}
-
-@ 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.
-
-@c
-#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; /* temp variable */
- 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_hex(chr_code);
- break;
- case math_given_cmd:
- if (math_umathcode_meaning_par == 1) {
- tprint_esc("Umathchar");
- show_mathcode_value(mathchar_from_integer(chr_code, tex_mathcode));
- } else {
- /* better for old macro packages that mess with meaning */
- 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 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;
- default:
- /* these are most commands, actually */
- prim_cmd_chr(cmd, chr_code);
- break;
- }
-}
diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.w b/Build/source/texk/web2c/luatexdir/tex/printing.c
index 72444156480..0a961071b05 100644
--- a/Build/source/texk/web2c/luatexdir/tex/printing.w
+++ b/Build/source/texk/web2c/luatexdir/tex/printing.c
@@ -1,87 +1,137 @@
-% 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/>.
-
-@ @c
+/*
+
+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" /* for luatex_banner */
+#include "lua/luatex-api.h"
-@ @c
#define wlog(A) fputc(A,log_file)
#define wterm(A) fputc(A,term_out)
int new_string_line = 0;
int escape_controls = 1;
-@ 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:
+/*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
-\yskip
-\hang |term_and_log|, the normal setting, prints on the terminal and on the
- transcript file.
+\stopitemize
-\hang |log_only|, prints only on the transcript file.
+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|.
-\hang |term_only|, prints only on the terminal.
+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.
-\hang |no_print|, doesn't print at all. This is used only in rare cases
- before the transcript file is open.
+*/
+
+/*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 */
-\hang |pseudo|, puts output into a cyclic buffer that is used
- by the |show_context| routine; when we get to that routine we shall discuss
- the reasoning behind this curious mode.
+int term_offset = 0;
-\hang |new_string|, appends the output to the current string in the
- string pool.
+/*tex the number of characters on the current file line */
-\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output.
+int file_offset = 0;
-\yskip
-\noindent The symbolic names `|term_and_log|', etc., have been assigned
-numeric codes that satisfy the convenient relations |no_print+1=term_only|,
-|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.
+/*tex circular buffer for pseudoprinting */
-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.
+packed_ASCII_code trick_buf[(ssup_error_line + 1)];
-@c
-alpha_file log_file; /* transcript of \TeX\ session */
-int selector = term_only; /* where to print a message */
-int dig[23]; /* digits in a number being output */
-int tally = 0; /* the number of characters recently printed */
-int term_offset = 0; /* the number of characters on the current terminal line */
-int file_offset = 0; /* the number of characters on the current file line */
-packed_ASCII_code trick_buf[(ssup_error_line + 1)]; /* circular buffer for pseudoprinting */
-int trick_count; /* threshold for pseudoprinting, explained later */
-int first_count; /* another variable for pseudoprinting */
-boolean inhibit_par_tokens = false; /* for minor adjustments to |show_token_list| */
+/*tex threshold for pseudoprinting, explained later */
-@ To end a line of text output, we call |print_ln|
+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|
+
+*/
-@c
void print_ln(void)
{
switch (selector) {
@@ -111,19 +161,21 @@ void print_ln(void)
fprintf(write_file[selector], "\n");
break;
}
- /* |tally| is not affected */
+ /*tex |tally| is not affected */
}
-@ 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).
+/*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.
+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.
+
+*/
-@c
#define needs_escaping(A) \
((! escape_controls) || (A>=0x20) || (A==0x0A) || (A==0x0D) || (A==0x09))
@@ -144,17 +196,6 @@ fuzzy anyway.
term_offset += 2; \
}
-/*
-
-#define needs_wrapping(A,B) \
- (((A>=0xF0)&&(B+4>=max_print_line)) || \
- ((A>=0xE0)&&(B+3>=max_print_line)) || \
- ((A>=0xC0)&&(B+2>=max_print_line)))
-
-we have mostly ascii in logs, so ...
-
-*/
-
#define needs_wrapping(A,B) \
( (A>=0xC0) && \
(((A>=0xF0) && (B+4>=max_print_line)) || \
@@ -236,23 +277,25 @@ void print_char(int s)
incr(tally);
}
-@ An entire string is output by calling |print|. Note that if we are outputting
-the single standard ASCII character \.c, we could call |print("c")|, since
-|"c"=99| is the number of a single-character string, as explained above. But
-|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char|
-routine when it knows that this is safe. (The present implementation
-assumes that it is always safe to print a visible ASCII character.)
+/*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.)
-@^system dependencies@>
+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.
-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.
-@c
+*/
+
void print(int s)
{
if (s >= str_ptr) {
@@ -262,9 +305,9 @@ void print(int s)
if (s < 0) {
normal_warning("print","bad string offset");
} else {
- /* TH not sure about this, disabled for now! */
+ /*tex We're not sure about this so it's disabled for now! */
if ((false) && (selector > pseudo)) {
- /* internal strings are not expanded */
+ /*tex internal strings are not expanded */
print_char(s);
return;
}
@@ -307,12 +350,13 @@ void print(int s)
}
void lprint (lstring *ss) {
- unsigned char *j, *l; /* current character code position */
+ /*tex current character code position */
+ unsigned char *j, *l;
j = ss->s;
l = j + ss->l;
while (j < l) {
- /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80 */
- /* I don't bother checking the last two bytes explicitly */
+ /*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);
@@ -325,12 +369,17 @@ void lprint (lstring *ss) {
}
}
-@ The procedure |print_nl| is like |print|, but it makes sure that the
-string appears at the beginning of a new line.
+/*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. */
-@c
void print_nlp(void)
-{ /* move to beginning of a line */
+{
if (new_string_line > 0) {
print_char(new_string_line);
} else if (((term_offset > 0) && (odd(selector))) ||
@@ -339,17 +388,22 @@ void print_nlp(void)
}
}
+/*tex Prints string |s| at beginning of the next line. */
+
void print_nl(str_number s)
-{ /* prints string |s| at beginning of line */
+{
print_nlp();
print(s);
}
-@ |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|.
+/*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|.
+
+*/
-@c
#define t_flush_buffer(target,offset) \
buffer[i++] = '\n'; \
buffer[i++] = '\0';\
@@ -361,7 +415,7 @@ most of the output actually comes through |tprint|.
void tprint(const char *sss)
{
char *buffer = NULL;
- int i = 0; /* buffer index */
+ int i = 0;
int newlinechar = new_line_char_par;
int dolog = 0;
int doterm = 0;
@@ -409,7 +463,7 @@ void tprint(const char *sss)
}
break;
}
- /* what is left is the 3 term/log settings */
+ /*tex What is left is the 3 term/log settings. */
if (dolog || doterm) {
buffer = xmalloc(strlen(sss)*3);
if (dolog) {
@@ -469,13 +523,16 @@ void tprint_nl(const char *s)
tprint(s);
}
-@ 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.
+/*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.
+
+*/
-@c
void print_banner(const char *v)
{
int callback_id = callback_defined(start_run_callback);
@@ -484,15 +541,16 @@ void print_banner(const char *v)
if (format_ident > 0)
print(format_ident);
print_ln();
- if (show_luahashchars){
+ if (show_luahashchars) {
wterm(' ');
fprintf(term_out,"Number of bits used by the hash function (" my_name "): %d",LUAI_HASHLIMIT);
- print_ln();
+ print_ln();
}
if (shellenabledp) {
wterm(' ');
- if (restrictedshell)
+ if (restrictedshell) {
fprintf(term_out, "restricted ");
+ }
fprintf(term_out, "system commands enabled.\n");
}
} else if (callback_id > 0) {
@@ -500,7 +558,6 @@ void print_banner(const char *v)
}
}
-@ @c
void log_banner(const char *v)
{
const char *months[] = { " ",
@@ -536,41 +593,51 @@ void log_banner(const char *v)
}
}
-@ @c
void print_version_banner(void)
{
fprintf(term_out, "%s", luatex_banner);
}
-@ The procedure |print_esc| prints a string that is preceded by
-the user's escape character (which is usually a backslash).
+/*tex
+
+The procedure |print_esc| prints a string that is preceded by the user's escape
+character (which is usually a backslash).
+
+*/
-@c
void print_esc(str_number s)
{
- int c = escape_char_par; /* Set variable |c| to the current escape character */
- if (c >= 0 && c < STRING_OFFSET)
+ /*tex Set variable |c| to the current escape character: */
+ int c = escape_char_par;
+ if (c >= 0 && c < 0x110000)
print(c);
print(s);
}
-@ This prints escape character, then |s|.
+/*tex
+
+This prints escape character, then |s|.
+
+*/
-@c
void tprint_esc(const char *s)
{
- int c = escape_char_par; /* Set variable |c| to the current escape character */
- if (c >= 0 && c < STRING_OFFSET)
+ /*tex Set variable |c| to the current escape character: */
+ int c = escape_char_par;
+ if (c >= 0 && c < 0x110000)
print(c);
tprint(s);
}
-@ An array of digits in the range |0..15| is printed by |print_the_digs|.
+/*tex
+
+An array of digits in the range |0..15| is printed by |print_the_digs|.
+
+*/
-@c
void print_the_digs(eight_bits k)
{
- /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
+ /*tex prints |dig[k-1]|$\,\ldots\,$|dig[0]| */
while (k-- > 0) {
if (dig[k] < 10)
print_char('0' + dig[k]);
@@ -579,17 +646,22 @@ void print_the_digs(eight_bits k)
}
}
-@ The following procedure, which prints out the decimal representation of a
-given integer |n|, has been written carefully so that it works properly
-if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
-to negative arguments, since such operations are not implemented consistently
-by all PASCAL compilers.
+/*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.
+
+*/
-@c
void print_int(longinteger n)
{
- int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */
- longinteger m; /* used to negate |n| in possibly dangerous cases */
+ /*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) {
@@ -615,11 +687,13 @@ void print_int(longinteger n)
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|.
+Here is a trivial procedure to print two digits; it is usually called with a
+parameter in the range |0<=n<=99|.
+
+*/
-@c
void print_two(int n)
{
n = abs(n) % 100;
@@ -627,12 +701,16 @@ void print_two(int n)
print_char('0' + (n % 10));
}
-@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
+/*tex
-@c
-void print_hex(int n)
+Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
+
+*/
+
+void print_qhex(int n)
{
- int k = 0 ; /* index to current digit; we assume that $0\L n<16^{22}$ */
+ /*tex index to current digit; we assume that $0\L n<16^{22}$ */
+ int k = 0 ;
print_char('"');
do {
dig[k] = n % 16;
@@ -642,16 +720,18 @@ void print_hex(int n)
print_the_digs((eight_bits) k);
}
-@ 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}.
+/*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}.
+
+*/
-@c
void print_roman_int(int n)
{
- char *j, *k; /* mysterious indices */
- int u, v; /* mysterious numbers */
+ char *j, *k;
+ int u, v;
char mystery[] = "m2d5c2l5x2v5i";
j = (char *) mystery;
v = 1000;
@@ -661,7 +741,7 @@ void print_roman_int(int n)
n = n - v;
}
if (n <= 0) {
- /* nonpositive input produces no output */
+ /*tex nonpositive input produces no output */
return;
}
k = j + 2;
@@ -680,25 +760,31 @@ void print_roman_int(int n)
}
}
-@ The |print| subroutine will not print a string that is still being
-created. The following procedure will.
+/*tex
+
+The |print| subroutine will not print a string that is still being created. The
+following procedure will.
+
+*/
-@c
void print_current_string(void)
{
- unsigned j = 0; /* points to current character code */
+ /*tex points to current character code */
+ unsigned j = 0;
while (j < cur_length)
print_char(cur_string[j++]);
}
-@ 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.
+/*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.
+
+*/
-@c
void print_cs(int p)
{
str_number t = cs_text(p);
@@ -731,10 +817,12 @@ void print_cs(int p)
}
}
-@ Here is a similar procedure; it avoids the error checks, and it never
-prints a space after the control sequence.
+/*tex
-@c
+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;
@@ -762,9 +850,12 @@ void sprint_cs_name(pointer p)
}
}
-@ This procedure is never called when |interaction<scroll_mode|.
+/*tex
+
+This procedure is never called when |interaction<scroll_mode|.
+
+*/
-@c
void prompt_input(const char *s)
{
wake_up_terminal();
@@ -772,10 +863,13 @@ void prompt_input(const char *s)
term_input();
}
-@ Then there is a subroutine that prints glue stretch and shrink, possibly
-followed by the name of finite units:
+/*tex
+
+Then there is a subroutine that prints glue stretch and shrink, possibly followed
+by the name of finite units:
+
+*/
-@c
void print_glue(scaled d, int order, const char *s)
{
print_scaled(d);
@@ -792,9 +886,12 @@ void print_glue(scaled d, int order, const char *s)
}
}
-@ The next subroutine prints a whole glue specification
+/*tex
+
+The next subroutine prints a whole glue specification
+
+*/
-@c
void print_spec(int p, const char *s)
{
if (p < 0) {
@@ -814,42 +911,47 @@ void print_spec(int p, const char *s)
}
}
-@ 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.
+/*tex
-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@>
+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.
-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.
+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@>
-@c
-int font_in_short_display; /* an internal font number */
+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.
-@ Boxes, rules, inserts, whatsits, marks, and things in general that are
-sort of ``complicated'' are indicated only by printing `\.{[]}'.
+*/
-@c
+/*tex An internal font number: */
-/*
-So, 0, 1 as well as any large value will behave the same as before. The reason
-for this extension is that a \name not always makes sense.
+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
+3 <bar @ ..> xyz
4 <id>
5 <id: bar>
-6 <id: bar @@ ..> xyz
+6 <id: bar @ ..> xyz
+\stoptyping
*/
@@ -858,12 +960,12 @@ 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) {
- /* < > is less likely to clash with text parenthesis */
+ /*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(" @@ ");
+ tprint(" @ ");
print_scaled(font_size(f));
tprint("pt");
}
@@ -873,7 +975,7 @@ void print_font_identifier(internal_font_number f)
tprint(": ");
print_font_name(f);
if (tracing_fonts_par >= 6 || font_size(f) != font_dsize(f)) {
- tprint(" @@ ");
+ tprint(" @ ");
print_scaled(font_size(f));
tprint("pt");
}
@@ -881,7 +983,7 @@ void print_font_identifier(internal_font_number f)
}
print_char('>');
} else {
- /* old method, inherited from pdftex */
+ /*tex old method, inherited from pdftex */
if (fonttext > 0) {
print_esc(fonttext);
} else {
@@ -892,7 +994,7 @@ void print_font_identifier(internal_font_number f)
tprint(" (");
print_font_name(f);
if (font_size(f) != font_dsize(f)) {
- tprint("@@");
+ tprint("@");
print_scaled(font_size(f));
tprint("pt");
}
@@ -901,9 +1003,12 @@ void print_font_identifier(internal_font_number f)
}
}
-@ This prints highlights of list |p|.
+/*tex
+
+This prints highlights of list |p|.
+
+*/
-@c
void short_display(int p)
{
while (p != null) {
@@ -922,20 +1027,27 @@ void short_display(int p)
print(character(p));
}
} else {
- /* Print a short indication of the contents of node |p| */
+ /*tex Print a short indication of the contents of node |p| */
print_short_node_contents(p);
}
p = vlink(p);
}
}
-@ 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.
+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.
+
+*/
-@c
void print_font_and_char(int p)
{
if (!is_valid_font(font(p)))
@@ -946,9 +1058,12 @@ void print_font_and_char(int p)
print(character(p));
}
-@ This prints token list data in braces
+/*tex
+
+This prints token list data in braces
+
+*/
-@c
void print_mark(int p)
{
print_char('{');
@@ -959,9 +1074,12 @@ void print_mark(int p)
print_char('}');
}
-@ This prints dimensions of a rule node.
+/*tex
+
+This prints dimensions of a rule node.
+
+*/
-@c
void print_rule_dimen(scaled d)
{
if (is_running(d))
@@ -970,41 +1088,54 @@ void print_rule_dimen(scaled d)
print_scaled(d);
}
-@ 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.
+/*tex
-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.
+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.
-@c
-int depth_threshold; /* maximum nesting depth in box displays */
-int breadth_max; /* maximum number of items shown at the same list level */
+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.
-@ The recursive machinery is started by calling |show_box|. Assign the values
+*/
+
+/*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|
-@c
+*/
+
void show_box(halfword p)
{
depth_threshold = show_box_depth_par;
breadth_max = show_box_breadth_par;
if (breadth_max <= 0)
breadth_max = 5;
- /* the show starts at |p| */
+ /*tex the show starts at |p| */
show_node_list(p);
print_ln();
}
-@ Helper for debugging purposes. It prints highlights of list |p|
+/*tex
+
+Helper for debugging purposes. It prints highlights of list |p|
+
+*/
-@c
void short_display_n(int p, int m)
{
int i = 0;
@@ -1041,7 +1172,7 @@ void short_display_n(int p, int m)
short_display(vlink(post_break(p)));
print_char('|');
} else {
- /* Print a short indication of the contents of node |p| */
+ /*tex Print a short indication of the contents of node |p| */
print_short_node_contents(p);
}
}
@@ -1052,13 +1183,16 @@ void short_display_n(int p, int m)
update_terminal();
}
-@ 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.)
+/*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.)
+
+*/
-@c
void print_csnames(int hstart, int hfinish)
{
int h;
@@ -1066,11 +1200,11 @@ void print_csnames(int hstart, int hfinish)
fprintf(stderr, "fmtdebug:csnames from %d to %d:", (int) hstart, (int) hfinish);
for (h = hstart; h <= hfinish; h++) {
if (cs_text(h) > 0) {
- /* we have anything at this position */
+ /*tex We have anything at this position. */
c = str_string(cs_text(h));
l = c + str_length(cs_text(h));
while (c < l) {
- /* print the characters */
+ /*tex Print the characters. */
fputc(*c++, stderr);
}
fprintf(stderr, "|");
@@ -1078,11 +1212,14 @@ void print_csnames(int hstart, int hfinish)
}
}
-@ 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.
+/*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.
+
+*/
-@c
void print_file_line(void)
{
int level = in_open;
@@ -1102,11 +1239,14 @@ void print_file_line(void)
}
}
-@ \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:
+/*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:
+
+*/
-@c
void begin_diagnostic(void)
{
global_old_setting = selector;
@@ -1117,9 +1257,12 @@ void begin_diagnostic(void)
}
}
-@ Restore proper conditions after tracing.
+/*tex
+
+Restore proper conditions after tracing.
+
+*/
-@c
void end_diagnostic(boolean blank_line)
{
tprint_nl("");
@@ -1128,8 +1271,11 @@ void end_diagnostic(boolean blank_line)
selector = global_old_setting;
}
-@ Of course we had better declare another global variable, if the previous
-routines are going to work.
+/*tex
+
+Of course we had better declare another global variable, if the previous routines
+are going to work.
+
+*/
-@c
int global_old_setting;
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..8f7e1d59178
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/scanning.c
@@ -0,0 +1,2743 @@
+/*
+
+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:
+ scan_normal_glue();
+ q = cur_val;
+ if (m == glue_stretch_order_code)
+ cur_val = stretch_order(q);
+ else
+ cur_val = shrink_order(q);
+ 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) &&
+ !is_char_node(cur_list.tail_field) &&
+ (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) {
+ 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");
+ help1("I'm going to ignore the # sign you just used.");
+ error();
+ } else {
+ incr(t);
+ if (cur_tok != t) {
+ print_err("Parameters must be numbered consecutively");
+ 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.w b/Build/source/texk/web2c/luatexdir/tex/scanning.w
deleted file mode 100644
index 7bed4d29016..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/scanning.w
+++ /dev/null
@@ -1,2621 +0,0 @@
-% scanning.w
-%
-% 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/>.
-
-@ @c
-
-#include "ptexlib.h"
-
-@ @c
-static void scan_expr(void);
-
-@ 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|.
-
-@c
-void scan_left_brace(void)
-{ /* reads a mandatory |left_brace| */
- /* 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);
- }
-}
-
-@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded
-by optional spaces; `\.{\\relax}' is not ignored here.
-
-@c
-void scan_optional_equals(void)
-{
- /* Get the next non-blank non-call token */
- do {
- get_x_token();
- } while (cur_cmd == spacer_cmd);
- if (cur_tok != other_token + '=')
- back_input();
-}
-
-@ Here is a procedure that sounds an alarm when mu and non-mu units
-are being switched.
-
-@c
-static void mu_error(void)
-{
- print_err("Incompatible glue units");
- help1("I'm going to assume that 1mu=1pt when they're mixed.");
- error();
-}
-
-@ The next routine `|scan_something_internal|' is used to fetch internal
-numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}'
-when expanding constructions like `\.{\\the\\toks0}' and
-`\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int|
-procedure, which calls |scan_something_internal|; on the other hand,
-|scan_something_internal| also calls |scan_int|, for constructions like
-`\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we
-have to declare |scan_int| as a |forward| procedure. A few other
-procedures are also declared at this point.
-
-\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.
-
-@c
-int cur_val; /* value returned by numeric scanners */
-int cur_val1; /* delcodes are sometimes 51 digits */
-int cur_val_level; /* the ``level'' of this value */
-
-#define scanned_result(A,B) do { \
- cur_val=A; \
- cur_val_level=B; \
-} while (0)
-
-@ 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.
-
-@c
-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 delete_glue)
-{
- if (cur_val_level >= glue_val_level) {
- halfword m = cur_val;
- cur_val = new_spec(m);
- if (delete_glue)
- flush_node(m);
- negate(width(cur_val));
- negate(stretch(cur_val));
- negate(shrink(cur_val));
- } else {
- negate(cur_val);
- }
-}
-*/
-
-void negate_cur_val(boolean modify_glue)
-{
- if (cur_val_level >= glue_val_level) {
- if (modify_glue) {
- /* 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);
- }
-}
-
-@ 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
-
-@c
-static boolean short_scan_something_internal(int cmd, int chr, int level,
- boolean negative)
-{
- halfword m; /* |chr_code| part of the operand token */
- halfword q; /* general purpose index */
- int p; /* index into |nest| */
- 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_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 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 math_style_cmd:
- scanned_result(m, int_val_level);
- break;
- case set_aux_cmd:
- /* 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:
- /* Fetch the |prev_graf| */
- if (cur_list.mode_field == 0) {
- scanned_result(0, int_val_level); /* |prev_graf=0| within \.{\\write} */
- } 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:
- /* Fetch the |dead_cycles| or the |insert_penalties| */
- if (m == 0)
- cur_val = dead_cycles;
- else if (m == 2)
- cur_val = interaction; /* interactionmode */
- else
- cur_val = insert_penalties;
- cur_val_level = int_val_level;
- break;
- case set_page_dimen_cmd:
- /* 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:
- /* 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:
- /* 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:
- /* 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;
- /* 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) {
- /* Process an expression and |return| */
- if (m < eTeX_mu) {
- switch (m) {
- case mu_to_glue_code:
- scan_mu_glue();
- break;
- }; /* there are no other cases */
- cur_val_level = glue_val_level;
- } else if (m < eTeX_expr) {
- switch (m) {
- case glue_to_mu_code:
- scan_normal_glue();
- break;
- } /* there are no other cases */
- cur_val_level = mu_val_level;
- } else {
- cur_val_level = m - eTeX_expr + int_val_level;
- scan_expr();
- }
- /* 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) {
- /*
- 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;
- } /* there are no other cases */
- } 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;
- } /* there are no other cases */
- 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:
- scan_normal_glue();
- q = cur_val;
- if (m == glue_stretch_order_code)
- cur_val = stretch_order(q);
- else
- cur_val = shrink_order(q);
- flush_node(q);
- break;
- } /* there are no other cases */
- cur_val_level = int_val_level;
- }
- } else {
- if (cur_chr == glue_val_level)
- cur_val = zero_glue; /* a pointer */
- 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 {
- cur_val_level = cur_chr; /* assumes identical values */
- }
- if ((cur_list.tail_field != contrib_head) &&
- !is_char_node(cur_list.tail_field) &&
- (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 = new_spec(cur_list.tail_field);
- 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;
- } /* there are no other cases */
- } 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;
- } /* there are no other cases */
- }
- }
- cur_chr = save_cur_chr;
- break;
- default:
- succeeded = false;
- }
- if (succeeded) {
- while (cur_val_level > level) {
- /* Convert |cur_val| to a lower level */
- downgrade_cur_val(false);
- }
- /* 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) {
- /* 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;
-}
-
-@ 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|.
-
-@c
-void scan_something_simple(halfword cmd, halfword subitem)
-{
- /* negative is never true */
- if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) {
- /* 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);
- }
-}
-
-@ 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|.
-
-@c
-void scan_something_internal(int level, boolean negative)
-{
- /* fetch an internal parameter */
- halfword m; /* |chr_code| part of the operand token */
- int n, k; /* accumulators */
- RESTART:
- m = cur_chr;
- if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) {
- switch (cur_cmd) {
- case def_char_code_cmd:
- /* 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: /* bonus */
- /* 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:
- /* 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:
- /* 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:
- /* 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:
- /* Fetch a math param */
- 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:
- /* 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:
- /* Fetch a font dimension */
- get_font_dimen();
- break;
- case assign_font_int_cmd:
- /* 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:
- /* 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;
- } /* there are no other cases */
- cur_val_level = m;
- break;
- case ignore_spaces_cmd: /* trap unexpandable primitives */
- if (cur_chr == 1) {
- /* 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:
- /* 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) {
- /* Convert |cur_val| to a lower level */
- downgrade_cur_val(false);
- }
- /* 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) {
- /* 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);
- }
- }
-}
-
-@ 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.
-
-@c
-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;
- }
-}
-
-@ @c
-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);
-}
-
-@ @c
-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;
-}
-
-@ 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.
-
-@c
-int radix; /* |scan_int| sets this to 8, 10, 16, or zero */
-
-@ 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.
-
-@c
-void scan_int(void)
-{ /* sets |cur_val| to an integer */
- boolean negative; /* should the answer be negated? */
- int m; /* |$2^{31}$ / radix|, the threshold of danger */
- int d; /* the digit just scanned */
- boolean vacuous; /* have no digits appeared? */
- boolean OK_so_far; /* has an error message been issued? */
- radix = 0;
- OK_so_far = true;
- /* Get the next non-blank non-sign token; set |negative| appropriately */
- negative = false;
- do {
- /* 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) {
- /* 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. */
- get_token(); /* suppress macro expansion */
- 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 { /* 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 {
- /* Scan an optional space */
- get_x_token();
- if (cur_cmd != spacer_cmd)
- back_input();
- }
-
- } else if (cur_tok == cs_token_flag + frozen_primitive) {
- /* 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 {
- /* Scan a numeric constant */
- radix = 10;
- m = 214748364;
- if (cur_tok == octal_token) {
- radix = 8;
- m = 02000000000;
- get_x_token();
- } else if (cur_tok == hex_token) {
- radix = 16;
- m = 01000000000;
- get_x_token();
- }
- vacuous = true;
- cur_val = 0;
- /* Accumulate the constant until |cur_tok| is not a suitable digit */
- while (1) {
- if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token)
- && (cur_tok <= zero_token + 9)) {
- d = cur_tok - zero_token;
- } else if (radix == 16) {
- if ((cur_tok <= A_token + 5) && (cur_tok >= A_token)) {
- d = cur_tok - A_token + 10;
- } else if ((cur_tok <= other_A_token + 5)
- && (cur_tok >= other_A_token)) {
- d = cur_tok - other_A_token + 10;
- } else {
- 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) {
- /* 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);
-}
-
-@ The following code is executed when |scan_something_internal| was
-called asking for |mu_val|, when we really wanted a ``mudimen'' instead
-of ``muglue.''
-
-@c
-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;
- }
-}
-
-@ 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|.
-
-@c
-int cur_order; /* order of infinity found by |scan_dimen| */
-
-
-@ Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen|
-may begin with |scan_int|. This explains why it is convenient to use
-|scan_int| also for the integer part of a decimal fraction.
-
-Several branches of |scan_dimen| work with |cur_val| as an integer and
-with an auxiliary fraction |f|, so that the actual quantity of interest is
-$|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked''
-representation is put into the single word |cur_val|, which suddenly
-switches significance from |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.
-
-@c
-
-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)
-
-/*
- 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)
-{
- boolean negative = false; /* should the answer be negated? */
- boolean is_true = false;
- int f = 0; /* numerator of a fraction whose denominator is $2^{16}$ */
- int num = 0; /* conversion ratio for the scanned units */
- int denom = 0;
- halfword q; /* top of decimal digit stack */
- scaled v; /* an internal dimension */
- int save_cur_val; /* temporary storage of |cur_val| */
- arith_error = false;
- cur_order = normal;
- if (!shortcut) {
- /* Get the next non-blank non-sign... */
- do {
- /* 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 + '+');
- if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) {
- /* 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)) {
- /*
- 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;
- get_token(); /* |point_token| is being re-scanned */
- while (1) {
- get_x_token();
- if ((cur_tok > zero_token + 9) || (cur_tok < zero_token))
- break;
- if (k < 17) {
- /* 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) {
- /* in this case |f=0| */
- negative = !negative;
- negate(cur_val);
- }
- /*
- Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there
- are |x| sp per unit; |goto attach_sign| if the units are internal. 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) {
- /*
- Scan for (f)\.{fil} units; |goto attach_fraction| if found. In traditional
- \TeX, a specification like `\.{filllll}' or `\.{fill L L L}' will lead to
- two error messages (one for each additional keyword \.{"l"}). Not so for
- \LuaTeX, it just parses the construct in reverse.
-
- if (scan_keyword("filll")) {
- cur_order = filll;
- goto ATTACH_FRACTION;
- } else if (scan_keyword("fill")) {
- cur_order = fill;
- goto ATTACH_FRACTION;
- } else if (scan_keyword("fil")) {
- cur_order = fil;
- goto ATTACH_FRACTION;
- } else if (scan_keyword("fi")) {
- cur_order = sfi;
- goto ATTACH_FRACTION;
- }
-
- But ... it failed in alignments so now we do this. And, as we support an extra
- l we don't issue an error message (we didn't do that anyway).
- */
- 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;
- }
- }
- /*
- Scan for (u)units that are internal dimensions; |goto attach_sign| with
- |cur_val| set if found
- */
- save_cur_val = cur_val;
- /* 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 {
- /* math_given_cmd xmath_given_cmd 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;
- }
- /* bah ... true forces to split the unit scanner */
- if (mu) {
- /* 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;
- }
- /* 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;
- /*
- 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")) {
- goto SCALE_VALUE; /* the easy case */
- } 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;
- }
- /* Complain about unknown unit and |goto done2| */
- scan_dimen_unknown_unit_error();
- goto BAD_NEWS;
- SCALE_VALUE:
- /* Adjust (f) for the magnification ratio */
- if (is_true) {
- /* 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 {
- /* in pdf mode we're always true */
- one_true_inch = one_inch; /* saveguard */
- }
- }
- /* */
- 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:
- /* Scan an optional space */ /* happens too often */
- get_x_token();
- if (cur_cmd != spacer_cmd) {
- back_input();
- }
- ATTACH_SIGN:
- if (arith_error || (abs(cur_val) >= 010000000000)) {
- /* 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);
- }
-}
-
-@ 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.
-
-@c
-void scan_glue(int level)
-{
- boolean negative = false; /* should the answer be negated? */
- halfword q = null; /* new glue specification */
- boolean mu = (level == mu_val_level); /* does |level=mu_val|? */
- /* Get the next non-blank non-sign ... */
- do {
- /* 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 + '+');
- 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);
- }
- /*
- 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;
-}
-
-@ This is an omega routine
-@c
-void scan_scaled(void)
-{ /* sets |cur_val| to a scaled value */
- boolean negative; /* should the answer be negated? */
- int f; /* numerator of a fraction whose denominator is $2^{16}$ */
- int k, kk; /* number of digits in a decimal fraction */
- halfword p, q; /* top of decimal digit stack */
- f = 0;
- arith_error = false;
- negative = false;
- /* Get the next non-blank non-sign... */
- do {
- /* 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 + '+');
-
- back_input();
- if (cur_tok == continental_point_token)
- cur_tok = point_token;
- if (cur_tok != point_token) {
- scan_int();
- } else {
- radix = 10;
- cur_val = 0;
- }
- if (cur_tok == continental_point_token)
- cur_tok = point_token;
- if ((radix == 10) && (cur_tok == point_token)) {
- /* Scan decimal fraction */
- /* TODO: merge this with the same block in |scan_dimen| */
- /* 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. */
-
- k = 0;
- p = null;
- get_token(); /* |point_token| is being re-scanned */
- while (1) {
- get_x_token();
- if ((cur_tok > zero_token + 9) || (cur_tok < zero_token))
- break;
- if (k < 17) { /* 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) { /* in this case |f=0| */
- negative = !negative;
- negate(cur_val);
- }
- if (cur_val > 040000)
- arith_error = true;
- else
- cur_val = cur_val * unity + f;
- if (arith_error || (abs(cur_val) >= 010000000000)) {
- print_err("Stack number too large");
- error();
- }
- if (negative)
- negate(cur_val);
-}
-
-@ 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}'.
-
-@c
-halfword the_toks(void)
-{
- int old_setting; /* holds |selector| setting */
- halfword p, q, r; /* used for copying a token list */
- int c; /* value of |cur_chr| */
- str_number s;
- halfword retval;
- /* 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) {
- /* 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) {
- r = token_link(cur_val); /* do not copy the reference count */
- 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(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;
- } /* there are no other cases */
- selector = old_setting;
- s = make_string();
- retval = str_toks(str_lstring(s));
- flush_str(s);
- return retval;
- }
-}
-
-@ @c
-str_number the_scanned_result(void)
-{
- int old_setting; /* holds |selector| setting */
- str_number r; /* return value * */
- 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(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;
- } /* there are no other cases */
- r = make_string();
- }
- selector = old_setting;
- return r;
-}
-
-@ 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.
-
-@c
-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; /* the parameter number */
- 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 {
- /* 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;
- int n; /* the parameter number */
- scan_int();
- n = cur_val;
- scan_font_ident();
- f = cur_val;
- cur_val = 0; /* initialize return value */
- 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 {
- /* 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);
-}
-
-@ 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).
-
-@c
-halfword scan_rule_spec(void)
-{
- /* |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;
-}
-
-
-@ Declare procedures that scan font-related stuff
-
-@c
-void scan_font_ident(void)
-{
- internal_font_number f;
- halfword m;
- /* 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;
-}
-
-@ 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.)
-
-@c
-void scan_general_text(void)
-{
- int s; /* to save |scanner_status| */
- halfword w; /* to save |warning_index| */
- halfword d; /* to save |def_ref| */
- halfword p; /* tail of the token list being built */
- halfword q; /* new node being added to the token list via |store_new_token| */
- halfword unbalance; /* number of unmatched left braces */
- s = scanner_status;
- w = warning_index;
- d = def_ref;
- scanner_status = absorbing;
- warning_index = cur_cs;
- p = get_avail();
- def_ref = p;
- set_token_ref_count(def_ref, 0);
- p = def_ref;
- scan_left_brace(); /* remove the compulsory 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);
- free_avail(def_ref); /* discard reference count */
- 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;
-}
-
-@ The |get_x_or_protected| procedure is like |get_x_token| except that
-protected macros are not expanded.
-
-@c
-void get_x_or_protected(void)
-{ /* sets |cur_cmd|, |cur_chr|, |cur_tok|,
- and expands non-protected macros */
- 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();
- }
-}
-
-@ |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.
-
-@c
-halfword scan_toks(boolean macro_def, boolean xpand)
-{
- halfword t; /* token representing the highest parameter number */
- halfword s; /* saved token */
- halfword p; /* tail of the token list being built */
- halfword q; /* new node being added to the token list via |store_new_token| */
- halfword unbalance; /* number of unmatched left braces */
- halfword hash_brace; /* possible `\.{\#\{}' token */
- if (macro_def)
- scanner_status = defining;
- else
- scanner_status = absorbing;
- warning_index = cur_cs;
- 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) {
- /* Scan and build the parameter part of the macro definition */
- while (1) {
- get_token(); /* set |cur_cmd|, |cur_chr|, |cur_tok| */
- if (cur_tok < right_brace_limit)
- break;
- if (cur_cmd == mac_param_cmd) {
- /* 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) {
- hash_brace = cur_tok;
- store_new_token(cur_tok);
- store_new_token(end_match_token);
- goto DONE;
- }
- if (t == zero_token + 9) {
- print_err("You already have nine parameters");
- help1("I'm going to ignore the # sign you just used.");
- error();
- } else {
- incr(t);
- if (cur_tok != t) {
- print_err("Parameters must be numbered consecutively");
- 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) {
- /* 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 {
- scan_left_brace(); /* remove the compulsory left brace */
- }
- DONE:
- /* Scan and build the body of the token list; |goto found| when finished */
- unbalance = 1;
- while (1) {
- if (xpand) {
- /* 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) {
- /* 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;
-}
-
-@ Here we declare two trivial procedures in order to avoid mutually
-recursive procedures with parameters.
-
-@c
-void scan_normal_glue(void)
-{
- scan_glue(glue_val_level);
-}
-
-void scan_mu_glue(void)
-{
- scan_glue(mu_val_level);
-}
-
-@ 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.
-
-@c
-typedef enum {
- expr_none = 0, /* \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */
- expr_add = 1, /* \.( $\langle\it expr\rangle$ \.+ seen */
- expr_sub = 2, /* \.( $\langle\it expr\rangle$ \.- seen */
- expr_mult = 3, /* $\langle\it term\rangle$ \.* seen */
- expr_div = 4, /* $\langle\it term\rangle$ \./ seen */
- expr_scale = 5, /* $\langle\it term\rangle$ \.* $\langle\it factor\rangle$ \./ seen */
-} expression_states;
-
-@ 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|
-
-@c
-#define num_error(A) do { \
- arith_error=true; \
- A=0; \
-} while (0)
-
-@ clear a glue spec and set |arith_error|
-
-@c
-#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)
-
-@ 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.
-
-@c
-#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)
-
-@
- 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|.
-
-@c
-inline static int add_or_sub(int x, int y, int max_answer, boolean negative)
-{
- int a; /* the answer */
- if (negative)
- negate(y);
- if (x >= 0) {
- if (y <= max_answer - x)
- a = x + y;
- else
- num_error(a);
- } else if (y >= -max_answer - x) {
- a = x + y;
- } else {
- num_error(a);
- }
- return a;
-}
-
-#define expr_m(A) A = nx_plus_y((A),f,0)
-#define expr_d(A) A=quotient((A),f)
-
-@ The function |quotient(n,d)| computes the rounded quotient
-$q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive.
-
-@c
-inline static int quotient(int n, int d)
-{
- boolean negative; /* should the answer be negated? */
- int a; /* the answer */
- if (d == 0) {
- num_error(a);
- } else {
- if (d > 0) {
- negative = false;
- } else {
- negate(d);
- negative = true;
- }
- if (n < 0) {
- negate(n);
- negative = !negative;
- }
- a = n / d;
- n = n - a * d;
- d = n - d; /* avoid certain compiler optimizations! */
- if (d + n >= 0)
- incr(a);
- if (negative)
- negate(a);
- }
- return a;
-}
-
-#define expr_s(A) A=fract((A),n,f,max_dimen)
-
-@ 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.
-
-@c
-int fract(int x, int n, int d, int max_answer)
-{
- boolean negative; /* should the answer be negated? */
- int a; /* the answer */
- int f; /* a proper fraction */
- int h; /* smallest integer such that |2*h>=d| */
- int r; /* intermediate remainder */
- int t; /* temp variable */
- if (d == 0)
- goto TOO_BIG;
- a = 0;
- if (d > 0) {
- negative = false;
- } else {
- negate(d);
- negative = true;
- }
- if (x < 0) {
- negate(x);
- negative = !negative;
- } else if (x == 0) {
- goto DONE;
- }
- if (n < 0) {
- negate(n);
- negative = !negative;
- }
- t = n / d;
- if (t > max_answer / x)
- goto TOO_BIG;
- a = t * x;
- n = n - t * d;
- if (n == 0)
- goto FOUND;
- t = x / d;
- if (t > (max_answer - a) / n)
- goto TOO_BIG;
- a = a + t * n;
- x = x - t * d;
- if (x == 0)
- goto FOUND;
- if (x < n) {
- t = x;
- x = n;
- n = t;
- }
- /* now |0<n<=x<d| */
- /* Compute $f=\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;
-}
-
-@ @c
-static void scan_expr(void)
-{ /* scans and evaluates an expression */
- boolean a, b; /* saved values of |arith_error| */
- int l; /* type of expression */
- int r; /* state of expression so far */
- int s; /* state of term so far */
- int o; /* next operation or type of next factor */
- int e; /* expression so far */
- int t; /* term so far */
- int f; /* current factor */
- int n; /* numerator of combined multiplication and division */
- halfword p; /* top of expression stack */
- halfword q; /* for stack manipulations */
- l = cur_val_level;
- a = arith_error;
- b = false;
- p = null;
- /* 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;
- /* 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 + '(') {
- /* 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:
- /* Scan the next operator and set |o| */
- /* 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;
- /* 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);
- }
-
- switch (s) {
- /* Cases for evaluation of the current term */
- case expr_none:
- /*
- 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)) {
- /* do we really need to copy here ? */
-// t = new_spec(f);
-// flush_node(f);
- normalize_glue(t);
- } else {
- t = f;
- }
- break;
- case expr_mult:
- /* 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:
- /* 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:
- /* 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;
- } /* there are no other cases */
- if (o > expr_sub) {
- s = o;
- } else {
- /* 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 {
- /* 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) {
- /* 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/stringpool.w b/Build/source/texk/web2c/luatexdir/tex/stringpool.c
index 5972657f56e..bc09f814b00 100644
--- a/Build/source/texk/web2c/luatexdir/tex/stringpool.w
+++ b/Build/source/texk/web2c/luatexdir/tex/stringpool.c
@@ -1,69 +1,89 @@
-% 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/>.
-
-@ @c
+/*
+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"
-@ Control sequence names and diagnostic messages are variable-length strings
-of eight-bit characters. Since PASCAL did not have a well-developed string
+/*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
+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.
+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|: */
-@c
-lstring *string_pool; /* the array of strings */
-lstring *_string_pool; /* this variable lives |STRING_OFFSET| below |string_pool|
- (handy for debugging:
- |_string_pool[str_ptr] == str_string(str_ptr)| */
+str_number init_str_ptr;
-str_number str_ptr = (STRING_OFFSET + 1); /* number of the current string being created */
-str_number init_str_ptr; /* the starting value of |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; /* current string buffer */
-unsigned cur_length; /* current index in that buffer */
-unsigned cur_string_size; /* malloced size of |cur_string| */
-unsigned pool_size; /* 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.
+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.
+
+*/
-@c
void reset_cur_string(void)
{
cur_length = 0;
@@ -72,44 +92,41 @@ void reset_cur_string(void)
memset(cur_string, 0, 256);
}
-@ current string enters the pool
-@c
str_number make_string(void)
{
- if (str_ptr == (max_strings + STRING_OFFSET))
- overflow("number of strings",
- (unsigned) (max_strings - init_str_ptr + STRING_OFFSET));
+ 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();
-#if 0
- printf("Made a string: %s (s=%d)\n", (char *)str_string(str_ptr), (int)str_ptr);
-#endif
str_ptr++;
return (str_ptr - 1);
}
-@ @c
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.
-@ 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.
+*/
-@c
boolean str_eq_buf(str_number s, int k)
-{ /* test equality of strings */
- int a; /* a unicode character */
+{
+ int a;
if (s < STRING_OFFSET) {
a = buffer_to_unichar(k);
if (a != s)
@@ -125,15 +142,17 @@ boolean str_eq_buf(str_number s, int k)
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.
-@ 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.
+*/
-@c
boolean str_eq_str(str_number s, str_number t)
-{ /* test equality of strings */
- int a = 0; /* a utf char */
- unsigned char *j, *k, *l; /* running indices */
+{
+ int a = 0;
+ unsigned char *j, *k, *l;
if (s < STRING_OFFSET) {
if (t >= STRING_OFFSET) {
k = str_string(t);
@@ -167,8 +186,8 @@ boolean str_eq_str(str_number s, str_number t)
return true;
}
-@ string compare
-@c
+/*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))
@@ -177,43 +196,47 @@ boolean str_eq_cstr(str_number r, const char *s, size_t l)
}
-@ 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.
+/*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.
+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.
+
+*/
-@ initializes the string pool, but returns |false| if something goes wrong
-@c
boolean get_strings_started(void)
{
reset_cur_string();
return true;
}
-@ 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.
+/*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.
+
+*/
-@c
str_number search_string(str_number search)
{
- str_number s; /* running index */
- size_t len; /* length of searched string */
+ str_number s;
+ size_t len;
len = str_length(search);
if (len == 0) {
return get_nullstr();
} else {
- s = search - 1; /* start search with newest string below |s|; |search>1|! */
+ /*tex We start the search with newest string below |s|; |search>1|! */
+ s = search - 1;
while (s >= STRING_OFFSET) {
- /* first |string_offset| strings depend on implementation!! */
+ /* The first |string_offset| of strings depend on the implementation! */
if (str_length(s) == len)
if (str_eq_str(s, search))
return s;
@@ -223,7 +246,6 @@ str_number search_string(str_number search)
return 0;
}
-@ @c
str_number maketexstring(const char *s)
{
if (s == NULL || *s == 0)
@@ -231,7 +253,6 @@ str_number maketexstring(const char *s)
return maketexlstring(s, strlen(s));
}
-@ @c
str_number maketexlstring(const char *s, size_t l)
{
if (s == NULL || l == 0)
@@ -243,8 +264,12 @@ str_number maketexlstring(const char *s, size_t l)
return (str_ptr - 1);
}
-@ append a C string to a TeX string
-@c
+/*tex
+
+ This appends a C string to a \TEX\ string:
+
+*/
+
void append_string(const unsigned char *s, unsigned l)
{
if (s == NULL || *s == 0)
@@ -256,14 +281,12 @@ void append_string(const unsigned char *s, unsigned l)
return;
}
-@ @c
char *makecstring(int s)
{
size_t l;
return makeclstring(s, &l);
}
-@ @c
char *makeclstring(int s, size_t * len)
{
if (s < STRING_OFFSET) {
@@ -279,7 +302,6 @@ char *makeclstring(int s, size_t * len)
}
}
-@ @c
int dump_string_pool(void)
{
int j;
@@ -297,7 +319,6 @@ int dump_string_pool(void)
return (k - STRING_OFFSET);
}
-@ @c
int undump_string_pool(void)
{
int j;
@@ -325,7 +346,6 @@ int undump_string_pool(void)
return str_ptr;
}
-@ @c
void init_string_pool_array(unsigned s)
{
string_pool = xmallocarray(lstring, s);
@@ -336,14 +356,16 @@ void init_string_pool_array(unsigned s)
string_pool[0].s[0] = '\0';
}
-@ To destroy an already made string, we say |flush_str|.
-@c
+/*tex
+
+ To destroy an already made string, we say |flush_str|.
+
+*/
+
void flush_str(str_number s)
{
-#if 0
- printf("Flushing a string: %s (s=%d,str_ptr=%d)\n", (char *)str_string(s), (int)s, (int)str_ptr);
-#endif
- if (s > STRING_OFFSET) { /* don't ever delete the null string */
+ 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));
diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.w b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c
index 7fe5ae1e18c..f0b057d40b1 100644
--- a/Build/source/texk/web2c/luatexdir/tex/texdeffont.w
+++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c
@@ -1,57 +1,73 @@
-% 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/>.
-
-@ @c
+/*
+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"
-@ 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.
+/*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.
+
+*/
-@c
int font_bytes;
void set_cur_font(internal_font_number f)
{
- int a = 0; /* never global */
+ int a = 0;
define(cur_font_loc, data_cmd, f);
}
-@ @c
+/*tex
+
+ This prints a scaled real, rounded to five digits.
+
+*/
+
static char *scaled_to_string(scaled s)
-{ /* prints scaled real, rounded to five digits */
+{
static char result[16];
int n, k;
- scaled delta; /* amount of allowable inaccuracy */
+ /*tex The amount of allowable inaccuracy: */
+ scaled delta;
k = 0;
if (s < 0) {
+ /*tex Only print the sign, if negative */
result[k++] = '-';
- s = -s; /* print the sign, if negative */
+ s = -s;
}
{
int l = 0;
char digs[8] = { 0 };
n = s / unity;
- /* process the integer part */
+ /*tex Process the integer part: */
do {
digs[l++] = (char) (n % 10);
n = n / 10;;
@@ -64,29 +80,37 @@ static char *scaled_to_string(scaled s)
s = 10 * (s % unity) + 5;
delta = 10;
do {
- if (delta > unity)
- s = s + 0100000 - 050000; /* round the last digit */
+ 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;
}
-@ @c
void tex_def_font(small_number a)
{
- pointer u; /* user's font identifier */
- internal_font_number f; /* runs through existing fonts */
- str_number t; /* name for the frozen font identifier */
- int old_setting; /* holds |selector| setting */
- scaled s = -1000; /* stated ``at'' size, or negative of scaled magnification */
- int natural_dir = -1; /* the natural direction of the font */
+ /*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;
+ /*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)
- open_log_file(); /* avoid confusing \.{texput} with the font name */
+ if (job_name == 0) {
+ /*tex Avoid confusing \.{texput} with the font name. */
+ open_log_file();
+ }
get_r_token();
u = cur_cs;
if (u >= null_cs)
@@ -99,7 +123,7 @@ void tex_def_font(small_number a)
eq_define(u, set_font_cmd, null_font);
}
scan_optional_equals();
- /* Get the next non-blank non-call token; */
+ /*tex Get the next non-blank non-call token. */
do {
get_x_token();
} while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
@@ -108,8 +132,10 @@ void tex_def_font(small_number a)
back_input();
scan_file_name();
if (cur_area != get_nullstr() || cur_ext != get_nullstr()) {
- /* Have to do some rescue-ing here, fonts only have a name,
- no area nor extension */
+ /*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()) {
@@ -134,26 +160,27 @@ void tex_def_font(small_number a)
token_show(def_ref);
selector = old_setting;
flush_list(def_ref);
- /* |str_room(1)|; *//* what did that do ? */
cur_name = make_string();
cur_ext = get_nullstr();
cur_area = get_nullstr();
}
- /* Scan the font size specification; */
- name_in_progress = true; /* this keeps |cur_name| from being changed */
+ /*tex
+ Scan the font size specification. The next variable keeps |cur_name| from
+ being changed
+ */
+ name_in_progress = true;
if (scan_keyword("at")) {
- /* Put the positive `at' size into |s| */
+ /*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",
+ 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));
+ snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt", scaled_to_string(s));
tex_error(err, errhelp);
s = 10 * unity;
}
@@ -162,27 +189,31 @@ void tex_def_font(small_number a)
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.",
+ 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);
+ 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];
cs_text(font_id_base + f) = t;
}
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..143e694e1ba
--- /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(fnam)) {
+ 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;
+ str_number fnam;
+ int callback_id;
+ boolean ret = false;
+ callback_id = callback_defined(find_write_file_callback);
+ if (callback_id > 0) {
+ fnam = 0;
+ test = run_callback(callback_id, "dS->s", n, fn, &fnam);
+ if ((test) && (fnam != 0) && (str_length(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, fn, FOPEN_W_MODE);
+ }
+ } 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;
+ str_number fnam;
+ int callback_id;
+ boolean ret = false;
+ callback_id = callback_defined(find_output_file_callback);
+ if (callback_id > 0) {
+ fnam = 0;
+ test = run_callback(callback_id, "S->s", fn, &fnam);
+ if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
+ ret = open_outfile(f, fn, FOPEN_WBIN_MODE);
+ }
+ } 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.w b/Build/source/texk/web2c/luatexdir/tex/texfileio.w
deleted file mode 100644
index a10e9ba63d0..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/texfileio.w
+++ /dev/null
@@ -1,1389 +0,0 @@
-% texfileio.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/>.
-
-@ @c
-
-
-#include "ptexlib.h"
-
-#include <string.h>
-#include <kpathsea/absolute.h>
-
-@ 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
-
-@c
-char *nameoffile;
-int namelength;
-
-
-@ 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.
-
-@c
-int *input_file_callback_id;
-int read_file_callback_id[17];
-
-@ 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.
-
-@c
-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);
- f_ptr = fopen(ftemp, "rb"); /* this code is used for input files only */
- if (f_ptr) {
- fclose(f_ptr);
- return ftemp;
- } else {
- free(ftemp);
-
- }
- }
- return NULL;
-}
-
-@ find an \.{\\input} or \.{\\read} file. |n| differentiates between those case.
-
-@c
-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")) {
- /* 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;
-}
-
-@ find other files types
-@c
-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")) {
- /* 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;
-}
-
-
-@ 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.
-
-@c
-#define openoutnameok(A) kpse_out_name_ok (A)
-#define openinnameok(A) kpse_in_name_ok (A)
-
-@ 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.
-
-@c
-boolean
-luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt,
- const_string fopen_mode, boolean must_exist)
-{
- string fname = NULL;
- /* We havent found anything yet. */
- *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);
- /* 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;
- }
- /* This fopen is not allowed to fail. */
- *f_ptr = xfopen(fname, fopen_mode);
- }
- if (*f_ptr) {
- recorder_record_input(fname);
- }
- return *f_ptr != NULL;
-}
-
-@ @c
-boolean luatex_open_output(FILE ** f_ptr, const char *fn,
- const_string fopen_mode)
-{
- char *fname;
- boolean absolute = kpse_absolute_p(fn, false);
-
- /* 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);
- }
-
- /* Is the filename openable as given? */
- *f_ptr = fopen(fname, fopen_mode);
-
- if (!*f_ptr) {
- /* 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;
-}
-
-
-@ @c
-boolean lua_a_open_in(alpha_file * f, char *fn, int n)
-{
- int k;
- char *fnam; /* string returned by find callback */
- int callback_id;
- boolean ret = true; /* return value */
- boolean file_ok = true; /* the status so far */
- 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 {
- file_ok = false; /* read failed */
- }
- } else { /* no read callback */
- if (openinnameok(fnam)) {
- ret =
- open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE,
- (n == 0 ? true : false));
- } else {
- file_ok = false; /* open failed */
- }
- }
- if (!file_ok) {
- ret = false;
- }
- return ret;
-}
-
-
-@ @c
-boolean lua_a_open_out(alpha_file * f, char *fn, int n)
-{
- boolean test;
- str_number fnam;
- int callback_id;
- boolean ret = false;
- callback_id = callback_defined(find_write_file_callback);
- if (callback_id > 0) {
- fnam = 0;
- test = run_callback(callback_id, "dS->s", n, fn, &fnam);
- if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
- /* 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, fn, FOPEN_W_MODE);
- }
- } else {
- if (openoutnameok(fn)) {
- if (n > 0 && selector != term_only) {
- /* 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;
-}
-
-
-@ @c
-boolean lua_b_open_out(alpha_file * f, char *fn)
-{
- boolean test;
- str_number fnam;
- int callback_id;
- boolean ret = false;
- callback_id = callback_defined(find_output_file_callback);
- if (callback_id > 0) {
- fnam = 0;
- test = run_callback(callback_id, "S->s", fn, &fnam);
- if ((test) && (fnam != 0) && (str_length(fnam) > 0)) {
- ret = open_outfile(f, fn, FOPEN_WBIN_MODE);
- }
- } else {
- if (openoutnameok(fn)) {
- ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE);
- }
- }
- return ret;
-}
-
-@ @c
-void lua_a_close_in(alpha_file f, int n)
-{ /* close a text file */
- 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);
- }
-}
-
-@ @c
-void lua_a_close_out(alpha_file f)
-{ /* close a text file */
- close_file_or_pipe(f);
-}
-
-
-@ 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.
-
-@c
-packed_ASCII_code *buffer; /* lines of characters being read */
-int first; /* the first unused position in |buffer| */
-int last; /* end of the line just input to |buffer| */
-int max_buf_stack; /* largest index used in |buffer| */
-
-
-@ 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.
-@^inner loop@>
-
-@c
-boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln)
-{
- boolean lua_result;
- int last_ptr;
- int callback_id;
- (void) bypass_eoln; /* todo: variable can be removed */
- 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) {
- /* 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;
-}
-
-
-@ We need a special routine to read the first line of \TeX\ input from
-the user's terminal. This line is different because it is read before we
-have opened the transcript file; there is sort of a ``chicken and
-egg'' problem here. If the user types `\.{\\input paper}' on the first
-line, or if some macro invoked by that line does such an \.{\\input},
-the transcript file will be named `\.{paper.log}'; but if no \.{\\input}
-commands are performed during the first line of terminal input, the transcript
-file will acquire its default name `\.{texput.log}'. (The transcript file
-will not contain error messages generated by the first line before the
-first \.{\\input} command.)
-@.texput@>
-
-The first line is 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:
-
-\yskip\textindent{1)}It should open file |term_in| for input from the
- terminal. (The file |term_out| will already be open for output to the
- terminal.)
-
-\textindent{2)}If the user has given a command line, this line should be
- considered the first line of terminal input. Otherwise the
- user should be prompted with `\.{**}', and the first line of input
- should be whatever is typed in response.
-
-\textindent{3)}The first line of input, which might or might not be a
- command line, should appear in locations |first| to |last-1| of the
- |buffer| array.
-
-\textindent{4)}The global variable |loc| should be set so that the
- character to be read next by \TeX\ is in |buffer[loc]|. This
- character should not be blank, and we should have |loc<last|.
-
-\yskip\noindent(It may be necessary to prompt the user several times
-before a non-blank line comes in. The prompt is `\.{**}' instead of the
-later `\.*' because the meaning is slightly different: `\.{\\input}' need
-not be typed immediately after~`\.{**}'.)
-
-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|.
-@^system dependencies@>
-
-@c
-boolean init_terminal(void)
-{ /* 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)) {
- /* 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);
- if (iloc < last) {
- return true; /* return unless the line was all blank */
- }
- fputs("Please type the name of your input file.\n", term_out);
- }
-}
-
-
-@ 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.
-
-@c
-void term_input(void)
-{ /* gets a line from the terminal */
- int k; /* index into |buffer| */
- update_terminal(); /* now the user sees the prompt for sure */
- if (!input_ln(term_in, true))
- fatal_error("End of file on the terminal!");
- term_offset = 0; /* the user's line ended with \.{<return>} */
- decr(selector); /* prepare to echo the input */
- if (last != first) {
- for (k = first; k <= last - 1; k++)
- print_char(buffer[k]);
- }
- print_ln();
- incr(selector); /* restore previous status */
-}
-
-
-@ It's time now to fret about file names. Besides the fact that different
-operating systems treat files in different ways, we must cope with the
-fact that completely different naming conventions are used by different
-groups of people. The following programs show what is required for one
-particular operating system; similar routines for other systems are not
-difficult to devise.
-@^fingers@>
-@^system dependencies@>
-
-\TeX\ assumes that a file name has three parts: the name proper; its
-``extension''; and a ``file area'' where it is found in an external file
-system. The extension of an input file or a write file is assumed to be
-`\.{.tex}' unless otherwise specified; it is `\.{.log}' on the
-transcript file that records each run of \TeX; it is `\.{.tfm}' on the font
-metric files that describe characters in the fonts \TeX\ uses; it is
-`\.{.dvi}' on the output files that specify typesetting information; and it
-is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX.
-The file area can be arbitrary on input files, but files are usually
-output to the user's current area. If an input file cannot be
-found on the specified area, \TeX\ will look for it on a special system
-area; this special area is intended for commonly used input files like
-\.{webmac.tex}.
-
-Simple uses of \TeX\ refer only to file names that have no explicit
-extension or area. For example, a person usually says `\.{\\input} \.{paper}'
-or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input}
-\.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file
-names are best, because they make the \TeX\ source files portable;
-whenever a file name consists entirely of letters and digits, it should be
-treated in the same way by all implementations of \TeX. However, users
-need the ability to refer to other files in their environment, especially
-when responding to error messages concerning unopenable files; therefore
-we want to let them use the syntax that appears in their favorite
-operating system.
-
-The following procedures don't allow spaces to be part of
-file names; but some users seem to like names that are spaced-out.
-System-dependent changes to allow such things should probably
-be made with reluctance, and only when an entire file name that
-includes spaces is ``quoted'' somehow.
-
-Here are the global values that file names will be scanned into.
-
-@c
-str_number cur_name; /* name of file just scanned */
-str_number cur_area; /* file area just scanned, or \.{""} */
-str_number cur_ext; /* file extension just scanned, or \.{""} */
-
-
-@ 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:
-
-@c
-pool_pointer area_delimiter; /* the most recent `\./', if any */
-pool_pointer ext_delimiter; /* the relevant `\..', if any */
-
-
-@ Input files that can't be found in the user's area may appear in a standard
-system area called |TEX_area|. Font metric files whose areas are not given
-explicitly are assumed to appear in a standard system area called
-|TEX_font_area|. $\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.
-
-@c
-#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)
-{
- ASCII_code c; /* character being packed */
- unsigned char *j; /* index into |str_pool| */
- int k = 0; /* number of positions filled in |fn| */
- 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;
-}
-
-
-
-@ 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.
-@^system dependencies@>
-
-Under {\mc 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.
-
-@c
-char *TEX_format_default;
-
-
-@ 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]<>" "|.
-
-@c
-char *open_fmt_file(void)
-{
- int j; /* the first space after the format file name */
- 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();
- }
- /* 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;
-}
-
-
-@ The global variable |name_in_progress| is used to prevent recursive
-use of |scan_file_name|, since the |begin_name| and other procedures
-communicate via global variables. Recursion would arise only by
-devious tricks like `\.{\\input\\input f}'; such attempts at sabotage
-must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input}
-@^recursion@>
-from being initiated when a font size specification is being scanned.
-
-Another global variable, |job_name|, contains the file name that was first
-\.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}'
-and `\.{.fmt}' in the names of \TeX's output files.
-
-@c
-boolean name_in_progress; /* is a file name being scanned? */
-str_number job_name; /* principal file name */
-boolean log_opened_global; /* has the transcript file been opened? */
-
-
-@ Initially |job_name=0|; it becomes nonzero as soon as the true name is known.
-We have |job_name=0| if and only if the `\.{log}' file has not been opened,
-except of course for a short time just after |job_name| has become nonzero.
-
-@c
-unsigned char *texmf_log_name; /* full name of the log file */
-
-@ 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.
-
-@c
-void open_log_file(void)
-{
- int old_setting; /* previous |selector| setting */
- int k; /* index into |buffer| */
- int l; /* end of first input line */
- char *fn;
- old_setting = selector;
- if (job_name == 0)
- job_name = getjobname(maketexstring("texput")); /* TODO */
- fn = pack_job_name(".fls");
- recorder_change_filename(fn);
- fn = pack_job_name(".log");
- while (!lua_a_open_out(&log_file, fn, 0)) {
- /* 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) {
- /* Print the banner line, including current date and time */
- log_banner(luatex_version_string);
-
- input_stack[input_ptr] = cur_input; /* make sure bottom level is in memory */
- tprint_nl("**");
- l = input_stack[0].limit_field; /* last position of first line */
- if (buffer[l] == end_line_char_par)
- decr(l); /* TODO: multichar endlinechar */
- for (k = 1; k <= l; k++)
- print_char(buffer[k]);
- print_ln(); /* now the transcript file contains the first line of input */
- }
- flush_loggable_info(); /* should be done always */
- selector = old_setting + 2; /* |log_only| or |term_and_log| */
-}
-
-@ This function is needed by synctex to make its log appear in the right
-spot when |output_directory| is set.
-
-@c
-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);
- }
-}
-
-@ Synctex uses this to get the anchored path of an input file.
-
-@c
-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;
-}
-
-
-@ Let's turn now to the procedure that is used to initiate file reading
-when an `\.{\\input}' command is being processed.
-
-@c
-void start_input(void)
-{ /* \TeX\ will \.{\\input} something */
- 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) {
- scan_file_name(); /* set |cur_name| to desired file name */
- } else {
- scan_file_name_toks();
- }
- fn = pack_file_name(cur_name, cur_area, cur_ext);
- while (1) {
- begin_file_reading(); /* set up |cur_file| and new level of input */
- if (lua_a_open_in(&cur_file, fn, 0))
- break;
- end_file_reading(); /* remove the level that didn't work */
- fn = prompt_file_name("input file name", "");
- }
- iname = maketexstring(fullnameoffile);
- /* 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);
- /* 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();
- }
- /* |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;
- /* Prepare new file {\sl Sync\TeX} information */
- if (! synctex_get_no_files()) {
- synctexstartinput(); /* Give control to the {\sl Sync\TeX} controller */
- }
- /* 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;
-}
-
-@ 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.
-
-@c
-static gzFile gz_fmtfile = NULL;
-
-@ 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.
-
-@c
-#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE)
-
-/* 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)
-
-/* 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);
- /* Since `size' does not change, we can write a while loop for each
- case, and avoid testing `size' for each time. */
- switch (size) {
- /* 16-byte items happen on the DEC Alpha machine when we are not
- doing sharable memory dumps. */
- case 16:
- 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:
- /* Nothing to do. */
- break;
-
- default:
- FATAL1("Can't swap a %d-byte item for (un)dumping", size);
- }
- memcpy(pp,q,total);
- xfree(q);
-}
-#endif /* not WORDS_BIGENDIAN and not NO_DUMP_SHARE */
-
-@ That second swap is to make sure following calls don't get
-confused in the case of |dump_things|.
-
-@c
-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
-}
-
-@ @c
-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
-}
-
-@ @c
-#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;
-}
-
-@ @c
-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;
-}
-
-@ @c
-void zwclose(FILE * f)
-{
- (void) f;
- gzclose(gz_fmtfile);
-}
-
-@ create the dvi or pdf file
-@c
-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;
-}
-
-
-@ the caller should set |tfm_buffer=NULL| and |tfm_size=0|
-@c
-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;
- }
- } /* seek failed, or zero-sized file */
- return 0;
-}
-
-@ 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.
-
-@c
-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
-
- /* If restrictedshell == 0, 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;
-}
-
-@ piped I/O
-
-
-@ The code that implements |popen()| needs an array for tracking
- possible pipe file pointers, because these need to be
- closed using |pclose()|.
-
-@c
-#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; /* iterator */
-
- /* 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 == '|') {
- /* 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; /* iterator */
-
- /* 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 == '|') {
- /* the user requested a pipe */
- fname = (string) xmalloc((unsigned) (strlen(fn) + 1));
- strcpy(fname, fn);
- if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) {
- /* mp and mf currently do not use this code, but it
- is better to be prepared */
- 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; /* iterator */
-
- if (shellenabledp) {
- for (i = 0; i <= 15; i++) {
- /* 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/texmath.w b/Build/source/texk/web2c/luatexdir/tex/texmath.c
index 6d4b87a3864..69e58b0ea77 100644
--- a/Build/source/texk/web2c/luatexdir/tex/texmath.w
+++ b/Build/source/texk/web2c/luatexdir/tex/texmath.c
@@ -1,46 +1,43 @@
-% texmath.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/>.
-
-@ @c
+/*
+
+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"
-@ @c
#define mode mode_par
#define tail tail_par
#define head head_par
#define dir_save dirs_par
-/*
-
- \mathdisplayskipmode
+/*tex
- tex normally always inserts before and only after when larger than zero
+ Concerning display skips, \TEX\ normally always inserts before and only after
+ when larger than zero. THis can ow be controlled with |\mathdisplayskipmode|:
- 0 = normal tex
- 1 = always
- 2 = non-zero
- 3 = ignore
+ \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
*/
-@ TODO: not sure if this is the right order
-@c
#define back_error(A,B) do { \
OK_to_interrupt=false; \
back_input(); \
@@ -48,90 +45,107 @@
tex_error(A,B); \
} while (0)
-@ @c
int scan_math(pointer, int);
int scan_math_style(pointer, int);
pointer fin_mlist(pointer);
-@ 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|.
-
-\yskip\hang|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.
-
-\yskip\hang|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.)
-
-\yskip\hang|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.
-
-\yskip\hang|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.
-
-\yskip\noindent 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).
-
-@c
+/*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();
@@ -141,10 +155,13 @@ static void unsave_math(void)
text_dir_ptr = saved_value(0);
}
-@ Sometimes it is necessary to destroy an mlist. The following
-subroutine empties the current list, assuming that |abs(mode)=mmode|.
+/*tex
+
+ Sometimes it is necessary to destroy an mlist. The following subroutine
+ empties the current list, assuming that |abs(mode)=mmode|.
+
+*/
-@c
void flush_math(void)
{
flush_node_list(vlink(head));
@@ -154,15 +171,13 @@ void flush_math(void)
incompleat_noad_par = null;
}
-@ Before we can do anything in math mode, we need fonts.
+/*tex Before we can do anything in math mode, we need fonts. */
-@c
#define MATHFONTSTACK 8
-#define MATHFONTDEFAULT 0 /* == nullfont */
+#define MATHFONTDEFAULT 0
static sa_tree math_fam_head = NULL;
-@ @c
int fam_fnt(int fam_id, int size_id)
{
int n = fam_id + (256 * size_id);
@@ -189,7 +204,6 @@ void def_fam_fnt(int fam_id, int size_id, int f, int lvl)
}
}
-@ @c
static void unsave_math_fam_data(int gl)
{
sa_stack_item st;
@@ -201,7 +215,7 @@ static void unsave_math_fam_data(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);
- /* now do a trace message, if requested */
+ /*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;
@@ -220,15 +234,13 @@ static void unsave_math_fam_data(int gl)
}
}
-@ and parameters
+/*tex Parameters */
-@c
#define MATHPARAMSTACK 8
#define MATHPARAMDEFAULT undefined_math_parameter
static sa_tree math_param_head = NULL;
-@ @c
void def_math_param(int param_id, int style_id, scaled value, int lvl)
{
int n = param_id + (256 * style_id);
@@ -254,7 +266,6 @@ scaled get_math_param(int param_id, int style_id)
return (scaled) get_sa_item(math_param_head, n).int_value;
}
-@ @c
static void unsave_math_param_data(int gl)
{
sa_stack_item st;
@@ -266,7 +277,7 @@ static void unsave_math_param_data(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);
- /* now do a trace message, if requested */
+ /*tex Do a trace message, if requested. */
if (tracing_restores_par > 1) {
int param_id = st.code % 256;
int style_id = st.code / 256;
@@ -285,17 +296,16 @@ static void unsave_math_param_data(int gl)
}
}
-@ saving and unsaving of both
+/*tex Saving and unsaving of both: */
-@c
void unsave_math_data(int gl)
{
unsave_math_fam_data(gl);
unsave_math_param_data(gl);
}
-@ Dumping and undumping
-@c
+/*tex Dumping and undumping: */
+
void dump_math_data(void)
{
sa_tree_item sa_value = { 0 };
@@ -317,7 +327,6 @@ void undump_math_data(void)
math_param_head = undump_sa_tree("mathparameters");
}
-@ @c
void initialize_math(void)
{
sa_tree_item sa_value = { 0 };
@@ -333,62 +342,63 @@ void initialize_math(void)
return;
}
-@ 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}.
-@:TeXbook}{\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
-
-@c
+/*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);
- /* all noad fields are zero after this */
+ /*tex All noad fields are zero after this. */
return p;
}
-@ @c
pointer new_sub_box(pointer curbox)
{
pointer p, q;
@@ -399,40 +409,41 @@ pointer new_sub_box(pointer curbox)
return p;
}
-@ A few more kinds of noads will complete the set: An |under_noad| has its
-nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
-an accent over its nucleus; the accent character appears as
-|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.
-@:TeXbook}{\sl The \TeX book@>
-
-@c
+/*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",
@@ -482,93 +493,96 @@ const char *math_param_names[] = {
NULL
};
-@ @c
pointer new_style(small_number s)
-{ /* create a style node */
+{
m_style = s;
return new_node(style_node, s);
}
-@ 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.
+/*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.
+
+*/
-@c
static pointer new_choice(void)
-{ /* create a choice node */
- return new_node(choice_node, 0); /* the |subtype| is not used */
-}
-
-@ 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\}\}\}\}\$}'.
-
-@c
-void display_normal_noad(pointer p); /* forward */
-void display_fence_noad(pointer p); /* forward */
-void display_fraction_noad(pointer p); /* forward */
+{
+ 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;
+ 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;
}
}
-@ Here are some simple routines used in the display of noads.
+/*tex Here are some simple routines used in the display of noads. */
-@c
static void print_fam_and_char(pointer p)
-{ /* prints family and character */
+{
tprint_esc("fam");
print_int(math_fam(p));
print_char(' ');
print(math_character(p));
}
-@ @c
static void print_delimiter(pointer p)
{
int a;
@@ -598,36 +612,38 @@ static void print_delimiter(pointer p)
tprint(" ");
}
if (small_fam(p) < 0) {
- print_int(-1); /* this should never happen */
- } else if (small_fam(p) < 16 && large_fam(p) < 16 &&
- small_char(p) < 256 && large_char(p) < 256) {
- /* traditional tex style */
+ /*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_hex(a);
- } else if ((large_fam(p) == 0 && large_char(p) == 0) ||
- small_char(p) > 65535 || large_char(p) > 65535) {
- /* modern xetex/luatex style */
- print_hex(small_fam(p));
- print_hex(small_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));
}
}
-@ 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).
-@^recursion@>
+/*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).
+
+*/
-@c
static void print_subsidiary_data(pointer p, ASCII_code c)
-{ /* display a noad field */
+{
if ((int) cur_length >= depth_threshold) {
if (p != null)
tprint(" []");
} else {
- append_char(c); /* include |c| in the recursion history */
+ /*tex Include |c| in the recursion history. */
+ append_char(c);
if (p != null) {
switch (type(p)) {
case math_char_node:
@@ -649,58 +665,58 @@ static void print_subsidiary_data(pointer p, ASCII_code c)
break;
}
}
- flush_char(); /* remove |c| from the recursion history */
+ /*tex Remove |c| from the recursion history. */
+ flush_char();
}
}
-@ @c
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;
+ 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:
@@ -810,7 +826,6 @@ void display_normal_noad(pointer p)
print_subsidiary_data(subscr(p), '_');
}
-@ @c
void display_fence_noad(pointer p)
{
if (subtype(p) == right_noad_side)
@@ -822,7 +837,6 @@ void display_fence_noad(pointer p)
print_delimiter(delimiter(p));
}
-@ @c
void display_fraction_noad(pointer p)
{
tprint_esc("fraction, thickness ");
@@ -831,18 +845,14 @@ void display_fraction_noad(pointer p)
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))) {
+ ((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))) {
+ ((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));
}
@@ -850,16 +860,19 @@ void display_fraction_noad(pointer p)
print_subsidiary_data(denominator(p), '/');
}
-@ 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.
+/*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|.
+ 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|.
+
+*/
-@c
static void new_save_level_math(group_code c)
{
set_saved_record(0, saved_textdir, 0, text_dir_ptr);
@@ -871,7 +884,6 @@ static void new_save_level_math(group_code c)
eq_word_define(int_base + text_direction_code, math_direction_par);
}
-@ @c
static void push_math(group_code c, int mstyle)
{
if (math_direction_par != text_direction_par)
@@ -883,7 +895,6 @@ static void push_math(group_code c, int mstyle)
new_save_level_math(c);
}
-@ @c
static void enter_ordinary_math(void)
{
push_math(math_shift_group, text_style);
@@ -892,19 +903,21 @@ static void enter_ordinary_math(void)
begin_token_list(every_math_par, every_math_text);
}
-@ @c
void enter_display_math(void);
-@ 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.
+/*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.
+
+*/
-@c
void init_math(void)
{
if (cur_cmd == math_shift_cmd) {
- get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */
+ /*tex |get_x_token| would fail on \.{\\ifmmode}\thinspace! */
+ get_token();
if ((cur_cmd == math_shift_cmd) && (mode > 0)) {
enter_display_math();
} else {
@@ -920,15 +933,17 @@ void init_math(void)
}
}
-@ 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.
+/*tex
-@ 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.
+ 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.
+
+*/
-@c
void start_eq_no(void)
{
set_saved_record(0, saved_eqno, 0, cur_chr);
@@ -936,21 +951,24 @@ void start_eq_no(void)
enter_ordinary_math();
}
-@ 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.
+/*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.
+ Here is the code that handles case (1); the other cases are not quite as
+ trivial, so we shall consider them later.
+
+*/
-@c
void math_left_brace(void)
{
pointer q;
@@ -961,18 +979,20 @@ void math_left_brace(void)
(void) scan_math(nucleus(tail), m_style);
}
-@ 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.
+/*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.
+ 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.
+
+*/
-@c
static boolean math_and_text_reversed_p(void)
{
int i = save_ptr - 1;
@@ -989,26 +1009,43 @@ static boolean math_and_text_reversed_p(void)
return false;
}
-@ 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|.
+/*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|.
+
+*/
-@c
void enter_display_math(void)
{
- scaled w; /* new or partial |pre_display_size| */
- scaled l; /* new |display_width| */
- scaled s; /* new |display_indent| */
+ /*tex new or partial |pre_display_size| */
+ scaled w;
+ /*tex new |display_width| */
+ scaled l;
+ /*tex new |display_indent| */
+ scaled s;
pointer p;
- int n; /* scope of paragraph shape specification */
- if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */
- (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */
+ /*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) {
- /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if
- there is another display immediately following, we have to get rid
- of that node */
+ /*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();
@@ -1017,9 +1054,13 @@ void enter_display_math(void)
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);
}
- /* 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|. */
+ /*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);
@@ -1042,7 +1083,6 @@ void enter_display_math(void)
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);
@@ -1058,12 +1098,15 @@ void enter_display_math(void)
}
}
-@ 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}).
+/*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}).
+
+*/
-@c
static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
{
const char *hlp[] = {
@@ -1072,9 +1115,14 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
};
delcodeval d;
int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0;
- if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */
+ if (extcode == tex_mathcode) {
+ /*tex
+
+ \.{\\delcode}, this is the easiest
+
+ */
scan_int();
- /* "MFCCFCC or "FCCFCC */
+ /*tex "MFCCFCC or "FCCFCC */
if (doclass) {
mcls = (cur_val / 0x1000000);
cur_val = (cur_val & 0xFFFFFF);
@@ -1087,8 +1135,12 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
mschr = (cur_val % 0x100000) / 0x1000;
mlfam = (cur_val & 0xFFF) / 0x100;
mlchr = (cur_val % 0x100);
- } else if (extcode == umath_mathcode) { /* \.{\\Udelcode} */
- /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */
+ } 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;
@@ -1104,11 +1156,13 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
}
mlfam = 0;
mlchr = 0;
- } else if (extcode == umathnum_mathcode) { /* \.{\\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
- */
+ } 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");
}
@@ -1123,7 +1177,7 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
mlfam = 0;
mlchr = 0;
} else {
- /* something's gone wrong */
+ /*tex Something's gone wrong! */
confusion("unknown_extcode");
}
d.class_value = mcls;
@@ -1134,7 +1188,6 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
return d;
}
-@ @c
void scan_extdef_del_code(int level, int extcode)
{
delcodeval d;
@@ -1144,11 +1197,9 @@ void scan_extdef_del_code(int level, int extcode)
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));
+ d.large_family_value, d.large_character_value, (quarterword) (level));
}
-@ @c
mathcodeval scan_mathchar(int extcode)
{
char errstr[255] = { 0 };
@@ -1158,15 +1209,11 @@ mathcodeval scan_mathchar(int extcode)
};
mathcodeval d;
int mcls = 0, mfam = 0, mchr = 0;
- if (extcode == tex_mathcode) { /* \.{\\mathcode} */
- /* "TFCC */
+ if (extcode == tex_mathcode) {
+ /*tex \.{\\mathcode}: "TFCC */
scan_int();
if (cur_val > 0x8000) {
- /*
- tex_error("Invalid math code", hlp);
- cur_val = 0;
- */
- /* needed for latex: fallback to umathnum_mathcode */
+ /*tex Needed for latex: fallback to umathnum_mathcode. */
mfam = (cur_val / 0x200000) & 0x7FF;
mcls = mfam % 0x08;
mfam = mfam / 0x08;
@@ -1188,7 +1235,7 @@ mathcodeval scan_mathchar(int extcode)
mchr = (cur_val % 0x100);
}
} else if (extcode == umath_mathcode) {
- /* <0-0x7> <0-0xFF> <0-0x10FFFF> */
+ /*tex <0-0x7> <0-0xFF> <0-0x10FFFF> */
scan_int();
mcls = cur_val;
scan_int();
@@ -1202,11 +1249,14 @@ mathcodeval scan_mathchar(int extcode)
mcls = 0;
}
} else if (extcode == umathnum_mathcode) {
- /* "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 */
+ /*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;
@@ -1219,7 +1269,7 @@ mathcodeval scan_mathchar(int extcode)
mchr = 0;
}
} else {
- /* something's gone wrong */
+ /*tex Something's gone wrong. */
confusion("unknown_extcode");
}
d.class_value = mcls;
@@ -1228,7 +1278,6 @@ mathcodeval scan_mathchar(int extcode)
return d;
}
-@ @c
void scan_extdef_math_code(int level, int extcode)
{
mathcodeval d;
@@ -1237,12 +1286,11 @@ void scan_extdef_math_code(int level, int extcode)
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));
+ set_math_code(p, d.class_value, d.family_value, d.character_value, (quarterword) (level));
}
-@ this reads in a delcode when actually a mathcode is needed
-@c
+/*tex This reads in a delcode when actually a mathcode is needed. */
+
mathcodeval scan_delimiter_as_mathchar(int extcode)
{
delcodeval dval;
@@ -1254,14 +1302,17 @@ mathcodeval scan_delimiter_as_mathchar(int extcode)
return mval;
}
-@ 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|.
+/*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|.
+
+*/
-@c
#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)
@@ -1277,7 +1328,6 @@ int scan_math_style(pointer p, int mstyle)
int scan_math(pointer p, int mstyle)
{
- /* label restart,reswitch,exit; */
mathcodeval mval = { 0, 0, 0 };
assert(p != null);
RESTART:
@@ -1289,7 +1339,7 @@ int scan_math(pointer p, int mstyle)
case char_given_cmd:
mval = get_math_code(cur_chr);
if (mval.class_value == 8) {
- /* An active character that is an |outer_call| is allowed here */
+ /*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);
@@ -1329,8 +1379,12 @@ int scan_math(pointer p, int mstyle)
confusion("scan_math");
break;
default:
- /* The pointer |p| is placed on |save_stack| while a complex subformula
- is being scanned. */
+ /*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);
@@ -1347,12 +1401,15 @@ int scan_math(pointer p, int mstyle)
return 0;
}
-@ 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.
+/*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.
+
+*/
-@c
#define math_class_to_type(target,source) \
switch (source) { \
case 0: target = ord_noad_type; break; \
@@ -1366,9 +1423,9 @@ nothing is appended.
void set_math_char(mathcodeval mval)
{
- pointer p; /* the new noad */
+ pointer p;
if (mval.class_value == 8) {
- /* An active character that is an |outer_call| is allowed here */
+ /*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);
@@ -1393,17 +1450,20 @@ void set_math_char(mathcodeval mval)
}
}
-@ 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.
+/*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.
+
+*/
-@c
void math_char_in_text(mathcodeval mval)
{
- pointer p; /* the new node */
+ pointer p;
if (mval.class_value == 8) {
- /* An active character that is an |outer_call| is allowed here */
+ /*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);
@@ -1416,7 +1476,6 @@ void math_char_in_text(mathcodeval mval)
}
}
-@ @c
void math_math_comp(void)
{
pointer q;
@@ -1430,7 +1489,6 @@ void math_math_comp(void)
(void) scan_math(nucleus(tail), m_style);
}
-@ @c
void math_limit_switch(void)
{
const char *hlp[] = {
@@ -1449,18 +1507,23 @@ void math_limit_switch(void)
tex_error("Limit controls must follow a math operator", hlp);
}
-@ 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.
+/*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.
+
+*/
-@c
static void scan_delimiter(pointer p, int r)
{
delcodeval dval = { 0, 0, 0, 0, 0 };
- if (r == tex_mathcode) { /* \.{\\radical} */
+ if (r == tex_mathcode) {
+ /*tex \.{\\radical} */
dval = do_scan_extdef_del_code(tex_mathcode, true);
- } else if (r == umath_mathcode) { /* \.{\\Uradical} */
+ } 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();
@@ -1470,12 +1533,15 @@ static void scan_delimiter(pointer p, int r)
dval = get_del_code(cur_chr);
break;
case delim_num_cmd:
- if (cur_chr == 0) /* \.{\\delimiter} */
+ if (cur_chr == 0) {
+ /*tex \.{\\delimiter} */
dval = do_scan_extdef_del_code(tex_mathcode, true);
- else if (cur_chr == 1) /* \.{\\Udelimiter} */
+ } else if (cur_chr == 1) {
+ /*tex \.{\\Udelimiter} */
dval = do_scan_extdef_del_code(umath_mathcode, true);
- else
+ } else {
confusion("scan_delimiter1");
+ }
break;
default:
dval.small_family_value = -1;
@@ -1510,7 +1576,6 @@ static void scan_delimiter(pointer p, int r)
return;
}
-@ @c
void math_radical(void)
{
halfword q;
@@ -1534,31 +1599,44 @@ void math_radical(void)
}
}
radicaloptions(tail) = options;
- if (chr_code == 0) /* \.{\\radical} */
+ if (chr_code == 0)
+ /*tex \.{\\radical} */
scan_delimiter(left_delimiter(tail), tex_mathcode);
- else if (chr_code == 1) /* \.{\\Uradical} */
+ else if (chr_code == 1)
+ /*tex \.{\\Uradical} */
scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 2) /* \.{\\Uroot} */
+ else if (chr_code == 2)
+ /*tex \.{\\Uroot} */
scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 3) /* \.{\\Uunderdelimiter} */
+ else if (chr_code == 3)
+ /*tex \.{\\Uunderdelimiter} */
scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 4) /* \.{\\Uoverdelimiter} */
+ else if (chr_code == 4)
+ /*tex \.{\\Uoverdelimiter} */
scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 5) /* \.{\\Udelimiterunder} */
+ else if (chr_code == 5)
+ /*tex \.{\\Udelimiterunder} */
scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 6) /* \.{\\Udelimiterover} */
+ else if (chr_code == 6)
+ /*tex \.{\\Udelimiterover} */
scan_delimiter(left_delimiter(tail), umath_mathcode);
- else if (chr_code == 7) /* \.{\\Uhextensible} */
+ else if (chr_code == 7)
+ /*tex \.{\\Uhextensible} */
scan_delimiter(left_delimiter(tail), umath_mathcode);
else
confusion("math_radical");
if (chr_code == 7) {
- q = new_node(sub_box_node, 0); /* type will change */
+ /*tex type will change */
+ q = new_node(sub_box_node, 0);
nucleus(tail) = q;
return;
} else if (chr_code == 2) {
- /* the trick with the |vlink(q)| is used by |scan_math|
- to decide whether it needs to go on */
+ /*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;
@@ -1575,7 +1653,6 @@ void math_radical(void)
}
}
-@ @c
void math_ac(void)
{
halfword q;
@@ -1591,15 +1668,17 @@ void math_ac(void)
tex_error("Please use \\mathaccent for accents in math mode", hlp);
}
tail_append(new_node(accent_noad, 0));
- if (cur_chr == 0) { /* \.{\\mathaccent} */
+ if (cur_chr == 0) {
+ /*tex \.{\\mathaccent} */
t = scan_mathchar(tex_mathcode);
- } else if (cur_chr == 1) { /* \.{\\Umathaccent} */
+ } else if (cur_chr == 1) {
+ /*tex \.{\\Umathaccent} */
if (scan_keyword("fixed")) {
- /* top */
+ /*tex top */
subtype(tail) = 1;
t = scan_mathchar(umath_mathcode);
} else if (scan_keyword("both")) {
- /* top bottom */
+ /*tex top bottom */
if (scan_keyword("fixed")) {
subtype(tail) = 1;
}
@@ -1609,13 +1688,13 @@ void math_ac(void)
}
b = scan_mathchar(umath_mathcode);
} else if (scan_keyword("bottom")) {
- /* bottom */
+ /*tex bottom */
if (scan_keyword("fixed")) {
subtype(tail) = 2;
}
b = scan_mathchar(umath_mathcode);
} else if (scan_keyword("top")) {
- /* top */
+ /*tex top */
if (scan_keyword("fixed")) {
subtype(tail) = 1;
}
@@ -1627,7 +1706,7 @@ void math_ac(void)
}
o = scan_mathchar(umath_mathcode);
} else {
- /* top */
+ /*tex top */
t = scan_mathchar(umath_mathcode);
}
if (scan_keyword("fraction")) {
@@ -1669,7 +1748,6 @@ void math_ac(void)
(void) scan_math(nucleus(tail), cramped_style(m_style));
}
-@ @c
pointer math_vcenter_group(pointer p)
{
pointer q, r;
@@ -1681,10 +1759,13 @@ pointer math_vcenter_group(pointer p)
return q;
}
-@ The routine that scans the four mlists of a \.{\\mathchoice} is very
-much like the routine that builds discretionary nodes.
+/*tex
+
+ The routine that scans the four mlists of a \.{\\mathchoice} is very much
+ like the routine that builds discretionary nodes.
+
+*/
-@c
void append_choices(void)
{
tail_append(new_choice());
@@ -1694,10 +1775,9 @@ void append_choices(void)
scan_left_brace();
}
-@ @c
void build_choices(void)
{
- pointer p; /* the current mlist */
+ pointer p;
int prev_style;
prev_style = m_style;
unsave_math();
@@ -1718,16 +1798,19 @@ void build_choices(void)
decr(save_ptr);
return;
break;
- } /* there are no other cases */
+ }
set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1));
push_math(math_choice_group, (prev_style + 2));
scan_left_brace();
}
-@ Subscripts and superscripts are attached to the previous nucleus by the
-action procedure called |sub_sup|.
+/*tex
+
+ Subscripts and superscripts are attached to the previous nucleus by the
+ action procedure called |sub_sup|.
+
+*/
-@c
static void do_sub_sup(int no)
{
pointer q;
@@ -1736,7 +1819,8 @@ static void do_sub_sup(int no)
q = new_node(sub_mlist_node, 0);
nucleus(tail) = q;
}
- if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */
+ 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
@@ -1781,19 +1865,21 @@ 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.
+ 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.
+
+*/
-@c
void math_fraction(void)
{
- halfword c; /* the type of generalized fraction we are scanning */
+ /*tex The type of generalized fraction we are scanning: */
+ halfword c;
pointer q;
halfword options = 0;
halfword temp_value;
@@ -1870,16 +1956,19 @@ void math_fraction(void)
}
}
-@ 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.
+/*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.
+
+*/
-@c
pointer fin_mlist(pointer p)
{
- pointer q; /* the mlist to return */
+ pointer q;
if (incompleat_noad_par != null) {
if (denominator(incompleat_noad_par) != null) {
type(denominator(incompleat_noad_par)) = sub_mlist_node;
@@ -1892,9 +1981,8 @@ pointer fin_mlist(pointer p)
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"); /* this can't happen */
+ 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;
@@ -1907,26 +1995,50 @@ pointer fin_mlist(pointer p)
return q;
}
-@ 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.
+/*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.
+
+*/
-@c
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) {
- if (vlink(p) == null) {
- if (type(p) == simple_noad && subtype(p) == ord_noad_type) {
- if (subscr(p) == null && supscr(p) == null) {
+ 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));
@@ -1941,24 +2053,21 @@ void close_math_group(pointer p)
node_attr(nucleus(p)) = null;
flush_node(p);
}
- } else if (type(p) == accent_noad) {
- if (saved_value(0) == nucleus(tail)) {
- if (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;
- }
- }
+ }
+ } 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;
}
}
}
@@ -1969,30 +2078,36 @@ void close_math_group(pointer p)
vlink(saved_value(0)) = null;
saved_value(0) = q;
(void) scan_math(saved_value(0), old_style);
- /* restart */
+ /*tex restart */
}
}
-@ 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}.
+/*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}.
+
+*/
-@c
void math_left_right(void)
{
- halfword t; /* |left_noad_side| .. |right_noad_side| */
- pointer p; /* new noad */
- pointer q; /* resulting mlist */
- pointer r; /* temporary */
+ /*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) {
- /* we have \Uleft \Uright \Umiddle */
+ /*tex we have \Uleft \Uright \Umiddle */
t = t - 10;
while (1) {
if (scan_keyword("height")) {
@@ -2015,7 +2130,6 @@ void math_left_right(void)
}
}
}
-
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);
@@ -2041,14 +2155,12 @@ void math_left_right(void)
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());
@@ -2058,7 +2170,6 @@ void math_left_right(void)
math_list(nucleus(tail)) = p;
return ;
}
-
if (t == left_noad_side) {
q = p;
} else {
@@ -2080,10 +2191,13 @@ void math_left_right(void)
}
}
-@ \TeX\ gets to the following part of the program when
-the first `\.\$' ending a display has been scanned.
+/*tex
+
+ \TeX\ gets to the following part of the program when the first `\.\$' ending
+ a display has been scanned.
+
+*/
-@c
static void check_second_math_shift(void)
{
get_x_token();
@@ -2119,7 +2233,6 @@ static void check_inline_math_end(void)
}
}
-@ @c
static void resume_after_display(void)
{
if (cur_group != math_shift_group)
@@ -2129,7 +2242,7 @@ static void resume_after_display(void)
push_nest();
mode = hmode;
space_factor_par = 1000;
- /* this needs to be intercepted in the display math start ! */
+ /*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)
@@ -2140,30 +2253,40 @@ static void resume_after_display(void)
}
}
-@ The fussiest part of math mode processing occurs when a displayed formula is
-being centered and placed with an optional equation number.
+/*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).
+ At this time we are in vertical mode (or internal vertical mode).
- |p| points to the mlist for the formula.
- |a| is either |null| or it points to a box containing the equation number.
- |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box).
+ \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
+
+*/
-@c
#define inject_display_skip_before(g) \
if (g > 0) { \
switch (display_skip_mode_par) { \
- case 0 : /* normal tex | always */ \
- case 1 : /* always */ \
+ case 0 : \
+ /*tex normal tex | always */ \
+ case 1 : \
+ /*tex always */ \
tail_append(new_param_glue(g)); \
break; \
- case 2 : /* non-zero */ \
+ case 2 : \
+ /*tex non-zero */ \
if (! glue_is_zero(glue_par(g))) \
tail_append(new_param_glue(g)); \
break; \
- case 3: /* ignore */ \
+ case 3: \
+ /*tex ignore */ \
break; \
- default: /* > 3 reserved for future use */ \
+ default: \
+ /*tex > 3 reserved for future use */ \
tail_append(new_param_glue(g)); \
break; \
} \
@@ -2172,17 +2295,22 @@ At this time we are in vertical mode (or internal vertical mode).
#define inject_display_skip_after(g) \
if (g > 0) { \
switch (display_skip_mode_par) { \
- case 0 : /* normal tex | always */ \
- case 1 : /* always */ \
+ case 0 : \
+ /*tex normal tex | always */ \
+ case 1 : \
+ /*tex always */ \
tail_append(new_param_glue(g)); \
break; \
- case 2 : /* non-zero */ \
+ case 2 : \
+ /*tex non-zero */ \
if (! glue_is_zero(glue_par(g))) \
tail_append(new_param_glue(g)); \
break; \
- case 3: /* ignore */ \
+ case 3: \
+ /*tex ignore */ \
break; \
- default: /* > 3 reserved for future use */ \
+ default: \
+ /*tex > 3 reserved for future use */ \
tail_append(new_param_glue(g)); \
break; \
} \
@@ -2190,18 +2318,30 @@ At this time we are in vertical mode (or internal vertical mode).
static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
{
- pointer eq_box; /* box containing the equation */
- scaled eq_w; /* width of the equation */
- scaled line_w; /* width of the line */
- scaled eqno_w; /* width of equation number */
- scaled eqno_w2; /* width of equation number plus space to separate from equation */
- scaled line_s; /* move the line right this much */
- scaled d; /* displacement of equation in the line */
- small_number g1, g2; /* glue parameter codes for before and after */
- pointer r,s; /* kern nodes used to position the display */
- pointer t; /* tail of adjustment list */
- pointer pre_t; /* tail of pre-adjustment list */
- boolean swap_dir; /* true if the math and surrounding text dirs are opposed */
+ /*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)
@@ -2209,7 +2349,7 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
adjust_tail = adjust_head;
pre_adjust_tail = pre_adjust_head;
eq_box = hpack(p, 0, additional, -1);
- subtype(eq_box) = equation_list; /* new */
+ subtype(eq_box) = equation_list;
build_attribute_list(eq_box);
p = list_ptr(eq_box);
t = adjust_tail;
@@ -2227,21 +2367,23 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
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; /* new */
- /* build_attribute_list(eqno_box); */ /* probably already set */
- }
+ subtype(eqno_box) = equation_number_list;
+ /*tex build_attribute_list(eqno_box); */
+ }
if (eq_w + eqno_w2 > line_w) {
- /* The user can force the equation number to go on a separate line
- by causing its width to be zero. */
+ /*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))) {
+ || (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; /* new */
+ subtype(eq_box) = equation_list;
build_attribute_list(eq_box);
} else {
eqno_w = 0;
@@ -2249,47 +2391,52 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
list_ptr(eq_box) = null;
flush_node(eq_box);
eq_box = hpack(p, line_w, exactly, -1);
- subtype(eq_box) = equation_list; /* new */
+ subtype(eq_box) = equation_list;
build_attribute_list(eq_box);
}
}
eq_w = width(eq_box);
}
- /* 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.
- */
+ /*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)) { /* too close */
+ 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) { /* not enough clearance */
+ 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.
- /* 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| */
+ \.{\\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;
@@ -2300,40 +2447,27 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
} 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) {
- /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 1\n");
-#endif
+ /*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 {
- /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 2\n");
-#endif
+ /*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) {
- /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 3\n");
-#endif
+ /*tex TLT + TLT + \leqno: (swap_dir=false, math_direction_par=TLT, l=true) */
s = new_kern(width(r) + eqno_w);
} else {
- /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 4\n");
-#endif
+ /*tex TRT + TRT + \leqno: (swap_dir=false, math_direction_par=TRT, l=true) */
s = new_kern(width(r));
}
try_couple_nodes(eqno_box,r);
@@ -2344,30 +2478,18 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
} else {
if (swap_dir) {
if (math_direction_par==dir_TLT) {
- /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 5\n");
-#endif
+ /*tex TRT + TLT + \leqno: (swap_dir=true, math_direction_par=TLT, l=false) */
} else {
- /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 6\n");
-#endif
+ /*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) {
- /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 7\n");
-#endif
+ /*tex TLT + TLT + \eqno: (swap_dir=false, math_direction_par=TLT, l=false) */
s = new_kern(d);
} else {
- /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */
-#ifdef DEBUG
- fprintf(stderr, "\nDEBUG: CASE 8\n");
-#endif
+ /*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);
@@ -2383,9 +2505,8 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
} else {
shift_amount(eq_box) = line_s + d;
}
-/* check for prev: */
+ /*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) { */
@@ -2395,15 +2516,14 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
append_to_vlist(eqno_box,lua_key_index(equation_number));
g2 = 0;
}
- if (t != adjust_head) { /* migrating material comes after equation number */
+ if (t != adjust_head) {
+ /*tex migrating material comes after equation number */
vlink(tail) = vlink(adjust_head);
- /* needs testing */
alink(adjust_tail) = alink(tail);
tail = t;
}
if (pre_t != pre_adjust_head) {
vlink(tail) = vlink(pre_adjust_head);
- /* needs testing */
alink(pre_adjust_tail) = alink(tail);
tail = pre_t;
}
@@ -2412,20 +2532,25 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
resume_after_display();
}
-@ @c
void after_math(void)
{
- int m; /* |mmode| or |-mmode| */
- pointer p; /* the formula */
- pointer a = null; /* box containing equation number */
- boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */
+ /*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;
- p = fin_mlist(null); /* this pops the nest */
+ /*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) { /* end of equation number */
+ if (mode == -m) {
+ /*tex end of equation number */
if (cur_cmd == math_shift_cmd) {
check_second_math_shift();
} else {
@@ -2435,7 +2560,8 @@ void after_math(void)
a = hpack(vlink(temp_head), 0, additional, -1);
build_attribute_list(a);
unsave_math();
- decr(save_ptr); /* now |cur_group=math_shift_group| */
+ /*tex now |cur_group=math_shift_group| */
+ decr(save_ptr);
assert(saved_type(0) == saved_eqno);
if (saved_value(0) == 1)
l = true;
@@ -2444,47 +2570,50 @@ void after_math(void)
}
if (m < 0) {
- /* 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.
- */
+ /*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));
- /* begin mathskip code */
+ /*tex begin mathskip code */
switch (math_skip_mode) {
case 0 :
- /* obey mathsurround when zero glue */
+ /*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 :
- /* always left */
+ /*tex always left */
case 3 :
- /* always both */
+ /*tex always both */
case 6 :
- /* only when skip */
+ /*tex only when skip */
copy_glue_values(tail,math_skip_par);
surround(tail) = 0;
break ;
case 2 :
- /* only right */
+ /*tex only right */
surround(tail) = 0;
break ;
case 4 :
- /* ignore, obey marthsurround */
+ /*tex ignore, obey marthsurround */
break ;
case 5:
- /* all spacing disabled */
+ /*tex all spacing disabled */
surround(tail) = 0;
break ;
}
- /* end mathskip code */
+ /*tex end mathskip code */
if (dir_math_save) {
tail_append(new_dir(math_direction_par));
}
@@ -2494,41 +2623,42 @@ void after_math(void)
tail = vlink(tail);
}
if (dir_math_save) {
- tail_append(new_dir(math_direction_par - dir_swap));
+ tail_append(new_dir(math_direction_par));
+ subtype(tail) = cancel_dir;
}
dir_math_save = false;
tail_append(new_math(math_surround_par, after));
- /* begin mathskip code */
+ /*tex begin mathskip code */
switch (math_skip_mode) {
case 0 :
- /* obey mathsurround when zero glue */
+ /*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 :
- /* always right */
+ /*tex always right */
case 3 :
- /* always both */
+ /*tex always both */
case 6 :
- /* only when skip */
+ /*tex only when skip */
copy_glue_values(tail,math_skip_par);
surround(tail) = 0;
break ;
case 1 :
- /* only left */
+ /*tex only left */
surround(tail) = 0;
break ;
case 4 :
- /* ignore, obey marthsurround */
+ /*tex ignore, obey marthsurround */
break ;
case 5:
- /* all spacing disabled */
+ /*tex all spacing disabled */
surround(tail) = 0;
break ;
}
- /* end mathskip code */
+ /*tex end mathskip code */
space_factor_par = 1000;
unsave_math();
} else {
@@ -2544,12 +2674,15 @@ void after_math(void)
}
}
-@ 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.
+/*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.
+
+*/
-@c
void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
{
do_assignments();
@@ -2570,9 +2703,8 @@ void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
resume_after_display();
}
-@ Interface to \.{\\Umath} and \.{\\mathstyle}
+/*tex Interface to \.{\\Umath} and \.{\\mathstyle}: */
-@c
void setup_math_style(void)
{
pointer q;
@@ -2582,7 +2714,6 @@ void setup_math_style(void)
(void) scan_math_style(nucleus(tail), num_style(m_style));
}
-@ @c
void print_math_style(void)
{
if (abs(mode) == mmode)
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..1dd28cdf56d
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.c
@@ -0,0 +1,4770 @@
+/*
+
+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_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 },
+ { -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_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 },
+
+ /*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(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_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(user_node_value(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_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':
+ 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_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_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.
+
+*/
+
+scaled glyph_width(halfword p)
+{
+ scaled w = char_width(font(p), character(p));
+ return w;
+}
+
+scaled glyph_height(halfword p)
+{
+ scaled w = char_height(font(p), character(p)) + y_displace(p);
+ if (w < 0)
+ w = 0;
+ return w;
+}
+
+scaled glyph_depth(halfword p)
+{
+ scaled w = char_depth(font(p), character(p));
+ if (y_displace(p) > 0)
+ w = w - y_displace(p);
+ if (w < 0)
+ w = 0;
+ return w;
+}
+
+/*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,0);
+ 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.w b/Build/source/texk/web2c/luatexdir/tex/texnodes.w
deleted file mode 100644
index 0c8bdb83726..00000000000
--- a/Build/source/texk/web2c/luatexdir/tex/texnodes.w
+++ /dev/null
@@ -1,3910 +0,0 @@
-% texnodes.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/>.
-
-@ @c
-
-#include "ptexlib.h"
-#include "lua/luatex-api.h"
-
-/* we can consider less mode sizes: 2 4 6 8 */
-
-@
-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.
-
-@c
-#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;
-
-int fix_node_lists = 1; /* used in font and lang */
-
-halfword slow_get_node(int s); /* defined below */
-
-#define fake_node 100
-#define fake_node_size 2
-#define fake_node_name "fake"
-
-#define variable_node_size 2
-
-/* core nodes */
-
-const char *node_fields_list[] = {
- "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign",
- "glue_set", "head", NULL
-};
-const char *node_fields_rule[] = {
- "attr", "width", "depth", "height", "dir", "index", NULL
-};
-const char *node_fields_insert[] = {
- "attr", "cost", "depth", "height", "spec", "head", NULL
-};
-const char *node_fields_mark[] = {
- "attr", "class", "mark", NULL
-};
-const char *node_fields_adjust[] = {
- "attr", "head", NULL
-};
-const char *node_fields_disc[] = {
- "attr", "pre", "post", "replace", "penalty", NULL
-};
-const char *node_fields_math[] = {
- "attr", "surround", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
-};
-const char *node_fields_glue[] = {
- "attr", "leader", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
-};
-const char *node_fields_kern[] = {
- "attr", "kern", "expansion_factor", NULL
-};
-const char *node_fields_penalty[] = {
- "attr", "penalty", NULL
-};
-const char *node_fields_unset[] = {
- "attr", "width", "depth", "height", "dir", "shrink", "glue_order",
- "glue_sign", "stretch", "span", "head", NULL
-};
-const char *node_fields_margin_kern[] = {
- "attr", "width", "glyph", NULL
-};
-const char *node_fields_glyph[] = {
- "attr", "char", "font", "lang", "left", "right", "uchyph", "components",
- "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL
-};
-const char *node_fields_inserting[] = {
- "height", "last_ins_ptr", "best_ins_ptr",
- "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
-};
-const char *node_fields_splitup[] = {
- "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL
-};
-const char *node_fields_attribute[] = {
- "number", "value", NULL
-};
-const char *node_fields_glue_spec[] = {
- "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
-};
-const char *node_fields_attribute_list[] = {
- NULL
-};
-const char *node_fields_local_par[] = {
- "attr", "pen_inter", "pen_broken", "dir", "box_left", "box_left_width",
- "box_right", "box_right_width", NULL
-};
-const char *node_fields_dir[] = {
- "attr", "dir", "level", NULL
-};
-const char *node_fields_boundary[] = {
- "attr", "value", NULL
-};
-
-/* math nodes */
-
-const char *node_fields_noad[] = {
- "attr", "nucleus", "sub", "sup", NULL
-};
-
-const char *node_fields_style[] = {
- "attr", "style", NULL
-};
-const char *node_fields_choice[] = {
- "attr", "display", "text", "script", "scriptscript", NULL
-};
-const char *node_fields_radical[] = {
- "attr", "nucleus", "sub", "sup", "left", "degree", "width", "options", NULL
-};
-const char *node_fields_fraction[] = {
- "attr", "width", "num", "denom", "left", "right", "middle", "options", NULL
-};
-const char *node_fields_accent[] = {
- "attr", "nucleus", "sub", "sup", "accent", "bot_accent", "top_accent",
- "overlay_accent", "fraction", NULL
-};
-const char *node_fields_fence[] = {
- "attr", "delim", "italic", "height", "depth", "options", "class", NULL
-};
-const char *node_fields_math_char[] = {
- "attr", "fam", "char", NULL
-};
-const char *node_fields_sub_box[] = {
- "attr", "head", NULL
-};
-const char *node_fields_sub_mlist[] = {
- "attr", "head", NULL
-};
-const char *node_fields_math_text_char[] = {
- "attr", "fam", "char", NULL
-};
-const char *node_fields_delim[] = {
- "attr", "small_fam", "small_char", "large_fam", "large_char", NULL
-};
-
-/* whatsit nodes */
-
-const char *node_fields_whatsit_open[] = {
- "attr", "stream", "name", "area", "ext", NULL
-};
-const char *node_fields_whatsit_write[] = {
- "attr", "stream", "data", NULL
-};
-const char *node_fields_whatsit_close[] = {
- "attr", "stream", NULL
-};
-const char *node_fields_whatsit_special[] = {
- "attr", "data", NULL
-};
-const char *node_fields_whatsit_save_pos[] = {
- "attr", NULL
-};
-const char *node_fields_whatsit_late_lua[] = {
- "attr", "reg", "data", "name", "string", NULL
-};
-const char *node_fields_whatsit_user_defined[] = {
- "attr", "user_id", "type", "value", NULL
-};
-
-/* pdf backend whatsit nodes */
-
-const char *node_fields_whatsit_pdf_literal[] = {
- "attr", "mode", "data", NULL
-};
-const char *node_fields_whatsit_pdf_refobj[] = {
- "attr", "objnum", NULL
-};
-const char *node_fields_whatsit_pdf_annot[] = {
- "attr", "width", "depth", "height", "objnum", "data", NULL
-};
-const char *node_fields_whatsit_pdf_start_link[] = {
- "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL
-};
-const char *node_fields_whatsit_pdf_end_link[] = {
- "attr", NULL
-};
-const char *node_fields_whatsit_pdf_dest[] = {
- "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type",
- "xyz_zoom", "objnum", NULL
-};
-const char *node_fields_whatsit_pdf_action[] = {
- "action_type", "named_id", "action_id", "file", "new_window", "data", NULL
-};
-const char *node_fields_whatsit_pdf_thread[] = {
- "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
-};
-const char *node_fields_whatsit_pdf_start_thread[] = {
- "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
-};
-const char *node_fields_whatsit_pdf_end_thread[] = {
- "attr", NULL
-};
-const char *node_fields_whatsit_pdf_colorstack[] = {
- "attr", "stack", "cmd", "data", NULL
-};
-const char *node_fields_whatsit_pdf_setmatrix[] = {
- "attr", "data", NULL
-};
-const char *node_fields_whatsit_pdf_save[] = {
- "attr", NULL
-};
-const char *node_fields_whatsit_pdf_restore[] = {
- "attr", NULL
-};
-
-/* subtypes */
-
-const char *node_subtypes_glue[] = {
- "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip",
- "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip",
- "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip",
- "mathskip", "thinmuskip", "medmuskip", "thickmuskip", NULL
-};
-const char *node_subtypes_mathglue[] = { /* 98+ */
- "conditionalmathskip", "muglue", NULL
-};
-const char *node_subtypes_leader[] = { /* 100+ */
- "leaders", "cleaders", "xleaders", "gleaders", NULL
-};
-const char *node_subtypes_fill[] = {
- "stretch", "fi", "fil", "fill", "filll", NULL
-};
-const char *node_subtypes_boundary[] = {
- "cancel", "user", "protrusion", "word", NULL
-};
-const char *node_subtypes_penalty[] = {
- "userpenalty", "linebreakpenalty", "linepenalty", "wordpenalty", "finalpenalty",
- "noadpenalty", "beforedisplaypenalty", "afterdisplaypenalty", "equationnumberpenalty", NULL
-};
-const char *node_subtypes_kern[] = {
- "fontkern", "userkern", "accentkern", "italiccorrection", NULL
-};
-const char *node_subtypes_rule[] = {
- "normal", "box", "image", "empty", "user", "over", "under", "fraction", "radical", NULL
-};
-const char *node_subtypes_glyph[] = {
- "character", "glyph", "ligature", "ghost", "left", "right", NULL
-};
-const char *node_subtypes_disc[] = {
- "discretionary", "explicit", "automatic", "regular", "first", "second", NULL
-};
-const char *node_subtypes_marginkern[] = {
- "left", "right", NULL
-};
-const char *node_subtypes_list[] = {
- "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL
-};
-const char *node_subtypes_adjust[] = {
- "normal", "pre", NULL
-};
-const char *node_subtypes_math[] = {
- "beginmath", "endmath", NULL
-};
-const char *node_subtypes_noad[] = {
- "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close",
- "punct", "inner", "under", "over", "vcenter", NULL
-};
-const char *node_subtypes_radical[] = {
- "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder",
- "udelimiterover", NULL
-};
-const char *node_subtypes_accent[] = {
- "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL,
-};
-const char *node_subtypes_fence[] = {
- "unset", "left", "middle", "right", NULL
-};
-
-node_info node_data[] = { /* the last entry in a row is the etex number */
- { hlist_node, box_node_size, node_fields_list, "hlist", 1 },
- { vlist_node, box_node_size, node_fields_list, "vlist", 2 },
- { rule_node, rule_node_size, node_fields_rule, "rule", 3 },
- { ins_node, ins_node_size, node_fields_insert, "ins", 4 },
- { mark_node, mark_node_size, node_fields_mark, "mark", 5 },
- { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 },
- { boundary_node, boundary_node_size, node_fields_boundary, "boundary", -1 },
- { disc_node, disc_node_size, node_fields_disc, "disc", 8 },
- { whatsit_node, -1, NULL, "whatsit", 9 },
- { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 },
- { dir_node, dir_node_size, node_fields_dir, "dir", -1 },
- { math_node, math_node_size, node_fields_math, "math", 10 },
- { glue_node, glue_node_size, node_fields_glue, "glue", 11 },
- { kern_node, kern_node_size, node_fields_kern, "kern", 12 },
- { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 },
- { unset_node, box_node_size, node_fields_unset, "unset", 14 },
- { style_node, style_node_size, node_fields_style, "style", 15 },
- { choice_node, style_node_size, node_fields_choice, "choice", 15 },
- { simple_noad, noad_size, node_fields_noad, "noad", 15 },
- { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 },
- { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 },
- { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 },
- { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 },
- { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 },
- { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 },
- { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 },
- { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 },
- { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 },
- { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 },
- { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 },
- { align_record_node, box_node_size, NULL, "align_record", -1 },
- { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 },
- { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 },
- { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 },
- { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 },
- { expr_node, expr_node_size, NULL, "expr_stack", -1 },
- { nesting_node, nesting_node_size, NULL, "nested_list", -1 },
- { span_node, span_node_size, NULL, "span", -1 },
- { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 },
- { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 },
- { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 },
- { temp_node, temp_node_size, NULL, "temp", -1 },
- { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 },
- { movement_node, movement_node_size, NULL, "movement_stack", -1 },
- { if_node, if_node_size, NULL, "if_stack", -1 },
- { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 },
- { hyphenated_node, active_node_size, NULL, "hyphenated", -1 },
- { delta_node, delta_node_size, NULL, "delta", -1 },
- { passive_node, passive_node_size, NULL, "passive", -1 },
- { shape_node, variable_node_size, NULL, "shape", -1 },
- { -1, -1, NULL, NULL, -1 },
-};
-
-const char *node_subtypes_pdf_destination[] = {
- "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL
-};
-const char *node_subtypes_pdf_literal[] = {
- "origin", "page", "direct", NULL
-};
-
-node_info whatsit_node_data[] = {
- { open_node, open_node_size, node_fields_whatsit_open, "open", -1 },
- { write_node, write_node_size, node_fields_whatsit_write, "write", -1 },
- { close_node, close_node_size, node_fields_whatsit_close, "close", -1 },
- { special_node, special_node_size, node_fields_whatsit_special, "special", -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 },
- { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 },
- { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- { fake_node, fake_node_size, NULL, fake_node_name, -1 },
- /* here starts the dvi backend section, todo: a separate list */
- /* nothing for dvi */
- /* here starts the pdf backend section, todo: a separate list */
- { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 },
- { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 },
- { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 },
- { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 },
- { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 },
- { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 },
- { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 },
- { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 },
- { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 },
- { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 },
- { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 },
- { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 },
- { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 },
- { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 },
- { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 },
- { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 },
- /* done */
- { -1, -1, NULL, NULL, -1 },
-};
-
-#define last_whatsit_node pdf_restore_node
-
-@
-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:
-
-- 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
-
-- 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
-
-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.
-
-@c
-int lua_properties_level = 0 ; /* can be private */
-int lua_properties_enabled = 0 ;
-int lua_properties_use_metatable = 0 ;
-
-@
-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.
-
-@c
-#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)
-
-/* No setting is needed: */
-
-#define lua_properties_set(target) do { \
-} while(0)
-
-/* 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)
-
-/*
- 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:
-
- 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)
-
-*/
-
-/* isn't there a faster way to metatable? */
-
-/*
-
-#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)
-
-*/
-
-/*
- 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)
-
-/* Here end the property handlers. */
-
-@ @c
-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;
-}
-
-@ @c
-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); }
-
-/* 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;
-}
-
-@ @c
-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;
-}
-
-@ @c
-static halfword synctex_anyway_mode = 0; /* 2 also glyphs */
-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;
-};
-
-@ @c
-/* if_stack is called a lot so maybe optimize */
-
-halfword new_node(int i, int j)
-{
- int s = get_node_size(i, j);
- halfword n = get_node(s);
- /*
- 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:
- /* 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;
- default:
- break;
- }
- if (synctex_anyway_mode) {
- switch (i) {
- /* 1 = all but glyphs */
- /* 2 = also glyphs */
- /* 3 = glyphs and glue */
- /* 4 = only glyphs */
- 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: /* 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: /* noads probably make more sense */
- 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) {
- /* handle synctex 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;
- }
- }
- /* take care of attributes */
- if (nodetype_has_attributes(i)) {
- build_attribute_list(n);
- /* 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);
- /* lua_properties_set */
- return n;
-}
-
-@ makes a duplicate of the node list that starts at |p| and returns a
-pointer to the new list
-
-@c
-halfword do_copy_node_list(halfword p, halfword end)
-{
- halfword q = null; /* previous position in new list */
- halfword h = null; /* head of the list */
- register halfword s ;
- lua_properties_push; /* saves stack and time */
- while (p != end) {
- s = copy_node(p);
- if (h == null) {
- h = s;
- } else {
- couple_nodes(q, s);
- }
- q = s;
- p = vlink(p);
- }
- lua_properties_pop; /* saves stack and time */
- 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)
-
-@ make a dupe of a single node
-
-@c
-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)
-{
- halfword r; /* current node being fabricated for new list */
- halfword w; /* whatsit subtype */
- halfword t; /* type of node */
- register halfword s; /* a helper variable for copying into variable mem */
- 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));
-
- /* possible speedup: */
- /*
- if t == glue_spec) {
- return r;
- }
- */
-
- if (synctex_anyway_mode) {
- /*
- 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;
- }
- }
- */
- } else if (synctex_par) {
- /* 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:
- case dir_node:
- case local_par_node:
- 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;
-}
-
-/* x */
-
-#define free_sub_list(source) if (source != null) flush_node_list(source);
-#define free_sub_node(source) if (source != null) flush_node(source);
-
-@ @c
-
-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(user_node_value(p));
- break;
- case 'n':
- flush_node_list(user_node_value(p));
- break;
- case 's':
- /* |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_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) /* 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:
- /* 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:
- /* this allows free-ing of lua-allocated glue specs */
-//if (valid_node(p)) {
-// free_node(p, subtype(p));
-//}
-// return ;
- break ;
- case dir_node:
- case local_par_node:
- 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: /* 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: /* nothing to do */
- case math_char_node:
- case math_text_char_node:
- 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;
-}
-
-@ @c
-void flush_node_list(halfword pp)
-{ /* erase list of nodes starting at |p| */
- register halfword p = pp;
- if (p == null) /* legal, but no-op */
- return;
- if (free_error(p))
- return;
- lua_properties_push; /* saves stack and time */
- while (p != null) {
- register halfword q = vlink(p);
- flush_node(p);
- p = q;
- }
- lua_properties_pop; /* saves stack and time */
-}
-
-@ @c
-static void check_node_wrapup_core(halfword p)
-{
- switch (subtype(p)) {
- /* 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':
- 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_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 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:
- case local_par_node:
- break;
- default:
- fprintf(stdout, "check_node: type is %d\n", type(p));
- */
- }
-}
-
-@ @c
-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;
-}
-
-@ @c
-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;
- var_used += s; /* maintain usage statistics */
- return r;
- }
- /* this is the end of the 'inner loop' */
- return slow_get_node(s);
- } else {
- normal_error("nodes","there is a problem in getting a node, case 1");
- return null;
- }
-}
-
-@ @c
-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 {
- /* 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;
- }
- /* maintain statistics */
- var_used -= s;
-}
-
-@ @c
-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;
-}
-
-@ 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.
-
-@c
-#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;
-
- /* 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);
-
- /* 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);
-}
-
-@ @c
-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);
-}
-
-@ it makes sense to enlarge the varmem array immediately
-@c
-
-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) {
- /* todo ? it is 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;
- }
-}
-
-@ @c
-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) {
- /* 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) { /* 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;
- var_used += s; /* maintain usage statistics */
- return r; /* this is the only exit */
- } else {
- normal_error("nodes","there is a problem in getting a node, case 2");
- return null;
- }
- } else {
- /* 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);
- }
- }
- }
- /* 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
- /* todo ? it is 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;
- }
-}
-
-@ @c
-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;
-}
-
-@ @c
-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;
-}
-
-@ @c
-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;
- }
- }
- /* newline, if needed */
- print_nlp();
-}
-
-/* this belongs in the web but i couldn't find the correct syntactic place */
-
-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;
-}
-
-@* Attribute stuff.
-
-@c
-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;
-}
-
-@ @c
-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);
- /* 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;
-}
-
-@ @c
-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;
-}
-
-@ @c
-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;
- }
-}
-
-@ @c
-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 ;
-}
-
-
-@ @c
-void reassign_attribute(halfword n, halfword new)
-{
- halfword old;
- old = node_attr(n);
- if (new == null) {
- /* there is nothing to assign but we need to check for an old value */
- if (old != null)
- delete_attribute_ref(old); /* also nulls attr field of n */
- } else if (old == null) {
- /* nothing is assigned so we just do that now */
- assign_attribute_ref(n,new);
- } else if (old != new) {
- /* something is assigned so we need to clean up and assign then */
- delete_attribute_ref(old);
- assign_attribute_ref(n,new);
- }
- /* else: same value so there is no need to assign and change the refcount */
- node_attr(n) = new ;
-}
-
-@ @c
-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);
- }
- /* 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);
- }
-}
-
-@ |p| is an attr list head, or zero
-@c
-halfword do_set_attribute(halfword p, int i, int val)
-{
- register halfword q;
- register int j = 0;
- if (p == null) { /* 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)
- return q; /* no need to do anything */
- 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 { /* 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 ;
- }
-}
-
-@ @c
-void set_attribute(halfword n, int i, int val)
-{
- register halfword p;
- register int j = 0;
- /* not all nodes can have an attribute list */
- if (!nodetype_has_attributes(type(n)))
- return;
- /* 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;
- }
- /* 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);
- }
- /* j has now the position (if found) .. we assume a sorted list ! */
- p = node_attr(n);
-
- if (attr_list_ref(p) == 0 ) {
- /* 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);
- /* the still dangling list gets ref count 1 */
- attr_list_ref(p) = 1;
- } else if (attr_list_ref(p) == 1) {
- /* this can really happen HH-LS */
- if (p == attr_list_cache) {
- /* we can invalidate the cache setting */
- /* attr_list_cache = cache_disabled */
- /* or save the list, as done below */
- p = copy_attribute_list(p);
- node_attr(n) = p;
- /* the copied list gets ref count 1 */
- attr_list_ref(p) = 1;
- }
- } else {
- /* the list is used multiple times so we make a copy */
- p = copy_attribute_list(p);
- /* we decrement the ref count or the original */
- delete_attribute_ref(node_attr(n));
- node_attr(n) = p;
- /* the copied list gets ref count 1 */
- attr_list_ref(p) = 1;
- }
-
-
- /* we go to position j in the list */
- while (j-- > 0)
- p = vlink(p);
- /* 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 { /* 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");
- }
-}
-
-@ @c
-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;
- /* 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;
- }
-}
-
-@ @c
-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;
-}
-
-@ @c
-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;
- }
-}
-
-@ @c
-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));
-}
-
-@ 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.
-
-@c
-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('-');
-}
-
-@ @c
-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_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;
- }
-}
-
-@ 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.
-
-
-@ |str_room| need not be checked; see |show_box|
-
-@ Recursive calls on |show_node_list| therefore use the following pattern:
-@c
-#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)
-
-/* prints a node list symbolically */
-
-void show_node_list(int p)
-{
- int n = 0; /* the number of items already printed at this level */
- halfword w;
- real g; /* a glue ratio, as a floating point number */
- if ((int) cur_length > depth_threshold) {
- if (p > null)
- tprint(" []"); /* indicate that there's been some truncation */
- return;
- }
- while (p != null) {
- print_ln();
- print_current_string(); /* display the nesting history */
- if (tracing_online_par < -2)
- print_int(p);
- incr(n);
- if (n > breadth_max) { /* time to stop */
- tprint("etc.");
- return;
- }
- /* Display node |p| */
- if (is_char_node(p)) {
- print_font_and_char(p);
- if (is_ligature(p)) {
- /* 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:
- /* 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) {
- /* 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 {
- /* 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(box_dir(p));
- }
- node_list_display(list_ptr(p)); /* recursive call */
- break;
- case rule_node:
- /* 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:
- /* 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));
- node_list_display(ins_ptr(p)); /* recursive call */
- break;
- case dir_node:
- if (dir_dir(p) < 0) {
- tprint_esc("enddir");
- print_char(' ');
- print_dir(dir_dir(p) + dir_swap);
- } else {
- tprint_esc("begindir");
- print_char(' ');
- print_dir(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:
- /* Display glue |p|; */
- if (subtype(p) >= a_leaders) {
- /* 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);
- node_list_display(leader_ptr(p)); /* recursive call */
- } 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:
- /* Display kern |p|; */
- /* An ``explicit'' kern value is indicated implicitly by an explicit space. */
- if (subtype(p) != mu_glue) {
- tprint_esc("kern");
- /*
- if (subtype(p) != normal)
- print_char(' ');
- */
- 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:
- /* 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:
- /* Display penalty |p|; */
- tprint_esc("penalty ");
- print_int(penalty(p));
- break;
- case disc_node:
- /* 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 ...
- 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();
- */
- 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:
- /* 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:
- /* Display adjustment |p|; */
- tprint_esc("vadjust");
- if (subtype(p) != 0)
- tprint(" pre ");
- node_list_display(adjust_ptr(p)); /* recursive call */
- break;
- case glue_spec_node:
- tprint("<glue_spec ");
- print_spec(p, NULL);
- tprint(">");
- break;
- default:
- show_math_node(p);
- break;
- }
- }
- p = vlink(p);
- }
-}
-
-@ This routine finds the 'base' width of a horizontal box, using the same logic
- that \TeX82 used for \.{\\predisplaywidth} */
-
-@c
-static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width)
-{
- scaled d; /* increment to |v| */
- scaled w = -max_dimen; /* calculated |size| */
- scaled v = initial_width; /* |w| plus possible glue amount */
- 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:
- /* 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:
- /* begin mathskip code */
- if (glue_is_zero(p)) {
- d = surround(p);
- break;
- } else {
- /* fall through */
- }
- /* end mathskip code */
- case glue_node:
- /* 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)
-{
- /* often this is the same as:
- 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));
- */
- return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width);
-}
-
-@ @c
-halfword tail_of_list(halfword p)
-{
- halfword q = p;
- while (vlink(q) != null)
- q = vlink(q);
- return q;
-}
-
-
-
-@ @c
-int var_used;
-
-@ 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.
-
-@c
-int max_used_attr; /* maximum assigned attribute id */
-halfword attr_list_cache;
-
-@ 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|.
-
-@c
-halfword new_null_box(void)
-{ /* creates a new box node */
- halfword p = new_node(hlist_node, min_quarterword);
- box_dir(p) = text_direction_par;
- return p;
-}
-
-@ 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.
-
-@c
-halfword new_rule(int s)
-{
- halfword p = new_node(rule_node,s);
- return p;
-}
-
-@ Insertions are represented by |ins_node| records, where the |subtype|
-indicates the corresponding box number. For example, `\.{\\insert 250}'
-leads to an |ins_node| whose |subtype| is |250+min_quarterword|.
-The |height| field of an |ins_node| is slightly misnamed; it actually holds
-the natural height plus depth of the vertical list being inserted.
-The |depth| field holds the |split_max_depth| to be used in case this
-insertion is split, and the |split_top_ptr| points to the corresponding
-|split_top_skip|. The |float_cost| field holds the |floating_penalty| that
-will be used if this insertion floats to a subsequent page after a
-split insertion of the same class. There is one more field, the
-|ins_ptr|, which points to the beginning of the vlist for the insertion.
-
-@ 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|).
-
-@c
-halfword new_glyph(int f, int c)
-{
- halfword p = null; /* the new node */
- if ((f == 0) || (char_exists(f, c))) {
- p = new_glyph_node();
- set_to_glyph(p);
- font(p) = f;
- character(p) = c;
- }
- return p;
-}
-
-@ 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|.
-
-@c
-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; /* the new node */
- 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;
-}
-
-@ 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.
-
-@c
-scaled glyph_width(halfword p)
-{
- scaled w = char_width(font(p), character(p));
- return w;
-}
-
-scaled glyph_height(halfword p)
-{
- scaled w = char_height(font(p), character(p)) + y_displace(p);
- if (w < 0)
- w = 0;
- return w;
-}
-
-scaled glyph_depth(halfword p)
-{
- scaled w = char_depth(font(p), character(p));
- if (y_displace(p) > 0)
- w = w - y_displace(p);
- if (w < 0)
- w = 0;
- return w;
-}
-
-@ 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|.
-
-{TODO: Knuth said: All three of the discretionary texts must be lists
-that consist entirely of character, kern, box and rule nodes.}
-
-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.
-
-@c
-halfword new_disc(void)
-{ /* creates an empty |disc_node| */
- 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,
-@^extensions to \TeX@>
-and the whatsit node might contain the desired parameters.
-
-The present implementation of \TeX\ treats the features associated with
-`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
-illustrate how such routines might be coded. We shall defer further
-discussion of extensions until the end of this program.
-
-@ 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}.
-
-@c
-halfword new_math(scaled w, int s)
-{
- halfword p = new_node(math_node, s);
- surround(p) = w;
- return p;
-}
-
-@ \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,
-@^reference counts@>
-representing |null| plus the number of glue nodes that point to it (less one).
-Note that the reference count appears in the same position as
-the |link| field in list nodes; this is the field that is initialized
-to |null| when a node is allocated, and it is also the field that is flagged
-by |empty_flag| in empty nodes.
-
-Glue specifications also contain three |scaled| fields, for the |width|,
-|stretch|, and |shrink| dimensions. Finally, there are two one-byte
-fields called |stretch_order| and |shrink_order|; these contain the
-orders of infinity (|normal|, |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.
-
-@c
-halfword new_spec(halfword q) /* safeguard for copying a glue node */
-{
- 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 {
- /* alternatively we can issue a warning */
- return copy_node(zero_glue);
- }
-}
-
-@ 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}.
-
-@c
-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;
-}
-
-@ Glue nodes that are more or less anonymous are created by |new_glue|,
-whose argument points to a glue specification.
-
-@c
-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;
-}
-
-@ 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.
-*/
-
-@c
-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;
-}
-
-@ 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.
-
-@c
-halfword new_kern(scaled w)
-{
- halfword p = new_node(kern_node, normal);
- width(p) = w;
- return p;
-}
-
-@ A |penalty_node| specifies the penalty associated with line or page
-breaking, in its |penalty| field. This field is a fullword integer, but
-the full range of integer values is not used: Any penalty |>=10000| is
-treated as infinity, and no break will be allowed for such high values.
-Similarly, any penalty |<=-10000| is treated as negative infinity, and a
-break will be forced.
-
-@ Anyone who has been reading the last few sections of the program will
-be able to guess what comes next.
-
-@c
-halfword new_penalty(int m, int s)
-{
- halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */
- penalty(p) = m;
- subtype(p) = s;
- return p;
-}
-
-@ You might think that we have introduced enough node types by now. Well,
-almost, but there is one more: An |unset_node| has nearly the same format
-as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign}
-or \.{\\valign} that are not yet in their final form, since the box
-dimensions are their ``natural'' sizes before any glue adjustment has been
-made. The |glue_set| word is not present; instead, we have a |glue_stretch|
-field, which contains the total stretch of order |glue_order| that is
-present in the hlist or vlist being boxed.
-Similarly, the |shift_amount| field is replaced by a |glue_shrink| field,
-containing the total shrink of order |glue_sign| that is present.
-The |subtype| field is called |span_count|; an unset box typically
-contains the data for |qo(span_count)+1| columns.
-Unset nodes will be changed to box nodes when alignment is completed.
-
-In fact, there are still more types coming. When we get to math formula
-processing we will see that a |style_node| has |type=14|; and a number
-of larger type codes will also be defined, for use in math mode only.
-
-Warning: If any changes are made to these data structure layouts, such as
-changing any of the node sizes or even reordering the words of nodes,
-the |copy_node_list| procedure and the memory initialization code
-below may have to be changed. Such potentially dangerous parts of the
-program are listed in the index under `data structure assumptions'.
-@!@^data structure assumptions@>
-However, other references to the nodes are made symbolically in terms of
-the \.{WEB} macro definitions above, so that format changes will leave
-\TeX's other algorithms intact.
-@^system dependencies@>
-
-@ This function creates a |local_paragraph| node
-
-@c
-
-halfword make_local_par_node(int mode)
-{
- int callback_id;
- halfword q;
- halfword p = new_node(local_par_node,0);
- 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;
- /* callback with node passed */
- callback_id = callback_defined(insert_local_par_callback);
- if (callback_id > 0) {
- int sfix = lua_gettop(Luas);
- if (!get_callback(Luas, callback_id)) {
- lua_settop(Luas, sfix);
- return p;
- }
- nodelist_to_lua(Luas, p);
- lua_push_local_par_mode(Luas,mode)
- if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */
- char errmsg[256]; /* temp hack ... we will have a formatted error */
- snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1));
- errmsg[255]='\0';
- lua_settop(Luas, sfix);
- normal_error("insert_local_par",errmsg); /* to be done */
- return p;
- }
- lua_settop(Luas, sfix);
- }
- /* done */
- return p;
-}
diff --git a/Build/source/texk/web2c/luatexdir/tex/textcodes.w b/Build/source/texk/web2c/luatexdir/tex/textcodes.c
index 89e93684132..a6a0b3a55e3 100644
--- a/Build/source/texk/web2c/luatexdir/tex/textcodes.w
+++ b/Build/source/texk/web2c/luatexdir/tex/textcodes.c
@@ -1,27 +1,35 @@
-% 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/>.
-
-@ @c
+/*
+
+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"
-@ catcodes @c
+/*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
@@ -174,7 +182,11 @@ static void freecatcodes(void)
xfree(catcode_valid);
}
-@ lccodes @c
+/*tex
+
+The lowercase mapping codes are also stored in a tree.
+
+*/
#define LCCODESTACK 8
#define LCCODEDEFAULT 0
@@ -220,7 +232,11 @@ static void freelccodes(void)
destroy_sa_tree(lccode_head);
}
-@ uccodes @c
+/*tex
+
+And the uppercase mapping codes are again stored in a tree.
+
+*/
#define UCCODESTACK 8
#define UCCODEDEFAULT 0
@@ -266,7 +282,11 @@ static void freeuccodes(void)
destroy_sa_tree(uccode_head);
}
-@ sfcodes @c
+/*tex
+
+By now it will be no surprise that the space factors get stored in a tree.
+
+*/
#define SFCODESTACK 8
#define SFCODEDEFAULT 1000
@@ -312,19 +332,29 @@ static void freesfcodes(void)
destroy_sa_tree(sfcode_head);
}
-@ hjcodes @c
+/*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 /* number of languages */
+#define HJCODE_MAX 16383
static sa_tree *hjcode_heads = NULL;
static int hjcode_max = 0;
static unsigned char *hjcode_valid = NULL;
-@ Here we set codes but we don't initialize from lccodes.
+/*tex
+
+Here we set codes but we don't initialize from lccodes.
+
+*/
-@c void set_hj_code(int h, int n, halfword v, quarterword gl)
+void set_hj_code(int h, int n, halfword v, quarterword gl)
{
sa_tree_item sa_value = { 0 };
sa_tree s = hjcode_heads[h];
@@ -339,9 +369,13 @@ static unsigned char *hjcode_valid = NULL;
set_sa_item(s, n, sa_value, gl);
}
-@ We just return the lccodes when nothing is set.
+/*tex
-@c halfword get_hj_code(int h, int n)
+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) {
@@ -350,12 +384,14 @@ static unsigned char *hjcode_valid = NULL;
return (halfword) get_sa_item(s, n).int_value;
}
-@ 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:
+/*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:
+
+*/
-@c
/*
static void unsavehjcodes(quarterword gl) { }
*/
@@ -444,7 +480,11 @@ static void freehjcodes(void)
xfree(hjcode_valid);
}
-/* management */
+/*tex
+
+The public management functions.
+
+*/
void unsave_text_codes(quarterword grouplevel)
{
diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.w b/Build/source/texk/web2c/luatexdir/tex/textoken.c
index d58aa3dc49b..6056bba2517 100644
--- a/Build/source/texk/web2c/luatexdir/tex/textoken.w
+++ b/Build/source/texk/web2c/luatexdir/tex/textoken.c
@@ -1,40 +1,26 @@
-% textoken.w
-%
-% 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/>.
-
-@ @c
+/*
-#include "ptexlib.h"
+Copyright 2006-2011 Taco Hoekwater <taco@luatex.org>
-@ @c
-#define detokenized_line() (line_catcode_table==NO_CAT_TABLE)
+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/>.
-/*
-#define do_get_cat_code(a,b) do { \
- if (line_catcode_table<=-0xFF) \
- a= - line_catcode_table - 0xFF ; \
- else if (line_catcode_table!=DEFAULT_CAT_TABLE) \
- a=get_cat_code(line_catcode_table,b); \
- else \
- a=get_cat_code(cat_code_table_par,b); \
- } while (0)
*/
+#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); \
@@ -45,47 +31,85 @@
} while (0)
-@ 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.
+/*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 */
-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.
+unsigned fix_mem_min;
-@ Locations in |fixmem| are used for storing one-word records; a conventional
-\.{AVAIL} stack is used for allocation in this array.
+/*tex the largest location of one-word memory in use */
-@c
-smemory_word *fixmem; /* the big dynamic storage area */
-unsigned fix_mem_min; /* the smallest location of one-word memory in use */
-unsigned fix_mem_max; /* the largest location of one-word memory in use */
+unsigned fix_mem_max;
-@ 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
+
+ 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.
+
+*/
-@c
-int var_used, dyn_used; /* how much memory is in use */
+/*tex how much memory is in use */
-halfword avail; /* head of the list of available one-word nodes */
-unsigned fix_mem_end; /* the last one-word node used in |mem| */
+int var_used, dyn_used;
-halfword garbage; /* head of a junk list, write only */
-halfword temp_token_head; /* head of a temporary list of some kind */
-halfword hold_token_head; /* head of a temporary list of another kind */
-halfword omit_template; /* a constant token list */
-halfword null_list; /* permanently empty list */
-halfword backup_head; /* head of token list built by |scan_keyword| */
+/*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;
-@ @c
void initialize_tokens(void)
{
halfword p;
@@ -109,37 +133,44 @@ void initialize_tokens(void)
p = get_avail();
garbage = p;
set_token_info(garbage, 0);
- dyn_used = 0; /* initialize statistics */
+ dyn_used = 0;
}
-@ The function |get_avail| returns a pointer to a new one-word node whose |link|
-field is null. However, \TeX\ will halt if there is no more room left.
-@^inner loop@>
+/*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.
+ 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:
+*/
-@c
halfword get_avail(void)
-{ /* single-word node allocation */
- unsigned p; /* the new node being got */
+{
+ /*tex The new node being got: */
+ unsigned p;
unsigned t;
- p = (unsigned) avail; /* get top location in the |avail| stack */
+ /*tex Get top location in the |avail| stack. */
+ p = (unsigned) avail;
if (p != null) {
- avail = token_link(avail); /* and pop it off */
- } else if (fix_mem_end < fix_mem_max) { /* or go into virgin territory */
+ /*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 {
- smemory_word *new_fixmem; /* the big dynamic storage area */
+ /*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)));
+ new_fixmem = fixmemcast(realloc(fixmem, sizeof(smemory_word) * (fix_mem_max + t + 1)));
if (new_fixmem == NULL) {
- runaway(); /* if memory is exhausted, display possible runaway text */
+ /*tex If memory is exhausted, display possible runaway text. */
+ runaway();
overflow("token memory size", fix_mem_max);
} else {
fixmem = new_fixmem;
@@ -148,111 +179,127 @@ halfword get_avail(void)
fix_mem_max += t;
p = ++fix_mem_end;
}
- token_link(p) = null; /* provide an oft-desired initialization of the new node */
- incr(dyn_used); /* maintain statistics */
+ /*tex Provide an oft-desired initialization of the new node. */
+ token_link(p) = null;
+ /*tex Maintain statistics. */
+ incr(dyn_used);
return (halfword) p;
}
-@ The procedure |flush_list(p)| frees an entire linked list of one-word nodes
-that starts at position |p|.
-@^inner loop@>
+/*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:
+
+*/
-@c
void flush_list(halfword p)
-{ /* makes list of single-word nodes available */
- halfword q, r; /* list traversers */
+{
+ halfword q, r;
if (p != null) {
r = p;
do {
q = r;
r = token_link(r);
decr(dyn_used);
- } while (r != null); /* now |q| is the last node on the list */
+ } while (r != null);
+ /*tex Now |q| is the last node on the list. */
token_link(q) = avail;
avail = p;
}
}
-@ A \TeX\ token is either a character or a control sequence, and it is @^token@>
-represented internally in one of two ways: (1)~A character whose ASCII code
-number is |c| and whose command code is |m| is represented as the number
-$2^{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
-
-$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$
-
-the definition of \.{\\mac} is represented as a token list containing
-
-$$\def\,{\hskip2pt}
-\vbox{\halign{\hfil#\hfil\cr
-(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ ,
-\.{\\b}, |end_match|,\cr
-|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#,
-|other_char|\,\.1,\cr
-|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$
-
-The procedure |scan_toks| builds such token lists, and |macro_call| does the
-parameter matching. @^reference counts@>
-
-Examples such as $$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$ explain why reference
-counts would be needed even if \TeX\ had no \.{\\let} operation: When the token
-list for \.{\\m} is being read, the redefinition of \.{\\m} changes the |eqtb|
-entry before the token list has been fully consumed, so we dare not simply
-destroy a token list when its control sequence is being redefined.
-
-If the parameter-matching part of a definition ends with `\.{\#\{}', the
-corresponding token list will have `\.\{' just before the `|end_match|' and also
-at the very end. The first `\.\{' is used to delimit the parameter; the second
-one keeps the first from disappearing.
-
-The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in symbolic form,
-including the expansion of a macro or mark.
-
-@c
+/*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)
{
- /* remap \mathchar onto \Umathchar */
-/*
+ /*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 {
- /* Show the meaning of a mark node */
+ /*tex Show the meaning of a mark node. */
if ((cur_cmd == top_bot_mark_cmd) && (cur_chr < marks_code)) {
print_char(':');
print_ln();
@@ -277,31 +324,35 @@ void print_meaning(void)
}
}
-@ 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\ }'.
-
-@c
+/*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: \
@@ -316,6 +367,18 @@ sequence named \.{BAD} would come out `\.{\\BAD\ }'.
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; \
@@ -323,18 +386,21 @@ sequence named \.{BAD} would come out `\.{\\BAD\ }'.
void show_token_list(int p, int q, int l)
{
- int m, c; /* pieces of a token */
- ASCII_code match_chr = '#'; /* character used in a `|match|' */
- ASCII_code n = '0'; /* the highest parameter number, as an ASCII digit */
+ /*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) {
- /* Do magic computation */
+ /*tex Do magic computation. */
set_trick_count();
}
- /* Display token |p|, and |return| if there are problems */
+ /*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;
@@ -349,11 +415,11 @@ void show_token_list(int p, int q, int l)
tprint_esc("BAD");
} else {
/*
- Display the token $(|m|,|c|)$
-
- The procedure usually ``learns'' the character code used for macro
- parameters by seeing one in a |match| command before it runs into any
+ 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:
@@ -405,31 +471,36 @@ void show_token_list(int p, int q, int l)
tprint_esc("ETC.");
}
-@ @c
#define do_buffer_to_unichar(a,b) do { \
a = (halfword)str2uni(buffer+b); \
b += utf8_size(a); \
} while (0)
-@ Here's the way we sometimes want to display a token list, given a pointer to
-its reference count; the pointer may be null.
+/*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.
+
+*/
-@c
void token_show(halfword p)
{
if (p != null)
show_token_list(token_link(p), null, 10000000);
}
-@ |delete_token_ref|, is called when a pointer to a token list's reference count
-is being removed. This means that the token list should disappear if the
-reference count was |null|, otherwise the count should be decreased by one.
-@^reference counts@>
+/*tex
-@ |p| points to the reference count of a token list that is losing one
-reference.
+ |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.
+
+*/
-@c
void delete_token_ref(halfword p)
{
if (token_ref_count(p) == 0)
@@ -438,7 +509,6 @@ void delete_token_ref(halfword p)
decr(token_ref_count(p));
}
-@ @c
int get_char_cat_code(int curchr)
{
int a;
@@ -446,7 +516,6 @@ int get_char_cat_code(int curchr)
return a;
}
-@ @c
static void invalid_character_error(void)
{
const char *hlp[] = {
@@ -459,10 +528,9 @@ static void invalid_character_error(void)
deletions_allowed = true;
}
-@ @c
-static boolean process_sup_mark(void); /* below */
+static boolean process_sup_mark(void);
-static int scan_control_sequence(void); /* below */
+static int scan_control_sequence(void);
typedef enum {
next_line_ok,
@@ -470,57 +538,83 @@ typedef enum {
next_line_restart
} next_line_retval;
-static next_line_retval next_line(void); /* below */
+static next_line_retval next_line(void);
-@ 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.
+/*tex
-If a match is found, the characters are effectively removed from the input and
-|true| is returned. Otherwise |false| is returned, and the input is left
-essentially unchanged (except for the fact that some macros may have been
-expanded, etc.). @^inner loop@>
+ 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.).
+
+*/
-@c
boolean scan_keyword(const char *s)
-{ /* look for a given string */
- halfword p; /* tail of the backup list */
- halfword q; /* new node being added to the token list via |store_new_token| */
- const char *k; /* index into |str_pool| */
+{
+ /*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) /* was assert (strlen(s) > 1); */
- return false ; /* but not with newtokenlib zero keyword simply doesn't match */
+ 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) {
- get_x_token(); /* recursion is possible here */
+ /*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)) {
- /*
- crashes on some alignments:
+ 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;
+}
- if (p != backup_head) {
- q = get_avail();
- token_info(q) = cur_tok;
- token_link(q) = null;
- token_link(p) = q;
- begin_token_list(token_link(backup_head), backed_up);
- } else {
- back_input();
- }
- */
+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;
}
@@ -531,36 +625,11 @@ boolean scan_keyword(const char *s)
return true;
}
-@ We can not return |undefined_control_sequence| under some conditions
- (inside |shift_case|, for example). This needs thinking.
+/*tex
-@c
+ We can not return |undefined_control_sequence| under some conditions (inside
+ |shift_case|, for example). This needs thinking.
-/*
- halfword active_to_cs(int curchr, int force)
- {
- halfword curcs;
- char *a, *b;
- char *utfbytes = xmalloc(8);
- int nncs = no_new_control_sequence;
- a = (char *) uni2str(0xFFFF);
- utfbytes = strcpy(utfbytes, a);
- if (force)
- no_new_control_sequence = false;
- if (curchr > 0) {
- b = (char *) uni2str((unsigned) curchr);
- utfbytes = strcat(utfbytes, b);
- free(b);
- curcs = string_lookup(utfbytes, strlen(utfbytes));
- } else {
- utfbytes[3] = '\0';
- curcs = string_lookup(utfbytes, 4);
- }
- no_new_control_sequence = nncs;
- free(a);
- free(utfbytes);
- return curcs;
- }
*/
/*static char * FFFF = "\xEF\xBF\xBF";*/ /* 0xFFFF */
@@ -581,7 +650,8 @@ halfword active_to_cs(int curchr, int force)
curcs = string_lookup(utfbytes, utf8_size(curchr)+3);
free(utfbytes);
} else {
- curcs = string_lookup("\xEF\xBF\xBF", 4); /* 0xFFFF ... why not 3 ? */
+ /*tex 0xFFFF ... why not 3 ? */
+ curcs = string_lookup("\xEF\xBF\xBF", 4);
}
no_new_control_sequence = nncs;
return curcs;
@@ -641,11 +711,12 @@ halfword active_to_cs(int curchr, int force)
*/
-@ TODO this function should listen to \.{\\escapechar}
+/*tex
-@ prints a control sequence
+ Maybe this function should listen to \.{\\escapechar} but we can do without.
+
+*/
-@c
static char *cs_to_string(halfword p)
{
const char *s;
@@ -687,9 +758,8 @@ static char *cs_to_string(halfword p)
return (char *) ret;
}
-@ TODO this is a quick hack, will be solved differently soon
+/*tex This is sort of a hack. */
-@c
static char *cmd_chr_to_string(int cmd, int chr)
{
char *s;
@@ -704,56 +774,76 @@ static char *cmd_chr_to_string(int cmd, int chr)
return s;
}
-@ The heart of \TeX's input mechanism is the |get_next| procedure, which we shall
-develop in the next few sections of the program. Perhaps we shouldn't actually
-call it the ``heart,'' however, because it really acts as \TeX's eyes and mouth,
-reading the source files and gobbling them up. And it also helps \TeX\ to
-regurgitate stored token lists that are to be processed again. @^eyes and mouth@>
-
-The main duty of |get_next| is to input one token and to set |cur_cmd| and
-|cur_chr| to that token's command code and modifier. Furthermore, if the input
-token is a control sequence, the |eqtb| location of that control sequence is
-stored in |cur_cs|; otherwise |cur_cs| is set to zero.
-
-Underlying this simple description is a certain amount of complexity because of
-all the cases that need to be handled. However, the inner loop of |get_next| is
-reasonably short and fast.
-
-When |get_next| is asked to get the next token of a \.{\\read} line,
-it sets |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens
-appear on that line. (There might not be any tokens at all, if the
-|end_line_char| has |ignore| as its catcode.)
-
-The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity is
-needed because a blank line of input is supposed to be exactly equivalent to the
-appearance of \.{\\par}; we must set |cur_cs:=par_loc| when detecting a blank
-line.
-
-@c
-halfword par_loc; /* location of `\.{\\par}' in |eqtb| */
-halfword par_token; /* token representing `\.{\\par}' */
-
-@ Parts |get_next| are executed more often than any other instructions of \TeX.
-@^mastication@>@^inner loop@>
-
-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|.
-
-@c
-boolean force_eof; /* should the next \.{\\input} be aborted early? */
-int luacstrings; /* how many lua strings are waiting to be input? */
-
-@ 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.
-
-@c
+/*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; /* an index into |buffer| */
+ int k;
ilimit = last;
if (pausing_par > 0) {
if (interaction > nonstop_mode) {
@@ -764,9 +854,10 @@ void firm_up_the_line(void)
print_char(buffer[k]);
}
first = ilimit;
- prompt_input("=>"); /* wait for user response */
+ prompt_input("=>");
if (last > first) {
- for (k = first; k < +last - 1; k++) /* move line down in buffer */
+ /*tex Move line down in buffer. */
+ for (k = first; k < +last - 1; k++)
buffer[k + istart - first] = buffer[k];
ilimit = istart + last - first;
}
@@ -774,31 +865,42 @@ void firm_up_the_line(void)
}
}
-@ 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.
+/*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.
+
+*/
-@c
void check_outer_validity(void)
{
- halfword p; /* points to inserted token list */
- halfword q; /* auxiliary pointer */
+ /*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;
- /* 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. */
+ /*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;
- begin_token_list(p, backed_up); /* prepare to read the control sequence again */
+ /*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 = ' '; /* replace it by a space */
+ cur_chr = ' ';
}
if (scanner_status > skipping) {
const char *errhlp[] = {
@@ -808,56 +910,64 @@ void check_outer_validity(void)
"you'd better type `E' or `X' now and fix your file.",
NULL
};
- char errmsg[256];
+ char errmsg[318];
const char *startmsg;
const char *scannermsg;
- /* Tell the user what has run away and try to recover */
- runaway(); /* print a definition, argument, or preamble */
+ /*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";
}
- /* 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}. */
+ /*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: /* can't happen */
- scannermsg = "unknown";
- break;
- } /*there are no other cases */
+ 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, 255, "%s while scanning %s of %s",
- startmsg, scannermsg, cs_to_string(warning_index));
+ snprintf(errmsg, 318, "%s while scanning %s of %s", startmsg, scannermsg, cs_to_string(warning_index));
tex_error(errmsg, errhlp);
} else {
char errmsg[256];
@@ -883,9 +993,9 @@ void check_outer_validity(void)
snprintf(errmsg, 255, "Incomplete %s; all text was ignored after line %d",
ss, (int) skip_line);
free(ss);
- /* Incomplete \\if... */
+ /*tex Incomplete |\if...| */
cur_tok = cs_token_flag + frozen_fi;
- /* back up one inserted token and call |error| */
+ /*tex back up one inserted token and call |error|. */
{
OK_to_interrupt = false;
back_input();
@@ -898,61 +1008,65 @@ void check_outer_validity(void)
}
}
-@ @c
-
#if 0
-/*
- 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.
+/*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) {
- /* current line not yet finished */
+ /*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.
+ 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:
+ Remark [ls/hh]: checking performance indicated that this switch was
+ the cause of many branch prediction errors but changing it to:
- 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;
+ \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.
- 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) {
@@ -961,13 +1075,13 @@ static boolean get_next_file(void)
case new_line + ignore_cmd:
case skip_blanks + spacer_cmd:
case new_line + spacer_cmd:
- /* Cases where character is ignored */
+ /*tex Cases where character is ignored. */
goto SWITCH;
break;
case mid_line + escape_cmd:
case new_line + escape_cmd:
case skip_blanks + escape_cmd:
- /* Scan a control sequence ...; */
+ /*tex Scan a control sequence. */
istate = (unsigned char) scan_control_sequence();
if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
check_outer_validity();
@@ -975,7 +1089,7 @@ static boolean get_next_file(void)
case mid_line + active_char_cmd:
case new_line + active_char_cmd:
case skip_blanks + active_char_cmd:
- /* Process an active-character */
+ /*tex Process an active-character. */
cur_cs = active_to_cs(cur_chr, false);
cur_cmd = eq_type(cur_cs);
cur_chr = equiv(cur_cs);
@@ -986,7 +1100,7 @@ static boolean get_next_file(void)
case mid_line + sup_mark_cmd:
case new_line + sup_mark_cmd:
case skip_blanks + sup_mark_cmd:
- /* If this |sup_mark| starts */
+ /*tex If this |sup_mark| starts. */
if (process_sup_mark())
goto RESWITCH;
else
@@ -995,22 +1109,27 @@ static boolean get_next_file(void)
case mid_line + invalid_char_cmd:
case new_line + invalid_char_cmd:
case skip_blanks + invalid_char_cmd:
- /* Decry the invalid character and |goto restart|; */
+ /*tex Decry the invalid character and |goto restart|. */
invalid_character_error();
- return false; /* because state may be |token_list| now */
+ /*tex Because state may be |token_list| now: */
+ return false;
break;
case mid_line + spacer_cmd:
- /* Enter |skip_blanks| state, emit a space; */
+ /*tex Enter |skip_blanks| state, emit a space. */
istate = skip_blanks;
cur_chr = ' ';
break;
case mid_line + car_ret_cmd:
- /*
- 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.
+ /*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;
@@ -1020,12 +1139,12 @@ static boolean get_next_file(void)
case mid_line + comment_cmd:
case new_line + comment_cmd:
case skip_blanks + comment_cmd:
- /* Finish line, |goto switch|; */
+ /*tex Finish line, |goto switch|; */
iloc = ilimit + 1;
goto SWITCH;
break;
case new_line + car_ret_cmd:
- /* Finish line, emit a \.{\\par}; */
+ /*tex Finish line, emit a \.{\\par}; */
iloc = ilimit + 1;
cur_cs = par_loc;
cur_cmd = eq_type(cur_cs);
@@ -1036,14 +1155,14 @@ static boolean get_next_file(void)
case skip_blanks + left_brace_cmd:
case new_line + left_brace_cmd:
istate = mid_line;
- /* fall through */
+ /*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;
- /* fall through */
+ /*tex Fall through. */
case mid_line + right_brace_cmd:
align_state--;
break;
@@ -1075,11 +1194,12 @@ static boolean get_next_file(void)
} else {
if (iname != 21)
istate = new_line;
- /*
- Move to next line of file,
- or |goto restart| if there is no next 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) {
@@ -1096,13 +1216,10 @@ static boolean get_next_file(void)
#else
-/* 10 times less Bim in callgrind */
+/*tex
+
+ This variant gives 10 times less Bim in vallgrind!
-/*
- escape_cmd left_brace_cmd right_brace_cmd math_shift_cmd
- tab_mark_cmd car_ret_cmd mac_param_cmd sup_mark_cmd
- sub_mark_cmd ignore_cmd spacer_cmd letter_cmd
- other_char_cmd active_char_cmd comment_cmd invalid_char_cmd
*/
static boolean get_next_file(void)
@@ -1110,7 +1227,7 @@ static boolean get_next_file(void)
int c = 0;
SWITCH:
if (iloc <= ilimit) {
- /* current line not yet finished */
+ /*tex current line not yet finished */
do_buffer_to_unichar(cur_chr, iloc);
RESWITCH:
if (detokenized_line()) {
@@ -1118,10 +1235,12 @@ static boolean get_next_file(void)
} else {
do_get_cat_code(cur_cmd, cur_chr);
}
- /*
- Change state if necessary, and |goto switch| if the current
- character should be ignored, or |goto reswitch| if the current
- character changes to another;
+ /*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)) {
@@ -1148,7 +1267,7 @@ static boolean get_next_file(void)
istate = mid_line;
return true;
case car_ret_cmd:
- /* Finish line, emit a \.{\\par}; */
+ /*tex Finish line, emit a \.{\\par}. */
iloc = ilimit + 1;
cur_cs = par_loc;
cur_cmd = eq_type(cur_cs);
@@ -1172,7 +1291,7 @@ static boolean get_next_file(void)
goto SWITCH;
return true;
case spacer_cmd:
- /* Cases where character is ignored */
+ /*tex Cases where character is ignored. */
goto SWITCH;
case letter_cmd:
istate = mid_line;
@@ -1192,8 +1311,9 @@ static boolean get_next_file(void)
iloc = ilimit + 1;
goto SWITCH;
case invalid_char_cmd:
+ /*tex Because state may be |token_list| now. */
invalid_character_error();
- return false; /* because state may be |token_list| now */
+ return false;
default:
istate = mid_line;
return true;
@@ -1201,7 +1321,7 @@ static boolean get_next_file(void)
} else if (c >= skip_blanks) {
switch (c-skip_blanks) {
case escape_cmd:
- /* Scan a control sequence ...; */
+ /*tex Scan a control sequence. */
istate = (unsigned char) scan_control_sequence();
if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd)
check_outer_validity();
@@ -1227,7 +1347,6 @@ static boolean get_next_file(void)
istate = mid_line;
return true;
case sup_mark_cmd:
- /* If this |sup_mark| starts */
if (process_sup_mark())
goto RESWITCH;
else
@@ -1255,13 +1374,14 @@ static boolean get_next_file(void)
check_outer_validity();
return true;
case comment_cmd:
- /* Finish line, |goto switch|; */
+ /*tex Finish line, |goto switch|. */
iloc = ilimit + 1;
goto SWITCH;
case invalid_char_cmd:
- /* Decry the invalid character and |goto restart|; */
+ /*tex Decry the invalid character and |goto restart|. */
invalid_character_error();
- return false; /* because state may be |token_list| now */
+ /*tex Because state may be |token_list| now. */
+ return false;
default:
istate = mid_line;
return true;
@@ -1284,13 +1404,17 @@ static boolean get_next_file(void)
case tab_mark_cmd:
return true;
case car_ret_cmd:
- /*
- 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.
- */
+ /*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 = ' ';
@@ -1308,7 +1432,7 @@ static boolean get_next_file(void)
case ignore_cmd:
goto SWITCH;
case spacer_cmd:
- /* Enter |skip_blanks| state, emit a space; */
+ /*tex Enter |skip_blanks| state, emit a space. */
istate = skip_blanks;
cur_chr = ' ';
return true;
@@ -1330,8 +1454,9 @@ static boolean get_next_file(void)
iloc = ilimit + 1;
goto SWITCH;
case invalid_char_cmd:
+ /*tex Because state may be |token_list| now. */
invalid_character_error();
- return false; /* because state may be |token_list| now */
+ return false;
default:
istate = mid_line;
return true;
@@ -1344,9 +1469,11 @@ static boolean get_next_file(void)
if (iname != 21) {
istate = new_line;
}
- /*
- Move to next line of file, or |goto restart| if there is no next line,
- or |return| if a \.{\\read} line has finished;
+ /*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();
@@ -1364,14 +1491,18 @@ static boolean get_next_file(void)
#endif
-@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
-We only support a limited set:
+/*tex
-^^^^^^XXXXXX
-^^^^XXXXXX
-^^XX ^^<char>
+ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
+ We only support a limited set:
-@c
+ \starttyping
+ ^^^^^^XXXXXX
+ ^^^^XXXXXX
+ ^^XX ^^<char>
+ \stoptyping
+
+*/
#define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f'))
@@ -1412,7 +1543,7 @@ static boolean process_sup_mark(void)
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])) {
- /* ^^^^^^XXXXXX */
+ /*tex |^^^^^^XXXXXX| */
if ((iloc + 10) <= ilimit) {
int c1 = buffer[iloc + 5];
int c2 = buffer[iloc + 6];
@@ -1432,7 +1563,7 @@ static boolean process_sup_mark(void)
tex_error("^^^^^^ needs six hex digits, end of input", NULL);
}
} else {
- /* ^^^^XXXX */
+ /*tex |^^^^XXXX| */
if ((iloc + 6) <= ilimit) {
int c1 = buffer[iloc + 3];
int c2 = buffer[iloc + 4];
@@ -1450,7 +1581,7 @@ static boolean process_sup_mark(void)
}
}
} else {
- /* ^^XX */
+ /*tex |^^XX| */
if ((iloc + 2) <= ilimit) {
int c1 = buffer[iloc + 1];
int c2 = buffer[iloc + 2];
@@ -1460,10 +1591,10 @@ static boolean process_sup_mark(void)
return true;
}
}
- /* go on, no error, good old tex */
+ /*tex Go on, no error, good old \TEX . */
}
}
- /* the rest */
+ /*tex The rest. */
{
int c1 = buffer[iloc + 1];
if (c1 < 0200) {
@@ -1484,37 +1615,43 @@ static boolean process_sup_mark(void)
return false;
}
-@ 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.
+/*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]|.
+ 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.
+ 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.
+
+*/
-@c
static boolean check_expanded_code(int *kk); /* below */
static int scan_control_sequence(void)
{
int retval = mid_line;
if (iloc > ilimit) {
- cur_cs = null_cs; /* |state| is irrelevant in this case */
+ /*tex |state| is irrelevant in this case. */
+ cur_cs = null_cs;
} else {
- register int cat; /* |cat_code(cur_chr)|, usually */
+ /*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);
- if (cat == sup_mark_cmd && check_expanded_code(&k)) /* If an expanded...; */
+ /*tex If an expanded \unknown */
+ if (cat == sup_mark_cmd && check_expanded_code(&k))
continue;
} else {
retval = skip_blanks;
@@ -1522,20 +1659,11 @@ static int scan_control_sequence(void)
do_buffer_to_unichar(cur_chr, k);
do_get_cat_code(cat, cur_chr);
} while (cat == letter_cmd && k <= ilimit);
-
- if (cat == sup_mark_cmd && check_expanded_code(&k)) /* If an expanded...; */
+ /*tex If an expanded \unknown */
+ if (cat == sup_mark_cmd && check_expanded_code(&k))
continue;
if (cat != letter_cmd) {
- /* backtrack one character which can be utf */
- /*
- decr(k);
- if (cur_chr > 0xFFFF)
- decr(k);
- if (cur_chr > 0x7FF)
- decr(k);
- if (cur_chr > 0x7F)
- decr(k);
- */
+ /*tex Backtrack one character which can be \UTF. */
if (cur_chr <= 0x7F) {
k -= 1; /* in most cases */
} else if (cur_chr > 0xFFFF) {
@@ -1545,7 +1673,7 @@ static int scan_control_sequence(void)
} else /* if (cur_chr > 0x7F) */ {
k -= 2;
}
- /* now |k| points to first nonletter */
+ /*tex Now |k| points to first nonletter. */
}
}
cur_cs = id_lookup(iloc, k - iloc);
@@ -1558,14 +1686,17 @@ static int scan_control_sequence(void)
return retval;
}
-@ 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.
+/*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.
+
+*/
-@c
static boolean check_expanded_code(int *kk)
{
int l;
@@ -1666,26 +1797,38 @@ static boolean check_expanded_code(int *kk)
return false;
}
-@ All of the easy branches of |get_next| have now been taken care of. There is
-one more branch.
+/*tex
-@c static next_line_retval next_line(void)
+ 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)
{
- boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */
+ /*tex A way to end a pseudo file without trailing space: */
+ boolean inhibit_eol = false;
if (iname > 17) {
- /* Read next line of file into |buffer|, or |goto restart| if the file has ended */
+ /*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()) { /* not end of file */
- firm_up_the_line(); /* this sets |ilimit| */
+ 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;
- eof_seen[iindex] = true; /* fake one empty line */
+ /*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;
@@ -1694,30 +1837,58 @@ one more branch.
}
} else {
if (iname == 21) {
- if (luacstring_input()) { /* not end of strings */
- 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;
- } else {
- force_eof = true;
+ 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 {
- if (lua_input_ln(cur_file, 0, true)) { /* not end of file */
- firm_up_the_line(); /* this sets |ilimit| */
- line_catcode_table = DEFAULT_CAT_TABLE;
- } else if ((every_eof_par != null) && (!eof_seen[iindex])) {
- ilimit = first - 1;
- eof_seen[iindex] = true; /* fake one empty line */
- begin_token_list(every_eof_par, every_eof_text);
- return next_line_restart;
- } else {
- force_eof = true;
- }
+ force_eof = true;
}
}
}
@@ -1725,7 +1896,7 @@ one more branch.
if (tracing_nesting_par > 0)
if ((grp_stack[in_open] != cur_boundary) || (if_stack[in_open] != cond_ptr))
if (!((iname == 19) || (iname == 21))) {
- /* give warning for some unfinished groups and/or conditionals */
+ /*tex Give warning for some unfinished groups and/or conditionals. */
file_warning();
}
if ((iname > 21) || (iname == 20)) {
@@ -1733,7 +1904,7 @@ one more branch.
decr(open_parens);
}
force_eof = false;
- /* lua input or \.{\\scantextokens} */
+ /*tex \LUA\ input or \.{\\scantextokens} */
if (iname == 21 || iname == 19) {
end_file_reading();
} else {
@@ -1748,18 +1919,20 @@ one more branch.
else
buffer[ilimit] = (packed_ASCII_code) end_line_char_par;
first = ilimit + 1;
- iloc = istart; /* ready to read */
+ iloc = istart;
+ /*tex We're ready to read. */
} else {
if (!terminal_input) {
- /* \.{\\read} line has ended */
+ /*tex \.{\\read} line has ended */
cur_cmd = 0;
cur_chr = 0;
- return next_line_return; /* OUTER */
+ return next_line_return;
}
if (input_ptr > 0) {
- /* text was inserted during error recovery */
+ /*tex Text was inserted during error recovery. */
end_file_reading();
- return next_line_restart; /* resume previous level */
+ /*tex Resume previous level. */
+ return next_line_restart;
}
if (selector < log_only)
open_log_file();
@@ -1767,12 +1940,13 @@ one more branch.
if (end_line_char_inactive)
ilimit++;
if (ilimit == istart) {
- /* previous line was empty */
+ /*tex Previous line was empty. */
tprint_nl("(Please type a command or say `\\end')");
}
print_ln();
first = istart;
- prompt_input("*"); /* input on-line into |buffer| */
+ /*tex Input on-line into |buffer| */
+ prompt_input("*");
ilimit = last;
if (end_line_char_inactive)
ilimit--;
@@ -1781,9 +1955,11 @@ one more branch.
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)");
}
@@ -1791,24 +1967,31 @@ one more branch.
return next_line_ok;
}
-@ Let's consider now what happens when |get_next| is looking at a token list.
+/*tex
+
+ Let's consider now what happens when |get_next| is looking at a token list.
+
+*/
-@c
static boolean get_next_tokenlist(void)
{
register halfword t = token_info(iloc);
- iloc = token_link(iloc); /* move to next */
+ /*tex Move to next. */
+ iloc = token_link(iloc);
if (t >= cs_token_flag) {
- /* a control sequence token */
+ /*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) {
- /*
- 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|.
+ /*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;
@@ -1834,7 +2017,7 @@ static boolean get_next_tokenlist(void)
align_state--;
break;
case out_param_cmd:
- /* Insert macro parameter and |goto restart|; */
+ /*tex Insert macro parameter and |goto restart|. */
begin_token_list(param_stack[param_start + cur_chr - 1], parameter);
return false;
break;
@@ -1843,61 +2026,73 @@ static boolean get_next_tokenlist(void)
return true;
}
-@ Now we're ready to take the plunge into |get_next| itself. Parts of this
-routine are executed more often than any other instructions of \TeX.
-@^mastication@>@^inner loop@>
+/*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.
-@ sets |cur_cmd|, |cur_chr|, |cur_cs| to next token
+ This sets |cur_cmd|, |cur_chr|, |cur_cs| to next token:
+
+*/
-@c
void get_next(void)
{
RESTART:
cur_cs = 0;
if (istate != token_list) {
- /* Input from external file, |goto restart| if no input found */
+ /*tex Input from external file, |goto restart| if no input found. */
if (!get_next_file())
goto RESTART;
} else {
if (iloc == null) {
end_token_list();
- goto RESTART; /* list exhausted, resume previous level */
+ /*tex List exhausted, resume previous level. */
+ goto RESTART;
} else if (!get_next_tokenlist()) {
- goto RESTART; /* parameter needs to be expanded */
+ /*tex Parameter needs to be expanded. */
+ goto RESTART;
}
}
- /* If an alignment entry has just ended, take appropriate action */
+ /*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;
}
}
-@ Since |get_next| is used so frequently in \TeX, it is convenient to define
-three related procedures that do a little more:
-
-\yskip\hang|get_token| not only sets |cur_cmd| and |cur_chr|, it also sets
-|cur_tok|, a packed halfword version of the current token.
-
-\yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like |get_token|,
-but if the current token turns out to be a user-defined control sequence (i.e., a
-macro call), or a conditional, or something like \.{\\topmark} or
-\.{\\expandafter} or \.{\\csname}, it is eliminated from the input by beginning
-the expansion of the macro or the evaluation of the conditional.
-
-\yskip\hang|x_token| is like |get_x_token| except that it assumes that |get_next|
-has already been called.
-
-\yskip\noindent In fact, these three procedures account for almost every use of
-|get_next|.
+/*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|:
-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.
-
-@ sets |cur_cmd|, |cur_chr|, |cur_tok|
+*/
-@c
void get_token(void)
{
no_new_control_sequence = false;
@@ -1909,14 +2104,16 @@ void get_token(void)
cur_tok = cs_token_flag + cur_cs;
}
-@ changes the string |s| to a token list
+/*tex This changes the string |s| to a token list. */
-@c
halfword string_to_toks(const char *ss)
{
- halfword p; /* tail of the token list */
- halfword q; /* new node being added to the token list via |store_new_token| */
- halfword t; /* token being appended */
+ /*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;
@@ -1933,29 +2130,37 @@ halfword string_to_toks(const char *ss)
return token_link(temp_token_head);
}
-@ The token lists for macros and for other things like \.{\\mark} and
-\.{\\output} and \.{\\write} are produced by a procedure called |scan_toks|.
+/*tex
-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 lists for macros and for other things like \.{\\mark} and
+ \.{\\output} and \.{\\write} are produced by a procedure called |scan_toks|.
-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.)
+ 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|.
-|lua_str_toks| is almost identical, but it also escapes the three symbols that
-|lua| considers special while scanning a literal string
+ 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.)
-@ changes the string |str_pool[b..pool_ptr]| to a token list
+ |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:
+
+*/
-@c
halfword lua_str_toks(lstring b)
{
- halfword p; /* tail of the token list */
- halfword q; /* new node being added to the token list via |store_new_token| */
- halfword t; /* token being appended */
- unsigned char *k; /* index into string */
+ /*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;
@@ -1978,18 +2183,25 @@ halfword lua_str_toks(lstring b)
return p;
}
-@ Incidentally, the main reason for wanting |str_toks| is the function
-|the_toks|, which has similar input/output characteristics.
+/*tex
+
+ Incidentally, the main reason for wanting |str_toks| is the function
+ |the_toks|, which has similar input/output characteristics.
-@ changes the string |str_pool[b..pool_ptr]| to a token list
+ This changes the string |str_pool[b..pool_ptr]| to a token list:
+
+*/
-@c
halfword str_toks(lstring s)
{
- halfword p; /* tail of the token list */
- halfword q; /* new node being added to the token list via |store_new_token| */
- halfword t; /* token being appended */
- unsigned char *k, *l; /* index into string */
+ /*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;
@@ -2006,18 +2218,25 @@ halfword str_toks(lstring s)
return p;
}
-/*
- hh: 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
+
+ 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)
-{ /* changes the string |str_pool[b..pool_ptr]| to a token list */
- halfword p; /* tail of the token list */
- halfword q; /* new node being added to the token list via |store_new_token| */
- halfword t; /* token being appended */
- unsigned char *k, *l; /* index into string */
+{
+ /*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);
@@ -2028,7 +2247,7 @@ halfword str_scan_toks(int ct, lstring s)
k += utf8_size(t);
cc = get_cat_code(ct,t);
if (cc == 0) {
- /* we have a potential control sequence so we check for it */
+ /*tex We have a potential control sequence so we check for it. */
int _lname = 0 ;
int _s = 0 ;
int _c = 0 ;
@@ -2042,7 +2261,7 @@ halfword str_scan_toks(int ct, lstring s)
k += _s ;
_lname = _lname + _s ;
} else if (_c == 10) {
- /* we ignore a trailing space like normal scanning does */
+ /*tex We ignore a trailing space like normal scanning does. */
k += _s ;
break ;
} else {
@@ -2050,7 +2269,7 @@ halfword str_scan_toks(int ct, lstring s)
}
}
if (_s > 0) {
- /* we have a potential \cs */
+ /*tex We have a potential |\cs|. */
_cs = string_lookup((const char *) _name, _lname);
if (_cs == undefined_control_sequence) {
/* let's play safe and backtrack */
@@ -2060,15 +2279,24 @@ halfword str_scan_toks(int ct, lstring s)
t = cs_token_flag + _cs;
}
} else {
- /* just a character with some meaning, so \unknown becomes effectively */
- /* \\unknown assuming that \\ has some useful meaning of course */
+ /*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 {
- /* whatever token, so for instance $x^2$ just works given a tex */
- /* catcode regime */
+ /*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);
@@ -2077,171 +2305,227 @@ halfword str_scan_toks(int ct, lstring s)
return p;
}
-@ Here's part of the |expand| subroutine that we are now ready to complete:
+/*tex
+
+ Here's part of the |expand| subroutine that we are now ready to complete:
+
+*/
-@c
void ins_the_toks(void)
{
(void) the_toks();
ins_list(token_link(temp_token_head));
}
-#define set_toks_register(n,t,g) { \
+#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
+
+*/
void combine_the_toks(int how)
{
- halfword nt;
+ 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();
- /* target */
+ /*tex The target. */
if (cur_cmd == assign_toks_cmd) {
+ /*tex Check range. */
nt = equiv(cur_cs) - toks_base;
- /* check range */
} else {
back_input();
- scan_int();
+ scan_register_num();
nt = cur_val;
}
- /* source */
+ /*tex The source. */
do {
get_x_token();
} while (cur_cmd == spacer_cmd);
if (cur_cmd == left_brace_cmd) {
- halfword x, source;
back_input();
- x = scan_toks(false,how > 1); /* expanded or not */
+ scan_toks(false,expand);
source = def_ref;
- /* action */
+ /*tex The action. */
if (source != null) {
- halfword target = toks(nt);
+ target = toks(nt);
if (target == null) {
- set_toks_register(nt,source,0);
+ set_toks_register(nt,source,global);
} else {
- halfword s = token_link(source);
+ s = token_link(source);
if (s != null) {
- halfword t = token_link(target);
+ t = token_link(target);
if (t == null) {
- /* can this happen ? */
+ /*tex Can this happen? */
set_token_link(target, s);
- } else if (odd(how)) {
- /* prepend */
- if (cur_level != eq_level_field(eqtb[toks_base+nt])) {
- halfword p = temp_token_head;
- halfword q;
- set_token_link(p, s); /* s = head, x = tail */
- p = x;
- while (t != null) {
- fast_store_new_token(token_info(t));
- t = token_link(t);
+ } else if (append) {
+ /*tex Append. */
+ if (token_ref_count(target) == 0) {
+ 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);
}
- set_toks_register(nt,temp_token_head,0);
} else {
- set_token_link(x,t);
- set_token_link(target,s);
+ token_ref_count(target)--;
+ append_copied_toks_list(t,s);
+ set_toks_register(nt,temp_token_head,global);
}
} else {
- /* append */
- if (cur_level != eq_level_field(eqtb[toks_base+nt])) {
- halfword p = temp_token_head;
- halfword q;
- set_token_link(p, null);
- while (t != null) {
- fast_store_new_token(token_info(t));
- t = token_link(t);
+ /* prepend */
+ if (token_ref_count(target) == 0) {
+ 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,s);
- set_toks_register(nt,temp_token_head,0);
+ set_token_link(p,t);
+ set_token_link(target,h);
} else {
- while (token_link(t) != null) {
- t = token_link(t);
- }
- set_token_link(t,s);
+ token_ref_count(target)--;
+ append_copied_toks_list(s,t);
+ set_toks_register(nt,temp_token_head,global);
}
}
}
}
}
} else {
- halfword source, ns;
if (cur_cmd == assign_toks_cmd) {
ns = equiv(cur_cs) - toks_base;
- /* check range */
+ /*tex Check range. */
} else {
- back_input();
- scan_int();
+ scan_register_num();
ns = cur_val;
}
- /* action */
+ /*tex The action. */
source = toks(ns);
if (source != null) {
- halfword target = toks(nt);
+ target = toks(nt);
if (target == null) {
+ /*tex The assign. */
+ token_ref_count(source)++;
equiv(toks_base+nt) = source;
- equiv(toks_base+ns) = null;
+ return;
+ }
+ s = token_link(source);
+ t = token_link(target);
+ if (append) {
+ /*tex Append. */
+ if (token_ref_count(target) == 0) {
+ 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 {
+ token_ref_count(target)--;
+ append_copied_toks_list(t,s);
+ set_toks_register(nt,temp_token_head,global);
+ }
} else {
- halfword s = token_link(source);
- if (s != null) {
- halfword t = token_link(target);
- if (t == null) {
- set_token_link(target, s);
- } else if (odd(how)) {
- /* prepend */
- halfword x = s;
- while (token_link(x) != null) {
- x = token_link(x);
- }
- set_token_link(x,t);
- set_token_link(target,s);
- } else {
- /* append */
- while (token_link(t) != null) {
- t = token_link(t);
+ /*tex Prepend. */
+ if (token_ref_count(target) == 0) {
+ h = null;
+ p = null;
+ while (s != null) {
+ fast_store_new_token(token_info(s));
+ if (h == null) {
+ h = p;
}
- set_token_link(t,s);
+ s = token_link(s);
}
- equiv(toks_base+ns) = null;
+ set_token_link(p,t);
+ set_token_link(target,h);
+ } else {
+ token_ref_count(target)--;
+ append_copied_toks_list(s,t);
+ set_toks_register(nt,temp_token_head,global);
}
}
}
}
}
-@ This routine, used in the next one, prints the job name, possibly modified by
-the |process_jobname| callback.
+/*tex
+
+ This routine, used in the next one, prints the job name, possibly modified by
+ the |process_jobname| callback.
+
+*/
-@c
static void print_job_name(void)
{
- if (job_name) {
- char *s, *ss; /* C strings for jobname before and after processing */
- 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);
- }
+ 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);
+ }
}
-@ 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.
+/*tex
-@ 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.
+ 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.
-@c
+ 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)
{
@@ -2263,15 +2547,18 @@ int scan_lua_state(void)
return sn;
}
-@ 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}'.
+/*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.
+ 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.
+
+*/
-@c
#define push_selector { \
old_setting = selector; \
selector = new_string; \
@@ -2327,6 +2614,8 @@ static int do_variable_pdf(halfword c)
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); }
@@ -2353,23 +2642,30 @@ static int do_feedback_dvi(halfword c)
return 0;
}
-/* codes not really needed but cleaner when testing */
+/*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)
{
- int old_setting; /* holds |selector| setting */
- int save_scanner_status; /* |scanner_status| upon entry */
- halfword save_def_ref; /* |def_ref| upon entry, important if inside `\.{\\message}' */
+ /*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;
- boolean bool; /* temp boolean */
- str_number s; /* first temp string */
- int ff; /* for use with |set_ff| */
- str_number u = 0; /* third temp string, will become non-nil if a string is already being built */
- char *str; /* color stack init str */
-
+ /*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);
@@ -2394,7 +2690,7 @@ static int do_feedback_pdf(halfword c)
pop_selector;
} else if (scan_keyword("creationdate")) {
ins_list(string_to_toks(getcreationdate(static_pdf)));
- /* no further action */
+ /*tex No further action. */
return 2;
} else if (scan_keyword("fontname")) {
scan_font_ident();
@@ -2488,19 +2784,27 @@ static int do_feedback_pdf(halfword c)
void conv_toks(void)
{
- int old_setting; /* holds |selector| setting */
+ /*tex holds |selector| setting */
+ int old_setting;
halfword p, q;
- int save_scanner_status; /* |scanner_status| upon entry */
- halfword save_def_ref; /* |def_ref| upon entry, important if inside `\.{\\message}' */
+ /*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;
- boolean bool; /* temp boolean */
- str_number s; /* first temp string */
- int sn; /* lua chunk name */
- str_number u = 0; /* third temp string, will become non-nil if a string is already being built */
- int c = cur_chr; /* desired type of conversion */
+ /*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;
- /* Scan the argument for command |c| */
+ /*tex Scan the argument for command |c|. */
switch (c) {
case number_code:
scan_int();
@@ -2520,7 +2824,20 @@ void conv_toks(void)
if (luacstrings > 0)
lua_string_start();
}
- /* no further action */
+ 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:
@@ -2537,10 +2854,10 @@ void conv_toks(void)
luacstrings = 0;
luatokencall(s, sn);
delete_token_ref(s);
- restore_cur_string(u); /* TODO: check this, was different */
+ restore_cur_string(u);
if (luacstrings > 0)
lua_string_start();
- /* no further action */
+ /*tex No further action. */
return;
break;
case expanded_code:
@@ -2554,7 +2871,56 @@ void conv_toks(void)
ins_list(token_link(def_ref));
def_ref = save_def_ref;
restore_cur_string(u);
- /* no further action */
+ /*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:
@@ -2629,7 +2995,7 @@ void conv_toks(void)
free(escstr.s);
return;
}
- /* no further action */
+ /*tex no further action */
break;
case font_id_code:
scan_font_ident();
@@ -2672,9 +3038,12 @@ void conv_toks(void)
p = list_ptr(box(cur_val));
if (p != null) {
p = tail_of_list(p);
- /*
- 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
+ /*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);
@@ -2685,10 +3054,12 @@ void conv_toks(void)
if ((q != null) && ((type(q) == margin_kern_node) && (subtype(q) == right_side))) {
p = q;
} else {
- /*
- 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
+ /*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.
*/
}
}
@@ -2832,7 +3203,7 @@ void do_feedback(void)
return ;
}
if (done==0) {
- /* we recover */
+ /*tex We recover. */
normal_warning("pdf backend","unexpected use of \\pdffeedback");
return;
} else if (done==2) {
@@ -2857,7 +3228,7 @@ void do_variable(void)
case dvi_variable_code:
done = do_variable_dvi(c);
if (done==0) {
- /* we recover */
+ /*tex We recover. */
normal_warning("dvi backend","unexpected use of \\dvivariable");
}
return;
@@ -2865,7 +3236,7 @@ void do_variable(void)
case pdf_variable_code:
done = do_variable_pdf(c);
if (done==0) {
- /* we recover */
+ /*tex We recover. */
normal_warning("pdf backend","unexpected use of \\pdfvariable");
}
return;
@@ -2876,33 +3247,8 @@ void do_variable(void)
}
}
-/*
- The following code is not used as we can only set math options and not query them. If
- an option is really important we will provide a proper variable. Most options are not
- meant for users anyway but for development.
-*/
-
-/*
+/*tex This boolean is keeping track of the lua string escape state */
-#define do_mathoption_int(i) \
- cur_cmd = assign_int_cmd; \
- cur_val = mathoption_int_base + i; \
- cur_tok = token_val(cur_cmd, cur_val); \
- back_input();
-
-void do_mathoption(void)
-{
- if (scan_keyword("old")) { do_mathoption_int(c_mathoption_no_italic_compensation_code); }
- if (scan_keyword("noitaliccompensation")) { do_mathoption_int(c_mathoption_no_char_italic_code); }
- else if (scan_keyword("nocharitalic")) { do_mathoption_int(c_mathoption_use_old_fraction_scaling_code); }
- else if (scan_keyword("useoldfractionscaling")) { do_mathoption_int(c_mathoption_old_code); }
- else if (scan_keyword("umathcodemeaning")) { do_mathoption_int(c_mathoption_umathcode_meaning_code); }
-}
-
-*/
-
-@ This boolean is keeping track of the lua string escape state
-@c
boolean in_lua_escape;
static int the_convert_string_dvi(halfword c, int i)
@@ -2948,7 +3294,7 @@ static int the_convert_string_pdf(halfword c, int i)
str_number the_convert_string(halfword c, int i)
{
- int old_setting; /* saved |selector| setting */
+ int old_setting;
str_number ret = 0;
boolean done = true ;
old_setting = selector;
@@ -3017,14 +3363,14 @@ str_number the_convert_string(halfword c, int i)
case font_identifier_code:
print_font_identifier(i);
break;
- /* backend: this might become obsolete */
+ /*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;
- /* done */
+ /*tex done */
default:
done = false;
break;
@@ -3036,16 +3382,24 @@ str_number the_convert_string(halfword c, int i)
return ret;
}
-@ 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
+
+ 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];
-@c
-FILE *read_file[16]; /* used for \.{\\read} */
-int read_open[17]; /* state of |read_file[n]| */
+/*tex state of |read_file[n]| */
+
+int read_open[17];
void initialize_read(void)
{
@@ -3054,41 +3408,51 @@ void initialize_read(void)
read_open[k] = closed;
}
-@ The |read_toks| procedure constructs a token list like that for any macro
-definition, and makes |cur_val| point to it. Parameter |r| points to the control
-sequence that will receive this token list.
+/*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.
+
+*/
-@c
void read_toks(int n, halfword r, halfword j)
{
- halfword p; /* tail of the token list */
- halfword q; /* new node being added to the token list via |store_new_token| */
- int s; /* saved value of |align_state| */
- int m; /* stream number */
+ /*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);
- p = def_ref; /* the reference count */
+ /*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;
- align_state = 1000000; /* disable tab marks, etc. */
+ /*tex disable tab marks, etc. */
+ align_state = 1000000;
do {
- /* Input and store tokens from the next line of the file */
+ /*tex Input and store tokens from the next line of the file. */
begin_file_reading();
iname = m + 1;
if (read_open[m] == closed) {
- /*
- Input for \.{\\read} from the terminal
+ /*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.
- Here we input on-line into the |buffer| array, prompting the user explicitly
- if |n>=0|. The value of |n| is set negative so that additional prompts
- will not be given in the case of multi-line input.
*/
if (interaction > nonstop_mode) {
if (n < 0) {
@@ -3106,11 +3470,12 @@ void read_toks(int n, halfword r, halfword j)
}
} else if (read_open[m] == just_open) {
- /*
- Input the first line of |read_file[m]|
+ /*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|.
- 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;
@@ -3120,10 +3485,11 @@ void read_toks(int n, halfword r, halfword j)
}
} else {
- /*
- Input the next line of |read_file[m]|
+ /*tex
+
+ Input the next line of |read_file[m]|. An empty line is appended
+ at the end of a |read_file|.
- 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));
@@ -3146,10 +3512,10 @@ void read_toks(int n, halfword r, halfword j)
first = ilimit + 1;
iloc = istart;
istate = new_line;
- /* Handle \.{\\readline} and |goto done|; */
+ /*tex Handle \.{\\readline} and |goto done|. */
if (j == 1) {
while (iloc <= ilimit) {
- /* current line not yet finished */
+ /*tex Current line not yet finished. */
do_buffer_to_unichar(cur_chr, iloc);
if (cur_chr == ' ')
cur_tok = space_token;
@@ -3161,11 +3527,11 @@ void read_toks(int n, halfword r, halfword j)
while (1) {
get_token();
if (cur_tok == 0) {
- /* |cur_cmd=cur_chr=0| will occur at the end of the line */
+ /*tex |cur_cmd=cur_chr=0| will occur at the end of the line. */
break;
}
if (align_state < 1000000) {
- /* unmatched `\.\}' aborts the line */
+ /*tex Unmatched right brace aborts the line. */
do {
get_token();
} while (cur_tok != 0);
@@ -3183,9 +3549,8 @@ void read_toks(int n, halfword r, halfword j)
align_state = s;
}
-@ return a string from tokens list
+/*tex Return a string from tokens list: */
-@c
str_number tokens_to_string(halfword p)
{
int old_setting;
@@ -3198,11 +3563,20 @@ str_number tokens_to_string(halfword p)
return make_string();
}
-@ @c
+/*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+64)); \
- alloci = alloci + 64; \
+ ret = xrealloc(ret,(alloci+alloci_step)); \
+ alloci = alloci + alloci_step; \
}
#define append_i_byte(a) ret[i++] = (char)(a)
@@ -3247,11 +3621,14 @@ str_number tokens_to_string(halfword p)
#define is_cat_letter(a) \
(get_char_cat_code(pool_to_unichar(str_string((a)))) == 11)
-@ 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.
+/*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.
+
+*/
-@c
char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz)
{
register int p, c, m;
@@ -3262,7 +3639,7 @@ char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz)
char *ret;
int match_chr = '#';
int n = '0';
- unsigned alloci = 1024;
+ unsigned alloci = alloci_default;
int i = 0;
p = pp;
if (p == null) {
@@ -3271,7 +3648,8 @@ char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz)
return NULL;
}
ret = xmalloc(alloci);
- p = token_link(p); /* skip refcount */
+ /*tex Skip refcount. */
+ p = token_link(p);
if (p != null) {
e = escape_char_par;
}
@@ -3389,7 +3767,7 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz)
char *ret;
int match_chr = '#';
int n = '0';
- unsigned alloci = 1024;
+ unsigned alloci = alloci_default;
int i = 0;
int skipping = 1;
p = pp;
@@ -3399,13 +3777,14 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz)
return NULL;
}
ret = xmalloc(alloci);
- p = token_link(p); /* skip refcount */
+ /*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) {
- /* nothing */
+ /*tex Nothing done. */
break;
}
infop = token_info(p);
@@ -3413,13 +3792,12 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz)
if (!(inhibit_par && infop == par_token)) {
q = infop - cs_token_flag;
if (q < hash_base) {
- /* nothing */
+ /*tex Nothing done. */
} else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) {
- /* nothing */
+ /*tex Nothing done. */
} else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) {
- /* nothing */
- } else {
-if (!skipping) {
+ /*tex Nothing done. */
+ } else if (!skipping) {
str_number txt = cs_text(q);
sh = makecstring(txt);
s = sh;
@@ -3440,12 +3818,11 @@ if (!skipping) {
}
}
free(sh);
-}
}
}
} else {
if (infop < 0) {
- /* nothing */
+ /*tex Nothing done. */
} else {
m = token_cmd(infop);
c = token_chr(infop);
@@ -3459,54 +3836,54 @@ if (!skipping) {
case spacer_cmd:
case letter_cmd:
case other_char_cmd:
-if (!skipping) {
- Print_uchar(c);
-}
+ if (!skipping) {
+ Print_uchar(c);
+ }
break;
case mac_param_cmd:
-if (!skipping) {
- if (!in_lua_escape && (is_in_csname==0))
+ if (!skipping) {
+ if (!in_lua_escape && (is_in_csname==0))
+ Print_uchar(c);
Print_uchar(c);
- Print_uchar(c);
-}
+ }
break;
case out_param_cmd:
-if (!skipping) {
- Print_uchar(match_chr);
-}
+ if (!skipping) {
+ Print_uchar(match_chr);
+ }
if (c <= 9) {
-if (!skipping) {
- Print_char(c + '0');
-}
+ if (!skipping) {
+ Print_char(c + '0');
+ }
} else {
- /* nothing */
+ /*tex Nothing done. */
goto EXIT;
}
break;
case match_cmd:
match_chr = c;
-if (!skipping) {
- Print_uchar(c);
-}
+ if (!skipping) {
+ Print_uchar(c);
+ }
n++;
-if (!skipping) {
- Print_char(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('>');
-}
+ if (!skipping) {
+ Print_char('-');
+ Print_char('>');
+ }
i = 0;
-skipping = 0 ;
+ skipping = 0 ;
}
break;
default:
- /* nothing */
+ /*tex Nothing done. */
break;
}
}
@@ -3520,7 +3897,6 @@ skipping = 0 ;
return ret;
}
-@ @c
lstring *tokenlist_to_lstring(int pp, int inhibit_par)
{
int siz;
@@ -3530,7 +3906,6 @@ lstring *tokenlist_to_lstring(int pp, int inhibit_par)
return ret;
}
-@ @c
void free_lstring(lstring * ls)
{
if (ls == NULL)