summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/luatexdir/tex
diff options
context:
space:
mode:
authorMartin Schröder <martin@oneiros.de>2008-03-24 15:47:15 +0000
committerMartin Schröder <martin@oneiros.de>2008-03-24 15:47:15 +0000
commit0b5550c241229764120d133af80a0cbab5937ddd (patch)
tree81309c18d6f0deba7407813cf15b9add2db375d1 /Build/source/texk/web2c/luatexdir/tex
parent51f55df6a36ce376952a1f2b30bdb4dd0e06548d (diff)
First step of importing luatex (up to r1121)
git-svn-id: svn://tug.org/texlive/trunk@7124 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex')
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/linebreak.c2095
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/postlinebreak.c512
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texnodes.c1694
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/texpdf.c169
-rw-r--r--Build/source/texk/web2c/luatexdir/tex/textoken.c753
5 files changed, 5223 insertions, 0 deletions
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..910d26b9f27
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.c
@@ -0,0 +1,2095 @@
+/* $Id: linebreak.c 1102 2008-03-11 09:52:54Z taco $ */
+
+#include "luatex-api.h"
+#include <ptexlib.h>
+#include "nodes.h"
+
+/* 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))
+
+static boolean no_shrink_error_yet; /*have we complained about infinite shrinkage?*/
+
+halfword
+finite_shrink(halfword p) { /* recovers from infinite shrinkage */
+ halfword q; /*new glue specification*/
+ 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);
+ }
+ q=new_spec(p); shrink_order(q)=normal;
+ delete_glue_ref(p);
+ return q;
+}
+
+/* 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|.
+*/
+
+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 integer threshold; /* maximum badness on feasible lines */
+
+/* skipable nodes at the margins during character protrusion */
+
+#define cp_skipable(a) ((! is_char_node((a))) && \
+ ((type((a)) == ins_node) \
+ || (type((a)) == mark_node) \
+ || (type((a)) == adjust_node) \
+ || (type((a)) == penalty_node) \
+ || ((type((a)) == whatsit_node) && \
+ (subtype((a)) != pdf_refximage_node) && \
+ (subtype((a)) != pdf_refxform_node)) \
+ /* reference to an image or XObject form */ \
+ || ((type((a)) == disc_node) && \
+ (vlink_pre_break(a) == null) && \
+ (vlink_post_break(a) == null) && \
+ (vlink_no_break(a) == null)) \
+ /* an empty |disc_node| */ \
+ || ((type((a)) == math_node) && \
+ (surround((a)) == 0)) \
+ || ((type((a)) == kern_node) && \
+ ((width((a)) == 0) || \
+ (subtype((a)) == normal))) \
+ || ((type((a)) == glue_node) && \
+ (glue_ptr((a)) == zero_glue)) \
+ || ((type((a)) == hlist_node) && \
+ (width((a)) == 0) && \
+ (height((a)) == 0) && \
+ (depth((a)) == 0) && \
+ (list_ptr((a)) == null)) \
+ ))
+
+/* 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;
+
+void push_node(halfword p) {
+ if (hlist_stack_level >= max_hlist_stack)
+ pdf_error("push_node", "stack overflow");
+ hlist_stack[hlist_stack_level++] = p;
+}
+
+halfword pop_node (void) {
+ if (hlist_stack_level <= 0) /* would point to some bug */
+ pdf_error("pop_node", "stack underflow (internal error)");
+ return hlist_stack[--hlist_stack_level];
+}
+
+#define null_font 0
+
+static integer max_stretch_ratio=0; /*maximal stretch ratio of expanded fonts*/
+static integer max_shrink_ratio=0; /*maximal shrink ratio of expanded fonts*/
+static integer cur_font_step=0; /*the current step of expanded fonts*/
+
+
+boolean check_expand_pars(internal_font_number f) {
+ internal_font_number k;
+
+ if ((pdf_font_step(f) == 0) || ((pdf_font_stretch(f) == null_font) &&
+ (pdf_font_shrink(f) == null_font)))
+ return false;
+ if (cur_font_step < 0)
+ cur_font_step = pdf_font_step(f);
+ else if (cur_font_step != pdf_font_step(f))
+ pdf_error("font expansion",
+ "using fonts with different step of expansion in one paragraph is not allowed");
+ k = pdf_font_stretch(f);
+ if( k != null_font) {
+ if (max_stretch_ratio < 0)
+ max_stretch_ratio = pdf_font_expand_ratio(k);
+ else if (max_stretch_ratio != pdf_font_expand_ratio(k))
+ pdf_error("font expansion",
+ "using fonts with different limit of expansion in one paragraph is not allowed");
+ }
+ k = pdf_font_shrink(f);
+ if( k != null_font) {
+ if (max_shrink_ratio < 0)
+ max_shrink_ratio = -pdf_font_expand_ratio(k);
+ else if (max_shrink_ratio != -pdf_font_expand_ratio(k))
+ pdf_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*/
+/*public*/ halfword find_protchar_left(halfword l, boolean d) {
+ halfword t;
+ boolean run;
+ if ((vlink(l) != null) && (type(l) == hlist_node) && (width(l) == 0)
+ && (height(l) == 0) && (depth(l) == 0) && (list_ptr(l) == null)) {
+ l = vlink(l); /*for paragraph start with \.{\\parindent = 0pt*/
+ } else if (d) {
+ while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) {
+ l = vlink(l); /* std.\ discardables at line break, \TeX book, p 95 */
+ }
+ }
+ 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 */
+ }
+ 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 */
+/*public*/ halfword find_protchar_right(halfword l, halfword r) {
+ halfword t;
+ boolean run;
+ if (r == null)
+ return null;
+ hlist_stack_level = 0;
+ run = true;
+ 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) ) {
+ assert(vlink(alink(r))==r);
+ r = alink(r);
+ } 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)
+
+#if 0
+/* returns the total width of character protrusion of a line;
+ |cur_break(break_node(q))| and |p| is the leftmost resp. rightmost
+ node in the horizontal list representing the actual line */
+
+scaled total_pw (halfword first_p, halfword q, halfword p) {
+ halfword l, r;
+ l = (break_node(q) == null) ? first_p : cur_break(break_node(q));
+ assert(vlink(alink(p))==p);
+ r = alink(r);
+ /* let's look at the right margin first */
+ if ((p != null) && (type(p) == disc_node) && (vlink(pre_break(p)) != null)) {
+ /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */
+ r = vlink(pre_break(p));
+ while (vlink(r) != null)
+ r = vlink(r);
+ } else {
+ r = find_protchar_right(l, r);
+ }
+ /* now the left margin */
+ if ((l != null) && (type(l) == disc_node)) {
+ if (vlink(post_break(l)) != null) {
+ l = vlink(post_break(l)); /* protrude the first char */
+ goto DONE;
+ }
+ }
+ l = find_protchar_left(l, true);
+DONE:
+ return (left_pw(l) + right_pw(r));
+}
+#endif
+
+
+/* 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 {
+ 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.
+*/
+
+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.
+*/
+
+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.
+*/
+
+#define save_active_width(a) prev_active_width[(a)] = active_width[(a)]
+#define restore_active_width(a) active_width[(a)] = prev_active_width[(a)]
+
+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|*/
+static boolean try_prev_break; /*force break at the previous legal breakpoint?*/
+static halfword prev_legal; /*the previous legal breakpoint*/
+static halfword rejected_cur_p; /*the last |cur_p| that has been rejected*/
+static boolean before_rejected_cur_p; /*|cur_p| is still before |rejected_cur_p|?*/
+
+/* 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|.
+*/
+
+
+
+static integer internal_pen_inter; /* running \.{\\localinterlinepenalty} */
+static integer internal_pen_broken; /* running \.{\\localbrokenpenalty} */
+static halfword internal_left_box; /* running \.{\\localleftbox} */
+static integer internal_left_box_width; /* running \.{\\localleftbox} width */
+static halfword init_internal_left_box; /* running \.{\\localleftbox} */
+static integer init_internal_left_box_width; /* running \.{\\localleftbox} width */
+static halfword internal_right_box; /* running \.{\\localrightbox} */
+static integer 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.
+*/
+
+
+static integer minimal_demerits[4]; /* best total demerits known for current
+ line class and position, given the fitness */
+static integer 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@>
+*/
+
+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 integer fewest_demerits; /*the demerits associated with |best_bet|*/
+static halfword best_line; /*line number following the last line of the new paragraph*/
+static integer actual_looseness; /*the difference between |line_number(best_bet)|
+ and the optimum |best_line|*/
+static integer 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.
+*/
+
+#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 (pdf_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 act_width active_width[1] /*length from first active node to current node*/
+
+#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, pdf_adjust_spacing, \
+ par_shape_ptr, adj_demerits, \
+ tracing_paragraphs, pdf_protrude_chars, \
+ line_penalty, last_line_fit, \
+ double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p); \
+ if (type(cur_p)!=math_node) act_width+=width(cur_p); \
+ else act_width+=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; \
+ } \
+ }
+
+#define inf_bad 10000 /* infinitely bad value */
+
+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 awful_bad 07777777777 /* more than a billion demerits */
+
+#define before 0 /* |subtype| for math node that introduces a formula */
+#define after 1 /* |subtype| for math node that winds up a formula */
+
+#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))
+
+
+/* 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|.
+*/
+
+/* assigned-to globals:
+ break_width[]
+*/
+/* used globals:
+ disc_width[]
+ line_break_dir
+*/
+static void
+compute_break_width (int break_type, int pdf_adjust_spacing, halfword p
+ /*, halfword s*/) {
+ halfword s; /* glue and other 'whitespace' to be skipped after a break
+ * used if unhyphenated, or post_break==empty */
+ s=p;
+ 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 _this_ position.
+ */
+
+ halfword v;
+ for (v=vlink_no_break(p); v!=null; v=vlink(v)) {
+ /* @<Subtract the width of node |v| from |break_width|@>; */
+ /* Replacement texts and discretionary texts are supposed to contain
+ only character nodes, kern nodes, and box or rule nodes.*/
+ if (is_char_node(v)) {
+ if (is_rotated(line_break_dir)) {
+ break_width[1] -= (glyph_height(v)+glyph_depth(v));
+ } else {
+ break_width[1] -= glyph_width(v);
+ }
+ if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) {
+ set_prev_char_p(v);
+ sub_char_stretch(break_width[8],v);
+ sub_char_shrink(break_width[9],v);
+ }
+ } else {
+ switch (type(v)) {
+ case hlist_node:
+ case vlist_node:
+ if (!(dir_orthogonal(dir_primary[box_dir(v)],
+ dir_primary[line_break_dir])))
+ break_width[1] -= width(v);
+ else
+ break_width[1] -= (depth(v)+height(v));
+ break;
+ case kern_node:
+ if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) {
+ sub_kern_stretch(break_width[8],v);
+ sub_kern_shrink(break_width[9],v);
+ }
+ /* fall through */
+ case rule_node:
+ break_width[1] -= width(v);
+ break;
+ case disc_node:
+ assert(vlink(v)==null); /* discs are _always_ last */
+ v = no_break(v);
+ break;
+ default:
+ tconfusion("disc1");
+ break;
+ }
+ }
+ }
+
+ for (v=vlink_post_break(p); v!=null; v=vlink(v)) {
+ /* @<Add the width of node |v| to |break_width|@>; */
+ if (is_char_node(v)) {
+ if (is_rotated(line_break_dir))
+ break_width[1] += (glyph_height(v)+glyph_depth(v));
+ else
+ break_width[1] += glyph_width(v);
+ if ((pdf_adjust_spacing > 1) && check_expand_pars(font(v))) {
+ set_prev_char_p(v);
+ add_char_stretch(break_width[8],v);
+ add_char_shrink(break_width[9],v);
+ }
+ } else {
+ switch (type(v)) {
+ case hlist_node:
+ case vlist_node:
+ if (!(dir_orthogonal(dir_primary[box_dir(v)],
+ dir_primary[line_break_dir])))
+ break_width[1] += width(v);
+ else
+ break_width[1] += (depth(v)+height(v));
+ break;
+ case kern_node:
+ if ((pdf_adjust_spacing > 1) && (subtype(v) == normal)) {
+ add_kern_stretch(break_width[8],v);
+ add_kern_shrink(break_width[9],v);
+ }
+ /* fall through */
+ case rule_node:
+ break_width[1] += width(v);
+ break;
+ case disc_node:
+ assert(vlink(v)==null);
+ v = no_break(v);
+ break;
+ default:
+ tconfusion("disc2");
+ }
+ }
+ }
+
+ 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;
+ }
+ } else {
+ s = p; /* unhyphenated: we need to 'skip' any 'whitespace' following */
+ }
+ while (s!=null) {
+ switch (type(s)) {
+ case glue_node:
+ /*@<Subtract glue from |break_width|@>;*/
+ {halfword v=glue_ptr(s);
+ break_width[1] -= width(v);
+ break_width[2+stretch_order(v)] -= stretch(v);
+ break_width[7] -= shrink(v);
+ }break;
+ case penalty_node:
+ break;
+ case math_node:
+ break_width[1] -= surround(s);
+ break;
+ case kern_node:
+ if (subtype(s)!=explicit)
+ 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) {
+ /* @<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)));
+}
+
+
+static void
+print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi,
+ integer 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);
+}
+
+#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 -= (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 max_dimen 07777777777 /* $2^{30}-1$ */
+
+#define left_side 0
+#define right_side 1
+
+
+#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(integer pi,
+ quarterword break_type,
+ int pdf_adjust_spacing,
+ int par_shape_ptr,
+ int adj_demerits,
+ int tracing_paragraphs,
+ int pdf_protrude_chars,
+ int line_penalty,
+ int last_line_fit,
+ int double_hyphen_demerits,
+ int final_hyphen_demerits,
+ halfword first_p,
+ halfword cur_p) {
+ /* 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; /* stays a step behind |r| */
+ halfword prev_prev_r; /*a step behind |prev_r|, if |type(prev_r)=delta_node|*/
+ halfword old_l; /* maximum line number in current equivalence class of lines */
+ boolean no_break_yet; /* 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; /*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*/
+ integer d; /*demerits of test line*/
+ boolean artificial_demerits; /*has |d| been forced to zero?*/
+
+ scaled shortfall; /*used in badness calculations*/
+ scaled g; /*glue stretch or shrink of test line, adjustment for last line*/
+ scaled cur_active_width[10] = {0}; /*distance from current active node*/
+
+ line_width=0; g=0; prev_prev_r=null;
+ /*@<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*/
+ }
+
+ no_break_yet=true;
+ prev_r=active;
+ old_l=0;
+ 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, pdf_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 \(a)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]
+ -(( break_node(r)==null)
+ ? init_internal_left_box_width
+ : passive_last_left_box_width(break_node(r)))
+ - internal_right_box_width;
+ if (pdf_protrude_chars > 1) {
+ halfword l, o;
+ l = (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);
+ }
+ /* 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(l, o);
+ }
+ /* now the left margin */
+ if ((l != null) && (type(l) == disc_node) && (vlink_post_break(l) != null)) {
+ /* FIXME: first 'char' could be a disc! */
+ l = vlink_post_break(l); /* protrude the first char */
+ } else {
+ l = find_protchar_left(l, true);
+ }
+ shortfall += (left_pw(l) + right_pw(o));
+ }
+ if ((shortfall != 0) && (pdf_adjust_spacing > 1)) {
+ margin_kern_stretch = 0;
+ margin_kern_shrink = 0;
+ if (pdf_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. @^inner loop@> */
+
+ 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 \(t)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.
+ @^overfull boxes@>
+ */
+ 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 \(a)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);
+ }
+ }
+ }
+}
+
+
+void
+ext_do_line_break (boolean d,
+ int pretolerance,
+ int tracing_paragraphs,
+ int tolerance,
+ scaled emergency_stretch,
+ int looseness,
+ int hyphen_penalty,
+ int ex_hyphen_penalty,
+ int pdf_adjust_spacing,
+ halfword par_shape_ptr,
+ int adj_demerits,
+ int pdf_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,
+ int pdf_each_line_height,
+ int pdf_each_line_depth,
+ int pdf_first_line_height,
+ int pdf_last_line_depth,
+ halfword inter_line_penalties_ptr,
+ int inter_line_penalty,
+ int club_penalty,
+ halfword club_penalties_ptr,
+ halfword display_widow_penalties_ptr,
+ halfword widow_penalties_ptr,
+ int display_widow_penalty,
+ 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 */
+
+ /* 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;
+
+ /* 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 {
+ /* @<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(hang_indent);
+ if (hang_indent>=0)
+ first_indent=hang_indent;
+ else
+ first_indent=0;
+ second_width=hsize;
+ second_indent=0;
+ } else {
+ first_width=hsize;
+ first_indent=0;
+ second_width=hsize-abs(hang_indent);
+ if (hang_indent>=0)
+ second_indent=hang_indent;
+ else
+ second_indent=0;
+ }
+ }
+ } else {
+ last_special_line=vinfo(par_shape_ptr+1)-1;
+ second_indent=varmem[(par_shape_ptr+(2*last_special_line))].cint;
+ second_width=varmem[(par_shape_ptr+2*(last_special_line+1)+1)].cint;
+ }
+ 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 (pdf_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=glue_ptr(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) {
+ fprintf(stdout,"-dir_node s %d\n",dir_ptr);
+ flush_node_list(dir_ptr);
+ dir_ptr=null;
+ }
+ /*push_dir(paragraph_dir);*/ /* TODO what was the point of this? */
+
+ /* @<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);
+ assert(alink(cur_p)==temp_head);
+ /* LOCAL: Initialize with first |local_paragraph| node */
+ if ((type(cur_p)==whatsit_node)&&(subtype(cur_p)==local_par_node)) {
+ 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);
+ prev_legal = null;
+ rejected_cur_p = null;
+ try_prev_break = false;
+ before_rejected_cur_p = false;
+ 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_nodes. */
+
+ while (is_char_node(cur_p)) {
+ /* Advance \(c)|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.
+ */
+ act_width += (is_rotated(line_break_dir))
+ ? (glyph_height(cur_p)+glyph_depth(cur_p))
+ : glyph_width(cur_p);
+ if ((pdf_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];
+ /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); */
+ }
+ }
+ if (cur_p==null) { /* TODO */
+ tconfusion("linebreak_tail");
+ }
+ /* 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 |act_width| for the component of active width that represents
+ real width as opposed to glue. */
+
+ switch (type(cur_p)) {
+
+ case hlist_node:
+ case vlist_node:
+ act_width += (dir_orthogonal(dir_primary[box_dir(cur_p)], dir_primary[line_break_dir]))
+ ? (depth(cur_p)+height(cur_p))
+ : width(cur_p);
+ break;
+ case rule_node:
+ act_width += width(cur_p);
+ break;
+ case whatsit_node:
+ /* @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>;*/
+ switch (subtype(cur_p)) {
+ 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 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(cur_p);
+ } else {
+ pop_dir_node();
+ if (dir_ptr!=null)
+ line_break_dir=dir_dir(dir_ptr);
+ }
+ break;
+ case pdf_refxform_node:
+ case pdf_refximage_node:
+ act_width += pdf_width(cur_p);
+ }
+ /* / Advance \(p)past a whatsit node in the \(l)|line_break| loop/; */
+ break;
+ case glue_node:
+ /* @<If node |cur_p| is a legal breakpoint, call |try_break|;
+ then update the active widths by including the glue in
+ |glue_ptr(cur_p)|@>; */
+ /* 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. */
+ if (auto_breaking) {
+ halfword prev_p = alink(cur_p);
+ if (prev_p!=temp_head &&
+ (is_char_node(prev_p) ||
+ precedes_break(prev_p)||
+ ((type(prev_p)==kern_node)&&(subtype(prev_p)!=explicit)))) {
+ ext_try_break(0,unhyphenated_node, pdf_adjust_spacing, par_shape_ptr,
+ adj_demerits, tracing_paragraphs, pdf_protrude_chars,
+ line_penalty, last_line_fit, double_hyphen_demerits,
+ final_hyphen_demerits,first_p,cur_p);
+ }
+ }
+ check_shrinkage(glue_ptr(cur_p));
+ q=glue_ptr(cur_p);
+ act_width += width(q);
+ active_width[2+stretch_order(q)] += stretch(q);
+ active_width[7] += shrink(q);
+ break;
+ case kern_node:
+ if (subtype(cur_p)==explicit) {
+ kern_break();
+ } else {
+ act_width += width(cur_p);
+ if ((pdf_adjust_spacing > 1) && (subtype(cur_p) == normal)) {
+ add_kern_stretch(active_width[8],cur_p);
+ add_kern_shrink(active_width[9],cur_p);
+ }
+ }
+ break;
+ case disc_node:
+ /* @<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. */
+ if (second_pass) {
+ int actual_penalty = hyphen_penalty;
+ if (subtype(cur_p)==automatic_disc)
+ actual_penalty = ex_hyphen_penalty;
+ 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, pdf_adjust_spacing,
+ par_shape_ptr, adj_demerits, tracing_paragraphs,
+ pdf_protrude_chars, line_penalty, last_line_fit,
+ double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p);
+ } else {
+ do {
+ /* @<Add the width of node |s| to |disc_width|@>;*/
+ if (is_char_node(s)) {
+ if (is_rotated(line_break_dir)) {
+ disc_width[1] += (glyph_height(s)+glyph_depth(s));
+ } else {
+ disc_width[1] += glyph_width(s);
+ }
+ if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) {
+ set_prev_char_p (s);
+ add_char_stretch(disc_width[8],s);
+ add_char_shrink(disc_width[9],s);
+ }
+ } else {
+ switch (type(s)) {
+ case hlist_node:
+ case vlist_node:
+ if (!(dir_orthogonal(dir_primary[box_dir(s)],
+ dir_primary[line_break_dir]))) {
+ disc_width[1] += width(s);
+ } else {
+ disc_width[1] += (depth(s)+height(s));
+ }
+ break;
+ case kern_node:
+ if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) {
+ add_kern_stretch(disc_width[8],s);
+ add_kern_shrink(disc_width[9],s);
+ }
+ /* fall through */
+ case rule_node:
+ disc_width[1] += width(s);
+ break;
+ case disc_node:
+ tconfusion("pre_break_disc");
+ break;
+ default:
+ tconfusion("disc3");
+ }
+ }
+ /* /Add the width of node |s| to |disc_width| */
+ s=vlink(s);
+ } while (s!=null);
+ do_one_seven_eight(add_disc_width_to_active_width);
+ ext_try_break(actual_penalty,hyphenated_node, pdf_adjust_spacing,
+ par_shape_ptr, adj_demerits, tracing_paragraphs,
+ pdf_protrude_chars, line_penalty, last_line_fit,
+ double_hyphen_demerits, final_hyphen_demerits,
+ first_p,cur_p);
+ do_one_seven_eight(sub_disc_width_from_active_width);
+ }
+#define FOO 1
+#if FOO
+#if 0
+ if (vlink(cur_p)!=null) nest_stack[nest_index++] = vlink(cur_p);
+ cur_p = no_break(cur_p);
+#else
+ s=vlink_no_break(cur_p);
+ while (s!=null) {
+ /* @<Add the width of node |s| to |act_width|@>;*/
+ if (is_char_node(s)) {
+ if (is_rotated(line_break_dir)) {
+ act_width += glyph_height(s)+glyph_depth(s);
+ } else {
+ act_width += glyph_width(s);
+ }
+ if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) {
+ set_prev_char_p (s);
+ add_char_stretch(active_width[8],s);
+ add_char_shrink(active_width[9],s);
+ };
+ } else {
+ switch (type(s)) {
+ case hlist_node:
+ case vlist_node:
+ if (!(dir_orthogonal(dir_primary[box_dir(s)],
+ dir_primary[line_break_dir])))
+ act_width += width(s);
+ else
+ act_width += (depth(s)+height(s));
+ break;
+ case kern_node:
+ if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) {
+ add_kern_stretch(active_width[8],s);
+ add_kern_shrink(active_width[9],s);
+ }
+ /* fall through */
+ case rule_node:
+ act_width += width(s);
+ break;
+ case disc_node:
+ /* what now */
+ assert(vlink(s)==null); /* discs are _always_ last */
+ /* NB: temporary solution: not good, but not bad either */
+ s = no_break(s);
+ break;
+ default:
+ tconfusion("disc5");
+ }
+ }
+ /* /Add the width of node |s| to |act_width|;*/
+ s=vlink(s);
+ }
+#endif
+ } else { /* first pass, just take the no_break path */
+#else /* FOO */
+ }
+#endif /* FOO */
+#if 0
+ if (vlink_no_break(cur_p)!=null) {
+ if (vlink(cur_p)!=null) nest_stack[nest_index++] = vlink(cur_p);
+ /* fprintf(stderr,"Node Push %d [%d]->[%d] / [%d]\n",(nest_index-1),(int)cur_p,(int)vlink(cur_p),(int)vlink_no_break(cur_p));*/
+ cur_p = no_break(cur_p);
+ }
+#else
+ s=vlink_no_break(cur_p);
+ while (s!=null) {
+ /* @<Add the width of node |s| to |act_width|@>;*/
+ if (is_char_node(s)) {
+ if (is_rotated(line_break_dir)) {
+ act_width += glyph_height(s)+glyph_depth(s);
+ } else {
+ act_width += glyph_width(s);
+ }
+ if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) {
+ set_prev_char_p (s);
+ add_char_stretch(active_width[8],s);
+ add_char_shrink(active_width[9],s);
+ };
+ } else {
+ switch (type(s)) {
+ case hlist_node:
+ case vlist_node:
+ if (!(dir_orthogonal(dir_primary[box_dir(s)],
+ dir_primary[line_break_dir])))
+ act_width += width(s);
+ else
+ act_width += (depth(s)+height(s));
+ break;
+ case kern_node:
+ if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) {
+ add_kern_stretch(active_width[8],s);
+ add_kern_shrink(active_width[9],s);
+ }
+ /* fall through */
+ case rule_node:
+ act_width += width(s);
+ break;
+ case disc_node:
+ assert(vlink(s)==null); /* discs are _always_ last */
+ s = no_break(s);
+ break;
+ default:
+ tconfusion("disc4");
+ }
+ }
+ /* /Add the width of node |s| to |act_width|;*/
+ s=vlink(s);
+ }
+#endif
+#if FOO
+ }
+#endif /* FOO */
+ break;
+ case math_node:
+ auto_breaking=(subtype(cur_p)==after);
+ kern_break();
+ break;
+ case penalty_node:
+ ext_try_break(penalty(cur_p),unhyphenated_node, pdf_adjust_spacing,
+ par_shape_ptr, adj_demerits, tracing_paragraphs,
+ pdf_protrude_chars, line_penalty, last_line_fit,
+ double_hyphen_demerits, final_hyphen_demerits,first_p,cur_p);
+ break;
+ case mark_node:
+ case ins_node:
+ case adjust_node:
+ break;
+ case glue_spec_node:
+ fprintf(stdout, "\nfound a glue_spec in a paragraph!");
+ break;
+ default:
+ fprintf(stdout, "\ntype=%d",type(cur_p));
+ tconfusion("paragraph") ;
+ }
+ cur_p=vlink(cur_p);
+ while (cur_p==null && nest_index>0) {
+ cur_p = nest_stack[--nest_index];
+ /* fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p);*/
+ }
+ }
+ 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, pdf_adjust_spacing,
+ par_shape_ptr, adj_demerits, tracing_paragraphs,
+ pdf_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 \(t)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 {
+ q=new_spec(glue_ptr(last_line_fill));
+ delete_glue_ref(glue_ptr(last_line_fill));
+ width(q) += (active_short(best_bet)-active_glue(best_bet));
+ stretch(q)=0;
+ glue_ptr(last_line_fill)=q;
+ }
+ /* /Adjust \(t)the final line of the paragraph;*/
+ }
+
+ /* @<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.)
+ */
+ ext_post_line_break(d,
+ right_skip,
+ left_skip,
+ pdf_protrude_chars,
+ par_shape_ptr,
+ pdf_adjust_spacing,
+ pdf_each_line_height,
+ pdf_each_line_depth,
+ pdf_first_line_height,
+ pdf_last_line_depth,
+ inter_line_penalties_ptr,
+ inter_line_penalty,
+ club_penalty,
+ club_penalties_ptr,
+ display_widow_penalties_ptr,
+ widow_penalties_ptr,
+ display_widow_penalty,
+ 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();
+ /*
+ flush_node_list(dir_ptr);
+ dir_ptr=null;
+ */
+}
+
+
diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c
new file mode 100644
index 00000000000..86b9562c262
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c
@@ -0,0 +1,512 @@
+/* $Id: postlinebreak.c 1078 2008-03-05 15:33:46Z taco $ */
+
+#include "luatex-api.h"
+#include <ptexlib.h>
+#include "nodes.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.
+
+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.
+
+*/
+
+static halfword
+do_push_dir_node (halfword p, halfword a ) {
+ halfword n;
+ n = copy_node(a);
+ vlink(n) = p;
+ return n;
+}
+
+static halfword
+do_pop_dir_node ( halfword p ) {
+ halfword n = vlink(p);
+ flush_node(p);
+ return n;
+}
+
+
+/* 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.
+
+*/
+
+#define next_break prev_break /*new name for |prev_break| after links are reversed*/
+
+#define append_list(a,b) \
+ { vlink(cur_list.tail_field)=vlink((a)); cur_list.tail_field = b; }
+
+#define left_skip_code 7 /*glue at left of justified lines*/
+#define right_skip_code 8 /*glue at right of justified lines*/
+
+/* the ints are actually halfwords */
+void ext_post_line_break(boolean d,
+ int right_skip,
+ int left_skip,
+ int pdf_protrude_chars,
+ halfword par_shape_ptr,
+ int pdf_adjust_spacing,
+ int pdf_each_line_height,
+ int pdf_each_line_depth,
+ int pdf_first_line_height,
+ int pdf_last_line_depth,
+ halfword inter_line_penalties_ptr,
+ int inter_line_penalty,
+ int club_penalty,
+ halfword club_penalties_ptr,
+ halfword display_widow_penalties_ptr,
+ halfword widow_penalties_ptr,
+ int display_widow_penalty,
+ int widow_penalty,
+ int broken_penalty,
+ halfword final_par_glue,
+ halfword best_bet,
+ halfword last_special_line,
+ scaled second_width,
+ scaled second_indent,
+ scaled first_width,
+ scaled first_indent,
+ halfword best_line ) {
+
+ boolean have_directional = true;
+ halfword q,r,s; /* temporary registers for list manipulation */
+ halfword p, 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|*/
+ integer pen; /*use when calculating penalties between lines */
+ halfword cur_p; /* cur_p, but localized */
+ halfword cur_line; /*the current line number being justified*/
+
+ 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.
+ */
+ q=break_node(best_bet);
+ /*used_discs = used_disc(best_bet);*/
+ /*has_direction*/
+ cur_p=null;
+ do {
+ r=q;
+ q=prev_break(q);
+ 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 */
+
+ 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);
+ couple_nodes(temp_head,tmp);
+ couple_nodes(tmp,nxt);
+ }
+ if (dir_ptr!=null) {
+ flush_node_list(dir_ptr); dir_ptr=null;
+ }
+
+ /* 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| */
+ r = cur_break(cur_p);
+ q = null;
+ disc_break=false;
+ post_disc_break=false;
+ glue_break = false;
+
+ if (r==null) {
+ for (r=temp_head; vlink(r)!=null; r=vlink(r));
+ if (r==final_par_glue) {
+ /* This should almost always be true... */
+ /* TODO assert ? */
+ q = r;
+ /* |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 */
+ } else if (type(r)==glue_node) {
+ delete_glue_ref(glue_ptr(r));
+ glue_ptr(r)=right_skip;
+ subtype(r)=right_skip_code+1;
+ incr(glue_ref_count(right_skip));
+ glue_break = true;
+ /* |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 */
+ } else if (type(r)==disc_node) {
+ halfword a = alink(r);
+ assert(a!=null);
+ halfword v = vlink(r);
+ if (v==null) { /* nested disc, let's unfold */
+ fprintf(stderr,"Nested disc [%d]<-[%d]->null\n",(int)a,(int)r);
+ do {
+ halfword d;
+ while (alink(a)!=null) a = alink(a);
+ assert(type(a)==nesting_node);
+ assert(subtype(a)=no_break_head(0)); /* No_break */
+ d = a - subtype(a); /* MAGIC subtype is offset of nesting with disc */
+ assert(type(d)==disc_node);
+ v = vlink(d);
+ a = alink(d);
+ fprintf(stderr,"Up to disc [%d]<-[%d]->[%d] & link [%d]\n",(int)a,(int)d,(int)v,(int)vlink_no_break(d));
+ assert(a!=null);
+ couple_nodes(a,vlink_no_break(d));
+ vlink_no_break(d)=null;
+ tlink_no_break(d)=null;
+ flush_node(d);
+ } while (v==null);
+ couple_nodes(r,v);
+ fprintf(stderr,"Close list [%d]<->[%d] a=[%d]\n",(int)r,(int)v,(int)alink(r));
+ a = alink(r);
+ }
+ if (vlink_no_break(r)!=null) {
+ flush_node_list(vlink_no_break(r));
+ vlink_no_break(r)=null;
+ tlink_no_break(r)=null;
+ }
+ if (vlink_pre_break(r)!=null) {
+ couple_nodes(a,vlink_pre_break(r));
+ couple_nodes(tlink_pre_break(r),r);
+ vlink_pre_break(r)=null;
+ tlink_pre_break(r)=null;
+ }
+ if (vlink_post_break(r)!=null) {
+ couple_nodes(r,vlink_post_break(r));
+ couple_nodes(tlink_post_break(r),v);
+ vlink_post_break(r)=null;
+ tlink_post_break(r)=null;
+ post_disc_break = true;
+ }
+ disc_break=true;
+ } else if (type(r)==kern_node) {
+ width(r)=0;
+ } else if (type(r)==math_node) {
+ surround(r)=0;
+ }
+
+ /* DIR: Adjust the dir stack based on dir nodes in this line; */
+ /* TODO what about the previousparagraph ??? */
+ if (have_directional) {
+ halfword e;
+ halfword p;
+ for(e=vlink(temp_head); e!=null && e!=cur_break(cur_p); e=vlink(e)) {
+ if (type(e)!=whatsit_node || subtype(e)!=dir_node)
+ continue;
+ if (dir_dir(e)>=0) {
+ dir_ptr = do_push_dir_node(dir_ptr,e);
+ } else if (dir_ptr!=null && dir_dir(dir_ptr)==(dir_dir(e)+64)) {
+ 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@>;*/
+ e = vlink(r);
+ for (p=dir_ptr; p!=null; p=vlink(p)) {
+ halfword s=new_dir(dir_dir(p)-64);
+ couple_nodes(r,s);
+ try_couple_nodes(s,e);
+ r=s;
+ }
+ }
+ 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);
+ try_couple_nodes(s,e);
+ r=s;
+ }
+ 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| */
+ if (q!=temp_head && pdf_protrude_chars > 0) {
+ halfword 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| */
+ ptmp = p;
+ } else {
+ p = alink(q); /* get |vlink(p) = 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| */
+ k = new_margin_kern(-w, last_rightmost_char, right_side);
+ vlink(k) = vlink(ptmp);
+ vlink(ptmp) = k;
+ if (ptmp == q)
+ q = vlink(q);
+ }
+ }
+ /* 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 \(r)\.{\\rightskip} glue after node |q|@>;*/
+ halfword r=new_param_glue(right_skip_code);
+ vlink(r)=vlink(q);
+ vlink(q)=r;
+ q=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|; */
+
+ /* Put the \(l)\.{\\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: */
+ r=copy_node_list(passive_left_box(cur_p));
+ s=vlink(q);
+ vlink(r)=q;
+ q=r;
+ if ((cur_line==cur_list.pg_field+1) && (s!=null)) {
+ if (type(s)==hlist_node) {
+ if (list_ptr(s)==null) {
+ q=vlink(q);
+ vlink(r)=vlink(s);
+ vlink(s)=r;
+ }
+ }
+ }
+ }
+ /*at this point |q| is the leftmost node; all discardable nodes have been discarded */
+ if (pdf_protrude_chars > 0) {
+ p = q;
+ p = find_protchar_left(p, false); /* no more discardables */
+ w = char_pw(p, left_side);
+ if (w != 0) {
+ k = new_margin_kern(-w, last_leftmost_char, left_side);
+ vlink(k) = q;
+ q = k;
+ }
+ };
+ if (left_skip!=zero_glue) {
+ r=new_param_glue(left_skip_code);
+ vlink(r)=q;
+ q=r;
+ }
+ /* /Put the \(l)\.{\\leftskip} glue at the left and detach this line;*/
+
+ /* Call the packaging subroutine, setting |just_box| to the justified box;*/
+ /* Now |q| points to the hlist that represents the current line of the
+ paragraph. We need to compute the appropriate line width, pack the
+ line into a box of this size, and shift the box by the appropriate
+ amount of indentation. */
+ if (cur_line>last_special_line) {
+ cur_width=second_width;
+ cur_indent=second_indent;
+ } else if (par_shape_ptr==null) {
+ cur_width=first_width;
+ cur_indent=first_indent;
+ } else {
+ cur_indent=varmem[(par_shape_ptr+2*cur_line)].cint;
+ cur_width=varmem[(par_shape_ptr+2*cur_line+1)].cint;
+ }
+ adjust_tail=adjust_head;
+ pack_direction=paragraph_dir;
+ pre_adjust_tail = pre_adjust_head;
+ if (pdf_adjust_spacing > 0 ) {
+ just_box = hpack(q, cur_width, cal_expand_ratio);
+ } else {
+ just_box = hpack(q, cur_width, exactly);
+ }
+ shift_amount(just_box)=cur_indent;
+ /* /Call the packaging subroutine, setting |just_box| to the justified box;*/
+
+ /* Append the new box to the current vertical list, followed by the list of
+ special nodes taken out of the box by the packager;*/
+ if (pdf_each_line_height!=0)
+ height(just_box) = pdf_each_line_height;
+ if (pdf_each_line_depth != 0)
+ depth(just_box) = pdf_each_line_depth;
+ if ((pdf_first_line_height != 0) && (cur_line == cur_list.pg_field + 1))
+ height(just_box) = pdf_first_line_height;
+ if ((pdf_last_line_depth != 0) && (cur_line + 1 == best_line))
+ depth(just_box) = pdf_last_line_depth;
+
+ if (pre_adjust_head != pre_adjust_tail)
+ append_list(pre_adjust_head,pre_adjust_tail);
+ pre_adjust_tail = null;
+ append_to_vlist(just_box);
+ if (adjust_head != adjust_tail)
+ append_list(adjust_head,adjust_tail);
+ 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. */
+ if (cur_line+1!=best_line) {
+ q=inter_line_penalties_ptr;
+ if (q!=null) {
+ r=cur_line;
+ if (r>penalty(q))
+ r=penalty(q);
+ pen=penalty(q+r);
+ } else {
+ if (passive_pen_inter(cur_p)!=0) {
+ pen=passive_pen_inter(cur_p);
+ } else {
+ pen=inter_line_penalty;
+ }
+ }
+ q=club_penalties_ptr;
+ if (q!=null) {
+ r=cur_line-cur_list.pg_field; /* prevgraf */
+ if (r>penalty(q))
+ r=penalty(q);
+ pen += penalty(q+r);
+ } else if (cur_line==cur_list.pg_field+1) { /* prevgraf */
+ pen += club_penalty;
+ }
+ if (d)
+ q = display_widow_penalties_ptr;
+ else
+ q = widow_penalties_ptr;
+ if (q!=null) {
+ r=best_line-cur_line-1;
+ if (r>penalty(q))
+ r=penalty(q);
+ pen += penalty(q+r);
+ } else if (cur_line+2==best_line) {
+ if (d)
+ pen += display_widow_penalty;
+ else
+ pen += widow_penalty;
+ }
+ if (disc_break) {
+ if (passive_pen_broken(cur_p)!=0) {
+ pen += passive_pen_broken(cur_p);
+ } else {
+ pen += broken_penalty;
+ }
+ }
+ if (pen!=0) {
+ r=new_penalty(pen);
+ couple_nodes(cur_list.tail_field,r);
+ 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@>; */
+ 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. */
+ r=temp_head;
+ while(1) {
+ q=vlink(r);
+ if (q==cur_break(cur_p) || is_char_node(q))
+ break;
+ if (!((type(q)==whatsit_node)&&(subtype(q)==local_par_node))) {
+ if (non_discardable(q)
+ || (type(q)==kern_node && subtype(q)!=explicit ))
+ break;
+ }
+ r=q;
+ };
+ if (r!=temp_head) {
+ vlink(r)=null;
+ flush_node_list(vlink(temp_head));
+ try_couple_nodes(temp_head,q);
+ }
+ }
+ } while (cur_p!=null);
+ if ((cur_line!=best_line)||(vlink(temp_head)!=null))
+ tconfusion("line breaking");
+ cur_list.pg_field=best_line-1; /* prevgraf */
+ cur_list.dirs_field=dir_ptr; /* dir_save */
+ dir_ptr = null;
+}
+
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..06004160503
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.c
@@ -0,0 +1,1694 @@
+/* $Id: texnodes.c 1103 2008-03-11 14:32:50Z taco $ */
+
+#include "luatex-api.h"
+#include <ptexlib.h>
+#include "nodes.h"
+
+#define MAX_CHAIN_SIZE 12
+
+volatile memory_word *varmem = NULL;
+
+#ifndef NDEBUG
+char *varmem_sizes = NULL;
+#endif
+
+halfword var_mem_max = 0;
+halfword rover = 0;
+
+halfword free_chain[MAX_CHAIN_SIZE] = {null};
+
+static int prealloc=0;
+
+int fix_node_lists=1;
+
+int free_error_seen = 0;
+int copy_error_seen = 0;
+
+halfword slow_get_node (integer s) ; /* defined below */
+int copy_error (halfword p) ; /* define below */
+
+#undef link /* defined by cpascal.h */
+#define info(a) fixmem[(a)].hhlh
+#define link(a) fixmem[(a)].hhrh
+
+#define fake_node 100
+#define fake_node_size 2
+#define fake_node_name "fake"
+
+#define variable_node_size 2
+
+
+char * node_fields_list [] = { "attr", "width", "depth", "height", "dir", "shift",
+ "glue_order", "glue_sign", "glue_set" , "list", NULL };
+char * node_fields_rule [] = { "attr", "width", "depth", "height", "dir", NULL };
+char * node_fields_insert [] = { "attr", "cost", "depth", "height", "spec", "list", NULL };
+char * node_fields_mark [] = { "attr", "class", "mark", NULL };
+char * node_fields_adjust [] = { "attr", "list", NULL };
+char * node_fields_disc [] = { "attr", "pre", "post", "replace", NULL };
+char * node_fields_math [] = { "attr", "surround", NULL };
+char * node_fields_glue [] = { "attr", "spec", "leader", NULL };
+char * node_fields_kern [] = { "attr", "kern", NULL };
+char * node_fields_penalty [] = { "attr", "penalty", NULL };
+char * node_fields_unset [] = { "attr", "width", "depth", "height", "dir", "shrink",
+ "glue_order", "glue_sign", "stretch" , "span", "list", NULL };
+char * node_fields_margin_kern [] = { "attr", "width", "glyph", NULL };
+char * node_fields_glyph [] = { "attr", "char", "font", "lang", "left", "right", "uchyph",
+ "components", "xoffset", "yoffset", NULL };
+char * node_fields_style [] = { NULL };
+char * node_fields_choice [] = { NULL };
+char * node_fields_ord [] = { NULL };
+char * node_fields_op [] = { NULL };
+char * node_fields_bin [] = { NULL };
+char * node_fields_rel [] = { NULL };
+char * node_fields_open [] = { NULL };
+char * node_fields_close [] = { NULL };
+char * node_fields_punct [] = { NULL };
+char * node_fields_inner [] = { NULL };
+char * node_fields_radical [] = { NULL };
+char * node_fields_fraction [] = { NULL };
+char * node_fields_under [] = { NULL };
+char * node_fields_over [] = { NULL };
+char * node_fields_accent [] = { NULL };
+char * node_fields_vcenter [] = { NULL };
+char * node_fields_left [] = { NULL };
+char * node_fields_right [] = { NULL };
+
+char * node_fields_inserting [] = { "height", "last_ins_ptr", "best_ins_ptr", NULL};
+
+char * node_fields_splitup [] = { "height", "last_ins_ptr", "best_ins_ptr",
+ "broken_ptr", "broken_ins", NULL};
+
+char * node_fields_action [] = { "action_type", "named_id", "action_id",
+ "file", "new_window", "data", "ref_count", NULL };
+char * node_fields_attribute [] = { "number", "value", NULL };
+char * node_fields_glue_spec [] = { "width", "stretch", "shrink",
+ "stretch_order", "shrink_order", "ref_count", NULL };
+char * node_fields_attribute_list [] = { NULL };
+
+char * node_fields_whatsit_open [] = { "attr", "stream", "name", "area", "ext", NULL };
+char * node_fields_whatsit_write [] = { "attr", "stream", "data", NULL };
+char * node_fields_whatsit_close [] = { "attr", "stream", NULL };
+char * node_fields_whatsit_special [] = { "attr", "data", NULL };
+
+char * node_fields_whatsit_local_par [] = { "attr", "pen_inter", "pen_broken", "dir",
+ "box_left", "box_left_width", "box_right", "box_right_width", NULL };
+char * node_fields_whatsit_dir [] = { "attr", "dir", "level", "dvi_ptr", "dvi_h", NULL };
+
+char * node_fields_whatsit_pdf_literal [] = { "attr", "mode", "data", NULL };
+char * node_fields_whatsit_pdf_refobj [] = { "attr", "objnum", NULL };
+char * node_fields_whatsit_pdf_refxform [] = { "attr", "width", "height", "depth", "objnum", NULL };
+char * node_fields_whatsit_pdf_refximage [] = { "attr", "width", "height", "depth", "objnum", NULL };
+char * node_fields_whatsit_pdf_annot [] = { "attr", "width", "height", "depth", "objnum", "data", NULL };
+char * node_fields_whatsit_pdf_start_link [] = { "attr", "width", "height", "depth",
+ "objnum", "link_attr", "action", NULL };
+char * node_fields_whatsit_pdf_end_link [] = { "attr", NULL };
+char * node_fields_whatsit_pdf_dest [] = { "attr", "width", "height", "depth",
+ "named_id", "dest_id", "dest_type", "xyz_zoom", "objnum", NULL };
+char * node_fields_whatsit_pdf_thread [] = { "attr", "width", "height", "depth",
+ "named_id", "thread_id", "thread_attr", NULL };
+char * node_fields_whatsit_pdf_start_thread [] = { "attr", "width", "height", "depth",
+ "named_id", "thread_id", "thread_attr", NULL };
+char * node_fields_whatsit_pdf_end_thread [] = { "attr", NULL };
+char * node_fields_whatsit_pdf_save_pos [] = { "attr", NULL };
+char * node_fields_whatsit_late_lua [] = { "attr", "reg", "data", NULL };
+char * node_fields_whatsit_close_lua [] = { "attr", "reg", NULL };
+char * node_fields_whatsit_pdf_colorstack [] = { "attr", "stack", "cmd", "data", NULL };
+char * node_fields_whatsit_pdf_setmatrix [] = { "attr", "data", NULL };
+char * node_fields_whatsit_pdf_save [] = { "attr", NULL };
+char * node_fields_whatsit_pdf_restore [] = { "attr", NULL };
+char * node_fields_whatsit_cancel_boundary [] = { "attr", NULL };
+char * node_fields_whatsit_user_defined [] = { "attr", "user_id", "type", "value", NULL };
+
+node_info node_data[] = {
+ { hlist_node, box_node_size, node_fields_list, "hlist" },
+ { vlist_node, box_node_size, node_fields_list, "vlist" },
+ { rule_node, rule_node_size, node_fields_rule, "rule" },
+ { ins_node, ins_node_size, node_fields_insert, "ins" },
+ { mark_node, mark_node_size, node_fields_mark, "mark" },
+ { adjust_node, adjust_node_size, node_fields_adjust, "adjust" },
+ { fake_node, fake_node_size, NULL, fake_node_name }, /* don't touch this! */
+ { disc_node, disc_node_size, node_fields_disc, "disc" },
+ { whatsit_node, -1, NULL, "whatsit" },
+ { math_node, math_node_size, node_fields_math, "math" },
+ { glue_node, glue_node_size, node_fields_glue, "glue" },
+ { kern_node, kern_node_size, node_fields_kern, "kern" },
+ { penalty_node, penalty_node_size, node_fields_penalty, "penalty" },
+ { unset_node, box_node_size, node_fields_unset, "unset" },
+ { style_node, style_node_size, node_fields_style, "style" },
+ { choice_node, style_node_size, node_fields_choice, "choice" },
+ { ord_noad, noad_size, node_fields_ord, "ord" },
+ { op_noad, noad_size, node_fields_op, "op" },
+ { bin_noad, noad_size, node_fields_bin, "bin" },
+ { rel_noad, noad_size, node_fields_rel, "rel" },
+ { open_noad, noad_size, node_fields_open, "open" },
+ { close_noad, noad_size, node_fields_close, "close" },
+ { punct_noad, noad_size, node_fields_punct, "punct" },
+ { inner_noad, noad_size, node_fields_inner, "inner" },
+ { radical_noad, radical_noad_size, node_fields_radical, "radical" },
+ { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction" },
+ { under_noad, noad_size, node_fields_under, "under" },
+ { over_noad, noad_size, node_fields_over, "over" },
+ { accent_noad, accent_noad_size, node_fields_accent, "accent" },
+ { vcenter_noad, noad_size, node_fields_vcenter, "vcenter" },
+ { left_noad, noad_size, node_fields_left, "left" },
+ { right_noad, noad_size, node_fields_right, "right" },
+ { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern" },
+ { glyph_node, glyph_node_size, node_fields_glyph, "glyph" }, /* 33 */
+ { align_record_node, box_node_size, NULL, "align_record" },
+ { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file" },
+ { pseudo_line_node, variable_node_size, NULL, "pseudo_line" },
+ { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert" },
+ { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert" },
+ { expr_node, expr_node_size, NULL, "expr_stack" },
+ { nesting_node, nesting_node_size, NULL, "nested_list" }, /* 40 */
+ { span_node, span_node_size, NULL, "span" },
+ { attribute_node, attribute_node_size, node_fields_attribute, "attribute" },
+ { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec" },
+ { attribute_list_node, attribute_node_size, node_fields_attribute_list,"attribute_list" },
+ { action_node, pdf_action_size, node_fields_action, "action" },
+ { temp_node, temp_node_size, NULL, "temp" },
+ { align_stack_node, align_stack_node_size, NULL, "align_stack" },
+ { movement_node, movement_node_size, NULL, "movement_stack" },
+ { if_node, if_node_size, NULL, "if_stack" },
+ { unhyphenated_node, active_node_size, NULL, "unhyphenated" }, /* 50 */
+ { hyphenated_node, active_node_size, NULL, "hyphenated" },
+ { delta_node, delta_node_size, NULL, "delta" },
+ { passive_node, passive_node_size, NULL, "passive" },
+ { shape_node, variable_node_size, NULL, "shape" },
+ { -1, -1, NULL, NULL }};
+
+#define last_normal_node shape_node
+
+node_info whatsit_node_data[] = {
+ { open_node, open_node_size, node_fields_whatsit_open, "open" },
+ { write_node, write_node_size, node_fields_whatsit_write, "write" },
+ { close_node, close_node_size, node_fields_whatsit_close, "close" },
+ { special_node, special_node_size, node_fields_whatsit_special, "special" },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { local_par_node, local_par_size, node_fields_whatsit_local_par, "local_par" },
+ { dir_node, dir_node_size, node_fields_whatsit_dir, "dir" },
+ { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal" },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj" },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { pdf_refxform_node, pdf_refxform_node_size, node_fields_whatsit_pdf_refxform, "pdf_refxform" },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { pdf_refximage_node, pdf_refximage_node_size, node_fields_whatsit_pdf_refximage, "pdf_refximage" },
+ { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot" },
+ { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link" },
+ { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link" },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest" },
+ { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread" },
+ { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread,"pdf_start_thread"},
+ { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread" },
+ { pdf_save_pos_node, pdf_save_pos_node_size, node_fields_whatsit_pdf_save_pos, "pdf_save_pos" },
+ { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data" },
+ { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data" },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { late_lua_node, write_node_size, node_fields_whatsit_late_lua, "late_lua" },
+ { close_lua_node, write_node_size, node_fields_whatsit_close_lua, "close_lua" },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { fake_node, fake_node_size, NULL, fake_node_name },
+ { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack" },
+ { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix" },
+ { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save" },
+ { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore" },
+ { cancel_boundary_node, cancel_boundary_size, node_fields_whatsit_cancel_boundary, "cancel_boundary" },
+ { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined" },
+ { -1, -1, NULL, NULL }};
+
+#define last_whatsit_node user_defined_node
+
+#define get_node_size(i,j) (i!=whatsit_node ? node_data[i].size : whatsit_node_data[j].size)
+#define get_node_name(i,j) (i!=whatsit_node ? node_data[i].name : whatsit_node_data[j].name)
+
+halfword
+new_node(int i, int j) {
+ register int s;
+ register halfword n;
+ s = get_node_size(i,j);
+ n = get_node(s);
+ /* it should be possible to do this memset at free_node() */
+ /* 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)*(s-1)));
+ switch (i) {
+ case glyph_node:
+ init_lang_data(n);
+ break;
+ case glue_node:
+ case kern_node:
+ case glue_spec_node:
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_dir(n)=-1;
+ 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 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;
+ rule_dir(n)=-1;
+ /* fall through */
+ 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().
+ */
+ free_node(n,variable_node_size);
+ n = slow_get_node(j);
+ (void)memset((void *)(varmem+n+1),0, (sizeof(memory_word)*(j-1)));
+ break;
+ default:
+ break;
+ }
+ if (nodetype_has_attributes(i)) {
+ build_attribute_list(n);
+ }
+ type(n)=i;
+ subtype(n)=j;
+ return n;
+}
+
+halfword
+raw_glyph_node(void) {
+ register halfword n;
+ n = get_node(glyph_node_size);
+ (void)memset((void *)(varmem+n+1),0, (sizeof(memory_word)*(glyph_node_size-1)));
+ type(n)=glyph_node;
+ subtype(n)=0;
+ return n;
+}
+
+halfword
+new_glyph_node(void) {
+ register halfword n;
+ n = get_node(glyph_node_size);
+ (void)memset((void *)(varmem+n+1),0, (sizeof(memory_word)*(glyph_node_size-1)));
+ type(n)=glyph_node;
+ subtype(n)=0;
+ build_attribute_list(n);
+ return n;
+}
+
+
+/* makes a duplicate of the node list that starts at |p| and returns a
+ pointer to the new list */
+
+halfword
+copy_node_list(halfword p) {
+ halfword q; /* previous position in new list */
+ halfword h = null; /* head of the list */
+ copy_error_seen = 0;
+ while (p!=null) {
+ register halfword s = copy_node(p);
+ if (h==null) {
+ h = s;
+ } else {
+ couple_nodes(q,s);
+ }
+ q=s;
+ p=vlink(p);
+ }
+ return h;
+}
+
+ /* make a dupe of a single node */
+halfword
+copy_node(const halfword p) {
+ halfword r; /* current node being fabricated for new list */
+ 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;
+ }
+ i = get_node_size(type(p), subtype(p));
+ r = get_node(i);
+ (void)memcpy((void *)(varmem+r),(void *)(varmem+p),(sizeof(memory_word)*i));
+
+ if (nodetype_has_attributes(type(p))) {
+ add_node_attr_ref(node_attr(p));
+ alink(r)=null; /* needs checking */
+ }
+ vlink(r)=null;
+ switch (type(p)) {
+ case glyph_node:
+ s=copy_node_list(lig_ptr(p));
+ lig_ptr(r)=s;
+ break;
+ case glue_node:
+ add_glue_ref(glue_ptr(p));
+ s=copy_node_list(leader_ptr(p));
+ leader_ptr(r)=s;
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ s=copy_node_list(list_ptr(p));
+ list_ptr(r)=s;
+ break;
+ case ins_node:
+ add_glue_ref(split_top_ptr(p));
+ s=copy_node_list(ins_ptr(p));
+ ins_ptr(r)=s;
+ break;
+ case margin_kern_node:
+ s=copy_node(margin_char(p));
+ margin_char(r)=s;
+ 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 mark_node:
+ add_token_ref(mark_ptr(p));
+ break;
+ case adjust_node:
+ s=copy_node_list(adjust_ptr(p));
+ adjust_ptr(r)=s;
+ break;
+ case glue_spec_node:
+ glue_ref_count(r)=null;
+ break;
+ case whatsit_node:
+ switch (subtype(p)) {
+ case dir_node:
+ case local_par_node:
+ break;
+ case write_node:
+ case special_node:
+ add_token_ref(write_tokens(p));
+ break;
+ case pdf_literal_node:
+ add_token_ref(pdf_literal_data(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 late_lua_node:
+ add_token_ref(late_lua_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;
+ case user_defined_node:
+ switch (user_node_type(p)) {
+ case 'a': add_node_attr_ref(user_node_value(p));
+ break;
+ case 't': add_token_ref(user_node_value(p));
+ break;
+ case 'n':
+ s=copy_node_list(user_node_value(p));
+ user_node_value(r)=s;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ return r;
+}
+
+int valid_node (halfword p) {
+ if (p>prealloc) {
+ if (p<var_mem_max) {
+ if (varmem_sizes[p]>0)
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+ return 0;
+}
+
+static void
+do_free_error (halfword p) {
+ char errstr[255]= {0};
+ char *errhlp[] = {"When I tried to free the node mentioned in the error message, it turned",
+ "out it was not (or no longer) actually in use.",
+ "Errors such as these are often caused by Lua node list alteration,",
+ "but could also point to a bug in the executable. It should be safe to continue.",
+ NULL};
+
+ check_node_mem();
+ if (free_error_seen)
+ return;
+
+ halfword r = null;
+ free_error_seen = 1;
+ if (type(p)==glyph_node) {
+ snprintf(errstr,255,"Attempt to double-free glyph (%c) node %d, ignored", (int)character (p), (int)p);
+ } else {
+ snprintf(errstr,255,"Attempt to double-free %s node %d, ignored", get_node_name(type(p),subtype(p)), (int)p);
+ }
+ tex_error(errstr,errhlp);
+ for (r=prealloc+1;r<var_mem_max;r++) {
+ if (vlink(r)==p) {
+ halfword s = r;
+ while (s>prealloc && varmem_sizes[s]==0)
+ s--;
+ if (s!=null
+ && s!=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);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+int
+free_error (halfword p) {
+ assert(p>prealloc);
+ assert(p<var_mem_max);
+ if (varmem_sizes[p]==0) {
+ do_free_error(p);
+ return 1; /* double free */
+ }
+ return 0;
+}
+
+
+static void
+do_copy_error (halfword p) {
+ char errstr[255]= {0};
+ char *errhlp[] = {"When I tried to copy the node mentioned in the error message, it turned",
+ "out it was not (or no longer) actually in use.",
+ "Errors such as these are often caused by Lua node list alteration,",
+ "but could also point to a bug in the executable. It should be safe to continue.",
+ NULL};
+
+ if (copy_error_seen)
+ return;
+
+ copy_error_seen = 1;
+ if (type(p)==glyph_node) {
+ snprintf(errstr,255,"Attempt to copy free glyph (%c) node %d, ignored", (int)character (p), (int)p);
+ } else {
+ snprintf(errstr,255,"Attempt to copy free %s node %d, ignored", get_node_name(type(p),subtype(p)), (int)p);
+ }
+ tex_error(errstr,errhlp);
+}
+
+
+int
+copy_error (halfword p) {
+ assert(p>=0);
+ assert(p<var_mem_max);
+ if (p>prealloc && varmem_sizes[p]==0) {
+ do_copy_error(p);
+ return 1; /* copy free node */
+ }
+ return 0;
+}
+
+
+
+void
+flush_node (halfword p) {
+
+ if (p==null) /* legal, but no-op */
+ return;
+
+ if (free_error(p))
+ return;
+
+ switch(type(p)) {
+ case glyph_node:
+ flush_node_list(lig_ptr(p));
+ break;
+ case glue_node:
+ delete_glue_ref(glue_ptr(p));
+ flush_node_list(leader_ptr(p));
+ break;
+
+ case attribute_node:
+ case attribute_list_node:
+ case temp_node:
+ case glue_spec_node:
+ case rule_node:
+ case kern_node:
+ case math_node:
+ case penalty_node:
+ break;
+
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ flush_node_list(list_ptr(p));
+ break;
+ case whatsit_node:
+ switch (subtype(p)) {
+
+ case dir_node:
+ break;
+ case open_node:
+ case write_node:
+ case close_node:
+ case pdf_save_node:
+ case pdf_restore_node:
+ case cancel_boundary_node:
+ case close_lua_node:
+ case pdf_refobj_node:
+ case pdf_refxform_node:
+ case pdf_refximage_node:
+ case pdf_end_link_node:
+ case pdf_end_thread_node:
+ case pdf_save_pos_node:
+ case local_par_node:
+ break;
+
+ case special_node:
+ delete_token_ref(write_tokens(p));
+ break;
+ case pdf_literal_node:
+ delete_token_ref(pdf_literal_data(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 late_lua_node:
+ delete_token_ref(late_lua_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_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;
+ case user_defined_node:
+ switch( user_node_type(p)) {
+ case 'a': delete_attribute_ref(user_node_value(p)); break;
+ case 't': delete_token_ref(user_node_value(p)); break;
+ case 'n': flush_node_list(user_node_value(p)); break;
+ default: tconfusion("extuser"); break;
+ }
+ break;
+
+ default:
+ tconfusion("ext3");
+ return;
+
+ }
+ break;
+ case ins_node:
+ flush_node_list(ins_ptr(p));
+ delete_glue_ref(split_top_ptr(p));
+ break;
+ case margin_kern_node:
+ flush_node(margin_char(p));
+ break;
+ case mark_node:
+ delete_token_ref(mark_ptr(p));
+ break;
+ case disc_node:
+ flush_node_list(vlink(pre_break(p)));
+ flush_node_list(vlink(post_break(p)));
+ flush_node_list(vlink(no_break(p)));
+ break;
+ case adjust_node:
+ flush_node_list(adjust_ptr(p));
+ break;
+ case style_node:
+ break;
+ case choice_node:
+ flush_node_list(display_mlist(p));
+ flush_node_list(text_mlist(p));
+ flush_node_list(script_mlist(p));
+ flush_node_list(script_script_mlist(p));
+ break;
+ case ord_noad:
+ case op_noad:
+ case bin_noad:
+ case rel_noad:
+ case open_noad:
+ case close_noad:
+ case punct_noad:
+ case inner_noad:
+ case radical_noad:
+ case over_noad:
+ case under_noad:
+ case vcenter_noad:
+ case accent_noad:
+
+ /*
+ * if (math_type(nucleus(p))>=sub_box)
+ * flush_node_list(vinfo(nucleus(p)));
+ * if (math_type(supscr(p))>=sub_box)
+ * flush_node_list(vinfo(supscr(p)));
+ * if (math_type(subscr(p))>=sub_box)
+ * flush_node_list(vinfo(subscr(p)));
+ */
+
+ break;
+ case left_noad:
+ case right_noad:
+ break;
+ case fraction_noad:
+ flush_node_list(vinfo(numerator(p)));
+ flush_node_list(vinfo(denominator(p)));
+ break;
+ case pseudo_file_node:
+ flush_node_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 action_node:
+ case inserting_node:
+ case split_up_node:
+ case expr_node:
+ break;
+ default:
+ fprintf(stdout,"flush_node: type is %d\n", type(p));
+ return;
+
+ }
+ if (nodetype_has_attributes(type(p)))
+ delete_attribute_ref(node_attr(p));
+ free_node(p,get_node_size(type(p),subtype(p)));
+ return;
+}
+
+void
+flush_node_list(halfword pp) { /* erase list of nodes starting at |p| */
+ register halfword p=pp;
+ free_error_seen = 0;
+ if (p==null) /* legal, but no-op */
+ return;
+ if (free_error(p))
+ return;
+
+ while (p!=null) {
+ register halfword q = vlink(p);
+ flush_node(p);
+ p=q;
+ }
+}
+
+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); \
+ tconfusion("dorangetest"); \
+ } } 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); \
+ tconfusion("dotest"); \
+ } } while (0)
+
+#define check_action_ref(a) { dorangetest(p,a,var_mem_max); }
+#define check_glue_ref(a) { dorangetest(p,a,var_mem_max); }
+#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); }
+#define check_token_ref(a) assert(1)
+
+void
+check_node (halfword p) {
+
+ switch(type(p)) {
+ case glyph_node:
+ dorangetest(p,lig_ptr(p),var_mem_max);
+ break;
+ case glue_node:
+ check_glue_ref(glue_ptr(p));
+ 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);
+ check_glue_ref(split_top_ptr(p));
+ break;
+ case whatsit_node:
+ switch (subtype(p)) {
+ case special_node:
+ check_token_ref(write_tokens(p));
+ break;
+ case pdf_literal_node:
+ check_token_ref(pdf_literal_data(p));
+ break;
+ case pdf_colorstack_node:
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ check_token_ref(pdf_colorstack_data(p));
+ break;
+ case pdf_setmatrix_node:
+ check_token_ref(pdf_setmatrix_data(p));
+ break;
+ case late_lua_node:
+ check_token_ref(late_lua_data(p));
+ break;
+ case pdf_annot_node:
+ check_token_ref(pdf_annot_data(p));
+ break;
+ case pdf_start_link_node:
+ if (pdf_link_attr(p) != null)
+ check_token_ref(pdf_link_attr(p));
+ check_action_ref(pdf_link_action(p));
+ break;
+ case pdf_dest_node:
+ if (pdf_dest_named_id(p) > 0)
+ check_token_ref(pdf_dest_id(p));
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (pdf_thread_named_id(p) > 0 ) check_token_ref(pdf_thread_id(p));
+ if (pdf_thread_attr(p) != null) check_token_ref(pdf_thread_attr(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(user_node_value(p)); break;
+ case 'n': dorangetest(p,user_node_value(p),var_mem_max); break;
+ default: tconfusion("extuser"); break;
+ }
+ break;
+ case dir_node:
+ case open_node:
+ case write_node:
+ case close_node:
+ case pdf_save_node:
+ case pdf_restore_node:
+ case cancel_boundary_node:
+ case close_lua_node:
+ case pdf_refobj_node:
+ case pdf_refxform_node:
+ case pdf_refximage_node:
+ case pdf_end_link_node:
+ case pdf_end_thread_node:
+ case pdf_save_pos_node:
+ case local_par_node:
+ break;
+ default:
+ tconfusion("ext3");
+ }
+ break;
+ case margin_kern_node:
+ check_node(margin_char(p));
+ 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,vinfo(numerator(p)),var_mem_max);
+ dorangetest(p,vinfo(denominator(p)),var_mem_max);
+ break;
+ case rule_node:
+ case kern_node:
+ case math_node:
+ case penalty_node:
+ case mark_node:
+ case style_node:
+ case ord_noad:
+ case op_noad:
+ case bin_noad:
+ case rel_noad:
+ case open_noad:
+ case close_noad:
+ case punct_noad:
+ case inner_noad:
+ case radical_noad:
+ case over_noad:
+ case under_noad:
+ case vcenter_noad:
+ case accent_noad:
+ case left_noad:
+ case right_noad:
+ 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:
+ break;
+ default:
+ fprintf(stdout,"check_node: type is %d\n", type(p));
+ }
+}
+
+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);
+}
+
+void
+check_node_mem(void ) {
+ int i;
+ check_static_node_mem();
+
+ for (i=(prealloc+1);i<var_mem_max;i++) {
+ if (varmem_sizes[i]>0) {
+ check_node(i);
+ }
+ }
+ test_count++;
+}
+
+void
+fix_node_list (halfword head) {
+ halfword p,q;
+ if (head==null)
+ return;
+ p = head;
+ q = vlink(p);
+ while (q!=null) {
+ alink(q) = p;
+ p = q;
+ q = vlink(p);
+ }
+}
+
+halfword
+get_node (integer s) {
+ register halfword r;
+
+ /*check_static_node_mem();*/
+ assert(s<MAX_CHAIN_SIZE);
+
+ r = free_chain[s];
+ if (r!=null) {
+ free_chain[s] = vlink(r);
+#ifndef NDEBUG
+ varmem_sizes[r] = 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);
+}
+
+void print_free_chain (int c) {
+ halfword p = free_chain[c];
+ fprintf(stdout, "\nfree chain[%d] =\n ",c);
+ while (p!=null) {
+ fprintf(stdout, "%d,",p);
+ p = vlink(p);
+ }
+ fprintf(stdout, "null;\n");
+}
+
+void
+free_node (halfword p, integer s) {
+
+ if (p<=prealloc) {
+ fprintf(stdout,"node %d (type %d) should not be freed!\n",(int)p, type(p));
+ return;
+ }
+
+#ifndef NDEBUG
+ 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;
+ }
+ var_used -= s; /* maintain statistics */
+}
+
+void
+free_node_chain (halfword q, integer s) {
+ register halfword p = q;
+ while (vlink(p)!=null) {
+#ifndef NDEBUG
+ varmem_sizes[p] = 0;
+#endif
+ var_used -= s;
+ p = vlink(p);
+ }
+ var_used -= s;
+#ifndef NDEBUG
+ varmem_sizes[p] = 0;
+#endif
+ vlink(p) = free_chain[s];
+ free_chain[s] = q;
+}
+
+
+void
+init_node_mem (halfword prealloced, halfword t) {
+ prealloc=prealloced;
+ assert(whatsit_node_data[user_defined_node].id==user_defined_node);
+ assert(node_data[passive_node].id==passive_node);
+
+ varmem = (memory_word *)realloc((void *)varmem,sizeof(memory_word)*t);
+ if (varmem==NULL) {
+ overflow_string("node memory size",var_mem_max);
+ }
+ memset ((void *)(varmem),0,t*sizeof(memory_word));
+
+#ifndef NDEBUG
+ varmem_sizes = (char *)realloc(varmem_sizes,sizeof(char)*t);
+ if (varmem_sizes==NULL) {
+ overflow_string("node memory size",var_mem_max);
+ }
+ memset ((void *)varmem_sizes,0,sizeof(char)*t);
+#endif
+ var_mem_max=t;
+ rover = prealloced+1; vlink(rover) = rover;
+ node_size(rover)=(t-rover);
+ var_used = 0;
+}
+
+void
+dump_node_mem (void) {
+ dump_int(var_mem_max);
+ dump_int(rover);
+ dump_things(varmem[0],var_mem_max);
+#ifndef NDEBUG
+ dump_things(varmem_sizes[0],var_mem_max);
+#endif
+ dump_things(free_chain[0],MAX_CHAIN_SIZE);
+ dump_int(var_used);
+ dump_int(prealloc);
+}
+
+/* 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, var_mem_max);
+ /*memset ((void *)varmem,0,x*sizeof(memory_word));*/
+ undump_things(varmem[0],x);
+#ifndef NDEBUG
+ varmem_sizes = xmallocarray (char, var_mem_max);
+ memset ((void *)varmem_sizes,0,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(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;
+ }
+}
+
+#if 0
+void test_rovers (char *s) {
+ int q = rover;
+ int r = q;
+ fprintf(stdout,"%s: {rover=%d,size=%d,link=%d}",s,r,node_size(r),vlink(r));
+ while (vlink(r)!=q) {
+ r = vlink(r);
+ fprintf(stdout,",{rover=%d,size=%d,link=%d}",r,node_size(r),vlink(r));
+ }
+ fprintf(stdout,"\n");
+}
+#else
+#define test_rovers(a)
+#endif
+
+halfword
+slow_get_node (integer s) {
+ register int t;
+
+ RETRY:
+ t = node_size(rover);
+ assert(vlink(rover)<var_mem_max);
+ assert(vlink(rover)!=0);
+ test_rovers("entry");
+ if (t>s) {
+ register halfword r;
+ /* allocating from the bottom helps decrease page faults */
+ 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;
+ }
+ test_rovers("taken");
+ assert(vlink(rover)<var_mem_max);
+#ifndef NDEBUG
+ varmem_sizes[r]=(s>127 ? 127 : s);
+#endif
+ vlink(r)=null;
+ var_used += s; /* maintain usage statistics */
+ return r; /* this is the only exit */
+ } else {
+ int x;
+ /* attempt to keep the free list small */
+ 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;
+ test_rovers("outtake");
+ 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)*(var_mem_max+x));
+ if (varmem==NULL) {
+ overflow_string("node memory size",var_mem_max);
+ }
+ memset ((void *)(varmem+var_mem_max),0,x*sizeof(memory_word));
+
+#ifndef NDEBUG
+ varmem_sizes = (char *)realloc(varmem_sizes,sizeof(char)*(var_mem_max+x));
+ if (varmem_sizes==NULL) {
+ overflow_string("node memory size",var_mem_max);
+ }
+ memset ((void *)(varmem_sizes+var_mem_max),0,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;
+ test_rovers("realloc");
+ var_mem_max += x;
+ goto RETRY;
+ }
+}
+
+char *
+sprint_node_mem_usage (void) {
+ int i,b;
+
+ char *s, *ss;
+ char msg[256];
+ int node_counts[last_normal_node+last_whatsit_node+2] = {0};
+
+ for (i=(var_mem_max-1);i>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)] ++;
+ }
+ }
+ }
+ s = strdup("");
+ b=0;
+ 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 = concat(s,msg);
+ free (s);
+ s = ss;
+ b=1;
+ }
+ }
+ return s;
+}
+
+halfword
+list_node_mem_usage (void) {
+ halfword i, j;
+ halfword p = null,q = null;
+ char *saved_varmem_sizes = xmallocarray (char, var_mem_max);
+ memcpy (saved_varmem_sizes,varmem_sizes,var_mem_max);
+ for (i=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);
+ return q;
+}
+
+void
+print_node_mem_stats (int num, int online) {
+ int i,b;
+ halfword j;
+ char msg[256];
+ char *s;
+ integer free_chain_counts[MAX_CHAIN_SIZE] = {0};
+
+ snprintf(msg,255,"node memory in use: %d words out of %d", (int)(var_used+prealloc), (int)var_mem_max);
+ tprint_nl(msg);
+ tprint_nl("rapidly available: ");
+ 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;
+ }
+ }
+ tprint(" nodes");
+ s = sprint_node_mem_usage();
+ tprint_nl("current usage: ");
+ tprint(s); free(s);
+ tprint(" nodes");
+ print_nlp(); /* newline, if needed */
+}
+
+/* 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;
+}
+
+halfword
+string_to_pseudo(integer l,integer pool_ptr, integer nl) {
+ halfword i, r, q = null;
+ four_quarters w;
+ int sz;
+ halfword h = new_node(pseudo_file_node,0);
+ while (l<pool_ptr) {
+ int m = l;
+ while ((l<pool_ptr)&&(str_pool[l]!=nl)) l++;
+ sz = (l-m+7) / 4;
+ if (sz==1)
+ sz=2;
+ r = new_node(pseudo_line_node,sz);
+ i = r;
+ while (--sz>1) {
+ w.b0=str_pool[m++];
+ w.b1=str_pool[m++];
+ w.b2=str_pool[m++];
+ w.b3=str_pool[m++];
+ varmem[++i].qqqq=w;
+ }
+ w.b0 = (l>m ? str_pool[m++] : ' ');
+ w.b1 = (l>m ? str_pool[m++] : ' ');
+ w.b2 = (l>m ? str_pool[m++] : ' ');
+ w.b3 = (l>m ? str_pool[m] : ' ');
+ varmem[++i].qqqq=w;
+ if (pseudo_lines(h)==null) {
+ pseudo_lines(h) = r;
+ q = r;
+ } else {
+ couple_nodes(q,r);
+ }
+ q = vlink(q);
+ if (str_pool[l]==nl) l++;
+ }
+ return h;
+}
+
+/* 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)= i;
+ attribute_value(r)= v;
+ 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);
+ /* 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 = get_attribute(i);
+ if (v>=0) {
+ register halfword r = new_attribute_node(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) {
+ update_attribute_cache();
+ if (attr_list_cache == null)
+ return;
+ }
+ attr_list_ref(attr_list_cache)++;
+ node_attr(b)=attr_list_cache;
+ }
+}
+
+void
+delete_attribute_ref(halfword b) {
+ if (b!=null){
+ assert(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;
+ }
+}
+
+/* |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) { /* 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(i,val);
+ vlink(q)=p;
+ return q;
+ }
+
+ assert(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(i,val);
+ vlink(r)=vlink(p);
+ vlink(p)=r;
+ }
+ return q;
+}
+
+void
+set_attribute (halfword n, int i, int val) {
+ register halfword p;
+ register int j = 0;
+ if (!nodetype_has_attributes(type(n)))
+ return;
+ 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(i,val);
+ vlink(node_attr(n))=p;
+ return;
+ }
+ assert(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);
+ }
+ p = node_attr(n);
+ if (attr_list_ref(p)!=1) {
+ if (attr_list_ref(p)>1) {
+ p = copy_attribute_list (p);
+ delete_attribute_ref(node_attr(n));
+ node_attr(n) = p;
+ } else {
+ fprintf(stdout,"Node %d has an attribute list that is free already\n",(int)n);
+ }
+ attr_list_ref(p)=1;
+ }
+ 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(i,val);
+ vlink(r)=vlink(p);
+ vlink(p)=r;
+ }
+ return;
+}
+
+
+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 -1;
+ assert (vlink(p)!=null);
+ while (vlink(p)!=null) {
+ t = attribute_id(vlink(p));
+ if (t>i)
+ return -1;
+ if (t==i)
+ break;
+ j++;
+ p = vlink(p);
+ }
+ /* if we are still here, the attribute exists */
+ p = node_attr(n);
+ if (attr_list_ref(p)!=1) {
+ if (attr_list_ref(p)>1) {
+ p = copy_attribute_list (p);
+ delete_attribute_ref(node_attr(n));
+ node_attr(n) = p;
+ } else {
+ fprintf(stdout,"Node %d has an attribute list that is free already\n",(int)n);
+ }
+ attr_list_ref(p)=1;
+ }
+ p = vlink(p);
+ while (j-->0) p = vlink(p);
+ t = attribute_value(p);
+ if (val==-1 || t==val) {
+ attribute_value(p)=-1;
+ }
+ return t;
+}
+
+int
+has_attribute (halfword n,int i, int val) {
+ register halfword p;
+ if (!nodetype_has_attributes(type(n)))
+ return -1;
+ p = node_attr(n);
+ if (p==null || vlink(p)==null)
+ return -1;
+ p = vlink(p);
+ while (p!=null) {
+ if (attribute_id(p)==i) {
+ int ret = attribute_value(p);
+ if (val==-1 || val==ret)
+ return ret;
+ return -1;
+ } else if (attribute_id(p)>i) {
+ return -1;
+ }
+ p=vlink(p);
+ }
+ return -1;
+}
+
diff --git a/Build/source/texk/web2c/luatexdir/tex/texpdf.c b/Build/source/texk/web2c/luatexdir/tex/texpdf.c
new file mode 100644
index 00000000000..fbb4c2b7e21
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/texpdf.c
@@ -0,0 +1,169 @@
+/* $Id: texpdf.c 1013 2008-02-14 00:09:02Z oneiros $ */
+
+#include "luatex-api.h"
+#include <ptexlib.h>
+#include <ctype.h>
+
+#define number_chars 1114112
+#define string_offset 2097152
+#define str_start_macro(a) str_start[(a) - string_offset]
+#define str_length(a) (str_start_macro((a)+1)-str_start_macro(a)) /* the number of characters in string number (a) */
+
+#define is_hex_char isxdigit
+
+/* output a byte to PDF buffer without checking of overflow */
+
+#define pdf_quick_out(a) pdf_buf[pdf_ptr++] = a
+
+/* make sure that there are at least |n| bytes free in PDF buffer */
+
+#define pdf_room(a) do { \
+ if ((pdf_os_mode) && ((a) + pdf_ptr > pdf_buf_size)) \
+ pdf_os_get_os_buf(a); \
+ else if ((!pdf_os_mode) && ((a) > pdf_buf_size) ) \
+ overflow("PDF output buffer", pdf_op_buf_size); \
+ else if ((!pdf_os_mode) && ((a) + pdf_ptr > pdf_buf_size)) \
+ pdf_flush(); \
+ } while (0)
+
+/* do the same as |pdf_quick_out| and flush the PDF buffer if necessary */
+
+#define pdf_out(a) do { \
+ pdf_room(1); \
+ pdf_quick_out(a); \
+ } while (0)
+
+
+/* print out a character to PDF buffer; the character will be printed in octal
+ * form in the following cases: chars <= 32, backslash (92), left parenthesis
+ * (40) and right parenthesis (41)
+ */
+
+#define pdf_print_escaped(c) \
+ if ((c)<=32||(c)=='\\'||(c)=='('||(c)==')'||(c)>127) { \
+ pdf_room(4); \
+ pdf_quick_out('\\'); \
+ pdf_quick_out('0' + (((c)>>6) & 0x3)); \
+ pdf_quick_out('0' + (((c)>>3) & 0x7)); \
+ pdf_quick_out('0' + ( (c) & 0x7)); \
+ } else { \
+ pdf_out((c)); \
+ }
+
+void
+pdf_print_char(internal_font_number f, integer cc) {
+ register int c;
+ pdf_mark_char(f, cc);
+ if (font_encodingbytes(f)==2) {
+ register int chari;
+ chari = char_index(f, cc);
+ c = chari >> 8;
+ pdf_print_escaped(c);
+ c = chari & 0xFF;
+ } else {
+ if (cc>255) return;
+ c = cc;
+ }
+ pdf_print_escaped(c);
+}
+
+/* print out a string to PDF buffer */
+
+void
+pdf_print(str_number s) {
+ if (s < number_chars) {
+ assert(s<256);
+ pdf_out(s);
+ } else {
+ register pool_pointer j = str_start_macro(s);
+ while (j < str_start_macro(s + 1)) {
+ pdf_out(str_pool[j++]);
+ }
+ }
+}
+
+/* print out a integer to PDF buffer */
+
+void
+pdf_print_int(integer n) {
+ register integer k = 0; /* current digit; we assume that $|n|<10^{23}$ */
+ if (n<0) {
+ pdf_out('-');
+ if (n<-0x7FFFFFFF) { /* need to negate |n| more carefully */
+ register integer m;
+ k++;
+ m =-1-n; n=m / 10; m= (m % 10)+1;
+ if (m<10) {
+ dig[0]=m ;
+ } else {
+ dig[0]=0; incr(n);
+ }
+ } else {
+ n = -n;
+ }
+ }
+ do {
+ dig[k++] = n % 10;
+ n = n / 10;
+ } while (n!=0);
+ pdf_room(k);
+ while (k-->0) {
+ pdf_quick_out('0'+dig[k]);
+ }
+}
+
+
+/* print $m/10^d$ as real */
+void
+pdf_print_real(integer m, integer d) {
+ if (m < 0) {
+ pdf_out('-');
+ m = -m;
+ };
+ pdf_print_int(m / ten_pow[d]);
+ m = m % ten_pow[d];
+ if (m > 0) {
+ pdf_out('.');
+ d--;
+ while (m < ten_pow[d]) {
+ pdf_out('0');
+ d--;
+ }
+ while (m % 10 == 0)
+ m = m / 10;
+ pdf_print_int(m);
+ }
+}
+
+/* print out |s| as string in PDF output */
+
+void
+pdf_print_str(str_number s) {
+ pool_pointer i, j;
+ i = str_start_macro(s);
+ j = str_start_macro(s+1) - 1;
+ if (i > j) {
+ pdf_room(2);
+ pdf_quick_out('(');
+ pdf_quick_out(')');
+ return;
+ }
+ /* the next is not really safe, the string could be "(a)xx(b)" */
+ if ((str_pool[i] == '(') && (str_pool[j] == ')')) {
+ pdf_print(s);
+ return;
+ }
+ if ((str_pool[i] != '<') || (str_pool[j] != '>') || odd(str_length(s))) {
+ pdf_out('('); pdf_print(s); pdf_out(')');
+ return;
+ }
+ i++;
+ j--;
+ while (i < j) {
+ if (!is_hex_char(str_pool[i++])) {
+ pdf_out('('); pdf_print(s); pdf_out(')');
+ return;
+ }
+ }
+ pdf_print(s); /* it was a hex string after all */
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.c b/Build/source/texk/web2c/luatexdir/tex/textoken.c
new file mode 100644
index 00000000000..20c57b9479b
--- /dev/null
+++ b/Build/source/texk/web2c/luatexdir/tex/textoken.c
@@ -0,0 +1,753 @@
+/* $Id: textoken.c 1013 2008-02-14 00:09:02Z oneiros $ */
+
+#include "luatex-api.h"
+#include <ptexlib.h>
+
+#include "tokens.h"
+
+/* Integer parameters and other command-related defines. This needs it's own header file! */
+
+#define end_line_char_code 48 /* character placed at the right end of the buffer */
+#define cat_code_table_code 63
+#define tex_int_pars 66 /* total number of \.{\\TeX} + Aleph integer parameters */
+/* this is not what happens in the pascal code! there the values shift from bare numbers to offsets ! */
+#define dir_base tex_int_pars
+#define dir_pars 5
+#define pdftex_first_integer_code dir_base+dir_pars /* base for \pdfTeX's integer parameters */
+#define pdf_int_pars pdftex_first_integer_code+27 /*total number of \pdfTeX's integer parameters*/
+#define etex_int_base pdf_int_pars /*base for \eTeX's integer parameters*/
+#define tracing_nesting_code etex_int_base+4 /*show incomplete groups and ifs within files*/
+
+#define int_par(a) zeqtb[static_int_base+a].cint /* an integer parameter */
+#define cat_code_table int_par(cat_code_table_code)
+#define tracing_nesting int_par(tracing_nesting_code)
+#define end_line_char int_par(end_line_char_code)
+
+#define every_eof get_every_eof()
+
+#define number_active_chars 65536+65536
+#define active_base 1 /* beginning of region 1, for active character equivalents */
+#define null_cs active_base+number_active_chars /* equivalent of \.{\\csname\\endcsname} */
+
+
+#define eq_level(a) zeqtb[a].hh.u.B1
+#define eq_type(a) zeqtb[a].hh.u.B0
+#define equiv(a) zeqtb[a].hh.v.RH
+
+/* leave an input level, re-enter the old */
+#define pop_input() cur_input=input_stack[--input_ptr]
+
+#define nonstop_mode 1
+
+/* command codes */
+#define relax 0
+#define out_param 5
+#define max_command 117 /* fetched from C output */
+#define dont_expand 133 /* fetched from C output */
+
+#define terminal_input (name==0) /* are we reading from the terminal? */
+#define special_char 1114113 /* |biggest_char+2| */
+#define cur_file input_file[index] /* the current |alpha_file| variable */
+
+#define no_expand_flag special_char /*this characterizes a special variant of |relax|*/
+
+extern void insert_vj_template (void);
+
+#define do_get_cat_code(a) do { \
+ if (local_catcode_table) \
+ a=get_cat_code(line_catcode_table,cur_chr); \
+ else \
+ a=get_cat_code(cat_code_table,cur_chr); \
+ } while (0)
+
+
+static void
+invalid_character_error (void) {
+ char *hlp[] = {"A funny symbol that I can't read has just been input.",
+ "Continue, and I'll forget that it ever happened.",
+ NULL};
+ deletions_allowed=false;
+ tex_error("Text line contains an invalid character", hlp);
+ deletions_allowed=true;
+}
+
+/* no longer done */
+
+static boolean process_sup_mark(void); /* below */
+
+static int scan_control_sequence(void); /* below */
+
+typedef enum {next_line_ok, next_line_return, next_line_restart } next_line_retval ;
+
+static next_line_retval next_line(void); /* below */
+
+/* @^inner loop@>*/
+
+static void
+utf_error (void) {
+ char *hlp[] = {"A funny symbol that I can't read has just been input.",
+ "Just continue, I'll change it to 0xFFFD.",
+ NULL };
+ deletions_allowed=false;
+ tex_error("Text line contains an invalid utf-8 sequence", hlp);
+ deletions_allowed=true;
+}
+
+static integer
+qbuffer_to_unichar (integer *k) {
+ register int ch;
+ int val = 0xFFFD;
+ unsigned char *text = buffer+*k;
+ if ( (ch = *text++)<0x80 ) {
+ val = ch;
+ *k+=1;
+ } else if ( ch<=0xbf ) { /* error */
+ *k+=1;
+ } else if ( ch<=0xdf ) {
+ if ( *text>=0x80 && *text<0xc0 )
+ val = ((ch&0x1f)<<6) | (*text++&0x3f);
+ *k+=2;
+ } else if ( ch<=0xef ) {
+ if ( *text>=0x80 && *text<0xc0 && text[1]>=0x80 && text[1]<0xc0 ) {
+ val = ((ch&0xf)<<12) | ((text[0]&0x3f)<<6) | (text[1]&0x3f);
+ *k+=3;
+ }
+ } else {
+ int w = ( ((ch&0x7)<<2) | ((text[0]&0x30)>>4) )-1, w2;
+ w = (w<<6) | ((text[0]&0xf)<<2) | ((text[1]&0x30)>>4);
+ w2 = ((text[1]&0xf)<<6) | (text[2]&0x3f);
+ val = w*0x400 + w2 + 0x10000;
+ if ( *text<0x80 || text[1]<0x80 || text[2]<0x80 ||
+ *text>=0xc0 || text[1]>=0xc0 || text[2]>=0xc0 )
+ val = 0xFFFD;
+ *k+=4;
+ }
+ if (val==0xFFFD)
+ utf_error();
+ return( val );
+}
+
+static boolean
+get_next_file (void) {
+ SWITCH:
+ if (loc<=limit) { /* current line not yet finished */
+ cur_chr = qbuffer_to_unichar(&loc);
+ RESWITCH:
+ if (detokenized_line) {
+ cur_cmd = (cur_chr==' ' ? 10 : 12) ;
+ } else {
+ do_get_cat_code(cur_cmd);
+ }
+ /*
+ @<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 \PASCAL\ compiler has translated the code. Note that the numeric
+ values for |mid_line|, |skip_blanks|, and |new_line| are spaced
+ apart from each other by |max_char_code+1|, so we can add a character's
+ command code to the state to get a single number that characterizes both.
+ */
+ switch(state+cur_cmd) {
+ case mid_line + ignore:
+ case skip_blanks + ignore:
+ case new_line + ignore:
+ case skip_blanks + spacer:
+ case new_line + spacer: /* @<Cases where character is ignored@> */
+ goto SWITCH;
+ break;
+ case mid_line + escape:
+ case new_line + escape:
+ case skip_blanks + escape: /* @<Scan a control sequence ...@>;*/
+ state = scan_control_sequence();
+ break;
+ case mid_line + active_char:
+ case new_line + active_char:
+ case skip_blanks + active_char: /* @<Process an active-character */
+ cur_cs=cur_chr+active_base;
+ cur_cmd=eq_type(cur_cs);
+ cur_chr=equiv(cur_cs);
+ state=mid_line;
+ break;
+ case mid_line + sup_mark:
+ case new_line + sup_mark:
+ case skip_blanks + sup_mark: /* @<If this |sup_mark| starts */
+ if (process_sup_mark())
+ goto RESWITCH;
+ else
+ state=mid_line;
+ break;
+ case mid_line + invalid_char:
+ case new_line + invalid_char:
+ case skip_blanks + invalid_char: /* @<Decry the invalid character and |goto restart|@>;*/
+ invalid_character_error();
+ return false; /* because state may be token_list now */
+ break;
+ case mid_line + spacer: /* @<Enter |skip_blanks| state, emit a space@>;*/
+ state=skip_blanks;
+ cur_chr=' ';
+ break;
+ case mid_line + car_ret: /* @<Finish line, emit a space@>;*/
+ /* When a character of type |spacer| gets through, its character code is
+ changed to $\.{"\ "}=@'40$. 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.
+ */
+ loc=limit+1;
+ cur_cmd=spacer;
+ cur_chr=' ';
+ break;
+ case skip_blanks + car_ret:
+ case mid_line + comment:
+ case new_line + comment:
+ case skip_blanks + comment: /* @<Finish line, |goto switch|@>;*/
+ loc=limit+1;
+ goto SWITCH;
+ break;
+ case new_line + car_ret: /* @<Finish line, emit a \.{\\par}@>;*/
+ loc=limit+1;
+ cur_cs=par_loc;
+ cur_cmd=eq_type(cur_cs);
+ cur_chr=equiv(cur_cs);
+ break;
+ case skip_blanks + left_brace:
+ case new_line + left_brace:
+ state=mid_line; /* fall through */
+ case mid_line + left_brace:
+ align_state++;
+ break;
+ case skip_blanks + right_brace:
+ case new_line + right_brace:
+ state=mid_line; /* fall through */
+ case mid_line + right_brace:
+ align_state--;
+ break;
+ case mid_line + math_shift:
+ case mid_line + tab_mark:
+ case mid_line + mac_param:
+ case mid_line + sub_mark:
+ case mid_line + letter:
+ case mid_line + other_char:
+ break;
+ default:
+ /*
+ case skip_blanks + math_shift:
+ case skip_blanks + tab_mark:
+ case skip_blanks + mac_param:
+ case skip_blanks + sub_mark:
+ case skip_blanks + letter:
+ case skip_blanks + other_char:
+ case new_line + math_shift:
+ case new_line + tab_mark:
+ case new_line + mac_param:
+ case new_line + sub_mark:
+ case new_line + letter:
+ case new_line + other_char:
+ */
+ state=mid_line;
+ break;
+ }
+ } else {
+ if (current_ocp_lstack>0) {
+ pop_input();
+ return false;
+ }
+ if (name!=21)
+ state=new_line;
+
+ /*
+ @<Move to next line of file,
+ or |goto restart| if there is no next line,
+ or |return| if a \.{\\read} line has finished@>;
+ */
+ do {
+ next_line_retval r = next_line();
+ if (r==next_line_return) {
+ return true;
+ } else if (r==next_line_restart) {
+ return false;
+ }
+ } while (0);
+ check_interrupt();
+ goto SWITCH;
+ }
+ return true;
+}
+
+#define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f'))
+
+#define add_nybble(a) do { \
+ if (a<='9') cur_chr=(cur_chr<<4)+a-'0'; \
+ else cur_chr=(cur_chr<<4)+a-'a'+10; \
+ } while (0)
+
+#define hex_to_cur_chr do { \
+ if (c<='9') cur_chr=c-'0'; \
+ else cur_chr=c-'a'+10; \
+ add_nybble(cc); \
+ } while (0)
+
+#define four_hex_to_cur_chr do { \
+ hex_to_cur_chr; \
+ add_nybble(ccc); add_nybble(cccc); \
+ } while (0)
+
+#define five_hex_to_cur_chr do { \
+ four_hex_to_cur_chr; \
+ add_nybble(ccccc); \
+ } while (0)
+
+#define six_hex_to_cur_chr do { \
+ five_hex_to_cur_chr; \
+ add_nybble(cccccc); \
+ } while (0)
+
+
+/* Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.*/
+
+static boolean
+process_sup_mark(void) {
+ if (cur_chr==buffer[loc]) {
+ int c,cc;
+ if (loc<limit) {
+ if ((cur_chr==buffer[loc+1]) && (cur_chr==buffer[loc+2])
+ && (cur_chr==buffer[loc+3]) && (cur_chr==buffer[loc+4])
+ && ((loc+10)<=limit)) {
+ int ccc,cccc,ccccc,cccccc; /* constituents of a possible expanded code */
+ c=buffer[loc+5];
+ cc=buffer[loc+6];
+ ccc=buffer[loc+7];
+ cccc=buffer[loc+8];
+ ccccc=buffer[loc+9];
+ cccccc=buffer[loc+10];
+ if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) && (is_hex(cccc))
+ && (is_hex(ccccc)) && (is_hex(cccccc))) {
+ loc=loc+11;
+ six_hex_to_cur_chr;
+ return true;
+ }
+ }
+ if ((cur_chr==buffer[loc+1]) && (cur_chr==buffer[loc+2])
+ && (cur_chr==buffer[loc+3]) && ((loc+8)<=limit)) {
+ int ccc,cccc,ccccc; /* constituents of a possible expanded code */
+ c=buffer[loc+4];
+ cc=buffer[loc+5];
+ ccc=buffer[loc+6];
+ cccc=buffer[loc+7];
+ ccccc=buffer[loc+8];
+ if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) && (is_hex(cccc)) && (is_hex(ccccc))) {
+ loc=loc+9;
+ five_hex_to_cur_chr;
+ return true;
+ }
+ }
+ if ((cur_chr==buffer[loc+1]) && (cur_chr==buffer[loc+2]) && ((loc+6)<=limit)) {
+ int ccc,cccc; /* constituents of a possible expanded code */
+ c=buffer[loc+3];
+ cc=buffer[loc+4];
+ ccc=buffer[loc+5];
+ cccc=buffer[loc+6];
+ if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) && (is_hex(cccc))) {
+ loc=loc+7;
+ four_hex_to_cur_chr;
+ return true;
+ }
+ }
+ c = buffer[loc+1];
+ if (c<0200) { /* yes we have an expanded char */
+ loc=loc+2;
+ if (is_hex(c) && loc<=limit) {
+ cc=buffer[loc];
+ if (is_hex(cc)){
+ incr(loc);
+ hex_to_cur_chr;
+ return true;
+ }
+ }
+ cur_chr = (c<0100 ? c+0100 : c-0100);
+ return true;
+ }
+ }
+ }
+ 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.
+
+ The program that scans a control sequence has been written carefully
+ in order to avoid the blowups that might otherwise occur if a malicious
+ user tried something like `\.{\\catcode\'15=0}'. The algorithm might
+ look at |buffer[limit+1]|, but it never looks at |buffer[limit+2]|.
+
+ If expanded characters like `\.{\^\^A}' or `\.{\^\^df}'
+ appear in or just following
+ a control sequence name, they are converted to single characters in the
+ buffer and the process is repeated, slowly but surely.
+*/
+
+static boolean check_expanded_code (integer *kk) ; /* below */
+
+static int scan_control_sequence (void ) {
+ int retval = mid_line;
+ if (loc>limit) {
+ cur_cs=null_cs; /* |state| is irrelevant in this case */
+ } else {
+ register int cat; /* |cat_code(cur_chr)|, usually */
+ while (1) {
+ integer k=loc;
+ cur_chr = qbuffer_to_unichar(&k);
+ do_get_cat_code(cat);
+ if (cat!=letter||k>limit) {
+ retval = (cat==spacer ? skip_blanks : mid_line);
+ if (cat==sup_mark && check_expanded_code(&k)) /* @<If an expanded...@>; */
+ continue;
+ } else {
+ retval=skip_blanks;
+ do {
+ cur_chr = qbuffer_to_unichar(&k);
+ do_get_cat_code(cat);
+ } while (cat==letter&&k<=limit);
+
+ if (cat==sup_mark && check_expanded_code(&k)) /* @<If an expanded...@>; */
+ continue;
+ if (cat!=letter) {
+ decr(k);
+ if (cur_chr>0xFFFF) decr(k);
+ if (cur_chr>0x7FF) decr(k);
+ if (cur_chr>0x7F) decr(k);
+ } /* now |k| points to first nonletter */
+ }
+ cur_cs=id_lookup(loc,k-loc);
+ loc=k;
+ break;
+ }
+ }
+ cur_cmd=eq_type(cur_cs);
+ cur_chr=equiv(cur_cs);
+ return retval;
+}
+
+/* Whenever we reach the following piece of code, we will have
+ |cur_chr=buffer[k-1]| and |k<=limit+1| and |cat=get_cat_code(cat_code_table,cur_chr)|. If an
+ expanded code like \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)..(k+1)]|
+ or |buffer[(k-1)..(k+2)]|, we
+ will store the corresponding code in |buffer[k-1]| and shift the rest of
+ the buffer left two or three places.
+*/
+
+static boolean
+check_expanded_code (integer *kk) {
+ int l;
+ int k = *kk;
+ int d =1; /* number of excess characters in an expanded code */
+ int c,cc,ccc,cccc,ccccc,cccccc; /* constituents of a possible expanded code */
+ if (buffer[k]==cur_chr && k<limit) {
+ if ((cur_chr==buffer[k+1]) && (cur_chr==buffer[k+2]) && ((k+6)<=limit)) {
+ d = 4;
+ if ((cur_chr==buffer[k+3]) && ((k+8)<=limit))
+ d = 5;
+ if ((cur_chr==buffer[k+4]) && ((k+10)<=limit))
+ d = 6;
+ c = buffer[k+d-1];
+ cc = buffer[k+d];
+ ccc = buffer[k+d+1];
+ cccc= buffer[k+d+2];
+ if (d==6) {
+ ccccc =buffer[k+d+3];
+ cccccc=buffer[k+d+4];
+ if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc)
+ && is_hex(ccccc) && is_hex(cccccc))
+ six_hex_to_cur_chr;
+ } else if (d==5) {
+ ccccc=buffer[k+d+3];
+ if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc) && is_hex(ccccc))
+ five_hex_to_cur_chr;
+ } else {
+ if (is_hex(c) && is_hex(cc) && is_hex(ccc) && is_hex(cccc))
+ four_hex_to_cur_chr;
+ }
+ } else {
+ c=buffer[k+1];
+ if (c<0200) {
+ d=1;
+ if (is_hex(c) && (k+2)<=limit) {
+ cc=buffer[k+2];
+ if (is_hex(c) && is_hex(cc)) {
+ d=2;
+ hex_to_cur_chr;
+ }
+ } else if (c<0100) {
+ cur_chr=c+0100;
+ } else {
+ cur_chr=c-0100;
+ }
+ }
+ }
+ if (d>2)
+ d=2*d-1;
+ else
+ d++;
+ if (cur_chr <= 0x7F) {
+ buffer[k-1]= cur_chr;
+ } else if (cur_chr<=0x7FF) {
+ buffer[k-1]=0xC0 + cur_chr / 0x40;
+ k++; d--;
+ buffer[k-1]=0x80 + cur_chr % 0x40;
+ } else if (cur_chr<=0xFFFF){
+ buffer[k-1]=0xE0 + cur_chr / 0x1000;
+ k++; d--;
+ buffer[k-1]=0x80 + (cur_chr % 0x1000) / 0x40;
+ k++; d--;
+ buffer[k-1]=0x80 + (cur_chr % 0x1000) % 0x40;
+ } else {
+ buffer[k-1]=0xF0 + cur_chr / 0x40000;
+ k++; d--;
+ buffer[k-1]=0x80 + (cur_chr % 0x40000) / 0x1000;
+ k++; d--;
+ buffer[k-1]=0x80 + ((cur_chr % 0x40000) % 0x1000) / 0x40;
+ k++; d--;
+ buffer[k-1]=0x80 + ((cur_chr % 0x40000) % 0x1000) % 0x40;
+ }
+ l = k;
+ limit = limit - d;
+ while (l<=limit) {
+ buffer[l]=buffer[l+d];
+ l++;
+ }
+ *kk =k;
+ return true;
+ }
+ return false;
+}
+
+#define end_line_char_inactive ((end_line_char<0)||(end_line_char>127))
+
+/* The global variable |force_eof| is normally |false|; it is set |true|
+ by an \.{\\endinput} command.
+
+ @<Glob...@>=
+ @!force_eof:boolean; {should the next \.{\\input} be aborted early?}
+
+*/
+
+
+/* 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) {
+ integer tab; /* a category table */
+ boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */
+ detokenized_line=false;
+ local_catcode_table=false;
+ if (name>17) {
+ /* @<Read next line of file into |buffer|, or |goto restart| if the file has ended@> */
+ incr(line);
+ first=start;
+ if (!force_eof) {
+ if (name<=20) {
+ if (pseudo_input()) { /* not end of file */
+ firm_up_the_line(); /* this sets |limit| */
+ if ((name==19)&&(pseudo_lines(pseudo_files)==null))
+ inhibit_eol=true;
+ } else if ((every_eof!=null)&&!eof_seen[index]) {
+ limit=first-1; eof_seen[index]=true; /* fake one empty line */
+ if (name!=19)
+ begin_token_list(every_eof,every_eof_text);
+ return next_line_restart;
+ } else {
+ force_eof=true;
+ }
+ } else {
+ if (name==21) {
+ if (luacstring_input()) { /* not end of strings */
+ firm_up_the_line();
+ if ((luacstring_penultimate())||(luacstring_simple()))
+ inhibit_eol=true;
+ if (!luacstring_simple())
+ state=new_line;
+ if (luacstring_detokenized()) {
+ inhibit_eol=true;
+ detokenized_line=true;
+ } else {
+ if (! luacstring_defaultcattable()) {
+ tab=luacstring_cattable();
+ if (valid_catcode_table(tab)) {
+ local_catcode_table=true;
+ line_catcode_table=tab;
+ }
+ }
+ }
+ } else {
+ force_eof=true;
+ }
+ } else {
+ if (lua_input_ln(cur_file,0,true)) { /* not end of file */
+ firm_up_the_line(); /* this sets |limit| */
+ } else if ((every_eof!=null)&& (!eof_seen[index])) {
+ limit=first-1; eof_seen[index]=true; /* fake one empty line */
+ begin_token_list(every_eof,every_eof_text);
+ return next_line_restart;
+ } else {
+ force_eof=true;
+ }
+ }
+ }
+ }
+ if (force_eof) {
+ if (tracing_nesting>0)
+ if ((grp_stack[in_open]!=cur_boundary)||(if_stack[in_open]!=cond_ptr))
+ if (!((name==19)||(name=21)))
+ file_warning(); /* give warning for some unfinished groups and/or conditionals */
+ if ((name>21)||(name==20)) {
+ if (tracefilenames)
+ print_char(')');
+ open_parens--;
+ /* update_terminal();*/ /* show user that file has been read */
+ }
+ force_eof=false;
+ end_file_reading();
+ return next_line_restart;
+ }
+ if (inhibit_eol||end_line_char_inactive)
+ limit--;
+ else
+ buffer[limit]=end_line_char;
+ first=limit+1;
+ loc=start; /* ready to read */
+ } else {
+ if (!terminal_input) { /* \.{\\read} line has ended */
+ cur_cmd=0;
+ cur_chr=0;
+ return next_line_return; /* OUTER */
+ }
+ if (input_ptr>0) { /* text was inserted during error recovery */
+ end_file_reading();
+ return next_line_restart; /* resume previous level */
+ }
+ if (selector<log_only)
+ open_log_file();
+ if (interaction>nonstop_mode) {
+ if (end_line_char_inactive)
+ limit++;
+ if (limit==start) { /* previous line was empty */
+ tprint_nl("(Please type a command or say `\\end')");
+ }
+ print_ln();
+ first=start;
+ prompt_input((str_number)'*'); /* input on-line into |buffer| */
+ limit=last;
+ if (end_line_char_inactive)
+ limit--;
+ else
+ buffer[limit]=end_line_char;
+ first=limit+1;
+ loc=start;
+ } else {
+ fatal_error(maketexstring("*** (job aborted, no legal \\end found)"));
+ /* nonstop mode, which is intended for overnight batch processing,
+ never waits for on-line input */
+ }
+ }
+ return next_line_ok;
+}
+
+
+
+
+/* Let's consider now what happens when |get_next| is looking at a token list. */
+
+static boolean
+get_next_tokenlist (void) {
+ register halfword t; /* a token*/
+ t=info(loc);
+ loc=link(loc); /* move to next */
+ if (t>=cs_token_flag) { /* a control sequence token */
+ cur_cs=t-cs_token_flag;
+ cur_cmd=eq_type(cur_cs);
+ if (cur_cmd==dont_expand) { /* @<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, |info(loc)| is known to be a control sequence token, and |link(loc)=null|.
+ */
+ cur_cs=info(loc)-cs_token_flag;
+ loc=null;
+ cur_cmd=eq_type(cur_cs);
+ if (cur_cmd>max_command) {
+ cur_cmd=relax;
+ cur_chr=no_expand_flag;
+ return true;
+ }
+ }
+ cur_chr=equiv(cur_cs);
+ } else {
+ cur_cmd=t >> string_offset_bits; /* cur_cmd=t / string_offset; */
+ cur_chr=t & (string_offset-1); /* cur_chr=t % string_offset; */
+ switch (cur_cmd) {
+ case left_brace:
+ align_state++;
+ break;
+ case right_brace:
+ align_state--;
+ break;
+ case out_param: /* @<Insert macro parameter and |goto restart|@>; */
+ begin_token_list(param_stack[param_start+cur_chr-1],parameter);
+ return false;
+ break;
+ }
+ }
+ 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@>
+*/
+
+/* sets |cur_cmd|, |cur_chr|, |cur_cs| to next token */
+
+void
+get_next (void) {
+ RESTART:
+ cur_cs=0;
+ if (state!=token_list) {
+ /* Input from external file, |goto restart| if no input found */
+ if (!get_next_file())
+ goto RESTART;
+ } else {
+ if (loc==null) {
+ end_token_list();
+ goto RESTART; /* list exhausted, resume previous level */
+ } else if (!get_next_tokenlist()) {
+ goto RESTART; /* parameter needs to be expanded */
+ }
+ }
+ /* @<If an alignment entry has just ended, take appropriate action@> */
+ if ((cur_cmd==tab_mark || cur_cmd==car_ret) && align_state==0) {
+ insert_vj_template();
+ goto RESTART;
+ }
+}
+
+void
+get_token_lua (void) {
+ register int callback_id ;
+ callback_id = callback_defined(token_filter_callback);
+ if (callback_id!=0) {
+ while (state==token_list && loc==null && index!=v_template)
+ end_token_list();
+ /* there is some stuff we don't want to see inside the callback */
+ if (!(state==token_list &&
+ ((nofilter==true) || (index==backed_up && loc!=null)))) {
+ do_get_token_lua(callback_id);
+ return;
+ }
+ }
+ get_next();
+}