diff options
author | Martin Schröder <martin@oneiros.de> | 2008-03-24 15:47:15 +0000 |
---|---|---|
committer | Martin Schröder <martin@oneiros.de> | 2008-03-24 15:47:15 +0000 |
commit | 0b5550c241229764120d133af80a0cbab5937ddd (patch) | |
tree | 81309c18d6f0deba7407813cf15b9add2db375d1 /Build/source/texk/web2c/luatexdir/tex | |
parent | 51f55df6a36ce376952a1f2b30bdb4dd0e06548d (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.c | 2095 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/postlinebreak.c | 512 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/texnodes.c | 1694 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/texpdf.c | 169 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/textoken.c | 753 |
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(); +} |