diff options
author | Taco Hoekwater <taco@elvenkind.com> | 2010-04-04 13:55:46 +0000 |
---|---|---|
committer | Taco Hoekwater <taco@elvenkind.com> | 2010-04-04 13:55:46 +0000 |
commit | f372862c42d234710b6057449b46818e63181c90 (patch) | |
tree | a9ebec66dd31ac37aed442502ebd1b9fe76245ec /Build/source/texk/web2c/luatexdir/tex | |
parent | 2dad291ab056f30816e0aea61970f38033c2f6a5 (diff) |
import luatex 0.60.0 (with small local changes, see luatexdir/ChangeLog)
and do an autoreconf
git-svn-id: svn://tug.org/texlive/trunk@17680 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex')
67 files changed, 33846 insertions, 4000 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/align.h b/Build/source/texk/web2c/luatexdir/tex/align.h new file mode 100644 index 00000000000..5afa7a754c8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/align.h @@ -0,0 +1,55 @@ +/* align.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: align.h 3100 2009-11-04 15:09:57Z taco $ */ + +#ifndef ALIGN_H +# define ALIGN_H 1 + +/* We enter `\.{\\span}' into |eqtb| with |tab_mark| as its command code, +and with |span_code| as the command modifier. This makes \TeX\ interpret it +essentially the same as an alignment delimiter like `\.\&', yet it is +recognizably different when we need to distinguish it from a normal delimiter. +It also turns out to be useful to give a special |cr_code| to `\.{\\cr}', +and an even larger |cr_cr_code| to `\.{\\crcr}'. + +The end of a template is represented by two ``frozen'' control sequences +called \.{\\endtemplate}. The first has the command code |end_template|, which +is |>outer_call|, so it will not easily disappear in the presence of errors. +The |get_x_token| routine converts the first into the second, which has |endv| +as its command code. +*/ + +# define tab_mark_cmd_code 1114113 /* {|biggest_char+2|} */ +# define span_code 1114114 /* {|biggest_char+3|} */ +# define cr_code (span_code+1) /* distinct from |span_code| and from any character */ +# define cr_cr_code (cr_code+1) /* this distinguishes \.{\\crcr} from \.{\\cr} */ + + +extern void init_align(void); +extern void initialize_alignments(void); + +extern boolean fin_col(void); +extern void fin_row(void); + +extern void align_peek(void); +extern void insert_vj_template(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/align.w b/Build/source/texk/web2c/luatexdir/tex/align.w new file mode 100644 index 00000000000..fd2d46b8f38 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/align.w @@ -0,0 +1,1153 @@ +% align.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +\def\<#1>{$#1$} + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: align.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/align.w $"; + +@ @c +void fin_align(void); +void init_row(void); +void init_col(void); + +#define noDEBUG + +#define end_template_token (cs_token_flag+frozen_end_template) + +#define prev_depth cur_list.prev_depth_field +#define space_factor cur_list.space_factor_field +#define incompleat_noad cur_list.incompleat_noad_field + +#define every_cr equiv(every_cr_loc) +#define display_indent dimen_par(display_indent_code) +#define max_depth dimen_par(max_depth_code) +#define pdf_ignored_dimen dimen_par(pdf_ignored_dimen_code) +#define overfull_rule dimen_par(overfull_rule_code) + +@ It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because +they cut across so many of the control structures of \TeX. + +Therefore the +present page is probably not the best place for a beginner to start reading +this program; it is better to master everything else first. + +Let us focus our thoughts on an example of what the input might be, in order +to get some idea about how the alignment miracle happens. The example doesn't +do anything useful, but it is sufficiently general to indicate all of the +special cases that must be dealt with; please do not be disturbed by its +apparent complexity and meaninglessness. +$$\vbox{\halign{\.{#}\hfil\cr +{}\\tabskip 2pt plus 3pt\cr +{}\\halign to 300pt\{u1\#v1\&\cr +\hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr +\hskip 50pt u3\#v3\\cr\cr +\hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr +\hskip 25pt \\noalign\{\\vskip 3pt\}\cr +\hskip 25pt b1\\span b2\\cr\cr +\hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$ +Here's what happens: + +\yskip +(0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine +places the 300pt dimension onto the |save_stack|, and an |align_group| +code is placed above it. This will make it possible to complete the alignment +when the matching `\.\}' is found. + +(1) The preamble is scanned next. Macros in the preamble are not expanded, +@^preamble@> +except as part of a tabskip specification. For example, if \.{u2} had been +a macro in the preamble above, it would have been expanded, since \TeX\ +must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list'' +is constructed based on the user's preamble; in our case it contains the +following seven items: +$$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr +{}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr +{}\\alignrecord, width $-\infty$&preamble info for column 1\cr +{}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr +{}\\alignrecord, width $-\infty$&preamble info for column 2\cr +{}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr +{}\\alignrecord, width $-\infty$&preamble info for column 3\cr +{}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$ +These ``alignrecord'' entries have the same size as an |unset_node|, +since they will later be converted into such nodes. These alignrecord +nodes have no |depth| field; this is split into |u_part| and |v_part|, +and they point to token lists for the templates of the alignment. For +example, the |u_part| field in the first alignrecord points to the +token list `\.{u1}', i.e., the template preceding the `\.\#' for +column~1. Furthermore, They have a |span_ptr| instead of a |node_attr| +field, and these |span_ptr| fields are initially set to the value +|end_span|, for reasons explained below. + +(2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble. +It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to +be read again, and the template `\.{u1}' is fed to the scanner. Just +before reading `\.{u1}', \TeX\ goes into restricted horizontal mode. +Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the +{\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv| +token, indicating the end of a column. At this point an |unset_node| is +created, containing the contents of the current hlist (i.e., `\.{u1a1v1}'). +The natural width of this unset node replaces the |width| field of the +alignrecord for column~1; in general, the alignrecords will record the +maximum natural width that has occurred so far in a given column. + +(3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2 +are now bypassed. Again \TeX\ goes into restricted horizontal mode and +makes an |unset_node| from the resulting hlist; but this time the +hlist contains simply `\.{a2}'. The natural width of the new unset box +is remembered in the |width| field of the alignrecord for column~2. + +(4) A third |unset_node| is created for column 3, using essentially the +mechanism that worked for column~1; this unset box contains `\.{u3\\vrule +v3}'. The vertical rule in this case has running dimensions that will later +extend to the height and depth of the whole first row, since each |unset_node| +in a row will eventually inherit the height and depth of its enclosing box. + +(5) The first row has now ended; it is made into a single unset box +comprising the following seven items: +$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 1 column: u1a1v1\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 1 column: a2\cr +{}\\glue 1pt plus 1fil\cr +{}\\unsetbox for 1 column: u3\\vrule v3\cr +{}\\glue 1pt plus 1fil\cr}}$$ +The width of this unset row is unimportant, but it has the correct height +and depth, so the correct baselineskip glue will be computed as the row +is inserted into a vertical list. + +(6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends +additional material (in this case \.{\\vskip 3pt}) to the vertical list. +While processing this material, \TeX\ will be in internal vertical +mode, and |no_align_group| will be on |save_stack|. + +(7) The next row produces an unset box that looks like this: +$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr +{}\\glue 1pt plus 1fil\cr +{}\\unsetbox for 1 column: {\rm(empty)}\cr +{}\\glue 1pt plus 1fil\cr}}$$ +The natural width of the unset box that spans columns 1~and~2 is stored +in a ``span node,'' which we will explain later; the |span_ptr| field of the +alignrecord for column~1 now points to the new span node, and the |span_ptr| +of the span node points to |end_span|. + +(8) The final row produces the unset box +$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 1 column: {\rm(empty)}\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 2 columns: u2c2v2\cr +{}\\glue 1pt plus 1fil\cr}}$$ +A new span node is attached to the alignrecord for column 2. + +(9) The last step is to compute the true column widths and to change all the +unset boxes to hboxes, appending the whole works to the vertical list that +encloses the \.{\\halign}. The rules for deciding on the final widths of +each unset column box will be explained below. + +\yskip\noindent +Note that as \.{\\halign} is being processed, we fearlessly give up control +to the rest of \TeX. At critical junctures, an alignment routine is +called upon to step in and do some little action, but most of the time +these routines just lurk in the background. It's something like +post-hypnotic suggestion. + + + +@ We have mentioned that alignrecords contain no |height| or |depth| fields. +Their |glue_sign| and |glue_order| are pre-empted as well, since it +is necessary to store information about what to do when a template ends. +This information is called the |extra_info| field. + +@c +#define u_part(A) vlink((A)+depth_offset) /* pointer to \<u_j> token list */ +#define v_part(A) vinfo((A)+depth_offset) /* pointer to \<v_j> token list */ +#define span_ptr(A) vinfo((A)+1) /* column spanning list */ +#define extra_info(A) vinfo((A)+list_offset) /* info to remember during template */ + + +@ Alignments can occur within alignments, so a small stack is used to access +the alignrecord information. At each level we have a |preamble| pointer, +indicating the beginning of the preamble list; a |cur_align| pointer, +indicating the current position in the preamble list; a |cur_span| pointer, +indicating the value of |cur_align| at the beginning of a sequence of +spanned columns; a |cur_loop| pointer, indicating the tabskip glue before +an alignrecord that should be copied next if the current list is extended; +and the |align_state| variable, which indicates the nesting of braces so +that \.{\\cr} and \.{\\span} and tab marks are properly intercepted. +There also are pointers |cur_head| and |cur_tail| to the head and tail +of a list of adjustments being moved out from horizontal mode to +vertical~mode, and alike |cur_pre_head| and |cur_pre_tail| for pre-adjust +lists. + +The current values of these nine quantities appear in global variables; +when they have to be pushed down, they are stored in 6-word nodes, and +|align_ptr| points to the topmost such node. + +@c +#define preamble vlink(align_head) /* the current preamble list */ + +pointer cur_align = null; /* current position in preamble list */ +pointer cur_span = null; /* start of currently spanned columns in preamble list */ +pointer cur_loop = null; /* place to copy when extending a periodic preamble */ +pointer align_ptr = null; /* most recently pushed-down alignment stack node */ +pointer cur_head = null, cur_tail = null; /* adjustment list pointers */ +pointer cur_pre_head = null, cur_pre_tail = null; /* pre-adjustment list pointers */ + +/* The |align_state| and |preamble| variables are initialized elsewhere. */ + +@ Alignment stack maintenance is handled by a pair of trivial routines +called |push_alignment| and |pop_alignment|. + +@c +void push_alignment(void) +{ + pointer p; /* the new alignment stack node */ + p = new_node(align_stack_node, 0); + vinfo(p + 1) = align_ptr; + vlink(p + 1) = cur_align; + vinfo(p + 2) = preamble; + vlink(p + 2) = cur_span; + vinfo(p + 3) = cur_loop; + vlink(p + 3) = align_state; + vinfo(p + 4) = cur_head; + vlink(p + 4) = cur_tail; + vinfo(p + 5) = cur_pre_head; + vlink(p + 5) = cur_pre_tail; + align_ptr = p; + cur_head = new_node(temp_node, 0); +} + +void pop_alignment(void) +{ + pointer p; /* the top alignment stack node */ + flush_node(cur_head); + p = align_ptr; + cur_pre_tail = vlink(p + 5); + cur_pre_head = vinfo(p + 5); + cur_tail = vlink(p + 4); + cur_head = vinfo(p + 4); + align_state = vlink(p + 3); + cur_loop = vinfo(p + 3); + cur_span = vlink(p + 2); + preamble = vinfo(p + 2); + cur_align = vlink(p + 1); + align_ptr = vinfo(p + 1); + flush_node(p); +} + + +@ \TeX\ has eight procedures that govern alignments: |init_align| and +|fin_align| are used at the very beginning and the very end; |init_row| and +|fin_row| are used at the beginning and end of individual rows; |init_span| +is used at the beginning of a sequence of spanned columns (possibly involving +only one column); |init_col| and |fin_col| are used at the beginning and +end of individual columns; and |align_peek| is used after \.{\\cr} to see +whether the next item is \.{\\noalign}. + +We shall consider these routines in the order they are first used during +the course of a complete \.{\\halign}, namely |init_align|, |align_peek|, +|init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|. + + +@ The preamble is copied directly, except that \.{\\tabskip} causes a change +to the tabskip glue, thereby possibly expanding macros that immediately +follow it. An appearance of \.{\\span} also causes such an expansion. + +Note that if the preamble contains `\.{\\global\\tabskip}', the `\.{\\global}' +token survives in the preamble and the `\.{\\tabskip}' defines new +tabskip glue (locally). + +@c +void get_preamble_token(void) +{ + RESTART: + get_token(); + while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) { + get_token(); /* this token will be expanded once */ + if (cur_cmd > max_command_cmd) { + expand(); + get_token(); + } + } + if (cur_cmd == endv_cmd) + fatal_error("(interwoven alignment preambles are not allowed)"); + if ((cur_cmd == assign_glue_cmd) + && (cur_chr == glue_base + tab_skip_code)) { + scan_optional_equals(); + scan_glue(glue_val_level); + if (int_par(global_defs_code) > 0) + geq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val); + else + eq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val); + goto RESTART; + } +} + + + +@ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate +mode, \TeX\ calls |init_align|, whose task is to get everything off to a +good start. This mostly involves scanning the preamble and putting its +information into the preamble list. +@^preamble@> + +@c +void init_align(void) +{ + /* label done, done1, done2, continue; */ + pointer save_cs_ptr; /* |warning_index| value for error messages */ + pointer p, r; /* for short-term temporary use */ + save_cs_ptr = cur_cs; /* \.{\\halign} or \.{\\valign}, usually */ + push_alignment(); + align_state = -1000000; /* enter a new alignment level */ + + /* When \.{\\halign} is used as a displayed formula, there should be + no other pieces of mlists present. */ + + if ((cur_list.mode_field == mmode) + && ((cur_list.tail_field != cur_list.head_field) + || (incompleat_noad != null))) { + const char *hlp[] = + { "Displays can use special alignments (like \\eqalignno)", + "only if nothing but the alignment itself is between $$'s.", + "So I've deleted the formulas that preceded this alignment.", + NULL + }; + tex_error("Improper \\halign inside $$'s", hlp); + flush_math(); + } + push_nest(); /* enter a new semantic level */ + /* In vertical modes, |prev_depth| already has the correct value. But + if we are in |mmode| (displayed formula mode), we reach out to the + enclosing vertical mode for the |prev_depth| value that produces the + correct baseline calculations. */ + if (cur_list.mode_field == mmode) { + cur_list.mode_field = -vmode; + prev_depth = nest[nest_ptr - 2].prev_depth_field; + } else if (cur_list.mode_field > 0) { + cur_list.mode_field = -(cur_list.mode_field); + } + scan_spec(align_group); + /* Scan the preamble */ + preamble = null; + cur_align = align_head; + cur_loop = null; + scanner_status = aligning; + warning_index = save_cs_ptr; + align_state = -1000000; + /* at this point, |cur_cmd=left_brace| */ + while (true) { + /* Append the current tabskip glue to the preamble list */ + r = new_param_glue(tab_skip_code); + vlink(cur_align) = r; + cur_align = vlink(cur_align); + + if (cur_cmd == car_ret_cmd) + break; /* \.{\\cr} ends the preamble */ + + /* Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| */ + /* Scan the template \<u_j>, putting the resulting token list in |hold_token_head| */ + /* Spaces are eliminated from the beginning of a template. */ + + p = hold_token_head; + token_link(p) = null; + while (1) { + get_preamble_token(); + if (cur_cmd == mac_param_cmd) + break; + if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) + && (align_state == -1000000)) { + if ((p == hold_token_head) && (cur_loop == null) + && (cur_cmd == tab_mark_cmd)) { + cur_loop = cur_align; + } else { + const char *hlp[] = + { "There should be exactly one # between &'s, when an", + "\\halign or \\valign is being set up. In this case you had", + "none, so I've put one in; maybe that will work.", + NULL + }; + back_input(); + tex_error("Missing # inserted in alignment preamble", hlp); + break; + } + } else if ((cur_cmd != spacer_cmd) || (p != hold_token_head)) { + r = get_avail(); + token_link(p) = r; + p = token_link(p); + token_info(p) = cur_tok; + } + } + r = new_node(align_record_node, 0); + vlink(cur_align) = r; + cur_align = vlink(cur_align); /* a new alignrecord */ + span_ptr(cur_align) = end_span; + width(cur_align) = null_flag; + u_part(cur_align) = token_link(hold_token_head); + /* Scan the template \<v_j>, putting the resulting token list in |hold_token_head| */ + + p = hold_token_head; + token_link(p) = null; + while (1) { + CONTINUE: + get_preamble_token(); + if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) + && (align_state == -1000000)) + break; + if (cur_cmd == mac_param_cmd) { + const char *hlp[] = + { "There should be exactly one # between &'s, when an", + "\\halign or \\valign is being set up. In this case you had", + "more than one, so I'm ignoring all but the first.", + NULL + }; + tex_error("Only one # is allowed per tab", hlp); + goto CONTINUE; + } + r = get_avail(); + token_link(p) = r; + p = token_link(p); + token_info(p) = cur_tok; + } + r = get_avail(); + token_link(p) = r; + p = token_link(p); + token_info(p) = end_template_token; /* put \.{\\endtemplate} at the end */ + + v_part(cur_align) = token_link(hold_token_head); + } + scanner_status = normal; + + new_save_level(align_group); + if (every_cr != null) + begin_token_list(every_cr, every_cr_text); + align_peek(); /* look for \.{\\noalign} or \.{\\omit} */ +} + + +@ The tricky part about alignments is getting the templates into the +scanner at the right time, and recovering control when a row or column +is finished. + +We usually begin a row after each \.{\\cr} has been sensed, unless that +\.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates +the alignment. The |align_peek| routine is used to look ahead and do +the right thing; it either gets a new row started, or gets a \.{\\noalign} +started, or finishes off the alignment. + +@c +void align_peek(void) +{ + RESTART: + align_state = 1000000; + do { + get_x_or_protected(); + } while (cur_cmd == spacer_cmd); + if (cur_cmd == no_align_cmd) { + scan_left_brace(); + new_save_level(no_align_group); + if (cur_list.mode_field == -vmode) + normal_paragraph(); + } else if (cur_cmd == right_brace_cmd) { + fin_align(); + } else if ((cur_cmd == car_ret_cmd) && (cur_chr == cr_cr_code)) { + goto RESTART; /* ignore \.{\\crcr} */ + } else { + init_row(); /* start a new row */ + init_col(); /* start a new column and replace what we peeked at */ + } +} + + +@ The parameter to |init_span| is a pointer to the alignrecord where the +next column or group of columns will begin. A new semantic level is +entered, so that the columns will generate a list for subsequent packaging. + +@c +void init_span(pointer p) +{ + push_nest(); + if (cur_list.mode_field == -hmode) { + space_factor = 1000; + } else { + prev_depth = pdf_ignored_dimen; + normal_paragraph(); + } + cur_span = p; +} + + +@ To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'), +we enter a new semantic level, copy the first tabskip glue, and change +from internal vertical mode to restricted horizontal mode or vice versa. +The |space_factor| and |prev_depth| are not used on this semantic level, +but we clear them to zero just to be tidy. + +@c +void init_row(void) +{ + push_nest(); + cur_list.mode_field = (-hmode - vmode) - cur_list.mode_field; + if (cur_list.mode_field == -hmode) + space_factor = 0; + else + prev_depth = 0; + tail_append(new_glue(glue_ptr(preamble))); + subtype(cur_list.tail_field) = tab_skip_code + 1; + cur_align = vlink(preamble); + cur_tail = cur_head; + cur_pre_tail = cur_pre_head; + init_span(cur_align); +} + + +@ When a column begins, we assume that |cur_cmd| is either |omit| or else +the current token should be put back into the input until the \<u_j> +template has been scanned. (Note that |cur_cmd| might be |tab_mark| or +|car_ret|.) We also assume that |align_state| is approximately 1000000 at +this time. We remain in the same mode, and start the template if it is +called for. + +@c +void init_col(void) +{ + extra_info(cur_align) = cur_cmd; + if (cur_cmd == omit_cmd) + align_state = 0; + else { + back_input(); + begin_token_list(u_part(cur_align), u_template); + } /* now |align_state=1000000| */ +} + + +@ The scanner sets |align_state| to zero when the \<u_j> template ends. When +a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|, +the scanner activates the following code, which fires up the \<v_j> template. +We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|, +|span_code|, or a character code, depending on how the column text has ended. + +This part of the program had better not be activated when the preamble +to another alignment is being scanned, or when no alignment preamble is active. + +@c +void insert_vj_template(void) +{ + if ((scanner_status == aligning) || (cur_align == null)) + fatal_error("(interwoven alignment preambles are not allowed)"); + cur_cmd = extra_info(cur_align); + extra_info(cur_align) = cur_chr; + if (cur_cmd == omit_cmd) + begin_token_list(omit_template, v_template); + else + begin_token_list(v_part(cur_align), v_template); + align_state = 1000000; +} + +/* Determine the stretch order */ +#define determine_stretch_order() do { \ + if (total_stretch[filll]!=0) o=filll; \ + else if (total_stretch[fill]!=0) o=fill; \ + else if (total_stretch[fil]!=0) o=fil; \ + else if (total_stretch[sfi]!=0) o=sfi; \ + else o=normal; \ + } while (0) + + +/* Determine the shrink order */ +#define determine_shrink_order() do { \ + if (total_shrink[filll]!=0) o=filll; \ + else if (total_shrink[fill]!=0) o=fill; \ + else if (total_shrink[fil]!=0) o=fil; \ + else if (total_shrink[sfi]!=0) o=sfi; \ + else o=normal; \ + } while (0) + + + +@ When the |endv| command at the end of a \<v_j> template comes through the +scanner, things really start to happen; and it is the |fin_col| routine +that makes them happen. This routine returns |true| if a row as well as a +column has been finished. + +@c +boolean fin_col(void) +{ + pointer p; /* the alignrecord after the current one */ + pointer q, r; /* temporary pointers for list manipulation */ + pointer s; /* a new span node */ + pointer u; /* a new unset box */ + scaled w; /* natural width */ + unsigned char o; /* order of infinity */ + halfword n; /* span counter */ + if (cur_align == null) + confusion("endv"); + q = vlink(cur_align); + if (q == null) + confusion("endv"); + if (align_state < 500000) + fatal_error("(interwoven alignment preambles are not allowed)"); + p = vlink(q); + /* If the preamble list has been traversed, check that the row has ended */ + if ((p == null) && (extra_info(cur_align) < cr_code)) { + if (cur_loop != null) { + /* Lengthen the preamble periodically */ + r = new_node(align_record_node, 0); + vlink(q) = r; + p = vlink(q); /* a new alignrecord */ + span_ptr(p) = end_span; + width(p) = null_flag; + cur_loop = vlink(cur_loop); + + /* Copy the templates from node |cur_loop| into node |p| */ + q = hold_token_head; + r = u_part(cur_loop); + while (r != null) { + s = get_avail(); + token_link(q) = s; + q = token_link(q); + token_info(q) = token_info(r); + r = token_link(r); + } + token_link(q) = null; + u_part(p) = token_link(hold_token_head); + q = hold_token_head; + r = v_part(cur_loop); + while (r != null) { + s = get_avail(); + token_link(q) = s; + q = token_link(q); + token_info(q) = token_info(r); + r = token_link(r); + } + token_link(q) = null; + v_part(p) = token_link(hold_token_head); + + cur_loop = vlink(cur_loop); + r = new_glue(glue_ptr(cur_loop)); + vlink(p) = r; + } else { + const char *hlp[] = + { "You have given more \\span or & marks than there were", + "in the preamble to the \\halign or \\valign now in progress.", + "So I'll assume that you meant to type \\cr instead.", + NULL + }; + extra_info(cur_align) = cr_code; + tex_error("Extra alignment tab has been changed to \\cr", hlp); + } + } + if (extra_info(cur_align) != span_code) { + unsave(); + new_save_level(align_group); + /* Package an unset box for the current column and record its width */ + if (cur_list.mode_field == -hmode) { + adjust_tail = cur_tail; + pre_adjust_tail = cur_pre_tail; + u = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, + additional, align_set_group, -1); + w = width(u); + cur_tail = adjust_tail; + adjust_tail = null; + cur_pre_tail = pre_adjust_tail; + pre_adjust_tail = null; + } else { + u = filtered_vpackage(vlink(cur_list.head_field), 0, additional, 0, + align_set_group, -1); + w = height(u); + } + n = min_quarterword; /* this represents a span count of 1 */ + if (cur_span != cur_align) { + /* Update width entry for spanned columns */ + q = cur_span; + do { + incr(n); + q = vlink(vlink(q)); + } while (q != cur_align); + if (n > max_quarterword) + confusion("too many spans"); /* this can happen, but won't */ + q = cur_span; + while (span_span(span_ptr(q)) < n) { + q = span_ptr(q); + } + if (span_span(span_ptr(q)) > n) { + s = new_span_node(span_ptr(q), n, w); + span_ptr(q) = s; + } else if (width(span_ptr(q)) < w) { + width(span_ptr(q)) = w; + } + + } else if (w > width(cur_align)) { + width(cur_align) = w; + } + type(u) = unset_node; + span_count(u) = (quarterword) n; + determine_stretch_order(); + glue_order(u) = o; + glue_stretch(u) = total_stretch[o]; + determine_shrink_order(); + glue_sign(u) = o; + glue_shrink(u) = total_shrink[o]; + pop_nest(); + vlink(cur_list.tail_field) = u; + cur_list.tail_field = u; + + /* Copy the tabskip glue between columns */ + tail_append(new_glue(glue_ptr(vlink(cur_align)))); + subtype(cur_list.tail_field) = tab_skip_code + 1; + + if (extra_info(cur_align) >= cr_code) { + return true; + } + init_span(p); + } + align_state = 1000000; + do { + get_x_or_protected(); + } while (cur_cmd == spacer_cmd); + cur_align = p; + init_col(); + return false; +} + + + +@ A span node is a 3-word record containing |width|, |span_span|, and +|span_ptr| fields. The |span_span| field indicates the number of +spanned columns; the |span_ptr| field points to a span node for the same +starting column, having a greater extent of spanning, or to +|end_span|, which has the largest possible |span_span| field; the |width| +field holds the largest natural width corresponding to a particular +set of spanned columns. + +A list of the maximum widths so far, for spanned columns starting at a +given column, begins with the |span_ptr| field of the alignrecord for +that column. The code has to make sure that there is room for +|span_ptr| in both the alignrecord and the span nodes, which is why +|span_ptr| replaces |node_attr|. +@^data structure assumptions@> + +The |new_span_node| function is defined in |texnodes.c|. + +@c +#ifndef span_span +# define span_span(A) vlink((A)+1) /* that is normally |alink| */ +#endif + + +@ At the end of a row, we append an unset box to the current vlist (for +\.{\\halign}) or the current hlist (for \.{\\valign}). This unset box +contains the unset boxes for the columns, separated by the tabskip glue. +Everything will be set later. + +@c +void fin_row(void) +{ + pointer p; /* the new unset box */ + if (cur_list.mode_field == -hmode) { + p = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, + additional, fin_row_group, -1); + pop_nest(); + if (cur_pre_head != cur_pre_tail) + append_list(cur_pre_head, cur_pre_tail); + append_to_vlist(p); + if (cur_head != cur_tail) + append_list(cur_head, cur_tail); + } else { + p = filtered_vpackage(vlink(cur_list.head_field), 0, additional, + max_depth, fin_row_group, -1); + pop_nest(); + vlink(cur_list.tail_field) = p; + cur_list.tail_field = p; + space_factor = 1000; + } + type(p) = unset_node; + glue_stretch(p) = 0; + if (every_cr != null) + begin_token_list(every_cr, every_cr_text); + align_peek(); + /* note that |glue_shrink(p)=0| since |glue_shrink==shift_amount| */ +} + + +@ Finally, we will reach the end of the alignment, and we can breathe a +sigh of relief that memory hasn't overflowed. All the unset boxes will now be +set so that the columns line up, taking due account of spanned columns. + +@c +void fin_align(void) +{ + pointer p, q, r, s, u, v, rr; /* registers for the list operations */ + scaled t, w; /* width of column */ + scaled o; /* shift offset for unset boxes */ + halfword n; /* matching span amount */ + scaled rule_save; /* temporary storage for |overfull_rule| */ + halfword pd; /* temporary storage for |prev_depth| */ + if (cur_group != align_group) + confusion("align1"); + unsave(); /* that |align_group| was for individual entries */ + if (cur_group != align_group) + confusion("align0"); + unsave(); /* that |align_group| was for the whole alignment */ + if (nest[nest_ptr - 1].mode_field == mmode) + o = display_indent; + else + o = 0; + /* Go through the preamble list, determining the column widths and + * changing the alignrecords to dummy unset boxes + */ + +/* It's time now to dismantle the preamble list and to compute the column +widths. Let $w_{ij}$ be the maximum of the natural widths of all entries +that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$ +contains $w_{ii}$ in its |width| field, and there is also a linked list of +the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field; +these span nodes contain the value $j-i+|min_quarterword|$ in their +|link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which +we regard as $-\infty$. + +The final column widths are defined by the formula +$$w_j=\max_{1\L i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$ +where $t_k$ is the natural width of the tabskip glue between columns +$k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range +|1<=i<=j| (i.e., if every entry that involved column~|j| also involved +column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after +column~|j|. + +\TeX\ computes these values by using the following scheme: First $w_1=w_{11}$. +Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$. +Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$ +for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its +value is changed to zero and so is the next tabskip. +*/ + q = vlink(preamble); + do { + flush_list(u_part(q)); + flush_list(v_part(q)); + p = vlink(vlink(q)); + if (width(q) == null_flag) { + /* Nullify |width(q)| and the tabskip glue following this column */ + width(q) = 0; + r = vlink(q); + s = glue_ptr(r); + if (s != zero_glue) { + add_glue_ref(zero_glue); + delete_glue_ref(s); + glue_ptr(r) = zero_glue; + } + } + if (span_ptr(q) != end_span) { + /* Merge the widths in the span nodes of |q| with those of |p|, + destroying the span nodes of |q| */ + /* + Merging of two span-node lists is a typical exercise in the manipulation of + linearly linked data structures. The essential invariant in the following + |repeat| loop is that we want to dispense with node |r|, in |q|'s list, + and |u| is its successor; all nodes of |p|'s list up to and including |s| + have been processed, and the successor of |s| matches |r| or precedes |r| + or follows |r|, according as |link(r)=n| or |link(r)>n| or |link(r)<n|. + */ + t = width(q) + width(glue_ptr(vlink(q))); + r = span_ptr(q); + s = end_span; + span_ptr(s) = p; + n = min_quarterword + 1; + do { + width(r) = width(r) - t; + u = span_ptr(r); + while (span_span(r) > n) { + s = span_ptr(s); + n = span_span(span_ptr(s)) + 1; + } + if (span_span(r) < n) { + span_ptr(r) = span_ptr(s); + span_ptr(s) = r; + decr(span_span(r)); + s = r; + } else { + if (width(r) > width(span_ptr(s))) + width(span_ptr(s)) = width(r); + flush_node(r); + } + r = u; + } while (r != end_span); + } + type(q) = unset_node; + span_count(q) = min_quarterword; + height(q) = 0; + depth(q) = 0; + glue_order(q) = normal; + glue_sign(q) = normal; + glue_stretch(q) = 0; + glue_shrink(q) = 0; + q = p; + } while (q != null); + + /* Package the preamble list, to determine the actual tabskip glue amounts, + and let |p| point to this prototype box */ + /* Now the preamble list has been converted to a list of alternating unset + boxes and tabskip glue, where the box widths are equal to the final + column sizes. In case of \.{\\valign}, we change the widths to heights, + so that a correct error message will be produced if the alignment is + overfull or underfull. + */ + + decr(save_ptr); + pack_begin_line = -cur_list.ml_field; + if (cur_list.mode_field == -vmode) { + rule_save = overfull_rule; + overfull_rule = 0; /* prevent rule from being packaged */ + p = hpack(preamble, saved_value(0), saved_level(0), -1); + overfull_rule = rule_save; + } else { + q = vlink(preamble); + do { + height(q) = width(q); + width(q) = 0; + q = vlink(vlink(q)); + } while (q != null); + p = filtered_vpackage(preamble, saved_value(0), saved_level(0), + max_depth, preamble_group, -1); + q = vlink(preamble); + do { + width(q) = height(q); + height(q) = 0; + q = vlink(vlink(q)); + } while (q != null); + } + pack_begin_line = 0; + + /* Set the glue in all the unset boxes of the current list */ + q = vlink(cur_list.head_field); + s = cur_list.head_field; + while (q != null) { + if (!is_char_node(q)) { + if (type(q) == unset_node) { + /* Set the unset box |q| and the unset boxes in it */ + /* The unset box |q| represents a row that contains one or more unset boxes, + depending on how soon \.{\\cr} occurred in that row. */ + + if (cur_list.mode_field == -vmode) { + type(q) = hlist_node; + subtype(q) = 0; + width(q) = width(p); + } else { + type(q) = vlist_node; + subtype(q) = 0; + height(q) = height(p); + } + glue_order(q) = glue_order(p); + glue_sign(q) = glue_sign(p); + glue_set(q) = glue_set(p); + shift_amount(q) = o; + r = vlink(list_ptr(q)); + s = vlink(list_ptr(p)); + do { + /* Set the glue in node |r| and change it from an unset node */ + /* A box made from spanned columns will be followed by tabskip glue nodes and + by empty boxes as if there were no spanning. This permits perfect alignment + of subsequent entries, and it prevents values that depend on floating point + arithmetic from entering into the dimensions of any boxes. + */ + n = span_count(r); + t = width(s); + w = t; + u = hold_head; + while (n > min_quarterword) { + decr(n); + /* Append tabskip glue and an empty box to list |u|, + and update |s| and |t| as the prototype nodes are passed */ + + s = vlink(s); + v = glue_ptr(s); + vlink(u) = new_glue(v); + u = vlink(u); + subtype(u) = tab_skip_code + 1; + t = t + width(v); + if (glue_sign(p) == stretching) { + if (stretch_order(v) == glue_order(p)) + t = t + + round(float_cast(glue_set(p)) * + float_cast(stretch(v))); + } else if (glue_sign(p) == shrinking) { + if (shrink_order(v) == glue_order(p)) + t = t - + round(float_cast(glue_set(p)) * + float_cast(shrink(v))); + } + s = vlink(s); + rr = new_null_box(); + vlink(u) = rr; + u = vlink(u); + t = t + width(s); + if (cur_list.mode_field == -vmode) { + width(u) = width(s); + } else { + type(u) = vlist_node; + height(u) = width(s); + } + + } + if (cur_list.mode_field == -vmode) { + /* Make the unset node |r| into an |hlist_node| of width |w|, + setting the glue as if the width were |t| */ + + height(r) = height(q); + depth(r) = depth(q); + if (t == width(r)) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } else if (t > width(r)) { + glue_sign(r) = stretching; + if (glue_stretch(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else + glue_set(r) = + unfloat((double) (t - width(r)) / + glue_stretch(r)); + } else { + glue_order(r) = glue_sign(r); + glue_sign(r) = shrinking; + if (glue_shrink(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else if ((glue_order(r) == normal) + && (width(r) - t > glue_shrink(r))) + set_glue_ratio_one(glue_set(r)); + else + glue_set(r) = + unfloat((double) (width(r) - t) / + glue_shrink(r)); + } + width(r) = w; + type(r) = hlist_node; + + } else { + /* Make the unset node |r| into a |vlist_node| of height |w|, + setting the glue as if the height were |t| */ + + width(r) = width(q); + if (t == height(r)) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } else if (t > height(r)) { + glue_sign(r) = stretching; + if (glue_stretch(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else + glue_set(r) = + unfloat((t - height(r)) / glue_stretch(r)); + } else { + glue_order(r) = glue_sign(r); + glue_sign(r) = shrinking; + if (glue_shrink(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else if ((glue_order(r) == normal) + && (height(r) - t > glue_shrink(r))) + set_glue_ratio_one(glue_set(r)); + else + glue_set(r) = + unfloat((height(r) - t) / glue_shrink(r)); + } + height(r) = w; + type(r) = vlist_node; + + } + subtype(r) = 0; + shift_amount(r) = 0; + if (u != hold_head) { /* append blank boxes to account for spanned nodes */ + vlink(u) = vlink(r); + vlink(r) = vlink(hold_head); + r = u; + } + + r = vlink(vlink(r)); + s = vlink(vlink(s)); + } while (r != null); + + } else if (type(q) == rule_node) { + /* Make the running dimensions in rule |q| extend to the + boundaries of the alignment */ + if (is_running(width(q))) + width(q) = width(p); + if (is_running(height(q))) + height(q) = height(p); + if (is_running(depth(q))) + depth(q) = depth(p); + if (o != 0) { + r = vlink(q); + vlink(q) = null; + q = hpack(q, 0, additional, -1); + shift_amount(q) = o; + vlink(q) = r; + vlink(s) = q; + } + } + } + s = q; + q = vlink(q); + } + flush_node_list(p); + pop_alignment(); + /* Insert the current list into its environment */ + /* We now have a completed alignment, in the list that starts at |cur_list.head_field| + and ends at |cur_list.tail_field|. This list will be merged with the one that encloses + it. (In case the enclosing mode is |mmode|, for displayed formulas, + we will need to insert glue before and after the display; that part of the + program will be deferred until we're more familiar with such operations.) + */ + pd = cur_list.prev_depth_field; + p = vlink(cur_list.head_field); + q = cur_list.tail_field; + pop_nest(); + if (cur_list.mode_field == mmode) { + finish_display_alignment(p, q, pd); + } else { + vlink(cur_list.tail_field) = p; + if (p != null) + cur_list.tail_field = q; + if (cur_list.mode_field == vmode) { + if (!output_active) + lua_node_filter_s(buildpage_filter_callback, "alignment"); + build_page(); + } + } +} + + + +@ The token list |omit_template| just referred to is a constant token +list that contains the special control sequence \.{\\endtemplate} only. + +@c +void initialize_alignments(void) +{ + token_info(omit_template) = end_template_token; /* |link(omit_template)=null| */ + span_span(end_span) = max_quarterword + 1; + span_ptr(end_span) = null; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/arithmetic.h b/Build/source/texk/web2c/luatexdir/tex/arithmetic.h new file mode 100644 index 00000000000..89dce4e4f74 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/arithmetic.h @@ -0,0 +1,81 @@ +/* arithmetic.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: arithmetic.h 3477 2010-03-12 13:59:34Z taco $ */ + +#ifndef ARITHMETIC_H +# define ARITHMETIC_H + +/* |incr| and |decr| are in texmfmp.h */ + + /* #define incr(A) (A)++ *//* increase a variable by unity */ + /* #define decr(A) (A)-- *//* decrease a variable by unity */ + +# define negate(A) (A)=-(A) /* change the sign of a variable */ + +# undef half +extern int half(int x); + +/* +Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples +of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit +positions from the right end of a binary computer word. +*/ + +# define unity 0200000 /* $2^{16}$, represents 1.00000 */ +# define two 0400000 /* $2^{17}$, represents 2.00000 */ + +typedef unsigned int nonnegative_integer; /* $0\L x<2^{31}$ */ + +extern scaled round_decimals(int k); +extern void print_scaled(scaled s); + +extern boolean arith_error; +extern scaled tex_remainder; + +extern scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer); + +# define nx_plus_y(A,B,C) mult_and_add((A),(B),(C),07777777777) +# define mult_integers(A,B) mult_and_add((A),(B),0,017777777777) + +extern scaled x_over_n(scaled x, int n); +extern scaled xn_over_d(scaled x, int n, int d); + +# define inf_bad 10000 /* infinitely bad value */ + +extern halfword badness(scaled t, scaled s); + +# define set_glue_ratio_zero(A) (A)=0.0 /* store the representation of zero ratio */ +# define set_glue_ratio_one(A) (A)=1.0 /* store the representation of unit ratio */ +# define float_cast(A) (float)(A) /* convert from |glue_ratio| to type |real| */ +# define unfloat(A) (glue_ratio)(A) /* convert from |real| to type |glue_ratio| */ +# define float_constant(A) (float)A /* convert |integer| constant to |real| */ +# define float_round round + +extern void initialize_arithmetic(void); + +extern scaled random_seed; /* the default random seed */ + +extern void init_randoms(int seed); +extern int unif_rand(int x); +extern int norm_rand(void); + +extern int fix_int(int val, int min, int max); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/arithmetic.w b/Build/source/texk/web2c/luatexdir/tex/arithmetic.w new file mode 100644 index 00000000000..eec0b309b7e --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/arithmetic.w @@ -0,0 +1,734 @@ +% arithmetic.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +\def\MP{MetaPost} + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: arithmetic.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/arithmetic.w $"; + + +@ The principal computations performed by \TeX\ are done entirely in terms of +integers less than $2^{31}$ in magnitude; and divisions are done only when both +dividend and divisor are nonnegative. Thus, the arithmetic specified in this +program can be carried out in exactly the same way on a wide variety of +computers, including some small ones. Why? Because the arithmetic +calculations need to be spelled out precisely in order to guarantee that +\TeX\ will produce identical output on different machines. If some +quantities were rounded differently in different implementations, we would +find that line breaks and even page breaks might occur in different places. +Hence the arithmetic of \TeX\ has been designed with care, and systems that +claim to be implementations of \TeX82 should follow precisely the +@:TeX82}{\TeX82@> +calculations as they appear in the present program. + +(Actually there are three places where \TeX\ uses |div| with a possibly negative +numerator. These are harmless; see |div| in the index. Also if the user +sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic +information will involve negative-numerator division. The same remarks +apply for |mod| as well as for |div|.) + +Here is a routine that calculates half of an integer, using an +unambiguous convention with respect to signed odd numbers. + +@c +int half(int x) +{ + if (odd(x)) + return ((x + 1) / 2); + else + return (x / 2); +} + + +@ The following function is used to create a scaled integer from a given decimal +fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is +given in |dig[i]|, and the calculation produces a correctly rounded result. + +@c +scaled round_decimals(int k) +{ /* converts a decimal fraction */ + int a; /* the accumulator */ + a = 0; + while (k-- > 0) { + a = (a + dig[k] * two) / 10; + } + return ((a + 1) / 2); +} + + +@ Conversely, here is a procedure analogous to |print_int|. If the output +of this procedure is subsequently read by \TeX\ and converted by the +|round_decimals| routine above, it turns out that the original value will +be reproduced exactly; the ``simplest'' such decimal number is output, +but there is always at least one digit following the decimal point. + +The invariant relation in the \&{repeat} loop is that a sequence of +decimal digits yet to be printed will yield the original number if and only if +they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$. +We can stop if and only if $f=0$ satisfies this condition; the loop will +terminate before $s$ can possibly become zero. + +@c +void print_scaled(scaled s) +{ /* prints scaled real, rounded to five digits */ + scaled delta; /* amount of allowable inaccuracy */ + if (s < 0) { + print_char('-'); + negate(s); /* print the sign, if negative */ + } + print_int(s / unity); /* print the integer part */ + print_char('.'); + s = 10 * (s % unity) + 5; + delta = 10; + do { + if (delta > unity) + s = s + 0100000 - 50000; /* round the last digit */ + print_char('0' + (s / unity)); + s = 10 * (s % unity); + delta = delta * 10; + } while (s > delta); +} + +@ Physical sizes that a \TeX\ user specifies for portions of documents are +represented internally as scaled points. Thus, if we define an `sp' (scaled +@^sp@> +point) as a unit equal to $2^{-16}$ printer's points, every dimension +inside of \TeX\ is an integer number of sp. There are exactly +4,736,286.72 sp per inch. Users are not allowed to specify dimensions +larger than $2^{30}-1$ sp, which is a distance of about 18.892 feet (5.7583 +meters); two such quantities can be added without overflow on a 32-bit +computer. + +The present implementation of \TeX\ does not check for overflow when +@^overflow in arithmetic@> +dimensions are added or subtracted. This could be done by inserting a +few dozen tests of the form `\ignorespaces|if x>=010000000000 then +@t\\{report\_overflow}@>|', but the chance of overflow is so remote that +such tests do not seem worthwhile. + +\TeX\ needs to do only a few arithmetic operations on scaled quantities, +other than addition and subtraction, and the following subroutines do most of +the work. A single computation might use several subroutine calls, and it is +desirable to avoid producing multiple error messages in case of arithmetic +overflow; so the routines set the global variable |arith_error| to |true| +instead of reporting errors directly to the user. Another global variable, +|tex_remainder|, holds the remainder after a division. + +@c +boolean arith_error; /* has arithmetic overflow occurred recently? */ +scaled tex_remainder; /* amount subtracted to get an exact division */ + + +@ The first arithmetical subroutine we need computes $nx+y$, where |x| +and~|y| are |scaled| and |n| is an integer. We will also use it to +multiply integers. + +@c +scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer) +{ + if (n == 0) + return y; + if (n < 0) { + negate(x); + negate(n); + } + if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) { + return (n * x + y); + } else { + arith_error = true; + return 0; + } +} + +@ We also need to divide scaled dimensions by integers. +@c +scaled x_over_n(scaled x, int n) +{ + boolean negative; /* should |tex_remainder| be negated? */ + negative = false; + if (n == 0) { + arith_error = true; + tex_remainder = x; + return 0; + } else { + if (n < 0) { + negate(x); + negate(n); + negative = true; + } + if (x >= 0) { + tex_remainder = x % n; + if (negative) + negate(tex_remainder); + return (x / n); + } else { + tex_remainder = -((-x) % n); + if (negative) + negate(tex_remainder); + return (-((-x) / n)); + } + } +} + + +@ Then comes the multiplication of a scaled number by a fraction |n/d|, +where |n| and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is +positive. It would be too dangerous to multiply by~|n| and then divide +by~|d|, in separate operations, since overflow might well occur; and it +would be too inaccurate to divide by |d| and then multiply by |n|. Hence +this subroutine simulates 1.5-precision arithmetic. + +@c +scaled xn_over_d(scaled x, int n, int d) +{ + nonnegative_integer t, u, v, xx, dd; /* intermediate quantities */ + boolean positive = true; /* was |x>=0|? */ + if (x < 0) { + negate(x); + positive = false; + } + xx = (nonnegative_integer) x; + dd = (nonnegative_integer) d; + t = ((xx % 0100000) * (nonnegative_integer) n); + u = ((xx / 0100000) * (nonnegative_integer) n + (t / 0100000)); + v = (u % dd) * 0100000 + (t % 0100000); + if (u / dd >= 0100000) + arith_error = true; + else + u = 0100000 * (u / dd) + (v / dd); + if (positive) { + tex_remainder = (int) (v % dd); + return (scaled) u; + } else { + /* casts are for ms cl */ + tex_remainder = -(int) (v % dd); + return -(scaled) (u); + } +} + + +@ The next subroutine is used to compute the ``badness'' of glue, when a +total~|t| is supposed to be made from amounts that sum to~|s|. According +to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$; +however, badness is simply a heuristic, so we need not squeeze out the +last drop of accuracy when computing it. All we really want is an +approximation that has similar properties. +@:TeXbook}{\sl The \TeX book@> + +The actual method used to compute the badness is easier to read from the +program than to describe in words. It produces an integer value that is a +reasonably close approximation to $100(t/s)^3$, and all implementations +of \TeX\ should use precisely this method. Any badness of $2^{13}$ or more is +treated as infinitely bad, and represented by 10000. + +It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s) +>=badness(t,s+1)|}.$$ The badness function defined here is capable of +computing at most 1095 distinct values, but that is plenty. + +@c +halfword badness(scaled t, scaled s) +{ /* compute badness, given |t>=0| */ + int r; /* approximation to $\alpha t/s$, where $\alpha^3\approx + 100\cdot2^{18}$ */ + if (t == 0) { + return 0; + } else if (s <= 0) { + return inf_bad; + } else { + if (t <= 7230584) + r = (t * 297) / s; /* $297^3=99.94\times2^{18}$ */ + else if (s >= 1663497) + r = t / (s / 297); + else + r = t; + if (r > 1290) + return inf_bad; /* $1290^3<2^{31}<1291^3$ */ + else + return ((r * r * r + 0400000) / 01000000); + /* that was $r^3/2^{18}$, rounded to the nearest integer */ + } +} + + +@ When \TeX\ ``packages'' a list into a box, it needs to calculate the +proportionality ratio by which the glue inside the box should stretch +or shrink. This calculation does not affect \TeX's decision making, +so the precise details of rounding, etc., in the glue calculation are not +of critical importance for the consistency of results on different computers. + +We shall use the type |glue_ratio| for such proportionality ratios. +A glue ratio should take the same amount of memory as an +|integer| (usually 32 bits) if it is to blend smoothly with \TeX's +other data structures. Thus |glue_ratio| should be equivalent to +|short_real| in some implementations of PASCAL. Alternatively, +it is possible to deal with glue ratios using nothing but fixed-point +arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the +routines cited there must be modified to allow negative glue ratios.) +@^system dependencies@> + + +@ This section is (almost) straight from MetaPost. I had to change +the types (use |integer| instead of |fraction|), but that should +not have any influence on the actual calculations (the original +comments refer to quantities like |fraction_four| ($2^{30}$), and +that is the same as the numeric representation of |max_dimen|). + +I've copied the low-level variables and routines that are needed, but +only those (e.g. |m_log|), not the accompanying ones like |m_exp|. Most +of the following low-level numeric routines are only needed within the +calculation of |norm_rand|. I've been forced to rename |make_fraction| +to |make_frac| because TeX already has a routine by that name with +a wholly different function (it creates a |fraction_noad| for math +typesetting) -- Taco + +And now let's complete our collection of numeric utility routines +by considering random number generation. +\MP{} generates pseudo-random numbers with the additive scheme recommended +in Section 3.6 of {\sl The Art of Computer Programming}; however, the +results are random fractions between 0 and |fraction_one-1|, inclusive. + +There's an auxiliary array |randoms| that contains 55 pseudo-random +fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$, +we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|. +The global variable |j_random| tells which element has most recently +been consumed. + +@c +static int randoms[55]; /* the last 55 random values generated */ +static int j_random; /* the number of unused |randoms| */ +scaled random_seed; /* the default random seed */ + +@ A small bit of metafont is needed. + +@c +#define fraction_half 01000000000 /* $2^{27}$, represents 0.50000000 */ +#define fraction_one 02000000000 /* $2^{28}$, represents 1.00000000 */ +#define fraction_four 010000000000 /* $2^{30}$, represents 4.00000000 */ +#define el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */ + +@ The |make_frac| routine produces the |fraction| equivalent of +|p/q|, given integers |p| and~|q|; it computes the integer +$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are +positive. If |p| and |q| are both of the same scaled type |t|, +the ``type relation'' |make_frac(t,t)=fraction| is valid; +and it's also possible to use the subroutine ``backwards,'' using +the relation |make_frac(t,fraction)=t| between scaled types. + +If the result would have magnitude $2^{31}$ or more, |make_frac| +sets |arith_error:=true|. Most of \MP's internal computations have +been designed to avoid this sort of error. + +If this subroutine were programmed in assembly language on a typical +machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a +double-precision product can often be input to a fixed-point division +instruction. But when we are restricted to PASCAL arithmetic it +is necessary either to resort to multiple-precision maneuvering +or to use a simple but slow iteration. The multiple-precision technique +would be about three times faster than the code adopted here, but it +would be comparatively long and tricky, involving about sixteen +additional multiplications and divisions. + +This operation is part of \MP's ``inner loop''; indeed, it will +consume nearly 10\%! of the running time (exclusive of input and output) +if the code below is left unchanged. A machine-dependent recoding +will therefore make \MP\ run faster. The present implementation +is highly portable, but slow; it avoids multiplication and division +except in the initial stage. System wizards should be careful to +replace it with a routine that is guaranteed to produce identical +results in all cases. +@^system dependencies@> + +As noted below, a few more routines should also be replaced by machine-dependent +code, for efficiency. But when a procedure is not part of the ``inner loop,'' +such changes aren't advisable; simplicity and robustness are +preferable to trickery, unless the cost is too high. + +@c +static int make_frac(int p, int q) +{ + int f; /* the fraction bits, with a leading 1 bit */ + int n; /* the integer part of $\vert p/q\vert$ */ + register int be_careful; /* disables certain compiler optimizations */ + boolean negative = false; /* should the result be negated? */ + if (p < 0) { + negate(p); + negative = true; + } + if (q <= 0) { +#ifdef DEBUG + if (q == 0) + confusion("/"); +#endif + negate(q); + negative = !negative; + } + n = p / q; + p = p % q; + if (n >= 8) { + arith_error = true; + if (negative) + return (-el_gordo); + else + return el_gordo; + } else { + n = (n - 1) * fraction_one; + /* Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$ */ + /* The |repeat| loop here preserves the following invariant relations + between |f|, |p|, and~|q|: + (i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and + $p_0$ is the original value of~$p$. + + Notice that the computation specifies + |(p-q)+p| instead of |(p+p)-q|, because the latter could overflow. + Let us hope that optimizing compilers do not miss this point; a + special variable |be_careful| is used to emphasize the necessary + order of computation. Optimizing compilers should keep |be_careful| + in a register, not store it in memory. + */ + f = 1; + do { + be_careful = p - q; + p = be_careful + p; + if (p >= 0) + f = f + f + 1; + else { + f += f; + p = p + q; + } + } while (f < fraction_one); + be_careful = p - q; + if (be_careful + p >= 0) + incr(f); + + if (negative) + return (-(f + n)); + else + return (f + n); + } +} + +@ @c +static int take_frac(int q, int f) +{ + int p; /* the fraction so far */ + int n; /* additional multiple of $q$ */ + register int be_careful; /* disables certain compiler optimizations */ + boolean negative = false; /* should the result be negated? */ + /* Reduce to the case that |f>=0| and |q>0| */ + if (f < 0) { + negate(f); + negative = true; + } + if (q < 0) { + negate(q); + negative = !negative; + } + + if (f < fraction_one) { + n = 0; + } else { + n = f / fraction_one; + f = f % fraction_one; + if (q <= el_gordo / n) { + n = n * q; + } else { + arith_error = true; + n = el_gordo; + } + } + f = f + fraction_one; + /* Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$ */ + /* The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor + =\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and + $f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$. + */ + p = fraction_half; /* that's $2^{27}$; the invariants hold now with $k=28$ */ + if (q < fraction_four) { + do { + if (odd(f)) + p = halfp(p + q); + else + p = halfp(p); + f = halfp(f); + } while (f != 1); + } else { + do { + if (odd(f)) + p = p + halfp(q - p); + else + p = halfp(p); + f = halfp(f); + } while (f != 1); + } + + be_careful = n - el_gordo; + if (be_careful + p > 0) { + arith_error = true; + n = el_gordo - p; + } + if (negative) + return (-(n + p)); + else + return (n + p); +} + + + +@ The subroutines for logarithm and exponential involve two tables. +The first is simple: |two_to_the[k]| equals $2^k$. The second involves +a bit more calculation, which the author claims to have done correctly: +|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)= +2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the +nearest integer. + +@c +static int two_to_the[31]; /* powers of two */ +static int spec_log[29]; /* special logarithms */ + +@ @c +void initialize_arithmetic(void) +{ + int k; + two_to_the[0] = 1; + for (k = 1; k <= 30; k++) + two_to_the[k] = 2 * two_to_the[k - 1]; + spec_log[1] = 93032640; + spec_log[2] = 38612034; + spec_log[3] = 17922280; + spec_log[4] = 8662214; + spec_log[5] = 4261238; + spec_log[6] = 2113709; + spec_log[7] = 1052693; + spec_log[8] = 525315; + spec_log[9] = 262400; + spec_log[10] = 131136; + spec_log[11] = 65552; + spec_log[12] = 32772; + spec_log[13] = 16385; + for (k = 14; k <= 27; k++) + spec_log[k] = two_to_the[27 - k]; + spec_log[28] = 1; +} + +@ @c +static int m_log(int x) +{ + int y, z; /* auxiliary registers */ + int k; /* iteration counter */ + if (x <= 0) { + /* Handle non-positive logarithm */ + print_err("Logarithm of "); + print_scaled(x); + tprint(" has been replaced by 0"); + help2("Since I don't take logs of non-positive numbers,", + "I'm zeroing this one. Proceed, with fingers crossed."); + error(); + return 0; + } else { + y = 1302456956 + 4 - 100; /* $14\times2^{27}\ln2\approx1302456956.421063$ */ + z = 27595 + 6553600; /* and $2^{16}\times .421063\approx 27595$ */ + while (x < fraction_four) { + x += x; + y = y - 93032639; + z = z - 48782; + } /* $2^{27}\ln2\approx 93032639.74436163$ + and $2^{16}\times.74436163\approx 48782$ */ + y = y + (z / unity); + k = 2; + while (x > fraction_four + 4) { + /* Increase |k| until |x| can be multiplied by a + factor of $2^{-k}$, and adjust $y$ accordingly */ + z = ((x - 1) / two_to_the[k]) + 1; /* $z=\lceil x/2^k\rceil$ */ + while (x < fraction_four + z) { + z = halfp(z + 1); + k = k + 1; + } + y = y + spec_log[k]; + x = x - z; + } + return (y / 8); + } +} + + + +@ The following somewhat different subroutine tests rigorously if $ab$ is +greater than, equal to, or less than~$cd$, +given integers $(a,b,c,d)$. In most cases a quick decision is reached. +The result is $+1$, 0, or~$-1$ in the three respective cases. + +@c +static int ab_vs_cd(int a, int b, int c, int d) +{ + int q, r; /* temporary registers */ + /* Reduce to the case that |a,c>=0|, |b,d>0| */ + if (a < 0) { + negate(a); + negate(b); + } + if (c < 0) { + negate(c); + negate(d); + } + if (d <= 0) { + if (b >= 0) + return (((a == 0 || b == 0) && (c == 0 || d == 0)) ? 0 : 1); + if (d == 0) + return (a == 0 ? 0 : -1); + q = a; + a = c; + c = q; + q = -b; + b = -d; + d = q; + } else if (b <= 0) { + if (b < 0 && a > 0) + return -1; + return (c == 0 ? 0 : -1); + } + + while (1) { + q = a / d; + r = c / b; + if (q != r) + return (q > r ? 1 : -1); + q = a % d; + r = c % b; + if (r == 0) + return (q == 0 ? 0 : 1); + if (q == 0) + return -1; + a = b; + b = q; + c = d; + d = r; /* now |a>d>0| and |c>b>0| */ + } +} + + + +@ To consume a random integer, the program below will say `|next_random|' +and then it will fetch |randoms[j_random]|. + +@c +#define next_random() do { \ + if (j_random==0) new_randoms(); else decr(j_random); \ + } while (0) + +static void new_randoms(void) +{ + int k; /* index into |randoms| */ + int x; /* accumulator */ + for (k = 0; k <= 23; k++) { + x = randoms[k] - randoms[k + 31]; + if (x < 0) + x = x + fraction_one; + randoms[k] = x; + } + for (k = 24; k <= 54; k++) { + x = randoms[k] - randoms[k - 24]; + if (x < 0) + x = x + fraction_one; + randoms[k] = x; + } + j_random = 54; +} + + +@ To initialize the |randoms| table, we call the following routine. + +@c +void init_randoms(int seed) +{ + int j, jj, k; /* more or less random integers */ + int i; /* index into |randoms| */ + j = abs(seed); + while (j >= fraction_one) + j = halfp(j); + k = 1; + for (i = 0; i <= 54; i++) { + jj = k; + k = j - k; + j = jj; + if (k < 0) + k = k + fraction_one; + randoms[(i * 21) % 55] = j; + } + new_randoms(); + new_randoms(); + new_randoms(); /* ``warm up'' the array */ +} + + +@ To produce a uniform random number in the range |0<=u<x| or |0>=u>x| +or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here. + +Note that the call of |take_frac| will produce the values 0 and~|x| +with about half the probability that it will produce any other particular +values between 0 and~|x|, because it rounds its answers. + +@c +int unif_rand(int x) +{ + int y; /* trial value */ + next_random(); + y = take_frac(abs(x), randoms[j_random]); + if (y == abs(x)) + return 0; + else if (x > 0) + return y; + else + return -y; +} + + +@ Finally, a normal deviate with mean zero and unit standard deviation +can readily be obtained with the ratio method (Algorithm 3.4.1R in +{\sl The Art of Computer Programming\/}). + +@c +int norm_rand(void) +{ + int x, u, l; /* what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$ */ + do { + do { + next_random(); + x = take_frac(112429, randoms[j_random] - fraction_half); + /* $2^{16}\sqrt{8/e}\approx 112428.82793$ */ + next_random(); + u = randoms[j_random]; + } while (abs(x) >= u); + x = make_frac(x, u); + l = 139548960 - m_log(u); /* $2^{24}\cdot12\ln2\approx139548959.6165$ */ + } while (ab_vs_cd(1024, l, x, x) < 0); + return x; +} + +@ This function could also be expressed as a macro, but it is a useful + breakpoint for debugging. + +@c +int fix_int(int val, int min, int max) +{ + return (val < min ? min : (val > max ? max : val)); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/buildpage.h b/Build/source/texk/web2c/luatexdir/tex/buildpage.h new file mode 100644 index 00000000000..f3e8f46abbc --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/buildpage.h @@ -0,0 +1,75 @@ +/* buildpage.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: buildpage.h 3261 2009-12-18 11:38:21Z taco $ */ + +#ifndef BUILDPAGE_H +# define BUILDPAGE_H + +/* empty = 0 */ +# define inserts_only 1 /* |page_contents| when an insert node has been contributed, but no boxes */ +# define box_there 2 /* |page_contents| when a box or rule has been contributed */ + +extern halfword page_tail; /* the final node on the current page */ +extern int page_contents; /* what is on the current page so far? */ +extern scaled page_max_depth; /* maximum box depth on page being built */ +extern halfword best_page_break; /* break here to get the best page known so far */ +extern int least_page_cost; /* the score for this currently best page */ +extern scaled best_size; /* its |page_goal| */ + +/* +The data structure definitions here use the fact that the |@!height| field +appears in the fourth word of a box node. +@^data structure assumptions@> +*/ + +# define broken_ptr(A) vlink((A)+2) /* an insertion for this class will break here if anywhere */ +# define broken_ins(A) vinfo((A)+2) /* this insertion might break at |broken_ptr| */ +# define last_ins_ptr(A) vlink((A)+3) /* the most recent insertion for this |subtype| */ +# define best_ins_ptr(A) vinfo((A)+3) /* the optimum most recent insertion */ + +extern void initialize_buildpage(void); + +# define page_goal page_so_far[0] /* desired height of information on page being built */ +# define page_total page_so_far[1] /* height of the current page */ +# define page_shrink page_so_far[6] /* shrinkability of the current page */ +# define page_depth page_so_far[7] /* depth of the current page */ + +extern scaled page_so_far[8]; /* height and glue of the current page */ +extern halfword last_glue; /* used to implement \.{\\lastskip */ +extern int last_penalty; /* used to implement \.{\\lastpenalty} */ +extern scaled last_kern; /* used to implement \.{\\lastkern} */ +extern int last_node_type; /* used to implement \.{\\lastnodetype} */ +extern int insert_penalties; /* sum of the penalties for held-over insertions */ + +extern void print_totals(void); +extern void freeze_page_specs(int s); + +extern boolean output_active; + +extern void start_new_page(void); + +# define contrib_tail nest[0].tail_field + /* tail of the contribution list */ + +extern void build_page(void); /* append contributions to the current page */ +extern void fire_up(halfword c); +extern void resume_after_output(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/buildpage.w b/Build/source/texk/web2c/luatexdir/tex/buildpage.w new file mode 100644 index 00000000000..de39703c8ae --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/buildpage.w @@ -0,0 +1,1057 @@ +% buildpage.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +static const char _svn_version[] = + "$Id: buildpage.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/buildpage.w $"; + +#include "ptexlib.h" + +@ @c +#define box(A) eqtb[box_base+(A)].hh.rh +#define count(A) eqtb[count_base+(A)].hh.rh +#undef skip +#define skip(A) eqtb[skip_base+(A)].hh.rh +#define dimen(A) eqtb[scaled_base+(A)].hh.rh + +#define vbadness int_par(vbadness_code) +#define max_dead_cycles int_par(max_dead_cycles_code) +#define output_box int_par(output_box_code) +#define body_direction int_par(body_direction_code) +#define holding_inserts int_par(holding_inserts_code) + +#define vsize dimen_par(vsize_code) +#define vfuzz dimen_par(vfuzz_code) +#define max_depth dimen_par(max_depth_code) +#define pdf_ignored_dimen dimen_par(pdf_ignored_dimen_code) + +#define output_routine equiv(output_routine_loc) +#define split_top_skip glue_par(split_top_skip_code) + +#define prev_depth cur_list.prev_depth_field +#define mode_line cur_list.ml_field +#define mode cur_list.mode_field +#define tail cur_list.tail_field +#define head cur_list.head_field + +@ When \TeX\ appends new material to its main vlist in vertical mode, it uses +a method something like |vsplit| to decide where a page ends, except that +the calculations are done ``on line'' as new items come in. +The main complication in this process is that insertions must be put +into their boxes and removed from the vlist, in a more-or-less optimum manner. + +We shall use the term ``current page'' for that part of the main vlist that +is being considered as a candidate for being broken off and sent to the +user's output routine. The current page starts at |vlink(page_head)|, and +it ends at |page_tail|. We have |page_head=page_tail| if this list is empty. +@^current page@> + +Utter chaos would reign if the user kept changing page specifications +while a page is being constructed, so the page builder keeps the pertinent +specifications frozen as soon as the page receives its first box or +insertion. The global variable |page_contents| is |empty| when the +current page contains only mark nodes and content-less whatsit nodes; it +is |inserts_only| if the page contains only insertion nodes in addition to +marks and whatsits. Glue nodes, kern nodes, and penalty nodes are +discarded until a box or rule node appears, at which time |page_contents| +changes to |box_there|. As soon as |page_contents| becomes non-|empty|, +the current |vsize| and |max_depth| are squirreled away into |page_goal| +and |page_max_depth|; the latter values will be used until the page has +been forwarded to the user's output routine. The \.{\\topskip} adjustment +is made when |page_contents| changes to |box_there|. + +Although |page_goal| starts out equal to |vsize|, it is decreased by the +scaled natural height-plus-depth of the insertions considered so far, and by +the \.{\\skip} corrections for those insertions. Therefore it represents +the size into which the non-inserted material should fit, assuming that +all insertions in the current page have been made. + +The global variables |best_page_break| and |least_page_cost| correspond +respectively to the local variables |best_place| and |least_cost| in the +|vert_break| routine that we have already studied; i.e., they record the +location and value of the best place currently known for breaking the +current page. The value of |page_goal| at the time of the best break is +stored in |best_size|. + +@c +halfword page_tail; /* the final node on the current page */ +int page_contents; /* what is on the current page so far? */ +scaled page_max_depth; /* maximum box depth on page being built */ +halfword best_page_break; /* break here to get the best page known so far */ +int least_page_cost; /* the score for this currently best page */ +scaled best_size; /* its |page_goal| */ + + +@ The page builder has another data structure to keep track of insertions. +This is a list of four-word nodes, starting and ending at |page_ins_head|. +That is, the first element of the list is node |r@t$_1$@>=vlink(page_ins_head)|; +node $r_j$ is followed by |r@t$_{j+1}$@>=vlink(r@t$_j$@>)|; and if there are +|n| items we have |r@t$_{n+1}$@>=page_ins_head|. The |subtype| field of +each node in this list refers to an insertion number; for example, `\.{\\insert +250}' would correspond to a node whose |subtype| is |qi(250)| +(the same as the |subtype| field of the relevant |ins_node|). These |subtype| +fields are in increasing order, and |subtype(page_ins_head)=65535|, so +|page_ins_head| serves as a convenient sentinel +at the end of the list. A record is present for each insertion number that +appears in the current page. + +The |type| field in these nodes distinguishes two possibilities that +might occur as we look ahead before deciding on the optimum page break. +If |type(r)=inserting_node|, then |height(r)| contains the total of the +height-plus-depth dimensions of the box and all its inserts seen so far. + |type(r)=split_up_node|, then no more insertions will be made into this box, +because at least one previous insertion was too big to fit on the current +page; |broken_ptr(r)| points to the node where that insertion will be +split, if \TeX\ decides to split it, |broken_ins(r)| points to the +insertion node that was tentatively split, and |height(r)| includes also the +natural height plus depth of the part that would be split off. + +In both cases, |last_ins_ptr(r)| points to the last |ins_node| +encountered for box |qo(subtype(r))| that would be at least partially +inserted on the next page; and |best_ins_ptr(r)| points to the last +such |ins_node| that should actually be inserted, to get the page with +minimum badness among all page breaks considered so far. We have +|best_ins_ptr(r)=null| if and only if no insertion for this box should +be made to produce this optimum page. + + + +@ Pages are built by appending nodes to the current list in \TeX's +vertical mode, which is at the outermost level of the semantic nest. This +vlist is split into two parts; the ``current page'' that we have been +talking so much about already, and the ``contribution list'' that receives +new nodes as they are created. The current page contains everything that +the page builder has accounted for in its data structures, as described +above, while the contribution list contains other things that have been +generated by other parts of \TeX\ but have not yet been +seen by the page builder. +The contribution list starts at |vlink(contrib_head)|, and it ends at the +current node in \TeX's vertical mode. + +When \TeX\ has appended new material in vertical mode, it calls the procedure +|build_page|, which tries to catch up by moving nodes from the contribution +list to the current page. This procedure will succeed in its goal of +emptying the contribution list, unless a page break is discovered, i.e., +unless the current page has grown to the point where the optimum next +page break has been determined. In the latter case, the nodes after the +optimum break will go back onto the contribution list, and control will +effectively pass to the user's output routine. + +We make |type(page_head)=glue_node|, so that an initial glue node on +the current page will not be considered a valid breakpoint. + +@c +void initialize_buildpage(void) +{ + subtype(page_ins_head) = 65535; + type(page_ins_head) = split_up_node; + vlink(page_ins_head) = page_ins_head; + + type(page_head) = glue_node; + subtype(page_head) = normal; +} + + +@ An array |page_so_far| records the heights and depths of everything +on the current page. This array contains six |scaled| numbers, like the +similar arrays already considered in |line_break| and |vert_break|; and it +also contains |page_goal| and |page_depth|, since these values are +all accessible to the user via |set_page_dimen| commands. The +value of |page_so_far[1]| is also called |page_total|. The stretch +and shrink components of the \.{\\skip} corrections for each insertion are +included in |page_so_far|, but the natural space components of these +corrections are not, since they have been subtracted from |page_goal|. + +The variable |page_depth| records the depth of the current page; it has been +adjusted so that it is at most |page_max_depth|. The variable +|last_glue| points to the glue specification of the most recent node +contributed from the contribution list, if this was a glue node; otherwise +|last_glue=max_halfword|. (If the contribution list is nonempty, +however, the value of |last_glue| is not necessarily accurate.) +The variables |last_penalty|, |last_kern|, and |last_node_type| +are similar. And +finally, |insert_penalties| holds the sum of the penalties associated with +all split and floating insertions. + +@c +scaled page_so_far[8]; /* height and glue of the current page */ +halfword last_glue; /* used to implement \.{\\lastskip} */ +int last_penalty; /* used to implement \.{\\lastpenalty} */ +scaled last_kern; /* used to implement \.{\\lastkern} */ +int last_node_type; /* used to implement \.{\\lastnodetype} */ +int insert_penalties; /* sum of the penalties for held-over insertions */ + +#define print_plus(A,B) do { \ + if (page_so_far[(A)]!=0) { \ + tprint(" plus "); \ + print_scaled(page_so_far[(A)]); \ + tprint((B)); \ + } \ + } while (0) + +void print_totals(void) +{ + print_scaled(page_total); + print_plus(2, ""); + print_plus(3, "fil"); + print_plus(4, "fill"); + print_plus(5, "filll"); + if (page_shrink != 0) { + tprint(" minus "); + print_scaled(page_shrink); + } +} + + +@ Here is a procedure that is called when the |page_contents| is changing +from |empty| to |inserts_only| or |box_there|. + +@c +#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7) +#define set_page_so_far_zero(A) page_so_far[(A)]=0 + +void freeze_page_specs(int s) +{ + page_contents = s; + page_goal = vsize; + page_max_depth = max_depth; + page_depth = 0; + do_all_six(set_page_so_far_zero); + least_page_cost = awful_bad; + if (int_par(tracing_pages_code) > 0) { + begin_diagnostic(); + tprint_nl("%% goal height="); + print_scaled(page_goal); + tprint(", max depth="); + print_scaled(page_max_depth); + end_diagnostic(false); + } +} + + +@ The global variable |output_active| is true during the time the +user's output routine is driving \TeX. + +@c +boolean output_active; /* are we in the midst of an output routine? */ + + +@ The page builder is ready to start a fresh page if we initialize +the following state variables. (However, the page insertion list is initialized +elsewhere.) + +@c +void start_new_page(void) +{ + page_contents = empty; + page_tail = page_head; + vlink(page_head) = null; + last_glue = max_halfword; + last_penalty = 0; + last_kern = 0; + last_node_type = -1; + page_depth = 0; + page_max_depth = 0; +} + + +@ At certain times box \.{\\outputbox} is supposed to be void (i.e., |null|), +or an insertion box is supposed to be ready to accept a vertical list. +If not, an error message is printed, and the following subroutine +flushes the unwanted contents, reporting them to the user. + +@c +static void box_error(int n) +{ + error(); + begin_diagnostic(); + tprint_nl("The following box has been deleted:"); + show_box(box(n)); + end_diagnostic(true); + flush_node_list(box(n)); + box(n) = null; +} + + +@ The following procedure guarantees that a given box register +does not contain an \.{\\hbox}. + +@c +static void ensure_vbox(int n) +{ + halfword p; /* the box register contents */ + p = box(n); + if (p != null && type(p) == hlist_node) { + print_err("Insertions can only be added to a vbox"); + help3("Tut tut: You're trying to \\insert into a", + "\\box register that now contains an \\hbox.", + "Proceed, and I'll discard its present contents."); + box_error(n); + } +} + + +@ \TeX\ is not always in vertical mode at the time |build_page| +is called; the current mode reflects what \TeX\ should return to, after +the contribution list has been emptied. A call on |build_page| should +be immediately followed by `|goto big_switch|', which is \TeX's central +control point. + +@c +void build_page(void) +{ /* append contributions to the current page */ + halfword p; /* the node being appended */ + halfword q, r; /* nodes being examined */ + int b, c; /* badness and cost of current page */ + int pi; /* penalty to be added to the badness */ + int n; /* insertion box number */ + scaled delta, h, w; /* sizes used for insertion calculations */ + pi = 0; + if ((vlink(contrib_head) == null) || output_active) + return; + do { + CONTINUE: + p = vlink(contrib_head); + /* Update the values of |last_glue|, |last_penalty|, and |last_kern| */ + if (last_glue != max_halfword) { + delete_glue_ref(last_glue); + last_glue = max_halfword; + } + last_penalty = 0; + last_kern = 0; + last_node_type = type(p) + 1; + if (type(p) == glue_node) { + last_glue = glue_ptr(p); + add_glue_ref(last_glue); + } else if (type(p) == penalty_node) { + last_penalty = penalty(p); + } else if (type(p) == kern_node) { + last_kern = width(p); + } + + /* Move node |p| to the current page; if it is time for a page break, + put the nodes following the break back onto the contribution list, + and |return| to the users output routine if there is one */ + + /* The code here is an example of a many-way switch into routines that + merge together in different places. Some people call this unstructured + programming, but the author doesn't see much wrong with it, as long as + the various labels have a well-understood meaning. + */ + /* If the current page is empty and node |p| is to be deleted, |goto done1|; + otherwise use node |p| to update the state of the current page; + if this node is an insertion, |goto contribute|; otherwise if this node + is not a legal breakpoint, |goto contribute| or |update_heights|; + otherwise set |pi| to the penalty associated with this breakpoint */ + /* The title of this section is already so long, it seems best to avoid + making it more accurate but still longer, by mentioning the fact that a + kern node at the end of the contribution list will not be contributed until + we know its successor. */ + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + if (page_contents < box_there) { + /* Initialize the current page, insert the \.{\\topskip} glue + ahead of |p|, and |goto continue| */ + if (page_contents == empty) + freeze_page_specs(box_there); + else + page_contents = box_there; + q = new_skip_param(top_skip_code); /* now |temp_ptr=glue_ptr(q)| */ + if ((type(p) == hlist_node) && is_mirrored(body_direction)) { + if (width(temp_ptr) > depth(p)) + width(temp_ptr) = width(temp_ptr) - depth(p); + else + width(temp_ptr) = 0; + } else { + if (width(temp_ptr) > height(p)) + width(temp_ptr) = width(temp_ptr) - height(p); + else + width(temp_ptr) = 0; + } + vlink(q) = p; + vlink(contrib_head) = q; + goto CONTINUE; + + } else { + /* Prepare to move a box or rule node to the current page, + then |goto contribute| */ + if ((type(p) == hlist_node) && is_mirrored(body_direction)) { + page_total = page_total + page_depth + depth(p); + page_depth = height(p); + } else { + page_total = page_total + page_depth + height(p); + page_depth = depth(p); + } + goto CONTRIBUTE; + + } + break; + case whatsit_node: + /* Prepare to move whatsit |p| to the current page, + then |goto contribute| */ + if ((subtype(p) == pdf_refxform_node) + || (subtype(p) == pdf_refximage_node)) { + page_total = page_total + page_depth + height(p); + page_depth = depth(p); + } + goto CONTRIBUTE; + + break; + case glue_node: + if (page_contents < box_there) + goto DONE1; + else if (precedes_break(page_tail)) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case kern_node: + if (page_contents < box_there) + goto DONE1; + else if (vlink(p) == null) + goto EXIT; + else if (type(vlink(p)) == glue_node) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case penalty_node: + if (page_contents < box_there) + goto DONE1; + else + pi = penalty(p); + break; + case mark_node: + goto CONTRIBUTE; + break; + case ins_node: + /* Append an insertion to the current page and |goto contribute| */ + if (page_contents == empty) + freeze_page_specs(inserts_only); + n = subtype(p); + r = page_ins_head; + while (n >= subtype(vlink(r))) + r = vlink(r); + if (subtype(r) != n) { + /* Create a page insertion node with |subtype(r)=qi(n)|, and + include the glue correction for box |n| in the + current page state */ + /* We take note of the value of \.{\\skip} |n| and the height plus depth + of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is + encountered for a new page. A user who changes the contents of \.{\\box}~|n| + after that first \.{\\insert}~|n| had better be either extremely careful + or extremely lucky, or both. */ + + q = new_node(inserting_node, n); + vlink(q) = vlink(r); + vlink(r) = q; + r = q; + ensure_vbox(n); + if (box(n) == null) + height(r) = 0; + else + height(r) = height(box(n)) + depth(box(n)); + best_ins_ptr(r) = null; + q = skip(n); + if (count(n) == 1000) + h = height(r); + else + h = x_over_n(height(r), 1000) * count(n); + page_goal = page_goal - h - width(q); + if (stretch_order(q) > 1) + page_so_far[1 + stretch_order(q)] = + page_so_far[1 + stretch_order(q)] + stretch(q); + else + page_so_far[2 + stretch_order(q)] = + page_so_far[2 + stretch_order(q)] + stretch(q); + page_shrink = page_shrink + shrink(q); + if ((shrink_order(q) != normal) && (shrink(q) != 0)) { + print_err("Infinite glue shrinkage inserted from \\skip"); + print_int(n); + help3 + ("The correction glue for page breaking with insertions", + "must have finite shrinkability. But you may proceed,", + "since the offensive shrinkability has been made finite."); + error(); + } + + } + if (type(r) == split_up_node) { + insert_penalties = insert_penalties + float_cost(p); + } else { + last_ins_ptr(r) = p; + delta = page_goal - page_total - page_depth + page_shrink; + /* this much room is left if we shrink the maximum */ + if (count(n) == 1000) + h = height(p); + else + h = x_over_n(height(p), 1000) * count(n); /* this much room is needed */ + if (((h <= 0) || (h <= delta)) + && (height(p) + height(r) <= dimen(n))) { + page_goal = page_goal - h; + height(r) = height(r) + height(p); + } else { + /* Find the best way to split the insertion, and change + |type(r)| to |split_up_node| */ + /* Here is the code that will split a long footnote between pages, in an + emergency. The current situation deserves to be recapitulated: Node |p| + is an insertion into box |n|; the insertion will not fit, in its entirety, + either because it would make the total contents of box |n| greater than + \.{\\dimen} |n|, or because it would make the incremental amount of growth + |h| greater than the available space |delta|, or both. (This amount |h| has + been weighted by the insertion scaling factor, i.e., by \.{\\count} |n| + over 1000.) Now we will choose the best way to break the vlist of the + insertion, using the same criteria as in the \.{\\vsplit} operation. + */ + if (count(n) <= 0) { + w = max_dimen; + } else { + w = page_goal - page_total - page_depth; + if (count(n) != 1000) + w = x_over_n(w, count(n)) * 1000; + } + if (w > dimen(n) - height(r)) + w = dimen(n) - height(r); + q = vert_break(ins_ptr(p), w, depth(p)); + height(r) = height(r) + best_height_plus_depth; + if (int_par(tracing_pages_code) > 0) { + /* Display the insertion split cost */ + begin_diagnostic(); + tprint_nl("% split"); + print_int(n); + tprint(" to "); + print_scaled(w); + print_char(','); + print_scaled(best_height_plus_depth); + tprint(" p="); + if (q == null) + print_int(eject_penalty); + else if (type(q) == penalty_node) + print_int(penalty(q)); + else + print_char('0'); + end_diagnostic(false); + + } + if (count(n) != 1000) + best_height_plus_depth = + x_over_n(best_height_plus_depth, 1000) * count(n); + page_goal = page_goal - best_height_plus_depth; + type(r) = split_up_node; + broken_ptr(r) = q; + broken_ins(r) = p; + if (q == null) + insert_penalties = insert_penalties + eject_penalty; + else if (type(q) == penalty_node) + insert_penalties = insert_penalties + penalty(q); + } + } + goto CONTRIBUTE; + + break; + default: + confusion("page"); + break; + } + + /* Check if node |p| is a new champion breakpoint; then if it is time for + a page break, prepare for output, and either fire up the users + output routine and |return| or ship out the page and |goto done| */ + + if (pi < inf_penalty) { + /* Compute the badness, |b|, of the current page, + using |awful_bad| if the box is too full */ + if (page_total < page_goal) { + if ((page_so_far[3] != 0) || (page_so_far[4] != 0) || + (page_so_far[5] != 0)) + b = 0; + else + b = badness(page_goal - page_total, page_so_far[2]); + } else if (page_total - page_goal > page_shrink) { + b = awful_bad; + } else { + b = badness(page_total - page_goal, page_shrink); + } + + if (b < awful_bad) { + if (pi <= eject_penalty) + c = pi; + else if (b < inf_bad) + c = b + pi + insert_penalties; + else + c = deplorable; + } else { + c = b; + } + if (insert_penalties >= 10000) + c = awful_bad; + if (int_par(tracing_pages_code) > 0) { + /* Display the page break cost */ + begin_diagnostic(); + tprint_nl("%"); + tprint(" t="); + print_totals(); + tprint(" g="); + print_scaled(page_goal); + tprint(" b="); + if (b == awful_bad) + print_char('*'); + else + print_int(b); + tprint(" p="); + print_int(pi); + tprint(" c="); + if (c == awful_bad) + print_char('*'); + else + print_int(c); + if (c <= least_page_cost) + print_char('#'); + end_diagnostic(false); + + } + if (c <= least_page_cost) { + best_page_break = p; + best_size = page_goal; + least_page_cost = c; + r = vlink(page_ins_head); + while (r != page_ins_head) { + best_ins_ptr(r) = last_ins_ptr(r); + r = vlink(r); + } + } + if ((c == awful_bad) || (pi <= eject_penalty)) { + fire_up(p); /* output the current page at the best place */ + if (output_active) + goto EXIT; /* user's output routine will act */ + goto DONE; /* the page has been shipped out by default output routine */ + } + } + + if ((type(p) < glue_node) || (type(p) > kern_node)) + goto CONTRIBUTE; + + UPDATE_HEIGHTS: /* go here to record glue in the |active_height| table */ + + /* Update the current page measurements with respect to the + glue or kern specified by node~|p| */ + if (type(p) == kern_node) { + q = p; + } else { + q = glue_ptr(p); + if (stretch_order(q) > 1) + page_so_far[1 + stretch_order(q)] = + page_so_far[1 + stretch_order(q)] + stretch(q); + else + page_so_far[2 + stretch_order(q)] = + page_so_far[2 + stretch_order(q)] + stretch(q); + page_shrink = page_shrink + shrink(q); + if ((shrink_order(q) != normal) && (shrink(q) != 0)) { + print_err("Infinite glue shrinkage found on current page"); + help4("The page about to be output contains some infinitely", + "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.", + "Such glue doesn't belong there; but you can safely proceed,", + "since the offensive shrinkability has been made finite."); + error(); + r = new_spec(q); + shrink_order(r) = normal; + delete_glue_ref(q); + glue_ptr(p) = r; + q = r; + } + } + page_total = page_total + page_depth + width(q); + page_depth = 0; + + CONTRIBUTE: /* go here to link a node into the current page */ + + /* Make sure that |page_max_depth| is not exceeded */ + if (page_depth > page_max_depth) { + page_total = page_total + page_depth - page_max_depth; + page_depth = page_max_depth; + } + + /* Link node |p| into the current page and |goto done| */ + vlink(page_tail) = p; + page_tail = p; + vlink(contrib_head) = vlink(p); + vlink(p) = null; + goto DONE; + DONE1: + /* Recycle node |p| */ + vlink(contrib_head) = vlink(p); + vlink(p) = null; + if (int_par(saving_vdiscards_code) > 0) { + if (page_disc == null) + page_disc = p; + else + vlink(tail_page_disc) = p; + tail_page_disc = p; + } else { + flush_node_list(p); + } + DONE: + ; + } while (vlink(contrib_head) != null); + /* Make the contribution list empty by setting its tail to |contrib_head| */ + contrib_tail = contrib_head; + EXIT: + ; +} + +@ When the page builder has looked at as much material as could appear before +the next page break, it makes its decision. The break that gave minimum +badness will be used to put a completed ``page'' into box \.{\\outputbox}, with insertions +appended to their other boxes. + +We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The +program uses the fact that |bot_mark(x)<>null| implies |first_mark(x)<>null|; +it also knows that |bot_mark(x)=null| implies |top_mark(x)=first_mark(x)=null|. + +The |fire_up| subroutine prepares to output the current page at the best +place; then it fires up the user's output routine, if there is one, +or it simply ships out the page. There is one parameter, |c|, which represents +the node that was being contributed to the page when the decision to +force an output was made. + +@c +void fire_up(halfword c) +{ + halfword p, q, r, s; /* nodes being examined and/or changed */ + halfword prev_p; /* predecessor of |p| */ + int n; /* insertion box number */ + boolean wait; /* should the present insertion be held over? */ + int save_vbadness; /* saved value of |vbadness| */ + scaled save_vfuzz; /* saved value of |vfuzz| */ + halfword save_split_top_skip; /* saved value of |split_top_skip| */ + halfword i; /* for looping through the marks */ + + /* Set the value of |output_penalty| */ + if (type(best_page_break) == penalty_node) { + geq_word_define(int_base + output_penalty_code, + penalty(best_page_break)); + penalty(best_page_break) = inf_penalty; + } else { + geq_word_define(int_base + output_penalty_code, inf_penalty); + } + + for (i = 0; i <= biggest_used_mark; i++) { + if (bot_mark(i) != null) { + if (top_mark(i) != null) + delete_token_ref(top_mark(i)); + set_top_mark(i, bot_mark(i)); + add_token_ref(top_mark(i)); + delete_first_mark(i); + } + } + /* Put the optimal current page into box |output_box|, update |first_mark| and + |bot_mark|, append insertions to their boxes, and put the + remaining nodes back on the contribution list; */ + + /* As the page is finally being prepared for output, + pointer |p| runs through the vlist, with |prev_p| trailing behind; + pointer |q| is the tail of a list of insertions that + are being held over for a subsequent page. */ + + if (c == best_page_break) + best_page_break = null; /* |c| not yet linked in */ + /* Ensure that box |output_box| is empty before output */ + if (box(output_box) != null) { + print_err("\\box"); + print_int(output_box); + tprint(" is not void"); + help2("You shouldn't use \\box\\outputbox except in \\output routines.", + "Proceed, and I'll discard its present contents."); + box_error(output_box); + } + + insert_penalties = 0; /* this will count the number of insertions held over */ + save_split_top_skip = split_top_skip; + if (holding_inserts <= 0) { + /* Prepare all the boxes involved in insertions to act as queues */ + /* If many insertions are supposed to go into the same box, we want to know + the position of the last node in that box, so that we don't need to waste time + when linking further information into it. The |last_ins_ptr| fields of the + page insertion nodes are therefore used for this purpose during the + packaging phase. */ + + r = vlink(page_ins_head); + while (r != page_ins_head) { + if (best_ins_ptr(r) != null) { + n = subtype(r); + ensure_vbox(n); + if (box(n) == null) + box(n) = new_null_box(); + p = box(n) + list_offset; + while (vlink(p) != null) + p = vlink(p); + last_ins_ptr(r) = p; + } + r = vlink(r); + } + + } + q = hold_head; + vlink(q) = null; + prev_p = page_head; + p = vlink(prev_p); + while (p != best_page_break) { + if (type(p) == ins_node) { + if (holding_inserts <= 0) { + /* Either insert the material specified by node |p| into the + appropriate box, or hold it for the next page; + also delete node |p| from the current page */ + /* We will set |best_ins_ptr:=null| and package the box corresponding to + insertion node~|r|, just after making the final insertion into that box. + If this final insertion is `|split_up_node|', the remainder after splitting + and pruning (if any) will be carried over to the next page. */ + r = vlink(page_ins_head); + while (subtype(r) != subtype(p)) + r = vlink(r); + if (best_ins_ptr(r) == null) { + wait = true; + } else { + wait = false; + s = last_ins_ptr(r); + vlink(s) = ins_ptr(p); + if (best_ins_ptr(r) == p) { + /* Wrap up the box specified by node |r|, splitting node |p| if + called for; set |wait:=true| if node |p| holds a remainder after + splitting */ + if (type(r) == split_up_node) { + if ((broken_ins(r) == p) && (broken_ptr(r) != null)) { + while (vlink(s) != broken_ptr(r)) + s = vlink(s); + vlink(s) = null; + split_top_skip = split_top_ptr(p); + ins_ptr(p) = + prune_page_top(broken_ptr(r), false); + if (ins_ptr(p) != null) { + temp_ptr = + vpack(ins_ptr(p), 0, additional, -1); + height(p) = + height(temp_ptr) + depth(temp_ptr); + list_ptr(temp_ptr) = null; + flush_node(temp_ptr); + wait = true; + } + } + } + best_ins_ptr(r) = null; + n = subtype(r); + temp_ptr = list_ptr(box(n)); + list_ptr(box(n)) = null; + flush_node(box(n)); + box(n) = vpack(temp_ptr, 0, additional, body_direction); + + } else { + while (vlink(s) != null) + s = vlink(s); + last_ins_ptr(r) = s; + } + } + /* Either append the insertion node |p| after node |q|, and remove it + from the current page, or delete |node(p)| */ + vlink(prev_p) = vlink(p); + vlink(p) = null; + if (wait) { + vlink(q) = p; + q = p; + incr(insert_penalties); + } else { + ins_ptr(p) = null; + flush_node(p); + } + p = prev_p; + + } + } else if (type(p) == mark_node) { + /* Update the values of |first_mark| and |bot_mark| */ + if (first_mark(mark_class(p)) == null) { + set_first_mark(mark_class(p), mark_ptr(p)); + add_token_ref(first_mark(mark_class(p))); + } + if (bot_mark(mark_class(p)) != null) + delete_token_ref(bot_mark(mark_class(p))); + set_bot_mark(mark_class(p), mark_ptr(p)); + add_token_ref(bot_mark(mark_class(p))); + + } + prev_p = p; + p = vlink(prev_p); + } + split_top_skip = save_split_top_skip; + /* Break the current page at node |p|, put it in box~|output_box|, + and put the remaining nodes on the contribution list */ + /* When the following code is executed, the current page runs from node + |vlink(page_head)| to node |prev_p|, and the nodes from |p| to |page_tail| + are to be placed back at the front of the contribution list. Furthermore + the heldover insertions appear in a list from |vlink(hold_head)| to |q|; we + will put them into the current page list for safekeeping while the user's + output routine is active. We might have |q=hold_head|; and |p=null| if + and only if |prev_p=page_tail|. Error messages are suppressed within + |vpackage|, since the box might appear to be overfull or underfull simply + because the stretch and shrink from the \.{\\skip} registers for inserts + are not actually present in the box. */ + + if (p != null) { + if (vlink(contrib_head) == null) { + contrib_tail = page_tail; + } + vlink(page_tail) = vlink(contrib_head); + vlink(contrib_head) = p; + vlink(prev_p) = null; + } + save_vbadness = vbadness; + vbadness = inf_bad; + save_vfuzz = vfuzz; + vfuzz = max_dimen; /* inhibit error messages */ + box(output_box) = + filtered_vpackage(vlink(page_head), best_size, exactly, page_max_depth, + output_group, body_direction); + vbadness = save_vbadness; + vfuzz = save_vfuzz; + if (last_glue != max_halfword) + delete_glue_ref(last_glue); + /* Start a new current page */ + start_new_page(); /* this sets |last_glue:=max_halfword| */ + if (q != hold_head) { + vlink(page_head) = vlink(hold_head); + page_tail = q; + } + + /* Delete the page-insertion nodes */ + r = vlink(page_ins_head); + while (r != page_ins_head) { + q = vlink(r); + flush_node(r); + r = q; + } + vlink(page_ins_head) = page_ins_head; + + for (i = 0; i <= biggest_used_mark; i++) { + if ((top_mark(i) != null) && (first_mark(i) == null)) { + set_first_mark(i, top_mark(i)); + add_token_ref(top_mark(i)); + } + } + if (output_routine != null) { + if (dead_cycles >= max_dead_cycles) { + /* Explain that too many dead cycles have occurred in a row */ + print_err("Output loop---"); + print_int(dead_cycles); + tprint(" consecutive dead cycles"); + help3("I've concluded that your \\output is awry; it never does a", + "\\shipout, so I'm shipping \\box\\outputbox out myself. Next time", + "increase \\maxdeadcycles if you want me to be more patient!"); + error(); + + } else { + /* Fire up the users output routine and |return| */ + output_active = true; + incr(dead_cycles); + push_nest(); + mode = -vmode; + prev_depth = pdf_ignored_dimen; + mode_line = -line; + begin_token_list(output_routine, output_text); + new_save_level(output_group); + normal_paragraph(); + scan_left_brace(); + return; + + } + } + /* Perform the default output routine */ + /* The list of heldover insertions, running from |vlink(page_head)| to + |page_tail|, must be moved to the contribution list when the user has + specified no output routine. */ + if (vlink(page_head) != null) { + if (vlink(contrib_head) == null) { + contrib_tail = page_tail; + } else { + vlink(page_tail) = vlink(contrib_head); + } + vlink(contrib_head) = vlink(page_head); + vlink(page_head) = null; + page_tail = page_head; + } + flush_node_list(page_disc); + page_disc = null; + ship_out(static_pdf, box(output_box), true); + box(output_box) = null; +} + + +@ When the user's output routine finishes, it has constructed a vlist +in internal vertical mode, and \TeX\ will do the following: + +@c +void resume_after_output(void) +{ + if ((iloc != null) + || ((token_type != output_text) && (token_type != backed_up))) { + /* Recover from an unbalanced output routine */ + print_err("Unbalanced output routine"); + help2("Your sneaky output routine has problematic {'s and/or }'s.", + "I can't handle that very well; good luck."); + error(); + do { + get_token(); + } while (iloc != null); + /* loops forever if reading from a file, since |null=min_halfword<=0| */ + + } + end_token_list(); /* conserve stack space in case more outputs are triggered */ + end_graf(bottom_level); + unsave(); + output_active = false; + insert_penalties = 0; + /* Ensure that box |output_box| is empty after output */ + if (box(output_box) != null) { + print_err("Output routine didn't use all of \\box"); + print_int(output_box); + help3("Your \\output commands should empty \\box\\outputbox,", + "e.g., by saying `\\shipout\\box\\outputbox'.", + "Proceed; I'll discard its present contents."); + box_error(output_box); + } + + if (tail != head) { /* current list goes after heldover insertions */ + vlink(page_tail) = vlink(head); + page_tail = tail; + } + if (vlink(page_head) != null) { /* and both go before heldover contributions */ + if (vlink(contrib_head) == null) + contrib_tail = page_tail; + vlink(page_tail) = vlink(contrib_head); + vlink(contrib_head) = vlink(page_head); + vlink(page_head) = null; + page_tail = page_head; + } + flush_node_list(page_disc); + page_disc = null; + pop_nest(); + lua_node_filter_s(buildpage_filter_callback, "after_output"); + build_page(); + +} diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.h b/Build/source/texk/web2c/luatexdir/tex/commands.h new file mode 100644 index 00000000000..88d012aaa5c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/commands.h @@ -0,0 +1,355 @@ +/* commands.h + + Copyright 2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: commands.h 2899 2009-07-21 23:03:53Z oneiros $ */ + +#ifndef COMMANDS_H +# define COMMANDS_H + +/* +Before we can go any further, we need to define symbolic names for the internal +code numbers that represent the various commands obeyed by \TeX. These codes +are somewhat arbitrary, but not completely so. For example, the command +codes for character types are fixed by the language, since a user says, +e.g., `\.{\\catcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter, +and the command code |math_shift| is equal to~3. Some other codes have +been made adjacent so that |case| statements in the program need not consider +cases that are widely spaced, or so that |case| statements can be replaced +by |if| statements. + +At any rate, here is the list, for future reference. First come the +``catcode'' commands, several of which share their numeric codes with +ordinary commands when the catcode cannot emerge from \TeX's scanning routine. + +@ Next are the ordinary run-of-the-mill command codes. Codes that are +|min_internal| or more represent internal quantities that might be +expanded by `\.{\\the}'. + + +@ The next codes are special; they all relate to mode-independent +assignment of values to \TeX's internal registers or tables. +Codes that are |max_internal| or less represent internal quantities +that might be expanded by `\.{\\the}'. + +There is no matching primitive to go with |assign_attr|, but even if +there was no \.{\\attributedef}, a reserved number would still be +needed because there is an implied correspondence between the +|assign_xxx| commands and |xxx_val| expression values. That would +break down otherwise. + +@ The remaining command codes are extra special, since they cannot get through +\TeX's scanner to the main control routine. They have been given values higher +than |max_command| so that their special nature is easily discernible. +The ``expandable'' commands come first. + + +*/ + +typedef enum { + relax_cmd = 0, /* do nothing ( \.{\\relax} ) */ +# define escape_cmd relax_cmd /* escape delimiter (called \.\\ in {\sl The \TeX book\/}) */ + left_brace_cmd, /* beginning of a group ( \.\{ ) */ + right_brace_cmd, /* ending of a group ( \.\} ) */ + math_shift_cmd, /* mathematics shift character ( \.\$ ) */ + tab_mark_cmd, /* alignment delimiter ( \.\&, \.{\\span} ) */ + car_ret_cmd, /* end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} ) */ +# define out_param_cmd car_ret_cmd /* output a macro parameter */ + mac_param_cmd, /* macro parameter symbol ( \.\# ) */ + sup_mark_cmd, /* superscript ( \.{\char'136} ) */ + sub_mark_cmd, /* subscript ( \.{\char'137} ) */ + endv_cmd, /* end of \<v_j> list in alignment template */ +# define ignore_cmd endv_cmd /* characters to ignore ( \.{\^\^@@} ) */ + spacer_cmd, /* characters equivalent to blank space ( \.{\ } ) */ + letter_cmd, /* characters regarded as letters ( \.{A..Z}, \.{a..z} ) */ + other_char_cmd, /* none of the special character types */ + par_end_cmd, /* end of paragraph ( \.{\\par} ) */ +# define active_char_cmd par_end_cmd /* characters that invoke macros ( \.{\char`\~} ) */ +# define match_cmd par_end_cmd /* match a macro parameter */ + stop_cmd, /* end of job ( \.{\\end}, \.{\\dump} ) */ +# define comment_cmd stop_cmd /* characters that introduce comments ( \.\% ) */ +# define end_match_cmd stop_cmd /* end of parameters to macro */ + delim_num_cmd, /* specify delimiter numerically ( \.{\\delimiter} ) */ +# define invalid_char_cmd delim_num_cmd /* characters that shouldn't appear ( \.{\^\^?} ) */ +# define max_char_code_cmd delim_num_cmd + /* largest catcode for individual characters */ + char_num_cmd, /* character specified numerically ( \.{\\char} ) */ + math_char_num_cmd, /* explicit math code ( \.{\\mathchar} ) */ + mark_cmd, /* mark definition ( \.{\\mark} ) */ + xray_cmd, /* peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~) */ + make_box_cmd, /* make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~) */ + hmove_cmd, /* horizontal motion ( \.{\\moveleft}, \.{\\moveright} ) */ + vmove_cmd, /* vertical motion ( \.{\\raise}, \.{\\lower} ) */ + un_hbox_cmd, /* unglue a box ( \.{\\unhbox}, \.{\\unhcopy} ) */ + un_vbox_cmd, /* unglue a box ( \.{\\unvbox}, \.{\\unvcopy} or + \.{\\pagediscards}, \.{\\splitdiscards} ) */ + remove_item_cmd, /* nullify last item ( \.{\\unpenalty}, \.{\\unkern}, \.{\\unskip} ) */ + hskip_cmd, /* horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~) */ + vskip_cmd, /* vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~) */ + mskip_cmd, /* math glue ( \.{\\mskip} ) */ + kern_cmd, /* fixed space ( \.{\\kern}) */ + mkern_cmd, /* math kern ( \.{\\mkern} ) */ + leader_ship_cmd, /* use a box ( \.{\\shipout}, \.{\\leaders}, etc.~) */ + halign_cmd, /* horizontal table alignment ( \.{\\halign} ) */ + valign_cmd, /* vertical table alignment ( \.{\\valign} ) */ + no_align_cmd, /* temporary escape from alignment ( \.{\\noalign} ) */ + vrule_cmd, /* vertical rule ( \.{\\vrule} ) */ + hrule_cmd, /* horizontal rule ( \.{\\hrule} ) */ + insert_cmd, /* vlist inserted in box ( \.{\\insert} ) */ + vadjust_cmd, /* vlist inserted in enclosing paragraph ( \.{\\vadjust} ) */ + ignore_spaces_cmd, /* gobble |spacer| tokens ( \.{\\ignorespaces} ) */ + after_assignment_cmd, /* save till assignment is done ( \.{\\afterassignment} ) */ + after_group_cmd, /* save till group is done ( \.{\\aftergroup} ) */ + break_penalty_cmd, /* additional badness ( \.{\\penalty} ) */ + start_par_cmd, /* begin paragraph ( \.{\\indent}, \.{\\noindent} ) */ + ital_corr_cmd, /* italic correction ( \.{\\/} ) */ + accent_cmd, /* attach accent in text ( \.{\\accent} ) */ + math_accent_cmd, /* attach accent in math ( \.{\\mathaccent} ) */ + discretionary_cmd, /* discretionary texts ( \.{\\-}, \.{\\discretionary} ) */ + eq_no_cmd, /* equation number ( \.{\\eqno}, \.{\\leqno} ) */ + left_right_cmd, /* variable delimiter ( \.{\\left}, \.{\\right} or \.{\\middle} ) */ + math_comp_cmd, /* component of formula ( \.{\\mathbin}, etc.~) */ + limit_switch_cmd, /* diddle limit conventions ( \.{\\displaylimits}, etc.~) */ + above_cmd, /* generalized fraction ( \.{\\above}, \.{\\atop}, etc.~) */ + math_style_cmd, /* style specification ( \.{\\displaystyle}, etc.~) */ + math_choice_cmd, /* choice specification ( \.{\\mathchoice} ) */ + non_script_cmd, /* conditional math glue ( \.{\\nonscript} ) */ + vcenter_cmd, /* vertically center a vbox ( \.{\\vcenter} ) */ + case_shift_cmd, /* force specific case ( \.{\\lowercase}, \.{\\uppercase}~) */ + message_cmd, /* send to user ( \.{\\message}, \.{\\errmessage} ) */ + extension_cmd, /* extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~) */ + in_stream_cmd, /* files for reading ( \.{\\openin}, \.{\\closein} ) */ + begin_group_cmd, /* begin local grouping ( \.{\\begingroup} ) */ + end_group_cmd, /* end local grouping ( \.{\\endgroup} ) */ + omit_cmd, /* omit alignment template ( \.{\\omit} ) */ + ex_space_cmd, /* explicit space ( \.{\\\ } ) */ + no_boundary_cmd, /* suppress boundary ligatures ( \.{\\noboundary} ) */ + radical_cmd, /* square root and similar signs ( \.{\\radical} ) */ + super_sub_script_cmd, /* explicit super- or subscript */ + math_shift_cs_cmd, /* start- and endmath */ + end_cs_name_cmd, /* end control sequence ( \.{\\endcsname} ) */ + char_ghost_cmd, /* \.{\\ghostleft}, \.{\\ghostright} character for kerning */ + assign_local_box_cmd, /* box for guillemets \.{\\localleftbox} or \.{\\localrightbox} */ + char_given_cmd, /* character code defined by \.{\\chardef} */ +# define min_internal_cmd char_given_cmd + /* the smallest code that can follow \.{\\the} */ + math_given_cmd, /* math code defined by \.{\\mathchardef} */ + omath_given_cmd, /* math code defined by \.{\\omathchardef} */ + xmath_given_cmd, /* math code defined by \.{\\LuaTeXmathchardef} */ + last_item_cmd, /* most recent item ( \.{\\lastpenalty}, \.{\\lastkern}, \.{\\lastskip} ) */ +# define max_non_prefixed_command_cmd last_item_cmd /* largest command code that can't be \.{\\global} */ + toks_register_cmd, /* token list register ( \.{\\toks} ) */ + assign_toks_cmd, /* special token list ( \.{\\output}, \.{\\everypar}, etc.~) */ + assign_int_cmd, /* user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~) */ + assign_attr_cmd, /* user-defined attributes */ + assign_dimen_cmd, /* user-defined length ( \.{\\hsize}, etc.~) */ + assign_glue_cmd, /* user-defined glue ( \.{\\baselineskip}, etc.~) */ + assign_mu_glue_cmd, /* user-defined muglue ( \.{\\thinmuskip}, etc.~) */ + assign_font_dimen_cmd, /* user-defined font dimension ( \.{\\fontdimen} ) */ + assign_font_int_cmd, /* user-defined font integer ( \.{\\hyphenchar}, \.{\\skewchar} ) */ + set_aux_cmd, /* specify state info ( \.{\\spacefactor}, \.{\\prevdepth} ) */ + set_prev_graf_cmd, /* specify state info ( \.{\\prevgraf} ) */ + set_page_dimen_cmd, /* specify state info ( \.{\\pagegoal}, etc.~) */ + set_page_int_cmd, /* specify state info ( \.{\\deadcycles}, \.{\\insertpenalties} ) */ + set_box_dimen_cmd, /* change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} ) */ + set_tex_shape_cmd, /* specify fancy paragraph shape ( \.{\\parshape} ) */ + set_etex_shape_cmd, /* specify etex extended list ( \.{\\interlinepenalties}, etc.~) */ + def_char_code_cmd, /* define a character code ( \.{\\catcode}, etc.~) */ + def_del_code_cmd, /* define a delimiter code ( \.{\\delcode}) */ + extdef_math_code_cmd, /* define an extended character code ( \.{\\omathcode}, etc.~) */ + extdef_del_code_cmd, /* define an extended delimiter code ( \.{\\odelcode}, etc.~) */ + def_family_cmd, /* declare math fonts ( \.{\\textfont}, etc.~) */ + set_math_param_cmd, /* set math parameters ( \.{\\mathquad}, etc.~) */ + set_font_cmd, /* set current font ( font identifiers ) */ + def_font_cmd, /* define a font file ( \.{\\font} ) */ + register_cmd, /* internal register ( \.{\\count}, \.{\\dimen}, etc.~) */ + assign_box_dir_cmd, /* (\.{\\boxdir}) */ + assign_dir_cmd, /* (\.{\\pagedir}, \.{\\textdir}) */ +# define max_internal_cmd assign_dir_cmd + /* the largest code that can follow \.{\\the} */ + advance_cmd, /* advance a register or parameter ( \.{\\advance} ) */ + multiply_cmd, /* multiply a register or parameter ( \.{\\multiply} ) */ + divide_cmd, /* divide a register or parameter ( \.{\\divide} ) */ + prefix_cmd, /* qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} ) */ + let_cmd, /* assign a command code ( \.{\\let}, \.{\\futurelet} ) */ + shorthand_def_cmd, /* code definition ( \.{\\chardef}, \.{\\countdef}, etc.~) */ + read_to_cs_cmd, /* read into a control sequence ( \.{\\read} ) */ + def_cmd, /* macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} ) */ + set_box_cmd, /* set a box ( \.{\\setbox} ) */ + hyph_data_cmd, /* hyphenation data ( \.{\\hyphenation}, \.{\\patterns} ) */ + set_interaction_cmd, /* define level of interaction ( \.{\\batchmode}, etc.~) */ + letterspace_font_cmd, /* letterspace a font ( \.{\\letterspacefont} ) */ + pdf_copy_font_cmd, /* create a new font instance ( \.{\\pdfcopyfont} ) */ + set_ocp_cmd, /* Place a translation process in the stream */ + def_ocp_cmd, /* Define and load a translation process */ + set_ocp_list_cmd, /* Place a list of OCPs in the stream */ + def_ocp_list_cmd, /* Define a list of OCPs */ + clear_ocp_lists_cmd, /* Remove all active OCP lists */ + push_ocp_list_cmd, /* Add to the sequence of active OCP lists */ + pop_ocp_list_cmd, /* Remove from the sequence of active OCP lists */ + ocp_list_op_cmd, /* Operations for building a list of OCPs */ + ocp_trace_level_cmd, /* Tracing of active OCPs, either 0 or 1 */ + undefined_cs_cmd, /* initial state of most |eq_type| fields */ + expand_after_cmd, /* special expansion ( \.{\\expandafter} ) */ + no_expand_cmd, /* special nonexpansion ( \.{\\noexpand} ) */ + input_cmd, /* input a source file ( \.{\\input}, \.{\\endinput} or + \.{\\scantokens} or \.{\\scantextokens} ) */ + if_test_cmd, /* conditional text ( \.{\\if}, \.{\\ifcase}, etc.~) */ + fi_or_else_cmd, /* delimiters for conditionals ( \.{\\else}, etc.~) */ + cs_name_cmd, /* make a control sequence from tokens ( \.{\\csname} ) */ + convert_cmd, /* convert to text ( \.{\\number}, \.{\\string}, etc.~) */ + the_cmd, /* expand an internal quantity ( \.{\\the} or \.{\\unexpanded}, \.{\\detokenize} ) */ + top_bot_mark_cmd, /* inserted mark ( \.{\\topmark}, etc.~) */ + call_cmd, /* non-long, non-outer control sequence */ + long_call_cmd, /* long, non-outer control sequence */ + outer_call_cmd, /* non-long, outer control sequence */ + long_outer_call_cmd, /* long, outer control sequence */ + end_template_cmd, /* end of an alignment template */ + dont_expand_cmd, /* the following token was marked by \.{\\noexpand} */ + glue_ref_cmd, /* the equivalent points to a glue specification */ + shape_ref_cmd, /* the equivalent points to a parshape specification */ + box_ref_cmd, /* the equivalent points to a box node, or is |null| */ + data_cmd, /* the equivalent is simply a halfword number */ +} tex_command_code; + +# define max_command_cmd ocp_trace_level_cmd /* the largest command code seen at |big_switch| */ +# define last_cmd data_cmd +# define max_non_prefixed_command last_item_cmd + +typedef enum { + above_code = 0, + over_code = 1, + atop_code = 2, + delimited_code = 3 +} fraction_codes; + +typedef enum { + number_code = 0, /* command code for \.{\\number} */ + roman_numeral_code, /* command code for \.{\\romannumeral} */ + string_code, /* command code for \.{\\string} */ + meaning_code, /* command code for \.{\\meaning} */ + font_name_code, /* command code for \.{\\fontname} */ + etex_code, /* command code for \.{\\eTeXVersion} */ + omega_code, /* command code for \.{\\OmegaVersion} */ + aleph_code, /* command code for \.{\\AlephVersion} */ + format_name_code, /* command code for \.{\\AlephVersion} */ + pdftex_revision_code, /* command code for \.{\\pdftexrevision} */ +# define pdftex_first_expand_code pdftex_revision_code /* base for \pdfTeX's command codes */ + pdftex_banner_code, /* command code for \.{\\pdftexbanner} */ + pdf_font_name_code, /* command code for \.{\\pdffontname} */ + pdf_font_objnum_code, /* command code for \.{\\pdffontobjnum} */ + pdf_font_size_code, /* command code for \.{\\pdffontsize} */ + pdf_page_ref_code, /* command code for \.{\\pdfpageref} */ + pdf_xform_name_code, /* command code for \.{\\pdfxformname} */ + left_margin_kern_code, /* command code for \.{\\leftmarginkern} */ + right_margin_kern_code, /* command code for \.{\\rightmarginkern} */ + pdf_creation_date_code, /* command code for \.{\\pdfcreationdate} */ + uniform_deviate_code, /* command code for \.{\\uniformdeviate} */ + normal_deviate_code, /* command code for \.{\\normaldeviate} */ + pdf_insert_ht_code, /* command code for \.{\\pdfinsertht} */ + pdf_ximage_bbox_code, /* command code for \.{\\pdfximagebbox} */ + lua_code, /* command code for \.{\\directlua} */ + lua_escape_string_code, /* command code for \.{\\luaescapestring} */ + pdf_colorstack_init_code, /* command code for \.{\\pdfcolorstackinit} */ + luatex_revision_code, /* command code for \.{\\luatexrevision} */ + luatex_date_code, /* command code for \.{\\luatexdate} */ + math_style_code, /* command code for \.{\\mathstyle} */ + expanded_code, /* command code for \.{\\expanded} */ + job_name_code, /* command code for \.{\\jobname} */ + Aleph_revision_code, /* command code for \.{\\Alephrevision} */ + Omega_revision_code, /* command code for \.{\\Omegarevision} */ + eTeX_revision_code, /* command code for \.{\\eTeXrevision} */ + font_identifier_code, /* command code for \.{tex.fontidentifier} (virtual) */ +} convert_codes; + +typedef enum { + lastpenalty_code = 0, /* code for \.{\\lastpenalty} */ + lastattr_code, /* not used */ + lastkern_code, /* code for \.{\\lastkern} */ + lastskip_code, /* code for \.{\\lastskip} */ + last_node_type_code, /* code for \.{\\lastnodetype} */ + input_line_no_code, /* code for \.{\\inputlineno} */ + badness_code, /* code for \.{\\badness} */ + pdftex_version_code, /* code for \.{\\pdftexversion} */ +# define pdftex_first_rint_code pdftex_version_code /* base for \pdfTeX's command codes */ + pdf_last_obj_code, /* code for \.{\\pdflastobj} */ + pdf_last_xform_code, /* code for \.{\\pdflastxform} */ + pdf_last_ximage_code, /* code for \.{\\pdflastximage} */ + pdf_last_ximage_pages_code, /* code for \.{\\pdflastximagepages} */ + pdf_last_annot_code, /* code for \.{\\pdflastannot} */ + pdf_last_x_pos_code, /* code for \.{\\pdflastxpos} */ + pdf_last_y_pos_code, /* code for \.{\\pdflastypos} */ + pdf_retval_code, /* global multi-purpose return value */ + pdf_last_ximage_colordepth_code, /* code for \.{\\pdflastximagecolordepth} */ + random_seed_code, /* code for \.{\\pdfrandomseed} */ + pdf_last_link_code, /* code for \.{\\pdflastlink} */ + luatex_version_code, /* code for \.{\\luatexversion} */ + Aleph_version_code, /* code for \.{\\Alephversion} */ +# define Aleph_int Aleph_version_code + Omega_version_code, /* code for \.{\\Omegaversion} */ + Aleph_minor_version_code, /* code for \.{\\Alephminorversion} */ + Omega_minor_version_code, /* code for \.{\\Omegaminorversion} */ + eTeX_minor_version_code, /* code for \.{\\eTeXminorversion} */ + eTeX_version_code, /* code for \.{\\eTeXversion} */ +# define eTeX_int eTeX_version_code /* first of \eTeX\ codes for integers */ + current_group_level_code, /* code for \.{\\currentgrouplevel} */ + current_group_type_code, /* code for \.{\\currentgrouptype} */ + current_if_level_code, /* code for \.{\\currentiflevel} */ + current_if_type_code, /* code for \.{\\currentiftype} */ + current_if_branch_code, /* code for \.{\\currentifbranch} */ + glue_stretch_order_code, /* code for \.{\\gluestretchorder} */ + glue_shrink_order_code, /* code for \.{\\glueshrinkorder} */ + font_char_wd_code, /* code for \.{\\fontcharwd} */ +# define eTeX_dim font_char_wd_code /* first of \eTeX\ codes for dimensions */ + font_char_ht_code, /* code for \.{\\fontcharht} */ + font_char_dp_code, /* code for \.{\\fontchardp} */ + font_char_ic_code, /* code for \.{\\fontcharic} */ + par_shape_length_code, /* code for \.{\\parshapelength} */ + par_shape_indent_code, /* code for \.{\\parshapeindent} */ + par_shape_dimen_code, /* code for \.{\\parshapedimen} */ + glue_stretch_code, /* code for \.{\\gluestretch} */ + glue_shrink_code, /* code for \.{\\glueshrink} */ + mu_to_glue_code, /* code for \.{\\mutoglue} */ +# define eTeX_glue mu_to_glue_code /* first of \eTeX\ codes for glue */ + glue_to_mu_code, /* code for \.{\\gluetomu} */ +# define eTeX_mu glue_to_mu_code + /* first of \eTeX\ codes for muglue */ + numexpr_code, /* code for \.{\\numexpr} */ +# define eTeX_expr numexpr_code /* first of \eTeX\ codes for expressions */ + attrexpr_code, /* not used */ + dimexpr_code, /* code for \.{\\dimexpr} */ + glueexpr_code, /* code for \.{\\glueexpr} */ + muexpr_code, /* code for \.{\\muexpr} */ +} last_item_codes; + +# define explicit 1 +# define acc_kern 2 +# define lp_code_base 2 +# define rp_code_base 3 +# define ef_code_base 4 +# define tag_code 5 +# define auto_kern explicit +# define no_lig_code 6 + +# define immediate_code 4 /* command modifier for \.{\\immediate} */ + +extern void initialize_commands(void); + +extern void initialize_etex_commands(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.w b/Build/source/texk/web2c/luatexdir/tex/commands.w new file mode 100644 index 00000000000..343c04fe6b8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/commands.w @@ -0,0 +1,1223 @@ +% commands.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +\def\eTeX{e-\TeX} + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: commands.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/commands.w $"; + +@ The symbolic names for glue parameters are put into \TeX's hash table +by using the routine called |primitive|, defined below. Let us enter them +now, so that we don't have to list all those parameter names anywhere else. + +@c +void initialize_commands(void) +{ + + primitive_tex("lineskip", assign_glue_cmd, glue_base + line_skip_code, + glue_base); + primitive_tex("baselineskip", assign_glue_cmd, + glue_base + baseline_skip_code, glue_base); + primitive_tex("parskip", assign_glue_cmd, glue_base + par_skip_code, + glue_base); + primitive_tex("abovedisplayskip", assign_glue_cmd, + glue_base + above_display_skip_code, glue_base); + primitive_tex("belowdisplayskip", assign_glue_cmd, + glue_base + below_display_skip_code, glue_base); + primitive_tex("abovedisplayshortskip", assign_glue_cmd, + glue_base + above_display_short_skip_code, glue_base); + primitive_tex("belowdisplayshortskip", assign_glue_cmd, + glue_base + below_display_short_skip_code, glue_base); + primitive_tex("leftskip", assign_glue_cmd, glue_base + left_skip_code, + glue_base); + primitive_tex("rightskip", assign_glue_cmd, glue_base + right_skip_code, + glue_base); + primitive_tex("topskip", assign_glue_cmd, glue_base + top_skip_code, + glue_base); + primitive_tex("splittopskip", assign_glue_cmd, + glue_base + split_top_skip_code, glue_base); + primitive_tex("tabskip", assign_glue_cmd, glue_base + tab_skip_code, + glue_base); + primitive_tex("spaceskip", assign_glue_cmd, glue_base + space_skip_code, + glue_base); + primitive_tex("xspaceskip", assign_glue_cmd, glue_base + xspace_skip_code, + glue_base); + primitive_tex("parfillskip", assign_glue_cmd, + glue_base + par_fill_skip_code, glue_base); + primitive_tex("thinmuskip", assign_mu_glue_cmd, + glue_base + thin_mu_skip_code, glue_base + thin_mu_skip_code); + primitive_tex("medmuskip", assign_mu_glue_cmd, glue_base + med_mu_skip_code, + glue_base + thin_mu_skip_code); + primitive_tex("thickmuskip", assign_mu_glue_cmd, + glue_base + thick_mu_skip_code, + glue_base + thin_mu_skip_code); + primitive_tex("output", assign_toks_cmd, output_routine_loc, local_base); + primitive_tex("everypar", assign_toks_cmd, every_par_loc, local_base); + primitive_tex("everymath", assign_toks_cmd, every_math_loc, local_base); + primitive_tex("everydisplay", assign_toks_cmd, every_display_loc, + local_base); + primitive_tex("everyhbox", assign_toks_cmd, every_hbox_loc, local_base); + primitive_tex("everyvbox", assign_toks_cmd, every_vbox_loc, local_base); + primitive_tex("everyjob", assign_toks_cmd, every_job_loc, local_base); + primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base); + primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base); + primitive_pdftex("pdfpagesattr", assign_toks_cmd, pdf_pages_attr_loc, + local_base); + primitive_pdftex("pdfpageattr", assign_toks_cmd, pdf_page_attr_loc, + local_base); + primitive_pdftex("pdfpageresources", assign_toks_cmd, + pdf_page_resources_loc, local_base); + primitive_pdftex("pdfxformattr", assign_toks_cmd, pdf_xform_attr_loc, + local_base); + primitive_pdftex("pdfxformresources", assign_toks_cmd, + pdf_xform_resources_loc, local_base); + primitive_pdftex("pdfpkmode", assign_toks_cmd, pdf_pk_mode_loc, local_base); + + /* The integer parameter names must be entered into the hash table */ + primitive_tex("pretolerance", assign_int_cmd, int_base + pretolerance_code, + int_base); + primitive_tex("tolerance", assign_int_cmd, int_base + tolerance_code, + int_base); + primitive_tex("linepenalty", assign_int_cmd, int_base + line_penalty_code, + int_base); + primitive_tex("hyphenpenalty", assign_int_cmd, + int_base + hyphen_penalty_code, int_base); + primitive_tex("exhyphenpenalty", assign_int_cmd, + int_base + ex_hyphen_penalty_code, int_base); + primitive_tex("clubpenalty", assign_int_cmd, int_base + club_penalty_code, + int_base); + primitive_tex("widowpenalty", assign_int_cmd, int_base + widow_penalty_code, + int_base); + primitive_tex("displaywidowpenalty", assign_int_cmd, + int_base + display_widow_penalty_code, int_base); + primitive_tex("brokenpenalty", assign_int_cmd, + int_base + broken_penalty_code, int_base); + primitive_tex("binoppenalty", assign_int_cmd, + int_base + bin_op_penalty_code, int_base); + primitive_tex("relpenalty", assign_int_cmd, int_base + rel_penalty_code, + int_base); + primitive_tex("predisplaypenalty", assign_int_cmd, + int_base + pre_display_penalty_code, int_base); + primitive_tex("postdisplaypenalty", assign_int_cmd, + int_base + post_display_penalty_code, int_base); + primitive_tex("interlinepenalty", assign_int_cmd, + int_base + inter_line_penalty_code, int_base); + primitive_tex("doublehyphendemerits", assign_int_cmd, + int_base + double_hyphen_demerits_code, int_base); + primitive_tex("finalhyphendemerits", assign_int_cmd, + int_base + final_hyphen_demerits_code, int_base); + primitive_tex("adjdemerits", assign_int_cmd, int_base + adj_demerits_code, + int_base); + primitive_tex("mag", assign_int_cmd, int_base + mag_code, int_base); + primitive_tex("delimiterfactor", assign_int_cmd, + int_base + delimiter_factor_code, int_base); + primitive_tex("looseness", assign_int_cmd, int_base + looseness_code, + int_base); + primitive_tex("time", assign_int_cmd, int_base + time_code, int_base); + primitive_tex("day", assign_int_cmd, int_base + day_code, int_base); + primitive_tex("month", assign_int_cmd, int_base + month_code, int_base); + primitive_tex("year", assign_int_cmd, int_base + year_code, int_base); + primitive_tex("showboxbreadth", assign_int_cmd, + int_base + show_box_breadth_code, int_base); + primitive_tex("showboxdepth", assign_int_cmd, + int_base + show_box_depth_code, int_base); + primitive_tex("hbadness", assign_int_cmd, int_base + hbadness_code, + int_base); + primitive_tex("vbadness", assign_int_cmd, int_base + vbadness_code, + int_base); + primitive_tex("pausing", assign_int_cmd, int_base + pausing_code, int_base); + primitive_tex("tracingonline", assign_int_cmd, + int_base + tracing_online_code, int_base); + primitive_tex("tracingmacros", assign_int_cmd, + int_base + tracing_macros_code, int_base); + primitive_tex("tracingstats", assign_int_cmd, int_base + tracing_stats_code, + int_base); + primitive_tex("tracingparagraphs", assign_int_cmd, + int_base + tracing_paragraphs_code, int_base); + primitive_tex("tracingpages", assign_int_cmd, int_base + tracing_pages_code, + int_base); + primitive_tex("tracingoutput", assign_int_cmd, + int_base + tracing_output_code, int_base); + primitive_tex("tracinglostchars", assign_int_cmd, + int_base + tracing_lost_chars_code, int_base); + primitive_tex("tracingcommands", assign_int_cmd, + int_base + tracing_commands_code, int_base); + primitive_tex("tracingrestores", assign_int_cmd, + int_base + tracing_restores_code, int_base); + primitive_tex("uchyph", assign_int_cmd, int_base + uc_hyph_code, int_base); + primitive_tex("outputpenalty", assign_int_cmd, + int_base + output_penalty_code, int_base); + primitive_tex("maxdeadcycles", assign_int_cmd, + int_base + max_dead_cycles_code, int_base); + primitive_tex("hangafter", assign_int_cmd, int_base + hang_after_code, + int_base); + primitive_tex("floatingpenalty", assign_int_cmd, + int_base + floating_penalty_code, int_base); + primitive_tex("globaldefs", assign_int_cmd, int_base + global_defs_code, + int_base); + primitive_tex("fam", assign_int_cmd, int_base + cur_fam_code, int_base); + primitive_tex("escapechar", assign_int_cmd, int_base + escape_char_code, + int_base); + primitive_tex("defaulthyphenchar", assign_int_cmd, + int_base + default_hyphen_char_code, int_base); + primitive_tex("defaultskewchar", assign_int_cmd, + int_base + default_skew_char_code, int_base); + primitive_tex("endlinechar", assign_int_cmd, int_base + end_line_char_code, + int_base); + primitive_tex("newlinechar", assign_int_cmd, int_base + new_line_char_code, + int_base); + primitive_tex("language", assign_int_cmd, int_base + language_code, + int_base); + primitive_tex("setlanguage", assign_int_cmd, int_base + cur_lang_code, + int_base); + primitive_tex("exhyphenchar", assign_int_cmd, + int_base + ex_hyphen_char_code, int_base); + primitive_tex("lefthyphenmin", assign_int_cmd, + int_base + left_hyphen_min_code, int_base); + primitive_tex("righthyphenmin", assign_int_cmd, + int_base + right_hyphen_min_code, int_base); + primitive_tex("holdinginserts", assign_int_cmd, + int_base + holding_inserts_code, int_base); + primitive_tex("errorcontextlines", assign_int_cmd, + int_base + error_context_lines_code, int_base); + primitive_luatex("luastartup", assign_int_cmd, + int_base + luastartup_id_code, int_base); + primitive_luatex("nokerns", assign_int_cmd, int_base + disable_kern_code, + int_base); + primitive_luatex("noligs", assign_int_cmd, int_base + disable_lig_code, + int_base); + primitive_luatex("catcodetable", assign_int_cmd, + int_base + cat_code_table_code, int_base); + primitive_luatex("outputbox", assign_int_cmd, int_base + output_box_code, + int_base); + primitive_pdftex("pdfoutput", assign_int_cmd, int_base + pdf_output_code, + int_base); + primitive_pdftex("pdfcompresslevel", assign_int_cmd, + int_base + pdf_compress_level_code, int_base); + primitive_pdftex("pdfobjcompresslevel", assign_int_cmd, + int_base + pdf_objcompresslevel_code, int_base); + primitive_pdftex("pdfdecimaldigits", assign_int_cmd, + int_base + pdf_decimal_digits_code, int_base); + primitive_pdftex("pdfimageresolution", assign_int_cmd, + int_base + pdf_image_resolution_code, int_base); + primitive_pdftex("pdfpkresolution", assign_int_cmd, + int_base + pdf_pk_resolution_code, int_base); + primitive_pdftex("pdfuniqueresname", assign_int_cmd, + int_base + pdf_unique_resname_code, int_base); + primitive_pdftex("pdfoptionpdfminorversion", assign_int_cmd, + int_base + pdf_minor_version_code, int_base); + primitive_pdftex("pdfminorversion", assign_int_cmd, + int_base + pdf_minor_version_code, int_base); + primitive_pdftex("pdfpagebox", assign_int_cmd, int_base + pdf_pagebox_code, + int_base); + primitive_pdftex("pdfinclusionerrorlevel", assign_int_cmd, + int_base + pdf_inclusion_errorlevel_code, int_base); + primitive_pdftex("pdfgamma", assign_int_cmd, int_base + pdf_gamma_code, + int_base); + primitive_pdftex("pdfimagegamma", assign_int_cmd, + int_base + pdf_image_gamma_code, int_base); + primitive_pdftex("pdfimagehicolor", assign_int_cmd, + int_base + pdf_image_hicolor_code, int_base); + primitive_pdftex("pdfimageapplygamma", assign_int_cmd, + int_base + pdf_image_apply_gamma_code, int_base); + primitive_pdftex("pdfadjustspacing", assign_int_cmd, + int_base + pdf_adjust_spacing_code, int_base); + primitive_pdftex("pdfprotrudechars", assign_int_cmd, + int_base + pdf_protrude_chars_code, int_base); + primitive_pdftex("pdftracingfonts", assign_int_cmd, + int_base + pdf_tracing_fonts_code, int_base); + primitive_pdftex("pdfgentounicode", assign_int_cmd, + int_base + pdf_gen_tounicode_code, int_base); + primitive_pdftex("pdfdraftmode", assign_int_cmd, + int_base + pdf_draftmode_code, int_base); + primitive_pdftex("pdfinclusioncopyfonts", assign_int_cmd, + int_base + pdf_inclusion_copy_font_code, int_base); + primitive_pdftex("pdfreplacefont", assign_int_cmd, + int_base + pdf_replace_font_code, int_base); + primitive_tex("parindent", assign_dimen_cmd, dimen_base + par_indent_code, + dimen_base); + primitive_tex("mathsurround", assign_dimen_cmd, + dimen_base + math_surround_code, dimen_base); + primitive_tex("lineskiplimit", assign_dimen_cmd, + dimen_base + line_skip_limit_code, dimen_base); + primitive_tex("hsize", assign_dimen_cmd, dimen_base + hsize_code, + dimen_base); + primitive_tex("vsize", assign_dimen_cmd, dimen_base + vsize_code, + dimen_base); + primitive_tex("maxdepth", assign_dimen_cmd, dimen_base + max_depth_code, + dimen_base); + primitive_tex("splitmaxdepth", assign_dimen_cmd, + dimen_base + split_max_depth_code, dimen_base); + primitive_tex("boxmaxdepth", assign_dimen_cmd, + dimen_base + box_max_depth_code, dimen_base); + primitive_tex("hfuzz", assign_dimen_cmd, dimen_base + hfuzz_code, + dimen_base); + primitive_tex("vfuzz", assign_dimen_cmd, dimen_base + vfuzz_code, + dimen_base); + primitive_tex("delimitershortfall", assign_dimen_cmd, + dimen_base + delimiter_shortfall_code, dimen_base); + primitive_tex("nulldelimiterspace", assign_dimen_cmd, + dimen_base + null_delimiter_space_code, dimen_base); + primitive_tex("scriptspace", assign_dimen_cmd, + dimen_base + script_space_code, dimen_base); + primitive_tex("predisplaysize", assign_dimen_cmd, + dimen_base + pre_display_size_code, dimen_base); + primitive_tex("displaywidth", assign_dimen_cmd, + dimen_base + display_width_code, dimen_base); + primitive_tex("displayindent", assign_dimen_cmd, + dimen_base + display_indent_code, dimen_base); + primitive_tex("overfullrule", assign_dimen_cmd, + dimen_base + overfull_rule_code, dimen_base); + primitive_tex("hangindent", assign_dimen_cmd, dimen_base + hang_indent_code, + dimen_base); + primitive_tex("hoffset", assign_dimen_cmd, dimen_base + h_offset_code, + dimen_base); + primitive_tex("voffset", assign_dimen_cmd, dimen_base + v_offset_code, + dimen_base); + primitive_tex("emergencystretch", assign_dimen_cmd, + dimen_base + emergency_stretch_code, dimen_base); + primitive_pdftex("pdfhorigin", assign_dimen_cmd, + dimen_base + pdf_h_origin_code, dimen_base); + primitive_pdftex("pdfvorigin", assign_dimen_cmd, + dimen_base + pdf_v_origin_code, dimen_base); + primitive_omega("pagewidth", assign_dimen_cmd, dimen_base + page_width_code, + dimen_base); + primitive_pdftex("pdfpagewidth", assign_dimen_cmd, + dimen_base + page_width_code, dimen_base); + primitive_omega("pageheight", assign_dimen_cmd, + dimen_base + page_height_code, dimen_base); + primitive_pdftex("pdfpageheight", assign_dimen_cmd, + dimen_base + page_height_code, dimen_base); + primitive_pdftex("pdflinkmargin", assign_dimen_cmd, + dimen_base + pdf_link_margin_code, dimen_base); + primitive_pdftex("pdfdestmargin", assign_dimen_cmd, + dimen_base + pdf_dest_margin_code, dimen_base); + primitive_pdftex("pdfthreadmargin", assign_dimen_cmd, + dimen_base + pdf_thread_margin_code, dimen_base); + primitive_pdftex("pdffirstlineheight", assign_dimen_cmd, + dimen_base + pdf_first_line_height_code, dimen_base); + primitive_pdftex("pdflastlinedepth", assign_dimen_cmd, + dimen_base + pdf_last_line_depth_code, dimen_base); + primitive_pdftex("pdfeachlineheight", assign_dimen_cmd, + dimen_base + pdf_each_line_height_code, dimen_base); + primitive_pdftex("pdfeachlinedepth", assign_dimen_cmd, + dimen_base + pdf_each_line_depth_code, dimen_base); + primitive_pdftex("pdfignoreddimen", assign_dimen_cmd, + dimen_base + pdf_ignored_dimen_code, dimen_base); + primitive_pdftex("pdfpxdimen", assign_dimen_cmd, + dimen_base + pdf_px_dimen_code, dimen_base); + + /* Many of \TeX's primitives need no |equiv|, since they are identifiable + by their |eq_type| alone. These primitives are loaded into the hash table + as follows: */ + + primitive_tex(" ", ex_space_cmd, 0, 0); + primitive_tex("/", ital_corr_cmd, 0, 0); + primitive_tex("accent", accent_cmd, 0, 0); + primitive_tex("advance", advance_cmd, 0, 0); + primitive_tex("afterassignment", after_assignment_cmd, 0, 0); + primitive_tex("aftergroup", after_group_cmd, 0, 0); + primitive_tex("begingroup", begin_group_cmd, 0, 0); + primitive_tex("char", char_num_cmd, 0, 0); + primitive_tex("csname", cs_name_cmd, 0, 0); + primitive_tex("delimiter", delim_num_cmd, 0, 0); + primitive_omega("odelimiter", delim_num_cmd, 1, 0); + primitive_luatex("Udelimiter", delim_num_cmd, 2, 0); + primitive_tex("divide", divide_cmd, 0, 0); + primitive_tex("endcsname", end_cs_name_cmd, 0, 0); + primitive_tex("endgroup", end_group_cmd, 0, 0); + cs_text(frozen_end_group) = maketexstring("endgroup"); + eqtb[frozen_end_group] = eqtb[cur_val]; + primitive_tex("expandafter", expand_after_cmd, 0, 0); + primitive_tex("font", def_font_cmd, 0, 0); + primitive_pdftex("letterspacefont", letterspace_font_cmd, 0, 0); + primitive_pdftex("pdfcopyfont", pdf_copy_font_cmd, 0, 0); + primitive_tex("fontdimen", assign_font_dimen_cmd, 0, 0); + primitive_tex("halign", halign_cmd, 0, 0); + primitive_tex("hrule", hrule_cmd, 0, 0); + primitive_tex("ignorespaces", ignore_spaces_cmd, 0, 0); + primitive_tex("insert", insert_cmd, 0, 0); + primitive_omega("leftghost", char_ghost_cmd, 0, 0); + primitive_tex("mark", mark_cmd, 0, 0); + primitive_tex("mathaccent", math_accent_cmd, 0, 0); + primitive_omega("omathaccent", math_accent_cmd, 1, 0); + primitive_luatex("Umathaccent", math_accent_cmd, 2, 0); + primitive_luatex("Umathbotaccent", math_accent_cmd, 3, 0); + primitive_luatex("Umathaccents", math_accent_cmd, 4, 0); + primitive_tex("mathchar", math_char_num_cmd, 0, 0); + primitive_omega("omathchar", math_char_num_cmd, 1, 0); + primitive_luatex("Umathchar", math_char_num_cmd, 2, 0); + primitive_luatex("Umathcharnum", math_char_num_cmd, 3, 0); + primitive_tex("mathchoice", math_choice_cmd, 0, 0); + primitive_luatex("Ustack", math_choice_cmd, 1, 0); + primitive_tex("multiply", multiply_cmd, 0, 0); + primitive_tex("noalign", no_align_cmd, 0, 0); + primitive_tex("noboundary", no_boundary_cmd, 0, 0); + primitive_tex("noexpand", no_expand_cmd, 0, 0); + primitive_luatex("primitive", no_expand_cmd, 1, 0); + primitive_pdftex("pdfprimitive", no_expand_cmd, 1, 0); + primitive_tex("nonscript", non_script_cmd, 0, 0); + primitive_tex("omit", omit_cmd, 0, 0); + primitive_tex("parshape", set_tex_shape_cmd, par_shape_loc, par_shape_loc); + primitive_tex("penalty", break_penalty_cmd, 0, 0); + primitive_tex("prevgraf", set_prev_graf_cmd, 0, 0); + primitive_tex("radical", radical_cmd, 0, 0); + primitive_omega("oradical", radical_cmd, 1, 0); + primitive_luatex("Uradical", radical_cmd, 2, 0); + primitive_luatex("Uroot", radical_cmd, 3, 0); + primitive_luatex("Uunderdelimiter", radical_cmd, 4, 0); + primitive_luatex("Uoverdelimiter", radical_cmd, 5, 0); + primitive_luatex("Udelimiterunder", radical_cmd, 6, 0); + primitive_luatex("Udelimiterover", radical_cmd, 7, 0); + primitive_tex("read", read_to_cs_cmd, 0, 0); + primitive_tex("relax", relax_cmd, too_big_char, too_big_char); + cs_text(frozen_relax) = maketexstring("relax"); + eqtb[frozen_relax] = eqtb[cur_val]; + primitive_omega("rightghost", char_ghost_cmd, 1, 0); + primitive_tex("setbox", set_box_cmd, 0, 0); + primitive_tex("the", the_cmd, 0, 0); + primitive_tex("toks", toks_register_cmd, 0, 0); + primitive_tex("vadjust", vadjust_cmd, 0, 0); + primitive_tex("valign", valign_cmd, 0, 0); + primitive_tex("vcenter", vcenter_cmd, 0, 0); + primitive_tex("vrule", vrule_cmd, 0, 0); + primitive_tex("par", par_end_cmd, too_big_char, too_big_char); /* cf.\ |scan_file_name| */ + par_loc = cur_val; + par_token = cs_token_flag + par_loc; + @<Create a bunch of primitives@>; + @<Create the math param primitives@>; + @<Create another bunch of primitives@>; +} + + +@ These are in a separate module due to a CWEAVE limitation. + +@<Create a bunch of primitives@>= + /* The processing of \.{\\input} involves the |start_input| subroutine, + which will be declared later; the processing of \.{\\endinput} is trivial. */ + primitive_tex("input", input_cmd, 0, 0); + primitive_tex("endinput", input_cmd, 1, 0); + primitive_tex("topmark", top_bot_mark_cmd, top_mark_code, 0); + primitive_tex("firstmark", top_bot_mark_cmd, first_mark_code, 0); + primitive_tex("botmark", top_bot_mark_cmd, bot_mark_code, 0); + primitive_tex("splitfirstmark", top_bot_mark_cmd, split_first_mark_code, 0); + primitive_tex("splitbotmark", top_bot_mark_cmd, split_bot_mark_code, 0); + primitive_luatex("clearmarks", mark_cmd, clear_marks_code, 0); + primitive_etex("marks", mark_cmd, marks_code, 0); + primitive_etex("topmarks", top_bot_mark_cmd, top_mark_code + marks_code, 0); + primitive_etex("firstmarks", top_bot_mark_cmd, first_mark_code + marks_code, + 0); + primitive_etex("botmarks", top_bot_mark_cmd, bot_mark_code + marks_code, 0); + primitive_etex("splitfirstmarks", top_bot_mark_cmd, + split_first_mark_code + marks_code, 0); + primitive_etex("splitbotmarks", top_bot_mark_cmd, + split_bot_mark_code + marks_code, 0); + + /* The hash table is initialized with `\.{\\count}', `\.{\\attribute}', + `\.{\\dimen}', `\.{\\skip}', and `\.{\\muskip}' all having |register| + as their command code; they are distinguished by the |chr_code|, which + is either |int_val|, |attr_val|, |dimen_val|, |glue_val|, or |mu_val|. + */ + primitive_tex("count", register_cmd, int_val_level, 0); + primitive_luatex("attribute", register_cmd, attr_val_level, 0); + primitive_tex("dimen", register_cmd, dimen_val_level, 0); + primitive_tex("skip", register_cmd, glue_val_level, 0); + primitive_tex("muskip", register_cmd, mu_val_level, 0); + + primitive_tex("spacefactor", set_aux_cmd, hmode, 0); + primitive_tex("prevdepth", set_aux_cmd, vmode, 0); + primitive_tex("deadcycles", set_page_int_cmd, 0, 0); + primitive_tex("insertpenalties", set_page_int_cmd, 1, 0); + primitive_tex("wd", set_box_dimen_cmd, width_offset, 0); + primitive_tex("ht", set_box_dimen_cmd, height_offset, 0); + primitive_tex("dp", set_box_dimen_cmd, depth_offset, 0); + primitive_tex("lastpenalty", last_item_cmd, lastpenalty_code, 0); + primitive_tex("lastkern", last_item_cmd, lastkern_code, 0); + primitive_tex("lastskip", last_item_cmd, lastskip_code, 0); + primitive_tex("inputlineno", last_item_cmd, input_line_no_code, 0); + primitive_tex("badness", last_item_cmd, badness_code, 0); + primitive_pdftex("pdftexversion", last_item_cmd, pdftex_version_code, 0); + primitive_luatex("luatexversion", last_item_cmd, luatex_version_code, 0); + primitive_pdftex("pdflastobj", last_item_cmd, pdf_last_obj_code, 0); + primitive_pdftex("pdflastxform", last_item_cmd, pdf_last_xform_code, 0); + primitive_pdftex("pdflastximage", last_item_cmd, pdf_last_ximage_code, 0); + primitive_pdftex("pdflastximagepages", last_item_cmd, + pdf_last_ximage_pages_code, 0); + primitive_pdftex("pdflastannot", last_item_cmd, pdf_last_annot_code, 0); + primitive_pdftex("pdflastxpos", last_item_cmd, pdf_last_x_pos_code, 0); + primitive_pdftex("pdflastypos", last_item_cmd, pdf_last_y_pos_code, 0); + primitive_pdftex("pdfretval", last_item_cmd, pdf_retval_code, 0); + primitive_pdftex("pdflastximagecolordepth", last_item_cmd, + pdf_last_ximage_colordepth_code, 0); + primitive_pdftex("pdfrandomseed", last_item_cmd, random_seed_code, 0); + primitive_pdftex("pdflastlink", last_item_cmd, pdf_last_link_code, 0); + + primitive_tex("number", convert_cmd, number_code, 0); + primitive_tex("romannumeral", convert_cmd, roman_numeral_code, 0); + primitive_tex("string", convert_cmd, string_code, 0); + primitive_tex("meaning", convert_cmd, meaning_code, 0); + primitive_etex("eTeXVersion", convert_cmd, etex_code, 0); + primitive_omega("OmegaVersion", convert_cmd, omega_code, 0); + primitive_aleph("AlephVersion", convert_cmd, aleph_code, 0); + primitive_tex("fontname", convert_cmd, font_name_code, 0); + primitive_pdftex("pdftexrevision", convert_cmd, pdftex_revision_code, 0); + primitive_luatex("luatexrevision", convert_cmd, luatex_revision_code, 0); + primitive_luatex("luatexdatestamp", convert_cmd, luatex_date_code, 0); + primitive_pdftex("pdftexbanner", convert_cmd, pdftex_banner_code, 0); + primitive_pdftex("pdffontname", convert_cmd, pdf_font_name_code, 0); + primitive_pdftex("pdffontobjnum", convert_cmd, pdf_font_objnum_code, 0); + primitive_pdftex("pdffontsize", convert_cmd, pdf_font_size_code, 0); + primitive_pdftex("pdfpageref", convert_cmd, pdf_page_ref_code, 0); + primitive_pdftex("leftmarginkern", convert_cmd, left_margin_kern_code, 0); + primitive_pdftex("rightmarginkern", convert_cmd, right_margin_kern_code, 0); + primitive_pdftex("pdfxformname", convert_cmd, pdf_xform_name_code, 0); + primitive_pdftex("pdfcreationdate", convert_cmd, pdf_creation_date_code, 0); + primitive_pdftex("pdfuniformdeviate", convert_cmd, uniform_deviate_code, 0); + primitive_pdftex("pdfcolorstackinit", convert_cmd, pdf_colorstack_init_code, + 0); + primitive_pdftex("pdfnormaldeviate", convert_cmd, normal_deviate_code, 0); + primitive_core("directlua", convert_cmd, lua_code, 0); + primitive_luatex("luaescapestring", convert_cmd, lua_escape_string_code, 0); + primitive_luatex("mathstyle", convert_cmd, math_style_code, 0); + primitive_pdftex("expanded", convert_cmd, expanded_code, 0); + primitive_tex("jobname", convert_cmd, job_name_code, 0); + primitive_luatex("formatname", convert_cmd, format_name_code, 0); + primitive_pdftex("pdfinsertht", convert_cmd, pdf_insert_ht_code, 0); + primitive_pdftex("pdfximagebbox", convert_cmd, pdf_ximage_bbox_code, 0); + + primitive_tex("if", if_test_cmd, if_char_code, 0); + primitive_tex("ifcat", if_test_cmd, if_cat_code, 0); + primitive_tex("ifnum", if_test_cmd, if_int_code, 0); + primitive_tex("ifdim", if_test_cmd, if_dim_code, 0); + primitive_tex("ifodd", if_test_cmd, if_odd_code, 0); + primitive_tex("ifvmode", if_test_cmd, if_vmode_code, 0); + primitive_tex("ifhmode", if_test_cmd, if_hmode_code, 0); + primitive_tex("ifmmode", if_test_cmd, if_mmode_code, 0); + primitive_tex("ifinner", if_test_cmd, if_inner_code, 0); + primitive_tex("ifvoid", if_test_cmd, if_void_code, 0); + + primitive_tex("ifhbox", if_test_cmd, if_hbox_code, 0); + primitive_tex("ifvbox", if_test_cmd, if_vbox_code, 0); + primitive_tex("ifx", if_test_cmd, ifx_code, 0); + primitive_tex("ifeof", if_test_cmd, if_eof_code, 0); + primitive_tex("iftrue", if_test_cmd, if_true_code, 0); + primitive_tex("iffalse", if_test_cmd, if_false_code, 0); + primitive_tex("ifcase", if_test_cmd, if_case_code, 0); + primitive_luatex("ifprimitive", if_test_cmd, if_primitive_code, 0); + primitive_pdftex("ifpdfprimitive", if_test_cmd, if_primitive_code, 0); + + primitive_tex("fi", fi_or_else_cmd, fi_code, 0); + cs_text(frozen_fi) = maketexstring("fi"); + eqtb[frozen_fi] = eqtb[cur_val]; + primitive_tex("or", fi_or_else_cmd, or_code, 0); + primitive_tex("else", fi_or_else_cmd, else_code, 0); + + /* \TeX\ always knows at least one font, namely the null font. It has no + characters, and its seven parameters are all equal to zero. */ + primitive_tex("nullfont", set_font_cmd, null_font, 0); + cs_text(frozen_null_font) = maketexstring("nullfont"); + eqtb[frozen_null_font] = eqtb[cur_val]; + /* $\Omega$ always knows at least one ocp, namely the null ocp. + It does nothing. */ + primitive_omega("nullocp", set_ocp_cmd, null_ocp, 0); + cs_text(frozen_null_ocp) = maketexstring("nullocp"); + eqtb[frozen_null_ocp] = eqtb[cur_val]; + geq_define(ocp_active_number_base, data_cmd, 0); + geq_define(ocp_active_min_ptr_base, data_cmd, 0); + geq_define(ocp_active_max_ptr_base, data_cmd, 0); + /* $\Omega$ always knows at least one ocp list, namely the null ocp list. */ + primitive_omega("nullocplist", set_ocp_list_cmd, null_ocp_list, 0); + cs_text(frozen_null_ocp_list) = maketexstring("nullocplist"); + eqtb[frozen_null_ocp_list] = eqtb[cur_val]; + + primitive_tex("span", tab_mark_cmd, span_code, tab_mark_cmd_code); + primitive_luatex("aligntab", tab_mark_cmd, tab_mark_cmd_code, + tab_mark_cmd_code); + primitive_luatex("alignmark", mac_param_cmd, tab_mark_cmd_code, + tab_mark_cmd_code); + primitive_tex("cr", car_ret_cmd, cr_code, cr_code); + cs_text(frozen_cr) = maketexstring("cr"); + eqtb[frozen_cr] = eqtb[cur_val]; + primitive_tex("crcr", car_ret_cmd, cr_cr_code, cr_code); + cs_text(frozen_end_template) = maketexstring("endtemplate"); + cs_text(frozen_endv) = maketexstring("endtemplate"); + set_eq_type(frozen_endv, endv_cmd); + set_equiv(frozen_endv, null_list); + set_eq_level(frozen_endv, level_one); + eqtb[frozen_end_template] = eqtb[frozen_endv]; + set_eq_type(frozen_end_template, end_template_cmd); + + primitive_tex("pagegoal", set_page_dimen_cmd, 0, 0); + primitive_tex("pagetotal", set_page_dimen_cmd, 1, 0); + primitive_tex("pagestretch", set_page_dimen_cmd, 2, 0); + primitive_tex("pagefilstretch", set_page_dimen_cmd, 3, 0); + primitive_tex("pagefillstretch", set_page_dimen_cmd, 4, 0); + primitive_tex("pagefilllstretch", set_page_dimen_cmd, 5, 0); + primitive_tex("pageshrink", set_page_dimen_cmd, 6, 0); + primitive_tex("pagedepth", set_page_dimen_cmd, 7, 0); + + /* Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the + endgame, since both of them have `|stop|' as their command code. */ + + primitive_tex("end", stop_cmd, 0, 0); + primitive_tex("dump", stop_cmd, 1, 0); + + primitive_tex("hskip", hskip_cmd, skip_code, 0); + primitive_tex("hfil", hskip_cmd, fil_code, 0); + primitive_tex("hfill", hskip_cmd, fill_code, 0); + primitive_tex("hss", hskip_cmd, ss_code, 0); + primitive_tex("hfilneg", hskip_cmd, fil_neg_code, 0); + primitive_tex("vskip", vskip_cmd, skip_code, 0); + primitive_tex("vfil", vskip_cmd, fil_code, 0); + primitive_tex("vfill", vskip_cmd, fill_code, 0); + primitive_tex("vss", vskip_cmd, ss_code, 0); + primitive_tex("vfilneg", vskip_cmd, fil_neg_code, 0); + primitive_tex("mskip", mskip_cmd, mskip_code, 0); + primitive_tex("kern", kern_cmd, explicit, 0); + primitive_tex("mkern", mkern_cmd, mu_glue, 0); + primitive_tex("moveleft", hmove_cmd, 1, 0); + primitive_tex("moveright", hmove_cmd, 0, 0); + primitive_tex("raise", vmove_cmd, 1, 0); + primitive_tex("lower", vmove_cmd, 0, 0); + primitive_tex("box", make_box_cmd, box_code, 0); + primitive_tex("copy", make_box_cmd, copy_code, 0); + primitive_tex("lastbox", make_box_cmd, last_box_code, 0); + primitive_tex("vsplit", make_box_cmd, vsplit_code, 0); + primitive_tex("vtop", make_box_cmd, vtop_code, 0); + primitive_tex("vbox", make_box_cmd, vtop_code + vmode, 0); + primitive_tex("hbox", make_box_cmd, vtop_code + hmode, 0); + primitive_tex("shipout", leader_ship_cmd, a_leaders - 1, 0); /* |ship_out_flag=leader_flag-1| */ + primitive_tex("leaders", leader_ship_cmd, a_leaders, 0); + primitive_tex("cleaders", leader_ship_cmd, c_leaders, 0); + primitive_tex("xleaders", leader_ship_cmd, x_leaders, 0); + primitive_luatex("gleaders", leader_ship_cmd, g_leaders, 0); + primitive_aleph("boxdir", assign_box_dir_cmd, 0, 0); + primitive_tex("indent", start_par_cmd, 1, 0); + primitive_tex("noindent", start_par_cmd, 0, 0); + primitive_pdftex("quitvmode", start_par_cmd, 2, 0); + primitive_tex("unpenalty", remove_item_cmd, penalty_node, 0); + primitive_tex("unkern", remove_item_cmd, kern_node, 0); + primitive_tex("unskip", remove_item_cmd, glue_node, 0); + primitive_tex("unhbox", un_hbox_cmd, box_code, 0); + primitive_tex("unhcopy", un_hbox_cmd, copy_code, 0); + primitive_tex("unvbox", un_vbox_cmd, box_code, 0); + primitive_tex("unvcopy", un_vbox_cmd, copy_code, 0); + primitive_tex("-", discretionary_cmd, explicit_disc, 0); + primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0); + primitive_omega("localleftbox", assign_local_box_cmd, 0, 0); + primitive_omega("localrightbox", assign_local_box_cmd, 1, 0); + + primitive_luatex("Ustartmath", math_shift_cs_cmd, text_style, 0); + primitive_luatex("Ustopmath", math_shift_cs_cmd, cramped_text_style, 0); + primitive_luatex("Ustartdisplaymath", math_shift_cs_cmd, display_style, 0); + primitive_luatex("Ustopdisplaymath", math_shift_cs_cmd, + cramped_display_style, 0); + primitive_tex("eqno", eq_no_cmd, 0, 0); + primitive_tex("leqno", eq_no_cmd, 1, 0); + primitive_tex("mathord", math_comp_cmd, ord_noad_type, 0); + primitive_tex("mathop", math_comp_cmd, op_noad_type_normal, 0); + primitive_tex("mathbin", math_comp_cmd, bin_noad_type, 0); + primitive_tex("mathrel", math_comp_cmd, rel_noad_type, 0); + primitive_tex("mathopen", math_comp_cmd, open_noad_type, 0); + primitive_tex("mathclose", math_comp_cmd, close_noad_type, 0); + primitive_tex("mathpunct", math_comp_cmd, punct_noad_type, 0); + primitive_tex("mathinner", math_comp_cmd, inner_noad_type, 0); + primitive_tex("underline", math_comp_cmd, under_noad_type, 0); + primitive_tex("overline", math_comp_cmd, over_noad_type, 0); + primitive_tex("displaylimits", limit_switch_cmd, op_noad_type_normal, 0); + primitive_tex("limits", limit_switch_cmd, op_noad_type_limits, 0); + primitive_tex("nolimits", limit_switch_cmd, op_noad_type_no_limits, 0); + primitive_tex("displaystyle", math_style_cmd, display_style, 0); + primitive_tex("textstyle", math_style_cmd, text_style, 0); + primitive_tex("scriptstyle", math_style_cmd, script_style, 0); + primitive_tex("scriptscriptstyle", math_style_cmd, script_script_style, 0); + primitive_luatex("crampeddisplaystyle", math_style_cmd, + cramped_display_style, 0); + primitive_luatex("crampedtextstyle", math_style_cmd, cramped_text_style, 0); + primitive_luatex("crampedscriptstyle", math_style_cmd, cramped_script_style, + 0); + primitive_luatex("crampedscriptscriptstyle", math_style_cmd, + cramped_script_script_style, 0); + primitive_luatex("Usuperscript", super_sub_script_cmd, sup_mark_cmd, + sup_mark_cmd); + primitive_luatex("Usubscript", super_sub_script_cmd, sub_mark_cmd, + sup_mark_cmd); + primitive_tex("above", above_cmd, above_code, 0); + primitive_tex("over", above_cmd, over_code, 0); + primitive_tex("atop", above_cmd, atop_code, 0); + primitive_tex("abovewithdelims", above_cmd, delimited_code + above_code, 0); + primitive_tex("overwithdelims", above_cmd, delimited_code + over_code, 0); + primitive_tex("atopwithdelims", above_cmd, delimited_code + atop_code, 0); + primitive_tex("left", left_right_cmd, left_noad_side, 0); + primitive_tex("right", left_right_cmd, right_noad_side, 0); + primitive_tex("middle", left_right_cmd, middle_noad_side, 0); + cs_text(frozen_right) = maketexstring("right"); + eqtb[frozen_right] = eqtb[cur_val]; + + primitive_tex("long", prefix_cmd, 1, 0); + primitive_tex("outer", prefix_cmd, 2, 0); + primitive_tex("global", prefix_cmd, 4, 0); + primitive_tex("def", def_cmd, 0, 0); + primitive_tex("gdef", def_cmd, 1, 0); + primitive_tex("edef", def_cmd, 2, 0); + primitive_tex("xdef", def_cmd, 3, 0); + primitive_tex("let", let_cmd, normal, 0); + primitive_tex("futurelet", let_cmd, normal + 1, 0); + primitive_tex("chardef", shorthand_def_cmd, char_def_code, 0); + primitive_tex("mathchardef", shorthand_def_cmd, math_char_def_code, 0); + primitive_omega("omathchardef", shorthand_def_cmd, omath_char_def_code, 0); + primitive_luatex("Umathchardef", shorthand_def_cmd, xmath_char_def_code, 0); + primitive_tex("countdef", shorthand_def_cmd, count_def_code, 0); + primitive_luatex("attributedef", shorthand_def_cmd, attribute_def_code, 0); + primitive_tex("dimendef", shorthand_def_cmd, dimen_def_code, 0); + primitive_tex("skipdef", shorthand_def_cmd, skip_def_code, 0); + primitive_tex("muskipdef", shorthand_def_cmd, mu_skip_def_code, 0); + primitive_tex("toksdef", shorthand_def_cmd, toks_def_code, 0); + primitive_tex("catcode", def_char_code_cmd, cat_code_base, cat_code_base); + primitive_tex("mathcode", def_char_code_cmd, math_code_base, cat_code_base); + primitive_tex("lccode", def_char_code_cmd, lc_code_base, cat_code_base); + primitive_tex("uccode", def_char_code_cmd, uc_code_base, cat_code_base); + primitive_tex("sfcode", def_char_code_cmd, sf_code_base, cat_code_base); + primitive_tex("delcode", def_del_code_cmd, del_code_base, del_code_base); + primitive_tex("textfont", def_family_cmd, text_size, 0); + primitive_tex("scriptfont", def_family_cmd, script_size, 0); + primitive_tex("scriptscriptfont", def_family_cmd, script_script_size, 0); + primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0); + primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0); + + +@ These are in a separate module due to a CWEAVE limitation. + +@<Create the math param primitives@>= + primitive_luatex("Umathoperatorsize", set_math_param_cmd, + math_param_operator_size, 0); + primitive_luatex("Umathoverbarkern", set_math_param_cmd, + math_param_overbar_kern, 0); + primitive_luatex("Umathoverbarrule", set_math_param_cmd, + math_param_overbar_rule, 0); + primitive_luatex("Umathoverbarvgap", set_math_param_cmd, + math_param_overbar_vgap, 0); + primitive_luatex("Umathunderbarkern", set_math_param_cmd, + math_param_underbar_kern, 0); + primitive_luatex("Umathunderbarrule", set_math_param_cmd, + math_param_underbar_rule, 0); + primitive_luatex("Umathunderbarvgap", set_math_param_cmd, + math_param_underbar_vgap, 0); + primitive_luatex("Umathradicalkern", set_math_param_cmd, + math_param_radical_kern, 0); + primitive_luatex("Umathradicalrule", set_math_param_cmd, + math_param_radical_rule, 0); + primitive_luatex("Umathradicalvgap", set_math_param_cmd, + math_param_radical_vgap, 0); + primitive_luatex("Umathradicaldegreebefore", set_math_param_cmd, + math_param_radical_degree_before, 0); + primitive_luatex("Umathradicaldegreeafter", set_math_param_cmd, + math_param_radical_degree_after, 0); + primitive_luatex("Umathradicaldegreeraise", set_math_param_cmd, + math_param_radical_degree_raise, 0); + primitive_luatex("Umathstackvgap", set_math_param_cmd, + math_param_stack_vgap, 0); + primitive_luatex("Umathstacknumup", set_math_param_cmd, + math_param_stack_num_up, 0); + primitive_luatex("Umathstackdenomdown", set_math_param_cmd, + math_param_stack_denom_down, 0); + primitive_luatex("Umathfractionrule", set_math_param_cmd, + math_param_fraction_rule, 0); + primitive_luatex("Umathfractionnumvgap", set_math_param_cmd, + math_param_fraction_num_vgap, 0); + primitive_luatex("Umathfractionnumup", set_math_param_cmd, + math_param_fraction_num_up, 0); + primitive_luatex("Umathfractiondenomvgap", set_math_param_cmd, + math_param_fraction_denom_vgap, 0); + primitive_luatex("Umathfractiondenomdown", set_math_param_cmd, + math_param_fraction_denom_down, 0); + primitive_luatex("Umathfractiondelsize", set_math_param_cmd, + math_param_fraction_del_size, 0); + primitive_luatex("Umathlimitabovevgap", set_math_param_cmd, + math_param_limit_above_vgap, 0); + primitive_luatex("Umathlimitabovebgap", set_math_param_cmd, + math_param_limit_above_bgap, 0); + primitive_luatex("Umathlimitabovekern", set_math_param_cmd, + math_param_limit_above_kern, 0); + primitive_luatex("Umathlimitbelowvgap", set_math_param_cmd, + math_param_limit_below_vgap, 0); + primitive_luatex("Umathlimitbelowbgap", set_math_param_cmd, + math_param_limit_below_bgap, 0); + primitive_luatex("Umathlimitbelowkern", set_math_param_cmd, + math_param_limit_below_kern, 0); + primitive_luatex("Umathunderdelimitervgap", set_math_param_cmd, + math_param_under_delimiter_vgap, 0); + primitive_luatex("Umathunderdelimiterbgap", set_math_param_cmd, + math_param_under_delimiter_bgap, 0); + primitive_luatex("Umathoverdelimitervgap", set_math_param_cmd, + math_param_over_delimiter_vgap, 0); + primitive_luatex("Umathoverdelimiterbgap", set_math_param_cmd, + math_param_over_delimiter_bgap, 0); + primitive_luatex("Umathsubshiftdrop", set_math_param_cmd, + math_param_sub_shift_drop, 0); + primitive_luatex("Umathsupshiftdrop", set_math_param_cmd, + math_param_sup_shift_drop, 0); + primitive_luatex("Umathsubshiftdown", set_math_param_cmd, + math_param_sub_shift_down, 0); + primitive_luatex("Umathsubsupshiftdown", set_math_param_cmd, + math_param_sub_sup_shift_down, 0); + primitive_luatex("Umathsubtopmax", set_math_param_cmd, + math_param_sub_top_max, 0); + primitive_luatex("Umathsupshiftup", set_math_param_cmd, + math_param_sup_shift_up, 0); + primitive_luatex("Umathsupbottommin", set_math_param_cmd, + math_param_sup_bottom_min, 0); + primitive_luatex("Umathsupsubbottommax", set_math_param_cmd, + math_param_sup_sub_bottom_max, 0); + primitive_luatex("Umathsubsupvgap", set_math_param_cmd, + math_param_subsup_vgap, 0); + primitive_luatex("Umathspaceafterscript", set_math_param_cmd, + math_param_space_after_script, 0); + primitive_luatex("Umathconnectoroverlapmin", set_math_param_cmd, + math_param_connector_overlap_min, 0); + primitive_luatex("Umathordordspacing", set_math_param_cmd, + math_param_ord_ord_spacing, 0); + primitive_luatex("Umathordopspacing", set_math_param_cmd, + math_param_ord_op_spacing, 0); + primitive_luatex("Umathordbinspacing", set_math_param_cmd, + math_param_ord_bin_spacing, 0); + primitive_luatex("Umathordrelspacing", set_math_param_cmd, + math_param_ord_rel_spacing, 0); + primitive_luatex("Umathordopenspacing", set_math_param_cmd, + math_param_ord_open_spacing, 0); + primitive_luatex("Umathordclosespacing", set_math_param_cmd, + math_param_ord_close_spacing, 0); + primitive_luatex("Umathordpunctspacing", set_math_param_cmd, + math_param_ord_punct_spacing, 0); + primitive_luatex("Umathordinnerspacing", set_math_param_cmd, + math_param_ord_inner_spacing, 0); + primitive_luatex("Umathopordspacing", set_math_param_cmd, + math_param_op_ord_spacing, 0); + primitive_luatex("Umathopopspacing", set_math_param_cmd, + math_param_op_op_spacing, 0); + primitive_luatex("Umathopbinspacing", set_math_param_cmd, + math_param_op_bin_spacing, 0); + primitive_luatex("Umathoprelspacing", set_math_param_cmd, + math_param_op_rel_spacing, 0); + primitive_luatex("Umathopopenspacing", set_math_param_cmd, + math_param_op_open_spacing, 0); + primitive_luatex("Umathopclosespacing", set_math_param_cmd, + math_param_op_close_spacing, 0); + primitive_luatex("Umathoppunctspacing", set_math_param_cmd, + math_param_op_punct_spacing, 0); + primitive_luatex("Umathopinnerspacing", set_math_param_cmd, + math_param_op_inner_spacing, 0); + primitive_luatex("Umathbinordspacing", set_math_param_cmd, + math_param_bin_ord_spacing, 0); + primitive_luatex("Umathbinopspacing", set_math_param_cmd, + math_param_bin_op_spacing, 0); + primitive_luatex("Umathbinbinspacing", set_math_param_cmd, + math_param_bin_bin_spacing, 0); + primitive_luatex("Umathbinrelspacing", set_math_param_cmd, + math_param_bin_rel_spacing, 0); + primitive_luatex("Umathbinopenspacing", set_math_param_cmd, + math_param_bin_open_spacing, 0); + primitive_luatex("Umathbinclosespacing", set_math_param_cmd, + math_param_bin_close_spacing, 0); + primitive_luatex("Umathbinpunctspacing", set_math_param_cmd, + math_param_bin_punct_spacing, 0); + primitive_luatex("Umathbininnerspacing", set_math_param_cmd, + math_param_bin_inner_spacing, 0); + primitive_luatex("Umathrelordspacing", set_math_param_cmd, + math_param_rel_ord_spacing, 0); + primitive_luatex("Umathrelopspacing", set_math_param_cmd, + math_param_rel_op_spacing, 0); + primitive_luatex("Umathrelbinspacing", set_math_param_cmd, + math_param_rel_bin_spacing, 0); + primitive_luatex("Umathrelrelspacing", set_math_param_cmd, + math_param_rel_rel_spacing, 0); + primitive_luatex("Umathrelopenspacing", set_math_param_cmd, + math_param_rel_open_spacing, 0); + primitive_luatex("Umathrelclosespacing", set_math_param_cmd, + math_param_rel_close_spacing, 0); + primitive_luatex("Umathrelpunctspacing", set_math_param_cmd, + math_param_rel_punct_spacing, 0); + primitive_luatex("Umathrelinnerspacing", set_math_param_cmd, + math_param_rel_inner_spacing, 0); + primitive_luatex("Umathopenordspacing", set_math_param_cmd, + math_param_open_ord_spacing, 0); + primitive_luatex("Umathopenopspacing", set_math_param_cmd, + math_param_open_op_spacing, 0); + primitive_luatex("Umathopenbinspacing", set_math_param_cmd, + math_param_open_bin_spacing, 0); + primitive_luatex("Umathopenrelspacing", set_math_param_cmd, + math_param_open_rel_spacing, 0); + primitive_luatex("Umathopenopenspacing", set_math_param_cmd, + math_param_open_open_spacing, 0); + primitive_luatex("Umathopenclosespacing", set_math_param_cmd, + math_param_open_close_spacing, 0); + primitive_luatex("Umathopenpunctspacing", set_math_param_cmd, + math_param_open_punct_spacing, 0); + primitive_luatex("Umathopeninnerspacing", set_math_param_cmd, + math_param_open_inner_spacing, 0); + primitive_luatex("Umathcloseordspacing", set_math_param_cmd, + math_param_close_ord_spacing, 0); + primitive_luatex("Umathcloseopspacing", set_math_param_cmd, + math_param_close_op_spacing, 0); + primitive_luatex("Umathclosebinspacing", set_math_param_cmd, + math_param_close_bin_spacing, 0); + primitive_luatex("Umathcloserelspacing", set_math_param_cmd, + math_param_close_rel_spacing, 0); + primitive_luatex("Umathcloseopenspacing", set_math_param_cmd, + math_param_close_open_spacing, 0); + primitive_luatex("Umathcloseclosespacing", set_math_param_cmd, + math_param_close_close_spacing, 0); + primitive_luatex("Umathclosepunctspacing", set_math_param_cmd, + math_param_close_punct_spacing, 0); + primitive_luatex("Umathcloseinnerspacing", set_math_param_cmd, + math_param_close_inner_spacing, 0); + primitive_luatex("Umathpunctordspacing", set_math_param_cmd, + math_param_punct_ord_spacing, 0); + primitive_luatex("Umathpunctopspacing", set_math_param_cmd, + math_param_punct_op_spacing, 0); + primitive_luatex("Umathpunctbinspacing", set_math_param_cmd, + math_param_punct_bin_spacing, 0); + primitive_luatex("Umathpunctrelspacing", set_math_param_cmd, + math_param_punct_rel_spacing, 0); + primitive_luatex("Umathpunctopenspacing", set_math_param_cmd, + math_param_punct_open_spacing, 0); + primitive_luatex("Umathpunctclosespacing", set_math_param_cmd, + math_param_punct_close_spacing, 0); + primitive_luatex("Umathpunctpunctspacing", set_math_param_cmd, + math_param_punct_punct_spacing, 0); + primitive_luatex("Umathpunctinnerspacing", set_math_param_cmd, + math_param_punct_inner_spacing, 0); + primitive_luatex("Umathinnerordspacing", set_math_param_cmd, + math_param_inner_ord_spacing, 0); + primitive_luatex("Umathinneropspacing", set_math_param_cmd, + math_param_inner_op_spacing, 0); + primitive_luatex("Umathinnerbinspacing", set_math_param_cmd, + math_param_inner_bin_spacing, 0); + primitive_luatex("Umathinnerrelspacing", set_math_param_cmd, + math_param_inner_rel_spacing, 0); + primitive_luatex("Umathinneropenspacing", set_math_param_cmd, + math_param_inner_open_spacing, 0); + primitive_luatex("Umathinnerclosespacing", set_math_param_cmd, + math_param_inner_close_spacing, 0); + primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, + math_param_inner_punct_spacing, 0); + primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, + math_param_inner_inner_spacing, 0); + +@ These are in a separate module due to a CWEAVE limitation. + +@<Create another bunch of primitives@>= + primitive_omega("omathcode", extdef_math_code_cmd, math_code_base, + math_code_base); + primitive_omega("odelcode", extdef_del_code_cmd, del_code_base, + del_code_base); + primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base + 1, + math_code_base); + primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base + 1, + del_code_base); + primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 2, + math_code_base); + primitive_luatex("Udelcodenum", extdef_del_code_cmd, del_code_base + 2, + del_code_base); + primitive_tex("hyphenation", hyph_data_cmd, 0, 0); + primitive_tex("patterns", hyph_data_cmd, 1, 0); + primitive_luatex("prehyphenchar", hyph_data_cmd, 2, 0); + primitive_luatex("posthyphenchar", hyph_data_cmd, 3, 0); + primitive_luatex("preexhyphenchar", hyph_data_cmd, 4, 0); + primitive_luatex("postexhyphenchar", hyph_data_cmd, 5, 0); + primitive_tex("hyphenchar", assign_font_int_cmd, 0, 0); + primitive_tex("skewchar", assign_font_int_cmd, 1, 0); + primitive_pdftex("lpcode", assign_font_int_cmd, lp_code_base, 0); + primitive_pdftex("rpcode", assign_font_int_cmd, rp_code_base, 0); + primitive_pdftex("efcode", assign_font_int_cmd, ef_code_base, 0); + primitive_pdftex("tagcode", assign_font_int_cmd, tag_code, 0); + primitive_pdftex("pdfnoligatures", assign_font_int_cmd, no_lig_code, 0); + primitive_tex("batchmode", set_interaction_cmd, batch_mode, 0); + primitive_tex("nonstopmode", set_interaction_cmd, nonstop_mode, 0); + primitive_tex("scrollmode", set_interaction_cmd, scroll_mode, 0); + primitive_tex("errorstopmode", set_interaction_cmd, error_stop_mode, 0); + primitive_tex("openin", in_stream_cmd, 1, 0); + primitive_tex("closein", in_stream_cmd, 0, 0); + primitive_tex("message", message_cmd, 0, 0); + primitive_tex("errmessage", message_cmd, 1, 0); + primitive_tex("lowercase", case_shift_cmd, lc_code_base, lc_code_base); + primitive_tex("uppercase", case_shift_cmd, uc_code_base, lc_code_base); + primitive_tex("show", xray_cmd, show_code, 0); + primitive_tex("showbox", xray_cmd, show_box_code, 0); + primitive_tex("showthe", xray_cmd, show_the_code, 0); + primitive_tex("showlists", xray_cmd, show_lists, 0); + + primitive_tex("openout", extension_cmd, open_node, 0); + primitive_tex("write", extension_cmd, write_node, 0); + write_loc = cur_val; + primitive_tex("closeout", extension_cmd, close_node, 0); + primitive_tex("special", extension_cmd, special_node, 0); + cs_text(frozen_special) = maketexstring("special"); + eqtb[frozen_special] = eqtb[cur_val]; + primitive_tex("immediate", extension_cmd, immediate_code, 0); + primitive_omega("localinterlinepenalty", assign_int_cmd, + int_base + local_inter_line_penalty_code, int_base); + primitive_omega("localbrokenpenalty", assign_int_cmd, + int_base + local_broken_penalty_code, int_base); + primitive_omega("pagedir", assign_dir_cmd, int_base + page_direction_code, + dir_base); + primitive_omega("bodydir", assign_dir_cmd, int_base + body_direction_code, + dir_base); + primitive_omega("pardir", assign_dir_cmd, int_base + par_direction_code, + dir_base); + primitive_omega("textdir", assign_dir_cmd, int_base + text_direction_code, + dir_base); + primitive_omega("mathdir", assign_dir_cmd, int_base + math_direction_code, + dir_base); + primitive_luatex("pageleftoffset", assign_dimen_cmd, + dimen_base + page_left_offset_code, dimen_base); + primitive_luatex("pagetopoffset", assign_dimen_cmd, + dimen_base + page_top_offset_code, dimen_base); + primitive_aleph("pagerightoffset", assign_dimen_cmd, + dimen_base + page_right_offset_code, dimen_base); + primitive_aleph("pagebottomoffset", assign_dimen_cmd, + dimen_base + page_bottom_offset_code, dimen_base); + primitive_pdftex("pdfliteral", extension_cmd, pdf_literal_node, 0); + primitive_pdftex("pdfcolorstack", extension_cmd, pdf_colorstack_node, 0); + primitive_pdftex("pdfsetmatrix", extension_cmd, pdf_setmatrix_node, 0); + primitive_pdftex("pdfsave", extension_cmd, pdf_save_node, 0); + primitive_pdftex("pdfrestore", extension_cmd, pdf_restore_node, 0); + primitive_pdftex("pdfobj", extension_cmd, pdf_obj_code, 0); + primitive_pdftex("pdfrefobj", extension_cmd, pdf_refobj_node, 0); + primitive_pdftex("pdfxform", extension_cmd, pdf_xform_code, 0); + primitive_pdftex("pdfrefxform", extension_cmd, pdf_refxform_node, 0); + primitive_pdftex("pdfximage", extension_cmd, pdf_ximage_code, 0); + primitive_pdftex("pdfrefximage", extension_cmd, pdf_refximage_node, 0); + primitive_pdftex("pdfannot", extension_cmd, pdf_annot_node, 0); + primitive_pdftex("pdfstartlink", extension_cmd, pdf_start_link_node, 0); + primitive_pdftex("pdfendlink", extension_cmd, pdf_end_link_node, 0); + primitive_pdftex("pdfoutline", extension_cmd, pdf_outline_code, 0); + primitive_pdftex("pdfdest", extension_cmd, pdf_dest_node, 0); + primitive_pdftex("pdfthread", extension_cmd, pdf_thread_node, 0); + primitive_pdftex("pdfstartthread", extension_cmd, pdf_start_thread_node, 0); + primitive_pdftex("pdfendthread", extension_cmd, pdf_end_thread_node, 0); + primitive_pdftex("pdfsavepos", extension_cmd, pdf_save_pos_node, 0); + primitive_pdftex("pdfinfo", extension_cmd, pdf_info_code, 0); + primitive_pdftex("pdfcatalog", extension_cmd, pdf_catalog_code, 0); + primitive_pdftex("pdfnames", extension_cmd, pdf_names_code, 0); + primitive_pdftex("pdfincludechars", extension_cmd, pdf_include_chars_code, + 0); + primitive_pdftex("pdffontattr", extension_cmd, pdf_font_attr_code, 0); + primitive_pdftex("pdfmapfile", extension_cmd, pdf_map_file_code, 0); + primitive_pdftex("pdfmapline", extension_cmd, pdf_map_line_code, 0); + primitive_pdftex("pdftrailer", extension_cmd, pdf_trailer_code, 0); + primitive_luatex("savecatcodetable", extension_cmd, + save_cat_code_table_code, 0); + primitive_luatex("initcatcodetable", extension_cmd, + init_cat_code_table_code, 0); + primitive_pdftex("pdfsetrandomseed", extension_cmd, set_random_seed_code, + 0); + primitive_luatex("latelua", extension_cmd, late_lua_node, 0); + primitive_pdftex("pdffontexpand", extension_cmd, pdf_font_expand_code, 0); + primitive_pdftex("pdfglyphtounicode", extension_cmd, + pdf_glyph_to_unicode_code, 0); + + primitive_omega("ocp", def_ocp_cmd, 0, 0); + primitive_omega("externalocp", def_ocp_cmd, 1, 0); + primitive_omega("ocplist", def_ocp_list_cmd, 0, 0); + primitive_omega("pushocplist", push_ocp_list_cmd, 0, 0); + primitive_omega("popocplist", pop_ocp_list_cmd, 0, 0); + primitive_omega("clearocplists", clear_ocp_lists_cmd, 0, 0); + primitive_omega("addbeforeocplist", ocp_list_op_cmd, add_before_op, 0); + primitive_omega("addafterocplist", ocp_list_op_cmd, add_after_op, 0); + primitive_omega("removebeforeocplist", ocp_list_op_cmd, remove_before_op, + 0); + primitive_omega("removeafterocplist", ocp_list_op_cmd, remove_after_op, 0); + primitive_omega("ocptracelevel", ocp_trace_level_cmd, 0, 0); + set_equiv(ocp_trace_level_base, 0); + + /* some of the internal integer parameters are not associated with actual + primitives at all. */ + + primitive_no("nolocalwhatsits", assign_int_cmd, + int_base + no_local_whatsits_code, int_base); + primitive_no("nolocaldirs", assign_int_cmd, + int_base + no_local_dirs_code, int_base); + + + +@ @c +void initialize_etex_commands(void) +{ + primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0); + primitive_aleph("Alephversion", last_item_cmd, Aleph_version_code, 0); + primitive_aleph("Omegaversion", last_item_cmd, Omega_version_code, 0); + primitive_etex("eTeXversion", last_item_cmd, eTeX_version_code, 0); + primitive_aleph("Alephminorversion", last_item_cmd, + Aleph_minor_version_code, 0); + primitive_aleph("Omegaminorversion", last_item_cmd, + Omega_minor_version_code, 0); + primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, + 0); + primitive_aleph("Alephrevision", convert_cmd, Aleph_revision_code, 0); + primitive_aleph("Omegarevision", convert_cmd, Omega_revision_code, 0); + primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0); + + /* First we implement the additional \eTeX\ parameters in the table of equivalents. */ + + primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base); + primitive_etex("tracingassigns", assign_int_cmd, + int_base + tracing_assigns_code, int_base); + primitive_etex("tracinggroups", assign_int_cmd, + int_base + tracing_groups_code, int_base); + primitive_etex("tracingifs", assign_int_cmd, int_base + tracing_ifs_code, + int_base); + primitive_etex("tracingscantokens", assign_int_cmd, + int_base + tracing_scan_tokens_code, int_base); + primitive_etex("tracingnesting", assign_int_cmd, + int_base + tracing_nesting_code, int_base); + primitive_etex("predisplaydirection", assign_int_cmd, + int_base + pre_display_direction_code, int_base); + primitive_etex("lastlinefit", assign_int_cmd, int_base + last_line_fit_code, + int_base); + primitive_etex("savingvdiscards", assign_int_cmd, + int_base + saving_vdiscards_code, int_base); + primitive_etex("savinghyphcodes", assign_int_cmd, + int_base + saving_hyph_codes_code, int_base); + primitive_luatex("suppressfontnotfounderror", assign_int_cmd, + int_base + suppress_fontnotfound_error_code, int_base); + primitive_luatex("suppresslongerror", assign_int_cmd, + int_base + suppress_long_error_code, int_base); + primitive_luatex("suppressifcsnameerror", assign_int_cmd, + int_base + suppress_ifcsname_error_code, int_base); + primitive_luatex("suppressoutererror", assign_int_cmd, + int_base + suppress_outer_error_code, int_base); + primitive_luatex("synctex", assign_int_cmd, int_base + synctex_code, + int_base); + + primitive_etex("currentgrouplevel", last_item_cmd, current_group_level_code, + 0); + primitive_etex("currentgrouptype", last_item_cmd, current_group_type_code, + 0); + + primitive_etex("currentiflevel", last_item_cmd, current_if_level_code, 0); + primitive_etex("currentiftype", last_item_cmd, current_if_type_code, 0); + primitive_etex("currentifbranch", last_item_cmd, current_if_branch_code, 0); + + primitive_omega("charwd", last_item_cmd, font_char_wd_code, 0); + primitive_etex("fontcharwd", last_item_cmd, font_char_wd_code, 0); + primitive_omega("charht", last_item_cmd, font_char_ht_code, 0); + primitive_etex("fontcharht", last_item_cmd, font_char_ht_code, 0); + primitive_omega("chardp", last_item_cmd, font_char_dp_code, 0); + primitive_etex("fontchardp", last_item_cmd, font_char_dp_code, 0); + primitive_omega("charit", last_item_cmd, font_char_ic_code, 0); + primitive_etex("fontcharic", last_item_cmd, font_char_ic_code, 0); + + primitive_etex("parshapelength", last_item_cmd, par_shape_length_code, 0); + primitive_etex("parshapeindent", last_item_cmd, par_shape_indent_code, 0); + primitive_etex("parshapedimen", last_item_cmd, par_shape_dimen_code, 0); + + primitive_etex("showgroups", xray_cmd, show_groups, 0); + /* The \.{\\showtokens} command displays a token list. */ + primitive_etex("showtokens", xray_cmd, show_tokens, 0); + /* + The \.{\\unexpanded} primitive prevents expansion of tokens much as + the result from \.{\\the} applied to a token variable. The + \.{\\detokenize} primitive converts a token list into a list of + character tokens much as if the token list were written to a file. We + use the fact that the command modifiers for \.{\\unexpanded} and + \.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe} + are even. + */ + primitive_etex("unexpanded", the_cmd, 1, 0); + primitive_etex("detokenize", the_cmd, show_tokens, 0); + + /* The \.{\\showifs} command displays all currently active conditionals. */ + + primitive_etex("showifs", xray_cmd, show_ifs, 0); + + /* The \.{\\interactionmode} primitive allows to query and set the interaction mode. */ + + primitive_etex("interactionmode", set_page_int_cmd, 2, 0); + + /* The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive. */ + + primitive_etex("scantokens", input_cmd, 2, 0); + primitive_luatex("scantextokens", input_cmd, 3, 0); + + primitive_etex("readline", read_to_cs_cmd, 1, 0); + + primitive_etex("unless", expand_after_cmd, 1, 0); + primitive_etex("ifdefined", if_test_cmd, if_def_code, 0); + primitive_etex("ifcsname", if_test_cmd, if_cs_code, 0); + primitive_etex("iffontchar", if_test_cmd, if_font_char_code, 0); + primitive_pdftex("ifincsname", if_test_cmd, if_in_csname_code, 0); + primitive_luatex("ifabsnum", if_test_cmd, if_abs_num_code, 0); + primitive_luatex("ifabsdim", if_test_cmd, if_abs_dim_code, 0); + primitive_pdftex("ifpdfabsnum", if_test_cmd, if_abs_num_code, 0); + primitive_pdftex("ifpdfabsdim", if_test_cmd, if_abs_dim_code, 0); + + /* The |protected| feature of \eTeX\ defines the \.{\\protected} prefix + command for macro definitions. Such macros are protected against + expansions when lists of expanded tokens are built, e.g., for \.{\\edef} + or during \.{\\write}. */ + + primitive_etex("protected", prefix_cmd, 8, 0); + + /* Here are the additional \eTeX\ primitives for expressions. */ + + primitive_etex("numexpr", last_item_cmd, + eTeX_expr - int_val_level + int_val_level, 0); + primitive_etex("dimexpr", last_item_cmd, + eTeX_expr - int_val_level + dimen_val_level, 0); + primitive_etex("glueexpr", last_item_cmd, + eTeX_expr - int_val_level + glue_val_level, 0); + primitive_etex("muexpr", last_item_cmd, + eTeX_expr - int_val_level + mu_val_level, 0); + + primitive_etex("gluestretchorder", last_item_cmd, glue_stretch_order_code, + 0); + primitive_etex("glueshrinkorder", last_item_cmd, glue_shrink_order_code, 0); + primitive_etex("gluestretch", last_item_cmd, glue_stretch_code, 0); + primitive_etex("glueshrink", last_item_cmd, glue_shrink_code, 0); + + primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0); + primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0); + + /* The \.{\\pagediscards} and \.{\\splitdiscards} commands share the + command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are + distinguished by their |chr_code| values |last_box_code| and + |vsplit_code|. These |chr_code| values are larger than |box_code| and + |copy_code|. */ + + primitive_etex("pagediscards", un_vbox_cmd, last_box_code, 0); + primitive_etex("splitdiscards", un_vbox_cmd, vsplit_code, 0); + + /* The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties}, + and \.{\\displaywidowpenalties} commands allow to define arrays of + penalty values to be used instead of the corresponding single values. */ + primitive_etex("interlinepenalties", set_etex_shape_cmd, + inter_line_penalties_loc, etex_pen_base); + primitive_etex("clubpenalties", set_etex_shape_cmd, club_penalties_loc, + etex_pen_base); + primitive_etex("widowpenalties", set_etex_shape_cmd, widow_penalties_loc, + etex_pen_base); + primitive_etex("displaywidowpenalties", set_etex_shape_cmd, + display_widow_penalties_loc, etex_pen_base); + +} diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.h b/Build/source/texk/web2c/luatexdir/tex/conditional.h new file mode 100644 index 00000000000..23c79a03d3a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/conditional.h @@ -0,0 +1,82 @@ +/* conditional.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: conditional.h 3261 2009-12-18 11:38:21Z taco $ */ + +#ifndef CONDITIONAL_H +# define CONDITIONAL_H + + +# define unless_code 32 /* amount added for `\.{\\unless}' prefix */ + +typedef enum { + if_char_code = 0, /* `\.{\\if}' */ + if_cat_code = 1, /* `\.{\\ifcat}' */ + if_int_code = 2, /* `\.{\\ifnum}' */ + if_dim_code = 3, /* `\.{\\ifdim}' */ + if_odd_code = 4, /* `\.{\\ifodd}' */ + if_vmode_code = 5, /* `\.{\\ifvmode}' */ + if_hmode_code = 6, /* `\.{\\ifhmode}' */ + if_mmode_code = 7, /* `\.{\\ifmmode}' */ + if_inner_code = 8, /* `\.{\\ifinner}' */ + if_void_code = 9, /* `\.{\\ifvoid}' */ + if_hbox_code = 10, /* `\.{\\ifhbox}' */ + if_vbox_code = 11, /* `\.{\\ifvbox}' */ + ifx_code = 12, /* `\.{\\ifx}' */ + if_eof_code = 13, /* `\.{\\ifeof}' */ + if_true_code = 14, /* `\.{\\iftrue}' */ + if_false_code = 15, /* `\.{\\iffalse}' */ + if_case_code = 16, /* `\.{\\ifcase}' */ + if_def_code = 17, /* `\.{\\ifdefined}' */ + if_cs_code = 18, /* `\.{\\ifcsname}' */ + if_font_char_code = 19, /* `\.{\\iffontchar}' */ + if_in_csname_code = 20, /* `\.{\\ifincsname}' */ + if_primitive_code = 21, /* `\.{\\ifprimitive}' */ + if_abs_num_code = 22, /* `\.{\\ifabsnum}' */ + if_abs_dim_code = 23, /* `\.{\\ifabsdim}' */ +} if_type_codes; + + + + +# define if_limit_subtype(A) subtype((A)+1) +# define if_limit_type(A) type((A)+1) +# define if_line_field(A) vlink((A)+1) + +typedef enum { + if_code = 1, /* code for \.{\\if...} being evaluated */ + fi_code = 2, /* code for \.{\\fi} */ + else_code = 3, /* code for \.{\\else} */ + or_code = 4, /* code for \.{\\or} */ +} else_type_codes; + +extern halfword cond_ptr; /* top of the condition stack */ +extern int if_limit; /* upper bound on |fi_or_else| codes */ +extern int cur_if; /* type of conditional being worked on */ +extern int if_line; /* line where that conditional began */ +extern int skip_line; /* skipping began here */ + +extern void pass_text(void); +extern void push_condition_stack(void); +extern void pop_condition_stack(void); +extern void change_if_limit(int l, halfword p); + +extern void conditional(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.w b/Build/source/texk/web2c/luatexdir/tex/conditional.w new file mode 100644 index 00000000000..dd9349ab5ef --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/conditional.w @@ -0,0 +1,539 @@ +% conditional.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: conditional.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/conditional.w $"; + +@ @c +#define box(A) eqtb[box_base+(A)].hh.rh + +@* We consider now the way \TeX\ handles various kinds of \.{\\if} commands. + +@ Conditions can be inside conditions, and this nesting has a stack +that is independent of the |save_stack|. + +Four global variables represent the top of the condition stack: +|cond_ptr| points to pushed-down entries, if any; |if_limit| specifies +the largest code of a |fi_or_else| command that is syntactically legal; +|cur_if| is the name of the current type of conditional; and |if_line| +is the line number at which it began. + +If no conditions are currently in progress, the condition stack has the +special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|. +Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and +|link| fields of the first word contain |if_limit|, |cur_if|, and +|cond_ptr| at the next level, and the second word contains the +corresponding |if_line|. + +@c +halfword cond_ptr; /* top of the condition stack */ +int if_limit; /* upper bound on |fi_or_else| codes */ +int cur_if; /* type of conditional being worked on */ +int if_line; /* line where that conditional began */ + + +@ When we skip conditional text, we keep track of the line number +where skipping began, for use in error messages. + +@c +int skip_line; /* skipping began here */ + + +@ Here is a procedure that ignores text until coming to an \.{\\or}, +\.{\\else}, or \.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$ +nesting. After it has acted, |cur_chr| will indicate the token that +was found, but |cur_tok| will not be set (because this makes the +procedure run faster). + +@c +void pass_text(void) +{ + int l; /* level of $\.{\\if}\ldots\.{\\fi}$ nesting */ + int save_scanner_status; /* |scanner_status| upon entry */ + save_scanner_status = scanner_status; + scanner_status = skipping; + l = 0; + skip_line = line; + while (1) { + get_token_lua(); + if (cur_cmd == fi_or_else_cmd) { + if (l == 0) + break; + if (cur_chr == fi_code) + decr(l); + } else if (cur_cmd == if_test_cmd) { + incr(l); + } + } + scanner_status = save_scanner_status; + if (int_par(tracing_ifs_code) > 0) + show_cur_cmd_chr(); +} + + +@ When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then +if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if} +condition has been evaluated, \.{\\relax} will be inserted. +For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}' +would otherwise require something after the `\.1'. + +@c +void push_condition_stack(void) +{ + halfword p = new_node(if_node, 0); + vlink(p) = cond_ptr; + if_limit_type(p) = (quarterword) if_limit; + if_limit_subtype(p) = (quarterword) cur_if; + if_line_field(p) = if_line; + cond_ptr = p; + cur_if = cur_chr; + if_limit = if_code; + if_line = line; +} + +void pop_condition_stack(void) +{ + halfword p; + if (if_stack[in_open] == cond_ptr) + if_warning(); + /* conditionals possibly not properly nested with files */ + p = cond_ptr; + if_line = if_line_field(p); + cur_if = if_limit_subtype(p); + if_limit = if_limit_type(p); + cond_ptr = vlink(p); + flush_node(p); +} + + +@ Here's a procedure that changes the |if_limit| code corresponding to +a given value of |cond_ptr|. + +@c +void change_if_limit(int l, halfword p) +{ + halfword q; + if (p == cond_ptr) { + if_limit = l; /* that's the easy case */ + } else { + q = cond_ptr; + while (1) { + if (q == null) + confusion("if"); + if (vlink(q) == p) { + if_limit_type(q) = (quarterword) l; + return; + } + q = vlink(q); + } + } +} + + +@ The conditional \.{\\ifcsname} is equivalent to \.{\\expandafter} +\.{\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new +control sequence will be entered into the hash table (once all tokens +preceding the mandatory \.{\\endcsname} have been expanded). + +@c +static boolean test_for_cs(void) +{ + boolean b; /*is the condition true? */ + int m, s; /*to be tested against the second operand */ + halfword n, p, q; /*for traversing token lists in \.{\\ifx} tests */ + n = get_avail(); + p = n; /*head of the list of characters */ + b = false; + while (1) { + get_x_token(); + if (cur_cs != 0) + break; + store_new_token(cur_tok); + } + if (cur_cmd != end_cs_name_cmd) { + if (int_par(suppress_ifcsname_error_code)) { + do { + get_x_token(); + } while (cur_cmd != end_cs_name_cmd); + flush_list(n); + return b; + } else { + complain_missing_csname(); + } + } + /* Look up the characters of list |n| in the hash table, and set |cur_cs| */ + m = first; + p = token_link(n); + while (p != null) { + if (m >= max_buf_stack) { + max_buf_stack = m + 4; + if (max_buf_stack == buf_size) + check_buffer_overflow(max_buf_stack); + } + s = token_chr(token_info(p)); + if (s <= 0x7F) { + buffer[m++] = (packed_ASCII_code) s; + } else if (s <= 0x7FF) { + buffer[m++] = (packed_ASCII_code) (0xC0 + s / 0x40); + buffer[m++] = (packed_ASCII_code) (0x80 + s % 0x40); + } else if (s <= 0xFFFF) { + buffer[m++] = (packed_ASCII_code) (0xE0 + s / 0x1000); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) / 0x40); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) % 0x40); + } else { + buffer[m++] = (packed_ASCII_code) (0xF0 + s / 0x40000); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x40000) / 0x1000); + buffer[m++] = + (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) / 0x40); + buffer[m++] = + (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) % 0x40); + } + p = token_link(p); + } + if (m > first) { + cur_cs = id_lookup(first, m - first); /*|no_new_control_sequence| is |true| */ + } else if (m == first) { + cur_cs = null_cs; /*the list is empty */ + } + b = (eq_type(cur_cs) != undefined_cs_cmd); + flush_list(n); + return b; +} + + +@ An active character will be treated as category 13 following +\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. + +@c +#define get_x_token_or_active_char() do { \ + get_x_token(); \ + if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \ + if (is_active_cs(cs_text(cur_cs))) { \ + cur_cmd=active_char_cmd; \ + cur_chr=active_cs_value(cs_text(cur_tok-cs_token_flag)); \ + } \ + } \ + } while (0) + + + + + +@ A condition is started when the |expand| procedure encounters +an |if_test| command; in that case |expand| reduces to |conditional|, +which is a recursive procedure. +@^recursion@> + +@c +void conditional(void) +{ + boolean b; /*is the condition true? */ + int r; /*relation to be evaluated */ + int m, n; /*to be tested against the second operand */ + halfword p, q; /*for traversing token lists in \.{\\ifx} tests */ + int save_scanner_status; /*|scanner_status| upon entry */ + halfword save_cond_ptr; /*|cond_ptr| corresponding to this conditional */ + int this_if; /*type of this conditional */ + boolean is_unless; /*was this if preceded by `\.{\\unless}' ? */ + b = false; /*default is false, just in case */ + if (int_par(tracing_ifs_code) > 0) + if (int_par(tracing_commands_code) <= 1) + show_cur_cmd_chr(); + push_condition_stack(); + save_cond_ptr = cond_ptr; + is_unless = (cur_chr >= unless_code); + this_if = cur_chr % unless_code; + /* Either process \.{\\ifcase} or set |b| to the value of a boolean condition */ + switch (this_if) { + case if_char_code: + case if_cat_code: + /* Test if two characters match */ + get_x_token_or_active_char(); + if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) { /*not a character */ + m = relax_cmd; + n = too_big_char; + } else { + m = cur_cmd; + n = cur_chr; + } + get_x_token_or_active_char(); + if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) { + cur_cmd = relax_cmd; + cur_chr = too_big_char; + } + if (this_if == if_char_code) + b = (n == cur_chr); + else + b = (m == cur_cmd); + break; + case if_int_code: + case if_dim_code: + case if_abs_dim_code: + case if_abs_num_code: + /* Test relation between integers or dimensions */ + /* Here we use the fact that |"<"|, |"="|, and |">"| are consecutive ASCII + codes. */ + if (this_if == if_int_code || this_if == if_abs_num_code) + scan_int(); + else + scan_normal_dimen(); + n = cur_val; + if (n < 0) + if (this_if == if_abs_dim_code || this_if == if_abs_num_code) + negate(n); + + /* Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if ((cur_tok >= other_token + '<') && (cur_tok <= other_token + '>')) { + r = cur_tok - other_token; + } else { + print_err("Missing = inserted for "); + print_cmd_chr(if_test_cmd, this_if); + help1("I was expecting to see `<', `=', or `>'. Didn't."); + back_error(); + r = '='; + } + if (this_if == if_int_code || this_if == if_abs_num_code) + scan_int(); + else + scan_normal_dimen(); + + if (cur_val < 0) + if (this_if == if_abs_dim_code || this_if == if_abs_num_code) + negate(cur_val); + + switch (r) { + case '<': + b = (n < cur_val); + break; + case '=': + b = (n == cur_val); + break; + case '>': + b = (n > cur_val); + break; + default: + b = false; + break; /*can't happen */ + } + break; + case if_odd_code: + /* Test if an integer is odd */ + scan_int(); + b = odd(cur_val); + break; + case if_vmode_code: + b = (abs(cur_list.mode_field) == vmode); + break; + case if_hmode_code: + b = (abs(cur_list.mode_field) == hmode); + break; + case if_mmode_code: + b = (abs(cur_list.mode_field) == mmode); + break; + case if_inner_code: + b = (cur_list.mode_field < 0); + break; + case if_void_code: + case if_hbox_code: + case if_vbox_code: + /* Test box register status */ + scan_register_num(); + p = box(cur_val); + if (this_if == if_void_code) + b = (p == null); + else if (p == null) + b = false; + else if (this_if == if_hbox_code) + b = (type(p) == hlist_node); + else + b = (type(p) == vlist_node); + break; + case ifx_code: + /* Test if two tokens match */ + /* Note that `\.{\\ifx}' will declare two macros different if one is \\{long} + or \\{outer} and the other isn't, even though the texts of the macros are + the same. + + We need to reset |scanner_status|, since \.{\\outer} control sequences + are allowed, but we might be scanning a macro definition or preamble. + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token_lua(); + n = cur_cs; + p = cur_cmd; + q = cur_chr; + get_token_lua(); + if (cur_cmd != p) { + b = false; + } else if (cur_cmd < call_cmd) { + b = (cur_chr == q); + } else { + /* Test if two macro texts match */ + /* Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are + different in examples like this: + $$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr + {}\\def\\a\{\\c\}& + {}\\def\\c\{\}\cr + {}\\def\\b\{\\d\}& + {}\\def\\d\{\}\cr}}$$ */ + p = token_link(cur_chr); + q = token_link(equiv(n)); /*omit reference counts */ + if (p == q) { + b = true; + } else { + while ((p != null) && (q != null)) { + if (token_info(p) != token_info(q)) { + p = null; + } else { + p = token_link(p); + q = token_link(q); + } + } + b = ((p == null) && (q == null)); + } + } + scanner_status = save_scanner_status; + break; + case if_eof_code: + scan_four_bit_int_or_18(); + if (cur_val == 18) + b = !shellenabledp; + else + b = (read_open[cur_val] == closed); + break; + case if_true_code: + b = true; + break; + case if_false_code: + b = false; + break; + case if_case_code: + /* Select the appropriate case + and |return| or |goto common_ending| */ + scan_int(); + n = cur_val; /*|n| is the number of cases to pass */ + if (int_par(tracing_commands_code) > 1) { + begin_diagnostic(); + tprint("{case "); + print_int(n); + print_char('}'); + end_diagnostic(false); + } + while (n != 0) { + pass_text(); + if (cond_ptr == save_cond_ptr) { + if (cur_chr == or_code) + decr(n); + else + goto COMMON_ENDING; + } else if (cur_chr == fi_code) { + pop_condition_stack(); + } + } + change_if_limit(or_code, save_cond_ptr); + return; /*wait for \.{\\or}, \.{\\else}, or \.{\\fi} */ + break; + case if_primitive_code: + save_scanner_status = scanner_status; + scanner_status = normal; + get_token_lua(); + scanner_status = save_scanner_status; + m = prim_lookup(cs_text(cur_cs)); + b = ((cur_cmd != undefined_cs_cmd) && + (m != undefined_primitive) && + (cur_cmd == get_prim_eq_type(m)) && + (cur_chr == get_prim_equiv(m))); + break; + case if_def_code: + /* The conditional \.{\\ifdefined} tests if a control sequence is defined. */ + /* We need to reset |scanner_status|, since \.{\\outer} control sequences + are allowed, but we might be scanning a macro definition or preamble. */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token_lua(); + b = (cur_cmd != undefined_cs_cmd); + scanner_status = save_scanner_status; + break; + case if_cs_code: + b = test_for_cs(); + break; + case if_in_csname_code: + b = is_in_csname; + break; + case if_font_char_code: + /* The conditional \.{\\iffontchar} tests the existence of a character in + a font. */ + scan_font_ident(); + n = cur_val; + scan_char_num(); + b = char_exists(n, cur_val); + break; + default: /* there are no other cases, but for -Wall: */ + b = false; + } + + if (is_unless) + b = !b; + if (int_par(tracing_commands_code) > 1) { + /* Display the value of |b| */ + begin_diagnostic(); + if (b) + tprint("{true}"); + else + tprint("{false}"); + end_diagnostic(false); + } + if (b) { + change_if_limit(else_code, save_cond_ptr); + return; /*wait for \.{\\else} or \.{\\fi} */ + } + /* Skip to \.{\\else} or \.{\\fi}, then |goto common_ending| */ + /* + In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first + \.{\\else} that we come to after learning that the \.{\\if} is false is + not the \.{\\else} we're looking for. Hence the following curious + logic is needed. + */ + while (1) { + pass_text(); + if (cond_ptr == save_cond_ptr) { + if (cur_chr != or_code) + goto COMMON_ENDING; + print_err("Extra \\or"); + help1("I'm ignoring this; it doesn't match any \\if."); + error(); + } else if (cur_chr == fi_code) { + pop_condition_stack(); + } + } + COMMON_ENDING: + if (cur_chr == fi_code) + pop_condition_stack(); + else + if_limit = fi_code; /*wait for \.{\\fi} */ +} diff --git a/Build/source/texk/web2c/luatexdir/tex/directions.h b/Build/source/texk/web2c/luatexdir/tex/directions.h new file mode 100644 index 00000000000..bea50879b64 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/directions.h @@ -0,0 +1,347 @@ +/* directions.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: directions.h 3523 2010-03-19 13:28:08Z taco $ */ + +#ifndef DIRECTIONS_H +# define DIRECTIONS_H + +# define dir_T 0 +# define dir_L 1 +# define dir_B 2 +# define dir_R 3 + +# define dir_TLT 0 +# define dir_TRT 4 +# define dir_LTL 9 +# define dir_RTT 24 + + +/* # define dir_array_size 25 */ /* |dir_RTT + 1| */ + +/* inv(primary) == tertiary */ +/* +boolean _is_mirrored[dir_array_size]; +_is_mirrored[dir_TLT] = 0; +_is_mirrored[dir_TRT] = 0; +_is_mirrored[dir_LTL] = 0; +_is_mirrored[dir_RTT] = 0; +*/ + +#define is_mirrored(a) 0 + +/* secondary == tertiary */ +/* +boolean _is_rotated[dir_array_size]; +_is_rotated[dir_TLT] = 0; +_is_rotated[dir_TRT] = 0; +_is_rotated[dir_LTL] = 0; +_is_rotated[dir_RTT] = 1; +*/ + +#define is_rotated(a) (a == dir_RTT) + +/* secondary == secondary */ +/* +boolean _textdir_parallel[dir_array_size][dir_array_size]; +_textdir_parallel[dir_TLT][dir_TLT] = 1; +_textdir_parallel[dir_TLT][dir_TRT] = 1; +_textdir_parallel[dir_TLT][dir_LTL] = 0; +_textdir_parallel[dir_TLT][dir_RTT] = 0; +_textdir_parallel[dir_TRT][dir_TLT] = 1; +_textdir_parallel[dir_TRT][dir_TRT] = 1; +_textdir_parallel[dir_TRT][dir_LTL] = 0; +_textdir_parallel[dir_TRT][dir_RTT] = 0; +_textdir_parallel[dir_LTL][dir_TLT] = 0; +_textdir_parallel[dir_LTL][dir_TRT] = 0; +_textdir_parallel[dir_LTL][dir_LTL] = 1; +_textdir_parallel[dir_LTL][dir_RTT] = 1; +_textdir_parallel[dir_RTT][dir_TLT] = 0; +_textdir_parallel[dir_RTT][dir_TRT] = 0; +_textdir_parallel[dir_RTT][dir_LTL] = 1; +_textdir_parallel[dir_RTT][dir_RTT] = 1; +*/ + +#define textdir_parallel(a,b) (((a == dir_TLT || a == dir_TRT)&&(b == dir_TLT || b == dir_TRT)) || \ + ((a == dir_LTL || a == dir_RTT)&&(b == dir_LTL || b == dir_RTT))) + + +/* primary == primary */ +/* +boolean _pardir_parallel[dir_array_size][dir_array_size]; +_pardir_parallel[dir_TLT][dir_TLT] = 1; +_pardir_parallel[dir_TLT][dir_TRT] = 1; +_pardir_parallel[dir_TLT][dir_LTL] = 0; +_pardir_parallel[dir_TLT][dir_RTT] = 0; +_pardir_parallel[dir_TRT][dir_TLT] = 1; +_pardir_parallel[dir_TRT][dir_TRT] = 1; +_pardir_parallel[dir_TRT][dir_LTL] = 0; +_pardir_parallel[dir_TRT][dir_RTT] = 0; +_pardir_parallel[dir_LTL][dir_TLT] = 0; +_pardir_parallel[dir_LTL][dir_TRT] = 0; +_pardir_parallel[dir_LTL][dir_LTL] = 1; +_pardir_parallel[dir_LTL][dir_RTT] = 1; +_pardir_parallel[dir_RTT][dir_TLT] = 0; +_pardir_parallel[dir_RTT][dir_TRT] = 0; +_pardir_parallel[dir_RTT][dir_LTL] = 1; +_pardir_parallel[dir_RTT][dir_RTT] = 1; +*/ + + +#define pardir_parallel(a,b) (((a == dir_TLT || a == dir_TRT)&&(b == dir_TLT || b == dir_TRT)) || \ + ((a == dir_LTL || a == dir_RTT)&&(b == dir_LTL || b == dir_RTT))) + + +/* inv(primary) == primary */ +/* +boolean _pardir_opposite[dir_array_size][dir_array_size]; +_pardir_opposite[dir_TLT][dir_TLT] = 0; +_pardir_opposite[dir_TLT][dir_TRT] = 0; +_pardir_opposite[dir_TLT][dir_LTL] = 0; +_pardir_opposite[dir_TLT][dir_RTT] = 0; +_pardir_opposite[dir_TRT][dir_TLT] = 0; +_pardir_opposite[dir_TRT][dir_TRT] = 0; +_pardir_opposite[dir_TRT][dir_LTL] = 0; +_pardir_opposite[dir_TRT][dir_RTT] = 0; +_pardir_opposite[dir_LTL][dir_TLT] = 0; +_pardir_opposite[dir_LTL][dir_TRT] = 0; +_pardir_opposite[dir_LTL][dir_LTL] = 0; +_pardir_opposite[dir_LTL][dir_RTT] = 1; +_pardir_opposite[dir_RTT][dir_TLT] = 0; +_pardir_opposite[dir_RTT][dir_TRT] = 0; +_pardir_opposite[dir_RTT][dir_LTL] = 1; +_pardir_opposite[dir_RTT][dir_RTT] = 0; +*/ + +#define pardir_opposite(a,b) ((a == dir_LTL && b == dir_RTT)||(a == dir_RTT && b == dir_LTL)) + + +/* inv(secondary) == secondary */ +/* +boolean _textdir_opposite[dir_array_size][dir_array_size]; +_textdir_opposite[dir_TLT][dir_TLT] = 0; +_textdir_opposite[dir_TLT][dir_TRT] = 1; +_textdir_opposite[dir_TLT][dir_LTL] = 0; +_textdir_opposite[dir_TLT][dir_RTT] = 0; +_textdir_opposite[dir_TRT][dir_TLT] = 1; +_textdir_opposite[dir_TRT][dir_TRT] = 0; +_textdir_opposite[dir_TRT][dir_LTL] = 0; +_textdir_opposite[dir_TRT][dir_RTT] = 0; +_textdir_opposite[dir_LTL][dir_TLT] = 0; +_textdir_opposite[dir_LTL][dir_TRT] = 0; +_textdir_opposite[dir_LTL][dir_LTL] = 0; +_textdir_opposite[dir_LTL][dir_RTT] = 0; +_textdir_opposite[dir_RTT][dir_TLT] = 0; +_textdir_opposite[dir_RTT][dir_TRT] = 0; +_textdir_opposite[dir_RTT][dir_LTL] = 0; +_textdir_opposite[dir_RTT][dir_RTT] = 0; +*/ + +#define textdir_opposite(a,b) ((a == dir_TLT && b == dir_TRT)||(a == dir_TRT && b == dir_TLT)) + + +/* inv(tertiary) == tertiary */ +/* +boolean _glyphdir_opposite[dir_array_size][dir_array_size]; +_glyphdir_opposite[dir_TLT][dir_TLT] = 0; +_glyphdir_opposite[dir_TLT][dir_TRT] = 0; +_glyphdir_opposite[dir_TLT][dir_LTL] = 0; +_glyphdir_opposite[dir_TLT][dir_RTT] = 0; +_glyphdir_opposite[dir_TRT][dir_TLT] = 0; +_glyphdir_opposite[dir_TRT][dir_TRT] = 0; +_glyphdir_opposite[dir_TRT][dir_LTL] = 0; +_glyphdir_opposite[dir_TRT][dir_RTT] = 0; +_glyphdir_opposite[dir_LTL][dir_TLT] = 0; +_glyphdir_opposite[dir_LTL][dir_TRT] = 0; +_glyphdir_opposite[dir_LTL][dir_LTL] = 0; +_glyphdir_opposite[dir_LTL][dir_RTT] = 0; +_glyphdir_opposite[dir_RTT][dir_TLT] = 0; +_glyphdir_opposite[dir_RTT][dir_TRT] = 0; +_glyphdir_opposite[dir_RTT][dir_LTL] = 0; +_glyphdir_opposite[dir_RTT][dir_RTT] = 0; +*/ + +#define glyphdir_opposite(a,b) 0 + + +/* primary == primary */ +/* +boolean _pardir_eq[dir_array_size][dir_array_size]; +_pardir_eq[dir_TLT][dir_TLT] = 1; +_pardir_eq[dir_TLT][dir_TRT] = 1; +_pardir_eq[dir_TLT][dir_LTL] = 0; +_pardir_eq[dir_TLT][dir_RTT] = 0; +_pardir_eq[dir_TRT][dir_TLT] = 1; +_pardir_eq[dir_TRT][dir_TRT] = 1; +_pardir_eq[dir_TRT][dir_LTL] = 0; +_pardir_eq[dir_TRT][dir_RTT] = 0; +_pardir_eq[dir_LTL][dir_TLT] = 0; +_pardir_eq[dir_LTL][dir_TRT] = 0; +_pardir_eq[dir_LTL][dir_LTL] = 1; +_pardir_eq[dir_LTL][dir_RTT] = 0; +_pardir_eq[dir_RTT][dir_TLT] = 0; +_pardir_eq[dir_RTT][dir_TRT] = 0; +_pardir_eq[dir_RTT][dir_LTL] = 0; +_pardir_eq[dir_RTT][dir_RTT] = 1; +*/ + +#define pardir_eq(a,b) (((a == dir_TLT || a == dir_TRT)&&(b == dir_TLT || b == dir_TRT))|| \ + (a == dir_LTL && b == dir_LTL) || \ + (a == dir_RTT && b == dir_RTT)) + +/* secondary == secondary */ +/* +boolean _textdir_eq[dir_array_size][dir_array_size]; +_textdir_eq[dir_TLT][dir_TLT] = 1; +_textdir_eq[dir_TLT][dir_TRT] = 0; +_textdir_eq[dir_TLT][dir_LTL] = 0; +_textdir_eq[dir_TLT][dir_RTT] = 0; +_textdir_eq[dir_TRT][dir_TLT] = 0; +_textdir_eq[dir_TRT][dir_TRT] = 1; +_textdir_eq[dir_TRT][dir_LTL] = 0; +_textdir_eq[dir_TRT][dir_RTT] = 0; +_textdir_eq[dir_LTL][dir_TLT] = 0; +_textdir_eq[dir_LTL][dir_TRT] = 0; +_textdir_eq[dir_LTL][dir_LTL] = 1; +_textdir_eq[dir_LTL][dir_RTT] = 1; +_textdir_eq[dir_RTT][dir_TLT] = 0; +_textdir_eq[dir_RTT][dir_TRT] = 0; +_textdir_eq[dir_RTT][dir_LTL] = 1; +_textdir_eq[dir_RTT][dir_RTT] = 1; +*/ + +#define textdir_eq(a,b) ((a == dir_TLT && b == dir_TLT) || \ + (a == dir_TRT && b == dir_TRT) || \ + (a == dir_LTL && (b == dir_LTL || b == dir_RTT)) || \ + (a == dir_RTT && (b == dir_LTL || b == dir_RTT)) + + +/* tertiary == tertiary */ +/* +boolean _glyphdir_eq[dir_array_size][dir_array_size]; +_glyphdir_eq[dir_TLT][dir_TLT] = 1; +_glyphdir_eq[dir_TLT][dir_TRT] = 1; +_glyphdir_eq[dir_TLT][dir_LTL] = 0; +_glyphdir_eq[dir_TLT][dir_RTT] = 1; +_glyphdir_eq[dir_TRT][dir_TLT] = 1; +_glyphdir_eq[dir_TRT][dir_TRT] = 1; +_glyphdir_eq[dir_TRT][dir_LTL] = 0; +_glyphdir_eq[dir_TRT][dir_RTT] = 1; +_glyphdir_eq[dir_LTL][dir_TLT] = 0; +_glyphdir_eq[dir_LTL][dir_TRT] = 0; +_glyphdir_eq[dir_LTL][dir_LTL] = 1; +_glyphdir_eq[dir_LTL][dir_RTT] = 0; +_glyphdir_eq[dir_RTT][dir_TLT] = 1; +_glyphdir_eq[dir_RTT][dir_TRT] = 1; +_glyphdir_eq[dir_RTT][dir_LTL] = 0; +_glyphdir_eq[dir_RTT][dir_RTT] = 1; +*/ + +#define glyphdir_eq(a,b) ((a != dir_LTL && b != dir_LTL) || \ + (a == dir_LTL && b == dir_LTL)) + + +/* primary == secondary */ +/* +boolean _partextdir_eq[dir_array_size][dir_array_size]; +_partextdir_eq[dir_TLT][dir_TLT] = 0; +_partextdir_eq[dir_TLT][dir_TRT] = 0; +_partextdir_eq[dir_TLT][dir_LTL] = 1; +_partextdir_eq[dir_TLT][dir_RTT] = 1; +_partextdir_eq[dir_TRT][dir_TLT] = 0; +_partextdir_eq[dir_TRT][dir_TRT] = 0; +_partextdir_eq[dir_TRT][dir_LTL] = 1; +_partextdir_eq[dir_TRT][dir_RTT] = 1; +_partextdir_eq[dir_LTL][dir_TLT] = 1; +_partextdir_eq[dir_LTL][dir_TRT] = 0; +_partextdir_eq[dir_LTL][dir_LTL] = 0; +_partextdir_eq[dir_LTL][dir_RTT] = 0; +_partextdir_eq[dir_RTT][dir_TLT] = 0; +_partextdir_eq[dir_RTT][dir_TRT] = 1; +_partextdir_eq[dir_RTT][dir_LTL] = 0; +_partextdir_eq[dir_RTT][dir_RTT] = 0; +*/ + +#define partextdir_eq(a,b) (((a == dir_TLT || a == dir_TRT)&&(b == dir_LTL || b == dir_RTT)) || \ + (a == dir_LTL && b == dir_TLT) || \ + (a == dir_RTT && b == dir_TRT)) + + +/* secondary != tertiary */ +/* +boolean _textglyphdir_orthogonal[dir_array_size] +_textglyphdir_orthogonal[dir_TLT] = 1; +_textglyphdir_orthogonal[dir_TRT] = 1; +_textglyphdir_orthogonal[dir_LTL] = 1; +_textglyphdir_orthogonal[dir_RTT] = 0; +*/ + +#define textglyphdir_orthogonal(a) (a != dir_RTT) + +/* secondary == L */ +/* +boolean _textdir_is[dir_array_size]; +_textdir_is[dir_TLT] = 1; +_textdir_is[dir_TRT] = 1; +_textdir_is[dir_LTL] = 1; +_textdir_is[dir_RTT] = 0; +*/ + +# define textdir_is_L(a) (a == dir_TLT) + +# define push_dir(a) \ + { halfword dir_tmp=new_dir((a)); \ + vlink(dir_tmp)=dir_ptr; \ + dir_ptr=dir_tmp; \ + } + +# define push_dir_node(a) \ + { halfword dir_tmp=copy_node((a)); \ + vlink(dir_tmp)=dir_ptr; \ + dir_ptr=dir_tmp; \ + } + +# define pop_dir_node() \ + { halfword dir_tmp=dir_ptr; \ + dir_ptr=vlink(dir_tmp); \ + flush_node(dir_tmp); \ + } + +extern halfword dir_ptr; + +extern halfword text_dir_ptr; + +extern void initialize_directions(void); +extern halfword new_dir(int s); + +extern const char *string_dir(int d); +extern void print_dir(int d); + +extern void scan_direction(void); + +extern halfword do_push_dir_node(halfword p, halfword a); +extern halfword do_pop_dir_node(halfword p); + +scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph); +scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, + boolean isglyph); + +void update_text_dir_ptr(int val); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/directions.w b/Build/source/texk/web2c/luatexdir/tex/directions.w new file mode 100644 index 00000000000..4f896b60dbe --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/directions.w @@ -0,0 +1,202 @@ +% directions.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: directions.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/directions.w $"; + +@ @c +void scan_direction(void) +{ + int save_cur_cmd = cur_cmd; + int save_cur_chr = cur_chr; + get_x_token(); + if (cur_cmd == assign_dir_cmd) { + cur_val = eqtb[cur_chr].cint; + goto EXIT; + } else { + back_input(); + } + if (scan_keyword("TLT")) { + cur_val = dir_TLT; + } else if (scan_keyword("TRT")) { + cur_val = dir_TRT; + } else if (scan_keyword("LTL")) { + cur_val = dir_LTL; + } else if (scan_keyword("RTT")) { + cur_val = dir_RTT; + } else { + tex_error("Bad direction", NULL); + cur_val = 0; + } + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + EXIT: + cur_cmd = save_cur_cmd; + cur_chr = save_cur_chr; +} + +@ the next two are used by postlinebreak.c + +@c +halfword do_push_dir_node(halfword p, halfword a) +{ + halfword n; + n = copy_node(a); + vlink(n) = p; + return n; +} + +halfword do_pop_dir_node(halfword p) +{ + halfword n = vlink(p); + flush_node(p); + return n; +} + +@ @c +halfword dir_ptr; + +halfword text_dir_ptr; + +@ There is no need to do anything here at the moment. +@c +void initialize_directions(void) +{ +} + +@ @c +halfword new_dir(int s) +{ + halfword p; /* the new node */ + p = new_node(whatsit_node, dir_node); + dir_dir(p) = s; + dir_dvi_ptr(p) = -1; + dir_level(p) = cur_level; + return p; +} + +@ @c +const char *string_dir(int d) +{ + if (d == dir_TLT) { + return "TLT"; + } else if (d == dir_TRT) { + return "TRT"; + } else if (d == dir_LTL) { + return "LTL"; + } else if (d == dir_RTT) { + return "RTT"; + } else { + return "???"; + } +} + + +@ @c +void print_dir(int d) +{ + tprint(string_dir(d)); +} + +@ @c +scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph) +{ + scaled wd = 0; + if (isglyph) { + if (textdir_parallel(curdir, pdir) == + textglyphdir_orthogonal(pdir)) + wd = glyph_width(p); + else + wd = glyph_depth(p) + glyph_height(p); + } else { /* hlist, vlist, image, form, rule */ + if (textdir_parallel(pdir, curdir)) + wd = width(p); + else + wd = depth(p) + height(p); + } + return wd; +} + +@ @c +scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, + boolean isglyph) +{ + scaled_whd whd = { 0, 0, 0 }; + whd.wd = pack_width(curdir, pdir, p, isglyph); + if (isglyph) { + if (is_rotated(curdir)) { + if (textdir_parallel(curdir, pdir)) + whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2; + else + whd.ht = whd.dp = glyph_width(p) / 2; + } else { + if (is_rotated(pdir)) { + if (textdir_parallel(curdir, pdir)) + whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2; + else + whd.ht = glyph_width(p); + } else { + if (glyphdir_eq(curdir, pdir)) { + whd.ht = glyph_height(p); + whd.dp = glyph_depth(p); + } else if (glyphdir_opposite(curdir, pdir)) { + whd.ht = glyph_depth(p); + whd.dp = glyph_height(p); + } else + whd.ht = glyph_width(p); + } + } + } else { + if (is_rotated(curdir)) { + if (textdir_parallel(curdir, pdir)) + whd.ht = whd.dp = (height(p) + depth(p)) / 2; + else + whd.ht = whd.dp = width(p) / 2; + } else { + if (pardir_eq(curdir, pdir)) { + whd.ht = height(p); + whd.dp = depth(p); + } else if (pardir_opposite(curdir, pdir)) { + whd.ht = depth(p); + whd.dp = height(p); + } else + whd.ht = width(p); + } + } + return whd; +} + +@ @c +void update_text_dir_ptr(int val) +{ + if (dir_level(text_dir_ptr) == cur_level) { + /* update */ + dir_dir(text_dir_ptr) = val; + } else { + /* addition */ + halfword text_dir_tmp = new_dir(val); + vlink(text_dir_tmp) = text_dir_ptr; + text_dir_ptr = text_dir_tmp; + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.h b/Build/source/texk/web2c/luatexdir/tex/dumpdata.h new file mode 100644 index 00000000000..810424b6a40 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.h @@ -0,0 +1,115 @@ +/* dumpdata.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: dumpdata.h 3388 2010-01-26 10:59:04Z taco $ */ + +#ifndef DUMPDATA_H +# define DUMPDATA_H + +extern str_number format_ident; +extern str_number format_name; /* principal file name */ +extern FILE *fmt_file; /* for input or output of format information */ + +extern void store_fmt_file(void); +extern boolean load_fmt_file(const char *); + +/* (Un)dumping. These are called from the change file. */ +# define dump_things(base, len) \ + do_zdump ((char *) &(base), sizeof (base), (int) (len), DUMP_FILE) +# define undump_things(base, len) \ + do_zundump ((char *) &(base), sizeof (base), (int) (len), DUMP_FILE) + +extern void do_zdump(char *, int, int, FILE *); +extern void do_zundump(char *, int, int, FILE *); + +/* Like do_undump, but check each value against LOW and HIGH. The + slowdown isn't significant, and this improves the chances of + detecting incompatible format files. In fact, Knuth himself noted + this problem with Web2c some years ago, so it seems worth fixing. We + can't make this a subroutine because then we lose the type of BASE. */ +# define undump_checked_things(low, high, base, len) \ + do { \ + unsigned i; \ + undump_things (base, len); \ + for (i = 0; i < (len); i++) { \ + if ((&(base))[i] < (low) || (&(base))[i] > (high)) { \ + FATAL5 ("Item %u (=%ld) of .fmt array at %lx <%ld or >%ld", \ + i, (unsigned long) (&(base))[i], (unsigned long) &(base), \ + (unsigned long) low, (unsigned long) high); \ + } \ + } \ + } while (0) + +/* Like undump_checked_things, but only check the upper value. We use + this when the base type is unsigned, and thus all the values will be + greater than zero by definition. */ +# define undump_upper_check_things(high, base, len) \ + do { \ + unsigned i; \ + undump_things (base, len); \ + for (i = 0; i < (len); i++) { \ + if ((&(base))[i] > (high)) { \ + FATAL4 ("Item %u (=%ld) of .fmt array at %lx >%ld", \ + i, (unsigned long) (&(base))[i], (unsigned long) &(base), \ + (unsigned long) high); \ + } \ + } \ + } while (0) + +/* Use the above for all the other dumping and undumping. */ +# define generic_dump(x) dump_things (x, 1) +# define generic_undump(x) undump_things (x, 1) + +# define dump_wd generic_dump +# define dump_hh generic_dump +# define dump_qqqq generic_dump +# define undump_wd generic_undump +# define undump_hh generic_undump +# define undump_qqqq generic_undump + +/* `dump_int' is called with constant integers, so we put them into a + variable first. */ +# define dump_int(x) \ + do \ + { \ + int x_val = (x); \ + generic_dump (x_val); \ + } \ + while (0) + +/* web2c/regfix puts variables in the format file loading into + registers. Some compilers aren't willing to take addresses of such + variables. So we must kludge. */ +# if defined(REGFIX) || defined(WIN32) +# define undump_int(x) \ + do \ + { \ + int x_val; \ + generic_undump (x_val); \ + x = x_val; \ + } \ + while (0) +# else +# define undump_int generic_undump +# endif /* not (REGFIX || WIN32) */ + + + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w b/Build/source/texk/web2c/luatexdir/tex/dumpdata.w new file mode 100644 index 00000000000..8b36e504182 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.w @@ -0,0 +1,527 @@ +% dumpdata.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: dumpdata.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/dumpdata.w $"; + +#define font_id_text(A) cs_text(font_id_base+(A)) +#define prev_depth cur_list.prev_depth_field + + +@ After \.{INITEX} has seen a collection of fonts and macros, it +can write all the necessary information on an auxiliary file so +that production versions of \TeX\ are able to initialize their +memory at high speed. The present section of the program takes +care of such output and input. We shall consider simultaneously +the processes of storing and restoring, +so that the inverse relation between them is clear. +@.INITEX@> + +The global variable |format_ident| is a string that is printed right +after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this +string says simply `\.{(INITEX)}'; for other versions of \TeX\ it says, +for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year, +month, and day that the format file was created. We have |format_ident=0| +before \TeX's tables are loaded. + +@c +str_number format_ident; +str_number format_name; /* principal file name */ + + +@ Format files consist of |memory_word| items, and we use the following +macros to dump words of different types: + +@c +FILE *fmt_file; /* for input or output of format information */ + +@ @c +void store_fmt_file(void) +{ + int j, k, l; /* all-purpose indices */ + halfword p; /* all-purpose pointer */ + int x; /* something to dump */ + char *format_engine; + char *fmtname = NULL; + /* If dumping is not allowed, abort */ + /* The user is not allowed to dump a format file unless |save_ptr=0|. + This condition implies that |cur_level=level_one|, hence + the |xeq_level| array is constant and it need not be dumped. */ + if (save_ptr != 0) { + print_err("You can't dump inside a group"); + help1("`{...\\dump}' is a no-no."); + succumb(); + } + + /* Create the |format_ident|, open the format file, and inform the user + that dumping has begun */ + selector = new_string; + tprint(" (format="); + print(job_name); + print_char(' '); + print_int(int_par(year_code)); + print_char('.'); + print_int(int_par(month_code)); + print_char('.'); + print_int(int_par(day_code)); + print_char(')'); + str_room(2); + format_ident = make_string(); + print(job_name); + format_name = make_string(); + if (interaction == batch_mode) + selector = log_only; + else + selector = term_and_log; + + fmtname = pack_job_name(format_extension); + while (!zopen_w_output(&fmt_file, fmtname, FOPEN_WBIN_MODE)) { + fmtname = prompt_file_name("format file name", format_extension); + } + tprint_nl("Beginning to dump on file "); + tprint(fmtname); + free(fmtname); + tprint_nl(""); + slow_print(format_ident); + + /* Dump constants for consistency check */ + /* The next few sections of the program should make it clear how we use the + dump/undump macros. */ + + dump_int(0x57325458); /* Web2C \TeX's magic constant: "W2TX" */ + /* Align engine to 4 bytes with one or more trailing NUL */ + x = (int) strlen(engine_name); + format_engine = xmalloc((unsigned) (x + 4)); + strcpy(format_engine, engine_name); + for (k = x; k <= x + 3; k++) + format_engine[k] = 0; + x = x + 4 - (x % 4); + dump_int(x); + dump_things(format_engine[0], x); + xfree(format_engine); + dump_int(0x57325458); /* TODO HM, what checksum would make sense? */ + dump_int(max_halfword); + dump_int(hash_high); + dump_int(eqtb_size); + dump_int(hash_prime); + + /* Dump the string pool */ + k = dump_string_pool(); + print_ln(); + print_int(k); + tprint(" strings using "); + print_int((longinteger) pool_size); + tprint(" bytes"); + + /* Dump the dynamic memory */ + /* By sorting the list of available spaces in the variable-size portion of + |mem|, we are usually able to get by without having to dump very much + of the dynamic memory. + + We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid + information even when it has not been gathering statistics. + */ + dump_node_mem(); + dump_int(temp_token_head); + dump_int(hold_token_head); + dump_int(omit_template); + dump_int(null_list); + dump_int(backup_head); + dump_int(garbage); + x = (int) fix_mem_min; + dump_int(x); + x = (int) fix_mem_max; + dump_int(x); + x = (int) fix_mem_end; + dump_int(x); + dump_int(avail); + dyn_used = (int) fix_mem_end + 1; + dump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); + x = x + (int) (fix_mem_end + 1 - fix_mem_min); + p = avail; + while (p != null) { + decr(dyn_used); + p = token_link(p); + } + dump_int(dyn_used); + print_ln(); + print_int(x); + tprint(" memory locations dumped; current usage is "); + print_int(var_used); + print_char('&'); + print_int(dyn_used); + + /* Dump the table of equivalents */ + /* Dump regions 1 to 4 of |eqtb| */ + /*The table of equivalents usually contains repeated information, so we dump it + in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the + format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra + copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. + */ + k = null_cs; + do { + j = k; + while (j < int_base - 1) { + if ((equiv(j) == equiv(j + 1)) && (eq_type(j) == eq_type(j + 1)) && + (eq_level(j) == eq_level(j + 1))) + goto FOUND1; + incr(j); + } + l = int_base; + goto DONE1; /* |j=int_base-1| */ + FOUND1: + incr(j); + l = j; + while (j < int_base - 1) { + if ((equiv(j) != equiv(j + 1)) || (eq_type(j) != eq_type(j + 1)) || + (eq_level(j) != eq_level(j + 1))) + goto DONE1; + incr(j); + } + DONE1: + dump_int(l - k); + dump_things(eqtb[k], l - k); + k = j + 1; + dump_int(k - l); + } while (k != int_base); + + /* Dump regions 5 and 6 of |eqtb| */ + do { + j = k; + while (j < eqtb_size) { + if (eqtb[j].cint == eqtb[j + 1].cint) + goto FOUND2; + incr(j); + } + l = eqtb_size + 1; + goto DONE2; /* |j=eqtb_size| */ + FOUND2: + incr(j); + l = j; + while (j < eqtb_size) { + if (eqtb[j].cint != eqtb[j + 1].cint) + goto DONE2; + incr(j); + } + DONE2: + dump_int(l - k); + dump_things(eqtb[k], l - k); + k = j + 1; + dump_int(k - l); + } while (k <= eqtb_size); + if (hash_high > 0) + dump_things(eqtb[eqtb_size + 1], hash_high); /* dump |hash_extra| part */ + + dump_int(par_loc); + dump_int(write_loc); + dump_math_codes(); + dump_text_codes(); + /* Dump the hash table */ + /* A different scheme is used to compress the hash table, since its lower + region is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output + two words, |p| and |hash[p]|. The hash table is, of course, densely packed + for |p>=hash_used|, so the remaining entries are output in a~block. + */ + dump_primitives(); + dump_int(hash_used); + cs_count = frozen_control_sequence - 1 - hash_used + hash_high; + for (p = hash_base; p <= hash_used; p++) { + if (cs_text(p) != 0) { + dump_int(p); + dump_hh(hash[p]); + incr(cs_count); + } + } + dump_things(hash[hash_used + 1], + undefined_control_sequence - 1 - hash_used); + if (hash_high > 0) + dump_things(hash[eqtb_size + 1], hash_high); + dump_int(cs_count); + print_ln(); + print_int(cs_count); + tprint(" multiletter control sequences"); + + /* Dump the font information */ + dump_int(max_font_id()); + for (k = 0; k <= max_font_id(); k++) { + /* Dump the array info for internal font number |k| */ + dump_font(k); + tprint_nl("\\font"); + print_esc(font_id_text(k)); + print_char('='); + tprint_file_name((unsigned char *) font_name(k), + (unsigned char *) font_area(k), NULL); + if (font_size(k) != font_dsize(k)) { + tprint(" at "); + print_scaled(font_size(k)); + tprint("pt"); + } + } + print_ln(); + print_int(max_font_id()); + tprint(" preloaded font"); + if (max_font_id() != 1) + print_char('s'); + dump_math_data(); + + /* Dump the ocp information */ + dump_active_ocp_info(); + dump_ocp_info(); + dump_ocplist_info(); + + /* Dump the hyphenation tables */ + dump_language_data(); + + /* Dump pdftex data */ + dump_pdftex_data(static_pdf); + + /* Dump a couple more things and the closing check word */ + dump_int(interaction); + dump_int(format_ident); + dump_int(format_name); + dump_int(69069); + /* We have already printed a lot of statistics, so we set |tracing_stats:=0| + to prevent them from appearing again. */ + int_par(tracing_stats_code) = 0; + + /* Dump the lua bytecodes */ + dump_luac_registers(); + + /* Close the format file */ + zwclose(fmt_file); +} + +@ Corresponding to the procedure that dumps a format file, we have a function +that reads one in. The function returns |false| if the dumped format is +incompatible with the present \TeX\ table sizes, etc. + +@c +#define too_small(A) do { \ + wake_up_terminal(); \ + wterm_cr(); \ + fprintf(term_out,"---! Must increase the %s",(A)); \ + goto BAD_FMT; \ + } while (0) + +@ The inverse macros are slightly more complicated, since we need to check +the range of the values we are reading in. We say `|undump(a)(b)(x)|' to +read an integer value |x| that is supposed to be in the range |a<=x<=b|. + +@c +#define undump(A,B,C) do { \ + undump_int(x); \ + if (x<(A) || x>(B)) goto BAD_FMT; \ + else (C) = x; \ + } while (0) + + +#define format_debug(A,B) do { \ + if (debug_format_file) { \ + fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \ + } \ + } while (0) + +#define undump_size(A,B,C,D) do { \ + undump_int(x); \ + if (x<(A)) goto BAD_FMT; \ + if (x>(B)) too_small(C); \ + else format_debug (C,x); \ + (D) = x; \ + } while (0) + + +@ @c +boolean load_fmt_file(const char *fmtname) +{ + int j, k; /* all-purpose indices */ + halfword p; /* all-purpose pointer */ + int x; /* something undumped */ + char *format_engine; + /* Undump constants for consistency check */ + if (ini_version) { + libcfree(hash); + libcfree(eqtb); + libcfree(fixmem); + libcfree(varmem); + } + undump_int(x); + format_debug("format magic number", x); + if (x != 0x57325458) + goto BAD_FMT; /* not a format file */ + undump_int(x); + format_debug("engine name size", x); + if ((x < 0) || (x > 256)) + goto BAD_FMT; /* corrupted format file */ + + format_engine = xmalloc((unsigned) x); + undump_things(format_engine[0], x); + format_engine[x - 1] = 0; /* force string termination, just in case */ + if (strcmp(engine_name, format_engine)) { + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "---! %s was written by %s", fmtname, format_engine); + xfree(format_engine); + goto BAD_FMT; + } + xfree(format_engine); + undump_int(x); + format_debug("string pool checksum", x); + if (x != 0x57325458) { /* todo: @@\$ *//* check that strings are the same */ + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "---! %s was written by a different version", + fmtname); + goto BAD_FMT; + } + undump_int(x); + if (x != max_halfword) + goto BAD_FMT; /* check |max_halfword| */ + undump_int(hash_high); + if ((hash_high < 0) || (hash_high > sup_hash_extra)) + goto BAD_FMT; + if (hash_extra < hash_high) + hash_extra = hash_high; + eqtb_top = eqtb_size + hash_extra; + if (hash_extra == 0) + hash_top = undefined_control_sequence; + else + hash_top = eqtb_top; + hash = xmallocarray(two_halves, (unsigned) (1 + hash_top)); + memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); + eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); + set_eq_type(undefined_control_sequence, undefined_cs_cmd); + set_equiv(undefined_control_sequence, null); + set_eq_level(undefined_control_sequence, level_zero); + for (x = eqtb_size + 1; x <= eqtb_top; x++) + eqtb[x] = eqtb[undefined_control_sequence]; + undump_int(x); + if (x != eqtb_size) + goto BAD_FMT; + undump_int(x); + if (x != hash_prime) + goto BAD_FMT; + + /* Undump the string pool */ + str_ptr = undump_string_pool(); + /* Undump the dynamic memory */ + undump_node_mem(); + undump_int(temp_token_head); + undump_int(hold_token_head); + undump_int(omit_template); + undump_int(null_list); + undump_int(backup_head); + undump_int(garbage); + undump_int(fix_mem_min); + undump_int(fix_mem_max); + fixmem = xmallocarray(smemory_word, fix_mem_max + 1); + memset(voidcast(fixmem), 0, (fix_mem_max + 1) * sizeof(smemory_word)); + undump_int(fix_mem_end); + undump_int(avail); + undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); + undump_int(dyn_used); + + /* Undump the table of equivalents */ + /* Undump regions 1 to 6 of |eqtb| */ + k = null_cs; + do { + undump_int(x); + if ((x < 1) || (k + x > eqtb_size + 1)) + goto BAD_FMT; + undump_things(eqtb[k], x); + k = k + x; + undump_int(x); + if ((x < 0) || (k + x > eqtb_size + 1)) + goto BAD_FMT; + for (j = k; j <= k + x - 1; j++) + eqtb[j] = eqtb[k - 1]; + k = k + x; + } while (k <= eqtb_size); + if (hash_high > 0) /* undump |hash_extra| part */ + undump_things(eqtb[eqtb_size + 1], hash_high); + + undump(hash_base, hash_top, par_loc); + par_token = cs_token_flag + par_loc; + undump(hash_base, hash_top, write_loc); + undump_math_codes(); + undump_text_codes(); + /* Undump the hash table */ + undump_primitives(); + undump(hash_base, frozen_control_sequence, hash_used); + p = hash_base - 1; + do { + undump(p + 1, hash_used, p); + undump_hh(hash[p]); + } while (p != hash_used); + undump_things(hash[hash_used + 1], + undefined_control_sequence - 1 - hash_used); + if (debug_format_file) + print_csnames(hash_base, undefined_control_sequence - 1); + if (hash_high > 0) { + undump_things(hash[eqtb_size + 1], hash_high); + if (debug_format_file) + print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1)); + } + undump_int(cs_count); + + /* Undump the font information */ + undump_int(x); + set_max_font_id(x); + for (k = 0; k <= max_font_id(); k++) { + /* Undump the array info for internal font number |k| */ + undump_font(k); + } + undump_math_data(); + make_pdftex_banner(); + + /* Undump the ocp information */ + undump_active_ocp_info(); + undump_ocp_info(); + undump_ocplist_info(); + + /* Undump the hyphenation tables */ + undump_language_data(); + + /* Undump pdftex data */ + undump_pdftex_data(static_pdf); + + /* Undump a couple more things and the closing check word */ + undump(batch_mode, error_stop_mode, interaction); + if (interactionoption != unspecified_mode) + interaction = interactionoption; + undump(0, str_ptr, format_ident); + undump(0, str_ptr, format_name); + undump_int(x); + if (x != 69069) + goto BAD_FMT; + + /* Undump the lua bytecodes */ + undump_luac_registers(); + + prev_depth = dimen_par(pdf_ignored_dimen_code); + return true; /* it worked! */ + BAD_FMT: + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "(Fatal format file error; I'm stymied)"); + return false; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.h b/Build/source/texk/web2c/luatexdir/tex/equivalents.h new file mode 100644 index 00000000000..99894d4226a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.h @@ -0,0 +1,560 @@ +/* equivalents.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: equivalents.h 3385 2010-01-26 05:45:55Z taco $ */ + +#ifndef EQUIVALENTS_H +# define EQUIVALENTS_H + +/* +Like the preceding parameters, the following quantities can be changed +at compile time to extend or reduce \TeX's capacity. But if they are changed, +it is necessary to rerun the initialization program \.{INITEX} +@.INITEX@> +to generate new tables for the production \TeX\ program. +One can't simply make helter-skelter changes to the following constants, +since certain rather complex initialization +numbers are computed from them. They are defined here using +\.{WEB} macros, instead of being put into \PASCAL's |const| list, in order to +emphasize this distinction. +*/ + +# define font_base 0 /* smallest internal font number; must not be less than |min_quarterword| */ +# define ocp_base 0 +# define number_ocps 32768 +# define ocp_list_base 0 +# define number_ocp_lists 32768 +# define max_active_ocp_lists 32768 +# define biggest_reg 65535 /* the largest allowed register number; must be |< max_quarterword| */ +# define number_regs 65536 /* |biggest_reg+1| */ +# define number_attrs 65536 /* total numbeer of attributes */ +# define biggest_char 1114111 /* the largest allowed character number; must be |< max_halfword| */ +# define too_big_char 1114112 /* |biggest_char+1| */ +# define special_char 1114113 /* |biggest_char+2| */ +# define number_chars 1114112 /* |biggest_char+1| */ +# define number_fonts (5535-font_base+1) +# define biggest_lang 32767 +# define too_big_lang 32768 +# define text_size 0 /* size code for the largest size in a family */ +# define script_size 1 /* size code for the medium size in a family */ +# define script_script_size 2 /* size code for the smallest size in a family */ + + +/* +Each entry in |eqtb| is a |memory_word|. Most of these words are of type +|two_halves|, and subdivided into three fields: + +\yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at +which this equivalent was defined. If the level is |level_zero|, the +equivalent has never been defined; |level_one| refers to the outer level +(outside of all groups), and this level is also used for global +definitions that never go away. Higher levels are for equivalents that +will disappear at the end of their group. @^global definitions@> + +\yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of +entry this is. There are many types, since each \TeX\ primitive like +\.{\\hbox}, \.{\\def}, etc., has its own special code. The list of +command codes above includes all possible settings of the |eq_type| field. + +\yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value. +This may be a font number, a pointer into |mem|, or a variety of other +things. +*/ + + +/* +Many locations in |eqtb| have symbolic names. The purpose of the next +paragraphs is to define these names, and to set up the initial values of the +equivalents. + +In the first region we have a single entry for the `null csname' of +length zero. In luatex, the active characters and and single-letter +control sequence names are part of the next region. + +Then comes region~2, which corresponds to the hash table that we will +define later. The maximum address in this region is used for a dummy +control sequence that is perpetually undefined. There also are several +locations for control sequences that are perpetually defined +(since they are used in error recovery). +*/ + + + +/* +@ Region 3 of |eqtb| contains the |number_regs| \.{\\skip} registers, as well as the +glue parameters defined here. It is important that the ``muskip'' +parameters have larger numbers than the others. + +@ Region 4 of |eqtb| contains the local quantities defined here. The +bulk of this region is taken up by five tables that are indexed by eight-bit +characters; these tables are important to both the syntactic and semantic +portions of \TeX. There are also a bunch of special things like font and +token parameters, as well as the tables of \.{\\toks} and \.{\\box} +registers. + +@ Region 5 of |eqtb| contains the integer parameters and registers defined +here, as well as the |del_code| table. The latter table differs from the +|cat_code..math_code| tables that precede it, since delimiter codes are +fullword integers while the other kinds of codes occupy at most a +halfword. This is what makes region~5 different from region~4. We will +store the |eq_level| information in an auxiliary array of quarterwords +that will be defined later. + +@ The integer parameters should really be initialized by a macro package; +the following initialization does the minimum to keep \TeX\ from +complete failure. +@^null delimiter@> + +@ The final region of |eqtb| contains the dimension parameters defined +here, and the |number_regs| \.{\\dimen} registers. +*/ + +# define null_cs 1 /* equivalent of \.{\\csname\\endcsname} */ +# define hash_base (null_cs+1) /* beginning of region 2, for the hash table */ +# define frozen_control_sequence (hash_base+hash_size) /* for error recovery */ +# define frozen_protection (frozen_control_sequence) /* inaccessible but definable */ +# define frozen_cr (frozen_control_sequence+1) /* permanent `\.{\\cr}' */ +# define frozen_end_group (frozen_control_sequence+2) /* permanent `\.{\\endgroup}' */ +# define frozen_right (frozen_control_sequence+3) /* permanent `\.{\\right}' */ +# define frozen_fi (frozen_control_sequence+4) /* permanent `\.{\\fi}' */ +# define frozen_end_template (frozen_control_sequence+5) + /* permanent `\.{\\endtemplate}' */ +# define frozen_endv (frozen_control_sequence+6) + /* second permanent `\.{\\endtemplate}' */ +# define frozen_relax (frozen_control_sequence+7) /* permanent `\.{\\relax}' */ +# define end_write (frozen_control_sequence+8) /* permanent `\.{\\endwrite}' */ +# define frozen_dont_expand (frozen_control_sequence+9 ) + /* permanent `\.{\\notexpanded:}' */ +# define frozen_primitive (frozen_control_sequence+11 ) /* permanent `\.{\\pdfprimitive}' */ +# define frozen_special (frozen_control_sequence+12 ) /* permanent `\.{\\special}' */ +# define frozen_null_font (frozen_control_sequence+13 ) /* permanent `\.{\\nullfont}' */ +# define font_id_base (frozen_null_font-font_base ) /* begins table of |number_fonts| permanent font identifiers */ +# define frozen_null_ocp (frozen_null_font+number_fonts ) /* permanent `\.{\\nullocp}' */ +# define ocp_id_base (frozen_null_ocp-ocp_base ) + /* begins table of |number_ocps| permanent ocp identifiers */ +# define frozen_null_ocp_list (frozen_null_ocp+number_ocps ) /* permanent `\.{\\nullocplist}' */ +# define ocp_list_id_base (frozen_null_ocp_list-ocp_list_base ) /* begins table of |number_ocp_lists| permanent ocp list identifiers */ +# define undefined_control_sequence (frozen_null_ocp_list+number_ocp_lists) +# define glue_base (undefined_control_sequence+1) /* beginning of region 3 */ + +# define line_skip_code 0 /* interline glue if |baseline_skip| is infeasible */ +# define baseline_skip_code 1 /* desired glue between baselines */ +# define par_skip_code 2 /* extra glue just above a paragraph */ +# define above_display_skip_code 3 /* extra glue just above displayed math */ +# define below_display_skip_code 4 /* extra glue just below displayed math */ +# define above_display_short_skip_code 5 + /* glue above displayed math following short lines */ +# define below_display_short_skip_code 6 + /* glue below displayed math following short lines */ +# define left_skip_code 7 /* glue at left of justified lines */ +# define right_skip_code 8 /* glue at right of justified lines */ +# define top_skip_code 9 /* glue at top of main pages */ +# define split_top_skip_code 10 /* glue at top of split pages */ +# define tab_skip_code 11 /* glue between aligned entries */ +# define space_skip_code 12 /* glue between words (if not |zero_glue|) */ +# define xspace_skip_code 13 /* glue after sentences (if not |zero_glue|) */ +# define par_fill_skip_code 14 /* glue on last line of paragraph */ +# define thin_mu_skip_code 15 /* thin space in math formula */ +# define med_mu_skip_code 16 /* medium space in math formula */ +# define thick_mu_skip_code 17 /* thick space in math formula */ +# define glue_pars 18 /* total number of glue parameters */ + +# define skip_base (glue_base+glue_pars) + /* table of |number_regs| ``skip'' registers */ +# define mu_skip_base (skip_base+number_regs) /* table of |number_regs| ``muskip'' registers */ +# define local_base (mu_skip_base+number_regs) /* beginning of region 4 */ + +# define par_shape_loc (local_base) /* specifies paragraph shape */ +# define output_routine_loc (local_base+1) /* points to token list for \.{\\output} */ +# define every_par_loc (local_base+2) /* points to token list for \.{\\everypar} */ +# define every_math_loc (local_base+3) /* points to token list for \.{\\everymath} */ +# define every_display_loc (local_base+4) /* points to token list for \.{\\everydisplay} */ +# define every_hbox_loc (local_base+5) /* points to token list for \.{\\everyhbox} */ +# define every_vbox_loc (local_base+6) /* points to token list for \.{\\everyvbox} */ +# define every_job_loc (local_base+7) /* points to token list for \.{\\everyjob} */ +# define every_cr_loc (local_base+8) /* points to token list for \.{\\everycr} */ +# define err_help_loc (local_base+9) /* points to token list for \.{\\errhelp} */ +# define every_eof_loc (local_base+10) /* points to token list for \.{\\everyeof} */ +# define tex_toks (local_base+11) /* end of (e-)\TeX's token list parameters */ +# define pdftex_first_loc (tex_toks) /* base for \pdfTeX's token list parameters */ +# define pdf_pages_attr_loc (pdftex_first_loc + 0) /* points to token list for \.{\\pdfpagesattr} */ +# define pdf_page_attr_loc (pdftex_first_loc + 1) /* points to token list for \.{\\pdfpageattr} */ +# define pdf_page_resources_loc (pdftex_first_loc + 2) /* points to token list for \.{\\pdfpageresources} */ +# define pdf_xform_attr_loc (pdftex_first_loc + 3) /* points to token list for \.{\\pdfxformattr} */ +# define pdf_xform_resources_loc (pdftex_first_loc + 4) /* points to token list for \.{\\pdfxformresources} */ +# define pdf_pk_mode_loc (pdftex_first_loc + 5) /* points to token list for \.{\\pdfpkmode} */ +# define pdf_toks (pdftex_first_loc+6) /* end of \pdfTeX's token list parameters */ +# define ocp_trace_level_base (pdf_toks) +# define ocp_active_number_base (ocp_trace_level_base+1) +# define ocp_active_min_ptr_base (ocp_active_number_base+1) +# define ocp_active_max_ptr_base (ocp_active_min_ptr_base+1) +# define ocp_active_base (ocp_active_max_ptr_base+1) +# define toks_base (ocp_active_base+max_active_ocp_lists) /* table of |number_regs| token list registers */ +# define etex_pen_base (toks_base+number_regs) /* start of table of \eTeX's penalties */ +# define inter_line_penalties_loc (etex_pen_base) /* additional penalties between lines */ +# define club_penalties_loc (etex_pen_base+1) /* penalties for creating club lines */ +# define widow_penalties_loc (etex_pen_base+2) /* penalties for creating widow lines */ +# define display_widow_penalties_loc (etex_pen_base+3) /* ditto, just before a display */ +# define etex_pens (etex_pen_base+4) /* end of table of \eTeX's penalties */ +# define local_left_box_base (etex_pens) +# define local_right_box_base (local_left_box_base+1) +# define box_base (local_right_box_base+1) /* table of |number_regs| box registers */ +# define cur_font_loc (box_base+number_regs) /* internal font number outside math mode */ +# define internal_math_param_base (cur_font_loc+1 ) /* current math parameter data index */ +# define cat_code_base (internal_math_param_base+1) /* current category code data index */ +# define lc_code_base (cat_code_base+1) /* table of |number_chars| lowercase mappings */ +# define uc_code_base (lc_code_base+1) /* table of |number_chars| uppercase mappings */ +# define sf_code_base (uc_code_base+1) /* table of |number_chars| spacefactor mappings */ +# define math_code_base (sf_code_base+1) + /* table of |number_chars| math mode mappings */ +# define int_base (math_code_base+1) /* beginning of region 5 */ + +# define pretolerance_code 0 /* badness tolerance before hyphenation */ +# define tolerance_code 1 /* badness tolerance after hyphenation */ +# define line_penalty_code 2 /* added to the badness of every line */ +# define hyphen_penalty_code 3 /* penalty for break after discretionary hyphen */ +# define ex_hyphen_penalty_code 4 /* penalty for break after explicit hyphen */ +# define club_penalty_code 5 /* penalty for creating a club line */ +# define widow_penalty_code 6 /* penalty for creating a widow line */ +# define display_widow_penalty_code 7 /* ditto, just before a display */ +# define broken_penalty_code 8 /* penalty for breaking a page at a broken line */ +# define bin_op_penalty_code 9 /* penalty for breaking after a binary operation */ +# define rel_penalty_code 10 /* penalty for breaking after a relation */ +# define pre_display_penalty_code 11 /* penalty for breaking just before a displayed formula */ +# define post_display_penalty_code 12 /* penalty for breaking just after a displayed formula */ +# define inter_line_penalty_code 13 /* additional penalty between lines */ +# define double_hyphen_demerits_code 14 /* demerits for double hyphen break */ +# define final_hyphen_demerits_code 15 /* demerits for final hyphen break */ +# define adj_demerits_code 16 /* demerits for adjacent incompatible lines */ +# define mag_code 17 /* magnification ratio */ +# define delimiter_factor_code 18 /* ratio for variable-size delimiters */ +# define looseness_code 19 /* change in number of lines for a paragraph */ +# define time_code 20 /* current time of day */ +# define day_code 21 /* current day of the month */ +# define month_code 22 /* current month of the year */ +# define year_code 23 /* current year of our Lord */ +# define show_box_breadth_code 24 /* nodes per level in |show_box| */ +# define show_box_depth_code 25 /* maximum level in |show_box| */ +# define hbadness_code 26 /* hboxes exceeding this badness will be shown by |hpack| */ +# define vbadness_code 27 /* vboxes exceeding this badness will be shown by |vpack| */ +# define pausing_code 28 /* pause after each line is read from a file */ +# define tracing_online_code 29 /* show diagnostic output on terminal */ +# define tracing_macros_code 30 /* show macros as they are being expanded */ +# define tracing_stats_code 31 /* show memory usage if \TeX\ knows it */ +# define tracing_paragraphs_code 32 /* show line-break calculations */ +# define tracing_pages_code 33 /* show page-break calculations */ +# define tracing_output_code 34 /* show boxes when they are shipped out */ +# define tracing_lost_chars_code 35 /* show characters that aren't in the font */ +# define tracing_commands_code 36 /* show command codes at |big_switch| */ +# define tracing_restores_code 37 /* show equivalents when they are restored */ +# define uc_hyph_code 38 /* hyphenate words beginning with a capital letter */ +# define output_penalty_code 39 /* penalty found at current page break */ +# define max_dead_cycles_code 40 + /* bound on consecutive dead cycles of output */ +# define hang_after_code 41 /* hanging indentation changes after this many lines */ +# define floating_penalty_code 42 /* penalty for insertions heldover after a split */ +# define global_defs_code 43 /* override \.{\\global} specifications */ +# define cur_fam_code 44 /* current family */ +# define escape_char_code 45 /* escape character for token output */ +# define default_hyphen_char_code 46 /* value of \.{\\hyphenchar} when a font is loaded */ +# define default_skew_char_code 47 /* value of \.{\\skewchar} when a font is loaded */ +# define end_line_char_code 48 /* character placed at the right end of the buffer */ +# define new_line_char_code 49 /* character that prints as |print_ln| */ +# define language_code 50 /* current hyphenation table */ +# define left_hyphen_min_code 51 + /* minimum left hyphenation fragment size */ +# define right_hyphen_min_code 52 /* minimum right hyphenation fragment size */ +# define holding_inserts_code 53 + /* do not remove insertion nodes from \.{\\box255} */ +# define error_context_lines_code 54 /* maximum intermediate line pairs shown */ +# define local_inter_line_penalty_code 55 /* local \.{\\interlinepenalty} */ +# define local_broken_penalty_code 56 /* local \.{\\brokenpenalty} */ +# define no_local_whatsits_code 57 /* counts local whatsits */ +# define no_local_dirs_code 58 + +# define luastartup_id_code 60 +# define disable_lig_code 61 +# define disable_kern_code 62 +# define cat_code_table_code 63 +# define output_box_code 64 +# define cur_lang_code 65 /* current language id */ +# define ex_hyphen_char_code 66 +# define pdftex_first_integer_code 67 /*base for \pdfTeX's integer parameters */ +# define pdf_output_code (pdftex_first_integer_code + 0) + /*switch on PDF output if positive */ +# define pdf_compress_level_code (pdftex_first_integer_code + 1) + /*compress level of streams */ +# define pdf_decimal_digits_code (pdftex_first_integer_code + 2) + /*digits after the decimal point of numbers */ +# define pdf_image_resolution_code (pdftex_first_integer_code + 3) /*default image resolution */ +# define pdf_pk_resolution_code (pdftex_first_integer_code + 4) /*default resolution of PK font */ +# define pdf_unique_resname_code (pdftex_first_integer_code + 5) + /*generate unique names for resouces */ +# define pdf_minor_version_code (pdftex_first_integer_code + 6) /*fractional part of the PDF version produced */ +# define pdf_pagebox_code (pdftex_first_integer_code + 7) /*default pagebox to use for PDF inclusion */ +# define pdf_inclusion_errorlevel_code (pdftex_first_integer_code + 8) /*if the PDF inclusion should treat pdfs + newer than |pdf_minor_version| as an error */ +# define pdf_gamma_code (pdftex_first_integer_code + 9) +# define pdf_image_gamma_code (pdftex_first_integer_code + 10) +# define pdf_image_hicolor_code (pdftex_first_integer_code + 11) +# define pdf_image_apply_gamma_code (pdftex_first_integer_code + 12) +# define pdf_adjust_spacing_code (pdftex_first_integer_code + 13) /*level of spacing adjusting */ +# define pdf_protrude_chars_code (pdftex_first_integer_code + 14) /*protrude chars at left/right edge of paragraphs */ +# define pdf_tracing_fonts_code (pdftex_first_integer_code + 15) + /*level of font detail in log */ +# define pdf_objcompresslevel_code (pdftex_first_integer_code + 16) /*activate object streams */ +# define pdf_gen_tounicode_code (pdftex_first_integer_code + 17) + /*generate ToUnicode for fonts? */ +# define pdf_draftmode_code (pdftex_first_integer_code + 18) /*switch on draftmode if positive */ +# define pdf_replace_font_code (pdftex_first_integer_code + 19) /*generate ToUnicode for fonts? */ +# define pdf_inclusion_copy_font_code (pdftex_first_integer_code + 20) /*generate ToUnicode for fonts? */ +# define pdf_int_pars (pdftex_first_integer_code + 21) /*total number of \pdfTeX's integer parameters */ +# define etex_first_integer_code (pdf_int_pars) /*base for \eTeX's integer parameters */ +# define tracing_assigns_code (etex_first_integer_code) /*show assignments */ +# define tracing_groups_code (etex_first_integer_code+1) /*show save/restore groups */ +# define tracing_ifs_code (etex_first_integer_code+2) /*show conditionals */ +# define tracing_scan_tokens_code (etex_first_integer_code+3) /*show pseudo file open and close */ +# define tracing_nesting_code (etex_first_integer_code+4) /*show incomplete groups and ifs within files */ +# define pre_display_direction_code (etex_first_integer_code+5) /*text direction preceding a display */ +# define last_line_fit_code (etex_first_integer_code+6) /*adjustment for last line of paragraph */ +# define saving_vdiscards_code (etex_first_integer_code+7) + /*save items discarded from vlists */ +# define saving_hyph_codes_code (etex_first_integer_code+8) /*save hyphenation codes for languages */ +# define suppress_fontnotfound_error_code (etex_first_integer_code+9) /*suppress errors for missing fonts */ +# define suppress_long_error_code (etex_first_integer_code+10) /*suppress errors for missing fonts */ +# define suppress_ifcsname_error_code (etex_first_integer_code+11) + /*suppress errors for failed \.{\\ifcsname} */ +# define suppress_outer_error_code (etex_first_integer_code+12) /*suppress errors for \.{\\outer} */ +# define synctex_code (etex_first_integer_code+13) /* is synctex file generation enabled ? */ +# define tex_int_pars (synctex_code+1) /* total number of integer parameters */ + +# define page_direction_code (tex_int_pars) +# define body_direction_code (tex_int_pars+1) +# define par_direction_code (tex_int_pars+2) +# define text_direction_code (tex_int_pars+3) +# define math_direction_code (tex_int_pars+4) +# define int_pars (tex_int_pars+5) /*total number of integer parameters */ + +# define dir_base (int_base+tex_int_pars) +# define count_base (int_base+int_pars) /*|number_regs| user \.{\\count} registers */ +# define attribute_base (count_base+number_regs) + /*|number_attrs| user \.{\\attribute} registers */ +# define del_code_base (attribute_base+number_attrs) /*|number_chars| delimiter code mappings */ +# define dimen_base (del_code_base+1) /*beginning of region 6 */ + +# define par_indent_code 0 /* indentation of paragraphs */ +# define math_surround_code 1 /* space around math in text */ +# define line_skip_limit_code 2 /* threshold for |line_skip| instead of |baseline_skip| */ +# define hsize_code 3 /* line width in horizontal mode */ +# define vsize_code 4 /* page height in vertical mode */ +# define max_depth_code 5 /* maximum depth of boxes on main pages */ +# define split_max_depth_code 6 /* maximum depth of boxes on split pages */ +# define box_max_depth_code 7 /* maximum depth of explicit vboxes */ +# define hfuzz_code 8 /* tolerance for overfull hbox messages */ +# define vfuzz_code 9 /* tolerance for overfull vbox messages */ +# define delimiter_shortfall_code 10 /* maximum amount uncovered by variable delimiters */ +# define null_delimiter_space_code 11 /* blank space in null delimiters */ +# define script_space_code 12 /* extra space after subscript or superscript */ +# define pre_display_size_code 13 /* length of text preceding a display */ +# define display_width_code 14 /* length of line for displayed equation */ +# define display_indent_code 15 /* indentation of line for displayed equation */ +# define overfull_rule_code 16 /* width of rule that identifies overfull hboxes */ +# define hang_indent_code 17 /* amount of hanging indentation */ +# define h_offset_code 18 /* amount of horizontal offset when shipping pages out */ +# define v_offset_code 19 /* amount of vertical offset when shipping pages out */ +# define emergency_stretch_code 20 /* reduces badnesses on final pass of line-breaking */ +# define page_left_offset_code 21 +# define page_top_offset_code 22 +# define page_right_offset_code 23 +# define page_bottom_offset_code 24 +# define pdftex_first_dimen_code 25 /* first number defined in this section */ +# define pdf_h_origin_code (pdftex_first_dimen_code + 0) /* horigin of the PDF output */ +# define pdf_v_origin_code (pdftex_first_dimen_code + 1) /* vorigin of the PDF output */ +# define page_width_code (pdftex_first_dimen_code + 2) /* page width of the PDF output */ +# define page_height_code (pdftex_first_dimen_code + 3) /* page height of the PDF output */ +# define pdf_link_margin_code (pdftex_first_dimen_code + 4) /* link margin in the PDF output */ +# define pdf_dest_margin_code (pdftex_first_dimen_code + 5) /* dest margin in the PDF output */ +# define pdf_thread_margin_code (pdftex_first_dimen_code + 6) /* thread margin in the PDF output */ +# define pdf_first_line_height_code (pdftex_first_dimen_code + 7) +# define pdf_last_line_depth_code (pdftex_first_dimen_code + 8) +# define pdf_each_line_height_code (pdftex_first_dimen_code + 9) +# define pdf_each_line_depth_code (pdftex_first_dimen_code + 10) +# define pdf_ignored_dimen_code (pdftex_first_dimen_code + 11) +# define pdf_px_dimen_code (pdftex_first_dimen_code + 12) +# define pdftex_last_dimen_code (pdftex_first_dimen_code + 12) /* last number defined in this section */ +# define dimen_pars (pdftex_last_dimen_code + 1) + /* total number of dimension parameters */ + +# define scaled_base (dimen_base+dimen_pars) /* table of |number_regs| user-defined \.{\\dimen} registers */ +# define eqtb_size (scaled_base+biggest_reg) /* largest subscript of |eqtb| */ + +extern memory_word *eqtb; + +extern halfword eqtb_top; /* maximum of the |eqtb| */ + +extern quarterword xeq_level[(eqtb_size + 1)]; +extern void initialize_equivalents(void); + +# define eq_level_field(A) (A).hh.b1 +# define eq_type_field(A) (A).hh.b0 +# define equiv_field(A) (A).hh.rh + +# define eq_level(A) eq_level_field(eqtb[(A)]) /* level of definition */ +# define eq_type(A) eq_type_field(eqtb[(A)]) /* command code for equivalent */ +# define equiv(A) equiv_field(eqtb[(A)]) + /* equivalent value */ + +# define set_eq_level(A,B) eq_level((A)) = (B) +# define set_eq_type(A,B) eq_type((A)) = (B) +# define set_equiv(A,B) equiv((A)) = (B) + +typedef struct save_record_ { + quarterword type_; + quarterword level_; + memory_word word_; +} save_record; + + +extern save_record *save_stack; +extern int save_ptr; /* first unused entry on |save_stack| */ +extern int max_save_stack; /* maximum usage of save stack */ +extern quarterword cur_level; /* current nesting level for groups */ +extern group_code cur_group; /* current group type */ +extern int cur_boundary; /* where the current level begins */ + + +# define save_type(A) save_stack[(A)].type_ /* classifies a |save_stack| entry */ +# define save_level(A) save_stack[(A)].level_ /* saved level for regions 5 and 6, or group code */ +# define save_value(A) save_stack[(A)].word_.cint /* |eqtb| location or token or |save_stack| location */ +# define save_word(A) save_stack[(A)].word_ /* |eqtb| entry */ + +/* +We use the notation |saved(k)| to stand for an item that +appears in location |save_ptr+k| of the save stack. +*/ + +# define saved_type(A) save_stack[save_ptr+(A)].type_ +# define saved_level(A) save_stack[save_ptr+(A)].level_ +# define saved_value(A) save_stack[save_ptr+(A)].word_.cint + +# define set_saved_record(A,B,C,D) do { \ + saved_type(A) = (quarterword)(B); \ + saved_level(A) = (quarterword)(C); \ + saved_value(A) = (D); \ + } while (0) + +# define restore_old_value 0 /* |save_type| when a value should be restored later */ +# define restore_zero 1 /* |save_type| when an undefined entry should be restored */ +# define insert_token 2 /* |save_type| when a token is being saved for later use */ +# define level_boundary 3 /* |save_type| corresponding to beginning of group */ +# define saved_line 4 +# define saved_adjust 5 +# define saved_insert 6 +# define saved_disc 7 +# define saved_boxtype 8 +# define saved_textdir 9 +# define saved_eqno 10 +# define saved_choices 11 +# define saved_math 12 +# define saved_boxcontext 13 +# define saved_boxspec 14 +# define saved_boxdir 15 +# define saved_boxattr 16 +# define saved_eqtb 17 + +extern void print_save_stack(void); + +# define assign_trace(A,B) if (int_par(tracing_assigns_code)>0) restore_trace((A),(B)) + +# define int_par(A) eqtb[int_base+(A)].cint +# define dimen_par(A) eqtb[dimen_base+(A)].cint +# define loc_par(A) equiv(local_base+(A)) +# define glue_par(A) equiv(glue_base+(A)) + +extern int mag_set; /* if nonzero, this magnification should be used henceforth */ +extern void prepare_mag(void); + +/* +@ Here are the group codes that are used to discriminate between different +kinds of groups. They allow \TeX\ to decide what special actions, if any, +should be performed when a group ends. +\def\grp{\.{\char'173...\char'175}} + +Some groups are not supposed to be ended by right braces. For example, +the `\.\$' that begins a math formula causes a |math_shift_group| to +be started, and this should be terminated by a matching `\.\$'. Similarly, +a group that starts with \.{\\left} should end with \.{\\right}, and +one that starts with \.{\\begingroup} should end with \.{\\endgroup}. + +*/ + + +typedef enum { + bottom_level = 0, /* group code for the outside world */ + simple_group, /* group code for local structure only */ + hbox_group, /* code for `\.{\\hbox}\grp' */ + adjusted_hbox_group, /* code for `\.{\\hbox}\grp' in vertical mode */ + vbox_group, /* code for `\.{\\vbox}\grp' */ + vtop_group, /* code for `\.{\\vtop}\grp' */ + align_group, /* code for `\.{\\halign}\grp', `\.{\\valign}\grp' */ + no_align_group, /* code for `\.{\\noalign}\grp' */ + output_group, /* code for output routine */ + math_group, /* code for, e.g., `\.{\char'136}\grp' */ + disc_group, /* code for `\.{\\discretionary}\grp\grp\grp' */ + insert_group, /* code for `\.{\\insert}\grp', `\.{\\vadjust}\grp' */ + vcenter_group, /* code for `\.{\\vcenter}\grp' */ + math_choice_group, /* code for `\.{\\mathchoice}\grp\grp\grp\grp' */ + semi_simple_group, /* code for `\.{\\begingroup...\\endgroup}' */ + math_shift_group, /* code for `\.{\$...\$}' */ + math_left_group, /* code for `\.{\\left...\\right}' */ + local_box_group, /* code for `\.{\\localleftbox...\\localrightbox}' */ + split_off_group, /* box code for the top part of a \.{\\vsplit} */ + split_keep_group, /* box code for the bottom part of a \.{\\vsplit} */ + preamble_group, /* box code for the preamble processing in an alignment */ + align_set_group, /* box code for the final item pass in an alignment */ + fin_row_group /* box code for a provisory line in an alignment */ +} tex_group_codes; + +# define max_group_code local_box_group /* which is wrong, but is what the web says */ + +extern int cur_cmd; /* current command set by |get_next| */ +extern halfword cur_chr; /* operand of current command */ +extern halfword cur_cs; /* control sequence found here, zero if none found */ +extern halfword cur_tok; /* packed representative of |cur_cmd| and |cur_chr| */ + +extern void show_cur_cmd_chr(void); + +extern void new_save_level(group_code c); /* begin a new level of grouping */ +extern void eq_destroy(memory_word w); /* gets ready to forget |w| */ +extern void eq_save(halfword p, quarterword l); /* saves |eqtb[p]| */ +extern void eq_define(halfword p, quarterword t, halfword e); /* new data for |eqtb| */ +extern void eq_word_define(halfword p, int w); +extern void geq_define(halfword p, quarterword t, halfword e); /* global |eq_define| */ +extern void geq_word_define(halfword p, int w); /* global |eq_word_define| */ +extern void save_for_after(halfword t); +extern void unsave(void); /* pops the top level off the save stack */ +extern void restore_trace(halfword p, const char *s); /* |eqtb[p]| has just been restored or retained */ +extern void show_save_groups(void); + +# define level_zero 0 /* level for undefined quantities */ +# define level_one 1 /* outermost level for defined quantities */ + +extern void show_eqtb(halfword n); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.w b/Build/source/texk/web2c/luatexdir/tex/equivalents.w new file mode 100644 index 00000000000..02fa1f48578 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.w @@ -0,0 +1,1171 @@ +% equivalents.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: equivalents.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/equivalents.w $"; + +@ @c +#define par_shape_ptr equiv(par_shape_loc) + +void show_eqtb_meaning(halfword n); /* forward */ + +@ Now that we have studied the data structures for \TeX's semantic routines, +we ought to consider the data structures used by its syntactic routines. In +other words, our next concern will be +the tables that \TeX\ looks at when it is scanning +what the user has written. + +The biggest and most important such table is called |eqtb|. It holds the +current ``equivalents'' of things; i.e., it explains what things mean +or what their current values are, for all quantities that are subject to +the nesting structure provided by \TeX's grouping mechanism. There are six +parts to |eqtb|: + +\yskip\hang 1) |eqtb[null_cs]| holds the current equivalent of the +zero-length control sequence. + +\yskip\hang 2) |eqtb[hash_base..(glue_base-1)]| holds the current +equivalents of single- and multiletter control sequences. + +\yskip\hang 3) |eqtb[glue_base..(local_base-1)]| holds the current +equivalents of glue parameters like the current baselineskip. + +\yskip\hang 4) |eqtb[local_base..(int_base-1)]| holds the current +equivalents of local halfword quantities like the current box registers, +the current ``catcodes,'' the current font, and a pointer to the current +paragraph shape. + +\yskip\hang 5) |eqtb[int_base..(dimen_base-1)]| holds the current +equivalents of fullword integer parameters like the current hyphenation +penalty. + +\yskip\hang 6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents +of fullword dimension parameters like the current hsize or amount of +hanging indentation. + +\yskip\noindent Note that, for example, the current amount of +baselineskip glue is determined by the setting of a particular location +in region~3 of |eqtb|, while the current meaning of the control sequence +`\.{\\baselineskip}' (which might have been changed by \.{\\def} or +\.{\\let}) appears in region~2. + +@ The last two regions of |eqtb| have fullword values instead of the +three fields |eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary, +but \TeX\ needs to store the |eq_level| information in another array +called |xeq_level|. + +@c +memory_word *eqtb; +halfword eqtb_top; /* maximum of the |eqtb| */ +quarterword xeq_level[(eqtb_size + 1)]; + +@ @c +void initialize_equivalents(void) +{ + int k; + for (k = int_base; k <= eqtb_size; k++) + xeq_level[k] = level_one; +} + + +@ The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups +in \TeX\ means that |eqtb| entries valid in outer groups should be saved +and restored later if they are overridden inside the braces. When a new |eqtb| +value is being assigned, the program therefore checks to see if the previous +entry belongs to an outer level. In such a case, the old value is placed +on the |save_stack| just before the new value enters |eqtb|. At the +end of a grouping level, i.e., when the right brace is sensed, the +|save_stack| is used to restore the outer values, and the inner ones are +destroyed. + +Entries on the |save_stack| are of type |save_record|. The top item on +this stack is |save_stack[p]|, where |p=save_ptr-1|; it contains three +fields called |save_type|, |save_level|, and |save_value|, and it is +interpreted in one of four ways: + +\yskip\hang 1) If |save_type(p)=restore_old_value|, then +|save_value(p)| is a location in |eqtb| whose current value should +be destroyed at the end of the current group and replaced by |save_word(p-1)| +(|save_type(p-1)==saved_eqtb|). +Furthermore if |save_value(p)>=int_base|, then |save_level(p)| should +replace the corresponding entry in |xeq_level| (if |save_value(p)<int_base|, +then the level is part of |save_word(p-1)|). + +\yskip\hang 2) If |save_type(p)=restore_zero|, then |save_value(p)| +is a location in |eqtb| whose current value should be destroyed at the end +of the current group, when it should be +replaced by the current value of |eqtb[undefined_control_sequence]|. + +\yskip\hang 3) If |save_type(p)=insert_token|, then |save_value(p)| +is a token that should be inserted into \TeX's input when the current +group ends. + +\yskip\hang 4) If |save_type(p)=level_boundary|, then |save_level(p)| +is a code explaining what kind of group we were previously in, and +|save_value(p)| points to the level boundary word at the bottom of +the entries for that group. Furthermore, |save_value(p-1)| contains the +source line number at which the current level of grouping was entered, +this field has itself a type: |save_type(p-1)==saved_line|. + + +Besides this `official' use, various subroutines push temporary +variables on the save stack when it is handy to do so. These all have +an explicit |save_type|, and they are: + +|saved_adjust| signifies an adjustment is beging scanned, +|saved_insert| an insertion is being scanned, +|saved_disc| the \.{\\discretionary} sublist we are working on right now, +|saved_boxtype| whether a \.{\\localbox} is \.{\\left} or \.{\\right}, +|saved_textdir| a text direction to be restored, +|saved_eqno| diffentiates between \.{\\eqno} and \.{\\leqno}, +|saved_choices| the \.{\\mathchoices} sublist we are working on right now, +|saved_math| and interrupted math list, +|saved_boxcontext| the box context value, +|saved_boxspec| the box \.{to} or \.{spread} specification, +|saved_boxdir| the box \.{dir} specification, +|saved_boxattr| the box \.{attr} specification. + +@ The global variable |cur_group| keeps track of what sort of group we are +currently in. Another global variable, |cur_boundary|, points to the +topmost |level_boundary| word. And |cur_level| is the current depth of +nesting. The routines are designed to preserve the condition that no entry +in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|. + +@c +save_record *save_stack; +int save_ptr; /* first unused entry on |save_stack| */ +int max_save_stack; /* maximum usage of save stack */ +quarterword cur_level = level_one; /* current nesting level for groups */ +group_code cur_group = bottom_level; /* current group type */ +int cur_boundary; /* where the current level begins */ + + +@ At this time it might be a good idea for the reader to review the introduction +to |eqtb| that was given above just before the long lists of parameter names. +Recall that the ``outer level'' of the program is |level_one|, since +undefined control sequences are assumed to be ``defined'' at |level_zero|. + + + +@ The following macro is used to test if there is room for up to eight more +entries on |save_stack|. By making a conservative test like this, we can +get by with testing for overflow in only a few places. + +@c +#define check_full_save_stack() do { \ + if (save_ptr>max_save_stack) { \ + max_save_stack=save_ptr; \ + if (max_save_stack>save_size-8) \ + overflow("save size",(unsigned)save_size); \ + } \ + } while (0) + + +@ Procedure |new_save_level| is called when a group begins. The +argument is a group identification code like `|hbox_group|'. After +calling this routine, it is safe to put six more entries on |save_stack|. + +In some cases integer-valued items are placed onto the +|save_stack| just below a |level_boundary| word, because this is a +convenient place to keep information that is supposed to ``pop up'' just +when the group has finished. +For example, when `\.{\\hbox to 100pt}' is being treated, the 100pt +dimension is stored on |save_stack| just before |new_save_level| is +called. + +@c +void new_save_level(group_code c) +{ /* begin a new level of grouping */ + check_full_save_stack(); + set_saved_record(0, saved_line, 0, line); + incr(save_ptr); + save_type(save_ptr) = level_boundary; + save_level(save_ptr) = cur_group; + save_value(save_ptr) = cur_boundary; + if (cur_level == max_quarterword) + overflow("grouping levels", max_quarterword - min_quarterword); + /* quit if |(cur_level+1)| is too big to be stored in |eqtb| */ + cur_boundary = save_ptr; + cur_group = c; + if (int_par(tracing_groups_code) > 0) + group_trace(false); + incr(cur_level); + incr(save_ptr); +} + +@ @c +static const char *save_stack_type(int v) +{ + const char *s = ""; + switch (save_type(v)) { +/* *INDENT-OFF* */ + case restore_old_value: s = "restore_old_value"; break; + case restore_zero: s = "restore_zero"; break; + case insert_token: s = "insert_token"; break; + case level_boundary: s = "level_boundary"; break; + case saved_line: s = "saved_line"; break; + case saved_adjust: s = "saved_adjust"; break; + case saved_insert: s = "saved_insert"; break; + case saved_disc: s = "saved_disc"; break; + case saved_boxtype: s = "saved_boxtype"; break; + case saved_textdir: s = "saved_textdir"; break; + case saved_eqno: s = "saved_eqno"; break; + case saved_choices: s = "saved_choices"; break; + case saved_math: s = "saved_math"; break; + case saved_boxcontext: s = "saved_boxcontext"; break; + case saved_boxspec: s = "saved_boxspec"; break; + case saved_boxdir: s = "saved_boxdir"; break; + case saved_boxattr: s = "saved_boxattr"; break; + case saved_eqtb: s = "saved_eqtb"; break; + default: break; +/* *INDENT-ON* */ + } + return s; +} + +@ @c +void print_save_stack(void) +{ + int i; + begin_diagnostic(); + selector = term_and_log; + print_ln(); + for (i = (save_ptr - 1); i >= 0; i--) { + tprint("save_stack["); + if (i < 100) + print_char(' '); + if (i < 10) + print_char(' '); + print_int(i); + tprint("]: "); + tprint(save_stack_type(i)); + switch (save_type(i)) { + case restore_old_value: + tprint(", "); + show_eqtb_meaning(save_value(i)); + tprint("="); + if (save_value(i) >= int_base) { + print_int(save_word(i - 1).cint); + } else { + print_int(eq_type_field(save_word(i - 1))); + print_char(','); /* |print_int(eq_level_field(save_word(i-1)));| */ + print_int(equiv_field(save_word(i - 1))); + } + i--; + break; + case restore_zero: + tprint(", "); + show_eqtb_meaning(save_value(i)); + break; + case insert_token: + tprint(", "); + { + halfword p = get_avail(); + set_token_info(p, save_value(i)); + show_token_list(p, null, 1); + free_avail(p); + } + break; + case level_boundary: + tprint(", old group="); + print_int(save_level(i)); + tprint(", boundary = "); + print_int(save_value(i)); + tprint(", line = "); + print_int(save_value(i - 1)); + i--; + break; + case saved_adjust: + tprint(", "); + print_int(save_level(i)); /* vadjust vs vadjust pre */ + break; + case saved_insert: + tprint(", "); + print_int(save_value(i)); /* insert number */ + break; + case saved_boxtype: /* \.{\\localleftbox} vs \.{\\localrightbox} */ + tprint(", "); + print_int(save_value(i)); + break; + case saved_eqno: /* \.{\\eqno} vs \.{\\leqno} */ + tprint(", "); + print_int(save_value(i)); + break; + case saved_disc: + case saved_choices: + tprint(", "); + print_int(save_value(i)); + break; + case saved_math: + tprint(", listptr="); + print_int(save_value(i)); + break; + case saved_boxcontext: + tprint(", "); + print_int(save_value(i)); + break; + case saved_boxspec: + tprint(", spec="); + print_int(save_level(i)); + tprint(", dimen="); + print_int(save_value(i)); + break; + case saved_textdir: + case saved_boxdir: + tprint(", "); + print_dir(dir_dir(save_value(i))); + break; + case saved_boxattr: + tprint(", "); + print_int(save_value(i)); + break; + case saved_line: + case saved_eqtb: + break; + default: + break; + } + print_ln(); + } + end_diagnostic(true); +} + + +@ The \.{\\showgroups} command displays all currently active grouping + levels. + + +@ The modifications of \TeX\ required for the display produced by the + |show_save_groups| procedure were first discussed by Donald~E. Knuth in + {\sl TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990. + @^Knuth, Donald Ervin@> + + In order to understand a group type we also have to know its mode. + Since unrestricted horizontal modes are not associated with grouping, + they are skipped when traversing the semantic nest. + +@c +void show_save_groups(void) +{ + int p; /* index into |nest| */ + int m; /* mode */ + save_pointer v; /* saved value of |save_ptr| */ + quarterword l; /* saved value of |cur_level| */ + group_code c; /* saved value of |cur_group| */ + int a; /* to keep track of alignments */ + int i; + quarterword j; + const char *s; +#ifdef DEBUG + print_save_stack(); +#endif + p = nest_ptr; + v = save_ptr; + l = cur_level; + c = cur_group; + save_ptr = cur_boundary; + decr(cur_level); + a = 1; + s = NULL; + tprint_nl(""); + print_ln(); + while (1) { + tprint_nl("### "); + print_group(true); + if (cur_group == bottom_level) + goto DONE; + do { + m = nest[p].mode_field; + if (p > 0) + decr(p); + else + m = vmode; + } while (m == hmode); + tprint(" ("); + switch (cur_group) { + case simple_group: + incr(p); + goto FOUND2; + break; + case hbox_group: + case adjusted_hbox_group: + s = "hbox"; + break; + case vbox_group: + s = "vbox"; + break; + case vtop_group: + s = "vtop"; + break; + case align_group: + if (a == 0) { + if (m == -vmode) + s = "halign"; + else + s = "valign"; + a = 1; + goto FOUND1; + } else { + if (a == 1) + tprint("align entry"); + else + tprint_esc("cr"); + if (p >= a) + p = p - a; + a = 0; + goto FOUND; + } + break; + case no_align_group: + incr(p); + a = -1; + tprint_esc("noalign"); + goto FOUND2; + break; + case output_group: + tprint_esc("output"); + goto FOUND; + break; + case math_group: + goto FOUND2; + break; + case disc_group: + tprint_esc("discretionary"); + for (i = 1; i < 3; i++) + if (i <= saved_value(-2)) + tprint("{}"); + goto FOUND2; + break; + case math_choice_group: + tprint_esc("mathchoice"); + for (i = 1; i < 4; i++) + if (i <= saved_value(-3)) /* different offset because |-2==saved_textdir| */ + tprint("{}"); + goto FOUND2; + break; + case insert_group: + if (saved_type(-1) == saved_adjust) { + tprint_esc("vadjust"); + if (saved_level(-1) != 0) + tprint(" pre"); + } else { + tprint_esc("insert"); + print_int(saved_value(-1)); + } + goto FOUND2; + break; + case vcenter_group: + s = "vcenter"; + goto FOUND1; + break; + case semi_simple_group: + incr(p); + tprint_esc("begingroup"); + goto FOUND; + break; + case math_shift_group: + if (m == mmode) { + print_char('$'); + } else if (nest[p].mode_field == mmode) { + print_cmd_chr(eq_no_cmd, saved_value(-2)); + goto FOUND; + } + print_char('$'); + goto FOUND; + break; + case math_left_group: + if (subtype(nest[p + 1].eTeX_aux_field) == left_noad_side) + tprint_esc("left"); + else + tprint_esc("middle"); + goto FOUND; + break; + default: + confusion("showgroups"); + break; + } + /* Show the box context */ + i = saved_value(-5); + if (i != 0) { + if (i < box_flag) { + if (abs(nest[p].mode_field) == vmode) + j = hmove_cmd; + else + j = vmove_cmd; + if (i > 0) + print_cmd_chr(j, 0); + else + print_cmd_chr(j, 1); + print_scaled(abs(i)); + tprint("pt"); + } else if (i < ship_out_flag) { + if (i >= global_box_flag) { + tprint_esc("global"); + i = i - (global_box_flag - box_flag); + } + tprint_esc("setbox"); + print_int(i - box_flag); + print_char('='); + } else { + print_cmd_chr(leader_ship_cmd, i - (leader_flag - a_leaders)); + } + } + FOUND1: + tprint_esc(s); + /* Show the box packaging info */ + { + /* offsets may vary */ + int ii = -1; + while (saved_type(ii) != saved_boxspec) + ii--; + if (saved_value(ii) != 0) { + print_char(' '); + if (saved_level(ii) == exactly) + tprint("to"); + else + tprint("spread"); + print_scaled(saved_value(ii)); + tprint("pt"); + } + } + FOUND2: + print_char('{'); + FOUND: + print_char(')'); + decr(cur_level); + cur_group = save_level(save_ptr); + save_ptr = save_value(save_ptr); + } + DONE: + save_ptr = v; + cur_level = l; + cur_group = c; +} + + + +@ Just before an entry of |eqtb| is changed, the following procedure should +be called to update the other data structures properly. It is important +to keep in mind that reference counts in |mem| include references from +within |save_stack|, so these counts must be handled carefully. +@^reference counts@> + +@c +void eq_destroy(memory_word w) +{ /* gets ready to forget |w| */ + halfword q; /* |equiv| field of |w| */ + switch (eq_type_field(w)) { + case call_cmd: + case long_call_cmd: + case outer_call_cmd: + case long_outer_call_cmd: + delete_token_ref(equiv_field(w)); + break; + case glue_ref_cmd: + delete_glue_ref(equiv_field(w)); + break; + case shape_ref_cmd: + q = equiv_field(w); /* we need to free a \.{\\parshape} block */ + if (q != null) + flush_node(q); + break; /* such a block is |2n+1| words long, where |n=vinfo(q)| */ + case box_ref_cmd: + flush_node_list(equiv_field(w)); + break; + default: + break; + } +} + + +@ To save a value of |eqtb[p]| that was established at level |l|, we +can use the following subroutine. + +@c +void eq_save(halfword p, quarterword l) +{ /* saves |eqtb[p]| */ + check_full_save_stack(); + if (l == level_zero) { + save_type(save_ptr) = restore_zero; + } else { + save_word(save_ptr) = eqtb[p]; + save_type(save_ptr) = saved_eqtb; + incr(save_ptr); + save_type(save_ptr) = restore_old_value; + } + save_level(save_ptr) = l; + save_value(save_ptr) = p; + incr(save_ptr); +} + + +@ The procedure |eq_define| defines an |eqtb| entry having specified +|eq_type| and |equiv| fields, and saves the former value if appropriate. +This procedure is used only for entries in the first four regions of |eqtb|, +i.e., only for entries that have |eq_type| and |equiv| fields. +After calling this routine, it is safe to put four more entries on +|save_stack|, provided that there was room for four more entries before +the call, since |eq_save| makes the necessary test. + +@c +void eq_define(halfword p, quarterword t, halfword e) +{ /* new data for |eqtb| */ + if ((eq_type(p) == t) && (equiv(p) == e)) { + assign_trace(p, "reassigning"); + eq_destroy(eqtb[p]); + return; + } + assign_trace(p, "changing"); + if (eq_level(p) == cur_level) + eq_destroy(eqtb[p]); + else if (cur_level > level_one) + eq_save(p, eq_level(p)); + set_eq_level(p, cur_level); + set_eq_type(p, t); + set_equiv(p, e); + assign_trace(p, "into"); +} + + +@ The counterpart of |eq_define| for the remaining (fullword) positions in +|eqtb| is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all +|p|, a `|restore_zero|' will never be used in this case. + +@c +void eq_word_define(halfword p, int w) +{ + if (eqtb[p].cint == w) { + assign_trace(p, "reassigning"); + return; + } + assign_trace(p, "changing"); + if (xeq_level[p] != cur_level) { + eq_save(p, xeq_level[p]); + xeq_level[p] = cur_level; + } + eqtb[p].cint = w; + assign_trace(p, "into"); +} + + +@ The |eq_define| and |eq_word_define| routines take care of local definitions. +@^global definitions@> +Global definitions are done in almost the same way, but there is no need +to save old values, and the new value is associated with |level_one|. + +@c +void geq_define(halfword p, quarterword t, halfword e) +{ /* global |eq_define| */ + assign_trace(p, "globally changing"); + eq_destroy(eqtb[p]); + set_eq_level(p, level_one); + set_eq_type(p, t); + set_equiv(p, e); + assign_trace(p, "into"); +} + +void geq_word_define(halfword p, int w) +{ /* global |eq_word_define| */ + assign_trace(p, "globally changing"); + eqtb[p].cint = w; + xeq_level[p] = level_one; + assign_trace(p, "into"); +} + + +@ Subroutine |save_for_after| puts a token on the stack for save-keeping. + +@c +void save_for_after(halfword t) +{ + if (cur_level > level_one) { + check_full_save_stack(); + save_type(save_ptr) = insert_token; + save_level(save_ptr) = level_zero; + save_value(save_ptr) = t; + incr(save_ptr); + } +} + + +@ The |unsave| routine goes the other way, taking items off of |save_stack|. +This routine takes care of restoration when a level ends; everything +belonging to the topmost group is cleared off of the save stack. + +@c +void unsave(void) +{ /* pops the top level off the save stack */ + halfword p; /* position to be restored */ + quarterword l; /* saved level, if in fullword regions of |eqtb| */ + boolean a; /* have we already processed an \.{\\aftergroup} ? */ + a = false; + l = level_one; /* just in case */ + unsave_math_codes(cur_level); + unsave_cat_codes(int_par(cat_code_table_code), cur_level); + unsave_text_codes(cur_level); + unsave_math_data(cur_level); + if (cur_level > level_one) { + decr(cur_level); + /* Clear off top level from |save_stack| */ + while (true) { + decr(save_ptr); + if (save_type(save_ptr) == level_boundary) + break; + p = save_value(save_ptr); + if (save_type(save_ptr) == insert_token) { + a = reinsert_token(a, p); + } else { + if (save_type(save_ptr) == restore_old_value) { + l = save_level(save_ptr); + decr(save_ptr); + } else { + save_word(save_ptr) = eqtb[undefined_control_sequence]; + } + /* Store |save_stack[save_ptr]| in |eqtb[p]|, unless + |eqtb[p]| holds a global value */ + /* A global definition, which sets the level to |level_one|, + will not be undone by |unsave|. If at least one global definition of + |eqtb[p]| has been carried out within the group that just ended, the + last such definition will therefore survive. + */ + if (p < int_base || p > eqtb_size) { + if (eq_level(p) == level_one) { + eq_destroy(save_word(save_ptr)); /* destroy the saved value */ + if (int_par(tracing_restores_code) > 0) + restore_trace(p, "retaining"); + } else { + eq_destroy(eqtb[p]); /* destroy the current value */ + eqtb[p] = save_word(save_ptr); /* restore the saved value */ + if (int_par(tracing_restores_code) > 0) + restore_trace(p, "restoring"); + } + } else if (xeq_level[p] != level_one) { + eqtb[p] = save_word(save_ptr); + xeq_level[p] = l; + if (int_par(tracing_restores_code) > 0) + restore_trace(p, "restoring"); + } else { + if (int_par(tracing_restores_code) > 0) + restore_trace(p, "retaining"); + } + + } + } + if (int_par(tracing_groups_code) > 0) + group_trace(true); + if (grp_stack[in_open] == cur_boundary) + group_warning(); /* groups possibly not properly nested with files */ + cur_group = save_level(save_ptr); + cur_boundary = save_value(save_ptr); + decr(save_ptr); + + } else { + confusion("curlevel"); /* |unsave| is not used when |cur_group=bottom_level| */ + } + attr_list_cache = cache_disabled; +} + +@ @c +void restore_trace(halfword p, const char *s) +{ /* |eqtb[p]| has just been restored or retained */ + begin_diagnostic(); + print_char('{'); + tprint(s); + print_char(' '); + show_eqtb(p); + print_char('}'); + end_diagnostic(false); +} + + +@ Most of the parameters kept in |eqtb| can be changed freely, but there's +an exception: The magnification should not be used with two different +values during any \TeX\ job, since a single magnification is applied to an +entire run. The global variable |mag_set| is set to the current magnification +whenever it becomes necessary to ``freeze'' it at a particular value. + +@c +int mag_set; /* if nonzero, this magnification should be used henceforth */ + + +@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| +for magnification. + +@c +#define mag int_par(mag_code) + +void prepare_mag(void) +{ + if ((mag_set > 0) && (mag != mag_set)) { + print_err("Incompatible magnification ("); + print_int(mag); + tprint(");"); + tprint_nl(" the previous value will be retained"); + help2("I can handle only one magnification ratio per job. So I've", + "reverted to the magnification you used earlier on this run."); + int_error(mag_set); + geq_word_define(int_base + mag_code, mag_set); /* |mag:=mag_set| */ + } + if ((mag <= 0) || (mag > 32768)) { + print_err("Illegal magnification has been changed to 1000"); + help1("The magnification ratio must be between 1 and 32768."); + int_error(mag); + geq_word_define(int_base + mag_code, 1000); + } + if ((mag_set == 0) && (mag != mag_set)) { + if (mag != 1000) + one_true_inch = xn_over_d(one_hundred_inch, 10, mag); + else + one_true_inch = one_inch; + } + mag_set = mag; +} + + +@ Let's pause a moment now and try to look at the Big Picture. +The \TeX\ program consists of three main parts: syntactic routines, +semantic routines, and output routines. The chief purpose of the +syntactic routines is to deliver the user's input to the semantic routines, +one token at a time. The semantic routines act as an interpreter +responding to these tokens, which may be regarded as commands. And the +output routines are periodically called on to convert box-and-glue +lists into a compact set of instructions that will be sent +to a typesetter. We have discussed the basic data structures and utility +routines of \TeX, so we are good and ready to plunge into the real activity by +considering the syntactic routines. + +Our current goal is to come to grips with the |get_next| procedure, +which is the keystone of \TeX's input mechanism. Each call of |get_next| +sets the value of three variables |cur_cmd|, |cur_chr|, and |cur_cs|, +representing the next input token. +$$\vbox{\halign{#\hfil\cr + \hbox{|cur_cmd| denotes a command code from the long list of codes + given above;}\cr + \hbox{|cur_chr| denotes a character code or other modifier of the command + code;}\cr + \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr + \hbox{\qquad if the current token was a control sequence, + otherwise it's zero.}\cr}}$$ +Underlying this external behavior of |get_next| is all the machinery +necessary to convert from character files to tokens. At a given time we +may be only partially finished with the reading of several files (for +which \.{\\input} was specified), and partially finished with the expansion +of some user-defined macros and/or some macro parameters, and partially +finished with the generation of some text in a template for \.{\\halign}, +and so on. When reading a character file, special characters must be +classified as math delimiters, etc.; comments and extra blank spaces must +be removed, paragraphs must be recognized, and control sequences must be +found in the hash table. Furthermore there are occasions in which the +scanning routines have looked ahead for a word like `\.{plus}' but only +part of that word was found, hence a few characters must be put back +into the input and scanned again. + +To handle these situations, which might all be present simultaneously, +\TeX\ uses various stacks that hold information about the incomplete +activities, and there is a finite state control for each level of the +input mechanism. These stacks record the current state of an implicitly +recursive process, but the |get_next| procedure is not recursive. +Therefore it will not be difficult to translate these algorithms into +low-level languages that do not support recursion. + +@c +int cur_cmd; /* current command set by |get_next| */ +halfword cur_chr; /* operand of current command */ +halfword cur_cs; /* control sequence found here, zero if none found */ +halfword cur_tok; /* packed representative of |cur_cmd| and |cur_chr| */ + +@ Here is a procedure that displays the current command. + +@c +#define mode cur_list.mode_field + +void show_cur_cmd_chr(void) +{ + int n; /* level of \.{\\if...\\fi} nesting */ + int l; /* line where \.{\\if} started */ + halfword p; + begin_diagnostic(); + tprint_nl("{"); + if (mode != shown_mode) { + print_mode(mode); + tprint(": "); + shown_mode = mode; + } + print_cmd_chr((quarterword) cur_cmd, cur_chr); + if (int_par(tracing_ifs_code) > 0) { + if (cur_cmd >= if_test_cmd) { + if (cur_cmd <= fi_or_else_cmd) { + tprint(": "); + if (cur_cmd == fi_or_else_cmd) { + print_cmd_chr(if_test_cmd, cur_if); + print_char(' '); + n = 0; + l = if_line; + } else { + n = 1; + l = line; + } + p = cond_ptr; + while (p != null) { + incr(n); + p = vlink(p); + } + tprint("(level "); + print_int(n); + print_char(')'); + print_if_line(l); + } + } + } + print_char('}'); + end_diagnostic(false); +} + + +@ Here is a procedure that displays the contents of |eqtb[n]| + symbolically. + +@c +void show_eqtb(halfword n) +{ + if (n < null_cs) { + print_char('?'); /* this can't happen */ + } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) { + /* Show equivalent |n|, in region 1 or 2 */ + /* Here is a routine that displays the current meaning of an |eqtb| entry + in region 1 or~2. (Similar routines for the other regions will appear + below.) */ + + sprint_cs(n); + print_char('='); + print_cmd_chr(eq_type(n), equiv(n)); + if (eq_type(n) >= call_cmd) { + print_char(':'); + show_token_list(token_link(equiv(n)), null, 32); + } + } else if (n < local_base) { + /* Show equivalent |n|, in region 3 */ + /* All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'. */ + if (n < skip_base) { + if (n < glue_base + thin_mu_skip_code) + print_cmd_chr(assign_glue_cmd, n); + else + print_cmd_chr(assign_mu_glue_cmd, n); + print_char('='); + if (n < glue_base + thin_mu_skip_code) + print_spec(equiv(n), "pt"); + else + print_spec(equiv(n), "mu"); + } else if (n < mu_skip_base) { + tprint_esc("skip"); + print_int(n - skip_base); + print_char('='); + print_spec(equiv(n), "pt"); + } else { + tprint_esc("muskip"); + print_int(n - mu_skip_base); + print_char('='); + print_spec(equiv(n), "mu"); + } + + } else if (n < int_base) { + /* Show equivalent |n|, in region 4 */ + /* We initialize most things to null or undefined values. An undefined font + is represented by the internal code |font_base|. + + However, the character code tables are given initial values based on the + conventional interpretation of ASCII code. These initial values should + not be changed when \TeX\ is adapted for use with non-English languages; + all changes to the initialization conventions should be made in format + packages, not in \TeX\ itself, so that global interchange of formats is + possible. */ + if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) { + if (n == par_shape_loc) + print_cmd_chr(set_tex_shape_cmd, n); + else + print_cmd_chr(set_etex_shape_cmd, n); + print_char('='); + if (equiv(n) == null) { + print_char('0'); + } else if (n > par_shape_loc) { + print_int(penalty(equiv(n))); + print_char(' '); + print_int(penalty(equiv(n) + 1)); + if (penalty(equiv(n)) > 1) + tprint_esc("ETC."); + } else { + print_int(vinfo(par_shape_ptr + 1)); + } + } else if (n < toks_base) { + /* TODO make extra cases for ocps here! */ + print_cmd_chr(assign_toks_cmd, n); + print_char('='); + if (equiv(n) != null) + show_token_list(token_link(equiv(n)), null, 32); + } else if (n < box_base) { + tprint_esc("toks"); + print_int(n - toks_base); + print_char('='); + if (equiv(n) != null) + show_token_list(token_link(equiv(n)), null, 32); + } else if (n < cur_font_loc) { + tprint_esc("box"); + print_int(n - box_base); + print_char('='); + if (equiv(n) == null) { + tprint("void"); + } else { + depth_threshold = 0; + breadth_max = 1; + show_node_list(equiv(n)); + } + } else if (n == cur_font_loc) { + /* Show the font identifier in |eqtb[n]| */ + tprint("current font"); + print_char('='); + print_esc(hash[font_id_base + equiv(n)].rh); /* that's |font_id_text(equiv(n))| */ + } + + } else if (n < dimen_base) { + /* Show equivalent |n|, in region 5 */ + if (n < dir_base) { + print_cmd_chr(assign_int_cmd, n); + print_char('='); + print_int(eqtb[n].cint); + } else if (n < count_base) { + print_cmd_chr(assign_dir_cmd, n); + print_char(' '); + print_dir(eqtb[n].cint); + } else if (n < attribute_base) { + tprint_esc("count"); + print_int(n - count_base); + print_char('='); + print_int(eqtb[n].cint); + } else if (n < del_code_base) { + tprint_esc("attribute"); + print_int(n - attribute_base); + print_char('='); + print_int(eqtb[n].cint); + } + + } else if (n <= eqtb_size) { + /* Show equivalent |n|, in region 6 */ + if (n < scaled_base) { + print_cmd_chr(assign_dimen_cmd, n); + } else { + tprint_esc("dimen"); + print_int(n - scaled_base); + } + print_char('='); + print_scaled(eqtb[n].cint); + tprint("pt"); + + } else { + print_char('?'); /* this can't happen either */ + } +} + +@ @c +void show_eqtb_meaning(halfword n) +{ + if (n < null_cs) { + print_char('?'); /* this can't happen */ + } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) { + /* Show equivalent |n|, in region 1 or 2 */ + /* Here is a routine that displays the current meaning of an |eqtb| entry + in region 1 or~2. (Similar routines for the other regions will appear + below.) */ + + sprint_cs(n); + } else if (n < local_base) { + /* Show equivalent |n|, in region 3 */ + /* All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'. */ + if (n < skip_base) { + if (n < glue_base + thin_mu_skip_code) + print_cmd_chr(assign_glue_cmd, n); + else + print_cmd_chr(assign_mu_glue_cmd, n); + } else if (n < mu_skip_base) { + tprint_esc("skip"); + print_int(n - skip_base); + } else { + tprint_esc("muskip"); + print_int(n - mu_skip_base); + } + + } else if (n < int_base) { + /* Show equivalent |n|, in region 4 */ + /* We initialize most things to null or undefined values. An undefined font + is represented by the internal code |font_base|. + + However, the character code tables are given initial values based on the + conventional interpretation of ASCII code. These initial values should + not be changed when \TeX\ is adapted for use with non-English languages; + all changes to the initialization conventions should be made in format + packages, not in \TeX\ itself, so that global interchange of formats is + possible. */ + if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) { + if (n == par_shape_loc) + print_cmd_chr(set_tex_shape_cmd, n); + else + print_cmd_chr(set_etex_shape_cmd, n); + } else if (n < toks_base) { + /* TODO make extra cases for ocps here! */ + print_cmd_chr(assign_toks_cmd, n); + } else if (n < box_base) { + tprint_esc("toks"); + print_int(n - toks_base); + } else if (n < cur_font_loc) { + tprint_esc("box"); + print_int(n - box_base); + } else if (n == cur_font_loc) { + /* Show the font identifier in |eqtb[n]| */ + tprint("current font"); + } + + } else if (n < dimen_base) { + /* Show equivalent |n|, in region 5 */ + if (n < dir_base) { + print_cmd_chr(assign_int_cmd, n); + } else if (n < count_base) { + print_cmd_chr(assign_dir_cmd, n); + } else if (n < attribute_base) { + tprint_esc("count"); + print_int(n - count_base); + } else if (n < del_code_base) { + tprint_esc("attribute"); + print_int(n - attribute_base); + } + + } else if (n <= eqtb_size) { + /* Show equivalent |n|, in region 6 */ + if (n < scaled_base) { + print_cmd_chr(assign_dimen_cmd, n); + } else { + tprint_esc("dimen"); + print_int(n - scaled_base); + } + } else { + print_char('?'); /* this can't happen either */ + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.h b/Build/source/texk/web2c/luatexdir/tex/errors.h new file mode 100644 index 00000000000..2d55e31aa75 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/errors.h @@ -0,0 +1,102 @@ +/* errors.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: errors.h 3385 2010-01-26 05:45:55Z taco $ */ + +#ifndef ERRORS_H +# define ERRORS_H + +/* +The global variable |interaction| has four settings, representing increasing +amounts of user interaction: +*/ + +typedef enum { + batch_mode = 0, /* omits all stops and omits terminal output */ + nonstop_mode = 1, /* omits all stops */ + scroll_mode = 2, /* omits error stops */ + error_stop_mode = 3, /* stops at every opportunity to interact */ + unspecified_mode = 4, /* extra value for command-line switch */ +} interaction_settings; + +extern int interaction; /* current level of interaction */ +extern int interactionoption; /* set from command line */ +extern const char *last_error; + +extern void initialize_errors(void); +extern void print_err(const char *s); + +extern void fixup_selector(boolean log_opened); + +extern boolean deletions_allowed; +extern boolean set_box_allowed; +extern int history; +extern int error_count; +extern int interrupt; +extern boolean OK_to_interrupt; + +typedef enum { + spotless = 0, /* |history| value when nothing has been amiss yet */ + warning_issued = 1, /* |history| value when |begin_diagnostic| has been called */ + error_message_issued = 2, /* |history| value when |error| has been called */ + fatal_error_stop = 3, /* |history| value when termination was premature */ +} error_states; + +extern const char *help_line[7]; /* helps for the next |error| */ +extern boolean use_err_help; /* should the |err_help| list be shown? */ + +/* these macros are just temporary, until everything is in C */ + +# define hlp1(A) help_line[0]=A +# define hlp2(A,B) help_line[1]=A; hlp1(B) +# define hlp3(A,B,C) help_line[2]=A; hlp2(B,C) +# define hlp4(A,B,C,D) help_line[3]=A; hlp3(B,C,D) +# define hlp5(A,B,C,D,E) help_line[4]=A; hlp4(B,C,D,E) +# define hlp6(A,B,C,D,E,F) help_line[5]=A; hlp5(B,C,D,E,F) + +# define help0() help_line[0]=NULL /* sometimes there might be no help */ +# define help1(A) do { help_line[1]=NULL; hlp1(A); } while (0) +# define help2(A,B) do { help_line[2]=NULL; hlp2(B,A); } while (0) +# define help3(A,B,C) do { help_line[3]=NULL; hlp3(C,B,A); } while (0) +# define help4(A,B,C,D) do { help_line[4]=NULL; hlp4(D,C,B,A); } while (0) +# define help5(A,B,C,D,E) do { help_line[5]=NULL; hlp5(E,D,C,B,A); } while (0) +# define help6(A,B,C,D,E,F) do { help_line[6]=NULL; hlp6(F,E,D,C,B,A); } while (0) + +extern void do_final_end(void); +extern void jump_out(void); +extern void error(void); +extern void int_error(int n); +extern void normalize_selector(void); +extern void succumb(void); +extern void fatal_error(const char *s); +extern void lua_norm_error(const char *s); +extern void lua_fatal_error(const char *s); +extern void overflow(const char *s, unsigned int n); +extern void confusion(const char *s); +extern void check_interrupt(void); +extern void pause_for_instructions(void); + +extern void tex_error(const char *msg, const char **hlp); + +extern void back_error(void); +extern void ins_error(void); + +extern void char_warning(internal_font_number f, int c); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.w b/Build/source/texk/web2c/luatexdir/tex/errors.w new file mode 100644 index 00000000000..5565605e6a2 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/errors.w @@ -0,0 +1,625 @@ +% errors.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: errors.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/errors.w $"; + +@ @c +#define new_line_char int_par(new_line_char_code) + + +@ When something anomalous is detected, \TeX\ typically does something like this: +$$\vbox{\halign{#\hfil\cr +|print_err("Something anomalous has been detected");|\cr +|help3("This is the first line of my offer to help.")|\cr +|("This is the second line. I'm trying to")|\cr +|("explain the best way for you to proceed.");|\cr +|error;|\cr}}$$ +A two-line help message would be given using |help2|, etc.; these informal +helps should use simple vocabulary that complements the words used in the +official error message that was printed. (Outside the U.S.A., the help +messages should preferably be translated into the local vernacular. Each +line of help is at most 60 characters long, in the present implementation, +so that |max_print_line| will not be exceeded.) + +The |print_err| procedure supplies a `\.!' before the official message, +and makes sure that the terminal is awake if a stop is going to occur. +The |error| procedure supplies a `\..' after the official message, then it +shows the location of the error; and if |interaction=error_stop_mode|, +it also enters into a dialog with the user, during which time the help +message may be printed. +@^system dependencies@> + +@c +int interaction; /* current level of interaction */ +int interactionoption; /* set from command line */ +const char *last_error; + +void print_err(const char *s) +{ + if (interaction == error_stop_mode) { + wake_up_terminal(); + } + if (filelineerrorstylep) + print_file_line(); + else + tprint_nl("! "); + tprint(s); + last_error = (const char *) s; +} + + +@ \TeX\ is careful not to call |error| when the print |selector| setting +might be unusual. The only possible values of |selector| at the time of +error messages are + +\yskip\hang|no_print| (when |interaction=batch_mode| + and |log_file| not yet open); + +\hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open); + +\hang|log_only| (when |interaction=batch_mode| and |log_file| is open); + +\hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open). + +@c +void fixup_selector(boolean log_opened) +{ + if (interaction == batch_mode) + selector = no_print; + else + selector = term_only; + if (log_opened) + selector = selector + 2; +} + + +@ A global variable |deletions_allowed| is set |false| if the |get_next| +routine is active when |error| is called; this ensures that |get_next| +and related routines like |get_token| will never be called recursively. +A similar interlock is provided by |set_box_allowed|. +@^recursion@> + +The global variable |history| records the worst level of error that +has been detected. It has four possible values: |spotless|, |warning_issued|, +|error_message_issued|, and |fatal_error_stop|. + +Another global variable, |error_count|, is increased by one when an +|error| occurs without an interactive dialog, and it is reset to zero at +the end of every paragraph. If |error_count| reaches 100, \TeX\ decides +that there is no point in continuing further. + +@c +boolean deletions_allowed; /* is it safe for |error| to call |get_token|? */ +boolean set_box_allowed; /* is it safe to do a \.{\\setbox} assignment? */ +int history; /* has the source input been clean so far? */ +int error_count; /* the number of scrolled errors since the last paragraph ended */ +int interrupt; /* should \TeX\ pause for instructions? */ +boolean OK_to_interrupt; /* should interrupts be observed? */ + + + +@ The value of |history| is initially |fatal_error_stop|, but it will +be changed to |spotless| if \TeX\ survives the initialization process. + +@c +void initialize_errors(void) +{ + if (interactionoption == unspecified_mode) + interaction = error_stop_mode; + else + interaction = interactionoption; + deletions_allowed = true; + set_box_allowed = true; + OK_to_interrupt = true; + /* |history| is initialized elsewhere */ +} + + +@ It is possible for |error| to be called recursively if some error arises +when |get_token| is being used to delete a token, and/or if some fatal error +occurs while \TeX\ is trying to fix a non-fatal one. But such recursion +@^recursion@> +is never more than two levels deep. + +@ Individual lines of help are recorded in the array |help_line|. + +@c +const char *help_line[7]; /* helps for the next |error| */ +boolean use_err_help; /* should the |err_help| list be shown? */ + + +@ The |jump_out| procedure just cuts across all active procedure levels and +exits the program. It is used when there is no recovery from a particular error. + +@c +__attribute__ ((noreturn)) +void do_final_end(void) +{ + update_terminal(); + ready_already = 0; + if ((history != spotless) && (history != warning_issued)) + uexit(1); + else + uexit(0); +} + +__attribute__ ((noreturn)) +void jump_out(void) +{ + close_files_and_terminate(); + do_final_end(); +} + +@ @c +void error(void) +{ /* completes the job of error reporting */ + ASCII_code c; /* what the user types */ + int callback_id; + int s1, s2, s3, s4; /* used to save global variables when deleting tokens */ + boolean t; + int i; + if (history < error_message_issued) + history = error_message_issued; + print_char('.'); + callback_id = callback_defined(show_error_hook_callback); + if (callback_id > 0) + t = run_callback(callback_id, "->"); + show_context(); + if (haltonerrorp) { + history = fatal_error_stop; + jump_out(); + } + if (interaction == error_stop_mode) { + /* Get user's advice and |return| */ + while (1) { + CONTINUE: + clear_for_error_prompt(); + prompt_input("? "); + if (last == first) + return; + c = buffer[first]; + if (c >= 'a') + c = c + 'A' - 'a'; /* convert to uppercase */ + /* Interpret code |c| and |return| if done */ + + /* It is desirable to provide an `\.E' option here that gives the user + an easy way to return from \TeX\ to the system editor, with the offending + line ready to be edited. But such an extension requires some system + wizardry, so the present implementation simply types out the name of the + file that should be edited and the relevant line number. + + There is a secret `\.D' option available when the debugging routines haven't + been commented~out. */ + + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (deletions_allowed) { + /* Delete |c-"0"| tokens and |goto continue| */ + /* We allow deletion of up to 99 tokens at a time */ + s1 = cur_tok; + s2 = cur_cmd; + s3 = cur_chr; + s4 = align_state; + align_state = 1000000; + OK_to_interrupt = false; + if ((last > first + 1) && (buffer[first + 1] >= '0') + && (buffer[first + 1] <= '9')) + c = c * 10 + buffer[first + 1] - '0' * 11; + else + c = c - '0'; + while (c > 0) { + get_token(); /* one-level recursive call of |error| is possible */ + decr(c); + } + cur_tok = s1; + cur_cmd = s2; + cur_chr = s3; + align_state = s4; + OK_to_interrupt = true; + help2("I have just deleted some text, as you asked.", + "You can now delete more, or insert, or whatever."); + show_context(); + goto CONTINUE; + } + break; +#ifdef DEBUG + case 'D': + debug_help(); + goto CONTINUE; + break; +#endif + case 'E': + if (base_ptr > 0) { + tprint_nl("You want to edit file "); + slow_print(input_stack[base_ptr].name_field); + tprint(" at line "); + print_int(line); + interaction = scroll_mode; + jump_out(); + } + break; + case 'H': + /* Print the help information and |goto continue| */ + if (use_err_help) { + give_err_help(); + use_err_help = false; + } else { + if (help_line[0] == NULL) { + help2 + ("Sorry, I don't know how to help in this situation.", + "Maybe you should try asking a human?"); + } + i = 0; + while (help_line[i] != NULL) + tprint_nl(help_line[i++]); + help4("Sorry, I already gave what help I could...", + "Maybe you should try asking a human?", + "An error might have occurred before I noticed any problems.", + "``If all else fails, read the instructions.''"); + goto CONTINUE; + } + break; + case 'I': + /* Introduce new material from the terminal and |return| */ + /* When the following code is executed, |buffer[(first+1)..(last-1)]| may + contain the material inserted by the user; otherwise another prompt will + be given. In order to understand this part of the program fully, you need + to be familiar with \TeX's input stacks. */ + + begin_file_reading(); /* enter a new syntactic level for terminal input */ + /* now |state=mid_line|, so an initial blank space will count as a blank */ + if (last > first + 1) { + iloc = first + 1; + buffer[first] = ' '; + } else { + prompt_input("insert>"); + iloc = first; + } + first = last; + ilimit = last - 1; /* no |end_line_char| ends this line */ + return; + break; + case 'Q': + case 'R': + case 'S': + /* Change the interaction level and |return| */ + /* Here the author of \TeX\ apologizes for making use of the numerical + relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings + |batch_mode|, |nonstop_mode|, |scroll_mode|. */ + error_count = 0; + interaction = batch_mode + c - 'Q'; + tprint("OK, entering "); + switch (c) { + case 'Q': + tprint_esc("batchmode"); + decr(selector); + break; + case 'R': + tprint_esc("nonstopmode"); + break; + case 'S': + tprint_esc("scrollmode"); + break; + } + tprint("..."); + print_ln(); + update_terminal(); + return; + break; + case 'X': + interaction = scroll_mode; + jump_out(); + break; + default: + break; + } + /* Print the menu of available options */ + tprint + ("Type <return> to proceed, S to scroll future error messages,"); + tprint_nl("R to run without stopping, Q to run quietly,"); + tprint_nl("I to insert something, "); + if (base_ptr > 0) + tprint("E to edit your file,"); + if (deletions_allowed) + tprint_nl + ("1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); + tprint_nl("H for help, X to quit."); + } + + } + incr(error_count); + if (error_count == 100) { + tprint_nl("(That makes 100 errors; please try again.)"); + history = fatal_error_stop; + jump_out(); + } + /* Put help message on the transcript file */ + if (interaction > batch_mode) + decr(selector); /* avoid terminal output */ + if (use_err_help) { + print_ln(); + give_err_help(); + } else { + int i = 0; + while (help_line[i] != NULL) + tprint_nl(help_line[i++]); + } + print_ln(); + if (interaction > batch_mode) + incr(selector); /* re-enable terminal output */ + print_ln(); +} + + + +@ A dozen or so error messages end with a parenthesized integer, so we +save a teeny bit of program space by declaring the following procedure: + +@c +void int_error(int n) +{ + tprint(" ("); + print_int(n); + print_char(')'); + error(); +} + + +@ In anomalous cases, the print selector might be in an unknown state; +the following subroutine is called to fix things just enough to keep +running a bit longer. + +@c +void normalize_selector(void) +{ + if (log_opened) + selector = term_and_log; + else + selector = term_only; + if (job_name == 0) + open_log_file(); + if (interaction == batch_mode) + decr(selector); +} + +@ The following procedure prints \TeX's last words before dying +@c +void succumb(void) +{ + if (interaction == error_stop_mode) + interaction = scroll_mode; /* no more interaction */ + if (log_opened) + error(); +#ifdef DEBUG + if (interaction > batch_mode) + debug_help(); +#endif + history = fatal_error_stop; + jump_out(); /* irrecoverable error */ +} + +@ @c +void fatal_error(const char *s) +{ /* prints |s|, and that's it */ + normalize_selector(); + print_err("Emergency stop"); + help1(s); + succumb(); +} + +@ @c +void lua_norm_error(const char *s) +{ /* lua found a problem */ + int saved_new_line_char; + saved_new_line_char = new_line_char; + new_line_char = 10; + print_err("LuaTeX error "); + tprint(s); + help2("The lua interpreter ran into a problem, so the", + "remainder of this lua chunk will be ignored."); + error(); + new_line_char = saved_new_line_char; +} + +@ @c +void lua_fatal_error(const char *s) +{ /* lua found a problem */ + new_line_char = 10; + normalize_selector(); + print_err("LuaTeX fatal error "); + tprint(s); + succumb(); +} + +@ Here is the most dreaded error message +@c +void overflow(const char *s, unsigned int n) +{ /* stop due to finiteness */ + normalize_selector(); + print_err("TeX capacity exceeded, sorry ["); + tprint(s); + print_char('='); + print_int((int) n); + print_char(']'); + help2("If you really absolutely need more capacity,", + "you can ask a wizard to enlarge me."); + succumb(); +} + + +@ The program might sometime run completely amok, at which point there is +no choice but to stop. If no previous error has been detected, that's bad +news; a message is printed that is really intended for the \TeX\ +maintenance person instead of the user (unless the user has been +particularly diabolical). The index entries for `this can't happen' may +help to pinpoint the problem. +@^dry rot@> + +@c +void confusion(const char *s) +{ /* consistency check violated; |s| tells where */ + normalize_selector(); + if (history < error_message_issued) { + print_err("This can't happen ("); + tprint(s); + print_char(')'); + help1("I'm broken. Please show this to someone who can fix can fix"); + } else { + print_err("I can't go on meeting you like this"); + help2("One of your faux pas seems to have wounded me deeply...", + "in fact, I'm barely conscious. Please fix it and try again."); + } + succumb(); +} + + +@ Users occasionally want to interrupt \TeX\ while it's running. +If the runtime system allows this, one can implement +a routine that sets the global variable |interrupt| to some nonzero value +when such an interrupt is signalled. Otherwise there is probably at least +a way to make |interrupt| nonzero using the debugger. +@^system dependencies@> +@^debugging@> + +@c +void check_interrupt(void) +{ + if (interrupt != 0) + pause_for_instructions(); +} + + + +@ When an interrupt has been detected, the program goes into its +highest interaction level and lets the user have nearly the full flexibility of +the |error| routine. \TeX\ checks for interrupts only at times when it is +safe to do this. + +@c +void pause_for_instructions(void) +{ + if (OK_to_interrupt) { + interaction = error_stop_mode; + if ((selector == log_only) || (selector == no_print)) + incr(selector); + print_err("Interruption"); + help3("You rang?", + "Try to insert some instructions for me (e.g.,`I\\showlists'),", + "unless you just want to quit by typing `X'."); + deletions_allowed = false; + error(); + deletions_allowed = true; + interrupt = 0; + } +} + +@ @c +void tex_error(const char *msg, const char **hlp) +{ + print_err(msg); + if (hlp != NULL) { + int i; + for (i = 0; (hlp[i] != NULL && i <= 5); i++) { + help_line[i] = hlp[i]; + } + help_line[i] = NULL; + } else { + help_line[0] = NULL; + } + error(); +} + +@ The |back_error| routine is used when we want to replace an offending token +just before issuing an error message. This routine, like |back_input|, +requires that |cur_tok| has been set. We disable interrupts during the +call of |back_input| so that the help message won't be lost. + +@c +void back_error(void) +{ /* back up one token and call |error| */ + OK_to_interrupt = false; + back_input(); + OK_to_interrupt = true; + error(); +} + +@ @c +void ins_error(void) +{ /* back up one inserted token and call |error| */ + OK_to_interrupt = false; + back_input(); + token_type = inserted; + OK_to_interrupt = true; + error(); +} + + + +@ When \TeX\ wants to typeset a character that doesn't exist, the +character node is not created; thus the output routine can assume +that characters exist when it sees them. The following procedure +prints a warning message unless the user has suppressed it. + +@c +void char_warning(internal_font_number f, int c) +{ + int old_setting; /* saved value of |tracing_online| */ + int k; /* index to current digit; we assume that $0\L n<16^{22}$ */ + if (int_par(tracing_lost_chars_code) > 0) { + old_setting = int_par(tracing_online_code); + if (int_par(tracing_lost_chars_code) > 1) + int_par(tracing_online_code) = 1; + begin_diagnostic(); + tprint_nl("Missing character: There is no "); + print(c); + tprint(" (U+"); + k = 0; + if (c < 16) + print_char('0'); + if (c < 256) + print_char('0'); + if (c < 4096) + print_char('0'); + do { + dig[k] = c % 16; + c = c / 16; + incr(k); + } while (c != 0); + print_the_digs((eight_bits) k); + tprint(") in font "); + print_font_name(f); + print_char('!'); + end_diagnostic(false); + int_par(tracing_online_code) = old_setting; + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.h b/Build/source/texk/web2c/luatexdir/tex/expand.h new file mode 100644 index 00000000000..ce1af9f552a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/expand.h @@ -0,0 +1,102 @@ +/* expand.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: expand.h 2728 2009-07-04 11:33:51Z oneiros $ */ + +#ifndef EXPAND_H +# define EXPAND_H + +extern boolean is_in_csname; +extern void expand(void); +extern void complain_missing_csname(void); +extern void manufacture_csname(void); +extern void insert_relax(void); +extern void get_x_token(void); +extern void x_token(void); + +# define top_mark_code 0 /* the mark in effect at the previous page break */ +# define first_mark_code 1 /* the first mark between |top_mark| and |bot_mark| */ +# define bot_mark_code 2 /* the mark in effect at the current page break */ +# define split_first_mark_code 3 + /* the first mark found by \.{\\vsplit} */ +# define split_bot_mark_code 4 /* the last mark found by \.{\\vsplit} */ +# define marks_code 5 + +# define top_mark(A) top_marks_array[(A)] +# define first_mark(A) first_marks_array[(A)] +# define bot_mark(A) bot_marks_array[(A)] +# define split_first_mark(A) split_first_marks_array[(A)] +# define split_bot_mark(A) split_bot_marks_array[(A)] + +# define set_top_mark(A,B) top_mark(A)=(B) +# define set_first_mark(A,B) first_mark(A)=(B) +# define set_bot_mark(A,B) bot_mark(A)=(B) +# define set_split_first_mark(A,B) split_first_mark(A)=(B) +# define set_split_bot_mark(A,B) split_bot_mark(A)=(B) + +# define delete_top_mark(A) do { \ + if (top_mark(A)!=null) \ + delete_token_ref(top_mark(A)); \ + top_mark(A)=null; \ + } while (0) + +# define delete_bot_mark(A) do { \ + if (bot_mark(A)!=null) \ + delete_token_ref(bot_mark(A)); \ + bot_mark(A)=null; \ + } while (0) + +# define delete_first_mark(A) do { \ + if (first_mark(A)!=null) \ + delete_token_ref(first_mark(A)); \ + first_mark(A)=null; \ + } while (0) + +# define delete_split_first_mark(A) do { \ + if (split_first_mark(A)!=null) \ + delete_token_ref(split_first_mark(A)); \ + split_first_mark(A)=null; \ + } while (0) + +# define delete_split_bot_mark(A) do { \ + if (split_bot_mark(A)!=null) \ + delete_token_ref(split_bot_mark(A)); \ + split_bot_mark(A)=null; \ +} while (0) + +# define clear_marks_code 1 + +# define biggest_mark 65535 + +extern halfword top_marks_array[(biggest_mark + 1)]; +extern halfword first_marks_array[(biggest_mark + 1)]; +extern halfword bot_marks_array[(biggest_mark + 1)]; +extern halfword split_first_marks_array[(biggest_mark + 1)]; +extern halfword split_bot_marks_array[(biggest_mark + 1)]; +extern halfword biggest_used_mark; + + +extern void initialize_marks(void); + +extern int long_state; +extern halfword pstack[9]; +extern void macro_call(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.w b/Build/source/texk/web2c/luatexdir/tex/expand.w new file mode 100644 index 00000000000..79072f7021e --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/expand.w @@ -0,0 +1,789 @@ +% expand.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: expand.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/expand.w $"; + + +@ Only a dozen or so command codes |>max_command| can possibly be returned by +|get_next|; in increasing order, they are |undefined_cs|, |expand_after|, +|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|, +|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and +|end_template|.{\emergencystretch=40pt\par} + +Sometimes, recursive calls to the following |expand| routine may +cause exhaustion of the run-time calling stack, resulting in +forced execution stops by the operating system. To diminish the chance +of this happening, a counter is used to keep track of the recursion +depth, in conjunction with a constant called |expand_depth|. + +Note that this does not catch all possible infinite recursion loops, +just the ones that exhaust the application calling stack. The +actual maximum value of |expand_depth| is outside of our control, but +the initial setting of |100| should be enough to prevent problems. +@^system dependencies@> + +@c +static int expand_depth_count = 0; + + +@ The |expand| subroutine is used when |cur_cmd>max_command|. It removes a +``call'' or a conditional or one of the other special operations just +listed. It follows that |expand| might invoke itself recursively. In all +cases, |expand| destroys the current token, but it sets things up so that +the next |get_next| will deliver the appropriate next token. The value of +|cur_tok| need not be known when |expand| is called. + +Since several of the basic scanning routines communicate via global variables, +their values are saved as local variables of |expand| so that +recursive calls don't invalidate them. +@^recursion@> + +@c +boolean is_in_csname = false; + +@ @c +void expand(void) +{ + halfword t; /* token that is being ``expanded after'' */ + halfword p; /* for list manipulation */ + halfword cur_ptr; /* for a local token list pointer */ + int cv_backup; /* to save the global quantity |cur_val| */ + int cvl_backup, radix_backup, co_backup; /* to save |cur_val_level|, etc. */ + halfword backup_backup; /* to save |link(backup_head)| */ + int save_scanner_status; /* temporary storage of |scanner_status| */ + incr(expand_depth_count); + if (expand_depth_count >= expand_depth) + overflow("expansion depth", (unsigned) expand_depth); + cv_backup = cur_val; + cvl_backup = cur_val_level; + radix_backup = radix; + co_backup = cur_order; + backup_backup = token_link(backup_head); + RESWITCH: + if (cur_cmd < call_cmd) { + /* Expand a nonmacro */ + if (int_par(tracing_commands_code) > 1) + show_cur_cmd_chr(); + switch (cur_cmd) { + case top_bot_mark_cmd: + /* Insert the appropriate mark text into the scanner */ + t = cur_chr % marks_code; + if (cur_chr >= marks_code) + scan_mark_num(); + else + cur_val = 0; + switch (t) { + case first_mark_code: + cur_ptr = first_mark(cur_val); + break; + case bot_mark_code: + cur_ptr = bot_mark(cur_val); + break; + case split_first_mark_code: + cur_ptr = split_first_mark(cur_val); + break; + case split_bot_mark_code: + cur_ptr = split_bot_mark(cur_val); + break; + default: + cur_ptr = top_mark(cur_val); + break; + } + if (cur_ptr != null) + begin_token_list(cur_ptr, mark_text); + break; + case expand_after_cmd: + if (cur_chr == 0) { + /* Expand the token after the next token */ + /* It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}. */ + get_token(); + t = cur_tok; + get_token(); + if (cur_cmd > max_command_cmd) + expand(); + else + back_input(); + cur_tok = t; + back_input(); + + } else { /* \\unless */ + /* Negate a boolean conditional and |goto reswitch| */ + /* The result of a boolean condition is reversed when the conditional is + preceded by \.{\\unless}. */ + get_token(); + if ((cur_cmd == if_test_cmd) && (cur_chr != if_case_code)) { + cur_chr = cur_chr + unless_code; + goto RESWITCH; + } + print_err("You can't use `\\unless' before `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help1("Continue, and I'll forget that it ever happened."); + back_error(); + } + break; + case no_expand_cmd: + if (cur_chr == 0) { + /* Suppress expansion of the next token */ + /* The implementation of \.{\\noexpand} is a bit trickier, because it is + necessary to insert a special `|dont_expand|' marker into \TeX's reading + mechanism. This special marker is processed by |get_next|, but it does + not slow down the inner loop. + + Since \.{\\outer} macros might arise here, we must also + clear the |scanner_status| temporarily. + */ + + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + t = cur_tok; + back_input(); /* now |start| and |loc| point to the backed-up token |t| */ + if (t >= cs_token_flag) { + p = get_avail(); + set_token_info(p, cs_token_flag + frozen_dont_expand); + set_token_link(p, iloc); + istart = p; + iloc = p; + } + + } else { + /* Implement \.{\\primitive} */ + /* + The \.{\\primitive} handling. If the primitive meaning of the next + token is an expandable command, it suffices to replace the current + token with the primitive one and restart |expand|. + + Otherwise, the token we just read has to be pushed back, as well + as a token matching the internal form of \.{\\primitive}, that is + sneaked in as an alternate form of |ignore_spaces|. + + An implementation problem surfaces: There really is no |cur_cs| + attached to the inserted primitive command, so it is safer to set + |cur_cs| to zero. |cur_tok| has a similar problem. And for the + non-expanded branch, simply pushing back a token that matches the + correct internal command does not work, because that approach would + not survive roundtripping to a temporary file or even a token list. + + In a next version, it would be smart to create |frozen_| versions of + all the primitives. Then, this problem would not happen, at the + expense of a few hundred extra control sequences. + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + t = get_prim_eq_type(cur_cs); + if (t > max_command_cmd) { + cur_cmd = t; + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + cur_cs = 0; + goto RESWITCH; + } else { + back_input(); /* now |loc| and |start| point to a one-item list */ + p = get_avail(); + set_token_info(p, cs_token_flag + frozen_primitive); + set_token_link(p, iloc); + iloc = p; + istart = p; + } + } else { + print_err("Missing primitive name"); + help2 + ("The control sequence marked <to be read again> does not", + "represent any known primitive."); + back_error(); + } + + } + break; + case cs_name_cmd: + /* Manufacture a control sequence name; */ + manufacture_csname(); + break; + case convert_cmd: + conv_toks(); /* this procedure is discussed in Part 27 below */ + break; + case the_cmd: + ins_the_toks(); /* this procedure is discussed in Part 27 below */ + break; + case if_test_cmd: + conditional(); /* this procedure is discussed in Part 28 below */ + break; + case fi_or_else_cmd: + /* Terminate the current conditional and skip to \.{\\fi} */ + /* The processing of conditionals is complete except for the following + code, which is actually part of |expand|. It comes into play when + \.{\\or}, \.{\\else}, or \.{\\fi} is scanned. */ + + if (int_par(tracing_ifs_code) > 0) + if (int_par(tracing_commands_code) <= 1) + show_cur_cmd_chr(); + if (cur_chr > if_limit) { + if (if_limit == if_code) { + insert_relax(); /* condition not yet evaluated */ + } else { + print_err("Extra "); + print_cmd_chr(fi_or_else_cmd, cur_chr); + help1("I'm ignoring this; it doesn't match any \\if."); + error(); + } + } else { + while (cur_chr != fi_code) + pass_text(); /* skip to \.{\\fi} */ + pop_condition_stack(); + } + + break; + case input_cmd: + /* Initiate or terminate input from a file */ + if (cur_chr == 1) + force_eof = true; + else if (cur_chr == 2) + pseudo_start(); + else if (cur_chr == 3) { + pseudo_start(); + iname = 19; + } else if (name_in_progress) + insert_relax(); + else + start_input(); + break; + default: + /* Complain about an undefined macro */ + print_err("Undefined control sequence"); + help5("The control sequence at the end of the top line", + "of your error message was never \\def'ed. If you have", + "misspelled it (e.g., `\\hobx'), type `I' and the correct", + "spelling (e.g., `I\\hbox'). Otherwise just continue,", + "and I'll forget about whatever was undefined."); + error(); + break; + } + } else if (cur_cmd < end_template_cmd) { + macro_call(); + } else { + /* Insert a token containing |frozen_endv| */ + /* An |end_template| command is effectively changed to an |endv| command + by the following code. (The reason for this is discussed below; the + |frozen_end_template| at the end of the template has passed the + |check_outer_validity| test, so its mission of error detection has been + accomplished.) + */ + cur_tok = cs_token_flag + frozen_endv; + back_input(); + + } + cur_val = cv_backup; + cur_val_level = cvl_backup; + radix = radix_backup; + cur_order = co_backup; + set_token_link(backup_head, backup_backup); + decr(expand_depth_count); +} + +@ @c +void complain_missing_csname(void) +{ + print_err("Missing \\endcsname inserted"); + help2("The control sequence marked <to be read again> should", + "not appear between \\csname and \\endcsname."); + back_error(); +} + +@ @c +void manufacture_csname(void) +{ + halfword p, q, r; + lstring *ss; + r = get_avail(); + p = r; /* head of the list of characters */ + is_in_csname = true; + do { + get_x_token(); + if (cur_cs == 0) + store_new_token(cur_tok); + } while (cur_cs == 0); + if (cur_cmd != end_cs_name_cmd) { + /* Complain about missing \.{\\endcsname} */ + complain_missing_csname(); + } + is_in_csname = false; + /* Look up the characters of list |r| in the hash table, and set |cur_cs| */ + + ss = tokenlist_to_lstring(r, true); + if (ss->l > 0) { + no_new_control_sequence = false; + cur_cs = string_lookup((char *) ss->s, ss->l); + no_new_control_sequence = true; + } else { + cur_cs = null_cs; /* the list is empty */ + } + xfree(ss->s); + flush_list(r); + if (eq_type(cur_cs) == undefined_cs_cmd) { + eq_define(cur_cs, relax_cmd, too_big_char); /* N.B.: The |save_stack| might change */ + }; /* the control sequence will now match `\.{\\relax}' */ + cur_tok = cur_cs + cs_token_flag; + back_input(); +} + + +@ Sometimes the expansion looks too far ahead, so we want to insert +a harmless \.{\\relax} into the user's input. + +@c +void insert_relax(void) +{ + cur_tok = cs_token_flag + cur_cs; + back_input(); + cur_tok = cs_token_flag + frozen_relax; + back_input(); + token_type = inserted; +} + + +@ Here is a recursive procedure that is \TeX's usual way to get the +next token of input. It has been slightly optimized to take account of +common cases. + +@c +void get_x_token(void) +{ /* sets |cur_cmd|, |cur_chr|, |cur_tok|, and expands macros */ + RESTART: + get_token_lua(); + if (cur_cmd <= max_command_cmd) + goto DONE; + if (cur_cmd >= call_cmd) { + if (cur_cmd < end_template_cmd) { + macro_call(); + } else { + cur_cs = frozen_endv; + cur_cmd = endv_cmd; + goto DONE; /* |cur_chr=null_list| */ + } + } else { + expand(); + } + goto RESTART; + DONE: + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + + +@ The |get_x_token| procedure is equivalent to two consecutive +procedure calls: |get_next; x_token|. + +@c +void x_token(void) +{ /* |get_x_token| without the initial |get_next| */ + while (cur_cmd > max_command_cmd) { + expand(); + get_token_lua(); + } + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + + +@ A control sequence that has been \.{\\def}'ed by the user is expanded by +\TeX's |macro_call| procedure. + +Before we get into the details of |macro_call|, however, let's consider the +treatment of primitives like \.{\\topmark}, since they are essentially +macros without parameters. The token lists for such marks are kept in five +global arrays of pointers; we refer to the individual entries of these +arrays by symbolic macros |top_mark|, etc. The value of |top_mark(x)|, etc. +is either |null| or a pointer to the reference count of a token list. + +The variable |biggest_used_mark| is an aid to try and keep the code +somehwat efficient without too much extra work: it registers the +highest mark class ever instantiated by the user, so the loops +in |fire_up| and |vsplit| do not have to traverse the full range +|0..biggest_mark|. + +@c +halfword top_marks_array[(biggest_mark + 1)]; +halfword first_marks_array[(biggest_mark + 1)]; +halfword bot_marks_array[(biggest_mark + 1)]; +halfword split_first_marks_array[(biggest_mark + 1)]; +halfword split_bot_marks_array[(biggest_mark + 1)]; +halfword biggest_used_mark; + +@ @c +void initialize_marks(void) +{ + int i; + biggest_used_mark = 0; + for (i = 0; i <= biggest_mark; i++) { + top_mark(i) = null; + first_mark(i) = null; + bot_mark(i) = null; + split_first_mark(i) = null; + split_bot_mark(i) = null; + } +} + + +@ Now let's consider |macro_call| itself, which is invoked when \TeX\ is +scanning a control sequence whose |cur_cmd| is either |call|, |long_call|, +|outer_call|, or |long_outer_call|. The control sequence definition +appears in the token list whose reference count is in location |cur_chr| +of |mem|. + +The global variable |long_state| will be set to |call| or to |long_call|, +depending on whether or not the control sequence disallows \.{\\par} +in its parameters. The |get_next| routine will set |long_state| to +|outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer} +control sequence occurs in the midst of an argument. + +@c +int long_state; /* governs the acceptance of \.{\\par} */ + +@ The parameters, if any, must be scanned before the macro is expanded. +Parameters are token lists without reference counts. They are placed on +an auxiliary stack called |pstack| while they are being scanned, since +the |param_stack| may be losing entries during the matching process. +(Note that |param_stack| can't be gaining entries, since |macro_call| is +the only routine that puts anything onto |param_stack|, and it +is not recursive.) + +@c +halfword pstack[9]; /* arguments supplied to a macro */ + + +@ After parameter scanning is complete, the parameters are moved to the +|param_stack|. Then the macro body is fed to the scanner; in other words, +|macro_call| places the defined text of the control sequence at the +top of\/ \TeX's input stack, so that |get_next| will proceed to read it +next. + +The global variable |cur_cs| contains the |eqtb| address of the control sequence +being expanded, when |macro_call| begins. If this control sequence has not been +declared \.{\\long}, i.e., if its command code in the |eq_type| field is +not |long_call| or |long_outer_call|, its parameters are not allowed to contain +the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro +call is aborted, and the \.{\\par} will be rescanned. + +@c +void macro_call(void) +{ /* invokes a user-defined control sequence */ + halfword r; /* current node in the macro's token list */ + halfword p = null; /* current node in parameter token list being built */ + halfword q; /* new node being put into the token list */ + halfword s; /* backup pointer for parameter matching */ + halfword t; /* cycle pointer for backup recovery */ + halfword u, v; /* auxiliary pointers for backup recovery */ + halfword rbrace_ptr = null; /* one step before the last |right_brace| token */ + int n = 0; /* the number of parameters scanned */ + halfword unbalance; /* unmatched left braces in current parameter */ + halfword m = 0; /* the number of tokens or groups (usually) */ + halfword ref_count; /* start of the token list */ + int save_scanner_status = scanner_status; /* |scanner_status| upon entry */ + halfword save_warning_index = warning_index; /* |warning_index| upon entry */ + int match_chr = 0; /* character used in parameter */ + warning_index = cur_cs; + ref_count = cur_chr; + r = token_link(ref_count); + if (int_par(tracing_macros_code) > 0) { + /* Show the text of the macro being expanded */ + begin_diagnostic(); + print_ln(); + print_cs(warning_index); + token_show(ref_count); + end_diagnostic(false); + } + if (token_info(r) == protected_token) + r = token_link(r); + if (token_info(r) != end_match_token) { + /* Scan the parameters and make |link(r)| point to the macro body; but + |return| if an illegal \.{\\par} is detected */ + /* At this point, the reader will find it advisable to review the explanation + of token list format that was presented earlier, since many aspects of that + format are of importance chiefly in the |macro_call| routine. + + The token list might begin with a string of compulsory tokens before the + first |match| or |end_match|. In that case the macro name is supposed to be + followed by those tokens; the following program will set |s=null| to + represent this restriction. Otherwise |s| will be set to the first token of + a string that will delimit the next parameter. + */ + + scanner_status = matching; + unbalance = 0; + long_state = eq_type(cur_cs); + if (long_state >= outer_call_cmd) + long_state = long_state - 2; + do { + set_token_link(temp_token_head, null); + if ((token_info(r) >= end_match_token) + || (token_info(r) < match_token)) { + s = null; + } else { + match_chr = token_info(r) - match_token; + s = token_link(r); + r = s; + p = temp_token_head; + m = 0; + } + /* Scan a parameter until its delimiter string has been found; or, if |s=null|, + simply scan the delimiter string; */ + + /* If |info(r)| is a |match| or |end_match| command, it cannot be equal to + any token found by |get_token|. Therefore an undelimited parameter---i.e., + a |match| that is immediately followed by |match| or |end_match|---will + always fail the test `|cur_tok=info(r)|' in the following algorithm. */ + CONTINUE: + get_token(); /* set |cur_tok| to the next token of input */ + if (cur_tok == token_info(r)) { + /* Advance |r|; |goto found| if the parameter delimiter has been + fully matched, otherwise |goto continue| */ + /* A slightly subtle point arises here: When the parameter delimiter ends + with `\.{\#\{}', the token list will have a left brace both before and + after the |end_match|\kern-.4pt. Only one of these should affect the + |align_state|, but both will be scanned, so we must make a correction. + */ + r = token_link(r); + if ((token_info(r) >= match_token) + && (token_info(r) <= end_match_token)) { + if (cur_tok < left_brace_limit) + decr(align_state); + goto FOUND; + } else { + goto CONTINUE; + } + + } + /* Contribute the recently matched tokens to the current parameter, and + |goto continue| if a partial match is still in effect; but abort if |s=null| */ + + /* When the following code becomes active, we have matched tokens from |s| to + the predecessor of |r|, and we have found that |cur_tok<>info(r)|. An + interesting situation now presents itself: If the parameter is to be + delimited by a string such as `\.{ab}', and if we have scanned `\.{aa}', + we want to contribute one `\.a' to the current parameter and resume + looking for a `\.b'. The program must account for such partial matches and + for others that can be quite complex. But most of the time we have |s=r| + and nothing needs to be done. + + Incidentally, it is possible for \.{\\par} tokens to sneak in to certain + parameters of non-\.{\\long} macros. For example, consider a case like + `\.{\\def\\a\#1\\par!\{...\}}' where the first \.{\\par} is not followed + by an exclamation point. In such situations it does not seem appropriate + to prohibit the \.{\\par}, so \TeX\ keeps quiet about this bending of + the rules. */ + + if (s != r) { + if (s == null) { + /* Report an improper use of the macro and abort */ + print_err("Use of "); + sprint_cs(warning_index); + tprint(" doesn't match its definition"); + help4 + ("If you say, e.g., `\\def\\a1{...}', then you must always", + "put `1' after `\\a', since control sequence names are", + "made up of letters only. The macro here has not been", + "followed by the required stuff, so I'm ignoring it."); + error(); + goto EXIT; + + } else { + t = s; + do { + store_new_token(token_info(t)); + incr(m); + u = token_link(t); + v = s; + while (1) { + if (u == r) { + if (cur_tok != token_info(v)) { + goto DONE; + } else { + r = token_link(v); + goto CONTINUE; + } + } + if (token_info(u) != token_info(v)) + goto DONE; + u = token_link(u); + v = token_link(v); + } + DONE: + t = token_link(t); + } while (t != r); + r = s; /* at this point, no tokens are recently matched */ + } + } + + if (cur_tok == par_token) + if (long_state != long_call_cmd) + if (!int_par(suppress_long_error_code)) { + goto RUNAWAY; + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + /* Contribute an entire group to the current parameter */ + unbalance = 1; + while (1) { + fast_store_new_token(cur_tok); + get_token(); + if (cur_tok == par_token) { + if (long_state != long_call_cmd) { + if (!int_par(suppress_long_error_code)) { + goto RUNAWAY; + + } + } + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + break; + } + } + } + rbrace_ptr = p; + store_new_token(cur_tok); + + } else { + /* Report an extra right brace and |goto continue| */ + back_input(); + print_err("Argument of "); + sprint_cs(warning_index); + tprint(" has an extra }"); + help6 + ("I've run across a `}' that doesn't seem to match anything.", + "For example, `\\def\\a#1{...}' and `\\a}' would produce", + "this error. If you simply proceed now, the `\\par' that", + "I've just inserted will cause me to report a runaway", + "argument that might be the root of the problem. But if", + "your `}' was spurious, just type `2' and it will go away."); + incr(align_state); + long_state = call_cmd; + cur_tok = par_token; + ins_error(); + goto CONTINUE; + /* a white lie; the \.{\\par} won't always trigger a runaway */ + } + } else { + /* Store the current token, but |goto continue| if it is + a blank space that would become an undelimited parameter */ + if (cur_tok == space_token) + if (token_info(r) <= end_match_token) + if (token_info(r) >= match_token) + goto CONTINUE; + store_new_token(cur_tok); + + } + incr(m); + if (token_info(r) > end_match_token) + goto CONTINUE; + if (token_info(r) < match_token) + goto CONTINUE; + FOUND: + if (s != null) { + /* Tidy up the parameter just scanned, and tuck it away */ + /* If the parameter consists of a single group enclosed in braces, we must + strip off the enclosing braces. That's why |rbrace_ptr| was introduced. */ + if ((m == 1) && (token_info(p) < right_brace_limit) + && (p != temp_token_head)) { + set_token_link(rbrace_ptr, null); + free_avail(p); + p = token_link(temp_token_head); + pstack[n] = token_link(p); + free_avail(p); + } else { + pstack[n] = token_link(temp_token_head); + } + incr(n); + if (int_par(tracing_macros_code) > 0) { + begin_diagnostic(); + print_nl(match_chr); + print_int(n); + tprint("<-"); + show_token_list(pstack[n - 1], null, 1000); + end_diagnostic(false); + } + + } + + /* now |info(r)| is a token whose command code is either |match| or |end_match| */ + } while (token_info(r) != end_match_token); + + } + /* Feed the macro body and its parameters to the scanner */ + /* Before we put a new token list on the input stack, it is wise to clean off + all token lists that have recently been depleted. Then a user macro that ends + with a call to itself will not require unbounded stack space. */ + while ((istate == token_list) && (iloc == null) + && (token_type != v_template)) + end_token_list(); /* conserve stack space */ + begin_token_list(ref_count, macro); + iname = warning_index; + iloc = token_link(r); + if (n > 0) { + if (param_ptr + n > max_param_stack) { + max_param_stack = param_ptr + n; + if (max_param_stack > param_size) + overflow("parameter stack size", (unsigned) param_size); + } + for (m = 0; m <= n - 1; m++) + param_stack[param_ptr + m] = pstack[m]; + param_ptr = param_ptr + n; + } + goto EXIT; + RUNAWAY: + /* Report a runaway argument and abort */ + /* If |long_state=outer_call|, a runaway argument has already been reported. */ + if (long_state == call_cmd) { + runaway(); + print_err("Paragraph ended before "); + sprint_cs(warning_index); + tprint(" was complete"); + help3("I suspect you've forgotten a `}', causing me to apply this", + "control sequence to too much text. How can we recover?", + "My plan is to forget the whole thing and hope for the best."); + back_error(); + } + pstack[n] = token_link(temp_token_head); + align_state = align_state - unbalance; + for (m = 0; m <= n; m++) + flush_list(pstack[m]); + + EXIT: + scanner_status = save_scanner_status; + warning_index = save_warning_index; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.h b/Build/source/texk/web2c/luatexdir/tex/extensions.h new file mode 100644 index 00000000000..590dec0e6bd --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/extensions.h @@ -0,0 +1,118 @@ +/* extensions.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: extensions.h 3261 2009-12-18 11:38:21Z taco $ */ + +#ifndef EXTENSIONS_H +# define EXTENSIONS_H + +extern alpha_file write_file[16]; +extern halfword write_file_mode[16]; +extern halfword write_file_translation[16]; +extern boolean write_open[18]; +extern scaled neg_wd; +extern scaled pos_wd; +extern scaled neg_ht; + +extern halfword write_loc; + +extern void do_extension(PDF pdf); + +/* Three extra node types carry information from |main_control|. */ + +/* +User defined whatsits can be inserted into node lists to pass data +along from one lua call to anotherb without interference from the +typesetting engine itself. Each has an id, a type, and a value. The +type of the value depends on the |user_node_type| field. +*/ + +extern void new_whatsit(int s); +extern void new_write_whatsit(int w); +extern void scan_pdf_ext_toks(void); +extern halfword prev_rightmost(halfword s, halfword e); +extern int pdf_last_xform; +extern int pdf_last_ximage; +extern int pdf_last_ximage_pages; +extern int pdf_last_ximage_colordepth; +extern int pdf_last_annot; +extern int pdf_last_link; +extern scaledpos pdf_last_pos; +extern halfword concat_tokens(halfword q, halfword r); +extern int pdf_retval; + +extern halfword make_local_par_node(void); + + +/* +The \.{\\pagediscards} and \.{\\splitdiscards} commands share the +command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are +distinguished by their |chr_code| values |last_box_code| and +|vsplit_code|. These |chr_code| values are larger than |box_code| and +|copy_code|. +*/ + +extern boolean *eof_seen; /* has eof been seen? */ +extern void print_group(boolean e); +extern void group_trace(boolean e); +extern save_pointer *grp_stack; /* initial |cur_boundary| */ +extern halfword *if_stack; /* initial |cond_ptr| */ +extern void group_warning(void); +extern void if_warning(void); +extern void file_warning(void); + +extern halfword last_line_fill; /* the |par_fill_skip| glue node of the new paragraph */ + +# define get_tex_dimen_register(j) dimen(j) +# define get_tex_skip_register(j) skip(j) +# define get_tex_count_register(j) count(j) +# define get_tex_attribute_register(j) attribute(j) +# define get_tex_box_register(j) box(j) + +extern int set_tex_dimen_register(int j, scaled v); +extern int set_tex_skip_register(int j, halfword v); +extern int set_tex_count_register(int j, scaled v); +extern int set_tex_box_register(int j, scaled v); +extern int set_tex_attribute_register(int j, scaled v); +extern int get_tex_toks_register(int l); +extern int set_tex_toks_register(int j, lstring s); +extern scaled get_tex_box_width(int j); +extern int set_tex_box_width(int j, scaled v); +extern scaled get_tex_box_height(int j); +extern int set_tex_box_height(int j, scaled v); +extern scaled get_tex_box_depth(int j); +extern int set_tex_box_depth(int j, scaled v); + +/* Synctex variables */ + +extern int synctexoption; +extern int synctexoffset; + +/* Here are extra variables for Web2c. */ + +extern pool_pointer edit_name_start; +extern int edit_name_length, edit_line; +extern int ipcon; +extern boolean stop_at_space; +extern int shellenabledp; +extern int restrictedshell; +extern char *output_comment; +extern boolean debug_format_file; + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.w b/Build/source/texk/web2c/luatexdir/tex/extensions.w new file mode 100644 index 00000000000..b8b89512ba2 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/extensions.w @@ -0,0 +1,1217 @@ +% extensions.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +\def\eTeX{e-\TeX} +\def\pdfTeX{pdf\TeX} + +@ @c +static const char _svn_version[] = + "$Id: extensions.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/extensions.w $"; + +#include "ptexlib.h" + +@ @c +#define mode cur_list.mode_field +#define tail cur_list.tail_field +#define head cur_list.head_field +#define prev_graf cur_list.pg_field +#define dir_save cur_list.dirs_field + +#define tracing_nesting int_par(tracing_nesting_code) +#define box(A) eqtb[box_base+(A)].hh.rh +#define global_defs int_par(global_defs_code) +#define cat_code_table int_par(cat_code_table_code) +#define par_direction int_par(par_direction_code) +#define toks(A) equiv(toks_base+(A)) + +#define local_inter_line_penalty int_par(local_inter_line_penalty_code) +#define local_broken_penalty int_par(local_broken_penalty_code) +#define local_left_box equiv(local_left_box_base) +#define local_right_box equiv(local_right_box_base) + + +@ The program above includes a bunch of ``hooks'' that allow further +capabilities to be added without upsetting \TeX's basic structure. +Most of these hooks are concerned with ``whatsit'' nodes, which are +intended to be used for special purposes; whenever a new extension to +\TeX\ involves a new kind of whatsit node, a corresponding change needs +to be made to the routines below that deal with such nodes, +but it will usually be unnecessary to make many changes to the +other parts of this program. + +In order to demonstrate how extensions can be made, we shall treat +`\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}', +and `\.{\\special}' as if they were extensions. +These commands are actually primitives of \TeX, and they should +appear in all implementations of the system; but let's try to imagine +that they aren't. Then the program below illustrates how a person +could add them. + +Sometimes, of course, an extension will require changes to \TeX\ itself; +no system of hooks could be complete enough for all conceivable extensions. +The features associated with `\.{\\write}' are almost all confined to the +following paragraphs, but there are small parts of the |print_ln| and +|print_char| procedures that were introduced specifically to \.{\\write} +characters. Furthermore one of the token lists recognized by the scanner +is a |write_text|; and there are a few other miscellaneous places where we +have already provided for some aspect of \.{\\write}. The goal of a \TeX\ +extender should be to minimize alterations to the standard parts of the +program, and to avoid them completely if possible. He or she should also +be quite sure that there's no easy way to accomplish the desired goals +with the standard features that \TeX\ already has. ``Think thrice before +extending,'' because that may save a lot of work, and it will also keep +incompatible extensions of \TeX\ from proliferating. +@^system dependencies@> +@^extensions to \TeX@> + +First let's consider the format of whatsit nodes that are used to represent +the data associated with \.{\\write} and its relatives. Recall that a whatsit +has |type=whatsit_node|, and the |subtype| is supposed to distinguish +different kinds of whatsits. Each node occupies two or more words; the +exact number is immaterial, as long as it is readily determined from the +|subtype| or other data. + +We shall introduce five |subtype| values here, corresponding to the +control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, and \.{\\special}. +The second word of I/O whatsits has a |write_stream| field +that identifies the write-stream number (0 to 15, or 16 for out-of-range and +positive, or 17 for out-of-range and negative). +In the case of \.{\\write} and \.{\\special}, there is also a field that +points to the reference count of a token list that should be sent. In the +case of \.{\\openout}, we need three words and three auxiliary subfields +to hold the string numbers for name, area, and extension. + +@ Extensions might introduce new command codes; but it's best to use +|extension| with a modifier, whenever possible, so that |main_control| +stays the same. + +@ The sixteen possible \.{\\write} streams are represented by the |write_file| +array. The |j|th file is open if and only if |write_open[j]=true|. The last +two streams are special; |write_open[16]| represents a stream number +greater than 15, while |write_open[17]| represents a negative stream number, +and both of these variables are always |false|. + +@c +alpha_file write_file[16]; +halfword write_file_mode[16]; +halfword write_file_translation[16]; +boolean write_open[18]; +scaled neg_wd; +scaled pos_wd; +scaled neg_ht; + + +@ The variable |write_loc| just introduced is used to provide an +appropriate error message in case of ``runaway'' write texts. + +@c +halfword write_loc; /* |eqtb| address of \.{\\write} */ + + +@ When an |extension| command occurs in |main_control|, in any mode, +the |do_extension| routine is called. + +@c +void do_extension(PDF pdf) +{ + int i, k; /* all-purpose integers */ + halfword p; /* all-purpose pointer */ + switch (cur_chr) { + case open_node: + /* Implement \.{\\openout} */ + new_write_whatsit(open_node_size); + scan_optional_equals(); + scan_file_name(); + open_name(tail) = cur_name; + open_area(tail) = cur_area; + open_ext(tail) = cur_ext; + break; + case write_node: + /* Implement \.{\\write} */ + /* When `\.{\\write 12\{...\}}' appears, we scan the token list `\.{\{...\}}' + without expanding its macros; the macros will be expanded later when this + token list is rescanned. */ + k = cur_cs; + new_write_whatsit(write_node_size); + cur_cs = k; + p = scan_toks(false, false); + write_tokens(tail) = def_ref; + break; + case close_node: + /* Implement \.{\\closeout} */ + new_write_whatsit(write_node_size); + write_tokens(tail) = null; + break; + case special_node: + /* Implement \.{\\special} */ + /* When `\.{\\special\{...\}}' appears, we expand the macros in the token + list as in \.{\\xdef} and \.{\\mark}. */ + new_whatsit(special_node); + write_stream(tail) = null; + p = scan_toks(false, true); + write_tokens(tail) = def_ref; + break; + case immediate_code: + /* Implement \.{\\immediate} */ + /* To write a token list, we must run it through \TeX's scanner, expanding + macros and \.{\\the} and \.{\\number}, etc. This might cause runaways, + if a delimited macro parameter isn't matched, and runaways would be + extremely confusing since we are calling on \TeX's scanner in the middle + of a \.{\\shipout} command. Therefore we will put a dummy control sequence as + a ``stopper,'' right after the token list. This control sequence is + artificially defined to be \.{\\outer}. + + The presence of `\.{\\immediate}' causes the |do_extension| procedure + to descend to one level of recursion. Nothing happens unless \.{\\immediate} + is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'. + */ + get_x_token(); + if (cur_cmd == extension_cmd) { + if (cur_chr <= close_node) { + p = tail; + /* |do_extension()| and |out_what()| here can only be open, write, or close */ + do_extension(pdf); /* append a whatsit node */ + out_what(pdf, tail); /* do the action immediately */ + flush_node_list(tail); + tail = p; + vlink(p) = null; + } else { + switch (cur_chr) { + case pdf_obj_code: + check_o_mode(pdf, "\\immediate\\pdfobj", 1 << OMODE_PDF, + true); + do_extension(pdf); /* scan object and set |pdf_last_obj| */ + if (obj_data_ptr(pdf, pdf_last_obj) == 0) /* this object has not been initialized yet */ + pdf_error("ext1", + "`\\pdfobj reserveobjnum' cannot be used with \\immediate"); + pdf_write_obj(pdf, pdf_last_obj); + break; + case pdf_xform_code: + check_o_mode(pdf, "\\immediate\\pdfxform", 1 << OMODE_PDF, + true); + do_extension(pdf); /* scan form and set |pdf_last_xform| */ + pdf_cur_form = pdf_last_xform; + ship_out(pdf, obj_xform_box(pdf, pdf_last_xform), false); + break; + case pdf_ximage_code: + check_o_mode(pdf, "\\immediate\\pdfximage", 1 << OMODE_PDF, + true); + do_extension(pdf); /* scan image and set |pdf_last_ximage| */ + pdf_write_image(pdf, pdf_last_ximage); + break; + default: + back_input(); + break; + } + } + } else { + back_input(); + } + break; + case pdf_annot_node: + /* Implement \.{\\pdfannot} */ + check_o_mode(pdf, "\\pdfannot", 1 << OMODE_PDF, false); + scan_annot(pdf); + break; + case pdf_catalog_code: + /* Implement \.{\\pdfcatalog} */ + check_o_mode(pdf, "\\pdfcatalog", 1 << OMODE_PDF, true); /* writes an object */ + scan_pdfcatalog(pdf); + break; + case pdf_dest_node: + /* Implement \.{\\pdfdest} */ + check_o_mode(pdf, "\\pdfdest", 1 << OMODE_PDF, false); + scan_pdfdest(pdf); + break; + case pdf_end_link_node: + /* Implement \.{\\pdfendlink} */ + check_o_mode(pdf, "\\pdfendlink", 1 << OMODE_PDF, false); + if (abs(mode) == vmode) + pdf_error("ext1", "\\pdfendlink cannot be used in vertical mode"); + new_whatsit(pdf_end_link_node); + break; + case pdf_end_thread_node: + /* Implement \.{\\pdfendthread} */ + check_o_mode(pdf, "\\pdfendthread", 1 << OMODE_PDF, false); + new_whatsit(pdf_end_thread_node); + break; + case pdf_font_attr_code: + /* Implement \.{\\pdffontattr} */ + /* A change from Thanh's original code: the font attributes are simply + initialized to zero now, this is easier to deal with from C than an + empty \TeX{} string, and surely nobody will want to set + \.{\\pdffontattr} to a string containing a single zero, as that + would be nonsensical in the PDF output. */ + check_o_mode(pdf, "\\pdffontattr", 1 << OMODE_PDF, false); + scan_font_ident(); + k = cur_val; + if (k == null_font) + pdf_error("font", "invalid font identifier"); + scan_pdf_ext_toks(); + set_pdf_font_attr(k, tokens_to_string(def_ref)); + if (str_length(pdf_font_attr(k)) == 0) { + flush_str((str_ptr - 1)); /* from |tokens_to_string| */ + set_pdf_font_attr(k, 0); + } + break; + case pdf_font_expand_code: + /* Implement \.{\\pdffontexpand} */ + read_expand_font(); + break; + case pdf_include_chars_code: + /* Implement \.{\\pdfincludechars} */ + check_o_mode(pdf, "\\pdfincludechars", 1 << OMODE_PDF, false); + pdf_include_chars(pdf); + break; + case pdf_info_code: + /* Implement \.{\\pdfinfo} */ + check_o_mode(pdf, "\\pdfinfo", 1 << OMODE_PDF, false); + scan_pdf_ext_toks(); + pdf_info_toks = concat_tokens(pdf_info_toks, def_ref); + break; + case pdf_literal_node: + /* Implement \.{\\pdfliteral} */ + check_o_mode(pdf, "\\pdfliteral", 1 << OMODE_PDF, false); + new_whatsit(pdf_literal_node); + if (scan_keyword("direct")) + set_pdf_literal_mode(tail, direct_always); + else if (scan_keyword("page")) + set_pdf_literal_mode(tail, direct_page); + else + set_pdf_literal_mode(tail, set_origin); + scan_pdf_ext_toks(); + set_pdf_literal_type(tail, normal); + set_pdf_literal_data(tail, def_ref); + break; + case pdf_colorstack_node: + /* Implement \.{\\pdfcolorstack} */ + check_o_mode(pdf, "\\pdfcolorstack", 1 << OMODE_PDF, false); + /* Scan and check the stack number and store in |cur_val| */ + scan_int(); + if (cur_val >= colorstackused()) { + print_err("Unknown color stack number "); + print_int(cur_val); + help3 + ("Allocate and initialize a color stack with \\pdfcolorstackinit.", + "I'll use default color stack 0 here.", + "Proceed, with fingers crossed."); + error(); + cur_val = 0; + } + if (cur_val < 0) { + print_err("Invalid negative color stack number"); + help2("I'll use default color stack 0 here.", + "Proceed, with fingers crossed."); + error(); + cur_val = 0; + } + if (scan_keyword("set")) + i = colorstack_set; + else if (scan_keyword("push")) + i = colorstack_push; + else if (scan_keyword("pop")) + i = colorstack_pop; + else if (scan_keyword("current")) + i = colorstack_current; + else + i = -1; /* error */ + + if (i >= 0) { + new_whatsit(pdf_colorstack_node); + set_pdf_colorstack_stack(tail, cur_val); + set_pdf_colorstack_cmd(tail, i); + set_pdf_colorstack_data(tail, null); + if (i <= colorstack_data) { + scan_pdf_ext_toks(); + set_pdf_colorstack_data(tail, def_ref); + } + } else { + print_err("Color stack action is missing"); + help3("The expected actions for \\pdfcolorstack:", + " set, push, pop, current", + "I'll ignore the color stack command."); + error(); + } + break; + case pdf_setmatrix_node: + /* Implement \.{\\pdfsetmatrix} */ + check_o_mode(pdf, "\\pdfsetmatrix", 1 << OMODE_PDF, false); + new_whatsit(pdf_setmatrix_node); + scan_pdf_ext_toks(); + set_pdf_setmatrix_data(tail, def_ref); + break; + case pdf_save_node: + /* Implement \.{\\pdfsave} */ + check_o_mode(pdf, "\\pdfsave", 1 << OMODE_PDF, false); + new_whatsit(pdf_save_node); + break; + case pdf_restore_node: + /* Implement \.{\\pdfrestore} */ + check_o_mode(pdf, "\\pdfrestore", 1 << OMODE_PDF, false); + new_whatsit(pdf_restore_node); + break; + case pdf_map_file_code: + /* Implement \.{\\pdfmapfile} */ + check_o_mode(pdf, "\\pdfmapfile", 1 << OMODE_PDF, false); + scan_pdf_ext_toks(); + pdfmapfile(def_ref); + delete_token_ref(def_ref); + break; + case pdf_map_line_code: + /* Implement \.{\\pdfmapline} */ + check_o_mode(pdf, "\\pdfmapline", 1 << OMODE_PDF, false); + scan_pdf_ext_toks(); + pdfmapline(def_ref); + delete_token_ref(def_ref); + break; + case pdf_names_code: + /* Implement \.{\\pdfnames} */ + check_o_mode(pdf, "\\pdfnames", 1 << OMODE_PDF, false); + scan_pdf_ext_toks(); + pdf_names_toks = concat_tokens(pdf_names_toks, def_ref); + break; + case pdf_obj_code: + /* Implement \.{\\pdfobj} */ + check_o_mode(pdf, "\\pdfobj", 1 << OMODE_PDF, false); + scan_obj(pdf); + break; + case pdf_outline_code: + /* Implement \.{\\pdfoutline} */ + check_o_mode(pdf, "\\pdfoutline", 1 << OMODE_PDF, true); + scan_pdfoutline(pdf); + break; + case pdf_refobj_node: + /* Implement \.{\\pdfrefobj} */ + check_o_mode(pdf, "\\pdfrefobj", 1 << OMODE_PDF, false); + scan_refobj(pdf); + break; + case pdf_refxform_node: + /* Implement \.{\\pdfrefxform} */ + check_o_mode(pdf, "\\pdfrefxform", 1 << OMODE_PDF, false); + scan_pdfrefxform(pdf); + break; + case pdf_refximage_node: + /* Implement \.{\\pdfrefximage} */ + check_o_mode(pdf, "\\pdfrefximage", 1 << OMODE_PDF, false); + scan_pdfrefximage(pdf); + break; + case pdf_save_pos_node: + /* Implement \.{\\pdfsavepos} */ + new_whatsit(pdf_save_pos_node); + break; + case pdf_start_link_node: + /* Implement \.{\\pdfstartlink} */ + check_o_mode(pdf, "\\pdfstartlink", 1 << OMODE_PDF, false); + scan_startlink(pdf); + break; + case pdf_start_thread_node: + /* Implement \.{\\pdfstartthread} */ + check_o_mode(pdf, "\\pdfstartthread", 1 << OMODE_PDF, false); + new_annot_whatsit(pdf_start_thread_node); + scan_thread_id(); + break; + case pdf_thread_node: + /* Implement \.{\\pdfthread} */ + check_o_mode(pdf, "\\pdfthread", 1 << OMODE_PDF, false); + new_annot_whatsit(pdf_thread_node); + scan_thread_id(); + break; + case pdf_trailer_code: + /* Implement \.{\\pdftrailer} */ + check_o_mode(pdf, "\\pdftrailer", 1 << OMODE_PDF, false); + scan_pdf_ext_toks(); + pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref); + break; + case pdf_xform_code: + /* Implement \.{\\pdfxform} */ + check_o_mode(pdf, "\\pdfxform", 1 << OMODE_PDF, false); + scan_pdfxform(pdf); + break; + case pdf_ximage_code: + /* Implement \.{\\pdfximage} */ + check_o_mode(pdf, "\\pdfximage", 1 << OMODE_PDF, false); + /* png, jpeg, and pdf image handling depends on this done so early: */ + fix_pdf_minorversion(pdf); + scan_pdfximage(pdf); + break; + case save_cat_code_table_code: + /* Implement \.{\\savecatcodetable} */ + scan_int(); + if ((cur_val < 0) || (cur_val > 0xFFFF)) { + print_err("Invalid \\catcode table"); + help1("All \\catcode table ids must be between 0 and 0xFFFF"); + error(); + } else { + if (cur_val == cat_code_table) { + print_err("Invalid \\catcode table"); + help1("You cannot overwrite the current \\catcode table"); + error(); + } else { + copy_cat_codes(cat_code_table, cur_val); + } + } + break; + case init_cat_code_table_code: + /* Implement \.{\\initcatcodetable} */ + scan_int(); + if ((cur_val < 0) || (cur_val > 0xFFFF)) { + print_err("Invalid \\catcode table"); + help1("All \\catcode table ids must be between 0 and 0xFFFF"); + error(); + } else { + if (cur_val == cat_code_table) { + print_err("Invalid \\catcode table"); + help1("You cannot overwrite the current \\catcode table"); + error(); + } else { + initex_cat_codes(cur_val); + } + } + break; + case set_random_seed_code: + /* Implement \.{\\pdfsetrandomseed} */ + /* Negative random seed values are silently converted to positive ones */ + scan_int(); + if (cur_val < 0) + negate(cur_val); + random_seed = cur_val; + init_randoms(random_seed); + break; + case pdf_glyph_to_unicode_code: + /* Implement \.{\\pdfglyphtounicode} */ + glyph_to_unicode(); + break; + case late_lua_node: + /* Implement \.{\\latelua} */ + new_whatsit(late_lua_node); + late_lua_name(tail) = scan_lua_state(); + (void) scan_toks(false, false); + late_lua_data(tail) = def_ref; + break; + default: + confusion("ext1"); + break; + } +} + + +@ Here is a subroutine that creates a whatsit node having a given |subtype| +and a given number of words. It initializes only the first word of the whatsit, +and appends it to the current list. + +@c +void new_whatsit(int s) +{ + halfword p; /* the new node */ + p = new_node(whatsit_node, s); + couple_nodes(tail, p); + tail = p; +} + + +@ The next subroutine uses |cur_chr| to decide what sort of whatsit is +involved, and also inserts a |write_stream| number. + +@c +void new_write_whatsit(int w) +{ + new_whatsit(cur_chr); + if (w != write_node_size) { + scan_four_bit_int(); + } else { + scan_int(); + if (cur_val < 0) + cur_val = 17; + else if ((cur_val > 15) && (cur_val != 18)) + cur_val = 16; + } + write_stream(tail) = cur_val; +} + + +@ We have to check whether \.{\\pdfoutput} is set for using \pdfTeX{} + extensions. + +@c +void scan_pdf_ext_toks(void) +{ + (void) scan_toks(false, true); /* like \.{\\special} */ +} + +@ We need to check whether the referenced object exists. + +finds the node preceding the rightmost node |e|; |s| is some node before |e| +@c +halfword prev_rightmost(halfword s, halfword e) +{ + halfword p = s; + if (p == null) + return null; + while (vlink(p) != e) { + p = vlink(p); + if (p == null) + return null; + } + return p; +} + +@ \.{\\pdfxform} and \.{\\pdfrefxform} are similiar to \.{\\pdfobj} and + \.{\\pdfrefobj} + +@c +int pdf_last_xform; + +@ \.{\\pdfximage} and \.{\\pdfrefximage} are similiar to \.{\\pdfxform} and + \.{\\pdfrefxform}. As we have to scan |<rule spec>| quite often, it is better + have a |rule_node| that holds the most recently scanned |<rule spec>|. + +@c +int pdf_last_ximage; +int pdf_last_ximage_pages; +int pdf_last_ximage_colordepth; +int pdf_last_annot; + +@ pdflastlink needs an extra global variable + +@c +int pdf_last_link; +scaledpos pdf_last_pos = { 0, 0 }; + + +@ To implement primitives as \.{\\pdfinfo}, \.{\\pdfcatalog} or +\.{\\pdfnames} we need to concatenate tokens lists. + +@c +halfword concat_tokens(halfword q, halfword r) +{ /* concat |q| and |r| and returns the result tokens list */ + halfword p; + if (q == null) + return r; + p = q; + while (token_link(p) != null) + p = token_link(p); + set_token_link(p, token_link(r)); + free_avail(r); + return q; +} + +@ @c +int pdf_retval; /* global multi-purpose return value */ + +@ @c +halfword make_local_par_node(void) +/* This function creates a |local_paragraph| node */ +{ + halfword p, q; + p = new_node(whatsit_node, local_par_node); + local_pen_inter(p) = local_inter_line_penalty; + local_pen_broken(p) = local_broken_penalty; + if (local_left_box != null) { + q = copy_node_list(local_left_box); + local_box_left(p) = q; + local_box_left_width(p) = width(local_left_box); + } + if (local_right_box != null) { + q = copy_node_list(local_right_box); + local_box_right(p) = q; + local_box_right_width(p) = width(local_right_box); + } + local_par_dir(p) = par_direction; + return p; +} + + + +@ The \eTeX\ features available in extended mode are grouped into two +categories: (1)~Some of them are permanently enabled and have no +semantic effect as long as none of the additional primitives are +executed. (2)~The remaining \eTeX\ features are optional and can be +individually enabled and disabled. For each optional feature there is +an \eTeX\ state variable named \.{\\...state}; the feature is enabled, +resp.\ disabled by assigning a positive, resp.\ non-positive value to +that integer. + + +@ In order to handle \.{\\everyeof} we need an array |eof_seen| of +boolean variables. + +@c +boolean *eof_seen; /* has eof been seen? */ + + +@ The |print_group| procedure prints the current level of grouping and +the name corresponding to |cur_group|. + +@c +void print_group(boolean e) +{ + switch (cur_group) { + case bottom_level: + tprint("bottom level"); + return; + break; + case simple_group: + case semi_simple_group: + if (cur_group == semi_simple_group) + tprint("semi "); + tprint("simple"); + break;; + case hbox_group: + case adjusted_hbox_group: + if (cur_group == adjusted_hbox_group) + tprint("adjusted "); + tprint("hbox"); + break; + case vbox_group: + tprint("vbox"); + break; + case vtop_group: + tprint("vtop"); + break; + case align_group: + case no_align_group: + if (cur_group == no_align_group) + tprint("no "); + tprint("align"); + break; + case output_group: + tprint("output"); + break; + case disc_group: + tprint("disc"); + break; + case insert_group: + tprint("insert"); + break; + case vcenter_group: + tprint("vcenter"); + break; + case math_group: + case math_choice_group: + case math_shift_group: + case math_left_group: + tprint("math"); + if (cur_group == math_choice_group) + tprint(" choice"); + else if (cur_group == math_shift_group) + tprint(" shift"); + else if (cur_group == math_left_group) + tprint(" left"); + break; + } /* there are no other cases */ + tprint(" group (level "); + print_int(cur_level); + print_char(')'); + if (saved_value(-1) != 0) { /* |saved_line| */ + if (e) + tprint(" entered at line "); + else + tprint(" at line "); + print_int(saved_value(-1)); /* |saved_line| */ + } +} + + +@ The |group_trace| procedure is called when a new level of grouping +begins (|e=false|) or ends (|e=true|) with |saved_value(-1)| containing the +line number. + +@c +void group_trace(boolean e) +{ + begin_diagnostic(); + print_char('{'); + if (e) + tprint("leaving "); + else + tprint("entering "); + print_group(e); + print_char('}'); + end_diagnostic(false); +} + +@ A group entered (or a conditional started) in one file may end in a +different file. Such slight anomalies, although perfectly legitimate, +may cause errors that are difficult to locate. In order to be able to +give a warning message when such anomalies occur, \eTeX\ uses the +|grp_stack| and |if_stack| arrays to record the initial |cur_boundary| +and |cond_ptr| values for each input file. + +@c +save_pointer *grp_stack; /* initial |cur_boundary| */ +halfword *if_stack; /* initial |cond_ptr| */ + + +@ When a group ends that was apparently entered in a different input +file, the |group_warning| procedure is invoked in order to update the +|grp_stack|. If moreover \.{\\tracingnesting} is positive we want to +give a warning message. The situation is, however, somewhat complicated +by two facts: (1)~There may be |grp_stack| elements without a +corresponding \.{\\input} file or \.{\\scantokens} pseudo file (e.g., +error insertions from the terminal); and (2)~the relevant information is +recorded in the |name_field| of the |input_stack| only loosely +synchronized with the |in_open| variable indexing |grp_stack|. + +@c +void group_warning(void) +{ + int i; /* index into |grp_stack| */ + boolean w; /* do we need a warning? */ + base_ptr = input_ptr; + input_stack[base_ptr] = cur_input; /* store current state */ + i = in_open; + w = false; + while ((grp_stack[i] == cur_boundary) && (i > 0)) { + /* Set variable |w| to indicate if this case should be reported */ + /* This code scans the input stack in order to determine the type of the + current input file. */ + if (tracing_nesting > 0) { + while ((input_stack[base_ptr].state_field == token_list) || + (input_stack[base_ptr].index_field > i)) + decr(base_ptr); + if (input_stack[base_ptr].name_field > 17) + w = true; + } + + grp_stack[i] = save_value(save_ptr); + decr(i); + } + if (w) { + tprint_nl("Warning: end of "); + print_group(true); + tprint(" of a different file"); + print_ln(); + if (tracing_nesting > 1) + show_context(); + if (history == spotless) + history = warning_issued; + } +} + + +@ When a conditional ends that was apparently started in a different +input file, the |if_warning| procedure is invoked in order to update the +|if_stack|. If moreover \.{\\tracingnesting} is positive we want to +give a warning message (with the same complications as above). + +@c +void if_warning(void) +{ + int i; /* index into |if_stack| */ + boolean w; /* do we need a warning? */ + base_ptr = input_ptr; + input_stack[base_ptr] = cur_input; /* store current state */ + i = in_open; + w = false; + while (if_stack[i] == cond_ptr) { + /* Set variable |w| to... */ + if (tracing_nesting > 0) { + while ((input_stack[base_ptr].state_field == token_list) || + (input_stack[base_ptr].index_field > i)) + decr(base_ptr); + if (input_stack[base_ptr].name_field > 17) + w = true; + } + + if_stack[i] = vlink(cond_ptr); + decr(i); + } + if (w) { + tprint_nl("Warning: end of "); + print_cmd_chr(if_test_cmd, cur_if); + print_if_line(if_line); + tprint(" of a different file"); + print_ln(); + if (tracing_nesting > 1) + show_context(); + if (history == spotless) + history = warning_issued; + } +} + + +@ Conversely, the |file_warning| procedure is invoked when a file ends +and some groups entered or conditionals started while reading from that +file are still incomplete. + +@c +void file_warning(void) +{ + halfword p; /* saved value of |save_ptr| or |cond_ptr| */ + int l; /* saved value of |cur_level| or |if_limit| */ + int c; /* saved value of |cur_group| or |cur_if| */ + int i; /* saved value of |if_line| */ + p = save_ptr; + l = cur_level; + c = cur_group; + save_ptr = cur_boundary; + while (grp_stack[in_open] != save_ptr) { + decr(cur_level); + tprint_nl("Warning: end of file when "); + print_group(true); + tprint(" is incomplete"); + cur_group = save_level(save_ptr); + save_ptr = save_value(save_ptr); + } + save_ptr = p; + cur_level = (quarterword) l; + cur_group = (group_code) c; /* restore old values */ + p = cond_ptr; + l = if_limit; + c = cur_if; + i = if_line; + while (if_stack[in_open] != cond_ptr) { + tprint_nl("Warning: end of file when "); + print_cmd_chr(if_test_cmd, cur_if); + if (if_limit == fi_code) + tprint_esc("else"); + print_if_line(if_line); + tprint(" is incomplete"); + if_line = if_line_field(cond_ptr); + cur_if = if_limit_subtype(cond_ptr); + if_limit = if_limit_type(cond_ptr); + cond_ptr = vlink(cond_ptr); + } + cond_ptr = p; + if_limit = l; + cur_if = c; + if_line = i; /* restore old values */ + print_ln(); + if (tracing_nesting > 1) + show_context(); + if (history == spotless) + history = warning_issued; +} + +@ @c +halfword last_line_fill; /* the |par_fill_skip| glue node of the new paragraph */ + + +@ The lua interface needs some extra functions. The functions +themselves are quite boring, but they are handy because otherwise this +internal stuff has to be accessed from C directly, where lots of the +defines are not available. + +@c +#define get_tex_dimen_register(j) dimen(j) +#define get_tex_skip_register(j) skip(j) +#define get_tex_count_register(j) count(j) +#define get_tex_attribute_register(j) attribute(j) +#define get_tex_box_register(j) box(j) + +int set_tex_dimen_register(int j, scaled v) +{ + int a; /* return non-nil for error */ + if (global_defs > 0) + a = 4; + else + a = 0; + word_define(j + scaled_base, v); + return 0; +} + +int set_tex_skip_register(int j, halfword v) +{ + int a; /* return non-nil for error */ + if (global_defs > 0) + a = 4; + else + a = 0; + if (type(v) != glue_spec_node) + return 1; + word_define(j + skip_base, v); + return 0; +} + +int set_tex_count_register(int j, scaled v) +{ + int a; /* return non-nil for error */ + if (global_defs > 0) + a = 4; + else + a = 0; + word_define(j + count_base, v); + return 0; +} + +int set_tex_box_register(int j, scaled v) +{ + int a; /* return non-nil for error */ + if (global_defs > 0) + a = 4; + else + a = 0; + define(j + box_base, box_ref_cmd, v); + return 0; +} + +int set_tex_attribute_register(int j, scaled v) +{ + int a; /* return non-nil for error */ + if (global_defs > 0) + a = 4; + else + a = 0; + if (j > max_used_attr) + max_used_attr = j; + attr_list_cache = cache_disabled; + word_define(j + attribute_base, v); + return 0; +} + +int get_tex_toks_register(int j) +{ + str_number s; + s = get_nullstr(); + if (toks(j) != null) { + s = tokens_to_string(toks(j)); + } + return s; +} + +int set_tex_toks_register(int j, lstring s) +{ + halfword ref; + int a; + ref = get_avail(); + (void) str_toks(s); + set_token_ref_count(ref, 0); + set_token_link(ref, token_link(temp_token_head)); + if (global_defs > 0) + a = 4; + else + a = 0; + define(j + toks_base, call_cmd, ref); + return 0; +} + +scaled get_tex_box_width(int j) +{ + halfword q = box(j); + if (q != null) + return width(q); + return 0; +} + +int set_tex_box_width(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + width(q) = v; + return 0; +} + +scaled get_tex_box_height(int j) +{ + halfword q = box(j); + if (q != null) + return height(q); + return 0; +} + +int set_tex_box_height(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + height(q) = v; + return 0; +} + + +scaled get_tex_box_depth(int j) +{ + halfword q = box(j); + if (q != null) + return depth(q); + return 0; +} + +int set_tex_box_depth(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + depth(q) = v; + return 0; +} + + +@ This section is devoted to the {\sl Synchronize \TeX nology} +- or simply {\sl Sync\TeX} - used to synchronize between input and output. +This section explains how synchronization basics are implemented. +Before we enter into more technical details, +let us recall in a few words what is synchronization. + +\TeX\ typesetting system clearly separates the input and the output material, +and synchronization will provide a new link between both that can help +text editors and viewers to work together. +More precisely, forwards synchronization is the ability, +given a location in the input source file, +to find what is the corresponding place in the output. +Backwards synchronization just performs the opposite: +given a location in the output, +retrieve the corresponding material in the input source file. + +For better code management and maintainance, we adopt a naming convention. +Throughout this program, code related to the {\sl Synchronize \TeX nology} is tagged +with the ``{\sl synctex}'' key word. Any code extract where {\sl Sync\TeX} plays +its part, either explicitly or implicitly, (should) contain the string ``{\sl synctex}''. +This naming convention also holds for external files. +Moreover, all the code related to {\sl Sync\TeX} is gathered in this section, +except the definitions. + +Enabling synchronization should be performed from the command line, +|synctexoption| is used for that purpose. +This global integer variable is declared here but it is not used here. +This is just a placeholder where the command line controller will put +the {\sl Sync\TeX} related options, and the {\sl Sync\TeX} controller will read them. + +@c +int synctexoption; + + +@ A convenient primitive is provided: +\.{\\synctex=1} in the input source file enables synchronization whereas +\.{\\synctex=0} disables it. +Its memory address is |synctex_code|. +It is initialized by the {\sl Sync\TeX} controller to the command-line option if given. +The controller may filter some reserved bits. + +In order to give the {\sl Sync\TeX} controller read and write access to +the contents of the \.{\\synctex} primitive, we declare |synctexoffset|, +such that |mem[synctexoffset]| and \.{\\synctex} correspond to +the same memory storage. |synctexoffset| is initialized to +the correct value when quite everything is initialized. + +@c +int synctexoffset; /* holds the true value of |synctex_code| */ + + +@ Synchronization is achieved with the help of an auxiliary file named +`\.{{\sl jobname}.synctex}' ({\sl jobname} is the contents of the +\.{\\jobname} macro), where a {\sl Sync\TeX} controller implemented +in the external |synctex.c| file will store geometrical information. +This {\sl Sync\TeX} controller will take care of every technical details +concerning the {\sl Sync\TeX} file, we will only focus on the messages +the controller will receive from the \TeX\ program. + +The most accurate synchronization information should allow to map +any character of the input source file to the corresponding location +in the output, if relevant. +Ideally, the synchronization information of the input material consists of +the file name, the line and column numbers of every character. +The synchronization information in the output is simply the page number and +either point coordinates, or box dimensions and position. +The problem is that the mapping between these informations is only known at +ship out time, which means that we must keep track of the input +synchronization information until the pages ship out. + +As \TeX\ only knows about file names and line numbers, +but forgets the column numbers, we only consider a +restricted input synchronization information called {\sl Sync\TeX\ information}. +It consists of a unique file name identifier, the {\sl Sync\TeX\ file tag}, +and the line number. + +Keeping track of such information, +should be different whether characters or nodes are involved. +Actually, only certain nodes are involved in {\sl Sync\TeX}, +we call them {\sl synchronized nodes}. +Synchronized nodes store the {\sl Sync\TeX} information in their last two words: +the first one contains a {\sl Sync\TeX\ file tag} uniquely identifying +the input file, and the second one contains the current line number, +as returned by the \.{\\inputlineno} primitive. +The |synctex_field_size| macro contains the necessary size to store +the {\sl Sync\TeX} information in a node. + +When declaring the size of a new node, it is recommanded to use the following +convention: if the node is synchronized, use a definition similar to +|my_synchronized_node_size|={\sl xxx}+|synctex_field_size|. +Moreover, one should expect that the {\sl Sync\TeX} information is always stored +in the last two words of a synchronized node. + +By default, every node with a sufficiently big size is initialized +at creation time in the |get_node| routine with the current +{\sl Sync\TeX} information, whether or not the node is synchronized. +One purpose is to set this information very early in order to minimize code +dependencies, including forthcoming extensions. +Another purpose is to avoid the assumption that every node type has a dedicated getter, +where initialization should take place. Actually, it appears that some nodes are created +using directly the |get_node| routine and not the dedicated constructor. +And finally, initializing the node at only one place is less error prone. + +Instead of storing the input file name, it is better to store just an identifier. +Each time \TeX\ opens a new file, it notifies the {\sl Sync\TeX} controller with +a |synctex_start_input| message. +This controller will create a new {\sl Sync\TeX} file tag and +will update the current input state record accordingly. +If the input comes from the terminal or a pseudo file, the |synctex_tag| is set to 0. +It results in automatically disabling synchronization for material +input from the terminal or pseudo files. + + +Synchronized nodes are boxes, math, kern and glue nodes. +Other nodes should be synchronized too, in particular math noads. +\TeX\ assumes that math, kern and glue nodes have the same size, +this is why both are synchronized. +{\sl In fine}, only horizontal lists are really used in {\sl Sync\TeX}, +but all box nodes are considered the same with respect to synchronization, +because a box node type is allowed to change at execution time. + +{\sl Nota Bene:} +The {\sl Sync\TeX} code is very close to the memory model. +It is not connected to any other part of the code, +except for memory management. It is possible to neutralize the {\sl Sync\TeX} code +rather simply. The first step is to define a null |synctex_field_size|. +The second step is to comment out the code in ``Initialize bigger nodes...'' and every +``Copy ... {\sl Sync\TeX} information''. +The last step will be to comment out the |synctex_tag_field| related code in the +definition of |synctex_tag| and the various ``Prepare ... {\sl Sync\TeX} information''. +Then all the remaining code should be just harmless. +The resulting program would behave exactly the same as if absolutely no {\sl Sync\TeX} +related code was there, including memory management. +Of course, all this assumes that {\sl Sync\TeX} is turned off from the command line. +@^synctex@> +@^synchronization@> + + +@ Here are extra variables for Web2c. (This numbering of the +system-dependent section allows easy integration of Web2c and e-\TeX, etc.) +@^<system dependencies@> + +@c +pool_pointer edit_name_start; /* where the filename to switch to starts */ +int edit_name_length, edit_line; /* what line to start editing at */ +int ipcon; /* level of IPC action, 0 for none [default] */ +boolean stop_at_space; /* whether |more_name| returns false for space */ + +@ The |edit_name_start| will be set to point into |str_pool| somewhere after +its beginning if \TeX\ is supposed to switch to an editor on exit. + +@c +int shellenabledp; +int restrictedshell; +char *output_comment; + +@ Are we printing extra info as we read the format file? + +@c +boolean debug_format_file; diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.c b/Build/source/texk/web2c/luatexdir/tex/filename.c deleted file mode 100644 index f6b9746e976..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/filename.c +++ /dev/null @@ -1,353 +0,0 @@ -/* filename.c - - Copyright 2009 Taco Hoekwater <taco@luatex.org> - - This file is part of LuaTeX. - - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. - - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ - -#include "luatex-api.h" -#include <ptexlib.h> -#include "tokens.h" -#include "commands.h" - - -static const char _svn_version[] = - "$Id: filename.c 2448 2009-06-08 07:43:50Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/filename.c $"; - - -#define wake_up_terminal() ; -#define clear_terminal() ; - -#define cur_length (pool_ptr - str_start_macro(str_ptr)) - -#define batch_mode 0 /* omits all stops and omits terminal output */ -#define nonstop_mode 1 /* omits all stops */ -#define scroll_mode 2 /* omits error stops */ -#define error_stop_mode 3 /* stops at every opportunity to interact */ -#define biggest_char 1114111 - -/* put |ASCII_code| \# at the end of |str_pool| */ -#define append_char(A) str_pool[pool_ptr++]=(A) -#define str_room(A) check_pool_overflow((pool_ptr+(A))) - -/* - In order to isolate the system-dependent aspects of file names, the - @^system dependencies@> - system-independent parts of \TeX\ are expressed in terms - of three system-dependent - procedures called |begin_name|, |more_name|, and |end_name|. In - essence, if the user-specified characters of the file name are $c_1\ldots c_n$, - the system-independent driver program does the operations - $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); - \,|end_name|.$$ - These three procedures communicate with each other via global variables. - Afterwards the file name will appear in the string pool as three strings - called |cur_name|\penalty10000\hskip-.05em, - |cur_area|, and |cur_ext|; the latter two are null (i.e., - |""|), unless they were explicitly specified by the user. - - Actually the situation is slightly more complicated, because \TeX\ needs - to know when the file name ends. The |more_name| routine is a function - (with side effects) that returns |true| on the calls |more_name|$(c_1)$, - \dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ - returns |false|; or, it returns |true| and the token following $c_n$ is - something like `\.{\\hbox}' (i.e., not a character). In other words, - |more_name| is supposed to return |true| unless it is sure that the - file name has been completely scanned; and |end_name| is supposed to be able - to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of - whether $|more_name|(c_n)$ returned |true| or |false|. -*/ - -/* Here now is the first of the system-dependent routines for file name scanning. - @^system dependencies@> */ - -static void begin_name(void) -{ - area_delimiter = 0; - ext_delimiter = 0; - quoted_filename = false; -} - -/* And here's the second. The string pool might change as the file name is - being scanned, since a new \.{\\csname} might be entered; therefore we keep - |area_delimiter| and |ext_delimiter| relative to the beginning of the current - string, instead of assigning an absolute address like |pool_ptr| to them. - @^system dependencies@> */ - -static boolean more_name(ASCII_code c) -{ - if (c == ' ' && stop_at_space && (!quoted_filename)) { - return false; - } else if (c == '"') { - quoted_filename = !quoted_filename; - return true; - } else { - str_room(1); - append_char(c); /* contribute |c| to the current string */ - if (ISDIRSEP(c)) { - area_delimiter = cur_length; - ext_delimiter = 0; - } else if (c == '.') - ext_delimiter = cur_length; - return true; - } -} - -/* The third. - @^system dependencies@> - -*/ - -static void end_name(void) -{ - if (str_ptr + 3 > (max_strings + string_offset)) - overflow_string("number of strings", max_strings - init_str_ptr); - /* @:TeX capacity exceeded number of strings}{\quad number of strings@> */ - - if (area_delimiter == 0) { - cur_area = get_nullstr(); - } else { - cur_area = str_ptr; - str_start_macro(str_ptr + 1) = - str_start_macro(str_ptr) + area_delimiter; - incr(str_ptr); - } - if (ext_delimiter == 0) { - cur_ext = get_nullstr(); - cur_name = make_string(); - } else { - cur_name = str_ptr; - str_start_macro(str_ptr + 1) = - str_start_macro(str_ptr) + ext_delimiter - area_delimiter - 1; - incr(str_ptr); - cur_ext = make_string(); - } -} - -/* Now let's consider the ``driver'' routines by which \TeX\ deals with file names - in a system-independent manner. First comes a procedure that looks for a - file name in the input by calling |get_x_token| for the information. -*/ - -void scan_file_name(void) -{ - name_in_progress = true; - begin_name(); - /* @<Get the next non-blank non-call token@>; */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - - while (true) { - if ((cur_cmd > other_char_cmd) || (cur_chr > biggest_char)) { /* not a character */ - back_input(); - break; - } - /* If |cur_chr| is a space and we're not scanning a token list, check - whether we're at the end of the buffer. Otherwise we end up adding - spurious spaces to file names in some cases. */ - if ((cur_chr == ' ') && (state != token_list) && (loc > limit) - && !quoted_filename) - break; - if (cur_chr > 127) { - unsigned char *bytes; - unsigned char thebytes[4]; - unsigned char *end = (unsigned char *)utf8_idpb((char *) thebytes, cur_chr); - bytes = thebytes; - while (bytes<end) { - if (!more_name(*bytes)) - break; - bytes++; - } - } else { - if (!more_name(cur_chr)) - break; - } - get_x_token(); - } - end_name(); - name_in_progress = false; -} - - -/* - Here is a routine that manufactures the output file names, assuming that - |job_name<>0|. It ignores and changes the current settings of |cur_area| - and |cur_ext|. -*/ - -#define pack_cur_name() pack_file_name(cur_name,cur_area,cur_ext) - -void pack_job_name(const char *s) -{ /* |s = ".log"|, |".dvi"|, or |format_extension| */ - cur_area = get_nullstr(); - cur_ext = maketexstring(s); - cur_name = job_name; - pack_cur_name(); -} - -/* If some trouble arises when \TeX\ tries to open a file, the following - routine calls upon the user to supply another file name. Parameter~|s| - is used in the error message to identify the type of file; parameter~|e| - is the default extension if none is given. Upon exit from the routine, - variables |cur_name|, |cur_area|, |cur_ext|, and |nameoffile| are - ready for another attempt at file opening. -*/ - -void prompt_file_name(const char *s, const char *e) -{ - int k; /* index into |buffer| */ - str_number saved_cur_name; /* to catch empty terminal input */ - char prompt[256]; - str_number texprompt; - char *ar, *na, *ex; - saved_cur_name = cur_name; - if (interaction == scroll_mode) { - wake_up_terminal(); - } - ar = xstrdup(makecstring(cur_area)); - na = xstrdup(makecstring(cur_name)); - ex = xstrdup(makecstring(cur_ext)); - if (strcmp(s, "input file name") == 0) { /* @.I can't find file x@> */ - snprintf(prompt, 255, "I can't find file `%s%s%s'.", ar, na, ex); - } else { /*@.I can't write on file x@> */ - snprintf(prompt, 255, "I can't write on file `%s%s%s'.", ar, na, ex); - } - free(ar); - free(na); - free(ex); - texprompt = maketexstring((char *) prompt); - do_print_err(texprompt); - flush_str(texprompt); - if ((strcmp(e, ".tex") == 0) || (strcmp(e, "") == 0)) - show_context(); - tprint_nl("Please type another "); /*@.Please type...@> */ - tprint(s); - if (interaction < scroll_mode) - fatal_error(maketexstring - ("*** (job aborted, file error in nonstop mode)")); - clear_terminal(); - texprompt = maketexstring(": "); - prompt_input(texprompt); - flush_str(texprompt); - begin_name(); - k = first; - while ((buffer[k] == ' ') && (k < last)) - k++; - while (true) { - if (k == last) - break; - if (!more_name(buffer[k])) - break; - k++; - } - end_name(); - if (cur_ext == get_nullstr()) - cur_ext = maketexstring(e); - if (length(cur_name) == 0) - cur_name = saved_cur_name; - pack_cur_name(); -} - - -str_number make_name_string(void) -{ - int k; /* index into |nameoffile| */ - pool_pointer save_area_delimiter, save_ext_delimiter; - boolean save_name_in_progress, save_stop_at_space; - str_number ret; - if ((pool_ptr + namelength > pool_size) || - (str_ptr == max_strings) || (cur_length > 0)) { - ret = maketexstring("?"); - } else { - for (k = 1; k <= namelength; k++) - append_char(nameoffile[k]); - ret = make_string(); - } - /* At this point we also reset |cur_name|, |cur_ext|, and |cur_area| to - match the contents of |nameoffile|. */ - save_area_delimiter = area_delimiter; - save_ext_delimiter = ext_delimiter; - save_name_in_progress = name_in_progress; - save_stop_at_space = stop_at_space; - name_in_progress = true; - begin_name(); - stop_at_space = false; - k = 1; - while ((k <= namelength) && (more_name(nameoffile[k]))) - k++; - stop_at_space = save_stop_at_space; - end_name(); - name_in_progress = save_name_in_progress; - area_delimiter = save_area_delimiter; - ext_delimiter = save_ext_delimiter; - return ret; -} - - - -void print_file_name(str_number n, str_number a, str_number e) -{ - boolean must_quote; /* whether to quote the filename */ - pool_pointer j; /* index into |str_pool| */ - must_quote = false; - if (a != 0) { - j = str_start_macro(a); - while ((!must_quote) && (j < str_start_macro(a + 1))) { - must_quote = (str_pool[j] == ' '); - incr(j); - } - } - if (n != 0) { - j = str_start_macro(n); - while ((!must_quote) && (j < str_start_macro(n + 1))) { - must_quote = (str_pool[j] == ' '); - incr(j); - } - } - if (e != 0) { - j = str_start_macro(e); - while ((!must_quote) && (j < str_start_macro(e + 1))) { - must_quote = (str_pool[j] == ' '); - incr(j); - } - } - /* FIXME: Alternative is to assume that any filename that has to be quoted has - at least one quoted component...if we pick this, a number of insertions - of |print_file_name| should go away. - |must_quote|:=((|a|<>0)and(|str_pool|[|str_start|[|a|]]=""""))or - ((|n|<>0)and(|str_pool|[|str_start|[|n|]]=""""))or - ((|e|<>0)and(|str_pool|[|str_start|[|e|]]="""")); */ - - if (must_quote) - print_char('"'); - if (a != 0) { - for (j = str_start_macro(a); j <= str_start_macro(a + 1) - 1; j++) - if (str_pool[j] != '"') - print_char(str_pool[j]); - } - if (n != 0) { - for (j = str_start_macro(n); j <= str_start_macro(n + 1) - 1; j++) - if (str_pool[j] != '"') - print_char(str_pool[j]); - } - if (e != 0) { - for (j = str_start_macro(e); j <= str_start_macro(e + 1) - 1; j++) - if (str_pool[j] != '"') - print_char(str_pool[j]); - } - if (must_quote) - print_char('"'); -} diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.h b/Build/source/texk/web2c/luatexdir/tex/filename.h new file mode 100644 index 00000000000..d0e54283436 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/filename.h @@ -0,0 +1,34 @@ +/* filename.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: filename.h 3376 2010-01-25 16:50:23Z taco $ */ + +#ifndef FILENAME_H +# define FILENAME_H + +extern void scan_file_name(void); +extern void scan_file_name_toks(void); +extern char *pack_job_name(const char *s); +extern char *prompt_file_name(const char *s, const char *e); +extern void tprint_file_name(unsigned char *n, unsigned char *a, + unsigned char *e); +extern void print_file_name(str_number, str_number, str_number); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.w b/Build/source/texk/web2c/luatexdir/tex/filename.w new file mode 100644 index 00000000000..239127c5852 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/filename.w @@ -0,0 +1,349 @@ +% filename.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: filename.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/filename.w $"; + + +@ In order to isolate the system-dependent aspects of file names, the + @^system dependencies@> + system-independent parts of \TeX\ are expressed in terms + of three system-dependent + procedures called |begin_name|, |more_name|, and |end_name|. In + essence, if the user-specified characters of the file name are $c_1\ldots c_n$, + the system-independent driver program does the operations + $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); + \,|end_name|.$$ + These three procedures communicate with each other via global variables. + Afterwards the file name will appear in the string pool as three strings + called |cur_name|\penalty10000\hskip-.05em, + |cur_area|, and |cur_ext|; the latter two are null (i.e., + |""|), unless they were explicitly specified by the user. + + Actually the situation is slightly more complicated, because \TeX\ needs + to know when the file name ends. The |more_name| routine is a function + (with side effects) that returns |true| on the calls |more_name|$(c_1)$, + \dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ + returns |false|; or, it returns |true| and the token following $c_n$ is + something like `\.{\\hbox}' (i.e., not a character). In other words, + |more_name| is supposed to return |true| unless it is sure that the + file name has been completely scanned; and |end_name| is supposed to be able + to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of + whether $|more_name|(c_n)$ returned |true| or |false|. + + +@ Here now is the first of the system-dependent routines for file name scanning. +@^system dependencies@> + +@c +static void begin_name(void) +{ + area_delimiter = 0; + ext_delimiter = 0; + quoted_filename = false; +} + +@ And here's the second. The string pool might change as the file name is + being scanned, since a new \.{\\csname} might be entered; therefore we keep + |area_delimiter| and |ext_delimiter| relative to the beginning of the current + string, instead of assigning an absolute address like |pool_ptr| to them. + @^system dependencies@> + +@c +static boolean more_name(ASCII_code c) +{ + if (c == ' ' && stop_at_space && (!quoted_filename)) { + return false; + } else if (c == '"') { + quoted_filename = !quoted_filename; + return true; + } else { + str_room(1); + append_char(c); /* contribute |c| to the current string */ + if (ISDIRSEP(c)) { + area_delimiter = (pool_pointer) cur_length; + ext_delimiter = 0; + } else if (c == '.') + ext_delimiter = (pool_pointer) cur_length; + return true; + } +} + +@ The third. +@^system dependencies@> + +@c +static void end_name(void) +{ + unsigned char *s; + if (str_ptr + 3 > (max_strings + STRING_OFFSET)) + overflow("number of strings", + (unsigned) (max_strings - init_str_ptr + STRING_OFFSET)); + /* at this point, the full string lives in |cur_string| */ + if (area_delimiter == 0) { + cur_area = get_nullstr(); + } else { + s = (unsigned char *) xstrdup((char *) (cur_string + area_delimiter)); + cur_string[area_delimiter] = '\0'; + cur_length = (unsigned) strlen((char *) cur_string); + cur_area = make_string(); + cur_length = (unsigned) strlen((char *) s); + cur_string = s; + } + if (ext_delimiter == 0) { + cur_name = make_string(); + cur_ext = get_nullstr(); + } else { + int l = (ext_delimiter - area_delimiter - 1); + s = (unsigned char *) xstrdup((char *) (cur_string + l)); + cur_string[l] = '\0'; + cur_length = (unsigned) strlen((char *) cur_string); + cur_name = make_string(); + cur_length = (unsigned) strlen((char *) s); + cur_string = s; + cur_ext = make_string(); + } +} + +@ Now let's consider the ``driver'' routines by which \TeX\ deals with file names + in a system-independent manner. First comes a procedure that looks for a + file name in the input by calling |get_x_token| for the information. + +@c +void scan_file_name(void) +{ + str_number u = 0; + name_in_progress = true; + begin_name(); + /* Get the next non-blank non-call token; */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + while (true) { + if ((cur_cmd > other_char_cmd) || (cur_chr > biggest_char)) { /* not a character */ + back_input(); + break; + } + /* If |cur_chr| is a space and we're not scanning a token list, check + whether we're at the end of the buffer. Otherwise we end up adding + spurious spaces to file names in some cases. */ + if ((cur_chr == ' ') && (istate != token_list) && (iloc > ilimit) + && !quoted_filename) + break; + if (cur_chr > 127) { + unsigned char *bytes; + unsigned char *thebytes; + thebytes = uni2str((unsigned) cur_chr); + bytes = thebytes; + while (*bytes) { + if (!more_name(*bytes)) + break; + bytes++; + } + xfree(thebytes); + } else { + if (!more_name(cur_chr)) + break; + } + u = save_cur_string(); + get_x_token(); + restore_cur_string(u); + } + end_name(); + name_in_progress = false; +} + +@ This function constructs a the three file name strings from a token list +@c +void scan_file_name_toks(void) +{ + char *a, *n, *e, *s = NULL; + int i, l = 0; + (void) scan_toks(false, true); + s = tokenlist_to_cstring(def_ref, true, &l); + a = n = s; + e = NULL; + for (i = 0; i < l; i++) { + if (ISDIRSEP(s[i])) { + n = s + i + 1; + e = NULL; + } else if (s[i] == '.') { + e = s + i; + } + } + if (n != s) { /* explicit area */ + cur_area = maketexlstring(a, (size_t) (n - a)); + } else { + cur_area = get_nullstr(); + } + if (e != NULL) { /* explicit extension */ + cur_name = maketexlstring(n, (size_t) (e - n)); + cur_ext = maketexstring(e); + } else { + cur_name = maketexstring(n); + cur_ext = get_nullstr(); + } +} + + + + +@ Here is a routine that manufactures the output file names, assuming that + |job_name<>0|. It ignores and changes the current settings of |cur_area| + and |cur_ext|. + +@c +char *pack_job_name(const char *s) +{ /* |s = ".log"|, |".dvi"|, or |format_extension| */ + cur_area = get_nullstr(); + cur_ext = maketexstring(s); + cur_name = job_name; + return pack_file_name(cur_name, cur_area, cur_ext); +} + +@ If some trouble arises when \TeX\ tries to open a file, the following + routine calls upon the user to supply another file name. Parameter~|s| + is used in the error message to identify the type of file; parameter~|e| + is the default extension if none is given. Upon exit from the routine, + variables |cur_name|, |cur_area|, and |cur_ext| are + ready for another attempt at file opening. + +@c +char *prompt_file_name(const char *s, const char *e) +{ + int k; /* index into |buffer| */ + str_number saved_cur_name; /* to catch empty terminal input */ + char prompt[256]; + char *ar, *na, *ex; + saved_cur_name = cur_name; + if (interaction == scroll_mode) { + wake_up_terminal(); + } + ar = makecstring(cur_area); + na = makecstring(cur_name); + ex = makecstring(cur_ext); + if (strcmp(s, "input file name") == 0) { + snprintf(prompt, 255, "I can't find file `%s%s%s'.", ar, na, ex); + } else { + snprintf(prompt, 255, "I can't write on file `%s%s%s'.", ar, na, ex); + } + free(ar); + free(na); + free(ex); + print_err(prompt); + if ((strcmp(e, ".tex") == 0) || (strcmp(e, "") == 0)) + show_context(); + tprint_nl("Please type another "); + tprint(s); + if (interaction < scroll_mode) + fatal_error("*** (job aborted, file error in nonstop mode)"); + clear_terminal(); + prompt_input(": "); + begin_name(); + k = first; + while ((buffer[k] == ' ') && (k < last)) + k++; + while (true) { + if (k == last) + break; + if (!more_name(buffer[k])) + break; + k++; + } + end_name(); + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(e); + if (str_length(cur_name) == 0) + cur_name = saved_cur_name; + return pack_file_name(cur_name, cur_area, cur_ext); +} + +@ @c +void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e) +{ + boolean must_quote; /* whether to quote the filename */ + unsigned char *j; /* index into string */ + must_quote = false; + if (a != NULL) { + j = a; + while ((!must_quote) && (*j)) { + must_quote = (*j == ' '); + j++; + } + } + if (n != NULL) { + j = n; + while ((!must_quote) && (*j)) { + must_quote = (*j == ' '); + j++; + } + } + if (e != NULL) { + j = e; + while ((!must_quote) && (*j)) { + must_quote = (*j == ' '); + j++; + } + } + /* FIXME: Alternative is to assume that any filename that has to be quoted has + at least one quoted component...if we pick this, a number of insertions + of |print_file_name| should go away. + |must_quote|:=((|a|<>0)and(|str_pool|[|str_start|[|a|]]=""""))or + ((|n|<>0)and(|str_pool|[|str_start|[|n|]]=""""))or + ((|e|<>0)and(|str_pool|[|str_start|[|e|]]="""")); */ + + if (must_quote) + print_char('"'); + if (a != NULL) { + for (j = a; *j; j++) + if (*j != '"') + print_char(*j); + } + if (n != NULL) { + for (j = n; *j; j++) + if (*j != '"') + print_char(*j); + } + if (e != NULL) { + for (j = e; *j; j++) + if (*j != '"') + print_char(*j); + } + if (must_quote) + print_char('"'); +} + +@ @c +void print_file_name(str_number n, str_number a, str_number e) +{ + char *nam, *are, *ext; + nam = makecstring(n); + are = makecstring(a); + ext = makecstring(e); + tprint_file_name((unsigned char *) nam, (unsigned char *) are, + (unsigned char *) ext); + free(nam); + free(are); + free(ext); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/inputstack.h b/Build/source/texk/web2c/luatexdir/tex/inputstack.h new file mode 100644 index 00000000000..e77e9259c8f --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.h @@ -0,0 +1,353 @@ +/* inputstack.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: inputstack.h 3261 2009-12-18 11:38:21Z taco $ */ + +#ifndef INPUTSTACK_H +# define INPUTSTACK_H 1 + +/* + The state of \TeX's input mechanism appears in the input stack, whose + entries are records with six fields, called |state|, |index|, |start|, |loc|, + |limit|, and |name|. +*/ + +typedef struct in_state_record { + halfword start_field; + halfword loc_field; + halfword limit_field; + halfword name_field; + halfword ocp_lstack_field; /* used for omega translation processes */ + int synctex_tag_field; /* stack the tag of the current file */ + halfword ocp_no_field:16; /* used for omega translation processes */ + int cattable_field:16; /* category table used by the current line (see textoken.c) */ + quarterword state_field:8; + quarterword index_field:8; + boolean partial_field:8; /* is the current line partial? (see textoken.c) */ + boolean nofilter_field:8; /* used by token filtering */ +} in_state_record; + +extern in_state_record *input_stack; +extern int input_ptr; +extern int max_in_stack; +extern in_state_record cur_input; /* the ``top'' input state */ + +# define iloc cur_input.loc_field /* location of first unread character in |buffer| */ +# define istate cur_input.state_field /* current scanner state */ +# define iindex cur_input.index_field /* reference for buffer information */ +# define istart cur_input.start_field /* starting position in |buffer| */ +# define ilimit cur_input.limit_field /* end of current line in |buffer| */ +# define iname cur_input.name_field /* name of the current file */ +# define nofilter cur_input.nofilter_field /* is token filtering explicitly disallowed? */ +# define synctex_tag cur_input.synctex_tag_field /* tag of the current file */ +# define current_ocp_lstack cur_input.ocp_lstack_field /* name of the current ocp */ +# define current_ocp_no cur_input.ocp_no_field /* name of the current ocp */ +# define line_catcode_table cur_input.cattable_field +# define line_partial cur_input.partial_field + +/* +Let's look more closely now at the control variables +(|state|,~|index|,~|start|,~|loc|,~|limit|,~|name|), +assuming that \TeX\ is reading a line of characters that have been input +from some file or from the user's terminal. There is an array called +|buffer| that acts as a stack of all lines of characters that are +currently being read from files, including all lines on subsidiary +levels of the input stack that are not yet completed. \TeX\ will return to +the other lines when it is finished with the present input file. + +(Incidentally, on a machine with byte-oriented addressing, it might be +appropriate to combine |buffer| with the |str_pool| array, +letting the buffer entries grow downward from the top of the string pool +and checking that these two tables don't bump into each other.) + +The line we are currently working on begins in position |start| of the +buffer; the next character we are about to read is |buffer[loc]|; and +|limit| is the location of the last character present. If |loc>limit|, +the line has been completely read. Usually |buffer[limit]| is the +|end_line_char|, denoting the end of a line, but this is not +true if the current line is an insertion that was entered on the user's +terminal in response to an error message. + +The |name| variable is a string number that designates the name of +the current file, if we are reading a text file. It is zero if we +are reading from the terminal; it is |n+1| if we are reading from +input stream |n|, where |0<=n<=16|. (Input stream 16 stands for +an invalid stream number; in such cases the input is actually from +the terminal, under control of the procedure |read_toks|.) +Finally |18<=name<=20| indicates that we are reading a pseudo file +created by the \.{\\scantokens} or \.{\\scantextokens} command. + +The |state| variable has one of three values, when we are scanning such +files: +$$\baselineskip 15pt\vbox{\halign{#\hfil\cr +1) |state=mid_line| is the normal state.\cr +2) |state=skip_blanks| is like |mid_line|, but blanks are ignored.\cr +3) |state=new_line| is the state at the beginning of a line.\cr}}$$ +These state values are assigned numeric codes so that if we add the state +code to the next character's command code, we get distinct values. For +example, `|mid_line+spacer|' stands for the case that a blank +space character occurs in the middle of a line when it is not being +ignored; after this case is processed, the next value of |state| will +be |skip_blanks|. +*/ + +# define max_char_code 15 /* largest catcode for individual characters */ + +typedef enum { + mid_line = 1, /* |state| code when scanning a line of characters */ + skip_blanks = 2 + max_char_code, /* |state| code when ignoring blanks */ + new_line = 3 + max_char_code + max_char_code, /* |state| code at start of line */ +} state_codes; + +/* +Additional information about the current line is available via the +|index| variable, which counts how many lines of characters are present +in the buffer below the current level. We have |index=0| when reading +from the terminal and prompting the user for each line; then if the user types, +e.g., `\.{\\input paper}', we will have |index=1| while reading +the file \.{paper.tex}. However, it does not follow that |index| is the +same as the input stack pointer, since many of the levels on the input +stack may come from token lists. For example, the instruction `\.{\\input +paper}' might occur in a token list. + +The global variable |in_open| is equal to the |index| +value of the highest non-token-list level. Thus, the number of partially read +lines in the buffer is |in_open+1|, and we have |in_open=index| +when we are not reading a token list. + +If we are not currently reading from the terminal, or from an input +stream, we are reading from the file variable |input_file[index]|. We use +the notation |terminal_input| as a convenient abbreviation for |name=0|, +and |cur_file| as an abbreviation for |input_file[index]|. + +The global variable |line| contains the line number in the topmost +open file, for use in error messages. If we are not reading from +the terminal, |line_stack[index]| holds the line number for the +enclosing level, so that |line| can be restored when the current +file has been read. Line numbers should never be negative, since the +negative of the current line number is used to identify the user's output +routine in the |mode_line| field of the semantic nest entries. + +If more information about the input state is needed, it can be +included in small arrays like those shown here. For example, +the current page or segment number in the input file might be +put into a variable |@!page|, maintained for enclosing levels in +`\ignorespaces|@!page_stack:array[1..max_in_open] of integer|\unskip' +by analogy with |line_stack|. +@^system dependencies@> +*/ + +# define terminal_input (iname==0) /* are we reading from the terminal? */ +# define cur_file input_file[iindex] /* the current |alpha_file| variable */ + +extern int in_open; +extern int open_parens; +extern alpha_file *input_file; +extern int line; +extern int *line_stack; +extern str_number *source_filename_stack; +extern char **full_source_filename_stack; + +/* +Users of \TeX\ sometimes forget to balance left and right braces properly, +and one of the ways \TeX\ tries to spot such errors is by considering an +input file as broken into subfiles by control sequences that +are declared to be \.{\\outer}. + +A variable called |scanner_status| tells \TeX\ whether or not to complain +when a subfile ends. This variable has six possible values: + +\yskip\hang|normal|, means that a subfile can safely end here without incident. + +\yskip\hang|skipping|, means that a subfile can safely end here, but not a file, +because we're reading past some conditional text that was not selected. + +\yskip\hang|defining|, means that a subfile shouldn't end now because a +macro is being defined. + +\yskip\hang|matching|, means that a subfile shouldn't end now because a +macro is being used and we are searching for the end of its arguments. + +\yskip\hang|aligning|, means that a subfile shouldn't end now because we are +not finished with the preamble of an \.{\\halign} or \.{\\valign}. + +\yskip\hang|absorbing|, means that a subfile shouldn't end now because we are +reading a balanced token list for \.{\\message}, \.{\\write}, etc. + +\yskip\noindent +If the |scanner_status| is not |normal|, the variable |warning_index| points +to the |eqtb| location for the relevant control sequence name to print +in an error message. +*/ + +typedef enum { + skipping = 1, /* |scanner_status| when passing conditional text */ + defining = 2, /* |scanner_status| when reading a macro definition */ + matching = 3, /* |scanner_status| when reading macro arguments */ + aligning = 4, /* |scanner_status| when reading an alignment preamble */ + absorbing = 5, /* |scanner_status| when reading a balanced text */ +} scanner_states; + +extern int scanner_status; +extern pointer warning_index; +extern pointer def_ref; + +extern void runaway(void); + +/* +However, the discussion about input state really applies only to the +case that we are inputting from a file. There is another important case, +namely when we are currently getting input from a token list. In this case +|state=token_list|, and the conventions about the other state variables +are different: + +\yskip\hang|loc| is a pointer to the current node in the token list, i.e., +the node that will be read next. If |loc=null|, the token list has been +fully read. + +\yskip\hang|start| points to the first node of the token list; this node +may or may not contain a reference count, depending on the type of token +list involved. + +\yskip\hang|token_type|, which takes the place of |index| in the +discussion above, is a code number that explains what kind of token list +is being scanned. + +\yskip\hang|name| points to the |eqtb| address of the control sequence +being expanded, if the current token list is a macro. + +\yskip\hang|param_start|, which takes the place of |limit|, tells where +the parameters of the current macro begin in the |param_stack|, if the +current token list is a macro. + +\yskip\noindent The |token_type| can take several values, depending on +where the current token list came from: + +\yskip\hang|parameter|, if a parameter is being scanned; + +\hang|u_template|, if the \<u_j> part of an alignment +template is being scanned; + +\hang|v_template|, if the \<v_j> part of an alignment +template is being scanned; + +\hang|backed_up|, if the token list being scanned has been inserted as +`to be read again'. + +\hang|inserted|, if the token list being scanned has been inserted as +the text expansion of a \.{\\count} or similar variable; + +\hang|macro|, if a user-defined control sequence is being scanned; + +\hang|output_text|, if an \.{\\output} routine is being scanned; + +\hang|every_par_text|, if the text of \.{\\everypar} is being scanned; + +\hang|every_math_text|, if the text of \.{\\everymath} is being scanned; + +\hang|every_display_text|, if the text of \.{\\everydisplay} is being scanned; + +\hang|every_hbox_text|, if the text of \.{\\everyhbox} is being scanned; + +\hang|every_vbox_text|, if the text of \.{\\everyvbox} is being scanned; + +\hang|every_job_text|, if the text of \.{\\everyjob} is being scanned; + +\hang|every_cr_text|, if the text of \.{\\everycr} is being scanned; + +\hang|mark_text|, if the text of a \.{\\mark} is being scanned; + +\hang|write_text|, if the text of a \.{\\write} is being scanned. + +\yskip\noindent +The codes for |output_text|, |every_par_text|, etc., are equal to a constant +plus the corresponding codes for token list parameters |output_routine_loc|, +|every_par_loc|, etc. + +The token list begins with a reference count if and +only if |token_type>=macro|. +@^reference counts@> + +Since \eTeX's additional token list parameters precede |toks_base|, the +corresponding token types must precede |write_text|. +*/ + +# define token_list 0 /* |state| code when scanning a token list */ +# define token_type iindex /* type of current token list */ +# define param_start ilimit /* base of macro parameters in |param_stack| */ + + +typedef enum { + parameter = 0, /* |token_type| code for parameter */ + u_template = 1, /* |token_type| code for \<u_j> template */ + v_template = 2, /* |token_type| code for \<v_j> template */ + backed_up = 3, /* |token_type| code for text to be reread */ + inserted = 4, /* |token_type| code for inserted texts */ + macro = 5, /* |token_type| code for defined control sequences */ + output_text = 6, /* |token_type| code for output routines */ + every_par_text = 7, /* |token_type| code for \.{\\everypar} */ + every_math_text = 8, /* |token_type| code for \.{\\everymath} */ + every_display_text = 9, /* |token_type| code for \.{\\everydisplay} */ + every_hbox_text = 10, /* |token_type| code for \.{\\everyhbox} */ + every_vbox_text = 11, /* |token_type| code for \.{\\everyvbox} */ + every_job_text = 12, /* |token_type| code for \.{\\everyjob} */ + every_cr_text = 13, /* |token_type| code for \.{\\everycr} */ + mark_text = 14, /* |token_type| code for \.{\\topmark}, etc. */ + every_eof_text = 15, /* |token_type| code for \.{\\everyeof} */ + write_text = 16, /* |token_type| code for \.{\\write} */ +} token_types; + +extern pointer *param_stack; +extern int param_ptr; +extern int max_param_stack; + +extern int align_state; + +extern int base_ptr; + +extern void show_context(void); +extern void set_trick_count(void); + +/* leave an input level, re-enter the old */ +# define pop_input() cur_input=input_stack[--input_ptr] + +extern void push_input(void); + +# define back_list(A) begin_token_list(A,backed_up) /* backs up a simple token list */ +# define ins_list(A) begin_token_list(A,inserted) /* inserts a simple token list */ + +extern void begin_token_list(halfword p, quarterword t); +extern void end_token_list(void); +extern void back_input(void); +extern int reinsert_token(boolean a, halfword pp); +extern void begin_file_reading(void); +extern void end_file_reading(void); +extern void clear_for_error_prompt(void); + +extern void initialize_inputstack(void); + +extern halfword pseudo_files; +extern void pseudo_from_string(void); +extern void pseudo_start(void); +extern void lua_string_start(void); +extern boolean pseudo_input(void); +extern void pseudo_close(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/inputstack.w b/Build/source/texk/web2c/luatexdir/tex/inputstack.w new file mode 100644 index 00000000000..ecb620fb185 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.w @@ -0,0 +1,800 @@ +% inputstack.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: inputstack.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/inputstack.w $"; + +@ @c +#define end_line_char int_par(end_line_char_code) +#define error_context_lines int_par(error_context_lines_code) + + +in_state_record *input_stack = NULL; +int input_ptr = 0; /* first unused location of |input_stack| */ +int max_in_stack = 0; /* largest value of |input_ptr| when pushing */ +in_state_record cur_input; /* the ``top'' input state */ + + +int in_open = 0; /* the number of lines in the buffer, less one */ +int open_parens = 0; /* the number of open text files */ +alpha_file *input_file = NULL; +int line = 0; /* current line number in the current source file */ +int *line_stack = NULL; +str_number *source_filename_stack = NULL; +char **full_source_filename_stack = NULL; + + +int scanner_status = 0; /* can a subfile end now? */ +pointer warning_index = null; /* identifier relevant to non-|normal| scanner status */ +pointer def_ref = null; /* reference count of token list being defined */ + + + +@ Here is a procedure that uses |scanner_status| to print a warning message +when a subfile has ended, and at certain other crucial times: + +@c +void runaway(void) +{ + pointer p = null; /* head of runaway list */ + if (scanner_status > skipping) { + switch (scanner_status) { + case defining: + tprint_nl("Runaway definition"); + p = def_ref; + break; + case matching: + tprint_nl("Runaway argument"); + p = temp_token_head; + break; + case aligning: + tprint_nl("Runaway preamble"); + p = hold_token_head; + break; + case absorbing: + tprint_nl("Runaway text"); + p = def_ref; + break; + default: /* there are no other cases */ + break; + } + print_char('?'); + print_ln(); + show_token_list(token_link(p), null, error_line - 10); + } +} + + +@ The |param_stack| is an auxiliary array used to hold pointers to the token +lists for parameters at the current level and subsidiary levels of input. +This stack is maintained with convention (2), and it grows at a different +rate from the others. + +@c +pointer *param_stack = NULL; /* token list pointers for parameters */ +int param_ptr = 0; /* first unused entry in |param_stack| */ +int max_param_stack = 0; /* largest value of |param_ptr|, will be |<=param_size+9| */ + +@ The input routines must also interact with the processing of +\.{\\halign} and \.{\\valign}, since the appearance of tab marks and +\.{\\cr} in certain places is supposed to trigger the beginning of special +$v_j$ template text in the scanner. This magic is accomplished by an +|align_state| variable that is increased by~1 when a `\.{\char'173}' is +scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state| +is nonzero during the $u_j$ template, after which it is set to zero; the +$v_j$ template begins when a tab mark or \.{\\cr} occurs at a time that +|align_state=0|. + +@c +int align_state = 0; /* group level with respect to current alignment */ + + +@ Thus, the ``current input state'' can be very complicated indeed; there +can be many levels and each level can arise in a variety of ways. The +|show_context| procedure, which is used by \TeX's error-reporting routine to +print out the current input state on all levels down to the most recent +line of characters from an input file, illustrates most of these conventions. +The global variable |base_ptr| contains the lowest level that was +displayed by this procedure. + +@c +int base_ptr = 0; /* shallowest level shown by |show_context| */ + + +@ The status at each level is indicated by printing two lines, where the first +line indicates what was read so far and the second line shows what remains +to be read. The context is cropped, if necessary, so that the first line +contains at most |half_error_line| characters, and the second contains +at most |error_line|. Non-current input levels whose |token_type| is +`|backed_up|' are shown only if they have not been fully read. + +@c +void print_token_list_type(int t) +{ + switch (t) { + case parameter: + tprint_nl("<argument> "); + break; + case u_template: + case v_template: + tprint_nl("<template> "); + break; + case backed_up: + if (iloc == null) + tprint_nl("<recently read> "); + else + tprint_nl("<to be read again> "); + break; + case inserted: + tprint_nl("<inserted text> "); + break; + case macro: + print_ln(); + print_cs(iname); + break; + case output_text: + tprint_nl("<output> "); + break; + case every_par_text: + tprint_nl("<everypar> "); + break; + case every_math_text: + tprint_nl("<everymath> "); + break; + case every_display_text: + tprint_nl("<everydisplay> "); + break; + case every_hbox_text: + tprint_nl("<everyhbox> "); + break; + case every_vbox_text: + tprint_nl("<everyvbox> "); + break; + case every_job_text: + tprint_nl("<everyjob> "); + break; + case every_cr_text: + tprint_nl("<everycr> "); + break; + case mark_text: + tprint_nl("<mark> "); + break; + case every_eof_text: + tprint_nl("<everyeof> "); + break; + case write_text: + tprint_nl("<write> "); + break; + default: + tprint_nl("?"); + break; /* this should never happen */ + } +} + + +@ Here it is necessary to explain a little trick. We don't want to store a long +string that corresponds to a token list, because that string might take up +lots of memory; and we are printing during a time when an error message is +being given, so we dare not do anything that might overflow one of \TeX's +tables. So `pseudoprinting' is the answer: We enter a mode of printing +that stores characters into a buffer of length |error_line|, where character +$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if +|k<trick_count|, otherwise character |k| is dropped. Initially we set +|tally:=0| and |trick_count:=1000000|; then when we reach the +point where transition from line 1 to line 2 should occur, we +set |first_count:=tally| and |trick_count:=@tmax@>(error_line, +tally+1+error_line-half_error_line)|. At the end of the +pseudoprinting, the values of |first_count|, |tally|, and +|trick_count| give us all the information we need to print the two lines, +and all of the necessary text is in |trick_buf|. + +Namely, let |l| be the length of the descriptive information that appears +on the first line. The length of the context information gathered for that +line is |k=first_count|, and the length of the context information +gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|, +where |h=half_error_line|, we print |trick_buf[0..k-1]| after the +descriptive information on line~1, and set |n:=l+k|; here |n| is the +length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h| +and print `\.{...}' followed by +$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$ +where subscripts of |trick_buf| are circular modulo |error_line|. The +second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|, +unless |n+m>error_line|; in the latter case, further cropping is done. +This is easier to program than to explain. + + + +@ The following code sets up the print routines so that they will gather +the desired information. + +@c +void set_trick_count(void) +{ + first_count = tally; + trick_count = tally + 1 + error_line - half_error_line; + if (trick_count < error_line) + trick_count = error_line; +} + +#define begin_pseudoprint() do { \ + l=tally; tally=0; selector=pseudo; \ + trick_count=1000000; \ + } while (0) + +#define PSEUDO_PRINT_THE_LINE() do { \ + begin_pseudoprint(); \ + if (buffer[ilimit]==end_line_char) j=ilimit; \ + else j=ilimit+1; /* determine the effective end of the line */ \ + if (j>0) { \ + for (i=istart;i<=j-1;i++) { \ + if (i==iloc) set_trick_count(); \ + print_char(buffer[i]); \ + } \ + } \ + } while (0) + +@ @c +void show_context(void) +{ /* prints where the scanner is */ + int old_setting; /* saved |selector| setting */ + int nn; /* number of contexts shown so far, less one */ + boolean bottom_line; /* have we reached the final context to be shown? */ + int i; /* index into |buffer| */ + int j; /* end of current line in |buffer| */ + int l; /* length of descriptive information on line 1 */ + int m; /* context information gathered for line 2 */ + int n; /* length of line 1 */ + int p; /* starting or ending place in |trick_buf| */ + int q; /* temporary index */ + + base_ptr = input_ptr; + input_stack[base_ptr] = cur_input; + /* store current state */ + nn = -1; + bottom_line = false; + while (true) { + cur_input = input_stack[base_ptr]; /* enter into the context */ + if (istate != token_list) { + if ((iname > 21) || (base_ptr == 0)) + bottom_line = true; + } + if ((base_ptr == input_ptr) || bottom_line + || (nn < error_context_lines)) { + /* Display the current context */ + if ((base_ptr == input_ptr) || (istate != token_list) || + (token_type != backed_up) || (iloc != null)) { + /* we omit backed-up token lists that have already been read */ + tally = 0; /* get ready to count characters */ + old_setting = selector; + if (current_ocp_lstack > 0) { + tprint_nl("OCP stack "); + print_scaled(current_ocp_lstack); + tprint(" entry "); + print_int(current_ocp_no); + tprint(":"); + PSEUDO_PRINT_THE_LINE(); + } else if (istate != token_list) { + /* Print location of current line */ + /* + This routine should be changed, if necessary, to give the best possible + indication of where the current line resides in the input file. + For example, on some systems it is best to print both a page and line number. + */ + if (iname <= 17) { + if (terminal_input) { + if (base_ptr == 0) + tprint_nl("<*>"); + else + tprint_nl("<insert> "); + } else { + tprint_nl("<read "); + if (iname == 17) + print_char('*'); + else + print_int(iname - 1); + print_char('>'); + }; + } else if (iindex != in_open) { /* input from a pseudo file */ + tprint_nl("l."); + print_int(line_stack[iindex + 1]); + } else { + tprint_nl("l."); + print_int(line); + } + print_char(' '); + PSEUDO_PRINT_THE_LINE(); + } else { + print_token_list_type(token_type); + + begin_pseudoprint(); + if (token_type < macro) + show_token_list(istart, iloc, 100000); + else + show_token_list(token_link(istart), iloc, 100000); /* avoid reference count */ + } + selector = old_setting; /* stop pseudoprinting */ + /* Print two lines using the tricky pseudoprinted information */ + if (trick_count == 1000000) + set_trick_count(); + /* |set_trick_count| must be performed */ + if (tally < trick_count) + m = tally - first_count; + else + m = trick_count - first_count; /* context on line 2 */ + if (l + first_count <= half_error_line) { + p = 0; + n = l + first_count; + } else { + tprint("..."); + p = l + first_count - half_error_line + 3; + n = half_error_line; + } + for (q = p; q <= first_count - 1; q++) + print_char(trick_buf[(q % error_line)]); + print_ln(); + for (q = 1; q <= n; q++) + print_char(' '); /* print |n| spaces to begin line~2 */ + if (m + n <= error_line) + p = first_count + m; + else + p = first_count + (error_line - n - 3); + for (q = first_count; q <= p - 1; q++) + print_char(trick_buf[(q % error_line)]); + if (m + n > error_line) + tprint("..."); + + incr(nn); + } + } else if (nn == error_context_lines) { + tprint_nl("..."); + incr(nn); /* omitted if |error_context_lines<0| */ + } + if (bottom_line) + break; + decr(base_ptr); + } + cur_input = input_stack[input_ptr]; /* restore original state */ +} + + +@ The following subroutines change the input status in commonly needed ways. + +First comes |push_input|, which stores the current state and creates a +new level (having, initially, the same properties as the old). + +@c +/* enter a new input level, save the old */ + +void push_input(void) +{ + if (input_ptr > max_in_stack) { + max_in_stack = input_ptr; + if (input_ptr == stack_size) + overflow("input stack size", (unsigned) stack_size); + } + input_stack[input_ptr] = cur_input; /* stack the record */ + nofilter = false; + incr(input_ptr); +} + +@ +Here is a procedure that starts a new level of token-list input, given +a token list |p| and its type |t|. If |t=macro|, the calling routine should +set |name| and |loc|. + +@c +void begin_token_list(halfword p, quarterword t) +{ + push_input(); + istate = token_list; + istart = p; + token_type = (unsigned char) t; + if (t >= macro) { /* the token list starts with a reference count */ + add_token_ref(p); + if (t == macro) { + param_start = param_ptr; + } else { + iloc = token_link(p); + if (int_par(tracing_macros_code) > 1) { + begin_diagnostic(); + tprint_nl(""); + if (t == mark_text) + tprint_esc("mark"); + else if (t == write_text) + tprint_esc("write"); + else + print_cmd_chr(assign_toks_cmd, + t - output_text + output_routine_loc); + tprint("->"); + token_show(p); + end_diagnostic(false); + } + } + } else { + iloc = p; + } +} + + +@ When a token list has been fully scanned, the following computations +should be done as we leave that level of input. The |token_type| tends +to be equal to either |backed_up| or |inserted| about 2/3 of the time. +@^inner loop@> + +@c +void end_token_list(void) +{ /* leave a token-list input level */ + if (token_type >= backed_up) { /* token list to be deleted */ + if (token_type <= inserted) { + flush_list(istart); + } else { + delete_token_ref(istart); /* update reference count */ + if (token_type == macro) { /* parameters must be flushed */ + while (param_ptr > param_start) { + decr(param_ptr); + flush_list(param_stack[param_ptr]); + } + } + } + } else if (token_type == u_template) { + if (align_state > 500000) + align_state = 0; + else + fatal_error("(interwoven alignment preambles are not allowed)"); + } + pop_input(); + check_interrupt(); +} + + +@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has +seen. The |back_input| procedure takes care of this by putting the token +just scanned back into the input stream, ready to be read again. This +procedure can be used only if |cur_tok| represents the token to be +replaced. Some applications of \TeX\ use this procedure a lot, +so it has been slightly optimized for speed. +@^inner loop@> + +@c +void back_input(void) +{ /* undoes one token of input */ + halfword p; /* a token list of length one */ + while ((istate == token_list) && (iloc == null) + && (token_type != v_template)) + end_token_list(); /* conserve stack space */ + p = get_avail(); + set_token_info(p, cur_tok); + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) + decr(align_state); + else + incr(align_state); + } + push_input(); + istate = token_list; + istart = p; + token_type = backed_up; + iloc = p; /* that was |back_list(p)|, without procedure overhead */ +} + +@ Insert token |p| into \TeX's input +@c +int reinsert_token(boolean a, halfword pp) +{ + halfword t; + t = cur_tok; + cur_tok = pp; + if (a) { + halfword p; + p = get_avail(); + set_token_info(p, cur_tok); + set_token_link(p, iloc); + iloc = p; + istart = p; + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) + decr(align_state); + else + incr(align_state); + } + } else { + back_input(); + a = true; /* etex is always on */ + } + cur_tok = t; + return a; +} + + +@ The |begin_file_reading| procedure starts a new level of input for lines +of characters to be read from a file, or as an insertion from the +terminal. It does not take care of opening the file, nor does it set |loc| +or |limit| or |line|. +@^system dependencies@> + +@c +void begin_file_reading(void) +{ + if (in_open == max_in_open) + overflow("text input levels", (unsigned) max_in_open); + if (first == buf_size) + check_buffer_overflow(first); + incr(in_open); + push_input(); + iindex = (unsigned char) in_open; + source_filename_stack[iindex] = 0; + full_source_filename_stack[iindex] = NULL; + eof_seen[iindex] = false; + grp_stack[iindex] = cur_boundary; + if_stack[iindex] = cond_ptr; + line_stack[iindex] = line; + istart = first; + istate = mid_line; + iname = 0; /* |terminal_input| is now |true| */ + line_catcode_table = DEFAULT_CAT_TABLE; + line_partial = false; + /* Prepare terminal input {\sl Sync\TeX} information */ + synctex_tag = 0; +} + + +@ Conversely, the variables must be downdated when such a level of input +is finished: + +@c +void end_file_reading(void) +{ + first = istart; + line = line_stack[iindex]; + if ((iname >= 18) && (iname <= 20)) + pseudo_close(); + else if (iname == 21) + luacstring_close(iindex); + else if (iname > 17) + lua_a_close_in(cur_file, 0); /* forget it */ + pop_input(); + decr(in_open); +} + + +@ In order to keep the stack from overflowing during a long sequence of +inserted `\.{\\show}' commands, the following routine removes completed +error-inserted lines from memory. + +@c +void clear_for_error_prompt(void) +{ + while ((istate != token_list) && terminal_input + && (input_ptr > 0) && (iloc > ilimit)) + end_file_reading(); + print_ln(); + clear_terminal(); +} + +@ To get \TeX's whole input mechanism going, we perform the following + actions. + +@c +void initialize_inputstack(void) +{ + input_ptr = 0; + max_in_stack = 0; + source_filename_stack[0] = 0; + + full_source_filename_stack[0] = NULL; + in_open = 0; + open_parens = 0; + max_buf_stack = 0; + + grp_stack[0] = 0; + if_stack[0] = null; + param_ptr = 0; + max_param_stack = 0; + first = buf_size; + do { + buffer[first] = 0; + decr(first); + } while (first != 0); + scanner_status = normal; + warning_index = null; + first = 1; + istate = new_line; + istart = 1; + iindex = 0; + line = 0; + iname = 0; + current_ocp_lstack = 0; + current_ocp_no = 0; + nofilter = false; + force_eof = false; + luacstrings = 0; + line_catcode_table = DEFAULT_CAT_TABLE; + line_partial = false; + align_state = 1000000; + if (!init_terminal()) + exit(EXIT_FAILURE); /* |goto final_end|; */ + ilimit = last; + first = last + 1; /* |init_terminal| has set |loc| and |last| */ +} + + + + +@ The global variable |pseudo_files| is used to maintain a stack of +pseudo files. The |pseudo_lines| field of each pseudo file points to +a linked list of variable size nodes representing lines not yet +processed: the |subtype| field contains the size of this node, +all the following words contain ASCII codes. + +@c +halfword pseudo_files; /* stack of pseudo files */ + +static halfword string_to_pseudo(str_number str, int nl) +{ + halfword i, r, q = null; + unsigned l, len; + four_quarters w; + int sz; + halfword h = new_node(pseudo_file_node, 0); + unsigned char *s = str_string(str); + len = (unsigned) str_length(str); + l = 0; + while (l < len) { + unsigned m = l; /* start of current line */ + while ((l < len) && (s[l] != nl)) + l++; + sz = (int) (l - m + 7) / 4; + if (sz == 1) + sz = 2; + r = new_node(pseudo_line_node, sz); + i = r; + while (--sz > 1) { + w.b0 = s[m++]; + w.b1 = s[m++]; + w.b2 = s[m++]; + w.b3 = s[m++]; + varmem[++i].qqqq = w; + } + w.b0 = (quarterword) (l > m ? s[m++] : ' '); + w.b1 = (quarterword) (l > m ? s[m++] : ' '); + w.b2 = (quarterword) (l > m ? s[m++] : ' '); + w.b3 = (quarterword) (l > m ? s[m] : ' '); + varmem[++i].qqqq = w; + if (pseudo_lines(h) == null) { + pseudo_lines(h) = r; + q = r; + } else { + couple_nodes(q, r); + } + q = vlink(q); + if (s[l] == nl) + l++; + } + return h; +} + + +@ The |pseudo_start| procedure initiates reading from a pseudo file. + +@c +void pseudo_from_string(void) +{ + str_number s; /* string to be converted into a pseudo file */ + halfword p; /* for list construction */ + s = make_string(); + /* Convert string |s| into a new pseudo file */ + p = string_to_pseudo(s, int_par(new_line_char_code)); + vlink(p) = pseudo_files; + pseudo_files = p; + flush_str(s); + /* Initiate input from new pseudo file */ + begin_file_reading(); /* set up |cur_file| and new level of input */ + line = 0; + ilimit = istart; + iloc = ilimit + 1; /* force line read */ + if (int_par(tracing_scan_tokens_code) > 0) { + if (term_offset > max_print_line - 3) + print_ln(); + else if ((term_offset > 0) || (file_offset > 0)) + print_char(' '); + iname = 20; + tprint("( "); + incr(open_parens); + update_terminal(); + } else { + iname = 18; + } + /* Prepare pseudo file {\sl Sync\TeX} information */ + synctex_tag = 0; +} + +void pseudo_start(void) +{ + int old_setting; /* holds |selector| setting */ + scan_general_text(); + old_setting = selector; + selector = new_string; + token_show(temp_token_head); + selector = old_setting; + flush_list(token_link(temp_token_head)); + str_room(1); + pseudo_from_string(); +} + +@ @c +void lua_string_start(void) +{ + begin_file_reading(); /* set up |cur_file| and new level of input */ + line = 0; + ilimit = istart; + iloc = ilimit + 1; /* force line read */ + iname = 21; + luacstring_start(iindex); +} + +@ Here we read a line from the current pseudo file into |buffer|. + +@c +boolean pseudo_input(void) +{ /* inputs the next line or returns |false| */ + halfword p; /* current line from pseudo file */ + int sz; /* size of node |p| */ + four_quarters w; /* four ASCII codes */ + halfword r; /* loop index */ + last = first; /* cf.\ Matthew 19\thinspace:\thinspace30 */ + p = pseudo_lines(pseudo_files); + if (p == null) { + return false; + } else { + pseudo_lines(pseudo_files) = vlink(p); + sz = subtype(p); + if (4 * sz - 3 >= buf_size - last) + check_buffer_overflow(last + 4 * sz); + last = first; + for (r = p + 1; r <= p + sz - 1; r++) { + w = varmem[r].qqqq; + buffer[last] = (packed_ASCII_code) w.b0; + buffer[last + 1] = (packed_ASCII_code) w.b1; + buffer[last + 2] = (packed_ASCII_code) w.b2; + buffer[last + 3] = (packed_ASCII_code) w.b3; + last += 4; + } + if (last >= max_buf_stack) + max_buf_stack = last + 1; + while ((last > first) && (buffer[last - 1] == ' ')) + decr(last); + flush_node(p); + } + return true; +} + +@ When we are done with a pseudo file we `close' it + +@c +void pseudo_close(void) +{ /* close the top level pseudo file */ + halfword p; + p = vlink(pseudo_files); + flush_node(pseudo_files); + pseudo_files = p; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.h b/Build/source/texk/web2c/luatexdir/tex/linebreak.h new file mode 100644 index 00000000000..791a5f0de7c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.h @@ -0,0 +1,106 @@ +/* linebreak.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: linebreak.h 3526 2010-03-19 20:56:38Z taco $ */ + +#ifndef LINEBREAK_H +# define LINEBREAK_H + +# define left_side 0 +# define right_side 1 + +extern halfword just_box; /* the |hlist_node| for the last line of the new paragraph */ + +extern void line_break(boolean d, int line_break_context); + +# define inf_bad 10000 /* infinitely bad value */ +# define awful_bad 07777777777 /* more than a billion demerits */ + +extern void initialize_active(void); + +extern void ext_do_line_break(int paragraph_dir, + int pretolerance, + int tracing_paragraphs, + int tolerance, + scaled emergency_stretch, + int looseness, + int hyphen_penalty, + int ex_hyphen_penalty, + int 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 widow_penalties_ptr, + int widow_penalty, + int broken_penalty, halfword final_par_glue, + halfword pdf_ignored_dimen); + +extern void get_linebreak_info (int *, int *) ; +extern halfword find_protchar_left(halfword l, boolean d); +extern halfword find_protchar_right(halfword l, halfword r); + +/* skipable nodes at the margins during character protrusion */ + +#define 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)) \ + )) + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.c b/Build/source/texk/web2c/luatexdir/tex/linebreak.w index 5cd5509389d..7c285f94430 100644 --- a/Build/source/texk/web2c/luatexdir/tex/linebreak.c +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.w @@ -1,37 +1,216 @@ -/* linebreak.c - - Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> +% linebreak.w +% +% Copyright 2006-2008 Taco Hoekwater <taco@@luatex.org> - This file is part of LuaTeX. +% This file is part of LuaTeX. - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. -#include "luatex-api.h" -#include <ptexlib.h> -#include "nodes.h" +@ @c +#include "ptexlib.h" static const char _svn_version[] = - "$Id: linebreak.c 2448 2009-06-08 07:43:50Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/linebreak.c $"; + "$Id: linebreak.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/linebreak.w $"; + +@ We come now to what is probably the most interesting algorithm of \TeX: +the mechanism for choosing the ``best possible'' breakpoints that yield +the individual lines of a paragraph. \TeX's line-breaking algorithm takes +a given horizontal list and converts it to a sequence of boxes that are +appended to the current vertical list. In the course of doing this, it +creates a special data structure containing three kinds of records that are +not used elsewhere in \TeX. Such nodes are created while a paragraph is +being processed, and they are destroyed afterwards; thus, the other parts +of \TeX\ do not need to know anything about how line-breaking is done. + +The method used here is based on an approach devised by Michael F. Plass and +@^Plass, Michael Frederick@> +@^Knuth, Donald Ervin@> +the author in 1977, subsequently generalized and improved by the same two +people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice +\AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the +line-breaking problem can be regarded as a special case of the problem of +computing the shortest path in an acyclic network. The cited paper includes +numerous examples and describes the history of line breaking as it has been +practiced by printers through the ages. The present implementation adds two +new ideas to the algorithm of 1980: Memory space requirements are considerably +reduced by using smaller records for inactive nodes than for active ones, +and arithmetic overflow is avoided by using ``delta distances'' instead of +keeping track of the total distance from the beginning of the paragraph to the +current point. + +The |line_break| procedure should be invoked only in horizontal mode; it +leaves that mode and places its output into the current vlist of the +enclosing vertical mode (or internal vertical mode). +There is one explicit parameter: |d| is true for partial paragraphs +preceding display math mode; in this case the amount of additional +penalty inserted before the final line is |display_widow_penalty| +instead of |widow_penalty|. + +There are also a number of implicit parameters: The hlist to be broken +starts at |vlink(head)|, and it is nonempty. The value of |prev_graf| in the +enclosing semantic level tells where the paragraph should begin in the +sequence of line numbers, in case hanging indentation or \.{\\parshape} +are in use; |prev_graf| is zero unless this paragraph is being continued +after a displayed formula. Other implicit parameters, such as the +|par_shape_ptr| and various penalties to use for hyphenation, etc., appear +in |eqtb|. + +After |line_break| has acted, it will have updated the current vlist and the +value of |prev_graf|. Furthermore, the global variable |just_box| will +point to the final box created by |line_break|, so that the width of this +line can be ascertained when it is necessary to decide whether to use +|above_display_skip| or |above_display_short_skip| before a displayed formula. + +@c +halfword just_box; /* the |hlist_node| for the last line of the new paragraph */ + + +@ In it's complete form, |line_break| is a rather lengthy +procedure---sort of a small world unto itself---we must build it up +little by little. Below you see only the general outline. + +The main task performed here is to move the list from |head| to +|temp_head| and go into the enclosing semantic level. We also append +the \.{\\parfillskip} glue to the end of the paragraph, removing a +space (or other glue node) if it was there, since spaces usually +precede blank lines and instances of `\.{\$\$}'. The |par_fill_skip| +is preceded by an infinite penalty, so it will never be considered as +a potential breakpoint. + +That code assumes that a |glue_node| and a |penalty_node| occupy the +same number of |mem|~words. +@^data structure assumptions@> + +Most other processing is delegated to external functions. + +@c +void line_break(boolean d, int line_break_context) +{ + int paragraph_dir = 0; /* main direction of paragraph */ + halfword final_par_glue; + halfword start_of_par; + int callback_id; + pack_begin_line = cur_list.ml_field; /* this is for over/underfull box messages */ + vlink(temp_head) = vlink(cur_list.head_field); + new_hyphenation(temp_head, cur_list.tail_field); + if ((!is_char_node(vlink(cur_list.head_field))) + && ((type(vlink(cur_list.head_field)) == whatsit_node) + && (subtype(vlink(cur_list.head_field)) == local_par_node))) + paragraph_dir = local_par_dir(vlink(cur_list.head_field)); + else { + assert(0); /* |paragraph_dir = 0|; */ + } + cur_list.tail_field = new_ligkern(temp_head, cur_list.tail_field); + if (is_char_node(cur_list.tail_field)) { + tail_append(new_penalty(inf_penalty)); + } else if (type(cur_list.tail_field) != glue_node) { + tail_append(new_penalty(inf_penalty)); + } else { + type(cur_list.tail_field) = penalty_node; + delete_glue_ref(glue_ptr(cur_list.tail_field)); + if (leader_ptr(cur_list.tail_field) != null) + flush_node_list(leader_ptr(cur_list.tail_field)); + penalty(cur_list.tail_field) = inf_penalty; + } + final_par_glue = new_param_glue(par_fill_skip_code); + couple_nodes(cur_list.tail_field, final_par_glue); + cur_list.tail_field = vlink(cur_list.tail_field); + lua_node_filter(pre_linebreak_filter_callback, + line_break_context, temp_head, + addressof(cur_list.tail_field)); + last_line_fill = cur_list.tail_field; + pop_nest(); + start_of_par = cur_list.tail_field; + callback_id = callback_defined(linebreak_filter_callback); + if (callback_id > 0) { + callback_id = + lua_linebreak_callback(d, temp_head, + addressof(cur_list.tail_field)); + if (callback_id > 0) { + just_box = cur_list.tail_field; + if (just_box != null) + while (vlink(just_box) != null) + just_box = vlink(just_box); + if ((just_box == null) || (type(just_box) != hlist_node)) { + help3 + ("A linebreaking routine should return a non-empty list of nodes", + "and the last one of those has to be a \\hbox.", + "Sorry, I cannot recover from this."); + print_err("Invalid linebreak_filter"); + succumb(); + } + } else { + if (int_par(tracing_paragraphs_code) > 0) { + begin_diagnostic(); + tprint_nl + ("Lua linebreak_filter failed, reverting to default on line "); + print_int(line); + end_diagnostic(true); + } + } + } + if (callback_id == 0) { + ext_do_line_break(paragraph_dir, + int_par(pretolerance_code), + int_par(tracing_paragraphs_code), + int_par(tolerance_code), + dimen_par(emergency_stretch_code), + int_par(looseness_code), + int_par(hyphen_penalty_code), + int_par(ex_hyphen_penalty_code), + int_par(pdf_adjust_spacing_code), + equiv(par_shape_loc), + int_par(adj_demerits_code), + int_par(pdf_protrude_chars_code), + int_par(line_penalty_code), + int_par(last_line_fit_code), + int_par(double_hyphen_demerits_code), + int_par(final_hyphen_demerits_code), + dimen_par(hang_indent_code), + dimen_par(hsize_code), + int_par(hang_after_code), + glue_par(left_skip_code), + glue_par(right_skip_code), + dimen_par(pdf_each_line_height_code), + dimen_par(pdf_each_line_depth_code), + dimen_par(pdf_first_line_height_code), + dimen_par(pdf_last_line_depth_code), + equiv(inter_line_penalties_loc), + int_par(inter_line_penalty_code), + int_par(club_penalty_code), + equiv(club_penalties_loc), + (d ? equiv(display_widow_penalties_loc) : equiv(widow_penalties_loc)), + (d ? int_par(display_widow_penalty_code) : int_par(widow_penalty_code)), + int_par(broken_penalty_code), + final_par_glue, dimen_par(pdf_ignored_dimen_code)); + } + lua_node_filter(post_linebreak_filter_callback, + line_break_context, start_of_par, + addressof(cur_list.tail_field)); + pack_begin_line = 0; +} -/* Glue nodes in a horizontal list that is being paragraphed are not supposed to + +@ Glue nodes in a horizontal list that is being paragraphed are not supposed to include ``infinite'' shrinkability; that is why the algorithm maintains four registers for stretching but only one for shrinking. If the user tries to introduce infinite shrinkability, the shrinkability will be reset to finite and an error message will be issued. A boolean variable |no_shrink_error_yet| prevents this error message from appearing more than once per paragraph. -*/ +@c #define check_shrinkage(a) \ if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \ a=finite_shrink((a)) @@ -59,7 +238,7 @@ static halfword finite_shrink(halfword p) return q; } -/* A pointer variable |cur_p| runs through the given horizontal list as we look +@ 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|. @@ -80,41 +259,11 @@ static halfword finite_shrink(halfword p) |true|, and an attempt is made to hyphenate as many words as possible. If that fails too, we add |emergency_stretch| to the background stretchability and set |final_pass=true|. -*/ +@c static boolean second_pass; /* is this our second attempt to break this paragraph? */ static boolean final_pass; /*is this our final attempt to break this paragraph? */ -static 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)) \ - )) +static int threshold; /* maximum badness on feasible lines */ /* maximum fill level for |hlist_stack|*/ #define max_hlist_stack 512 /* maybe good if larger than |2 * @@ -127,70 +276,68 @@ static halfword hlist_stack[max_hlist_stack]; /* fill level for |hlist_stack| */ static short hlist_stack_level = 0; +@ @c static void push_node(halfword p) { if (hlist_stack_level >= max_hlist_stack) - pdf_error(maketexstring("push_node"), maketexstring("stack overflow")); + pdf_error("push_node", "stack overflow"); hlist_stack[hlist_stack_level++] = p; } static halfword pop_node(void) { if (hlist_stack_level <= 0) /* would point to some bug */ - pdf_error(maketexstring("pop_node"), - maketexstring("stack underflow (internal error)")); + 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 */ +@ @c +static int max_stretch_ratio = 0; /*maximal stretch ratio of expanded fonts */ +static int max_shrink_ratio = 0; /*maximal shrink ratio of expanded fonts */ +static int cur_font_step = 0; /*the current step of expanded fonts */ static boolean check_expand_pars(internal_font_number f) { internal_font_number k; - if ((pdf_font_step(f) == 0) || ((pdf_font_stretch(f) == null_font) && - (pdf_font_shrink(f) == null_font))) + if ((font_step(f) == 0) || ((font_stretch(f) == null_font) && + (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(maketexstring("font expansion"), - maketexstring - ("using fonts with different step of expansion in one paragraph is not allowed")); - k = pdf_font_stretch(f); + cur_font_step = font_step(f); + else if (cur_font_step != font_step(f)) + pdf_error("font expansion", + "using fonts with different step of expansion in one paragraph is not allowed"); + k = 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(maketexstring("font expansion"), - maketexstring - ("using fonts with different limit of expansion in one paragraph is not allowed")); + max_stretch_ratio = font_expand_ratio(k); + else if (max_stretch_ratio != 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); + k = 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(maketexstring("font expansion"), - maketexstring - ("using fonts with different limit of expansion in one paragraph is not allowed")); + max_shrink_ratio = -font_expand_ratio(k); + else if (max_shrink_ratio != -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*/ +@ searches left to right from list head |l|, returns 1st non-skipable item + +@c /*public*/ halfword find_protchar_left(halfword l, boolean d) { halfword t; boolean run; 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 */ + 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 */ @@ -207,6 +354,7 @@ static boolean check_expand_pars(internal_font_number f) while (run && cp_skipable(l)) { while ((vlink(l) == null) && (hlist_stack_level > 0)) { l = pop_node(); /* don't visit this node again */ + run = false; } if (vlink(l) != null) l = vlink(l); @@ -218,7 +366,9 @@ static boolean check_expand_pars(internal_font_number f) } -/* searches right to left from list tail |r| to head |l|, returns 1st non-skipable item */ +@ searches right to left from list tail |r| to head |l|, returns 1st non-skipable item + +@c /*public*/ halfword find_protchar_right(halfword l, halfword r) { halfword t; @@ -246,8 +396,12 @@ static boolean check_expand_pars(internal_font_number f) l = pop_node(); } if ((r != l) && (r != null)) { - assert(vlink(alink(r)) == r); - r = alink(r); + if (alink(r) != null) { + assert(vlink(alink(r)) == r); + r = alink(r); + } else { /* this is the input: \.{\\leavevmode\\penalty-10000\\penalty-10000} (bug \#268) */ + run = false; + } } else if ((r == l) && (hlist_stack_level == 0)) run = false; } @@ -255,10 +409,11 @@ static boolean check_expand_pars(internal_font_number f) return r; } +@ @c #define left_pw(a) char_pw((a), left_side) #define right_pw(a) char_pw((a), right_side) -/* When looking for optimal line breaks, \TeX\ creates a ``break node'' for +@ 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 @@ -266,8 +421,8 @@ static boolean check_expand_pars(internal_font_number f) or |disc_node|); the ordinal number of the line that will follow this breakpoint; and the fitness classification of the line that has just ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|. -*/ +@c typedef enum { very_loose_fit = 0, /* fitness classification for lines stretching more than their stretchability */ @@ -279,7 +434,7 @@ typedef enum { } fitness_value; -/* The algorithm essentially determines the best possible way to achieve +@ 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 @@ -294,9 +449,8 @@ typedef enum { 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: +@ 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|. @@ -320,8 +474,8 @@ The value of |vlink(active)| points to the first active node on a vlinked list of all currently active nodes. This list is in order by |line_number|, except that nodes with |line_number>easy_line| may be in any order relative to each other. -*/ +@c void initialize_active(void) { type(active) = hyphenated_node; @@ -329,7 +483,7 @@ void initialize_active(void) subtype(active) = 0; /* the |subtype| is never examined */ } -/* The passive node for a given breakpoint contains EIGHT fields: +@ 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|. @@ -352,13 +506,13 @@ There is a global variable called |passive| that points to the most recently created passive node. Another global variable, |printed_node|, is used to help print out the paragraph when detailed information about the line-breaking computation is being displayed. -*/ +@c static halfword passive; /* most recent node on passive list */ static halfword printed_node; /*most recent node that has been printed */ static halfword pass_number; /*the number of passive nodes allocated on this pass */ -/* + @ The active list also contains ``delta'' nodes that help the algorithm compute the badness of individual lines. Such nodes appear only between two active nodes, and they have |type=delta_node|. If |p| and |r| are active nodes @@ -376,9 +530,9 @@ 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 + +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 @@ -391,15 +545,15 @@ $$\hbox{\ignorespaces|for k:=1 to 7 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we want to do this without the overhead of |for| loops. The |do_all_six| macro makes such six-tuples convenient. -*/ +@c static scaled active_width[10] = { 0 }; /*distance from first active node to~|cur_p| */ static scaled background[10] = { 0 }; /*length of an ``empty'' line */ static scaled break_width[10] = { 0 }; /*length being computed after current break */ static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_break| */ -/* Let's state the principles of the delta nodes more precisely and concisely, +@ 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| @@ -437,9 +591,9 @@ static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_b $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 + +@ 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 @@ -454,22 +608,21 @@ static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_b 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} */ +@c +static int internal_pen_inter; /* running \.{\\localinterlinepenalty} */ +static int internal_pen_broken; /* running \.{\\localbrokenpenalty} */ static halfword internal_left_box; /* running \.{\\localleftbox} */ -static integer internal_left_box_width; /* running \.{\\localleftbox} width */ +static int 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 int 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 int internal_right_box_width; /* running \.{\\localrightbox} width */ static scaled disc_width[10] = { 0 }; /* the length of discretionary material preceding a break */ -/* As we consider various ways to end a line at |cur_p|, in a given line number +@ 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 @@ -480,19 +633,18 @@ static scaled disc_width[10] = { 0 }; /* the length of discretionary material 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 */ +@c +static int minimal_demerits[4]; /* best total demerits known for current + line class and position, given the fitness */ +static int minimum_demerits; /* best total demerits known for current line class + and position */ static halfword best_place[4]; /* how to achieve |minimal_demerits| */ static halfword best_pl_line[4]; /*corresponding line number */ -/* - The length of lines depends on whether the user has specified + +@ 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 @@ -507,8 +659,8 @@ $|hsize|-\vert|hang_indent|\vert$. The normal setting is |par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|. Note that if |hang_indent=0|, the value of |hang_after| is irrelevant. @^length of lines@> @^hanging indentation@> -*/ +@c static halfword easy_line; /*line numbers |>easy_line| are equivalent in break nodes */ static halfword last_special_line; /*line numbers |>last_special_line| all have the same width */ static scaled first_width; /*the width of all lines |<=last_special_line|, if @@ -518,23 +670,23 @@ 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 int 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 +static int actual_looseness; /*the difference between |line_number(best_bet)| + and the optimum |best_line| */ +static int line_diff; /*the difference between the current line number and the optimum |best_line| */ -/* \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, +@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, |rule_node|, |ins_node|, |mark_node|, |adjust_node|, |disc_node|, |whatsit_node|, and |math_node| are at the low end of the type codes, by permitting a break at glue in a list if and only if the |type| of the previous node is less than |math_node|. Furthermore, a node is discarded after a break if its type is |math_node| or~more. -*/ +@c #define do_all_six(a) a(1);a(2);a(3);a(4);a(5);a(6);a(7) #define do_seven_eight(a) if (pdf_adjust_spacing > 1) { a(8);a(9); } #define do_all_eight(a) do_all_six(a); do_seven_eight(a) @@ -545,7 +697,7 @@ static integer line_diff; /*the difference between the current line number #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, \ + ext_try_break(0,unhyphenated_node, line_break_dir, pdf_adjust_spacing, \ par_shape_ptr, adj_demerits, \ tracing_paragraphs, pdf_protrude_chars, \ line_penalty, last_line_fit, \ @@ -570,18 +722,11 @@ static integer line_diff; /*the difference between the current line number } \ } -#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)] @@ -598,24 +743,20 @@ static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */ #define sub_kern_stretch(a,b) a -= kern_stretch((b)) -/* This function is used to add the width of a list of nodes - * (from a discretionary) to one of the width arrays. - */ +@ This function is used to add the width of a list of nodes +(from a discretionary) to one of the width arrays. + -/* Replacement texts and discretionary texts are supposed to contain - * only character nodes, kern nodes, and box or rule nodes. - */ +Replacement texts and discretionary texts are supposed to contain +only character nodes, kern nodes, and box or rule nodes. -static void add_to_widths(halfword s, integer line_break_dir, - integer pdf_adjust_spacing, scaled * widths) +@c +static void add_to_widths(halfword s, int line_break_dir, + int pdf_adjust_spacing, scaled * widths) { while (s != null) { if (is_char_node(s)) { - if (is_rotated(line_break_dir)) { - widths[1] += glyph_height(s) + glyph_depth(s); - } else { - widths[1] += glyph_width(s); - } + widths[1] += pack_width(line_break_dir, dir_TRT, s, true); if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) { set_prev_char_p(s); add_char_stretch(widths[8], s); @@ -625,11 +766,7 @@ static void add_to_widths(halfword s, integer line_break_dir, switch (type(s)) { case hlist_node: case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(s)], - dir_primary[line_break_dir]))) - widths[1] += width(s); - else - widths[1] += (depth(s) + height(s)); + widths[1] += pack_width(line_break_dir, box_dir(s), s, false); break; case kern_node: if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) { @@ -643,30 +780,26 @@ static void add_to_widths(halfword s, integer line_break_dir, case disc_node: /* TH temp */ break; default: - tconfusion("add_disc_widths"); + confusion("add_disc_widths"); } } s = vlink(s); } } -/* This function is used to substract the width of a list of nodes - * (from a discretionary) from one of the width arrays. - * It is used only once, but deserves it own function because of orthogonality - * with the |add_to_widths| function. - */ +@ This function is used to substract the width of a list of nodes +(from a discretionary) from one of the width arrays. +It is used only once, but deserves it own function because of orthogonality +with the |add_to_widths| function. -static void sub_from_widths(halfword s, integer line_break_dir, - integer pdf_adjust_spacing, scaled * widths) +@c +static void sub_from_widths(halfword s, int line_break_dir, + int pdf_adjust_spacing, scaled * widths) { while (s != null) { - /* @<Subtract the width of node |s| from |break_width|@>; */ + /* Subtract the width of node |s| from |break_width|; */ if (is_char_node(s)) { - if (is_rotated(line_break_dir)) { - widths[1] -= (glyph_height(s) + glyph_depth(s)); - } else { - widths[1] -= glyph_width(s); - } + widths[1] -= pack_width(line_break_dir, dir_TRT, s, true); if ((pdf_adjust_spacing > 1) && check_expand_pars(font(s))) { set_prev_char_p(s); sub_char_stretch(widths[8], s); @@ -676,11 +809,7 @@ static void sub_from_widths(halfword s, integer line_break_dir, switch (type(s)) { case hlist_node: case vlist_node: - if (!(dir_orthogonal(dir_primary[box_dir(s)], - dir_primary[line_break_dir]))) - widths[1] -= width(s); - else - widths[1] -= (depth(s) + height(s)); + widths[1] -= pack_width(line_break_dir, box_dir(s), s, false); break; case kern_node: if ((pdf_adjust_spacing > 1) && (subtype(s) == normal)) { @@ -694,7 +823,7 @@ static void sub_from_widths(halfword s, integer line_break_dir, case disc_node: /* TH temp */ break; default: - tconfusion("sub_disc_widths"); + confusion("sub_disc_widths"); break; } } @@ -703,7 +832,7 @@ static void sub_from_widths(halfword s, integer line_break_dir, } -/* When we insert a new active node for a break at |cur_p|, suppose this +@ 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)$ @@ -721,24 +850,18 @@ static void sub_from_widths(halfword s, integer line_break_dir, 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 -*/ + +@c static void -compute_break_width(int break_type, int pdf_adjust_spacing, halfword p +compute_break_width(int break_type, int line_break_dir, 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 */ + * used if unhyphenated, or |post_break==empty| */ s = p; if (break_type > unhyphenated_node && p != null) { - /*@<Compute the discretionary |break_width| values@>; */ + /*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 @@ -761,8 +884,8 @@ compute_break_width(int break_type, int pdf_adjust_spacing, halfword p 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. + /* In case of nested discretionaries, we always follow the no-break + path, as we are talking about the breaking on {\it this} position. */ sub_from_widths(vlink_no_break(p), line_break_dir, pdf_adjust_spacing, @@ -771,7 +894,7 @@ compute_break_width(int break_type, int pdf_adjust_spacing, halfword p break_width); do_one_seven_eight(add_disc_width_to_break_width); if (vlink_post_break(p) == null) { - s = vlink(p); /* no post_break: 'skip' any 'whitespace' following */ + s = vlink(p); /* no |post_break|: 'skip' any 'whitespace' following */ } else { s = null; } @@ -781,7 +904,7 @@ compute_break_width(int break_type, int pdf_adjust_spacing, halfword p while (s != null) { switch (type(s)) { case glue_node: - /*@<Subtract glue from |break_width|@>; */ + /*Subtract glue from |break_width|; */ { halfword v = glue_ptr(s); break_width[1] -= width(v); @@ -807,13 +930,13 @@ compute_break_width(int break_type, int pdf_adjust_spacing, halfword p } } - +@ @c static void print_break_node(halfword q, fitness_value fit_class, quarterword break_type, halfword cur_p) { - /* @<Print a symbolic description of the new break node@> */ - tprint_nl("@@"); + /* Print a symbolic description of the new break node */ + tprint_nl("@@@@"); print_int(serial(passive)); tprint(": line "); print_int(line_number(q) - 1); @@ -824,7 +947,7 @@ print_break_node(halfword q, fitness_value fit_class, tprint(" t="); print_int(total_demerits(q)); if (do_last_line_fit) { - /*@<Print additional data in the new active node@>; */ + /*Print additional data in the new active node; */ tprint(" s="); print_scaled(active_short(q)); if (cur_p == null) @@ -840,15 +963,15 @@ print_break_node(halfword q, fitness_value fit_class, print_int(serial(prev_break(passive))); } - +@ @c static void -print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, - integer d, boolean artificial_demerits) +print_feasible_break(halfword cur_p, pointer r, halfword b, int pi, + int d, boolean artificial_demerits) { - /* @<Print a symbolic description of this feasible break@>; */ + /* Print a symbolic description of this feasible break; */ if (printed_node != cur_p) { - /* @<Print the list between |printed_node| and |cur_p|, then - set |printed_node:=cur_p|@>; */ + /* Print 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)); @@ -861,7 +984,7 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, } printed_node = cur_p; } - tprint_nl("@"); + tprint_nl("@@"); if (cur_p == null) { tprint_esc("par"); } else if (type(cur_p) != glue_node) { @@ -893,6 +1016,7 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, print_int(d); } +@ @c #define add_disc_width_to_active_width(a) active_width[a] += disc_width[a] #define update_width(a) cur_active_width[a] += varmem[(r+(a))].cint @@ -918,10 +1042,6 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, #define total_font_stretch cur_active_width[8] #define total_font_shrink cur_active_width[9] -#define left_side 0 -#define right_side 1 - - #define cal_margin_kern_var(a) { \ character(cp) = character((a)); \ font(cp) = font((a)); \ @@ -935,8 +1055,9 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, } static void -ext_try_break(integer pi, +ext_try_break(int pi, quarterword break_type, + int line_break_dir, int pdf_adjust_spacing, int par_shape_ptr, int adj_demerits, @@ -947,7 +1068,7 @@ ext_try_break(integer pi, int double_hyphen_demerits, int final_hyphen_demerits, halfword first_p, halfword cur_p) { - /* CONTINUE,DEACTIVATE,FOUND,NOT_FOUND; */ + /* labels: |CONTINUE,DEACTIVATE,FOUND,NOT_FOUND|; */ pointer r; /* runs through the active list */ scaled margin_kern_stretch; scaled margin_kern_shrink; @@ -962,7 +1083,7 @@ ext_try_break(integer pi, 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 */ + int d; /*demerits of test line */ boolean artificial_demerits; /*has |d| been forced to zero? */ scaled shortfall; /*used in badness calculations */ @@ -972,7 +1093,7 @@ ext_try_break(integer pi, line_width = 0; g = 0; prev_prev_r = null; - /*@<Make sure that |pi| is in the proper range@>; */ + /*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) { @@ -986,8 +1107,8 @@ ext_try_break(integer pi, 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|@>; */ + /* 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 */ @@ -995,9 +1116,9 @@ ext_try_break(integer pi, prev_r = r; continue; } - /* @<If a line number class has ended, create new active nodes for + /* 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|@>; */ + 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 @@ -1008,7 +1129,7 @@ ext_try_break(integer pi, 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@> */ + /*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 @@ -1018,12 +1139,13 @@ ext_try_break(integer pi, 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); + compute_break_width(break_type, line_break_dir, + pdf_adjust_spacing, cur_p); } - /* @<Insert a delta node to prepare for breaks at |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 */ + 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 { @@ -1042,8 +1164,8 @@ ext_try_break(integer pi, 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|@>; */ + /* 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. */ @@ -1077,7 +1199,7 @@ ext_try_break(integer pi, 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@> */ + /*Store additional data in the new active node */ /* Here we save these data in the active node representing a potential line break. */ active_short(q) = best_pl_short[fit_class]; @@ -1092,7 +1214,7 @@ ext_try_break(integer pi, minimal_demerits[fit_class] = awful_bad; } minimum_demerits = awful_bad; - /* @<Insert a delta node to prepare for the next active node@>; */ + /* 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|. */ @@ -1107,7 +1229,7 @@ ext_try_break(integer pi, } if (r == active) return; - /*@<Compute the new line width@>; */ + /*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 @@ -1131,12 +1253,12 @@ ext_try_break(integer pi, } /* /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|@>; */ + if |r=active|, otherwise compute the new |line_width|; */ - /* @<Consider the demerits for a line from |r| to |cur_p|; + /* 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@>; */ + otherwise record a new feasible break; */ artificial_demerits = false; shortfall = line_width - cur_active_width[1]; if (break_node(r) == null) @@ -1149,17 +1271,18 @@ ext_try_break(integer pi, 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))) */ + } else { /* TODO |if (is_character_node(alink(cur_p)))| */ o = alink(cur_p); assert(vlink(o) == cur_p); } /* MAGIC: the disc could be a SELECT subtype, to we might need - * to get the last character as 'pre_break' from either the - * pre_break list (if the previous INIT disc was taken), or the - * no_break (sic) list (if the previous INIT disc was not taken) - * BUT: - * the last characters (hyphenation character) if these two list - * should always be the same anyway, so we just look at pre_break. + to get the last character as |pre_break| from either the + |pre_break| list (if the previous INIT disc was taken), or the + |no_break| (sic) list (if the previous INIT disc was not taken) + + BUT: + the last characters (hyphenation character) if these two list + should always be the same anyway, so we just look at |pre_break|. */ /* let's look at the right margin first */ if ((cur_p != null) && (type(cur_p) == disc_node) @@ -1183,7 +1306,7 @@ ext_try_break(integer pi, margin_kern_stretch = 0; margin_kern_shrink = 0; if (pdf_protrude_chars > 1) { - /* @<Calculate variations of marginal kerns@>; */ + /* Calculate variations of marginal kerns; */ lp = last_leftmost_char; rp = last_rightmost_char; cp = raw_glyph_node(); @@ -1212,8 +1335,8 @@ ext_try_break(integer pi, } } if (shortfall > 0) { - /* @<Set the value of |b| to the badness for stretching the line, - and compute the corresponding |fit_class|@> */ + /* 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 @@ -1223,13 +1346,13 @@ ext_try_break(integer pi, 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@> */ + subroutine. */ if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) || (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) { if (do_last_line_fit) { if (cur_p == null) { /* the last line of a paragraph */ - /* @<Perform computations for last line and |goto found|@>; */ + /* 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 @@ -1268,9 +1391,9 @@ ext_try_break(integer pi, g = -max_dimen; } if (g > 0) { - /*@<Set the value of |b| to the badness of the last line + /*Set the value of |b| to the badness of the last line for stretching, compute the corresponding |fit_class, - and |goto found||@> */ + and |goto found|| */ /* These badness computations are rather similar to those of the standard algorithm, with the adjustment amount |g| replacing the |shortfall|. */ @@ -1293,9 +1416,9 @@ ext_try_break(integer pi, } goto FOUND; } else if (g < 0) { - /*@<Set the value of |b| to the badness of the last line + /*Set the value of |b| to the badness of the last line for shrinking, compute the corresponding |fit_class, - and |goto found||@>; */ + and |goto found||; */ if (-g > cur_active_width[7]) g = -cur_active_width[7]; b = badness(-g, cur_active_width[7]); @@ -1328,7 +1451,7 @@ ext_try_break(integer pi, } } else { /* Set the value of |b| to the badness for shrinking the line, - and compute the corresponding |fit_class|@>; */ + 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]|. */ @@ -1342,7 +1465,7 @@ ext_try_break(integer pi, fit_class = decent_fit; } if (do_last_line_fit) { - /* @<Adjust \(t)the additional data for last line@>; */ + /* Adjust the additional data for last line; */ if (cur_p == null) shortfall = 0; if (shortfall > 0) { @@ -1355,8 +1478,8 @@ ext_try_break(integer pi, } 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|@> */ + /* 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 @@ -1364,7 +1487,6 @@ ext_try_break(integer pi, 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)) { @@ -1379,7 +1501,7 @@ ext_try_break(integer pi, continue; node_r_stays_active = true; } - /* @<Record a new feasible break@>; */ + /* 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, @@ -1391,7 +1513,7 @@ ext_try_break(integer pi, if (artificial_demerits) { d = 0; } else { - /* @<Compute the demerits, |d|, from |r| to |cur_p|@>; */ + /* Compute the demerits, |d|, from |r| to |cur_p|; */ d = line_penalty + b; if (abs(d) >= 10000) d = 100000000; @@ -1422,7 +1544,7 @@ ext_try_break(integer pi, 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@>; */ + /* Store additional data for this feasible break; */ /* For each feasible break we record the shortfall and glue stretch or shrink (or adjustment). */ best_pl_short[fit_class] = shortfall; @@ -1435,7 +1557,7 @@ ext_try_break(integer pi, if (node_r_stays_active) continue; /*|prev_r| has been set to |r| */ DEACTIVATE: - /* @<Deactivate node |r|@>; */ + /* 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 @@ -1445,8 +1567,8 @@ ext_try_break(integer pi, vlink(prev_r) = vlink(r); flush_node(r); if (prev_r == active) { - /*@<Update the active widths, since the first active node has been - deleted@> */ + /*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 @@ -1462,13 +1584,13 @@ ext_try_break(integer pi, } else if (type(prev_r) == delta_node) { r = vlink(prev_r); if (r == active) { - do_all_eight(downdate_width); /* IMPLICIT prev_r */ + 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 */ + do_all_eight(combine_two_deltas); /* IMPLICIT r |prev_r| */ vlink(prev_r) = vlink(r); flush_node(r); } @@ -1476,8 +1598,9 @@ ext_try_break(integer pi, } } +@ @c void -ext_do_line_break(boolean d, +ext_do_line_break(int paragraph_dir, int pretolerance, int tracing_paragraphs, int tolerance, @@ -1506,15 +1629,14 @@ ext_do_line_break(boolean d, 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 pdf_ignored_dimen) { /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE; */ halfword cur_p, q, r, s; /* miscellaneous nodes of temporary interest */ + int line_break_dir = paragraph_dir; /* Get ready to start ... */ minimum_demerits = awful_bad; @@ -1522,7 +1644,7 @@ ext_do_line_break(boolean d, 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) { @@ -1531,7 +1653,7 @@ ext_do_line_break(boolean d, second_width = hsize; second_indent = 0; } else { - /* @<Set line length parameters in preparation for hanging indentation@> */ + /* 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); @@ -1587,7 +1709,7 @@ ext_do_line_break(boolean d, cur_font_step = -1; set_prev_char_p(null); } - /* @<Check for special treatment of last line of paragraph@>; */ + /* 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. @@ -1607,19 +1729,21 @@ ext_do_line_break(boolean d, } } } - /* @<DIR: Initialize |dir_ptr| for |line_break|@> */ + /* DIR: Initialize |dir_ptr| for |line_break| */ if (dir_ptr != null) { flush_node_list(dir_ptr); dir_ptr = null; } - /*push_dir(paragraph_dir); *//* TODO what was the point of this? */ +#if 0 + push_dir(paragraph_dir); /* TODO what was the point of this? */ +#endif - /* @<Find optimal breakpoints@>; */ + /* Find optimal breakpoints; */ threshold = pretolerance; if (threshold >= 0) { if (tracing_paragraphs > 0) { begin_diagnostic(); - tprint_nl("@firstpass"); + tprint_nl("@@firstpass"); } second_pass = false; final_pass = false; @@ -1682,18 +1806,17 @@ ext_do_line_break(boolean d, /* 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. */ + /* |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, also look at |disc_node|s. */ while (is_char_node(cur_p)) { - /* Advance \(c)|cur_p| to the node following the present string of characters ; */ + /* Advance |cur_p| to the node following the present string of characters ; */ /* The code that passes over the characters of words in a paragraph is part of \TeX's inner loop, so it has been streamlined for speed. We use the fact that `\.{\\parfillskip}' glue appears at the end of each paragraph; it is therefore unnecessary to check if |vlink(cur_p)=null| when |cur_p| is a character node. */ - active_width[1] += (is_rotated(line_break_dir)) - ? (glyph_height(cur_p) + glyph_depth(cur_p)) - : glyph_width(cur_p); + active_width[1] += + pack_width(line_break_dir, dir_TRT, cur_p, true); 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); @@ -1702,11 +1825,13 @@ ext_do_line_break(boolean d, 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); */ +#ifdef DEBUG + fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); +#endif } } if (cur_p == null) { /* TODO */ - tconfusion("linebreak_tail"); + confusion("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 @@ -1715,22 +1840,18 @@ ext_do_line_break(boolean d, real width as opposed to glue. */ switch (type(cur_p)) { - case hlist_node: case vlist_node: active_width[1] += - (dir_orthogonal - (dir_primary[box_dir(cur_p)], dir_primary[line_break_dir])) - ? (depth(cur_p) + height(cur_p)) - : width(cur_p); + pack_width(line_break_dir, box_dir(cur_p), cur_p, false); break; case rule_node: active_width[1] += width(cur_p); break; case whatsit_node: - /* @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>; */ + /* Advance past a whatsit node in the |line_break| loop; */ switch (subtype(cur_p)) { - case local_par_node: /* @<LOCAL: Advance past a |local_paragraph| node@>; */ + 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); @@ -1738,10 +1859,10 @@ ext_do_line_break(boolean d, 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@>; */ + 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); /* adds to dir_ptr */ + push_dir_node(cur_p); /* adds to |dir_ptr| */ } else { pop_dir_node(); if (dir_ptr != null) @@ -1750,14 +1871,14 @@ ext_do_line_break(boolean d, break; case pdf_refxform_node: case pdf_refximage_node: - active_width[1] += pdf_width(cur_p); + active_width[1] += width(cur_p); } - /* / Advance \(p)past a whatsit node in the \(l)|line_break| loop/; */ + /* / Advance past a whatsit node in the |line_break| loop/; */ break; case glue_node: - /* @<If node |cur_p| is a legal breakpoint, call |try_break|; + /* 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)|@>; */ + |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. */ @@ -1769,7 +1890,7 @@ ext_do_line_break(boolean d, precedes_break(prev_p) || ((type(prev_p) == kern_node) && (subtype(prev_p) != explicit)))) { - ext_try_break(0, unhyphenated_node, pdf_adjust_spacing, + ext_try_break(0, unhyphenated_node, line_break_dir, pdf_adjust_spacing, par_shape_ptr, adj_demerits, tracing_paragraphs, pdf_protrude_chars, line_penalty, last_line_fit, @@ -1796,10 +1917,10 @@ ext_do_line_break(boolean d, } break; case disc_node: - /* select_discs are handled by the leading init_disc */ + /* |select_disc|s are handled by the leading |init_disc| */ if (subtype(cur_p) == select_disc) break; - /* @<Try to break after a discretionary fragment, then |goto done5|@>; */ + /* 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. */ @@ -1811,27 +1932,29 @@ ext_do_line_break(boolean d, 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, + line_break_dir, 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 { add_to_widths(s, line_break_dir, pdf_adjust_spacing, disc_width); 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, + line_break_dir, 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 (subtype(cur_p) == init_disc) { /* we should at two break points after the one we - * added above: - * 1 which does a possible break in INIT's post_break - * 2 which means the no_break actually was broken - * just a character later */ + added above: + \item1 which does a possible break in INIT's |post_break| + \item2 which means the |no_break| actually was broken + just a character later */ /* do the select-0 case 'f-f-i' */ assert(type(vlink(cur_p)) == disc_node && subtype(vlink(cur_p)) == select_disc); @@ -1839,8 +1962,9 @@ ext_do_line_break(boolean d, add_to_widths(s, line_break_dir, pdf_adjust_spacing, disc_width); ext_try_break(actual_penalty, hyphenated_node, - pdf_adjust_spacing, par_shape_ptr, - adj_demerits, tracing_paragraphs, + line_break_dir, 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, @@ -1851,13 +1975,14 @@ ext_do_line_break(boolean d, do_one_seven_eight (sub_disc_width_from_active_width); do_one_seven_eight(reset_disc_width); - /* add select no_break to active_width */ + /* add select |no_break| to |active_width| */ s = vlink_no_break(vlink(cur_p)); add_to_widths(s, line_break_dir, pdf_adjust_spacing, disc_width); ext_try_break(actual_penalty, hyphenated_node, - pdf_adjust_spacing, par_shape_ptr, - adj_demerits, tracing_paragraphs, + line_break_dir, 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, @@ -1876,7 +2001,7 @@ ext_do_line_break(boolean d, kern_break(); break; case penalty_node: - ext_try_break(penalty(cur_p), unhyphenated_node, + ext_try_break(penalty(cur_p), unhyphenated_node, line_break_dir, pdf_adjust_spacing, par_shape_ptr, adj_demerits, tracing_paragraphs, pdf_protrude_chars, line_penalty, last_line_fit, @@ -1892,12 +2017,14 @@ ext_do_line_break(boolean d, break; default: fprintf(stdout, "\ntype=%d", type(cur_p)); - tconfusion("paragraph"); + confusion("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); */ +#ifdef DEBUG + fprintf(stderr,"Node Pop %d [%d]\n",nest_index,(int)cur_p); +#endif } } if (cur_p == null) { @@ -1915,13 +2042,13 @@ ext_do_line_break(boolean d, 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); + ext_try_break(eject_penalty, hyphenated_node, line_break_dir, + 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@>; */ + /* Find an active node with fewest demerits; */ r = vlink(active); fewest_demerits = awful_bad; do { @@ -1938,7 +2065,7 @@ ext_do_line_break(boolean d, /* /Find an active node with fewest demerits; */ if (looseness == 0) goto DONE; - /*@<Find the best active node for the desired looseness@>; */ + /*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 @@ -1979,13 +2106,13 @@ ext_do_line_break(boolean d, if (!second_pass) { if (tracing_paragraphs > 0) - tprint_nl("@secondpass"); + 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"); + tprint_nl("@@emergencypass"); background[2] += emergency_stretch; final_pass = true; } @@ -1997,7 +2124,7 @@ ext_do_line_break(boolean d, normalize_selector(); } if (do_last_line_fit) { - /* Adjust \(t)the final line of the paragraph; */ + /* Adjust the final line of the paragraph; */ /* Here we either reset |do_last_line_fit| or adjust the |par_fill_skip| glue. */ if (active_short(best_bet) == 0) { @@ -2011,7 +2138,7 @@ ext_do_line_break(boolean d, } } - /* @<Break the paragraph at the chosen...@>; */ + /* 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| @@ -2021,8 +2148,7 @@ ext_do_line_break(boolean d, /* first thing |ext_post_line_break| does is reset |dir_ptr| */ flush_node_list(dir_ptr); dir_ptr = null; - - ext_post_line_break(d, + ext_post_line_break(paragraph_dir, right_skip, left_skip, pdf_protrude_chars, @@ -2036,9 +2162,7 @@ ext_do_line_break(boolean d, 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, @@ -2051,3 +2175,10 @@ ext_do_line_break(boolean d, /* Clean up the memory by removing the break nodes; */ clean_up_the_memory(); } + +@ @c +void get_linebreak_info (int *f, int *a) +{ + *f = fewest_demerits; + *a = actual_looseness; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/mainbody.h b/Build/source/texk/web2c/luatexdir/tex/mainbody.h new file mode 100644 index 00000000000..0b108ad896f --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mainbody.h @@ -0,0 +1,252 @@ +/* mainbody.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: mainbody.h 3436 2010-02-21 15:34:00Z taco $ */ + +#ifndef MAINBODY_H +# define MAINBODY_H + +extern int luatex_svn; +extern int luatex_version; +extern int luatex_revision; +extern int luatex_date_info; +extern const char *luatex_version_string; +extern const char *engine_name; + +/* +The following parameters can be changed at compile time to extend or +reduce \TeX's capacity. They may have different values in \.{INITEX} and +in production versions of \TeX. +@.INITEX@> +@^system dependencies@> +*/ + +# define ssup_max_strings 262143 + +# define inf_max_strings 100000 +# define sup_max_strings ssup_max_strings +# define inf_strings_free 100 +# define sup_strings_free sup_max_strings + +# define inf_buf_size 500 +# define sup_buf_size 100000000 + +# define inf_nest_size 40 +# define sup_nest_size 4000 + +# define inf_max_in_open 6 +# define sup_max_in_open 127 + +# define inf_param_size 60 +# define sup_param_size 6000 + +# define inf_save_size 600 +# define sup_save_size 80000 + +# define inf_stack_size 200 +# define sup_stack_size 30000 + +# define inf_dvi_buf_size 800 +# define sup_dvi_buf_size 65536 + +# define sup_hash_extra sup_max_strings +# define inf_hash_extra 0 + +# define sup_ocp_list_size 1000000 +# define inf_ocp_list_size 1000 +# define sup_ocp_buf_size 100000000 +# define inf_ocp_buf_size 1000 +# define sup_ocp_stack_size 1000000 +# define inf_ocp_stack_size 1000 + +# define inf_expand_depth 100 +# define sup_expand_depth 1000000 + + +# include <stdio.h> + +/* Types in the outer block */ +typedef int ASCII_code; /* characters */ +typedef unsigned char eight_bits; /* unsigned one-byte quantity */ +typedef FILE *alpha_file; /* files that contain textual data */ +typedef FILE *byte_file; /* files that contain binary data */ + +typedef int str_number; +typedef int pool_pointer; +typedef unsigned char packed_ASCII_code; + +typedef int scaled; /* this type is used for scaled integers */ +typedef char small_number; /* this type is self-explanatory */ + +typedef float glue_ratio; /* one-word representation of a glue expansion factor */ + +typedef unsigned short quarterword; +typedef int halfword; + +typedef unsigned char glue_ord; /* infinity to the 0, 1, 2, 3, or 4 power */ + +typedef unsigned short group_code; /* |save_level| for a level boundary */ + +typedef int internal_font_number; /* |font| in a |char_node| */ +typedef int font_index; /* index into |font_info| */ + +typedef int save_pointer; + +/* +Characters of text that have been converted to \TeX's internal form +are said to be of type |ASCII_code|, which is a subrange of the integers. + +We are assuming that our runtime system is able to read and write UTF-8. + +Some of the ASCII codes without visible characters have been given symbolic +names in this program because they are used with a special meaning. +*/ + + +# define null_code '\0' /* ASCII code that might disappear */ +# define carriage_return '\r' /* ASCII code used at end of line */ + +/* Global variables */ +extern int bad; /* is some ``constant'' wrong? */ +extern boolean luainit; /* are we using lua for initializations */ +extern boolean tracefilenames; /* print file open-close info? */ + + +extern boolean ini_version; /* are we \.{INITEX}? */ +extern boolean dump_option; +extern boolean dump_line; +extern int bound_default; +extern char *bound_name; +extern int error_line; +extern int half_error_line; +extern int max_print_line; +extern int ocp_list_size; +extern int ocp_buf_size; +extern int ocp_stack_size; +extern int max_strings; +extern int strings_free; +extern int font_k; +extern int buf_size; +extern int stack_size; +extern int max_in_open; +extern int param_size; +extern int nest_size; +extern int save_size; +extern int expand_depth; +extern int parsefirstlinep; +extern int filelineerrorstylep; +extern int haltonerrorp; +extern boolean quoted_filename; + +/* +In order to make efficient use of storage space, \TeX\ bases its major data +structures on a |memory_word|, which contains either a (signed) integer, +possibly scaled, or a (signed) |glue_ratio|, or a small number of +fields that are one half or one quarter of the size used for storing +integers. + +If |x| is a variable of type |memory_word|, it contains up to four +fields that can be referred to as follows: +$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr +|x|&.|int|&(an |integer|)\cr +|x|&.|sc|\qquad&(a |scaled| integer)\cr +|x|&.|gr|&(a |glue_ratio|)\cr +|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr +|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword + field)\cr +|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt + &\qquad\qquad\qquad(four quarterword fields)\cr}}$$ +This is somewhat cumbersome to write, and not very readable either, but +macros will be used to make the notation shorter and more transparent. +The \PASCAL\ code below gives a formal definition of |memory_word| and +its subsidiary types, using packed variant records. \TeX\ makes no +assumptions about the relative positions of the fields within a word. + +We are assuming 32-bit integers, a halfword must contain at least +32 bits, and a quarterword must contain at least 16 bits. +@^system dependencies@> + +N.B.: Valuable memory space will be dreadfully wasted unless \TeX\ is compiled +by a \PASCAL\ that packs all of the |memory_word| variants into +the space of a single integer. This means, for example, that |glue_ratio| +words should be |short_real| instead of |real| on some computers. Some +\PASCAL\ compilers will pack an integer whose subrange is `|0..255|' into +an eight-bit field, but others insist on allocating space for an additional +sign bit; on such systems you can get 256 values into a quarterword only +if the subrange is `|-128..127|'. + +The present implementation tries to accommodate as many variations as possible, +so it makes few assumptions. If integers having the subrange +`|min_quarterword..max_quarterword|' can be packed into a quarterword, +and if integers having the subrange `|min_halfword..max_halfword|' +can be packed into a halfword, everything should work satisfactorily. + +It is usually most efficient to have |min_quarterword=min_halfword=0|, +so one should try to achieve this unless it causes a severe problem. +The values defined here are recommended for most 32-bit computers. + +We cannot use the full range of 32 bits in a halfword, because we have +to allow negative values for potential backend tricks like web2c's +dynamic allocation, and parshapes pointers have to be able to store at +least twice the value |max_halfword| (see below). Therefore, +|max_halfword| is $2^{30}-1$ +*/ + +# include "tex/memoryword.h" + +# define min_quarterword 0 /*smallest allowable value in a |quarterword| */ +# define max_quarterword 65535 /* largest allowable value in a |quarterword| */ +# define min_halfword -0x3FFFFFFF /* smallest allowable value in a |halfword| */ +# define max_halfword 0x3FFFFFFF + /* largest allowable value in a |halfword| */ + + +/* +The following procedure, which is called just before \TeX\ initializes its +input and output, establishes the initial values of the date and time. +It calls a macro-defined |dateandtime| routine. |dateandtime| in turn is +also a C macro, which calls |get_date_and_time|, passing it the addresses of +the day, month, etc., so they can be set by the routine. +|get_date_and_time| also sets up interrupt catching if that +is conditionally compiled in the C code. +@^system dependencies@> +*/ + + +# define fix_date_and_time() dateandtime(int_par(time_code),int_par(day_code),int_par(month_code),int_par(year_code)) + +extern int get_luatexsvn(void); +extern int get_luatexversion(void); +extern str_number get_luatexrevision(void); +extern int get_luatex_date_info(void); + +extern int ready_already; + +extern void main_body(void); +extern void close_files_and_terminate(void); + +extern void final_cleanup(void); +extern void debug_help(void); /* routine to display various things */ + +/* lazy me */ +# define get_cur_font() equiv(cur_font_loc) +# define zset_cur_font set_cur_font + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/mainbody.w b/Build/source/texk/web2c/luatexdir/tex/mainbody.w new file mode 100644 index 00000000000..abdf1af920b --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mainbody.w @@ -0,0 +1,745 @@ +% mainbody.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +\def\eTeX{e-\TeX} +\def\Aleph{Aleph} +\def\pdfTeX{pdf\TeX} + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: mainbody.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/mainbody.w $"; + +@ +pdfTeX is copyright (C) 1996-2006 Han The Thanh, <thanh@@pdftex.org>. + +e-TeX is copyright (C) 1994,98 by Peter Breitenlohner. + +This is LuaTeX, a continuation of $\pdfTeX$ and $\Aleph$. LuaTeX is a +document compiler intended to simplify high-quality typesetting for +many of the world's languages. It is an extension of D. E. Knuth's +\TeX, which was designed essentially for the typesetting of languages +using the Latin alphabet. + +The $\Aleph$ subsystem loosens many of the restrictions imposed by~\TeX: +register numbers are no longer limited to 8~bits; fonts may have more +than 256~characters; more than 256~fonts may be used; etc. + +% This program is directly derived from Donald E. Knuth's TeX; +% the change history which follows and the reward offered for finders of +% bugs refer specifically to TeX; they should not be taken as referring +% to LuaTeX, pdfTeX, nor e-TeX, although the change history is relevant in that it +% demonstrates the evolutionary path followed. This program is not TeX; +% that name is reserved strictly for the program which is the creation +% and sole responsibility of Professor Knuth. + +% Version 0 was released in September 1982 after it passed a variety of tests. +% Version 1 was released in November 1983 after thorough testing. +% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984). +% Version 1.2 allowed `0' in response to an error, et alia (October 1984). +% Version 1.3 made memory allocation more flexible and local (November 1984). +% Version 1.4 fixed accents right after line breaks, et alia (April 1985). +% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985). +% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986). +% Version 2.1 corrected anomalies in discretionary breaks (January 1987). +% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987). +% Version 2.3 avoided incomplete page in premature termination (August 1987). +% Version 2.4 fixed \noaligned rules in indented displays (August 1987). +% Version 2.5 saved cur_order when expanding tokens (September 1987). +% Version 2.6 added 10sp slop when shipping leaders (November 1987). +% Version 2.7 improved rounding of negative-width characters (November 1987). +% Version 2.8 fixed weird bug if no \patterns are used (December 1987). +% Version 2.9 made \csname\endcsname's "relax" local (December 1987). +% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988). +% Version 2.92 fixed \patterns, also file names with complex macros (May 1988). +% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988). +% Version 2.94 kept open_log_file from calling fatal_error (November 1988). +% Version 2.95 solved that problem a better way (December 1988). +% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989). +% Version 2.97 corrected blunder in creating 2.95 (February 1989). +% Version 2.98 omitted save_for_after at outer level (March 1989). +% Version 2.99 caught $$\begingroup\halign..$$ (June 1989). +% Version 2.991 caught .5\ifdim.6... (June 1989). +% Version 2.992 introduced major changes for 8-bit extensions (September 1989). +% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989). +% Version 3.0 fixed unusual displays; was more \output robust (March 1990). +% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990). +% Version 3.14 fixed unprintable font names and corrected typos (March 1991). +% Version 3.141 more of same; reconstituted ligatures better (March 1992). +% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993). +% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995). +% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002). +% Version 3.1415926 was a general cleanup with minor fixes (February 2008). + + +% Although considerable effort has been expended to make the LuaTeX program +% correct and reliable, no warranty is implied; the authors disclaim any +% obligation or liability for damages, including but not limited to +% special, indirect, or consequential damages arising out of or in +% connection with the use or performance of this software. This work has +% been a ``labor of love'' and the authors hope that users enjoy it. + +A large piece of software like \TeX\ has inherent complexity that cannot +be reduced below a certain level of difficulty, although each individual +part is fairly simple by itself. The \.{WEB} language is intended to make +the algorithms as readable as possible, by reflecting the way the +individual program pieces fit together and by providing the +cross-references that connect different parts. Detailed comments about +what is going on, and about why things were done in certain ways, have +been liberally sprinkled throughout the program. These comments explain +features of the implementation, but they rarely attempt to explain the +\TeX\ language itself, since the reader is supposed to be familiar with +{\sl The \TeX book}. +@.WEB@> +@:TeXbook}{\sl The \TeX book@> + +The present implementation has a long ancestry, beginning in the summer +of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded +a prototype +@^Plass, Michael Frederick@> +@^Liang, Franklin Mark@> +@^Knuth, Donald Ervin@> +based on some specifications that the author had made in May of that year. +This original proto\TeX\ included macro definitions and elementary +manipulations on boxes and glue, but it did not have line-breaking, +page-breaking, mathematical formulas, alignment routines, error recovery, +or the present semantic nest; furthermore, +it used character lists instead of token lists, so that a control sequence +like \.{\\halign} was represented by a list of seven characters. A +complete version of \TeX\ was designed and coded by the author in late +1977 and early 1978; that program, like its prototype, was written in the +{\mc SAIL} language, for which an excellent debugging system was +available. Preliminary plans to convert the {\mc SAIL} code into a form +somewhat like the present ``web'' were developed by Luis Trabb~Pardo and +@^Trabb Pardo, Luis Isidoro@> +the author at the beginning of 1979, and a complete implementation was +created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which +@^Zabala Salelles, Ignacio Andr\'es@> +was written by the author during the latter part of 1981 and the early +part of 1982, also incorporates ideas from the 1979 implementation of +@^Guibas, Leonidas Ioannis@> +@^Sedgewick, Robert@> +@^Wyatt, Douglas Kirk@> +\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick, +and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred +refinements were introduced into \TeX82 based on the experiences gained with +the original implementations, so that essentially every part of the system +has been substantially improved. After the appearance of ``Version 0'' in +September 1982, this program benefited greatly from the comments of +many other people, notably David~R. Fuchs and Howard~W. Trickey. +A final revision in September 1989 extended the input character set to +eight-bit codes and introduced the ability to hyphenate words from +different languages, based on some ideas of Michael~J. Ferguson. +@^Fuchs, David Raymond@> +@^Trickey, Howard Wellington@> +@^Ferguson, Michael John@> + +No doubt there still is plenty of room for improvement, but the author +is firmly committed to keeping \TeX82 ``frozen'' from now on; stability +and reliability are to be its main virtues. + +On the other hand, the \.{WEB} description can be extended without changing +the core of \TeX82 itself, and the program has been designed so that such +extensions are not extremely difficult to make. +The |banner| string defined here should be changed whenever \TeX\ +undergoes any modifications, so that it will be clear which version of +\TeX\ might be the guilty party when a problem arises. +@^extensions to \TeX@> +@^system dependencies@> + +This program contains code for various features extending \TeX, +therefore this program is called `\eTeX' and not +`\TeX'; the official name `\TeX' by itself is reserved +for software systems that are fully compatible with each other. +A special test suite called the ``\.{TRIP} test'' is available for +helping to determine whether a particular implementation deserves to be +known as `\TeX' [cf.~Stanford Computer Science report CS1027, +November 1984]. + +A similar test suite called the ``\.{e-TRIP} test'' is available for +helping to determine whether a particular implementation deserves to be +known as `\eTeX'. + +@ In case somebody has inadvertently made bad settings of the ``constants,'' +\TeX\ checks them using a global variable called |bad|. + +This is the first of many sections of \TeX\ where global variables are +defined. + +@c +int bad; /* is some ``constant'' wrong? */ +boolean luainit; /* are we using lua for initializations */ +boolean tracefilenames; /* print file open-close info? */ + + +@ This program has two important variations: (1) There is a long and slow +version called \.{INITEX}, which does the extra calculations needed to +@.INITEX@> +initialize \TeX's internal tables; and (2)~there is a shorter and faster +production version, which cuts the initialization to a bare minimum. + +@c +boolean ini_version; /* are we \.{INITEX}? */ +boolean dump_option; /* was the dump name option used? */ +boolean dump_line; /* was a \.{\%\AM format} line seen? */ +int bound_default; /* temporary for setup */ +char *bound_name; /* temporary for setup */ +int error_line; /* width of context lines on terminal error messages */ +int half_error_line; /* width of first lines of contexts in terminal + error messages; should be between 30 and |error_line-15| */ +int max_print_line; /* width of longest text lines output; should be at least 60 */ +int ocp_list_size; +int ocp_buf_size; +int ocp_stack_size; +int max_strings; /* maximum number of strings; must not exceed |max_halfword| */ +int strings_free; /* strings available after format loaded */ +int font_k; /* loop variable for initialization */ +int buf_size; /* maximum number of characters simultaneously present in + current lines of open files and in control sequences between + \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword| */ +int stack_size; /* maximum number of simultaneous input sources */ +int max_in_open; /* maximum number of input files and error insertions that + can be going on simultaneously */ +int param_size; /* maximum number of simultaneous macro parameters */ +int nest_size; /* maximum number of semantic levels simultaneously active */ +int save_size; /* space for saving values outside of current group; must be + at most |max_halfword| */ +int expand_depth; /* limits recursive calls of the |expand| procedure */ +int parsefirstlinep; /* parse the first line for options */ +int filelineerrorstylep; /* format messages as file:line:error */ +int haltonerrorp; /* stop at first error */ +boolean quoted_filename; /* current filename is quoted */ + +@ @c +int get_luatexversion(void) +{ + return luatex_version; +} + +int get_luatexsvn(void) +{ + return luatex_svn; +} + +str_number get_luatexrevision(void) +{ + return luatex_revision; +} + +int get_luatex_date_info(void) +{ + return luatex_date_info; /* todo, silly value */ +} + + +@ This is it: the part of \TeX\ that executes all those procedures we have +written. + +We have noted that there are two versions of \TeX82. One, called \.{INITEX}, +@.INITEX@> +has to be run first; it initializes everything from scratch, without +reading a format file, and it has the capability of dumping a format file. +The other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs +@.VIRTEX@> +to input a format file in order to get started. + +@c +#define const_chk(A) do { \ + if (A < inf_##A) A = inf_##A; \ + if (A > sup_##A) A = sup_##A; \ + } while (0) + +#define setup_bound_var(A,B,C) do { \ + if (luainit>0) { \ + get_lua_number("texconfig",B,&C); \ + if (C==0) C=A; \ + } else { \ + integer x; \ + setupboundvariable(&x, B, A); \ + C = (int)x; \ + } \ + } while (0) + + +int ready_already = 0; + +int main_initialize(void) +{ + int bad = 0; + /* Bounds that may be set from the configuration file. We want the user to + be able to specify the names with underscores, but \.{TANGLE} removes + underscores, so we're stuck giving the names twice, once as a string, + once as the identifier. How ugly. */ + + setup_bound_var(15000, "max_strings", max_strings); + setup_bound_var(100, "strings_free", strings_free); + setup_bound_var(3000, "buf_size", buf_size); + setup_bound_var(50, "nest_size", nest_size); + setup_bound_var(15, "max_in_open", max_in_open); + setup_bound_var(60, "param_size", param_size); + setup_bound_var(4000, "save_size", save_size); + setup_bound_var(300, "stack_size", stack_size); + setup_bound_var(16384, "dvi_buf_size", dvi_buf_size); + setup_bound_var(79, "error_line", error_line); + setup_bound_var(50, "half_error_line", half_error_line); + setup_bound_var(79, "max_print_line", max_print_line); + setup_bound_var(1000, "ocp_list_size", ocp_list_size); + setup_bound_var(1000, "ocp_buf_size", ocp_buf_size); + setup_bound_var(1000, "ocp_stack_size", ocp_stack_size); + setup_bound_var(0, "hash_extra", hash_extra); + setup_bound_var(72, "pk_dpi", pk_dpi); + setup_bound_var(10000, "expand_depth", expand_depth); + + /* Check other constants against their sup and inf. */ + const_chk(buf_size); + const_chk(nest_size); + const_chk(max_in_open); + const_chk(param_size); + const_chk(save_size); + const_chk(stack_size); + const_chk(dvi_buf_size); + const_chk(max_strings); + const_chk(strings_free); + const_chk(hash_extra); + const_chk(pk_dpi); + if (error_line > ssup_error_line) + error_line = ssup_error_line; + + /* array memory allocation */ + buffer = xmallocarray(packed_ASCII_code, (unsigned) buf_size); + nest = xmallocarray(list_state_record, (unsigned) nest_size); + save_stack = xmallocarray(save_record, (unsigned) save_size); + input_stack = xmallocarray(in_state_record, (unsigned) stack_size); + input_file = xmallocarray(alpha_file, (unsigned) max_in_open); + input_file_callback_id = xmallocarray(int, (unsigned) max_in_open); + line_stack = xmallocarray(int, (unsigned) max_in_open); + eof_seen = xmallocarray(boolean, (unsigned) max_in_open); + grp_stack = xmallocarray(save_pointer, (unsigned) max_in_open); + if_stack = xmallocarray(pointer, (unsigned) max_in_open); + source_filename_stack = xmallocarray(str_number, (unsigned) max_in_open); + full_source_filename_stack = xmallocarray(char *, (unsigned) max_in_open); + param_stack = xmallocarray(halfword, (unsigned) param_size); + dvi_buf = xmallocarray(eight_bits, (unsigned) dvi_buf_size); + initialize_ocplist_arrays(ocp_list_size); + initialize_ocp_buffers(ocp_buf_size, ocp_stack_size); + + if (ini_version) { + fixmem = xmallocarray(smemory_word, fix_mem_init + 1); + memset(voidcast(fixmem), 0, (fix_mem_init + 1) * sizeof(smemory_word)); + fix_mem_min = 0; + fix_mem_max = fix_mem_init; + eqtb_top = eqtb_size + hash_extra; + if (hash_extra == 0) + hash_top = undefined_control_sequence; + else + hash_top = eqtb_top; + hash = xmallocarray(two_halves, (unsigned) (hash_top + 1)); + memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); + eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); + memset(eqtb, 0, sizeof(memory_word) * (unsigned) (eqtb_top + 1)); + init_string_pool_array((unsigned) max_strings); + reset_cur_string(); + } + /* Check the ``constant'' values... */ + if ((half_error_line < 30) || (half_error_line > error_line - 15)) + bad = 1; + if (max_print_line < 60) + bad = 2; + if (dvi_buf_size % 8 != 0) + bad = 3; + if (hash_prime > hash_size) + bad = 5; + if (max_in_open >= 128) + bad = 6; + /* Here are the inequalities that the quarterword and halfword values + must satisfy (or rather, the inequalities that they mustn't satisfy): */ + if ((min_quarterword > 0) || (max_quarterword < 0x7FFF)) + bad = 11; + if ((min_halfword > 0) || (max_halfword < 0x3FFFFFFF)) + bad = 12; + if ((min_quarterword < min_halfword) || (max_quarterword > max_halfword)) + bad = 13; + if (font_base < min_quarterword) + bad = 15; + if ((save_size > max_halfword) || (max_strings > max_halfword)) + bad = 17; + if (buf_size > max_halfword) + bad = 18; + if (max_quarterword - min_quarterword < 0xFFFF) + bad = 19; + if (cs_token_flag + eqtb_size + hash_extra > max_halfword) + bad = 21; + if (bad > 0) { + wterm_cr(); + fprintf(term_out, + "Ouch---my internal constants have been clobbered! ---case %d", + (int) bad); + } else { + initialize(); /* set global variables to their starting values */ + if (ini_version) { + /* initialize all the primitives */ + no_new_control_sequence = false; + first = 0; + initialize_commands(); + initialize_etex_commands(); + init_str_ptr = str_ptr; + no_new_control_sequence = true; + fix_date_and_time(); + } + ready_already = 314159; + } + return bad; +} + +@ @c +void main_body(void) +{ + static char pdftex_map[] = "pdftex.map"; + bad = main_initialize(); + history = fatal_error_stop; /* in case we quit during initialization */ + t_open_out(); /* open the terminal for output */ + if (!luainit) + tracefilenames = true; + if (bad > 0) { + goto FINAL_END; + } + print_banner(luatex_version_string, luatex_date_info, luatex_svn); + + /* Get the first line of input and prepare to start */ + /* When we begin the following code, \TeX's tables may still contain garbage; + the strings might not even be present. Thus we must proceed cautiously to get + bootstrapped in. + + But when we finish this part of the program, \TeX\ is ready to call on the + |main_control| routine to do its work. + */ + initialize_inputstack(); /* this copies the command-line */ + if (buffer[iloc] == '*') + incr(iloc); + if ((format_ident == 0) || (buffer[iloc] == '&') || dump_line) { + char *fname = NULL; + if (format_ident != 0) + initialize(); /* erase preloaded format */ + if ((fname = open_fmt_file()) == NULL) + goto FINAL_END; + if (!load_fmt_file(fname)) { + zwclose(fmt_file); + goto FINAL_END; + } + zwclose(fmt_file); + while ((iloc < ilimit) && (buffer[iloc] == ' ')) + incr(iloc); + } + if (pdf_output_option != 0) + int_par(pdf_output_code) = pdf_output_value; + if (pdf_draftmode_option != 0) + int_par(pdf_draftmode_code) = pdf_draftmode_value; + pdf_init_map_file((char *) pdftex_map); + if (end_line_char_inactive()) + decr(ilimit); + else + buffer[ilimit] = (packed_ASCII_code) int_par(end_line_char_code); + fix_date_and_time(); + if (ini_version) + make_pdftex_banner(); + random_seed = (microseconds * 1000) + (epochseconds % 1000000); + init_randoms(random_seed); + initialize_math(); + fixup_selector(log_opened); + check_texconfig_init(); + if ((iloc < ilimit) && (get_cat_code(int_par(cat_code_table_code), + buffer[iloc]) != escape_cmd)) + start_input(); /* \.{\\input} assumed */ + /* DIR: Initialize |text_dir_ptr| */ + text_dir_ptr = new_dir(0); + + history = spotless; /* ready to go! */ + /* Initialize synctex primitive */ + synctexinitcommand(); + main_control(); /* come to life */ + flush_node(text_dir_ptr); + final_cleanup(); /* prepare for death */ + close_files_and_terminate(); + FINAL_END: + do_final_end(); +} + + +@ Here we do whatever is needed to complete \TeX's job gracefully on the +local operating system. The code here might come into play after a fatal +error; it must therefore consist entirely of ``safe'' operations that +cannot produce error messages. For example, it would be a mistake to call +|str_room| or |make_string| at this time, because a call on |overflow| +might lead to an infinite loop. +@^system dependencies@> + +Actually there's one way to get error messages, via |prepare_mag|; +but that can't cause infinite recursion. +@^recursion@> + +This program doesn't bother to close the input files that may still be open. + +@c +void close_files_and_terminate(void) +{ + int k; /* all-purpose index */ + int callback_id; + PDF pdf = static_pdf; + callback_id = callback_defined(stop_run_callback); + /* Finish the extensions */ + for (k = 0; k <= 15; k++) + if (write_open[k]) + lua_a_close_out(write_file[k]); + if (int_par(tracing_stats_code) > 0) { + if (callback_id == 0) { + /* Output statistics about this job */ + /* The present section goes directly to the log file instead of using + |print| commands, because there's no need for these strings to take + up |string_pool| memory when a non-{\bf stat} version of \TeX\ is being used. + */ + + if (log_opened) { + fprintf(log_file, + "\n\nHere is how much of LuaTeX's memory you used:\n"); + fprintf(log_file, " %d string%s out of %d\n", + (int) (str_ptr - init_str_ptr), + (str_ptr == (init_str_ptr + 1) ? "" : "s"), + (int) (max_strings - init_str_ptr + STRING_OFFSET)); + fprintf(log_file, " %d,%d words of node,token memory allocated", + (int) var_mem_max, (int) fix_mem_max); + print_node_mem_stats(); + fprintf(log_file, + " %d multiletter control sequences out of %ld+%d\n", + (int) cs_count, (long) hash_size, (int) hash_extra); + fprintf(log_file, " %d font%s using %d bytes\n", + (int) max_font_id(), (max_font_id() == 1 ? "" : "s"), + (int) font_bytes); + fprintf(log_file, + " %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds\n", + (int) max_in_stack, (int) max_nest_stack, + (int) max_param_stack, (int) max_buf_stack, + (int) max_save_stack + 6, (int) stack_size, + (int) nest_size, (int) param_size, (int) buf_size, + (int) save_size); + } + } + } + wake_up_terminal(); + ensure_output_state(pdf, ST_OMODE_FIX); + switch (pdf->o_mode) { + case OMODE_NONE: /* during initex run */ + break; + case OMODE_PDF: + if (history == fatal_error_stop) { + remove_pdffile(pdf); + print_err + (" ==> Fatal error occurred, no output PDF file produced!"); + } else + finish_pdf_file(pdf, luatex_version, get_luatexrevision()); + break; + case OMODE_DVI: + finish_dvi_file(pdf, luatex_version, get_luatexrevision()); + break; + case OMODE_LUA: + finish_lua_file(pdf); + break; + default: + assert(0); + } + /* Close {\sl Sync\TeX} file and write status */ + synctexterminate(log_opened); /* Let the {\sl Sync\TeX} controller close its files. */ + + free_text_codes(); + free_math_codes(); + if (log_opened) { + wlog_cr(); + selector = selector - 2; + if ((selector == term_only) && (callback_id == 0)) { + tprint_nl("Transcript written on "); + tprint_file_name(NULL, texmf_log_name, NULL); + print_char('.'); + print_ln(); + } + lua_a_close_out(log_file); + } +} + + +@ We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has +been scanned and |its_all_over|\kern-2pt. + +@c +void final_cleanup(void) +{ + int c; /* 0 for \.{\\end}, 1 for \.{\\dump} */ + halfword i; /* for looping marks */ + c = cur_chr; + if (job_name == 0) + open_log_file(); + while (input_ptr > 0) + if (istate == token_list) + end_token_list(); + else + end_file_reading(); + while (open_parens > 0) { + if (tracefilenames) + tprint(" )"); + decr(open_parens); + } + if (cur_level > level_one) { + tprint_nl("(\\end occurred inside a group at level "); + print_int(cur_level - level_one); + print_char(')'); + show_save_groups(); + } + while (cond_ptr != null) { + tprint_nl("(\\end occurred when "); + print_cmd_chr(if_test_cmd, cur_if); + if (if_line != 0) { + tprint(" on line "); + print_int(if_line); + } + tprint(" was incomplete)"); + if_line = if_line_field(cond_ptr); + cur_if = subtype(cond_ptr); + temp_ptr = cond_ptr; + cond_ptr = vlink(cond_ptr); + flush_node(temp_ptr); + } + if (callback_defined(stop_run_callback) == 0) + if (history != spotless) + if ((history == warning_issued) || (interaction < error_stop_mode)) + if (selector == term_and_log) { + selector = term_only; + tprint_nl + ("(see the transcript file for additional information)"); + selector = term_and_log; + } + if (c == 1) { + if (ini_version) { + for (i = 0; i <= biggest_used_mark; i++) { + delete_top_mark(i); + delete_first_mark(i); + delete_bot_mark(i); + delete_split_first_mark(i); + delete_split_bot_mark(i); + } + for (c = last_box_code; c <= vsplit_code; c++) + flush_node_list(disc_ptr[c]); + if (last_glue != max_halfword) + delete_glue_ref(last_glue); + while (pseudo_files != null) + pseudo_close(); /* flush pseudo files */ + store_fmt_file(); + return; + } + tprint_nl("(\\dump is performed only by INITEX)"); + return; + } +} + +@ Once \TeX\ is working, you should be able to diagnose most errors with +the \.{\\show} commands and other diagnostic features. +An additional routine called |debug_help| +will come into play when you type `\.D' after an error message; +|debug_help| also occurs just before a fatal error causes \TeX\ to succumb. +@^debugging@> +@^system dependencies@> + +The interface to |debug_help| is primitive, but it is good enough when used +with a debugger that allows you to set breakpoints and to read +variables and change their values. After getting the prompt `\.{debug \#}', you +type either a negative number (this exits |debug_help|), or zero (this +goes to a location where you can set a breakpoint, thereby entering into +dialog with the debugger), or a positive number |m| followed by +an argument |n|. The meaning of |m| and |n| will be clear from the +program below. (If |m=13|, there is an additional argument, |l|.) +@.debug \#@> + +@c +#ifdef DEBUG +void debug_help(void) +{ /* routine to display various things */ + int k; + int m = 0, n = 0, l = 0; + while (1) { + wake_up_terminal(); + tprint_nl("debug # (-1 to exit):"); + update_terminal(); + (void) fscanf(term_in, "%d", &m); + if (m < 0) + return; + else if (m == 0) + abort(); /* go to every label at least once */ + else { + (void) fscanf(term_in, "%d", &n); + switch (m) { + case 1: + print_word(varmem[n]); /* display |varmem[n]| in all forms */ + break; + case 2: + print_int(info(n)); + break; + case 3: + print_int(link(n)); + break; + case 4: + print_word(eqtb[n]); + break; + case 6: + print_int(save_type(n)); + print_int(save_level(n)); + print_word(save_word(n)); + break; + case 7: + show_box(n); /* show a box, abbreviated by |show_box_depth| and |show_box_breadth| */ + break; + case 8: + breadth_max = 10000; + depth_threshold = 0x7FFFFFFF; + show_node_list(n); /* show a box in its entirety */ + break; + case 9: + show_token_list(n, null, 1000); + break; + case 10: + slow_print(n); + break; + case 13: + (void) fscanf(term_in, "%d", &l); + print_cmd_chr(n, l); + break; + case 14: + for (k = 0; k <= n; k++) + print(buffer[k]); + break; + case 15: + font_in_short_display = null_font; + short_display(n); + break; + default: + tprint("?"); + break; + } + } + } +} +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.h b/Build/source/texk/web2c/luatexdir/tex/maincontrol.h new file mode 100644 index 00000000000..e07f1e23cd9 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.h @@ -0,0 +1,196 @@ +/* maincontrol.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: maincontrol.h 3354 2010-01-11 08:49:33Z taco $ */ + +#ifndef MAINCONTROL_H +# define MAINCONTROL_H + +# define fil_code 0 /* identifies \.{\\hfil} and \.{\\vfil} */ +# define fill_code 1 /* identifies \.{\\hfill} and \.{\\vfill} */ +# define ss_code 2 /* identifies \.{\\hss} and \.{\\vss} */ +# define fil_neg_code 3 /* identifies \.{\\hfilneg} and \.{\\vfilneg} */ +# define skip_code 4 /* identifies \.{\\hskip} and \.{\\vskip} */ +# define mskip_code 5 /* identifies \.{\\mskip} */ + +/* The |prefixed_command| does not have to adjust |a| so that |a mod 4=0|, +since the following routines test for the \.{\\global} prefix as follows. +*/ + +# define is_global(a) (a>=4) + +# define define(A,B,C) do { \ + if (is_global(a)) \ + geq_define((A),(quarterword)(B),(C)); \ + else \ + eq_define((A),(quarterword)(B),(C)); \ + } while (0) + +# define word_define(A,B) do { \ + if (is_global(a)) \ + geq_word_define((A),(B)); \ + else \ + eq_word_define((A),(B)); \ + } while (0) + +# define define_lc_code(A,B) do { \ + if (is_global(a)) \ + set_lc_code((A),(B),level_one); \ + else \ + set_lc_code((A),(B),cur_level); \ + } while (0) + +# define define_uc_code(A,B) do { \ + if (is_global(a)) \ + set_uc_code((A),(B),level_one); \ + else \ + set_uc_code((A),(B),cur_level); \ +} while (0) + +# define define_sf_code(A,B) do { \ + if (is_global(a)) \ + set_sf_code((A),(B),level_one); \ + else \ + set_sf_code((A),(B),cur_level); \ + } while (0) + +# define define_cat_code(A,B) do { \ + if (is_global(a)) \ + set_cat_code(int_par(cat_code_table_code),(A),(B),level_one); \ + else \ + set_cat_code(int_par(cat_code_table_code),(A),(B),cur_level); \ + } while (0) + +# define define_fam_fnt(A,B,C) do { \ + if (is_global(a)) \ + def_fam_fnt((A),(B),(C),level_one); \ + else \ + def_fam_fnt((A),(B),(C),cur_level); \ + } while (0) + +# define define_math_param(A,B,C) do { \ + if (is_global(a)) \ + def_math_param((A),(B),(C),level_one); \ + else \ + def_math_param((A),(B),(C),cur_level); \ + } while (0) + +extern halfword cur_box; /* box to be placed into its context */ + +/* +A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|; +a \.{\\mathchardef} creates a control sequence whose |cmd| is |math_given|; +and the corresponding |chr| is the character code or math code. A \.{\\countdef} +or \.{\\dimendef} or \.{\\skipdef} or \.{\\muskipdef} creates a control +sequence whose |cmd| is |assign_int| or \dots\ or |assign_mu_glue|, and the +corresponding |chr| is the |eqtb| location of the internal register in question. +*/ + +# define char_def_code 0 /* |shorthand_def| for \.{\\chardef} */ +# define math_char_def_code 1 /* |shorthand_def| for \.{\\mathchardef} */ +# define omath_char_def_code 2 /* |shorthand_def| for \.{\\omathchardef} */ +# define xmath_char_def_code 3 /* |shorthand_def| for \.{\\Umathchardef} */ +# define count_def_code 4 /* |shorthand_def| for \.{\\countdef} */ +# define attribute_def_code 5 /* |shorthand_def| for \.{\\attributedef} */ +# define dimen_def_code 6 /* |shorthand_def| for \.{\\dimendef} */ +# define skip_def_code 7 /* |shorthand_def| for \.{\\skipdef} */ +# define mu_skip_def_code 8 /* |shorthand_def| for \.{\\muskipdef} */ +# define toks_def_code 9 /* |shorthand_def| for \.{\\toksdef} */ + +extern void adjust_space_factor(void); +extern void main_control(void); +extern void app_space(void); +extern void insert_dollar_sign(void); +extern void you_cant(void); +extern void report_illegal_case(void); +extern boolean privileged(void); +extern boolean its_all_over(void); +extern void append_glue(void); +extern void append_kern(void); +extern void off_save(void); +extern void handle_right_brace(void); +extern void extra_right_brace(void); +extern void normal_paragraph(void); +extern void box_end(int box_context); +extern void scan_box(int box_context); +extern void new_graf(boolean indented); +extern void indent_in_hmode(void); +extern void head_for_vmode(void); +extern void end_graf(int); +extern void begin_insert_or_adjust(void); +extern void handle_mark(void); +extern void append_penalty(void); +extern void delete_last(void); +extern void unpackage(void); +extern void append_italic_correction(void); +extern void append_local_box(int kind); +extern void append_discretionary(void); +extern void build_local_box(void); +extern void build_discretionary(void); +extern void make_accent(void); +extern void align_error(void); +extern void no_align_error(void); +extern void omit_error(void); +extern void do_endv(void); +extern void cs_error(void); +extern void prefixed_command(void); +extern void fixup_directions(void); + + +/* Assignments from Lua need helpers. */ + +# define is_int_assign(cmd) (cmd==assign_int_cmd) +# define is_dim_assign(cmd) (cmd==assign_dimen_cmd) +# define is_toks_assign(cmd) (cmd==assign_toks_cmd) + +# define show_code 0 /* \.{\\show} */ +# define show_box_code 1 /* \.{\\showbox} */ +# define show_the_code 2 /* \.{\\showthe} */ +# define show_lists 3 /* \.{\\showlists} */ +# define show_groups 4 /* \.{\\showgroups} */ +# define show_tokens 5 /* \.{\\showtokens} , must be odd! */ +# define show_ifs 6 /* \.{\\showifs} */ + +# define print_if_line(A) if ((A)!=0) { \ + tprint(" entered on line "); print_int((A)); \ + } + +extern void get_r_token(void); +extern void assign_internal_value(int a, halfword p, int cur_val); +extern void trap_zero_glue(void); +extern void do_register_command(int a); +extern void alter_aux(void); +extern void alter_prev_graf(void); +extern void alter_page_so_far(void); +extern void alter_integer(void); +extern void alter_box_dimen(void); +extern void new_interaction(void); +extern halfword after_token; +extern void do_assignments(void); +extern void open_or_close_in(void); +extern boolean long_help_seen; +extern void issue_message(void); +extern void give_err_help(void); +extern void shift_case(void); +extern void show_whatever(void); + +extern void initialize(void); /* this procedure gets things started properly */ + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w b/Build/source/texk/web2c/luatexdir/tex/maincontrol.w new file mode 100644 index 00000000000..4e49ea88578 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.w @@ -0,0 +1,3610 @@ +% maincontrol.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +static const char _svn_version[] = + "$Id: maincontrol.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/maincontrol.w $"; + +#include "ptexlib.h" + +@ @c +#define explicit 1 +#define acc_kern 2 +#define lp_code_base 2 +#define rp_code_base 3 +#define ef_code_base 4 +#define tag_code 5 +#define auto_kern explicit +#define no_lig_code 6 + + +#define prev_depth cur_list.prev_depth_field +#define space_factor cur_list.space_factor_field +#define par_shape_ptr equiv(par_shape_loc) + +#define cur_lang int_par(cur_lang_code) +#define global_defs int_par(global_defs_code) +#define output_box int_par(output_box_code) +#define end_line_char int_par(end_line_char_code) +#define new_line_char int_par(new_line_char_code) +#define tracing_online int_par(tracing_online_code) +#define no_local_whatsits int_par(no_local_whatsits_code) +#define no_local_dirs int_par(no_local_dirs_code) +#define err_help equiv(err_help_loc) +#define text_direction int_par(text_direction_code) +#define every_par equiv(every_par_loc) +#define pdf_ignored_dimen dimen_par(pdf_ignored_dimen_code) +#define par_direction int_par(par_direction_code) + +#define pdf_first_line_height dimen_par(pdf_first_line_height_code) +#define pdf_last_line_depth dimen_par(pdf_last_line_depth_code) +#define pdf_each_line_height dimen_par(pdf_each_line_height_code) +#define pdf_each_line_depth dimen_par(pdf_each_line_depth_code) +#define page_left_offset dimen_par(page_left_offset_code) +#define page_top_offset dimen_par(page_top_offset_code) +#define page_right_offset dimen_par(page_right_offset_code) +#define page_bottom_offset dimen_par(page_bottom_offset_code) +#define pdf_h_origin dimen_par(pdf_h_origin_code) +#define pdf_v_origin dimen_par(pdf_v_origin_code) +#define pdf_px_dimen dimen_par(pdf_px_dimen_code) + +#define pdf_image_resolution int_par(pdf_image_resolution_code) +#define escape_char int_par(escape_char_code) +#define max_dead_cycles int_par(max_dead_cycles_code) +#define tolerance int_par(tolerance_code) +#define mag int_par(mag_code) +#define cat_code_table int_par(cat_code_table_code) + +#define par_indent dimen_par(par_indent_code) +#define looseness int_par(looseness_code) +#define space_skip glue_par(space_skip_code) +#define xspace_skip glue_par(xspace_skip_code) +#define every_vbox equiv(every_vbox_loc) + +#define split_top_skip glue_par(split_top_skip_code) +#define split_max_depth dimen_par(split_max_depth_code) + +#define hang_indent dimen_par(hang_indent_code) +#define hang_after int_par(hang_after_code) +#define inter_line_penalties_ptr equiv(inter_line_penalties_loc) + +#define box(A) eqtb[box_base+(A)].hh.rh +#define cur_font equiv(cur_font_loc) +#define hsize dimen_par(hsize_code) +#define ex_hyphen_char int_par(ex_hyphen_char_code) +#define floating_penalty int_par(floating_penalty_code) + +#define mode cur_list.mode_field +#define tail cur_list.tail_field +#define head cur_list.head_field +#define prev_graf cur_list.pg_field +#define dir_save cur_list.dirs_field + +#define check_filter(A) if (!output_active) lua_node_filter_s(buildpage_filter_callback,(A)) + +#define var_code 7 /* math code meaning ``use the current family'' */ + +@ We come now to the |main_control| routine, which contains the master +switch that causes all the various pieces of \TeX\ to do their things, +in the right order. + +In a sense, this is the grand climax of the program: It applies all the +tools that we have worked so hard to construct. In another sense, this is +the messiest part of the program: It necessarily refers to other pieces +of code all over the place, so that a person can't fully understand what is +going on without paging back and forth to be reminded of conventions that +are defined elsewhere. We are now at the hub of the web, the central nervous +system that touches most of the other parts and ties them together. +@^brain@> + +The structure of |main_control| itself is quite simple. There's a label +called |big_switch|, at which point the next token of input is fetched +using |get_x_token|. Then the program branches at high speed into one of +about 100 possible directions, based on the value of the current +mode and the newly fetched command code; the sum |abs(mode)+cur_cmd| +indicates what to do next. For example, the case `|vmode+letter|' arises +when a letter occurs in vertical mode (or internal vertical mode); this +case leads to instructions that initialize a new paragraph and enter +horizontal mode. + +The big |case| statement that contains this multiway switch has been labeled +|reswitch|, so that the program can |goto reswitch| when the next token +has already been fetched. Most of the cases are quite short; they call +an ``action procedure'' that does the work for that case, and then they +either |goto reswitch| or they ``fall through'' to the end of the |case| +statement, which returns control back to |big_switch|. Thus, |main_control| +is not an extremely large procedure, in spite of the multiplicity of things +it must do; it is small enough to be handled by PASCAL compilers that put +severe restrictions on procedure size. +@!@^action procedure@> + +One case is singled out for special treatment, because it accounts for most +of \TeX's activities in typical applications. The process of reading simple +text and converting it into |char_node| records, while looking for ligatures +and kerns, is part of \TeX's ``inner loop''; the whole program runs +efficiently when its inner loop is fast, so this part has been written +with particular care. + +@c +static halfword main_p; /* temporary register for list manipulation */ +static halfword main_s; /* space factor value */ + + +@ We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we +set it equal to |sf_code(cur_chr)|, except that it should never change +from a value less than 1000 to a value exceeding 1000. The most common +case is |sf_code(cur_chr)=1000|, so we want that case to be fast. + +@c +void adjust_space_factor(void) +{ + main_s = get_sf_code(cur_chr); + if (main_s == 1000) { + space_factor = 1000; + } else if (main_s < 1000) { + if (main_s > 0) + space_factor = main_s; + } else if (space_factor < 1000) { + space_factor = 1000; + } else { + space_factor = main_s; + } +} + + +@ From Knuth: ``Having |font_glue| allocated for each text font saves +both time and memory.'' That may be true, but it also punches through +the API wall for fonts, so I removed that -- Taco. But a bit of caching +is very welcome, which is why I need to have the next two globals: + +@c +internal_font_number space_spec_font; +halfword space_spec_cache; + +@ for mode-independent commands, the following macros is useful: + +@c +#define any_mode(A) vmode+(A): case hmode+(A): case mmode+(A) + + +@ There is a list of cases where the user has probably gotten into or out of math +mode by mistake. \TeX\ will insert a dollar sign and rescan the current token. + +@c +#define non_math(A) vmode+(A): case hmode+(A) + +@ @c +void main_control(void) +{ /* governs \TeX's activities */ + int t; /* general-purpose temporary variable */ + halfword p; /* for whatsit nodes and testing whether an auto kern should be inserted */ + int chr_stack; /* to temporarily save an |cur_chr| to be appended */ + mathcodeval mval; /* to build up an argument to |set_math_char| */ + t = 0; /* for -Wall */ + chr_stack = -1; + if (equiv(every_job_loc) != null) + begin_token_list(equiv(every_job_loc), every_job_text); + + BIG_SWITCH: + get_x_token(); + + RESWITCH: + /* Give diagnostic information, if requested */ + /* When a new token has just been fetched at |big_switch|, we have an + ideal place to monitor \TeX's activity. */ + if (interrupt != 0) { + if (OK_to_interrupt) { + back_input(); + check_interrupt(); + goto BIG_SWITCH; + } + } + if (int_par(tracing_commands_code) > 0) + show_cur_cmd_chr(); + + switch (abs(mode) + cur_cmd) { + case hmode + letter_cmd: + case hmode + other_char_cmd: + case hmode + char_given_cmd: + case hmode + char_num_cmd: + if (abs(mode) + cur_cmd == hmode + char_num_cmd) { + scan_char_num(); + cur_chr = cur_val; + } + if (is_last_ocp(current_ocp_lstack, current_ocp_no)) { + /* Append character |cur_chr| and the following characters (if~any) + to the current hlist in the current font; |goto reswitch| when + a non-character has been fetched */ + CONTINUE: + adjust_space_factor(); + chr_stack = cur_chr; + tail_append(new_char(cur_font, chr_stack)); + get_x_token(); + if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd) || + (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) { + if (cur_cmd == char_num_cmd) { + scan_char_num(); + cur_chr = cur_val; + } + if ((chr_stack == ex_hyphen_char) && (cur_chr != ex_hyphen_char) + && (mode > 0)) { + tail = compound_word_break(tail, cur_lang); + subtype(tail) = automatic_disc; + } + goto CONTINUE; + } else { + if ((chr_stack == ex_hyphen_char) && (mode > 0)) { + tail = compound_word_break(tail, cur_lang); + subtype(tail) = automatic_disc; + } + chr_stack = -1; + goto RESWITCH; + } + + } else { + /* Create a buffer with character |cur_chr| and the following + characters (if~any) and then apply the current active OCP filter + to this buffer */ + run_ocp(); + goto BIG_SWITCH; + + } + break; + case hmode + spacer_cmd: + case hmode + ex_space_cmd: + case mmode + ex_space_cmd: + if ((abs(mode) + cur_cmd == hmode + spacer_cmd) + && (!(space_factor == 1000))) { + app_space(); + } else { + /* Append a normal inter-word space to the current list */ + /* The occurrence of blank spaces is almost part of \TeX's inner loop, + since we usually encounter about one space for every five non-blank characters. + Therefore |main_control| gives second-highest priority to ordinary spaces. + + When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will + see to it later that the corresponding glue specification is precisely + |zero_glue|, not merely a pointer to some specification that happens + to be full of zeroes. Therefore it is simple to test whether a glue parameter + is zero or~not. + */ + if (space_skip == zero_glue) { + /* Find the glue specification, |main_p|, for + text spaces in the current font */ + if (cur_font != space_spec_font) { + if (space_spec_cache != zero_glue) + delete_glue_ref(space_spec_cache); + space_spec_cache = new_spec(zero_glue); + width(space_spec_cache) = space(cur_font); + stretch(space_spec_cache) = space_stretch(cur_font); + shrink(space_spec_cache) = space_shrink(cur_font); + space_spec_font = cur_font; + } + main_p = space_spec_cache; + + temp_ptr = new_glue(main_p); + } else { + temp_ptr = new_param_glue(space_skip_code); + } + vlink(tail) = temp_ptr; + tail = temp_ptr; + + } + break; + + case hmode + no_boundary_cmd: + /* Append a |cancel_boundary_node| */ + new_whatsit(cancel_boundary_node); + break; + case hmode + char_ghost_cmd: + t = cur_chr; + get_x_token(); + if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd) + || (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) { + p = new_glyph(get_cur_font(), cur_chr); + if (t == 0) { + set_is_leftghost(p); + } else { + set_is_rightghost(p); + } + tail_append(p); + } + break; + /* Cases of |main_control| that are not part of the inner loop */ + case any_mode(relax_cmd): + case vmode + spacer_cmd: + case mmode + spacer_cmd: + case mmode + no_boundary_cmd: + break; + case any_mode(ignore_spaces_cmd): + if (cur_chr == 0) { + /* Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + goto RESWITCH; + } else { + t = scanner_status; + scanner_status = normal; + get_token_lua(); + scanner_status = t; + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = (cur_cmd * STRING_OFFSET) + cur_chr; + goto RESWITCH; + } + } + break; + case vmode + stop_cmd: + if (its_all_over()) + return; /* this is the only way out */ + break; + /* Math cases in non-math modes */ + case vmode + math_char_num_cmd: + case vmode + math_given_cmd: + case vmode + omath_given_cmd: + case vmode + xmath_given_cmd: + back_input(); + new_graf(true); + break; + case hmode + math_char_num_cmd: + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(aleph_mathcode); + else if (cur_chr == 2) + mval = scan_mathchar(xetex_mathcode); + else + mval = scan_mathchar(xetexnum_mathcode); + math_char_in_text(mval); + break; + case hmode + math_given_cmd: + mval = mathchar_from_integer(cur_chr, tex_mathcode); + math_char_in_text(mval); + break; + case hmode + omath_given_cmd: + mval = mathchar_from_integer(cur_chr, aleph_mathcode); + math_char_in_text(mval); + break; + case hmode + xmath_given_cmd: + mval = mathchar_from_integer(cur_chr, xetex_mathcode); + math_char_in_text(mval); + break; + + /* Forbidden cases detected in |main_control| */ + case vmode + vmove_cmd: + case hmode + hmove_cmd: + case mmode + hmove_cmd: + case any_mode(last_item_cmd): + case vmode + vadjust_cmd: + case vmode + ital_corr_cmd: + case non_math(eq_no_cmd): + case any_mode(mac_param_cmd): + /* When erroneous situations arise, \TeX\ usually issues an error message + specific to the particular error. For example, `\.{\\noalign}' should + not appear in any mode, since it is recognized by the |align_peek| routine + in all of its legitimate appearances; a special error message is given + when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate + error message is simply that the user is not allowed to do what he or she + has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode, + and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated + here and in the other sections referred to under `See also \dots.' + */ + report_illegal_case(); + break; + + /* Math-only cases in non-math modes, or vice versa */ + case non_math(sup_mark_cmd): + case non_math(sub_mark_cmd): + case non_math(super_sub_script_cmd): + case non_math(math_comp_cmd): + case non_math(delim_num_cmd): + case non_math(left_right_cmd): + case non_math(above_cmd): + case non_math(radical_cmd): + case non_math(math_style_cmd): + case non_math(math_choice_cmd): + case non_math(vcenter_cmd): + case non_math(non_script_cmd): + case non_math(mkern_cmd): + case non_math(limit_switch_cmd): + case non_math(mskip_cmd): + case non_math(math_accent_cmd): + case mmode + endv_cmd: + case mmode + par_end_cmd: + case mmode + stop_cmd: + case mmode + vskip_cmd: + case mmode + un_vbox_cmd: + case mmode + valign_cmd: + case mmode + hrule_cmd: + insert_dollar_sign(); + break; + /* Cases of |main_control| that build boxes and lists */ + case vmode + hrule_cmd: + case hmode + vrule_cmd: + case mmode + vrule_cmd: + /* The most important parts of |main_control| are concerned with \TeX's + chief mission of box-making. We need to control the activities that put + entries on vlists and hlists, as well as the activities that convert + those lists into boxes. All of the necessary machinery has already been + developed; it remains for us to ``push the buttons'' at the right times. + + As an introduction to these routines, let's consider one of the simplest + cases: What happens when `\.{\\hrule}' occurs in vertical mode, or + `\.{\\vrule}' in horizontal mode or math mode? The code in |main_control| + is short, since the |scan_rule_spec| routine already does most of what is + required; thus, there is no need for a special action procedure. + + Note that baselineskip calculations are disabled after a rule in vertical + mode, by setting |prev_depth:=pdf_ignored_dimen|. + */ + tail_append(scan_rule_spec()); + if (abs(mode) == vmode) + prev_depth = pdf_ignored_dimen; + else if (abs(mode) == hmode) + space_factor = 1000; + break; + + case vmode + vskip_cmd: + case hmode + hskip_cmd: + case mmode + hskip_cmd: + case mmode + mskip_cmd: + /* The processing of things like \.{\\hskip} and \.{\\vskip} is slightly + more complicated. But the code in |main_control| is very short, since + it simply calls on the action routine |append_glue|. Similarly, \.{\\kern} + activates |append_kern|. */ + append_glue(); + break; + case any_mode(kern_cmd): + case mmode + mkern_cmd: + append_kern(); + break; + + case non_math(left_brace_cmd): + /* Many of the actions related to box-making are triggered by the appearance + of braces in the input. For example, when the user says `\.{\\hbox} + \.{to} \.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode, + the information about the box size (100pt, |exactly|) is put onto |save_stack| + with a level boundary word just above it, and |cur_group:=adjusted_hbox_group|; + \TeX\ enters restricted horizontal mode to process the hlist. The right + brace eventually causes |save_stack| to be restored to its former state, + at which time the information about the box size (100pt, |exactly|) is + available once again; a box is packaged and we leave restricted horizontal + mode, appending the new box to the current list of the enclosing mode + (in this case to the current list of vertical mode), followed by any + vertical adjustments that were removed from the box by |hpack|. + + The next few sections of the program are therefore concerned with the + treatment of left and right curly braces. + + If a left brace occurs in the middle of a page or paragraph, it simply + introduces a new level of grouping, and the matching right brace will not have + such a drastic effect. Such grouping affects neither the mode nor the + current list. */ + new_save_level(simple_group); + eq_word_define(int_base + no_local_whatsits_code, 0); + eq_word_define(int_base + no_local_dirs_code, 0); + break; + case any_mode(begin_group_cmd): + new_save_level(semi_simple_group); + eq_word_define(int_base + no_local_whatsits_code, 0); + eq_word_define(int_base + no_local_dirs_code, 0); + break; + case any_mode(end_group_cmd): + if (cur_group == semi_simple_group) { + fixup_directions(); + } else { + off_save(); + } + break; + case any_mode(right_brace_cmd): + handle_right_brace(); + break; + + case vmode + hmove_cmd: + case hmode + vmove_cmd: + case mmode + vmove_cmd: + /* Constructions that require a box are started by calling |scan_box| with + a specified context code. The |scan_box| routine verifies + that a |make_box| command comes next and then it calls |begin_box|. + */ + t = cur_chr; + scan_normal_dimen(); + if (t == 0) + scan_box(cur_val); + else + scan_box(-cur_val); + break; + case any_mode(leader_ship_cmd): + scan_box(leader_flag - a_leaders + cur_chr); + break; + case any_mode(make_box_cmd): + begin_box(0); + break; + case any_mode(assign_box_dir_cmd): + scan_register_num(); + cur_box = box(cur_val); + scan_optional_equals(); + scan_direction(); + if (cur_box != null) + box_dir(cur_box) = cur_val; + break; + + case vmode + start_par_cmd: + /* There is a really small patch to add a new primitive called + \.{\\quitvmode}. In vertical modes, it is identical to \.{\\indent}, + but in horizontal and math modes it is really a no-op (as opposed to + \.{\\indent}, which executes the |indent_in_hmode| procedure). + */ + /* A paragraph begins when horizontal-mode material occurs in vertical mode, + or when the paragraph is explicitly started by `\.{\\quitvmode}', + `\.{\\indent}' or `\.{\\noindent}'. + */ + new_graf((cur_chr > 0)); + break; + case hmode + start_par_cmd: + case mmode + start_par_cmd: + if (cur_chr != 2) + indent_in_hmode(); + break; + case vmode + letter_cmd: + case vmode + other_char_cmd: + case vmode + char_num_cmd: + case vmode + char_given_cmd: + case vmode + char_ghost_cmd: + case vmode + math_shift_cmd: + case vmode + math_shift_cs_cmd: + case vmode + un_hbox_cmd: + case vmode + vrule_cmd: + case vmode + accent_cmd: + case vmode + discretionary_cmd: + case vmode + hskip_cmd: + case vmode + valign_cmd: + case vmode + ex_space_cmd: + case vmode + no_boundary_cmd: + back_input(); + new_graf(true); + break; + + case vmode + par_end_cmd: + /* A paragraph ends when a |par_end| command is sensed, or when we are in + horizontal mode when reaching the right brace of vertical-mode routines + like \.{\\vbox}, \.{\\insert}, or \.{\\output}. */ + normal_paragraph(); + if (mode > 0) { + check_filter("vmode_par"); + build_page(); + } + break; + case hmode + par_end_cmd: + if (align_state < 0) + off_save(); /* this tries to recover from an alignment that didn't end properly */ + end_graf(bottom_level); /* this takes us to the enclosing mode, if |mode>0| */ + if (mode == vmode) { + check_filter("hmode_par"); + build_page(); + } + break; + case hmode + stop_cmd: + case hmode + vskip_cmd: + case hmode + hrule_cmd: + case hmode + un_vbox_cmd: + case hmode + halign_cmd: + head_for_vmode(); + break; + case any_mode(insert_cmd): + case hmode + vadjust_cmd: + case mmode + vadjust_cmd: + /* Insertion and adjustment and mark nodes are constructed by the following + pieces of the program. */ + begin_insert_or_adjust(); + break; + case any_mode(mark_cmd): + handle_mark(); + break; + case any_mode(break_penalty_cmd): + /* Penalty nodes get into a list via the |break_penalty| command. */ + append_penalty(); + break; + case any_mode(remove_item_cmd): + /* The |remove_item| command removes a penalty, kern, or glue node if it + appears at the tail of the current list, using a brute-force linear scan. + Like \.{\\lastbox}, this command is not allowed in vertical mode (except + internal vertical mode), since the current list in vertical mode is sent + to the page builder. But if we happen to be able to implement it in + vertical mode, we do. */ + delete_last(); + break; + case vmode + un_vbox_cmd: + case hmode + un_hbox_cmd: + case mmode + un_hbox_cmd: + /* The |un_hbox| and |un_vbox| commands unwrap one of the |number_regs| + current boxes. */ + unpackage(); + break; + case hmode + ital_corr_cmd: + /* Italic corrections are converted to kern nodes when the |ital_corr| command + follows a character. In math mode the same effect is achieved by appending + a kern of zero here, since italic corrections are supplied later. */ + append_italic_correction(); + break; + case mmode + ital_corr_cmd: + tail_append(new_kern(0)); + break; + case hmode + discretionary_cmd: + case mmode + discretionary_cmd: + append_discretionary(); + break; + case any_mode(assign_local_box_cmd): + append_local_box(cur_chr); + break; + case hmode + accent_cmd: + /* We need only one more thing to complete the horizontal mode routines, namely + the \.{\\accent} primitive. */ + make_accent(); + break; + case any_mode(car_ret_cmd): + case any_mode(tab_mark_cmd): + align_error(); + break; + case any_mode(no_align_cmd): + no_align_error(); + break; + case any_mode(omit_cmd): + omit_error(); + break; + case vmode + halign_cmd: + case hmode + valign_cmd: + init_align(); + break; + case mmode + halign_cmd: + if (privileged()) { + if (cur_group == math_shift_group) + init_align(); + else + off_save(); + } + break; + case vmode + endv_cmd: + case hmode + endv_cmd: + do_endv(); + break; + case any_mode(end_cs_name_cmd): + cs_error(); + break; + case hmode + math_shift_cmd: + case hmode + math_shift_cs_cmd: + init_math(); + break; + case mmode + eq_no_cmd: + if (privileged()) { + if (cur_group == math_shift_group) + start_eq_no(); + else + off_save(); + } + break; + case mmode + left_brace_cmd: + math_left_brace(); + break; + case mmode + letter_cmd: + case mmode + other_char_cmd: + case mmode + char_given_cmd: + set_math_char(get_math_code(cur_chr)); + break; + case mmode + char_num_cmd: + scan_char_num(); + cur_chr = cur_val; + set_math_char(get_math_code(cur_chr)); + break; + case mmode + math_char_num_cmd: + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(aleph_mathcode); + else if (cur_chr == 2) + mval = scan_mathchar(xetex_mathcode); + else + mval = scan_mathchar(xetexnum_mathcode); + set_math_char(mval); + break; + case mmode + math_given_cmd: + mval = mathchar_from_integer(cur_chr, tex_mathcode); + set_math_char(mval); + break; + case mmode + omath_given_cmd: + mval = mathchar_from_integer(cur_chr, aleph_mathcode); + set_math_char(mval); + break; + case mmode + xmath_given_cmd: + mval = mathchar_from_integer(cur_chr, xetex_mathcode); + set_math_char(mval); + break; + case mmode + delim_num_cmd: + if (cur_chr == 0) + mval = scan_delimiter_as_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_delimiter_as_mathchar(aleph_mathcode); + else + mval = scan_delimiter_as_mathchar(xetex_mathcode); + set_math_char(mval); + break; + case mmode + math_comp_cmd: + math_math_comp(); + break; + case mmode + limit_switch_cmd: + math_limit_switch(); + break; + case mmode + radical_cmd: + math_radical(); + break; + case mmode + accent_cmd: + case mmode + math_accent_cmd: + math_ac(); + break; + case mmode + vcenter_cmd: + scan_spec(vcenter_group); + normal_paragraph(); + push_nest(); + mode = -vmode; + prev_depth = pdf_ignored_dimen; + if (every_vbox != null) + begin_token_list(every_vbox, every_vbox_text); + break; + case mmode + math_style_cmd: + tail_append(new_style((small_number) cur_chr)); + break; + case mmode + non_script_cmd: + tail_append(new_glue(zero_glue)); + subtype(tail) = cond_math_glue; + break; + case mmode + math_choice_cmd: + if (cur_chr == 0) + append_choices(); + else + setup_math_style(); + break; + case mmode + above_cmd: + math_fraction(); + break; + case mmode + sub_mark_cmd: + case mmode + sup_mark_cmd: + case mmode + super_sub_script_cmd: + sub_sup(); + break; + case mmode + left_right_cmd: + math_left_right(); + break; + case mmode + math_shift_cmd: + case mmode + math_shift_cs_cmd: + if (cur_group == math_shift_group) + after_math(); + else + off_save(); + break; + + /* Cases of |main_control| that do not depend on |mode| */ + /* + The long |main_control| procedure has now been fully specified, except for + certain activities that are independent of the current mode. These activities + do not change the current vlist or hlist or mlist; if they change anything, + it is the value of a parameter or the meaning of a control sequence. + */ + case any_mode(toks_register_cmd): + case any_mode(assign_toks_cmd): + case any_mode(assign_int_cmd): + case any_mode(assign_attr_cmd): + case any_mode(assign_dir_cmd): + case any_mode(assign_dimen_cmd): + case any_mode(assign_glue_cmd): + case any_mode(assign_mu_glue_cmd): + case any_mode(assign_font_dimen_cmd): + case any_mode(assign_font_int_cmd): + case any_mode(set_aux_cmd): + case any_mode(set_prev_graf_cmd): + case any_mode(set_page_dimen_cmd): + case any_mode(set_page_int_cmd): + case any_mode(set_box_dimen_cmd): + case any_mode(set_tex_shape_cmd): + case any_mode(set_etex_shape_cmd): + case any_mode(def_char_code_cmd): + case any_mode(def_del_code_cmd): + case any_mode(extdef_math_code_cmd): + case any_mode(extdef_del_code_cmd): + case any_mode(def_family_cmd): + case any_mode(set_math_param_cmd): + case any_mode(set_font_cmd): + case any_mode(def_font_cmd): + case any_mode(letterspace_font_cmd): + case any_mode(pdf_copy_font_cmd): + case any_mode(register_cmd): + case any_mode(advance_cmd): + case any_mode(multiply_cmd): + case any_mode(divide_cmd): + case any_mode(prefix_cmd): + case any_mode(let_cmd): + case any_mode(shorthand_def_cmd): + case any_mode(read_to_cs_cmd): + case any_mode(def_cmd): + case any_mode(set_box_cmd): + case any_mode(hyph_data_cmd): + case any_mode(set_interaction_cmd): + case any_mode(set_ocp_cmd): + case any_mode(def_ocp_cmd): + case any_mode(set_ocp_list_cmd): + case any_mode(def_ocp_list_cmd): + case any_mode(clear_ocp_lists_cmd): + case any_mode(push_ocp_list_cmd): + case any_mode(pop_ocp_list_cmd): + case any_mode(ocp_list_op_cmd): + case any_mode(ocp_trace_level_cmd): + prefixed_command(); + break; + case any_mode(after_assignment_cmd): + get_token(); + after_token = cur_tok; + break; + case any_mode(after_group_cmd): + get_token(); + save_for_after(cur_tok); + break; + case any_mode(in_stream_cmd): + open_or_close_in(); + break; + case any_mode(message_cmd): + issue_message(); + break; + case any_mode(case_shift_cmd): + shift_case(); + break; + case any_mode(xray_cmd): + show_whatever(); + break; + + /* Cases of |main_control| that are for extensions to \TeX */ + case any_mode(extension_cmd): + do_extension(static_pdf); + break; + } /* end of the big |switch| statement */ + + goto BIG_SWITCH; /* restart */ +} + +@ @c +void app_space(void) +{ /* handle spaces when |space_factor<>1000| */ + halfword q; /* glue node */ + if ((space_factor >= 2000) && (xspace_skip != zero_glue)) { + q = new_param_glue(xspace_skip_code); + } else { + if (space_skip != zero_glue) { + main_p = new_spec(space_skip); + } else { + main_p = new_spec(zero_glue); + width(main_p) = space(cur_font); + stretch(main_p) = space_stretch(cur_font); + shrink(main_p) = space_shrink(cur_font); + } + /* Modify the glue specification in |main_p| according to the space factor */ + if (space_factor >= 2000) + width(main_p) = width(main_p) + extra_space(cur_font); + stretch(main_p) = xn_over_d(stretch(main_p), space_factor, 1000); + shrink(main_p) = xn_over_d(shrink(main_p), 1000, space_factor); + + q = new_glue(main_p); + glue_ref_count(main_p) = null; + } + couple_nodes(tail, q); + tail = q; +} + +@ @c +void insert_dollar_sign(void) +{ + back_input(); + cur_tok = math_shift_token + '$'; + print_err("Missing $ inserted"); + help2("I've inserted a begin-math/end-math symbol since I think", + "you left one out. Proceed, with fingers crossed."); + ins_error(); +} + + +@ The `|you_cant|' procedure prints a line saying that the current command +is illegal in the current mode; it identifies these things symbolically. + +@c +void you_cant(void) +{ + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_in_mode(mode); +} + +void report_illegal_case(void) +{ + you_cant(); + help4("Sorry, but I'm not programmed to handle this case;", + "I'll just pretend that you didn''t ask for it.", + "If you're in the wrong mode, you might be able to", + "return to the right one by typing `I}' or `I$' or `I\\par'."); + error(); +} + + +@ Some operations are allowed only in privileged modes, i.e., in cases +that |mode>0|. The |privileged| function is used to detect violations +of this rule; it issues an error message and returns |false| if the +current |mode| is negative. + +@c +boolean privileged(void) +{ + if (mode > 0) { + return true; + } else { + report_illegal_case(); + return false; + } +} + + +@ We don't want to leave |main_control| immediately when a |stop| command +is sensed, because it may be necessary to invoke an \.{\\output} routine +several times before things really grind to a halt. (The output routine +might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.) +Therefore |its_all_over| is |true| only when the current page +and contribution list are empty, and when the last output was not a +``dead cycle.'' + +@c +boolean its_all_over(void) +{ /* do this when \.{\\end} or \.{\\dump} occurs */ + if (privileged()) { + if ((page_head == page_tail) && (head == tail) && (dead_cycles == 0)) { + return true; + } + back_input(); /* we will try to end again after ejecting residual material */ + tail_append(new_null_box()); + width(tail) = hsize; + tail_append(new_glue(fill_glue)); + tail_append(new_penalty(-010000000000)); + lua_node_filter_s(buildpage_filter_callback, "end"); + build_page(); /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */ + } + return false; +} + + +@ The |hskip| and |vskip| command codes are used for control sequences +like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}. +The difference is in the value of |cur_chr|. + +All the work relating to glue creation has been relegated to the +following subroutine. It does not call |build_page|, because it is +used in at least one place where that would be a mistake. + +@c +void append_glue(void) +{ + int s; /* modifier of skip command */ + s = cur_chr; + switch (s) { + case fil_code: + cur_val = fil_glue; + break; + case fill_code: + cur_val = fill_glue; + break; + case ss_code: + cur_val = ss_glue; + break; + case fil_neg_code: + cur_val = fil_neg_glue; + break; + case skip_code: + scan_glue(glue_val_level); + break; + case mskip_code: + scan_glue(mu_val_level); + break; + } /* now |cur_val| points to the glue specification */ + tail_append(new_glue(cur_val)); + if (s >= skip_code) { + decr(glue_ref_count(cur_val)); + if (s > skip_code) + subtype(tail) = mu_glue; + } +} + +@ @c +void append_kern(void) +{ + int s; /* |subtype| of the kern node */ + s = cur_chr; + scan_dimen((s == mu_glue), false, false); + tail_append(new_kern(cur_val)); + subtype(tail) = (quarterword) s; +} + + +@ We have to deal with errors in which braces and such things are not +properly nested. Sometimes the user makes an error of commission by +inserting an extra symbol, but sometimes the user makes an error of omission. +\TeX\ can't always tell one from the other, so it makes a guess and tries +to avoid getting into a loop. + +The |off_save| routine is called when the current group code is wrong. It tries +to insert something into the user's input that will help clean off +the top level. + +@c +void off_save(void) +{ + halfword p, q; /* inserted token */ + if (cur_group == bottom_level) { + /* Drop current token and complain that it was unmatched */ + print_err("Extra "); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + help1("Things are pretty mixed up, but I think the worst is over."); + error(); + + } else { + back_input(); + p = get_avail(); + set_token_link(temp_token_head, p); + print_err("Missing "); + /* Prepare to insert a token that matches |cur_group|, and print what it is */ + /* At this point, |link(temp_token_head)=p|, a pointer to an empty one-word node. */ + switch (cur_group) { + case semi_simple_group: + set_token_info(p, cs_token_flag + frozen_end_group); + tprint_esc("endgroup"); + break; + case math_shift_group: + set_token_info(p, math_shift_token + '$'); + print_char('$'); + break; + case math_left_group: + set_token_info(p, cs_token_flag + frozen_right); + q = get_avail(); + set_token_link(p, q); + p = token_link(p); + set_token_info(p, other_token + '.'); + tprint_esc("right."); + break; + default: + set_token_info(p, right_brace_token + '}'); + print_char('}'); + break; + } + + tprint(" inserted"); + ins_list(token_link(temp_token_head)); + help5("I've inserted something that you may have forgotten.", + "(See the <inserted text> above.)", + "With luck, this will get me unwedged. But if you", + "really didn't forget anything, try typing `2' now; then", + "my insertion and my current dilemma will both disappear."); + error(); + } +} + + +@ The routine for a |right_brace| character branches into many subcases, +since a variety of things may happen, depending on |cur_group|. Some +types of groups are not supposed to be ended by a right brace; error +messages are given in hopes of pinpointing the problem. Most branches +of this routine will be filled in later, when we are ready to understand +them; meanwhile, we must prepare ourselves to deal with such errors. + +@c +void handle_right_brace(void) +{ + halfword p, q; /* for short-term use */ + scaled d; /* holds |split_max_depth| in |insert_group| */ + int f; /* holds |floating_penalty| in |insert_group| */ + p = null; + switch (cur_group) { + case simple_group: + fixup_directions(); + break; + case bottom_level: + print_err("Too many }'s"); + help2("You've closed more groups than you opened.", + "Such booboos are generally harmless, so keep going."); + error(); + break; + case semi_simple_group: + case math_shift_group: + case math_left_group: + extra_right_brace(); + break; + case hbox_group: + /* When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or + \.{\\vtop} construction, the |package| routine comes into action. We might + also have to finish a paragraph that hasn't ended. */ + package(0); + break; + case adjusted_hbox_group: + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + package(0); + break; + case vbox_group: + end_graf(vbox_group); + package(0); + break; + case vtop_group: + end_graf(vtop_group); + package(vtop_code); + break; + case insert_group: + end_graf(insert_group); + q = split_top_skip; + add_glue_ref(q); + d = split_max_depth; + f = floating_penalty; + unsave(); + save_ptr--; + /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */ + p = vpack(vlink(head), 0, additional, -1); + pop_nest(); + if (saved_type(0) == saved_insert) { + tail_append(new_node(ins_node, saved_value(0))); + height(tail) = height(p) + depth(p); + ins_ptr(tail) = list_ptr(p); + split_top_ptr(tail) = q; + depth(tail) = d; + float_cost(tail) = f; + } else if (saved_type(0) == saved_adjust) { + tail_append(new_node(adjust_node, saved_value(0))); + adjust_ptr(tail) = list_ptr(p); + delete_glue_ref(q); + } else { + confusion("insert_group"); + } + list_ptr(p) = null; + flush_node(p); + if (nest_ptr == 0) { + check_filter("insert"); + build_page(); + } + break; + case output_group: + /* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */ + if (dir_level(text_dir_ptr) == cur_level) { + /* DIR: Remove from |text_dir_ptr| */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; + } + resume_after_output(); + break; + case disc_group: + build_discretionary(); + break; + case local_box_group: + build_local_box(); + break; + case align_group: + back_input(); + cur_tok = cs_token_flag + frozen_cr; + print_err("Missing \\cr inserted"); + help1("I'm guessing that you meant to end an alignment here."); + ins_error(); + break; + case no_align_group: + end_graf(no_align_group); + unsave(); + align_peek(); + break; + case vcenter_group: + end_graf(vcenter_group); + finish_vcenter(); + break; + case math_choice_group: + build_choices(); + break; + case math_group: + close_math_group(p); + break; + default: + confusion("rightbrace"); + break; + } +} + +@ @c +void extra_right_brace(void) +{ + print_err("Extra }, or forgotten "); + switch (cur_group) { + case semi_simple_group: + tprint_esc("endgroup"); + break; + case math_shift_group: + print_char('$'); + break; + case math_left_group: + tprint_esc("right"); + break; + } + help5("I've deleted a group-closing symbol because it seems to be", + "spurious, as in `$x}$'. But perhaps the } is legitimate and", + "you forgot something else, as in `\\hbox{$x}'. In such cases", + "the way to recover is to insert both the forgotten and the", + "deleted material, e.g., by typing `I$}'."); + error(); + incr(align_state); +} + + +@ Here is where we clear the parameters that are supposed to revert to their +default values after every paragraph and when internal vertical mode is entered. + +@c +void normal_paragraph(void) +{ + if (looseness != 0) + eq_word_define(int_base + looseness_code, 0); + if (hang_indent != 0) + eq_word_define(dimen_base + hang_indent_code, 0); + if (hang_after != 1) + eq_word_define(int_base + hang_after_code, 1); + if (par_shape_ptr != null) + eq_define(par_shape_loc, shape_ref_cmd, null); + if (inter_line_penalties_ptr != null) + eq_define(inter_line_penalties_loc, shape_ref_cmd, null); +} + + +@ The global variable |cur_box| will point to a newly-made box. If the box +is void, we will have |cur_box=null|. Otherwise we will have +|type(cur_box)=hlist_node| or |vlist_node| or |rule_node|; the |rule_node| +case can occur only with leaders. + +@c +halfword cur_box; /* box to be placed into its context */ + + +@ The |box_end| procedure does the right thing with |cur_box|, if +|box_context| represents the context as explained above. + +@c +void box_end(int box_context) +{ + if (box_context < box_flag) { + /* Append box |cur_box| to the current list, shifted by |box_context| */ + /* + The global variable |adjust_tail| will be non-null if and only if the + current box might include adjustments that should be appended to the + current vertical list. + */ + if (cur_box != null) { + shift_amount(cur_box) = box_context; + if (abs(mode) == vmode) { + if (pre_adjust_tail != null) { + if (pre_adjust_head != pre_adjust_tail) + append_list(pre_adjust_head, pre_adjust_tail); + pre_adjust_tail = null; + } + append_to_vlist(cur_box); + if (adjust_tail != null) { + if (adjust_head != adjust_tail) + append_list(adjust_head, adjust_tail); + adjust_tail = null; + } + if (mode > 0) { + check_filter("box"); + build_page(); + } + } else { + if (abs(mode) == hmode) + space_factor = 1000; + else + cur_box = new_sub_box(cur_box); + vlink(tail) = cur_box; + tail = cur_box; + } + } + + } else if (box_context < ship_out_flag) { + /* Store |cur_box| in a box register */ + if (box_context < global_box_flag) + eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box); + else + geq_define(box_base + box_context - global_box_flag, box_ref_cmd, + cur_box); + + } else if (cur_box != null) { + if (box_context > ship_out_flag) { + /* Append a new leader node that uses |cur_box| */ + /* Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (((cur_cmd == hskip_cmd) && (abs(mode) != vmode)) || + ((cur_cmd == vskip_cmd) && (abs(mode) == vmode))) { + append_glue(); + subtype(tail) = + (quarterword) (box_context - (leader_flag - a_leaders)); + leader_ptr(tail) = cur_box; + } else { + print_err("Leaders not followed by proper glue"); + help3 + ("You should say `\\leaders <box or rule><hskip or vskip>'.", + "I found the <box or rule>, but there's no suitable", + "<hskip or vskip>, so I'm ignoring these leaders."); + back_error(); + flush_node_list(cur_box); + } + + } else + ship_out(static_pdf, cur_box, true); + } +} + +@ the next input should specify a box or perhaps a rule + +@c +void scan_box(int box_context) +{ + /* Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd == make_box_cmd) { + begin_box(box_context); + } else if ((box_context >= leader_flag) + && ((cur_cmd == hrule_cmd) || (cur_cmd == vrule_cmd))) { + cur_box = scan_rule_spec(); + box_end(box_context); + } else { + print_err("A <box> was supposed to be here"); + help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or", + "something like that. So you might find something missing in", + "your output. But keep trying; you can fix this later."); + back_error(); + } +} + +@ @c +void new_graf(boolean indented) +{ + halfword p, q, dir_graf_tmp; + halfword dir_rover; + prev_graf = 0; + if ((mode == vmode) || (head != tail)) { + tail_append(new_param_glue(par_skip_code)); + } + push_nest(); + mode = hmode; + space_factor = 1000; + /* LOCAL: Add local paragraph node */ + tail_append(make_local_par_node()); + + if (indented) { + p = new_null_box(); + box_dir(p) = par_direction; + width(p) = par_indent; + q = tail; + tail_append(p); + } else { + q = tail; + } + dir_rover = text_dir_ptr; + while (dir_rover != null) { + if ((vlink(dir_rover) != null) || (dir_dir(dir_rover) != par_direction)) { + dir_graf_tmp = new_dir(dir_dir(dir_rover)); + vlink(dir_graf_tmp) = vlink(q); + vlink(q) = dir_graf_tmp; + } + dir_rover = vlink(dir_rover); + } + q = head; + while (vlink(q) != null) + q = vlink(q); + tail = q; + if (every_par != null) + begin_token_list(every_par, every_par_text); + if (nest_ptr == 1) { + check_filter("new_graf"); + build_page(); /* put |par_skip| glue on current page */ + } +} + +@ @c +void indent_in_hmode(void) +{ + halfword p; + if (cur_chr > 0) { /* \.{\\indent} */ + p = new_null_box(); + width(p) = par_indent; + if (abs(mode) == hmode) + space_factor = 1000; + else + p = new_sub_box(p); + tail_append(p); + } +} + +@ @c +void head_for_vmode(void) +{ + if (mode < 0) { + if (cur_cmd != hrule_cmd) { + off_save(); + } else { + print_err("You can't use `\\hrule' here except with leaders"); + help2("To put a horizontal rule in an hbox or an alignment,", + "you should use \\leaders or \\hrulefill (see The TeXbook)."); + error(); + } + } else { + back_input(); + cur_tok = par_token; + back_input(); + token_type = inserted; + } +} + + +@ TODO (BUG?): |dir_save| would have been set by |line_break| by means +of |post_line_break|, but this is not done right now, as it introduces +pretty heavy memory leaks. This means the current code is probably +wrong in some way that relates to in-paragraph displays. + +@c +void end_graf(int line_break_context) +{ + if (mode == hmode) { + if ((head == tail) || (vlink(head) == tail)) { + if (vlink(head) == tail) + flush_node(vlink(head)); + pop_nest(); /* null paragraphs are ignored, all contain a |local_paragraph| node */ + } else { + line_break(false, line_break_context); + } + if (dir_save != null) { + flush_node_list(dir_save); + dir_save = null; + } + normal_paragraph(); + error_count = 0; + } +} + + +@ @c +void begin_insert_or_adjust(void) +{ + if (cur_cmd != vadjust_cmd) { + scan_register_num(); + if (cur_val == output_box) { + print_err("You can't \\insert"); + print_int(output_box); + help1("I'm changing to \\insert0; box \\outputbox is special."); + error(); + cur_val = 0; + } + set_saved_record(0, saved_insert, 0, cur_val); + } else if (scan_keyword("pre")) { + set_saved_record(0, saved_adjust, 0, 1); + } else { + set_saved_record(0, saved_adjust, 0, 0); + } + save_ptr++; + new_save_level(insert_group); + scan_left_brace(); + normal_paragraph(); + push_nest(); + mode = -vmode; + prev_depth = pdf_ignored_dimen; +} + + +@ I (TH)'ve renamed the |make_mark| procedure to this, because if the +current chr code is 1, then the actual command was \.{\\clearmarks}, +which does not generate a mark node but instead destroys the current +mark tokenlists. + +@c +void handle_mark(void) +{ + halfword p; /* new node */ + halfword c; /* the mark class */ + if (cur_chr == clear_marks_code) { + scan_mark_num(); + c = cur_val; + delete_top_mark(c); + delete_bot_mark(c); + delete_first_mark(c); + delete_split_first_mark(c); + delete_split_bot_mark(c); + } else { + if (cur_chr == 0) { + c = 0; + } else { + scan_mark_num(); + c = cur_val; + if (c > biggest_used_mark) + biggest_used_mark = c; + } + p = scan_toks(false, true); + p = new_node(mark_node, 0); /* the |subtype| is not used */ + mark_class(p) = c; + mark_ptr(p) = def_ref; + couple_nodes(tail, p); + tail = p; + } +} + + +@ @c +void append_penalty(void) +{ + scan_int(); + tail_append(new_penalty(cur_val)); + if (mode == vmode) { + check_filter("penalty"); + build_page(); + } +} + + +@ When |delete_last| is called, |cur_chr| is the |type| of node that +will be deleted, if present. + +@c +void delete_last(void) +{ + halfword p, q; /* run through the current list */ + if ((mode == vmode) && (tail == head)) { + /* Apologize for inability to do the operation now, + unless \.{\\unskip} follows non-glue */ + if ((cur_chr != glue_node) || (last_glue != max_halfword)) { + you_cant(); + if (cur_chr == kern_node) { + help2 + ("Sorry...I usually can't take things from the current page.", + "Try `I\\kern-\\lastkern' instead."); + } else if (cur_chr != glue_node) { + help2 + ("Sorry...I usually can't take things from the current page.", + "Perhaps you can make the output routine do it."); + } else { + help2 + ("Sorry...I usually can't take things from the current page.", + "Try `I\\vskip-\\lastskip' instead."); + } + error(); + } + + } else { + /* todo: clean this up */ + if (!is_char_node(tail)) { + if (type(tail) == cur_chr) { + q = head; + do { + p = q; + if (!is_char_node(q)) { + if (type(q) == disc_node) { + if (p == tail) + return; + } + } + q = vlink(p); + } while (q != tail); + vlink(p) = null; + flush_node_list(tail); + tail = p; + } + } + } +} + +@ @c +void unpackage(void) +{ + halfword p; /* the box */ + halfword r; /* to remove marginal kern nodes */ + int c; /* should we copy? */ + halfword s; /* for varmem assignment */ + if (cur_chr > copy_code) { + /* Handle saved items and |goto done| */ + vlink(tail) = disc_ptr[cur_chr]; + disc_ptr[cur_chr] = null; + goto DONE; + } + c = cur_chr; + scan_register_num(); + p = box(cur_val); + if (p == null) + return; + if ((abs(mode) == mmode) + || ((abs(mode) == vmode) && (type(p) != vlist_node)) + || ((abs(mode) == hmode) && (type(p) != hlist_node))) { + print_err("Incompatible list can't be unboxed"); + help3("Sorry, Pandora. (You sneaky devil.)", + "I refuse to unbox an \\hbox in vertical mode or vice versa.", + "And I can't open any boxes in math mode."); + error(); + return; + } + if (c == copy_code) { + s = copy_node_list(list_ptr(p)); + vlink(tail) = s; + } else { + vlink(tail) = list_ptr(p); + box(cur_val) = null; + list_ptr(p) = null; + flush_node(p); + } + DONE: + while (vlink(tail) != null) { + r = vlink(tail); + if (!is_char_node(r) && (type(r) == margin_kern_node)) { + vlink(tail) = vlink(r); + flush_node(r); + } + tail = vlink(tail); + } +} + +@ @c +void append_italic_correction(void) +{ + halfword p; /* |char_node| at the tail of the current list */ + internal_font_number f; /* the font in the |char_node| */ + if (tail != head) { + if (is_char_node(tail)) + p = tail; + else + return; + f = font(p); + tail_append(new_kern(char_italic(f, character(p)))); + subtype(tail) = explicit; + } +} + + +@ @c +void append_local_box(int kind) +{ + incr(save_ptr); + set_saved_record(-1, saved_boxtype, 0, kind); + new_save_level(local_box_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor = 1000; +} + + +@ Discretionary nodes are easy in the common case `\.{\\-}', but in the +general case we must process three braces full of items. + +The space factor does not change when we append a discretionary node, +but it starts out as 1000 in the subsidiary lists. + +@c +void append_discretionary(void) +{ + int c; + tail_append(new_disc()); + subtype(tail) = (quarterword) cur_chr; + if (cur_chr == explicit_disc) { + c = get_pre_hyphen_char(cur_lang); + if (c != 0) { + vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(pre_break(tail))) = pre_break(tail); + tlink(pre_break(tail)) = vlink(pre_break(tail)); + } + c = get_post_hyphen_char(cur_lang); + if (c != 0) { + vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(post_break(tail))) = post_break(tail); + tlink(post_break(tail)) = vlink(post_break(tail)); + } + } else { + incr(save_ptr); + set_saved_record(-1, saved_disc, 0, 0); + new_save_level(disc_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor = 1000; + } +} + +@ The test for |p != null| ensures that empty \.{\\localleftbox} and + \.{\\localrightbox} commands are not applied. + +@c +void build_local_box(void) +{ + halfword p; + int kind; + unsave(); + assert(saved_type(-1) == saved_boxtype); + kind = saved_value(-1); + decr(save_ptr); + p = vlink(head); + pop_nest(); + if (p != null) + p = hpack(p, 0, additional, -1); + if (kind == 0) + eq_define(local_left_box_base, box_ref_cmd, p); + else + eq_define(local_right_box_base, box_ref_cmd, p); + if (abs(mode) == hmode) { + /* LOCAL: Add local paragraph node */ + tail_append(make_local_par_node()); + } + eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits + 1); +} + + +@ The three discretionary lists are constructed somewhat as if they were +hboxes. A~subroutine called |build_discretionary| handles the transitions. +(This is sort of fun.) + +@c +void build_discretionary(void) +{ + halfword p, q; /* for link manipulation */ + int n; /* length of discretionary list */ + unsave(); + /* Prune the current list, if necessary, until it contains only + |char_node|, |kern_node|, |hlist_node|, |vlist_node| and + |rule_node| items; set |n| to the length of the list, + and set |q| to the lists tail */ + /* During this loop, |p=vlink(q)| and there are |n| items preceding |p|. */ + q = head; + p = vlink(q); + n = 0; + while (p != null) { + if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) { + print_err("Improper discretionary list"); + help1("Discretionary lists must contain only boxes and kerns."); + error(); + begin_diagnostic(); + tprint_nl("The following discretionary sublist has been deleted:"); + show_box(p); + end_diagnostic(true); + flush_node_list(p); + vlink(q) = null; + break; + } + alink(p) = q; + q = p; + p = vlink(q); + incr(n); + } + + p = vlink(head); + pop_nest(); + assert(saved_type(-1) == saved_disc); + switch (saved_value(-1)) { + case 0: + if (n > 0) { + vlink(pre_break(tail)) = p; + alink(p) = pre_break(tail); + tlink(pre_break(tail)) = q; + } + break; + case 1: + if (n > 0) { + vlink(post_break(tail)) = p; + alink(p) = post_break(tail); + tlink(post_break(tail)) = q; + } + break; + case 2: + /* Attach list |p| to the current list, and record its length; + then finish up and |return| */ + if ((n > 0) && (abs(mode) == mmode)) { + print_err("Illegal math \\discretionary"); + help2("Sorry: The third part of a discretionary break must be", + "empty, in math formulas. I had to delete your third part."); + flush_node_list(p); + error(); + } else { + if (n > 0) { + vlink(no_break(tail)) = p; + alink(p) = no_break(tail); + tlink(no_break(tail)) = q; + } + } + decr(save_ptr); + return; + break; + } /* there are no other cases */ + set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1)); + new_save_level(disc_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor = 1000; +} + + +@ The positioning of accents is straightforward but tedious. Given an accent +of width |a|, designed for characters of height |x| and slant |s|; +and given a character of width |w|, height |h|, and slant |t|: We will shift +the accent down by |x-h|, and we will insert kern nodes that have the effect of +centering the accent over the character and shifting the accent to the +right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is +absent from the font, we will simply use the other, without shifting. + +@c +void make_accent(void) +{ + double s, t; /* amount of slant */ + halfword p, q, r; /* character, box, and kern nodes */ + internal_font_number f; /* relevant font */ + scaled a, h, x, w, delta; /* heights and widths, as explained above */ + scan_char_num(); + f = equiv(cur_font_loc); + p = new_glyph(f, cur_val); + if (p != null) { + x = x_height(f); + s = float_cast(slant(f)) / float_constant(65536); /* real division */ + a = glyph_width(p); + do_assignments(); + /* Create a character node |q| for the next character, + but set |q:=null| if problems arise */ + q = null; + f = equiv(cur_font_loc); + if ((cur_cmd == letter_cmd) || + (cur_cmd == other_char_cmd) || (cur_cmd == char_given_cmd)) { + q = new_glyph(f, cur_chr); + } else if (cur_cmd == char_num_cmd) { + scan_char_num(); + q = new_glyph(f, cur_val); + } else { + back_input(); + } + + if (q != null) { + /* Append the accent with appropriate kerns, then set |p:=q| */ + /* The kern nodes appended here must be distinguished from other kerns, lest + they be wiped away by the hyphenation algorithm or by a previous line break. + + The two kerns are computed with (machine-dependent) |real| arithmetic, but + their sum is machine-independent; the net effect is machine-independent, + because the user cannot remove these nodes nor access them via \.{\\lastkern}. + */ + t = float_cast(slant(f)) / float_constant(65536); /* real division */ + w = glyph_width(q); + h = glyph_height(q); + if (h != x) { /* the accent must be shifted up or down */ + p = hpack(p, 0, additional, -1); + shift_amount(p) = x - h; + } + delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); /* real multiplication */ + r = new_kern(delta); + subtype(r) = acc_kern; + couple_nodes(tail, r); + couple_nodes(r, p); + tail = new_kern(-a - delta); + subtype(tail) = acc_kern; + couple_nodes(p, tail); + p = q; + + } + couple_nodes(tail, p); + tail = p; + space_factor = 1000; + } +} + + +@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner +into |main_control|, it might be that the user has foolishly inserted +one of them into something that has nothing to do with alignment. But it is +far more likely that a left brace or right brace has been omitted, since +|get_next| takes actions appropriate to alignment only when `\.{\\cr}' +or `\.{\\span}' or tab marks occur with |align_state=0|. The following +program attempts to make an appropriate recovery. + +@c +void align_error(void) +{ + if (abs(align_state) > 2) { + /* Express consternation over the fact that no alignment is in progress */ + print_err("Misplaced "); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + if (cur_tok == tab_token + '&') { + help6("I can't figure out why you would want to use a tab mark", + "here. If you just want an ampersand, the remedy is", + "simple: Just type `I\\&' now. But if some right brace", + "up above has ended a previous alignment prematurely,", + "you're probably due for more error messages, and you", + "might try typing `S' now just to see what is salvageable."); + } else { + help5("I can't figure out why you would want to use a tab mark", + "or \\cr or \\span just now. If something like a right brace", + "up above has ended a previous alignment prematurely,", + "you're probably due for more error messages, and you", + "might try typing `S' now just to see what is salvageable."); + } + error(); + + } else { + back_input(); + if (align_state < 0) { + print_err("Missing { inserted"); + incr(align_state); + cur_tok = left_brace_token + '{'; + } else { + print_err("Missing } inserted"); + decr(align_state); + cur_tok = right_brace_token + '}'; + } + help3("I've put in what seems to be necessary to fix", + "the current column of the current alignment.", + "Try to go on, since this might almost work."); + ins_error(); + } +} + + +@ The help messages here contain a little white lie, since \.{\\noalign} +and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'. + +@c +void no_align_error(void) +{ + print_err("Misplaced \\noalign"); + help2("I expect to see \\noalign only after the \\cr of", + "an alignment. Proceed, and I'll ignore this case."); + error(); +} + +void omit_error(void) +{ + print_err("Misplaced \\omit"); + help2("I expect to see \\omit only after tab marks or the \\cr of", + "an alignment. Proceed, and I'll ignore this case."); + error(); +} + + +@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}. +Let's take a look at what happens when they are used correctly. + +An |align_group| code is supposed to remain on the |save_stack| +during an entire alignment, until |fin_align| removes it. + +A devious user might force an |endv| command to occur just about anywhere; +we must defeat such hacks. + +@c +void do_endv(void) +{ + base_ptr = input_ptr; + input_stack[base_ptr] = cur_input; + while ((input_stack[base_ptr].index_field != v_template) && + (input_stack[base_ptr].loc_field == null) && + (input_stack[base_ptr].state_field == token_list)) + decr(base_ptr); + if ((input_stack[base_ptr].index_field != v_template) || + (input_stack[base_ptr].loc_field != null) || + (input_stack[base_ptr].state_field != token_list)) + fatal_error("(interwoven alignment preambles are not allowed)"); + /*.interwoven alignment preambles... */ + if (cur_group == align_group) { + end_graf(align_group); + if (fin_col()) + fin_row(); + } else { + off_save(); + } +} + +@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|. + +@c +void cs_error(void) +{ + print_err("Extra \\endcsname"); + help1("I'm ignoring this, since I wasn't doing a \\csname."); + error(); +} + + +@ + Assignments to values in |eqtb| can be global or local. Furthermore, a + control sequence can be defined to be `\.{\\long}', `\.{\\protected}', + or `\.{\\outer}', and it might or might not be expanded. The prefixes + `\.{\\global}', `\.{\\long}', `\.{\\protected}', + and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric + codes, making it possible to accumulate the union of all specified prefixes + by adding the corresponding codes. (PASCAL's |set| operations could also + have been used.) + + Every prefix, and every command code that might or might not be prefixed, + calls the action procedure |prefixed_command|. This routine accumulates + a sequence of prefixes until coming to a non-prefix, then it carries out + the command. + + + +@ If the user says, e.g., `\.{\\global\\global}', the redundancy is +silently accepted. + + +@ The different types of code values have different legal ranges; the +following program is careful to check each case properly. + +@c +#define check_def_code(A) do { \ + if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \ + print_err("Invalid code ("); \ + print_int(cur_val); \ + if (p<(A)) \ + tprint("), should be in the range 0.."); \ + else \ + tprint("), should be at most "); \ + print_int(n); \ + help1("I'm going to use 0 instead of that illegal code value."); \ + error(); \ + cur_val=0; \ + } \ + } while (0) + + +@ @c +void prefixed_command(void) +{ + int a; /* accumulated prefix codes so far */ + internal_font_number f; /* identifies a font */ + halfword j; /* index into a \.{\\parshape} specification */ + halfword p, q; /* for temporary short-term use */ + int n; /* ditto */ + boolean e; /* should a definition be expanded? or was \.{\\let} not done? */ + mathcodeval mval; /* for handling of \.{\\mathchardef}s */ + a = 0; + while (cur_cmd == prefix_cmd) { + if (!odd(a / cur_chr)) + a = a + cur_chr; + /* Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd <= max_non_prefixed_command) { + /* Discard erroneous prefixes and |return| */ + print_err("You can't use a prefix with `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help2 + ("I'll pretend you didn't say \\long or \\outer or \\global or", + "\\protected."); + back_error(); + return; + } + if (int_par(tracing_commands_code) > 2) + show_cur_cmd_chr(); + } + /* Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant */ + if (a >= 8) { + j = protected_token; + a = a - 8; + } else { + j = 0; + } + if ((cur_cmd != def_cmd) && ((a % 4 != 0) || (j != 0))) { + print_err("You can't use `\\long' or `\\outer' with `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help1("I'll pretend you didn't say \\long or \\outer here."); + error(); + } + + /* Adjust for the setting of \.{\\globaldefs} */ + if (global_defs != 0) { + if (global_defs < 0) { + if (is_global(a)) + a = a - 4; + } else { + if (!is_global(a)) + a = a + 4; + } + } + switch (cur_cmd) { + case set_font_cmd: + /* Here's an example of the way many of the following routines operate. + (Unfortunately, they aren't all as simple as this.) */ + define(cur_font_loc, data_cmd, cur_chr); + break; + case def_cmd: + /* When a |def| command has been scanned, + |cur_chr| is odd if the definition is supposed to be global, and + |cur_chr>=2| if the definition is supposed to be expanded. */ + + if (odd(cur_chr) && !is_global(a) && (global_defs >= 0)) + a = a + 4; + e = (cur_chr >= 2); + get_r_token(); + p = cur_cs; + q = scan_toks(true, e); + if (j != 0) { + q = get_avail(); + set_token_info(q, j); + set_token_link(q, token_link(def_ref)); + set_token_link(def_ref, q); + } + define(p, call_cmd + (a % 4), def_ref); + break; + case let_cmd: + n = cur_chr; + get_r_token(); + p = cur_cs; + if (n == normal) { + do { + get_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '=') { + get_token(); + if (cur_cmd == spacer_cmd) + get_token(); + } + } else { + get_token(); + q = cur_tok; + get_token(); + back_input(); + cur_tok = q; + back_input(); /* look ahead, then back up */ + } /* note that |back_input| doesn't affect |cur_cmd|, |cur_chr| */ + if (cur_cmd >= call_cmd) + add_token_ref(cur_chr); + define(p, cur_cmd, cur_chr); + break; + case shorthand_def_cmd: + /* We temporarily define |p| to be |relax|, so that an occurrence of |p| + while scanning the definition will simply stop the scanning instead of + producing an ``undefined control sequence'' error or expanding the + previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'. + */ + n = cur_chr; + get_r_token(); + p = cur_cs; + define(p, relax_cmd, too_big_char); + scan_optional_equals(); + switch (n) { + case char_def_code: + scan_char_num(); + define(p, char_given_cmd, cur_val); + break; + case math_char_def_code: + mval = scan_mathchar(tex_mathcode); + cur_val = + (mval.class_value * 16 + mval.family_value) * 256 + + mval.character_value; + define(p, math_given_cmd, cur_val); + break; + case omath_char_def_code: + mval = scan_mathchar(aleph_mathcode); + cur_val = + (mval.class_value * 256 + mval.family_value) * 65536 + + mval.character_value; + define(p, omath_given_cmd, cur_val); + break; + case xmath_char_def_code: + mval = scan_mathchar(xetex_mathcode); + cur_val = + (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + + mval.character_value; + define(p, xmath_given_cmd, cur_val); + break; + default: + scan_register_num(); + switch (n) { + case count_def_code: + define(p, assign_int_cmd, count_base + cur_val); + break; + case attribute_def_code: + define(p, assign_attr_cmd, attribute_base + cur_val); + break; + case dimen_def_code: + define(p, assign_dimen_cmd, scaled_base + cur_val); + break; + case skip_def_code: + define(p, assign_glue_cmd, skip_base + cur_val); + break; + case mu_skip_def_code: + define(p, assign_mu_glue_cmd, mu_skip_base + cur_val); + break; + case toks_def_code: + define(p, assign_toks_cmd, toks_base + cur_val); + break; + default: + confusion("shorthand_def"); + break; + } + break; + } + break; + case read_to_cs_cmd: + j = cur_chr; + scan_int(); + n = cur_val; + if (!scan_keyword("to")) { + print_err("Missing `to' inserted"); + help2("You should have said `\\read<number> to \\cs'.", + "I'm going to look for the \\cs now."); + error(); + } + get_r_token(); + p = cur_cs; + read_toks(n, p, j); + define(p, call_cmd, cur_val); + break; + case toks_register_cmd: + case assign_toks_cmd: + /* The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive + their values in the following way. (For safety's sake, we place an + enclosing pair of braces around an \.{\\output} list.) */ + q = cur_cs; + if (cur_cmd == toks_register_cmd) { + scan_register_num(); + p = toks_base + cur_val; + } else { + p = cur_chr; /* |p=every_par_loc| or |output_routine_loc| or \dots */ + } + scan_optional_equals(); + /* Get the next non-blank non-relax non-call token */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd != left_brace_cmd) { + /* If the right-hand side is a token parameter + or token register, finish the assignment and |goto done| */ + if (cur_cmd == toks_register_cmd) { + scan_register_num(); + cur_cmd = assign_toks_cmd; + cur_chr = toks_base + cur_val; + } + if (cur_cmd == assign_toks_cmd) { + q = equiv(cur_chr); + if (q == null) { + define(p, undefined_cs_cmd, null); + } else { + add_token_ref(q); + define(p, call_cmd, q); + } + goto DONE; + } + } + back_input(); + cur_cs = q; + q = scan_toks(false, false); + if (token_link(def_ref) == null) { /* empty list: revert to the default */ + define(p, undefined_cs_cmd, null); + free_avail(def_ref); + } else { + if (p == output_routine_loc) { /* enclose in curlies */ + p = get_avail(); + set_token_link(q, p); + p = output_routine_loc; + q = token_link(q); + set_token_info(q, right_brace_token + '}'); + q = get_avail(); + set_token_info(q, left_brace_token + '{'); + set_token_link(q, token_link(def_ref)); + set_token_link(def_ref, q); + } + define(p, call_cmd, def_ref); + } + break; + case assign_int_cmd: + /* Similar routines are used to assign values to the numeric parameters. */ + p = cur_chr; + scan_optional_equals(); + scan_int(); + assign_internal_value(a, p, cur_val); + break; + case assign_attr_cmd: + p = cur_chr; + scan_optional_equals(); + scan_int(); + if ((p - attribute_base) > max_used_attr) + max_used_attr = (p - attribute_base); + attr_list_cache = cache_disabled; + word_define(p, cur_val); + break; + case assign_dir_cmd: + /* DIR: Assign direction codes */ + scan_direction(); + switch (cur_chr) { + case int_base + page_direction_code: + eq_word_define(int_base + page_direction_code, cur_val); + break; + case int_base + body_direction_code: + eq_word_define(int_base + body_direction_code, cur_val); + break; + case int_base + par_direction_code: + eq_word_define(int_base + par_direction_code, cur_val); + break; + case int_base + math_direction_code: + eq_word_define(int_base + math_direction_code, cur_val); + break; + case int_base + text_direction_code: + if ((no_local_dirs > 0) && (abs(mode) == hmode)) { + /* DIR: Add local dir node */ + tail_append(new_dir(text_direction)); + } + update_text_dir_ptr(cur_val); + if (abs(mode) == hmode) { + /* DIR: Add local dir node */ + tail_append(new_dir(cur_val)); + dir_level(tail) = cur_level; + } + eq_word_define(int_base + text_direction_code, cur_val); + eq_word_define(int_base + no_local_dirs_code, no_local_dirs + 1); + break; + } + break; + case assign_dimen_cmd: + p = cur_chr; + scan_optional_equals(); + scan_normal_dimen(); + assign_internal_value(a, p, cur_val); + break; + case assign_glue_cmd: + case assign_mu_glue_cmd: + p = cur_chr; + n = cur_cmd; + scan_optional_equals(); + if (n == assign_mu_glue_cmd) + scan_glue(mu_val_level); + else + scan_glue(glue_val_level); + trap_zero_glue(); + define(p, glue_ref_cmd, cur_val); + break; + case def_char_code_cmd: + case def_del_code_cmd: + /* Let |n| be the largest legal code value, based on |cur_chr| */ + if (cur_chr == cat_code_base) + n = max_char_code; + else if (cur_chr == sf_code_base) + n = 077777; + else + n = biggest_char; + + p = cur_chr; + if (cur_chr == math_code_base) { + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + scan_extdef_math_code(cur_val1, tex_mathcode); + } else if (cur_chr == lc_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(lc_code_base); + define_lc_code(p, cur_val); + } else if (cur_chr == uc_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(uc_code_base); + define_uc_code(p, cur_val); + } else if (cur_chr == sf_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(sf_code_base); + define_sf_code(p, cur_val); + } else if (cur_chr == cat_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(cat_code_base); + define_cat_code(p, cur_val); + } else if (cur_chr == del_code_base) { + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + scan_extdef_del_code(cur_val1, tex_mathcode); + } + break; + case extdef_math_code_cmd: + case extdef_del_code_cmd: + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + if (cur_chr == math_code_base) + scan_extdef_math_code(cur_val1, aleph_mathcode); + else if (cur_chr == math_code_base + 1) + scan_extdef_math_code(cur_val1, xetex_mathcode); + else if (cur_chr == math_code_base + 2) + scan_extdef_math_code(cur_val1, xetexnum_mathcode); + else if (cur_chr == del_code_base) + scan_extdef_del_code(cur_val1, aleph_mathcode); + else if (cur_chr == del_code_base + 1) + scan_extdef_del_code(cur_val1, xetex_mathcode); + else if (cur_chr == del_code_base + 2) + scan_extdef_del_code(cur_val1, xetexnum_mathcode); + break; + case def_family_cmd: + p = cur_chr; + scan_math_family_int(); + cur_val1 = cur_val; + scan_optional_equals(); + scan_font_ident(); + define_fam_fnt(cur_val1, p, cur_val); + break; + case set_math_param_cmd: + p = cur_chr; + get_token(); + if (cur_cmd != math_style_cmd) { + print_err("Missing math style, treated as \\displaystyle"); + help1 + ("A style should have been here; I inserted `\\displaystyle'."); + cur_val1 = display_style; + back_error(); + } else { + cur_val1 = cur_chr; + } + scan_optional_equals(); + if (p < math_param_first_mu_glue) { + if (p == math_param_radical_degree_raise) + scan_int(); + else + scan_dimen(false, false, false); + } else { + scan_glue(mu_val_level); + trap_zero_glue(); + if (cur_val == glue_par(thin_mu_skip_code)) + cur_val = thin_mu_skip_code; + else if (cur_val == glue_par(med_mu_skip_code)) + cur_val = med_mu_skip_code; + else if (cur_val == glue_par(thick_mu_skip_code)) + cur_val = thick_mu_skip_code; + } + define_math_param(p, cur_val1, cur_val); + break; + case register_cmd: + case advance_cmd: + case multiply_cmd: + case divide_cmd: + do_register_command(a); + break; + case set_box_cmd: + /* The processing of boxes is somewhat different, because we may need + to scan and create an entire box before we actually change the value + of the old one. */ + scan_register_num(); + if (is_global(a)) + n = global_box_flag + cur_val; + else + n = box_flag + cur_val; + scan_optional_equals(); + if (set_box_allowed) { + scan_box(n); + } else { + print_err("Improper \\setbox"); + help2("Sorry, \\setbox is not allowed after \\halign in a display,", + "or between \\accent and an accented character."); + error(); + } + break; + case set_aux_cmd: + /* The |space_factor| or |prev_depth| settings are changed when a |set_aux| + command is sensed. Similarly, |prev_graf| is changed in the presence of + |set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of + |set_page_int|. These definitions are always global. */ + alter_aux(); + break; + case set_prev_graf_cmd: + alter_prev_graf(); + break; + case set_page_dimen_cmd: + alter_page_so_far(); + break; + case set_page_int_cmd: + alter_integer(); + break; + case set_box_dimen_cmd: + /* When some dimension of a box register is changed, the change isn't exactly + global; but \TeX\ does not look at the \.{\\global} switch. */ + alter_box_dimen(); + break; + case set_tex_shape_cmd: + q = cur_chr; + scan_optional_equals(); + scan_int(); + n = cur_val; + if (n <= 0) { + p = null; + } else { + p = new_node(shape_node, 2 * (n + 1) + 1); + vinfo(p + 1) = n; + for (j = 1; j <= n; j++) { + scan_normal_dimen(); + varmem[p + 2 * j].cint = cur_val; /* indentation */ + scan_normal_dimen(); + varmem[p + 2 * j + 1].cint = cur_val; /* width */ + } + } + define(q, shape_ref_cmd, p); + break; + case set_etex_shape_cmd: + q = cur_chr; + scan_optional_equals(); + scan_int(); + n = cur_val; + if (n <= 0) { + p = null; + } else { + n = (cur_val / 2) + 1; + p = new_node(shape_node, 2 * n + 1 + 1); + vinfo(p + 1) = n; + n = cur_val; + varmem[p + 2].cint = n; /* number of penalties */ + for (j = p + 3; j <= p + n + 2; j++) { + scan_int(); + varmem[j].cint = cur_val; /* penalty values */ + } + if (!odd(n)) + varmem[p + n + 3].cint = 0; /* unused */ + } + define(q, shape_ref_cmd, p); + break; + case hyph_data_cmd: + /* All of \TeX's parameters are kept in |eqtb| except the font information, + the interaction mode, and the hyphenation tables; these are strictly global. + */ + switch (cur_chr) { + case 0: + new_hyph_exceptions(); + break; + case 1: + new_patterns(); + break; + case 2: + new_pre_hyphen_char(); + break; + case 3: + new_post_hyphen_char(); + break; + case 4: + new_pre_exhyphen_char(); + break; + case 5: + new_post_exhyphen_char(); + break; + } + break; + case assign_font_dimen_cmd: + set_font_dimen(); + break; + case assign_font_int_cmd: + n = cur_chr; + scan_font_ident(); + f = cur_val; + if (n == no_lig_code) { + set_no_ligatures(f); + } else if (n < lp_code_base) { + scan_optional_equals(); + scan_int(); + if (n == 0) + set_hyphen_char(f, cur_val); + else + set_skew_char(f, cur_val); + } else { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + switch (n) { + case lp_code_base: + set_lp_code(f, p, cur_val); + break; + case rp_code_base: + set_rp_code(f, p, cur_val); + break; + case ef_code_base: + set_ef_code(f, p, cur_val); + break; + case tag_code: + set_tag_code(f, p, cur_val); + break; + } + } + break; + case def_font_cmd: + /* Here is where the information for a new font gets loaded. */ + tex_def_font((small_number) a); + break; + case letterspace_font_cmd: + new_letterspaced_font((small_number) a); + break; + case pdf_copy_font_cmd: + make_font_copy((small_number) a); + break; + case set_interaction_cmd: + new_interaction(); + break; + case set_ocp_cmd: + print_err("To use ocps, use the \\pushocplist primitive"); + print_ln(); + break; + case def_ocp_cmd: + new_ocp((small_number) a); + break; + case set_ocp_list_cmd: + print_err("To use ocp lists, use the \\pushocplist primitive"); + print_ln(); + break; + case def_ocp_list_cmd: + new_ocp_list((small_number) a); + break; + case push_ocp_list_cmd: + do_push_ocp_list((small_number) a); + break; + case pop_ocp_list_cmd: + do_pop_ocp_list((small_number) a); + break; + case clear_ocp_lists_cmd: + do_clear_ocp_lists((small_number) a); + break; + case ocp_list_op_cmd: + print_err("To build ocp lists, use the \\ocplist primitive"); + print_ln(); + break; + case ocp_trace_level_cmd: + scan_optional_equals(); + scan_int(); + if (cur_val != 0) + cur_val = 1; + define(ocp_trace_level_base, data_cmd, cur_val); + break; + default: + confusion("prefix"); + break; + } /* end of Assignments cases */ + DONE: + /* Insert a token saved by \.{\\afterassignment}, if any */ + if (after_token != 0) { + cur_tok = after_token; + back_input(); + after_token = 0; + } +} + +@ @c +void fixup_directions(void) +{ + int temp_no_whatsits; + int temp_no_dirs; + int temporary_dir; + temp_no_whatsits = no_local_whatsits; + temp_no_dirs = no_local_dirs; + temporary_dir = text_direction; + if (dir_level(text_dir_ptr) == cur_level) { + /* DIR: Remove from |text_dir_ptr| */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; + + } + unsave(); + if (abs(mode) == hmode) { + if (temp_no_dirs != 0) { + /* DIR: Add local dir node */ + tail_append(new_dir(text_direction)); + + dir_dir(tail) = temporary_dir - 64; + } + if (temp_no_whatsits != 0) { + /* LOCAL: Add local paragraph node */ + tail_append(make_local_par_node()); + + } + } +} + + +@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or +something similar, the |get_r_token| routine will substitute a special +control sequence for a token that is not redefinable. + +@c +void get_r_token(void) +{ + RESTART: + do { + get_token(); + } while (cur_tok == space_token); + if ((cur_cs == 0) || (cur_cs > eqtb_top) || + ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) { + print_err("Missing control sequence inserted"); + help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.", + "I've inserted an inaccessible control sequence so that your", + "definition will be completed without mixing me up too badly.", + "You can recover graciously from this error, if you're", + "careful; see exercise 27.2 in The TeXbook."); + if (cur_cs == 0) + back_input(); + cur_tok = cs_token_flag + frozen_protection; + ins_error(); + goto RESTART; + } +} + +@ @c +void assign_internal_value(int a, halfword p, int cur_val) +{ + halfword n; + if ((p >= int_base) && (p < attribute_base)) { + switch ((p - int_base)) { + case cat_code_table_code: + if (valid_catcode_table(cur_val)) { + if (cur_val != int_par(cat_code_table_code)) + word_define(p, cur_val); + } else { + print_err("Invalid \\catcode table"); + help2 + ("You can only switch to a \\catcode table that is initialized", + "using \\savecatcodetable or \\initcatcodetable, or to table 0"); + error(); + } + break; + case output_box_code: + if ((cur_val > 65535) | (cur_val < 0)) { + print_err("Invalid \\outputbox"); + help1 + ("The value for \\outputbox has to be between 0 and 65535."); + error(); + } else { + word_define(p, cur_val); + } + break; + case new_line_char_code: + if (cur_val > 127) { + print_err("Invalid \\newlinechar"); + help2 + ("The value for \\newlinechar has to be between 0 and 127.", + "Your invalid assignment will be ignored."); + error(); + } else if (cur_val < 0) { + word_define(p, -1); + } else { + word_define(p, cur_val); + } + break; + case end_line_char_code: + if (cur_val < 0) { + word_define(p, -1); + } else if (cur_val > 127) { + print_err("Invalid \\endlinechar"); + help2 + ("The value for \\endlinechar has to be no higher than 127.", + "Your invalid assignment will be ignored."); + error(); + } else { + word_define(p, cur_val); + } + break; + case pdf_compress_level_code: + static_pdf->compress_level = cur_val; + word_define(p, cur_val); + break; + case pdf_objcompresslevel_code: + static_pdf->objcompresslevel = cur_val; + word_define(p, cur_val); + break; + case language_code: + word_define(int_base + cur_lang_code, cur_val); + word_define(p, cur_val); + break; + default: + word_define(p, cur_val); + break; + } + /* If we are defining subparagraph penalty levels while we are + in hmode, then we put out a whatsit immediately, otherwise + we leave it alone. This mechanism might not be sufficiently + powerful, and some other algorithm, searching down the stack, + might be necessary. Good first step. */ + if ((abs(mode) == hmode) && + ((p == (int_base + local_inter_line_penalty_code)) || + (p == (int_base + local_broken_penalty_code)))) { + /* LOCAL: Add local paragraph node */ + tail_append(make_local_par_node()); + + eq_word_define(int_base + no_local_whatsits_code, + no_local_whatsits + 1); + } + + } else if ((p >= dimen_base) && (p < eqtb_size)) { + if (p == (dimen_base + page_left_offset_code)) { + n = cur_val - one_true_inch; + word_define(dimen_base + h_offset_code, n); + } else if (p == (dimen_base + h_offset_code)) { + n = cur_val + one_true_inch; + word_define(dimen_base + page_left_offset_code, n); + } else if (p == (dimen_base + page_top_offset_code)) { + n = cur_val - one_true_inch; + word_define(dimen_base + v_offset_code, n); + } else if (p == (dimen_base + v_offset_code)) { + n = cur_val + one_true_inch; + word_define(dimen_base + page_top_offset_code, n); + } + word_define(p, cur_val); + + } else if ((p >= local_base) && (p < toks_base)) { /* internal locals */ + define(p, call_cmd, cur_val); + } else { + confusion("assign internal value"); + } +} + + +@ When a glue register or parameter becomes zero, it will always point to +|zero_glue| because of the following procedure. (Exception: The tabskip +glue isn't trapped while preambles are being scanned.) + +@c +void trap_zero_glue(void) +{ + if ((width(cur_val) == 0) && (stretch(cur_val) == 0) + && (shrink(cur_val) == 0)) { + add_glue_ref(zero_glue); + delete_glue_ref(cur_val); + cur_val = zero_glue; + } +} + +@ We use the fact that |register<advance<multiply<divide| + +@c +void do_register_command(int a) +{ + halfword l, q, r, s; /* for list manipulation */ + int p; /* type of register involved */ + q = cur_cmd; + /* Compute the register location |l| and its type |p|; but |return| if invalid */ + /* Here we use the fact that the consecutive codes |int_val..mu_val| and + |assign_int..assign_mu_glue| correspond to each other nicely. */ + l = 0; + if (q != register_cmd) { + get_x_token(); + if ((cur_cmd >= assign_int_cmd) && (cur_cmd <= assign_mu_glue_cmd)) { + l = cur_chr; + p = cur_cmd - assign_int_cmd; + goto FOUND; + } + if (cur_cmd != register_cmd) { + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + tprint("' after "); + print_cmd_chr((quarterword) q, 0); + help1("I'm forgetting what you said and not changing anything."); + error(); + return; + } + } + p = cur_chr; + scan_register_num(); + if (p == int_val_level) + l = cur_val + count_base; + else if (p == attr_val_level) + l = cur_val + attribute_base; + else if (p == dimen_val_level) + l = cur_val + scaled_base; + else if (p == glue_val_level) + l = cur_val + skip_base; + else if (p == mu_val_level) + l = cur_val + mu_skip_base; + + FOUND: + if (q == register_cmd) { + scan_optional_equals(); + } else if (scan_keyword("by")) { + ; /* optional `\.{by}' */ + } + arith_error = false; + if (q < multiply_cmd) { + /* Compute result of |register| or |advance|, put it in |cur_val| */ + if (p < glue_val_level) { + if ((p == int_val_level) || (p == attr_val_level)) + scan_int(); + else + scan_normal_dimen(); + if (q == advance_cmd) + cur_val = cur_val + eqtb[l].cint; + } else { + scan_glue(p); + if (q == advance_cmd) { + /* Compute the sum of two glue specs */ + q = new_spec(cur_val); + r = equiv(l); + delete_glue_ref(cur_val); + width(q) = width(q) + width(r); + if (stretch(q) == 0) { + stretch_order(q) = normal; + } + if (stretch_order(q) == stretch_order(r)) { + stretch(q) = stretch(q) + stretch(r); + } else if ((stretch_order(q) < stretch_order(r)) + && (stretch(r) != 0)) { + stretch(q) = stretch(r); + stretch_order(q) = stretch_order(r); + } + if (shrink(q) == 0) { + shrink_order(q) = normal; + } + if (shrink_order(q) == shrink_order(r)) { + shrink(q) = shrink(q) + shrink(r); + } else if ((shrink_order(q) < shrink_order(r)) + && (shrink(r) != 0)) { + shrink(q) = shrink(r); + shrink_order(q) = shrink_order(r); + } + cur_val = q; + } + } + + } else { + /* Compute result of |multiply| or |divide|, put it in |cur_val| */ + scan_int(); + if (p < glue_val_level) { + if (q == multiply_cmd) { + if ((p == int_val_level) || (p == attr_val_level)) { + cur_val = mult_integers(eqtb[l].cint, cur_val); + } else { + cur_val = nx_plus_y(eqtb[l].cint, cur_val, 0); + } + } else { + cur_val = x_over_n(eqtb[l].cint, cur_val); + } + } else { + s = equiv(l); + r = new_spec(s); + if (q == multiply_cmd) { + width(r) = nx_plus_y(width(s), cur_val, 0); + stretch(r) = nx_plus_y(stretch(s), cur_val, 0); + shrink(r) = nx_plus_y(shrink(s), cur_val, 0); + } else { + width(r) = x_over_n(width(s), cur_val); + stretch(r) = x_over_n(stretch(s), cur_val); + shrink(r) = x_over_n(shrink(s), cur_val); + } + cur_val = r; + } + + } + if (arith_error) { + print_err("Arithmetic overflow"); + help2("I can't carry out that multiplication or division,", + "since the result is out of range."); + if (p >= glue_val_level) + delete_glue_ref(cur_val); + error(); + return; + } + if (p < glue_val_level) { + if (p == attr_val_level) { + if ((l - attribute_base) > max_used_attr) + max_used_attr = (l - attribute_base); + attr_list_cache = cache_disabled; + } + if ((p == int_val_level) || (p == dimen_val_level)) + assign_internal_value(a, l, cur_val); + else + word_define(l, cur_val); + } else { + trap_zero_glue(); + define(l, glue_ref_cmd, cur_val); + } +} + + +@ @c +void alter_aux(void) +{ + halfword c; /* |hmode| or |vmode| */ + if (cur_chr != abs(mode)) { + report_illegal_case(); + } else { + c = cur_chr; + scan_optional_equals(); + if (c == vmode) { + scan_normal_dimen(); + prev_depth = cur_val; + } else { + scan_int(); + if ((cur_val <= 0) || (cur_val > 32767)) { + print_err("Bad space factor"); + help1("I allow only values in the range 1..32767 here."); + int_error(cur_val); + } else { + space_factor = cur_val; + } + } + } +} + +@ @c +void alter_prev_graf(void) +{ + int p; /* index into |nest| */ + p = nest_ptr; + while (abs(nest[p].mode_field) != vmode) + decr(p); + scan_optional_equals(); + scan_int(); + if (cur_val < 0) { + print_err("Bad \\prevgraf"); + help1("I allow only nonnegative values here."); + int_error(cur_val); + } else { + nest[p].pg_field = cur_val; + } +} + +@ @c +void alter_page_so_far(void) +{ + int c; /* index into |page_so_far| */ + c = cur_chr; + scan_optional_equals(); + scan_normal_dimen(); + page_so_far[c] = cur_val; +} + +@ @c +void alter_integer(void) +{ + int c; /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */ + c = cur_chr; + scan_optional_equals(); + scan_int(); + if (c == 0) { + dead_cycles = cur_val; + } else if (c == 2) { + if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) { + print_err("Bad interaction mode"); + help2("Modes are 0=batch, 1=nonstop, 2=scroll, and", + "3=errorstop. Proceed, and I'll ignore this case."); + int_error(cur_val); + } else { + cur_chr = cur_val; + new_interaction(); + } + } else { + insert_penalties = cur_val; + } +} + +@ @c +void alter_box_dimen(void) +{ + int c; /* |width_offset| or |height_offset| or |depth_offset| */ + int b; /* box number */ + c = cur_chr; + scan_register_num(); + b = cur_val; + scan_optional_equals(); + scan_normal_dimen(); + if (box(b) != null) + varmem[box(b) + c].cint = cur_val; +} + + +@ @c +void new_interaction(void) +{ + print_ln(); + interaction = cur_chr; + if (interaction == batch_mode) + kpsemaketexdiscarderrors = 1; + else + kpsemaketexdiscarderrors = 0; + fixup_selector(log_opened); +} + + +@ The \.{\\afterassignment} command puts a token into the global +variable |after_token|. This global variable is examined just after +every assignment has been performed. + +@c +halfword after_token; /* zero, or a saved token */ + + +@ Here is a procedure that might be called `Get the next non-blank non-relax +non-call non-assignment token'. + +@c +void do_assignments(void) +{ + while (true) { + /* Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd <= max_non_prefixed_command) + return; + set_box_allowed = false; + prefixed_command(); + set_box_allowed = true; + } +} + +@ @c +void open_or_close_in(void) +{ + int c; /* 1 for \.{\\openin}, 0 for \.{\\closein} */ + int n; /* stream number */ + char *fn; + c = cur_chr; + scan_four_bit_int(); + n = cur_val; + if (read_open[n] != closed) { + lua_a_close_in(read_file[n], (n + 1)); + read_open[n] = closed; + } + if (c != 0) { + scan_optional_equals(); + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + back_input(); + if (cur_cmd != left_brace_cmd) { + scan_file_name(); /* set |cur_name| to desired file name */ + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(".tex"); + } else { + scan_file_name_toks(); + } + fn = pack_file_name(cur_name, cur_area, cur_ext); + if (lua_a_open_in(&(read_file[n]), fn, (n + 1))) { + read_open[n] = just_open; + } + } +} + +@ @c +boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */ + +void issue_message(void) +{ + int old_setting; /* holds |selector| setting */ + int c; /* identifies \.{\\message} and \.{\\errmessage} */ + str_number s; /* the message */ + c = cur_chr; + (void) scan_toks(false, true); + old_setting = selector; + selector = new_string; + token_show(def_ref); + selector = old_setting; + flush_list(def_ref); + str_room(1); + s = make_string(); + if (c == 0) { + /* Print string |s| on the terminal */ + if (term_offset + (int) str_length(s) > max_print_line - 2) + print_ln(); + else if ((term_offset > 0) || (file_offset > 0)) + print_char(' '); + print(s); + update_terminal(); + + } else { + /* Print string |s| as an error message */ + /* If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined + \.{\\errhelp}, we don't want to give a long help message each time. So we + give a verbose explanation only once. */ + print_err(""); + print(s); + if (err_help != null) { + use_err_help = true; + } else if (long_help_seen) { + help1("(That was another \\errmessage.)"); + } else { + if (interaction < error_stop_mode) + long_help_seen = true; + help4("This error message was generated by an \\errmessage", + "command, so I can't give any explicit help.", + "Pretend that you're Hercule Poirot: Examine all clues,", + "and deduce the truth by order and method."); + } + error(); + use_err_help = false; + + } + flush_str(s); +} + + +@ The |error| routine calls on |give_err_help| if help is requested from +the |err_help| parameter. + +@c +void give_err_help(void) +{ + token_show(err_help); +} + + + +@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by +building a token list and then changing the cases of the letters in it. + +@c +void shift_case(void) +{ + halfword b; /* |lc_code_base| or |uc_code_base| */ + halfword p; /* runs through the token list */ + halfword t; /* token */ + halfword c; /* character code */ + halfword i; /* inbetween */ + b = cur_chr; + p = scan_toks(false, false); + p = token_link(def_ref); + while (p != null) { + /* Change the case of the token in |p|, if a change is appropriate */ + /* + When the case of a |chr_code| changes, we don't change the |cmd|. + We also change active characters. + */ + t = token_info(p); + if (t < cs_token_flag) { + c = t % STRING_OFFSET; + if (b == uc_code_base) + i = get_uc_code(c); + else + i = get_lc_code(c); + if (i != 0) + set_token_info(p, t - c + i); + } else if (is_active_cs(cs_text(t - cs_token_flag))) { + c = active_cs_value(cs_text(t - cs_token_flag)); + if (b == uc_code_base) + i = get_uc_code(c); + else + i = get_lc_code(c); + if (i != 0) + set_token_info(p, active_to_cs(i, true) + cs_token_flag); + } + p = token_link(p); + } + back_list(token_link(def_ref)); + free_avail(def_ref); /* omit reference count */ +} + + +@ We come finally to the last pieces missing from |main_control|, namely the +`\.{\\show}' commands that are useful when debugging. + +@c +void show_whatever(void) +{ + halfword p; /* tail of a token list to show */ + int t; /* type of conditional being shown */ + int m; /* upper bound on |fi_or_else| codes */ + int l; /* line where that conditional began */ + int n; /* level of \.{\\if...\\fi} nesting */ + switch (cur_chr) { + case show_lists: + begin_diagnostic(); + show_activities(); + break; + case show_box_code: + /* Show the current contents of a box */ + scan_register_num(); + begin_diagnostic(); + tprint_nl("> \\box"); + print_int(cur_val); + print_char('='); + if (box(cur_val) == null) + tprint("void"); + else + show_box(box(cur_val)); + break; + case show_code: + /* Show the current meaning of a token, then |goto common_ending| */ + get_token(); + if (interaction == error_stop_mode) + wake_up_terminal(); + tprint_nl("> "); + if (cur_cs != 0) { + sprint_cs(cur_cs); + print_char('='); + } + print_meaning(); + goto COMMON_ENDING; + break; + /* Cases for |show_whatever| */ + case show_groups: + begin_diagnostic(); + show_save_groups(); + break; + case show_ifs: + begin_diagnostic(); + tprint_nl(""); + print_ln(); + if (cond_ptr == null) { + tprint_nl("### "); + tprint("no active conditionals"); + } else { + p = cond_ptr; + n = 0; + do { + incr(n); + p = vlink(p); + } while (p != null); + p = cond_ptr; + t = cur_if; + l = if_line; + m = if_limit; + do { + tprint_nl("### level "); + print_int(n); + tprint(": "); + print_cmd_chr(if_test_cmd, t); + if (m == fi_code) + tprint_esc("else"); + print_if_line(l); + decr(n); + t = if_limit_subtype(p); + l = if_line_field(p); + m = if_limit_type(p); + p = vlink(p); + } while (p != null); + } + break; + + default: + /* Show the current value of some parameter or register, + then |goto common_ending| */ + p = the_toks(); + if (interaction == error_stop_mode) + wake_up_terminal(); + tprint_nl("> "); + token_show(temp_token_head); + flush_list(token_link(temp_token_head)); + goto COMMON_ENDING; + break; + } + /* Complete a potentially long \.{\\show} command */ + end_diagnostic(true); + print_err("OK"); + if (selector == term_and_log) { + if (tracing_online <= 0) { + selector = term_only; + tprint(" (see the transcript file)"); + selector = term_and_log; + } + } + + COMMON_ENDING: + if (interaction < error_stop_mode) { + help0(); + decr(error_count); + } else if (tracing_online > 0) { + help3("This isn't an error message; I'm just \\showing something.", + "Type `I\\show...' to show more (e.g., \\show\\cs,", + "\\showthe\\count10, \\showbox255, \\showlists)."); + } else { + help5("This isn't an error message; I'm just \\showing something.", + "Type `I\\show...' to show more (e.g., \\show\\cs,", + "\\showthe\\count10, \\showbox255, \\showlists).", + "And type `I\\tracingonline=1\\show...' to show boxes and", + "lists on your terminal as well as in the transcript file."); + } + error(); +} + + +@ @c +void initialize(void) +{ /* this procedure gets things started properly */ + int k; /* index into |mem|, |eqtb|, etc. */ + /* Initialize whatever \TeX\ might access */ + /* Set initial values of key variables */ + initialize_errors(); + initialize_arithmetic(); + max_used_attr = -1; + attr_list_cache = cache_disabled; + initialize_nesting(); + + /* Start a new current page */ + page_contents = empty; + page_tail = page_head; +#if 0 + vlink(page_head) = null; +#endif + last_glue = max_halfword; + last_penalty = 0; + last_kern = 0; + last_node_type = -1; + page_depth = 0; + page_max_depth = 0; + + initialize_equivalents(); + no_new_control_sequence = true; /* new identifiers are usually forbidden */ + init_primitives(); + + mag_set = 0; + initialize_marks(); + initialize_read(); + + assert(static_pdf == NULL); + static_pdf = init_pdf_struct(static_pdf); + + format_ident = 0; + format_name = get_nullstr(); + for (k = 0; k <= 17; k++) + write_open[k] = false; + initialize_directions(); + seconds_and_micros(epochseconds, microseconds); + init_start_time(static_pdf); + + edit_name_start = 0; + stop_at_space = true; + + if (ini_version) { + /* Initialize table entries (done by \.{INITEX} only) */ + + init_node_mem(500); + initialize_tokens(); + /* Initialize the special list heads and constant nodes */ + initialize_alignments(); + initialize_buildpage(); + + initialize_active(); + + set_eq_type(undefined_control_sequence, undefined_cs_cmd); + set_equiv(undefined_control_sequence, null); + set_eq_level(undefined_control_sequence, level_zero); + for (k = null_cs; k <= (eqtb_top - 1); k++) + eqtb[k] = eqtb[undefined_control_sequence]; + set_equiv(glue_base, zero_glue); + set_eq_level(glue_base, level_one); + set_eq_type(glue_base, glue_ref_cmd); + for (k = glue_base + 1; k <= local_base - 1; k++) + eqtb[k] = eqtb[glue_base]; + glue_ref_count(zero_glue) = + glue_ref_count(zero_glue) + local_base - glue_base; + + par_shape_ptr = null; + set_eq_type(par_shape_loc, shape_ref_cmd); + set_eq_level(par_shape_loc, level_one); + for (k = etex_pen_base; k <= (etex_pens - 1); k++) + eqtb[k] = eqtb[par_shape_loc]; + for (k = output_routine_loc; k <= toks_base + biggest_reg; k++) + eqtb[k] = eqtb[undefined_control_sequence]; + box(0) = null; + set_eq_type(box_base, box_ref_cmd); + set_eq_level(box_base, level_one); + for (k = box_base + 1; k <= (box_base + biggest_reg); k++) + eqtb[k] = eqtb[box_base]; + cur_font = null_font; + set_eq_type(cur_font_loc, data_cmd); + set_eq_level(cur_font_loc, level_one); + set_equiv(cat_code_base, 0); + set_eq_type(cat_code_base, data_cmd); + set_eq_level(cat_code_base, level_one); + eqtb[internal_math_param_base] = eqtb[cat_code_base]; + eqtb[lc_code_base] = eqtb[cat_code_base]; + eqtb[uc_code_base] = eqtb[cat_code_base]; + eqtb[sf_code_base] = eqtb[cat_code_base]; + eqtb[math_code_base] = eqtb[cat_code_base]; + cat_code_table = 0; + initialize_math_codes(); + initialize_text_codes(); + initex_cat_codes(0); + for (k = '0'; k <= '9'; k++) + set_math_code(k, tex_mathcode, var_code, 0, k, level_one); + for (k = 'A'; k <= 'Z'; k++) { + set_math_code(k, tex_mathcode, var_code, 1, k, level_one); + set_math_code((k + 32), tex_mathcode, var_code, 1, (k + 32), + level_one); + set_lc_code(k, k + 32, level_one); + set_lc_code(k + 32, k + 32, level_one); + set_uc_code(k, k, level_one); + set_uc_code(k + 32, k, level_one); + set_sf_code(k, 999, level_one); + } + for (k = int_base; k <= attribute_base - 1; k++) + eqtb[k].cint = 0; + for (k = attribute_base; k <= del_code_base - 1; k++) + eqtb[k].cint = UNUSED_ATTRIBUTE; + mag = 1000; + tolerance = 10000; + hang_after = 1; + max_dead_cycles = 25; + escape_char = '\\'; + end_line_char = carriage_return; + set_del_code('.', tex_mathcode, 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */ + ex_hyphen_char = '-'; + output_box = 255; + for (k = dimen_base; k <= eqtb_size; k++) + eqtb[k].cint = 0; + page_left_offset = one_inch; + page_top_offset = one_inch; + page_right_offset = one_inch; + page_bottom_offset = one_inch; + pdf_ignored_dimen = ignore_depth; + pdf_each_line_height = pdf_ignored_dimen; + pdf_each_line_depth = pdf_ignored_dimen; + pdf_first_line_height = pdf_ignored_dimen; + pdf_last_line_depth = pdf_ignored_dimen; + ini_init_primitives(); + hash_used = frozen_control_sequence; /* nothing is used */ + hash_high = 0; + cs_count = 0; + set_eq_type(frozen_dont_expand, dont_expand_cmd); + cs_text(frozen_dont_expand) = maketexstring("notexpanded:"); + set_eq_type(frozen_primitive, ignore_spaces_cmd); + set_equiv(frozen_primitive, 1); + set_eq_level(frozen_primitive, level_one); + cs_text(frozen_primitive) = maketexstring("primitive"); + create_null_font(); + font_bytes = 0; + init_null_ocp(get_nullstr(), maketexstring("nullocp")); + initialize_init_ocplists(); + pdf_h_origin = one_inch; + pdf_v_origin = one_inch; + pdf_compress_level = 9; + pdf_objcompresslevel = 0; + pdf_decimal_digits = 3; + pdf_image_resolution = 72; + pdf_minor_version = 4; + pdf_gamma = 1000; + pdf_image_gamma = 2200; + pdf_image_hicolor = 1; + pdf_image_apply_gamma = 0; + pdf_px_dimen = one_bp; + pdf_draftmode = 0; + cs_text(frozen_protection) = maketexstring("inaccessible"); + format_ident = maketexstring(" (INITEX)"); + cs_text(end_write) = maketexstring("endwrite"); + set_eq_level(end_write, level_one); + set_eq_type(end_write, outer_call_cmd); + set_equiv(end_write, null); + + } + synctexoffset = int_base + synctex_code; + +} diff --git a/Build/source/texk/web2c/luatexdir/tex/mathcodes.h b/Build/source/texk/web2c/luatexdir/tex/mathcodes.h new file mode 100644 index 00000000000..676c50f7b3c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mathcodes.h @@ -0,0 +1,76 @@ +/* mathcodes.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: mathcodes.h 3261 2009-12-18 11:38:21Z taco $ */ + +#ifndef MATHCODES_H +# define MATHCODES_H + +/* mathcodes.c */ + +# define no_mathcode 0 /* this is a flag for |scan_delimiter| */ +# define tex_mathcode 8 +# define aleph_mathcode 16 +# define xetex_mathcode 21 +# define xetexnum_mathcode 22 + +typedef struct mathcodeval { + int class_value; + int origin_value; + int family_value; + int character_value; +} mathcodeval; + +void set_math_code(int n, + int commandorigin, + int mathclass, + int mathfamily, int mathcharacter, quarterword gl); + +mathcodeval get_math_code(int n); +int get_math_code_num(int n); +int get_del_code_num(int n); +mathcodeval scan_mathchar(int extcode); +mathcodeval scan_delimiter_as_mathchar(int extcode); + +mathcodeval mathchar_from_integer(int value, int extcode); +void show_mathcode_value(mathcodeval d); + +typedef struct delcodeval { + int class_value; + int origin_value; + int small_family_value; + int small_character_value; + int large_family_value; + int large_character_value; +} delcodeval; + +void set_del_code(int n, + int commandorigin, + int smathfamily, + int smathcharacter, + int lmathfamily, int lmathcharacter, quarterword gl); + +delcodeval get_del_code(int n); + +void unsave_math_codes(quarterword grouplevel); +void initialize_math_codes(void); +void dump_math_codes(void); +void undump_math_codes(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/mathcodes.w b/Build/source/texk/web2c/luatexdir/tex/mathcodes.w new file mode 100644 index 00000000000..ef5772b6b33 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mathcodes.w @@ -0,0 +1,508 @@ +% mathnodes.w +% +% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: mathcodes.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/mathcodes.w $"; + +@ math codes +@c +static sa_tree mathcode_head = NULL; + +#define MATHCODEHEAP 8 + +static mathcodeval *mathcode_heap = NULL; +static int mathcode_heapsize = MATHCODEHEAP; +static int mathcode_heapptr = 0; + +/* the 0xFFFFFFFF is a flag value */ +#define MATHCODESTACK 8 +#define MATHCODEDEFAULT 0xFFFFFFFF + +@ delcodes +@c +static sa_tree delcode_head = NULL; + +#define DELCODEHEAP 8 + +static delcodeval *delcode_heap = NULL; +static int delcode_heapsize = DELCODEHEAP; +static int delcode_heapptr = 0; + +#define DELCODESTACK 4 +#define DELCODEDEFAULT 0xFFFFFFFF + +@ some helpers for mathcode printing + +@c +#define print_hex_digit(A) do { \ + if ((A)>=10) print_char('A'+(A)-10); \ + else print_char('0'+(A)); \ + } while (0) + +#define two_hex(A) do { \ + print_hex_digit((A)/16); \ + print_hex_digit((A)%16); \ + } while (0) + +#define four_hex(A) do { \ + two_hex((A)/256); \ + two_hex((A)%256); \ + } while (0) + +#define six_hex(A) do { \ + two_hex((A)/65536); \ + two_hex(((A)%65536)/256); \ + two_hex((A)%256); \ + } while (0) + +@ @c +void show_mathcode_value(mathcodeval c) +{ + if (c.origin_value == aleph_mathcode) { + print_char('"'); + print_hex_digit(c.class_value); + two_hex(c.family_value); + four_hex(c.character_value); + } else if (c.origin_value == xetex_mathcode) { + print_char('"'); + print_hex_digit(c.class_value); + print_char('"'); + two_hex(c.family_value); + print_char('"'); + six_hex(c.character_value); + } else if (c.origin_value == xetexnum_mathcode) { + int m; + m = (c.class_value + (c.family_value * 8)) * 2097152 + + c.character_value; + print_int(m); + } else { + print_char('"'); + print_hex_digit(c.class_value); + print_hex_digit(c.family_value); + two_hex(c.character_value); + } +} + +@ @c +void show_mathcode(int n) +{ + mathcodeval c = get_math_code(n); + if (c.origin_value == aleph_mathcode) { + tprint_esc("omathcode"); + } else if (c.origin_value == xetex_mathcode) { + tprint_esc("Umathcode"); + } else if (c.origin_value == xetexnum_mathcode) { + tprint_esc("Umathcodenum"); + } else { + tprint_esc("mathcode"); + } + print_int(n); + print_char('='); + show_mathcode_value(c); +} + +@ @c +void unsavemathcode(quarterword gl) +{ + sa_stack_item st; + if (mathcode_head->stack == NULL) + return; + while (mathcode_head->stack_ptr > 0 && + abs(mathcode_head->stack[mathcode_head->stack_ptr].level) + >= (int) gl) { + st = mathcode_head->stack[mathcode_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(mathcode_head, st.code, st.value); + /* now do a trace message, if requested */ + if (int_par(tracing_restores_code) > 0) { + begin_diagnostic(); + print_char('{'); + tprint("restoring"); + print_char(' '); + show_mathcode(st.code); + print_char('}'); + end_diagnostic(false); + } + } + (mathcode_head->stack_ptr)--; + } +} + +@ @c +void set_math_code(int n, + int commandorigin, + int mathclass, + int mathfamily, int mathcharacter, quarterword level) +{ + mathcodeval d; + d.origin_value = commandorigin; + d.class_value = mathclass; + d.family_value = mathfamily; + d.character_value = mathcharacter; + if (mathcode_heapptr == mathcode_heapsize) { + mathcode_heapsize += MATHCODEHEAP; + mathcode_heap = + Mxrealloc_array(mathcode_heap, mathcodeval, mathcode_heapsize); + } + mathcode_heap[mathcode_heapptr] = d; + set_sa_item(mathcode_head, n, (sa_tree_item) mathcode_heapptr, level); + mathcode_heapptr++; + if (int_par(tracing_assigns_code) > 0) { + begin_diagnostic(); + print_char('{'); + tprint("assigning"); + print_char(' '); + show_mathcode(n); + print_char('}'); + end_diagnostic(false); + } +} + +@ @c +mathcodeval get_math_code(int n) +{ + unsigned int ret; + ret = get_sa_item(mathcode_head, n); + if (ret == MATHCODEDEFAULT) { + mathcodeval d; + d.class_value = 0; + d.family_value = 0; + d.origin_value = (n < 256 ? tex_mathcode : + (n < 65536 ? aleph_mathcode : xetex_mathcode)); + d.character_value = n; + return d; + } else { + return mathcode_heap[ret]; + } +} + + +@ @c +int get_math_code_num(int n) +{ + mathcodeval mval; + mval = get_math_code(n); + if (mval.origin_value == tex_mathcode) { + return (mval.class_value * 16 + mval.family_value) * 256 + + mval.character_value; + } else if (mval.origin_value == aleph_mathcode) { + return (mval.class_value * 256 + mval.family_value) * 65536 + + mval.character_value; + } else if (mval.origin_value == xetexnum_mathcode + || mval.origin_value == xetex_mathcode) { + return (mval.class_value + (mval.family_value * 8)) * (65536 * 32) + + mval.character_value; + } + return 0; +} + +@ @c +void initializemathcode(void) +{ + mathcode_head = new_sa_tree(MATHCODESTACK, MATHCODEDEFAULT); + mathcode_heap = Mxmalloc_array(mathcodeval, MATHCODEHEAP); +} + +@ @c +void dumpmathcode(void) +{ + int k; + mathcodeval d; + dump_sa_tree(mathcode_head); + dump_int(mathcode_heapsize); + dump_int(mathcode_heapptr); + for (k = 0; k < mathcode_heapptr; k++) { + d = mathcode_heap[k]; + dump_int(d.origin_value); + dump_int(d.class_value); + dump_int(d.family_value); + dump_int(d.character_value); + } +} + +void undumpmathcode(void) +{ + int k, x; + mathcodeval d; + mathcode_head = undump_sa_tree(); + undump_int(mathcode_heapsize); + undump_int(mathcode_heapptr); + mathcode_heap = Mxmalloc_array(mathcodeval, mathcode_heapsize); + for (k = 0; k < mathcode_heapptr; k++) { + undump_int(x); + d.origin_value = x; + undump_int(x); + d.class_value = x; + undump_int(x); + d.family_value = x; + undump_int(x); + d.character_value = x; + mathcode_heap[k] = d; + } + d.origin_value = 0; + d.class_value = 0; + d.family_value = 0; + d.character_value = 0; + for (k = mathcode_heapptr; k < mathcode_heapsize; k++) { + mathcode_heap[k] = d; + } +} + +@ @c +void show_delcode(int n) +{ + delcodeval c; + c = get_del_code(n); + if (c.origin_value == tex_mathcode) { + tprint_esc("delcode"); + } else if (c.origin_value == aleph_mathcode) { + tprint_esc("odelcode"); + } else if (c.origin_value == xetex_mathcode) { + tprint_esc("Udelcode"); + } else if (c.origin_value == xetexnum_mathcode) { + tprint_esc("Udelcodenum"); + } + print_int(n); + print_char('='); + if (c.small_family_value < 0) { + print_char('-'); + print_char('1'); + } else { + if (c.origin_value == tex_mathcode) { + print_char('"'); + print_hex_digit(c.small_family_value); + two_hex(c.small_character_value); + print_hex_digit(c.large_family_value); + two_hex(c.large_character_value); + } else if (c.origin_value == aleph_mathcode) { + print_char('"'); + two_hex(c.small_family_value); + four_hex(c.small_character_value); + print_char('"'); + two_hex(c.large_family_value); + four_hex(c.large_character_value); + } else if (c.origin_value == xetex_mathcode) { + print_char('"'); + two_hex(c.small_family_value); + six_hex(c.small_character_value); + } else if (c.origin_value == xetexnum_mathcode) { + int m; + m = c.small_family_value * 2097152 + c.small_character_value; + print_int(m); + } + } +} + + + +@ TODO: clean up the heap +@c +void unsavedelcode(quarterword gl) +{ + sa_stack_item st; + if (delcode_head->stack == NULL) + return; + while (delcode_head->stack_ptr > 0 && + abs(delcode_head->stack[delcode_head->stack_ptr].level) + >= (int) gl) { + st = delcode_head->stack[delcode_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(delcode_head, st.code, st.value); + /* now do a trace message, if requested */ + if (int_par(tracing_restores_code) > 0) { + begin_diagnostic(); + print_char('{'); + tprint("restoring"); + print_char(' '); + show_delcode(st.code); + print_char('}'); + end_diagnostic(false); + } + } + (delcode_head->stack_ptr)--; + } + +} + +@ @c +void set_del_code(int n, + int commandorigin, + int smathfamily, + int smathcharacter, + int lmathfamily, int lmathcharacter, quarterword gl) +{ + delcodeval d; + d.class_value = 0; + d.origin_value = commandorigin; + d.small_family_value = smathfamily; + d.small_character_value = smathcharacter; + d.large_family_value = lmathfamily; + d.large_character_value = lmathcharacter; + if (delcode_heapptr == delcode_heapsize) { + delcode_heapsize += DELCODEHEAP; + delcode_heap = + Mxrealloc_array(delcode_heap, delcodeval, delcode_heapsize); + } + delcode_heap[delcode_heapptr] = d; + set_sa_item(delcode_head, n, (sa_tree_item) delcode_heapptr, gl); + if (int_par(tracing_assigns_code) > 0) { + begin_diagnostic(); + print_char('{'); + tprint("assigning"); + print_char(' '); + show_delcode(n); + print_char('}'); + end_diagnostic(false); + } + delcode_heapptr++; +} + +@ @c +delcodeval get_del_code(int n) +{ + unsigned ret; + ret = get_sa_item(delcode_head, n); + if (ret == DELCODEDEFAULT) { + delcodeval d; + d.class_value = 0; + d.origin_value = tex_mathcode; + d.small_family_value = -1; + d.small_character_value = 0; + d.large_family_value = 0; + d.large_character_value = 0; + return d; + } else { + return delcode_heap[ret]; + } +} + +@ this really only works for old-style delcodes! + +@c +int get_del_code_num(int n) +{ + unsigned ret; + ret = get_sa_item(delcode_head, n); + if (ret == DELCODEDEFAULT) { + return -1; + } else { + delcodeval d = delcode_heap[ret]; + if (d.origin_value == tex_mathcode) { + return ((d.small_family_value * 256 + + d.small_character_value) * 4096 + + (d.large_family_value * 256) + d.large_character_value); + } else { + return -1; + } + } +} + +@ @c +void initializedelcode(void) +{ + delcode_head = new_sa_tree(DELCODESTACK, DELCODEDEFAULT); + delcode_heap = Mxmalloc_array(delcodeval, DELCODEHEAP); +} + +@ @c +void dumpdelcode(void) +{ + int k; + delcodeval d; + dump_sa_tree(delcode_head); + dump_int(delcode_heapsize); + dump_int(delcode_heapptr); + for (k = 0; k < delcode_heapptr; k++) { + d = delcode_heap[k]; + dump_int(d.origin_value); + dump_int(d.class_value); + dump_int(d.small_family_value); + dump_int(d.small_character_value); + dump_int(d.large_family_value); + dump_int(d.large_character_value); + } +} + +void undumpdelcode(void) +{ + int k; + delcodeval d; + delcode_head = undump_sa_tree(); + undump_int(delcode_heapsize); + undump_int(delcode_heapptr); + delcode_heap = Mxmalloc_array(delcodeval, delcode_heapsize); + for (k = 0; k < delcode_heapptr; k++) { + undump_int(d.origin_value); + undump_int(d.class_value); + undump_int(d.small_family_value); + undump_int(d.small_character_value); + undump_int(d.large_family_value); + undump_int(d.large_character_value); + delcode_heap[k] = d; + } + d.origin_value = tex_mathcode; + d.class_value = 0; + d.small_family_value = -1; + d.small_character_value = 0; + d.large_family_value = 0; + d.large_character_value = 0; + for (k = delcode_heapptr; k < delcode_heapsize; k++) { + delcode_heap[k] = d; + } +} + +@ @c +void unsave_math_codes(quarterword grouplevel) +{ + unsavemathcode(grouplevel); + unsavedelcode(grouplevel); +} + +@ @c +void initialize_math_codes(void) +{ + initializemathcode(); + initializedelcode(); +} + +@ @c +void free_math_codes(void) +{ + destroy_sa_tree(mathcode_head); + xfree(mathcode_heap); + destroy_sa_tree(delcode_head); + xfree(delcode_heap); +} + +@ @c +void dump_math_codes(void) +{ + dumpmathcode(); + dumpdelcode(); +} + +void undump_math_codes(void) +{ + undumpmathcode(); + undumpdelcode(); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/memoryword.h b/Build/source/texk/web2c/luatexdir/tex/memoryword.h new file mode 100644 index 00000000000..8c44a7e4e0d --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/memoryword.h @@ -0,0 +1,129 @@ +/* memoryword.h + + Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: memoryword.h 3352 2010-01-10 15:35:31Z hhenkel $ */ + +/* This header file is extra special because it is read in from + within the pascal source */ + +#ifndef MEMORYWORD_H +# define MEMORYWORD_H + +/* texmfmem.h: the memory_word type, which is too hard to translate + automatically from Pascal. We have to make sure the byte-swapping + that the (un)dumping routines do suffices to put things in the right + place in memory. + + A memory_word can be broken up into a `twohalves' or a + `fourquarters', and a `twohalves' can be further broken up. Here is + a picture. ..._M = most significant byte, ..._L = least significant + byte. + + + BigEndian: + twohalves.v: RH_MM RH_ML RH_LM RH_LL LH_MM LH_ML LH_LM LH_LL + twohalves.u: ---------JUNK---------- ----B0----- ----B1----- + fourquarters: ----B0----- ----B1----- ----B2----- ----B3----- + twoints: ---------CINT0--------- ---------CINT1--------- + + LittleEndian: + twohalves.v: LH_LL LH_LM LH_ML LH_MM RH_LL RH_LM RH_ML RH_MM + twohalves.u: ----B1----- ----B0----- + fourquarters: ----B3----- ----B2----- ----B1----- ----B0----- + twoints: ---------CINT1--------- ---------CINT0--------- + +*/ + + +typedef union { + struct { +# ifdef WORDS_BIGENDIAN + halfword RH, LH; +# else + halfword LH, RH; +# endif + } v; + + struct { /* Make B0,B1 overlap the most significant bytes of LH. */ +# ifdef WORDS_BIGENDIAN + halfword junk; + quarterword B0, B1; +# else /* not WORDS_BIGENDIAN */ + /* If 32-bit memory words, have to do something. */ + quarterword B1, B0; +# endif /* LittleEndian */ + } u; +} two_halves; + +typedef struct { + struct { +# ifdef WORDS_BIGENDIAN + quarterword B0, B1, B2, B3; +# else + quarterword B3, B2, B1, B0; +# endif + } u; +} four_quarters; + +typedef struct { +# ifdef WORDS_BIGENDIAN + int CINT0, CINT1; +# else + int CINT1, CINT0; +# endif +} two_ints; + +typedef struct { + glue_ratio GLUE; +} glues; + +typedef union { + two_halves hh; + four_quarters qqqq; + two_ints ii; + glues gg; +} memory_word; + +# define b0 u.B0 +# define b1 u.B1 +# define b2 u.B2 +# define b3 u.B3 + +# define rh v.RH +# define lhfield v.LH + +# define cint ii.CINT0 +# define cint1 ii.CINT1 + +# define gr gg.GLUE + +/* the next five defines are needed for the prototypes in web2c's coerce.h */ + +# define memoryword memory_word +# define strnumber str_number +# define packedASCIIcode packed_ASCII_code +# define poolpointer pool_pointer + +typedef FILE *word_file; + +# ifdef DEBUG +extern procedure print_word(memory_word w); +# endif + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/memoryword.w b/Build/source/texk/web2c/luatexdir/tex/memoryword.w new file mode 100644 index 00000000000..a1ede056dc5 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/memoryword.w @@ -0,0 +1,58 @@ +% memoryword.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: memoryword.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/memoryword.w $"; + + +@ When debugging, we may want to print a |memory_word| without knowing +what type it is; so we print it in all modes. + +@c +#ifdef DEBUG +void print_word(memory_word w) +{ + /* prints |w| in all ways */ + print_int(w.cint); + print_char(' '); + print_scaled(w.cint); + print_char(' '); + print_scaled(round(unity * float_cast(w.gr))); + print_ln(); + print_int(w.hh.lhfield); + print_char('='); + print_int(w.hh.b0); + print_char(':'); + print_int(w.hh.b1); + print_char(';'); + print_int(w.hh.rh); + print_char(' '); + print_int(w.qqqq.b0); + print_char(':'); + print_int(w.qqqq.b1); + print_char(':'); + print_int(w.qqqq.b2); + print_char(':'); + print_int(w.qqqq.b3); +} +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.h b/Build/source/texk/web2c/luatexdir/tex/mlist.h new file mode 100644 index 00000000000..c3c8a08b1be --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mlist.h @@ -0,0 +1,35 @@ +/* mlist.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: mlist.h 3265 2009-12-18 16:21:40Z taco $ */ + +#ifndef MLIST_H +# define MLIST_H 1 + +extern int cur_size; + +extern void run_mlist_to_hlist(halfword, int, boolean); +extern void fixup_math_parameters(int fam_id, int size_id, int f, int lvl); + +extern scaled get_math_quad(int a); + +extern void mlist_to_hlist_args(halfword, int, boolean); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.c b/Build/source/texk/web2c/luatexdir/tex/mlist.w index a1a07e4bee7..0b9e80802b1 100644 --- a/Build/source/texk/web2c/luatexdir/tex/mlist.c +++ b/Build/source/texk/web2c/luatexdir/tex/mlist.w @@ -1,44 +1,42 @@ -/* mlist.c +% mlist.w - Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> +% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> - This file is part of LuaTeX. +% This file is part of LuaTeX. - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. -#include "luatex-api.h" -#include <ptexlib.h> +\def\LuaTeX{Lua\TeX} -#include "nodes.h" -#include "commands.h" +@ @c +#include "ptexlib.h" + +#include "lua/luatex-api.h" static const char _svn_version[] = - "$Id: mlist.c 2448 2009-06-08 07:43:50Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/mlist.c $"; - -#define delimiter_factor int_par(param_delimiter_factor_code) -#define delimiter_shortfall dimen_par(param_delimiter_shortfall_code) -#define bin_op_penalty int_par(param_bin_op_penalty_code) -#define rel_penalty int_par(param_rel_penalty_code) -#define null_delimiter_space dimen_par(param_null_delimiter_space_code) -#define script_space dimen_par(param_script_space_code) -#define disable_lig int_par(param_disable_lig_code) -#define disable_kern int_par(param_disable_kern_code) - -#define level_one 1 -#define thin_mu_skip param_thin_mu_skip_code -#define med_mu_skip param_med_mu_skip_code -#define thick_mu_skip param_thick_mu_skip_code + "$Id: mlist.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/mlist.w $"; + +@ @c +#define delimiter_factor int_par(delimiter_factor_code) +#define delimiter_shortfall dimen_par(delimiter_shortfall_code) +#define bin_op_penalty int_par(bin_op_penalty_code) +#define rel_penalty int_par(rel_penalty_code) +#define null_delimiter_space dimen_par(null_delimiter_space_code) +#define script_space dimen_par(script_space_code) +#define disable_lig int_par(disable_lig_code) +#define disable_kern int_par(disable_kern_code) #define nDEBUG @@ -81,9 +79,8 @@ static const char _svn_version[] = (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter) -/* here are the math parameters that are font-dependant */ +@ here are the math parameters that are font-dependant -/* @ Before an mlist is converted to an hlist, \TeX\ makes sure that the fonts in family~2 have enough parameters to be math-symbol fonts, and that the fonts in family~3 have enough parameters to be @@ -92,8 +89,8 @@ following macros, which take a size code as their parameter; for example, |num1(cur_size)| gives the value of the |num1| parameter for the current size. @^parameters for symbols@> @^font parameters@> -*/ +@c #define total_mathsy_params 22 #define total_mathex_params 13 @@ -116,13 +113,13 @@ following macros, which take a size code as their parameter; for example, #define delim2(A) mathsy(A,21) /* size of \.{\\atopwithdelims} delimiters in non-displays */ #define axis_height(A) mathsy(A,22) /* height of fraction lines above the baseline */ -/* -The math-extension parameters have similar macros, but the size code is + +@ The math-extension parameters have similar macros, but the size code is omitted (since it is always |cur_size| when we refer to such parameters). @^parameters for symbols@> @^font parameters@> -*/ +@c #define mathex(A,B) font_param(fam_fnt(3,A),B) #define default_rule_thickness(A) mathex(A,8) /* thickness of \.{\\over} bars */ #define big_op_spacing1(A) mathex(A,9) /* minimum clearance above a displayed op */ @@ -131,7 +128,7 @@ omitted (since it is always |cur_size| when we refer to such parameters). #define big_op_spacing4(A) mathex(A,12) /* minimum baselineskip below displayed op */ #define big_op_spacing5(A) mathex(A,13) /* padding above and below displayed limits */ -/* I made a bunch of extensions cf. the MATH table in OpenType, but some of +@ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of the MathConstants values have no matching usage in \LuaTeX\ right now. ScriptPercentScaleDown, @@ -162,10 +159,9 @@ Also still TODO for OpenType Math: * extensible large operators * prescripts -*/ - -/* this is not really a math parameter at all */ +@ this is not really a math parameter at all +@c static void math_param_error(const char *param, int style) { char s[256]; @@ -179,12 +175,15 @@ static void math_param_error(const char *param, int style) snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", param, math_style_names[style]); tex_error(s, hlp); - /* flush_math(); */ +#if 0 + flush_math(); +#endif return; } -static scaled accent_base_height(integer f) +@ @c +static scaled accent_base_height(int f) { scaled a; a = x_height(f); @@ -196,8 +195,9 @@ static scaled accent_base_height(integer f) return a; } -/* the non-staticness of this function is for the benefit of |math.c| */ +@ the non-staticness of this function is for the benefit of |texmath.w| +@c scaled get_math_quad(int var) { scaled a = get_math_param(math_param_quad, var); @@ -208,9 +208,10 @@ scaled get_math_quad(int var) return a; } -/* this parameter is different because it is called with a size - specifier instead of a style specifier. */ +@ this parameter is different because it is called with a size + specifier instead of a style specifier. +@c static scaled math_axis(int b) { scaled a; @@ -229,6 +230,7 @@ static scaled math_axis(int b) return a; } +@ @c static scaled get_math_quad_size(int b) { int var; @@ -242,452 +244,95 @@ static scaled get_math_quad_size(int b) } +@ @c static scaled minimum_operator_size(int var) { scaled a = get_math_param(math_param_operator_size, var); return a; } -/* Old-style fonts do not define the radical_rule. This allows |make_radical| to select - * the backward compatibility code, and it means that we can't raise an error here. - */ +@ Old-style fonts do not define the |radical_rule|. This allows |make_radical| to select + the backward compatibility code, and it means that we can't raise an error here. +@c static scaled radical_rule(int var) { scaled a = get_math_param(math_param_radical_rule, var); return a; } -static scaled radical_degree_before(int var) -{ - scaled a = get_math_param(math_param_radical_degree_before, var); - if (a == undefined_math_parameter) { - math_param_error("radicaldegreebefore", var); - a = 0; - } - return a; -} - -static scaled connector_overlap_min(int var) -{ - scaled a = get_math_param(math_param_connector_overlap_min, var); - if (a == undefined_math_parameter) { - math_param_error("connectoroverlapmin", var); - a = 0; - } - return a; -} - +@ now follow all the trivial math parameters -static scaled radical_degree_after(int var) -{ - scaled a = get_math_param(math_param_radical_degree_after, var); - if (a == undefined_math_parameter) { - math_param_error("radicaldegreeafter", var); - a = 0; - } - return a; -} - -static scaled radical_degree_raise(int var) -{ - scaled a = get_math_param(math_param_radical_degree_raise, var); - if (a == undefined_math_parameter) { - math_param_error("radicaldegreeraise", var); - a = 0; - } - return a; -} - - -/* now follow all the trivial functions for the math parameters */ - -static scaled overbar_kern(int var) -{ - scaled a = get_math_param(math_param_overbar_kern, var); - if (a == undefined_math_parameter) { - math_param_error("overbarkern", var); - a = 0; - } - return a; -} - -static scaled overbar_rule(int var) -{ - scaled a = get_math_param(math_param_overbar_rule, var); - if (a == undefined_math_parameter) { - math_param_error("overbarrule", var); - a = 0; - } - return a; -} - -static scaled overbar_vgap(int var) -{ - scaled a = get_math_param(math_param_overbar_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("overbarvgap", var); - a = 0; - } - return a; -} - -static scaled underbar_rule(int var) -{ - scaled a = get_math_param(math_param_underbar_rule, var); - if (a == undefined_math_parameter) { - math_param_error("underbarrule", var); - a = 0; - } - return a; -} +@c +#define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b) -static scaled underbar_vgap(int var) +static scaled do_get_math_param_or_error(int var, int param, const char *name) { - scaled a = get_math_param(math_param_underbar_vgap, var); + scaled a = get_math_param(param, var); if (a == undefined_math_parameter) { - math_param_error("underbarvgap", var); + math_param_error(name, var); a = 0; } return a; } -static scaled underbar_kern(int var) -{ - scaled a = get_math_param(math_param_underbar_kern, var); - if (a == undefined_math_parameter) { - math_param_error("underbarkern", var); - a = 0; - } - return a; -} +@ @c +#define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before) +#define radical_degree_after(a) get_math_param_or_error(a, radical_degree_after) +#define radical_degree_raise(a) get_math_param_or_error(a, radical_degree_raise) -static scaled under_delimiter_vgap(int var) -{ - scaled a = get_math_param(math_param_under_delimiter_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("underdelimitervgap", var); - a = 0; - } - return a; -} +#define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min) -static scaled under_delimiter_bgap(int var) -{ - scaled a = get_math_param(math_param_under_delimiter_bgap, var); - if (a == undefined_math_parameter) { - math_param_error("underdelimiterbgap", var); - a = 0; - } - return a; -} +#define overbar_rule(a) get_math_param_or_error(a, overbar_rule) +#define overbar_kern(a) get_math_param_or_error(a, overbar_kern) +#define overbar_vgap(a) get_math_param_or_error(a, overbar_vgap) +#define underbar_rule(a) get_math_param_or_error(a, underbar_rule) +#define underbar_kern(a) get_math_param_or_error(a, underbar_kern) +#define underbar_vgap(a) get_math_param_or_error(a, underbar_vgap) -static scaled over_delimiter_vgap(int var) -{ - scaled a = get_math_param(math_param_over_delimiter_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("overdelimitervgap", var); - a = 0; - } - return a; -} +#define under_delimiter_vgap(a) get_math_param_or_error(a, under_delimiter_vgap) +#define under_delimiter_bgap(a) get_math_param_or_error(a, under_delimiter_bgap) -static scaled over_delimiter_bgap(int var) -{ - scaled a = get_math_param(math_param_over_delimiter_bgap, var); - if (a == undefined_math_parameter) { - math_param_error("overdelimiterbgap", var); - a = 0; - } - return a; -} +#define over_delimiter_vgap(a) get_math_param_or_error(a, over_delimiter_vgap) +#define over_delimiter_bgap(a) get_math_param_or_error(a, over_delimiter_bgap) +#define radical_vgap(a) get_math_param_or_error(a, radical_vgap) +#define radical_kern(a) get_math_param_or_error(a, radical_kern) +#define stack_vgap(a) get_math_param_or_error(a, stack_vgap) +#define stack_num_up(a) get_math_param_or_error(a, stack_num_up) +#define stack_denom_down(a) get_math_param_or_error(a, stack_denom_down) -static scaled radical_vgap(int var) -{ - scaled a = get_math_param(math_param_radical_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("radicalvgap", var); - a = 0; - } - return a; -} +#define fraction_rule(a) get_math_param_or_error(a, fraction_rule) +#define fraction_num_vgap(a) get_math_param_or_error(a, fraction_num_vgap) +#define fraction_denom_vgap(a) get_math_param_or_error(a, fraction_denom_vgap) +#define fraction_num_up(a) get_math_param_or_error(a, fraction_num_up) +#define fraction_denom_down(a) get_math_param_or_error(a, fraction_denom_down) +#define fraction_del_size(a) get_math_param_or_error(a, fraction_del_size) +#define limit_above_vgap(a) get_math_param_or_error(a, limit_above_vgap) +#define limit_above_bgap(a) get_math_param_or_error(a, limit_above_bgap) +#define limit_above_kern(a) get_math_param_or_error(a, limit_above_kern) -static scaled radical_kern(int var) -{ - scaled a = get_math_param(math_param_radical_kern, var); - if (a == undefined_math_parameter) { - math_param_error("radicalkern", var); - a = 0; - } - return a; -} +#define limit_below_vgap(a) get_math_param_or_error(a, limit_below_vgap) +#define limit_below_bgap(a) get_math_param_or_error(a, limit_below_bgap) +#define limit_below_kern(a) get_math_param_or_error(a, limit_below_kern) -static scaled stack_vgap(int var) -{ - scaled a = get_math_param(math_param_stack_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("stackvgap", var); - a = 0; - } - return a; -} +#define sub_shift_drop(a) get_math_param_or_error(a, sub_shift_drop) +#define sup_shift_drop(a) get_math_param_or_error(a, sup_shift_drop) +#define sub_shift_down(a) get_math_param_or_error(a, sub_shift_down) +#define sub_sup_shift_down(a) get_math_param_or_error(a, sub_sup_shift_down) +#define sup_shift_up(a) get_math_param_or_error(a, sup_shift_up) +#define sub_top_max(a) get_math_param_or_error(a, sub_top_max) +#define sup_bottom_min(a) get_math_param_or_error(a, sup_bottom_min) +#define sup_sub_bottom_max(a) get_math_param_or_error(a, sup_sub_bottom_max) +#define subsup_vgap(a) get_math_param_or_error(a, subsup_vgap) -static scaled stack_num_up(int var) -{ - scaled a = get_math_param(math_param_stack_num_up, var); - if (a == undefined_math_parameter) { - math_param_error("stacknumup", var); - a = 0; - } - return a; -} +#define space_after_script(a) get_math_param_or_error(a, space_after_script) -static scaled stack_denom_down(int var) -{ - scaled a = get_math_param(math_param_stack_denom_down, var); - if (a == undefined_math_parameter) { - math_param_error("stackdenomdown", var); - a = 0; - } - return a; -} - -static scaled fraction_rule(int var) -{ - scaled a = get_math_param(math_param_fraction_rule, var); - if (a == undefined_math_parameter) { - math_param_error("fractionrule", var); - a = 0; - } - return a; -} - - -static scaled fraction_num_vgap(int var) -{ - scaled a = get_math_param(math_param_fraction_num_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("fractionnumvgap", var); - a = 0; - } - return a; -} - -static scaled fraction_denom_vgap(int var) -{ - scaled a = get_math_param(math_param_fraction_denom_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("fractiondenomvgap", var); - a = 0; - } - return a; -} - -static scaled fraction_num_up(int var) -{ - scaled a = get_math_param(math_param_fraction_num_up, var); - if (a == undefined_math_parameter) { - math_param_error("fractionnumup", var); - a = 0; - } - return a; -} - -static scaled fraction_denom_down(int var) -{ - scaled a = get_math_param(math_param_fraction_denom_down, var); - if (a == undefined_math_parameter) { - math_param_error("fractiondenomdown", var); - a = 0; - } - return a; -} - -static scaled fraction_del_size(int var) -{ - scaled a = get_math_param(math_param_fraction_del_size, var); - if (a == undefined_math_parameter) { - math_param_error("fractiondelsize", var); - a = 0; - } - return a; -} - -static scaled limit_above_vgap(int var) -{ - scaled a = get_math_param(math_param_limit_above_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("limitabovevgap", var); - a = 0; - } - return a; -} - -static scaled limit_above_bgap(int var) -{ - scaled a = get_math_param(math_param_limit_above_bgap, var); - if (a == undefined_math_parameter) { - math_param_error("limitabovebgap", var); - a = 0; - } - return a; -} - -static scaled limit_above_kern(int var) -{ - scaled a = get_math_param(math_param_limit_above_kern, var); - if (a == undefined_math_parameter) { - math_param_error("limitabovekern", var); - a = 0; - } - return a; -} - -static scaled limit_below_vgap(int var) -{ - scaled a = get_math_param(math_param_limit_below_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("limitbelowvgap", var); - a = 0; - } - return a; -} - -static scaled limit_below_bgap(int var) -{ - scaled a = get_math_param(math_param_limit_below_bgap, var); - if (a == undefined_math_parameter) { - math_param_error("limitbelowbgap", var); - a = 0; - } - return a; -} - -static scaled limit_below_kern(int var) -{ - scaled a = get_math_param(math_param_limit_below_kern, var); - if (a == undefined_math_parameter) { - math_param_error("limitbelowkern", var); - a = 0; - } - return a; -} - - -static scaled sub_shift_drop(int var) -{ - scaled a = get_math_param(math_param_sub_shift_drop, var); - if (a == undefined_math_parameter) { - math_param_error("subshiftdrop", var); - a = 0; - } - return a; -} - -static scaled sup_shift_drop(int var) -{ - scaled a = get_math_param(math_param_sup_shift_drop, var); - if (a == undefined_math_parameter) { - math_param_error("supshiftdrop", var); - a = 0; - } - return a; -} - -static scaled sub_shift_down(int var) -{ - scaled a = get_math_param(math_param_sub_shift_down, var); - if (a == undefined_math_parameter) { - math_param_error("subshiftdown", var); - a = 0; - } - return a; -} - -static scaled sub_sup_shift_down(int var) -{ - scaled a = get_math_param(math_param_sub_sup_shift_down, var); - if (a == undefined_math_parameter) { - math_param_error("subsupshiftdown", var); - a = 0; - } - return a; -} - -static scaled sub_top_max(int var) -{ - scaled a = get_math_param(math_param_sub_top_max, var); - if (a == undefined_math_parameter) { - math_param_error("subtopmax", var); - a = 0; - } - return a; -} - -static scaled sup_shift_up(int var) -{ - scaled a = get_math_param(math_param_sup_shift_up, var); - if (a == undefined_math_parameter) { - math_param_error("supshiftup", var); - a = 0; - } - return a; -} - -static scaled sup_bottom_min(int var) -{ - scaled a = get_math_param(math_param_sup_bottom_min, var); - if (a == undefined_math_parameter) { - math_param_error("supbottommin", var); - a = 0; - } - return a; -} - -static scaled sup_sub_bottom_max(int var) -{ - scaled a = get_math_param(math_param_sup_sub_bottom_max, var); - if (a == undefined_math_parameter) { - math_param_error("supsubbottommax", var); - a = 0; - } - return a; -} - -static scaled subsup_vgap(int var) -{ - scaled a = get_math_param(math_param_subsup_vgap, var); - if (a == undefined_math_parameter) { - math_param_error("subsupvgap", var); - a = 0; - } - return a; -} - -static scaled space_after_script(int var) -{ - scaled a = get_math_param(math_param_space_after_script, var); - if (a == undefined_math_parameter) { - math_param_error("spaceafterscript", var); - a = 0; - } - return a; -} - - -/* This function is no longer useful */ - -boolean check_necessary_fonts(void) -{ - return false; /* temp */ -} - -void fixup_math_parameters(integer fam_id, integer size_id, integer f, - integer lvl) +@ @c +void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) { if (is_new_mathfont(f)) { /* fix all known parameters */ @@ -924,8 +569,13 @@ void fixup_math_parameters(integer fam_id, integer size_id, integer f, font_MATH_par(f, FractionDenominatorDisplayStyleShiftDown), lvl); - DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id, 0, lvl); - DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id, 0, lvl); + + DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id, + font_MATH_par(f, FractionDelimiterSize), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id, + font_MATH_par(f, + FractionDelimiterDisplayStyleSize), + lvl); DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id, font_MATH_par(f, SpaceAfterScript), lvl); @@ -1041,7 +691,7 @@ void fixup_math_parameters(integer fam_id, integer size_id, integer f, DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, (abs(math_x_height(size_id) * 4) / 5), lvl); - /* The display-size radical_vgap is done twice because it needs + /* The display-size |radical_vgap| is done twice because it needs values from both the sy and the ex font. */ DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id, (default_rule_thickness(size_id) + @@ -1144,7 +794,7 @@ void fixup_math_parameters(integer fam_id, integer size_id, integer f, 4 * default_rule_thickness(size_id), lvl); DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id, 4 * default_rule_thickness(size_id), lvl); - /* All of the |space_after_script|s are done in finalize_math_parameters because the + /* All of the |space_after_script|s are done in |finalize_math_parameters| because the \.{\\scriptspace} may have been altered by the user */ DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id, 0, @@ -1169,7 +819,7 @@ void fixup_math_parameters(integer fam_id, integer size_id, integer f, DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, big_op_spacing3(size_id), lvl); - /* The display-size radical_vgap is done twice because it needs + /* The display-size |radical_vgap| is done twice because it needs values from both the sy and the ex font. */ DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id, (default_rule_thickness(size_id) + @@ -1177,11 +827,14 @@ void fixup_math_parameters(integer fam_id, integer size_id, integer f, } } -/* this needs to be called just at the start of |mlist_to_hlist| */ -static void finalize_math_parameters(void) +@ This needs to be called just at the start of |mlist_to_hlist|, for +backward compatibility with \.{\\scriptspace}. + +@c +void finalize_math_parameters(void) { - integer saved_trace = int_par(param_tracing_assigns_code); - int_par(param_tracing_assigns_code) = 0; + int saved_trace = int_par(tracing_assigns_code); + int_par(tracing_assigns_code) = 0; if (get_math_param(math_param_space_after_script, display_style) == undefined_math_parameter) { def_math_param(math_param_space_after_script, display_style, @@ -1201,13 +854,12 @@ static void finalize_math_parameters(void) def_math_param(math_param_space_after_script, cramped_script_script_style, script_space, level_one); } - int_par(param_tracing_assigns_code) = saved_trace; + int_par(tracing_assigns_code) = saved_trace; } -/* -In order to convert mlists to hlists, i.e., noads to nodes, we need several +@ In order to convert mlists to hlists, i.e., noads to nodes, we need several subroutines that are conveniently dealt with now. Let us first introduce the macros that make it easy to get at the parameters and @@ -1215,10 +867,9 @@ other font information. A size code, which is a multiple of 256, is added to a family number to get an index into the table of internal font numbers for each combination of family and size. (Be alert: Size codes get larger as the type gets smaller.) -*/ - -static const char *math_size_string(integer s) +@c +const char *math_size_string(int s) { if (s == text_size) return "textfont"; @@ -1228,25 +879,24 @@ static const char *math_size_string(integer s) return "scriptscriptfont"; } -/* - @ When the style changes, the following piece of program computes associated - information: -*/ - -#define setup_cur_size_and_mu() do { \ - if (cur_style==script_style || \ - cur_style==cramped_script_style) \ - cur_size=script_size; \ - else if (cur_style==script_script_style || \ - cur_style==cramped_script_script_style) \ - cur_size=script_script_size; \ - else cur_size=text_size; \ - cur_mu=x_over_n(get_math_quad(cur_size),18); \ - } while (0) +@ When the style changes, the following piece of program computes associated +information: + +@c +#define setup_cur_size(a) do { \ + if (a==script_style || \ + a==cramped_script_style) \ + cur_size=script_size; \ + else if (a==script_script_style || \ + a==cramped_script_script_style) \ + cur_size=script_script_size; \ + else cur_size=text_size; \ + } while (0) -/* a simple routine that creates a flat copy of a nucleus */ -static pointer math_clone(pointer q) +@ a simple routine that creates a flat copy of a nucleus +@c +pointer math_clone(pointer q) { pointer x; if (q == null) @@ -1265,13 +915,12 @@ static pointer math_clone(pointer q) -/* - @ Here is a function that returns a pointer to a rule node having a given +@ Here is a function that returns a pointer to a rule node having a given thickness |t|. The rule will extend horizontally to the boundary of the vlist that eventually contains it. -*/ -static pointer do_fraction_rule(scaled t, pointer att) +@c +pointer do_fraction_rule(scaled t, pointer att) { pointer p; /* the new node */ p = new_rule(); @@ -1282,13 +931,13 @@ static pointer do_fraction_rule(scaled t, pointer att) return p; } -/* - @ The |overbar| function returns a pointer to a vlist box that consists of + +@ The |overbar| function returns a pointer to a vlist box that consists of a given box |b|, above which has been placed a kern of height |k| under a fraction rule of thickness |t| under additional space of height |ht|. -*/ -static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att) +@c +pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att) { pointer p, q; /* nodes being constructed */ p = new_kern(k); @@ -1299,21 +948,19 @@ static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att) p = new_kern(ht); reset_attributes(p, att); vlink(p) = q; - pack_direction = math_direction; - q = vpackage(p, 0, additional, max_dimen); + q = vpackage(p, 0, additional, max_dimen, math_direction); reset_attributes(q, att); return q; } -/* - Here is a subroutine that creates a new box, whose list contains a +@ Here is a subroutine that creates a new box, whose list contains a single character, and whose width includes the italic correction for that character. The height or depth of the box will be negative, if the height or depth of the character is negative; thus, this routine may deliver a slightly different result than |hpack| would produce. -*/ -static pointer char_box(internal_font_number f, integer c, pointer bb) +@c +static pointer char_box(internal_font_number f, int c, pointer bb) { pointer b, p; /* the new box and its character node */ b = new_null_box(); @@ -1327,24 +974,22 @@ static pointer char_box(internal_font_number f, integer c, pointer bb) return b; } -/* - Another handy subroutine computes the height plus depth of +@ Another handy subroutine computes the height plus depth of a given character: -*/ -static scaled height_plus_depth(internal_font_number f, integer c) +@c +scaled height_plus_depth(internal_font_number f, int c) { return (char_height(f, c) + char_depth(f, c)); } -/* - When we build an extensible character, it's handy to have the +@ When we build an extensible character, it's handy to have the following subroutine, which puts a given character on top of the characters already in box |b|: -*/ -static scaled stack_into_box(pointer b, internal_font_number f, integer c) +@c +scaled stack_into_box(pointer b, internal_font_number f, int c) { pointer p; /* new node placed into |b| */ p = char_box(f, c, node_attr(b)); @@ -1355,7 +1000,7 @@ static scaled stack_into_box(pointer b, internal_font_number f, integer c) } -static scaled stack_into_hbox(pointer b, internal_font_number f, integer c) +scaled stack_into_hbox(pointer b, internal_font_number f, int c) { pointer p, q; /* new node placed into |b| */ p = char_box(f, c, node_attr(b)); @@ -1375,8 +1020,8 @@ static scaled stack_into_hbox(pointer b, internal_font_number f, integer c) } - -static void add_delim_kern(pointer b, scaled s) +@ @c +void add_delim_kern(pointer b, scaled s) { pointer p; /* new node placed into |b| */ p = new_kern(s); @@ -1385,7 +1030,7 @@ static void add_delim_kern(pointer b, scaled s) list_ptr(b) = p; } -static void add_delim_hkern(pointer b, scaled s) +void add_delim_hkern(pointer b, scaled s) { pointer p, q; /* new node placed into |b| */ p = new_kern(s); @@ -1400,12 +1045,28 @@ static void add_delim_hkern(pointer b, scaled s) } } +@ \TeX's most important routine for dealing with formulas is called + |mlist_to_hlist|. After a formula has been scanned and represented + as an mlist, this routine converts it to an hlist that can be placed + into a box or incorporated into the text of a paragraph. The + explicit parameter |cur_mlist| points to the first node or noad in + the given mlist (and it might be |null|); the parameter |penalties| + is |true| if penalty nodes for potential line breaks are to be + inserted into the resulting hlist, the parameter |cur_style| is a + style code. After |mlist_to_hlist| has acted, |vlink(temp_head)| + points to the translated hlist. + Since mlists can be inside mlists, the procedure is recursive. And since this + is not part of \TeX's inner loop, the program has been written in a manner + that stresses compactness over efficiency. +@^recursion@> -/* */ +@c +int cur_size; /* size code corresponding to |cur_style| */ +@ @c static pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v, - pointer att, int boxtype) + pointer att, int boxtype, int cur_style) { pointer b; extinfo *cur; @@ -1413,15 +1074,15 @@ static pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v, scaled b_max; /* natural (maximum) height of the stack */ scaled s_max; /* amount of possible shrink in the stack */ scaled a, wd, ht, dp, last_ht; - integer cc; /* a temporary character code for extensibles */ - integer i; /* a temporary counter number of extensible pieces */ + int cc; /* a temporary character code for extensibles */ + int i; /* a temporary counter number of extensible pieces */ int with_extenders; int num_extenders, num_normal, num_total; scaled c, d, u; scaled *max_shrinks = NULL; assert(ext != NULL); b = new_null_box(); - type(b) = boxtype; + type(b) = (quarterword) boxtype; reset_attributes(b, att); min_overlap = connector_overlap_min(cur_style); assert(min_overlap >= 0); @@ -1586,7 +1247,7 @@ static pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v, depth(b) = char_depth(f, cc); return b; } - max_shrinks = xcalloc(num_total, sizeof(scaled)); + max_shrinks = xcalloc((unsigned) num_total, sizeof(scaled)); cur = ext; prev_overlap = -1; c = 0; @@ -1672,17 +1333,19 @@ static pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v, height(b) = ht; depth(b) = 0; /* the next correction is needed for radicals */ - if (list_ptr(b) != null && type(list_ptr(b)) == hlist_node && list_ptr(list_ptr(b)) != null && type(list_ptr(list_ptr(b))) == glyph_node) { /* and it should be */ + if (list_ptr(b) != null && + type(list_ptr(b)) == hlist_node && list_ptr(list_ptr(b)) != null + && type(list_ptr(list_ptr(b))) == glyph_node) { /* and it should be */ last_ht = char_height(font(list_ptr(list_ptr(b))), character(list_ptr(list_ptr(b)))); height(b) = last_ht; depth(b) = ht - last_ht; } - /* +#if 0 fprintf (stdout,"v=%f,b_max=%f,ht=%f,n=%d\n", (float)v/65536.0, (float)b_max/65536.0,(float)height(b)/65536.0,num_total); - */ +#endif width(b) = wd; } else { /* horizontal version */ @@ -1733,21 +1396,20 @@ static pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v, } static pointer get_delim_vbox(extinfo * ext, internal_font_number f, scaled v, - pointer att) + pointer att, int cur_style) { - return get_delim_box(ext, f, v, att, vlist_node); + return get_delim_box(ext, f, v, att, vlist_node, cur_style); } static pointer get_delim_hbox(extinfo * ext, internal_font_number f, scaled v, - pointer att) + pointer att, int cur_style) { - return get_delim_box(ext, f, v, att, hlist_node); + return get_delim_box(ext, f, v, att, hlist_node, cur_style); } -/* - @ The |var_delimiter| function, which finds or constructs a sufficiently +@ The |var_delimiter| function, which finds or constructs a sufficiently large delimiter, is the most interesting of the auxiliary functions that currently concern us. Given a pointer |d| to a delimiter field in some noad, together with a size code |s| and a vertical distance |v|, this function @@ -1761,9 +1423,9 @@ static pointer get_delim_hbox(extinfo * ext, internal_font_number f, scaled v, the box is vertically centered with respect to the axis in the given size. If a built-up symbol is returned, the height of the box before shifting will be the height of its topmost component. -*/ -static void endless_loop_error(internal_font_number g, integer y) +@c +static void endless_loop_error(internal_font_number g, int y) { char s[256]; const char *hlp[] = { @@ -1777,15 +1439,16 @@ static void endless_loop_error(internal_font_number g, integer y) tex_error(s, hlp); } -static pointer var_delimiter(pointer d, integer s, scaled v) +static pointer do_var_delimiter(pointer d, int s, scaled v, scaled * ic, + boolean flat, int cur_style) { /* label found,continue; */ pointer b; /* the box that will be constructed */ internal_font_number f, g; /* best-so-far and tentative font codes */ - integer c, i, x, y; /* best-so-far and tentative character codes */ + int c, i, x, y; /* best-so-far and tentative character codes */ scaled u; /* height-plus-depth of a tentative character */ scaled w; /* largest height-plus-depth so far */ - integer z; /* runs through font family members */ + int z; /* runs through font family members */ boolean large_attempt; /* are we trying the ``large'' variant? */ pointer att; /* to save the current attribute list */ extinfo *ext; @@ -1815,7 +1478,10 @@ static pointer var_delimiter(pointer d, integer s, scaled v) c = y; goto FOUND; } - u = height_plus_depth(g, y); + if (flat) + u = char_width(g, y); + else + u = height_plus_depth(g, y); if (u > w) { f = g; c = y; @@ -1853,116 +1519,56 @@ static pointer var_delimiter(pointer d, integer s, scaled v) */ ext = NULL; if ((char_tag(f, c) == ext_tag) && - ((ext = get_charinfo_vert_variants(char_info(f, c))) != NULL)) { - b = get_delim_vbox(ext, f, v, att); - width(b) += char_italic(f, c); + ((!flat + && (ext = get_charinfo_vert_variants(char_info(f, c))) != NULL) + || (flat + && (ext = + get_charinfo_hor_variants(char_info(f, c))) != NULL))) { + b = (flat ? get_delim_hbox(ext, f, v, att, cur_style) : + get_delim_vbox(ext, f, v, att, cur_style)); } else { b = char_box(f, c, att); } - } else { - b = new_null_box(); - reset_attributes(b, att); - width(b) = null_delimiter_space; /* use this width if no delimiter was found */ - } - shift_amount(b) = half(height(b) - depth(b)) - math_axis(s); - delete_attribute_ref(att); - return b; -} - -static pointer flat_var_delimiter(pointer d, integer s, scaled v) -{ - /* label found,continue; */ - pointer b; /* the box that will be constructed */ - internal_font_number f, g; /* best-so-far and tentative font codes */ - integer c, i, x, y; /* best-so-far and tentative character codes */ - scaled u; /* height-plus-depth of a tentative character */ - scaled w; /* largest height-plus-depth so far */ - integer z; /* runs through font family members */ - boolean large_attempt; /* are we trying the ``large'' variant? */ - pointer att; /* to save the current attribute list */ - extinfo *ext; - f = null_font; - c = 0; - w = 0; - att = null; - large_attempt = false; - if (d == null) - goto FOUND; - z = small_fam(d); - x = small_char(d); - i = 0; - while (true) { - /* The search process is complicated slightly by the facts that some of the - characters might not be present in some of the fonts, and they might not - be probed in increasing order of height. */ - if ((z != 0) || (x != 0)) { - g = fam_fnt(z, s); - if (g != null_font) { - y = x; - CONTINUE: - i++; - if (char_exists(g, y)) { - if (char_tag(g, y) == ext_tag) { - f = g; - c = y; - goto FOUND; - } - u = char_width(g, y); - if (u > w) { - f = g; - c = y; - w = u; - if (u >= v) - goto FOUND; - } - if (i > 10000) { - /* endless loop */ - endless_loop_error(g, y); - goto FOUND; - } - if (char_tag(g, y) == list_tag) { - y = char_remainder(g, y); - goto CONTINUE; - } - } - } - } - if (large_attempt) - goto FOUND; /* there were none large enough */ - large_attempt = true; - z = large_fam(d); - x = large_char(d); - } - FOUND: - if (d != null) { - att = node_attr(d); - node_attr(d) = null; - flush_node(d); - } - if (f != null_font) { - /* When the following code is executed, |char_tag(q)| will be equal to - |ext_tag| if and only if a built-up symbol is supposed to be returned. + /* This next test is because for OT MATH fonts, the italic correction of an + extensible character is only used for the placement of a subscript + (in negated form), and it is not supposed to be added to the + width of the character box at all. + + This has an effect later on in |make_op| as well, where it has to do + an extra correction for |make_script|'s addition of yet another italic + correction. */ - ext = NULL; - if ((char_tag(f, c) == ext_tag) && - ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) { - b = get_delim_hbox(ext, f, v, att); + if (!is_new_mathfont(f)) { width(b) += char_italic(f, c); - } else { - b = char_box(f, c, att); } + if (ic != NULL) + *ic = char_italic(f, c); } else { b = new_null_box(); reset_attributes(b, att); - width(b) = 0; /* use this width if no delimiter was found */ + width(b) = (flat ? 0 : null_delimiter_space); /* use this width if no delimiter was found */ + if (ic != NULL) + *ic = 0; } + if (!flat) + shift_amount(b) = half(height(b) - depth(b)) - math_axis(s); delete_attribute_ref(att); return b; } -/* -The next subroutine is much simpler; it is used for numerators and +static pointer var_delimiter(pointer d, int s, scaled v, scaled * ic, + int cur_style) +{ + return do_var_delimiter(d, s, v, ic, false, cur_style); +} + +static pointer flat_delimiter(pointer d, int s, scaled v, int cur_style) +{ + return do_var_delimiter(d, s, v, NULL, true, cur_style); +} + +@ The next subroutine is much simpler; it is used for numerators and denominators of fractions as well as for displayed operators and their limits above and below. It takes a given box~|b| and changes it so that the new box is centered in a box of width~|w|. @@ -1975,8 +1581,8 @@ infinite glue. The given box might contain a single character whose italic correction has been added to the width of the box; in this case a compensating kern is inserted. -*/ +@c static pointer rebox(pointer b, scaled w) { pointer p, q, r, att; /* temporary registers for list manipulation */ @@ -1985,7 +1591,7 @@ static pointer rebox(pointer b, scaled w) if ((width(b) != w) && (list_ptr(b) != null)) { if (type(b) == vlist_node) { - p = hpack(b, 0, additional); + p = hpack(b, 0, additional, -1); reset_attributes(p, node_attr(b)); b = p; } @@ -2011,7 +1617,7 @@ static pointer rebox(pointer b, scaled w) q = new_glue(ss_glue); reset_attributes(q, att); vlink(p) = q; - r = hpack(b, w, exactly); + r = hpack(b, w, exactly, -1); reset_attributes(r, att); delete_attribute_ref(att); return r; @@ -2021,17 +1627,16 @@ static pointer rebox(pointer b, scaled w) } } -/* - Here is a subroutine that creates a new glue specification from another +@ Here is a subroutine that creates a new glue specification from another one that is expressed in `\.{mu}', given the value of the math unit. -*/ +@c #define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen) static pointer math_glue(pointer g, scaled m) { pointer p; /* the new glue specification */ - integer n; /* integer part of |m| */ + int n; /* integer part of |m| */ scaled f; /* fraction part of |m| */ n = x_over_n(m, unity); f = tex_remainder; @@ -2054,14 +1659,13 @@ static pointer math_glue(pointer g, scaled m) return p; } -/* -The |math_kern| subroutine removes |mu_glue| from a kern node, given +@ The |math_kern| subroutine removes |mu_glue| from a kern node, given the value of the math unit. -*/ +@c static void math_kern(pointer p, scaled m) { - integer n; /* integer part of |m| */ + int n; /* integer part of |m| */ scaled f; /* fraction part of |m| */ if (subtype(p) == mu_glue) { n = x_over_n(m, unity); @@ -2075,30 +1679,8 @@ static void math_kern(pointer p, scaled m) } } -/* - \TeX's most important routine for dealing with formulas is called -|mlist_to_hlist|. After a formula has been scanned and represented as an -mlist, this routine converts it to an hlist that can be placed into a box -or incorporated into the text of a paragraph. There are three implicit -parameters, passed in global variables: |cur_mlist| points to the first -node or noad in the given mlist (and it might be |null|); |cur_style| is a -style code; and |mlist_penalties| is |true| if penalty nodes for potential -line breaks are to be inserted into the resulting hlist. After -|mlist_to_hlist| has acted, |vlink(temp_head)| points to the translated hlist. - -Since mlists can be inside mlists, the procedure is recursive. And since this -is not part of \TeX's inner loop, the program has been written in a manner -that stresses compactness over efficiency. -@^recursion@> -*/ - -pointer cur_mlist; /* beginning of mlist to be translated */ -integer cur_style; /* style code at current place in the list */ -integer cur_size; /* size code corresponding to |cur_style| */ -scaled cur_mu; /* the math unit width corresponding to |cur_size| */ -boolean mlist_penalties; /* should |mlist_to_hlist| insert penalties? */ - -void run_mlist_to_hlist(pointer p, integer mstyle, boolean penalties) +@ @c +void run_mlist_to_hlist(halfword p, int mstyle, boolean penalties) { int callback_id; int a, sfix; @@ -2128,17 +1710,12 @@ void run_mlist_to_hlist(pointer p, integer mstyle, boolean penalties) lua_settop(L, sfix); vlink(temp_head) = a; } else if (callback_id == 0) { - cur_mlist = p; - cur_style = mstyle; - mlist_penalties = penalties; - mlist_to_hlist(); + mlist_to_hlist_args(p, mstyle, penalties); } else { vlink(temp_head) = null; } } - -/* @ The recursion in |mlist_to_hlist| is due primarily to a subroutine called |clean_box| that puts a given noad field into a box using a given math style; |mlist_to_hlist| can call |clean_box|, which can call @@ -2148,46 +1725,42 @@ math style; |mlist_to_hlist| can call |clean_box|, which can call The box returned by |clean_box| is ``clean'' in the sense that its |shift_amount| is zero. -*/ -static pointer clean_box(pointer p, integer s) +@c +static pointer clean_box(pointer p, int s, int cur_style) { pointer q; /* beginning of a list to be boxed */ - integer save_style; /* |cur_style| to be restored */ pointer x; /* box to be returned */ pointer r; /* temporary pointer */ + pointer mlist = null; /* beginning of mlist to be translated */ switch (type(p)) { case math_char_node: - cur_mlist = new_noad(); + mlist = new_noad(); r = math_clone(p); - nucleus(cur_mlist) = r; + nucleus(mlist) = r; break; case sub_box_node: q = math_list(p); goto FOUND; break; case sub_mlist_node: - cur_mlist = math_list(p); + mlist = math_list(p); break; default: q = new_null_box(); goto FOUND; } - save_style = cur_style; - cur_style = s; - mlist_penalties = false; - mlist_to_hlist(); + mlist_to_hlist_args(mlist, s, false); q = vlink(temp_head); /* recursive call */ - cur_style = save_style; /* restore the style */ - setup_cur_size_and_mu(); + setup_cur_size(cur_style); FOUND: if (is_char_node(q) || (q == null)) - x = hpack(q, 0, additional); + x = hpack(q, 0, additional, -1); else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0)) x = q; /* it's already clean */ else - x = hpack(q, 0, additional); + x = hpack(q, 0, additional, -1); if (x != q && q != null) reset_attributes(x, node_attr(q)); /* Here we save memory space in a common case. */ @@ -2209,18 +1782,18 @@ static pointer clean_box(pointer p, integer s) return x; } -/* - It is convenient to have a procedure that converts a |math_char| +@ It is convenient to have a procedure that converts a |math_char| field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c| to the font code and character code of a given noad field. It also takes care of issuing error messages for nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false| after |fetch| has acted, and the field will also have been reset to |null|. -*/ -/* The outputs of |fetch| are placed in global variables. */ +The outputs of |fetch| are placed in global variables. + +@c internal_font_number cur_f; /* the |font| field of a |math_char| */ -integer cur_c; /* the |character| field of a |math_char| */ +int cur_c; /* the |character| field of a |math_char| */ static void fetch(pointer a) { /* unpack the |math_char| field |a| */ @@ -2248,8 +1821,7 @@ static void fetch(pointer a) } -/* -We need to do a lot of different things, so |mlist_to_hlist| makes two +@ We need to do a lot of different things, so |mlist_to_hlist| makes two passes over the given mlist. The first pass does most of the processing: It removes ``mu'' spacing from @@ -2264,8 +1836,8 @@ field, an integer field that replaces the |nucleus| or |thickness|. The second pass eliminates all noads and inserts the correct glue and penalties between nodes. -*/ +@c static void assign_new_hlist(pointer q, pointer r) { switch (type(q)) { @@ -2290,20 +1862,21 @@ static void assign_new_hlist(pointer q, pointer r) new_hlist(q) = r; } +@ @c #define choose_mlist(A) do { p=A(q); A(q)=null; } while (0) -/* -Most of the actual construction work of |mlist_to_hlist| is done + +@ Most of the actual construction work of |mlist_to_hlist| is done by procedures with names like |make_fraction|, |make_radical|, etc. To illustrate the general setup of such procedures, let's begin with a couple of simple ones. -*/ -static void make_over(pointer q) +@c +static void make_over(pointer q, int cur_style) { pointer p; - p = overbar(clean_box(nucleus(q), cramped_style(cur_style)), + p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style), overbar_vgap(cur_style), overbar_rule(cur_style), overbar_kern(cur_style), node_attr(nucleus(q))); @@ -2311,18 +1884,17 @@ static void make_over(pointer q) type(nucleus(q)) = sub_box_node; } -static void make_under(pointer q) +static void make_under(pointer q, int cur_style) { pointer p, x, y, r; /* temporary registers for box construction */ scaled delta; /* overall height plus depth */ - x = clean_box(nucleus(q), cur_style); + x = clean_box(nucleus(q), cur_style, cur_style); p = new_kern(underbar_vgap(cur_style)); reset_attributes(p, node_attr(q)); vlink(x) = p; r = do_fraction_rule(underbar_rule(cur_style), node_attr(q)); vlink(p) = r; - pack_direction = math_direction; - y = vpackage(x, 0, additional, max_dimen); + y = vpackage(x, 0, additional, max_dimen, math_direction); reset_attributes(y, node_attr(q)); delta = height(y) + depth(y) + underbar_kern(cur_style); height(y) = height(x); @@ -2337,13 +1909,12 @@ static void make_vcenter(pointer q) scaled delta; /* its height plus depth */ v = math_list(nucleus(q)); if (type(v) != vlist_node) - tconfusion("vcenter"); /* this can't happen vcenter */ + confusion("vcenter"); /* this can't happen vcenter */ delta = height(v) + depth(v); height(v) = math_axis(cur_size) + half(delta); depth(v) = delta - height(v); } -/* @ According to the rules in the \.{DVI} file specifications, we ensure alignment @^square roots@> between a square root sign and the rule above its nucleus by assuming that the @@ -2352,23 +1923,23 @@ height of the square-root symbol will be the thickness of the rule, and the depth of the square-root symbol should exceed or equal the height-plus-depth of the nucleus plus a certain minimum clearance~|psi|. The symbol will be placed so that the actual clearance is |psi| plus half the excess. -*/ -static void make_radical(pointer q) +@c +static void make_radical(pointer q, int cur_style) { pointer x, y, p; /* temporary registers for box construction */ scaled delta, clr, theta, h; /* dimensions involved in the calculation */ - x = clean_box(nucleus(q), cramped_style(cur_style)); + x = clean_box(nucleus(q), cramped_style(cur_style), cur_style); clr = radical_vgap(cur_style); theta = radical_rule(cur_style); if (theta == undefined_math_parameter) { theta = fraction_rule(cur_style); y = var_delimiter(left_delimiter(q), cur_size, - height(x) + depth(x) + clr + theta); + height(x) + depth(x) + clr + theta, NULL, cur_style); theta = height(y); } else { y = var_delimiter(left_delimiter(q), cur_size, - height(x) + depth(x) + clr + theta); + height(x) + depth(x) + clr + theta, NULL, cur_style); } left_delimiter(q) = null; delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr); @@ -2380,7 +1951,7 @@ static void make_radical(pointer q) vlink(y) = p; if (degree(q) != null) { scaled wr, br, ar; - pointer r = clean_box(degree(q), script_script_style); + pointer r = clean_box(degree(q), script_script_style, cur_style); reset_attributes(r, node_attr(degree(q))); wr = width(r); if (wr == 0) { @@ -2402,17 +1973,18 @@ static void make_radical(pointer q) vlink(x) = r; y = x; } - math_list(degree(q)) = null; /* for \Uroot ..{<list>}{} */ + math_list(degree(q)) = null; /* for \.{\\Uroot ..{<list>}{}} */ flush_node(degree(q)); } - p = hpack(y, 0, additional); + p = hpack(y, 0, additional, -1); reset_attributes(p, node_attr(q)); math_list(nucleus(q)) = p; type(nucleus(q)) = sub_box_node; } -/* Construct a vlist box */ +@ Construct a vlist box +@c static pointer wrapup_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down) @@ -2431,6 +2003,7 @@ wrapup_delimiter(pointer x, pointer y, pointer q, return v; } +@ @c #define fixup_widths(x,y) do { \ if (width(y) >= width(x)) { \ width(x) = width(y); \ @@ -2439,18 +2012,19 @@ wrapup_delimiter(pointer x, pointer y, pointer q, } \ } while (0) -/* this has the |nucleus| box |x| as a limit above an extensible delimiter |y| */ +@ this has the |nucleus| box |x| as a limit above an extensible delimiter |y| -static void make_over_delimiter(pointer q) +@c +static void make_over_delimiter(pointer q, int cur_style) { pointer x, y, v; /* temporary registers for box construction */ scaled shift_up, shift_down, clr, delta; - x = clean_box(nucleus(q), sub_style(cur_style)); - y = flat_var_delimiter(left_delimiter(q), cur_size, width(x)); + x = clean_box(nucleus(q), sub_style(cur_style), cur_style); + y = flat_delimiter(left_delimiter(q), cur_size, width(x), cur_style); left_delimiter(q) = null; fixup_widths(x, y); shift_up = over_delimiter_bgap(cur_style); - shift_down = 0; /* under_delimiter_bgap(cur_style); */ + shift_down = 0; clr = over_delimiter_vgap(cur_style); delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); if (delta > 0) { @@ -2462,16 +2036,17 @@ static void make_over_delimiter(pointer q) type(nucleus(q)) = sub_box_node; } -/* this has the extensible delimiter |x| as a limit above |nucleus| box |y| */ +@ this has the extensible delimiter |x| as a limit above |nucleus| box |y| -static void make_delimiter_over(pointer q) +@c +static void make_delimiter_over(pointer q, int cur_style) { pointer x, y, v; /* temporary registers for box construction */ scaled shift_up, shift_down, clr, delta; - y = clean_box(nucleus(q), cur_style); - x = flat_var_delimiter(left_delimiter(q), - cur_size + (cur_size == script_script_size ? 0 : 1), - width(y)); + y = clean_box(nucleus(q), cur_style, cur_style); + x = flat_delimiter(left_delimiter(q), + cur_size + (cur_size == script_script_size ? 0 : 1), + width(y), cur_style); left_delimiter(q) = null; fixup_widths(x, y); shift_up = over_delimiter_bgap(cur_style); @@ -2488,19 +2063,20 @@ static void make_delimiter_over(pointer q) } -/* this has the extensible delimiter |y| as a limit below a |nucleus| box |x| */ +@ this has the extensible delimiter |y| as a limit below a |nucleus| box |x| -static void make_delimiter_under(pointer q) +@c +static void make_delimiter_under(pointer q, int cur_style) { pointer x, y, v; /* temporary registers for box construction */ scaled shift_up, shift_down, clr, delta; - x = clean_box(nucleus(q), cur_style); - y = flat_var_delimiter(left_delimiter(q), - cur_size + (cur_size == script_script_size ? 0 : 1), - width(x)); + x = clean_box(nucleus(q), cur_style, cur_style); + y = flat_delimiter(left_delimiter(q), + cur_size + (cur_size == script_script_size ? 0 : 1), + width(x), cur_style); left_delimiter(q) = null; fixup_widths(x, y); - shift_up = 0; /* over_delimiter_bgap(cur_style); */ + shift_up = 0; shift_down = under_delimiter_bgap(cur_style); clr = under_delimiter_vgap(cur_style); delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); @@ -2514,17 +2090,18 @@ static void make_delimiter_under(pointer q) } -/* this has the extensible delimiter |x| as a limit below |nucleus| box |y| */ +@ this has the extensible delimiter |x| as a limit below |nucleus| box |y| -static void make_under_delimiter(pointer q) +@c +static void make_under_delimiter(pointer q, int cur_style) { pointer x, y, v; /* temporary registers for box construction */ scaled shift_up, shift_down, clr, delta; - y = clean_box(nucleus(q), sup_style(cur_style)); - x = flat_var_delimiter(left_delimiter(q), cur_size, width(y)); + y = clean_box(nucleus(q), sup_style(cur_style), cur_style); + x = flat_delimiter(left_delimiter(q), cur_size, width(y), cur_style); left_delimiter(q) = null; fixup_widths(x, y); - shift_up = 0; /* over_delimiter_bgap(cur_style); */ + shift_up = 0; shift_down = under_delimiter_bgap(cur_style); clr = under_delimiter_vgap(cur_style); delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); @@ -2538,17 +2115,17 @@ static void make_under_delimiter(pointer q) } -/* -Slants are not considered when placing accents in math mode. The accenter is + +@ Slants are not considered when placing accents in math mode. The accenter is centered over the accentee, and the accent width is treated as zero with respect to the size of the final box. -*/ +@c #define TOP_CODE 1 #define BOT_CODE 2 -static void do_make_math_accent(pointer q, internal_font_number f, integer c, - int top_or_bot) +static void do_make_math_accent(pointer q, internal_font_number f, int c, + int top_or_bot, int cur_style) { pointer p, r, x, y; /* temporary registers for box construction */ scaled s; /* amount to skew the accent to the right */ @@ -2583,7 +2160,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, integer c, } } } - x = clean_box(nucleus(q), cramped_style(cur_style)); + x = clean_box(nucleus(q), cramped_style(cur_style), cur_style); w = width(x); h = height(x); /* Switch to a larger accent if available and appropriate */ @@ -2595,12 +2172,12 @@ static void do_make_math_accent(pointer q, internal_font_number f, integer c, scaled w1 = xn_over_d(w, delimiter_factor, 1000); if (w - w1 > delimiter_shortfall) w1 = w - delimiter_shortfall; - y = get_delim_hbox(ext, f, w1, node_attr(attr_p)); + y = get_delim_hbox(ext, f, w1, node_attr(attr_p), cur_style); break; } else if (char_tag(f, c) != list_tag) { break; } else { - integer yy = char_remainder(f, c); + int yy = char_remainder(f, c); if (!char_exists(f, yy)) break; if (char_width(f, yy) > w) @@ -2632,7 +2209,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, integer c, subscr(q) = null; type(nucleus(q)) = sub_mlist_node; math_list(nucleus(q)) = x; - x = clean_box(nucleus(q), cur_style); + x = clean_box(nucleus(q), cur_style, cur_style); delta = delta + height(x) - h; h = height(x); } @@ -2656,17 +2233,16 @@ static void do_make_math_accent(pointer q, internal_font_number f, integer c, vlink(p) = x; vlink(y) = p; } else { - /* +#if 0 p = new_kern(-delta); vlink(x) = p; vlink(p) = y; y = x; - */ +#endif vlink(x) = y; y = x; } - pack_direction = math_direction; - r = vpackage(y, 0, additional, max_dimen); + r = vpackage(y, 0, additional, max_dimen, math_direction); reset_attributes(r, node_attr(q)); width(r) = width(x); y = r; @@ -2686,12 +2262,12 @@ static void do_make_math_accent(pointer q, internal_font_number f, integer c, type(nucleus(q)) = sub_box_node; } -static void make_math_accent(pointer q) +static void make_math_accent(pointer q, int cur_style) { if (accent_chr(q) != null) { fetch(accent_chr(q)); if (char_exists(cur_f, cur_c)) { - do_make_math_accent(q, cur_f, cur_c, TOP_CODE); + do_make_math_accent(q, cur_f, cur_c, TOP_CODE, cur_style); } flush_node(accent_chr(q)); accent_chr(q) = null; @@ -2699,19 +2275,18 @@ static void make_math_accent(pointer q) if (bot_accent_chr(q) != null) { fetch(bot_accent_chr(q)); if (char_exists(cur_f, cur_c)) { - do_make_math_accent(q, cur_f, cur_c, BOT_CODE); + do_make_math_accent(q, cur_f, cur_c, BOT_CODE, cur_style); } flush_node(bot_accent_chr(q)); bot_accent_chr(q) = null; } } -/* -The |make_fraction| procedure is a bit different because it sets +@ The |make_fraction| procedure is a bit different because it sets |new_hlist(q)| directly rather than making a sub-box. -*/ -static void make_fraction(pointer q) +@c +static void make_fraction(pointer q, int cur_style) { pointer p, v, x, y, z; /* temporary registers for box construction */ scaled delta, delta1, delta2, shift_up, shift_down, clr; @@ -2721,8 +2296,8 @@ static void make_fraction(pointer q) /* Create equal-width boxes |x| and |z| for the numerator and denominator, and compute the default amounts |shift_up| and |shift_down| by which they are displaced from the baseline */ - x = clean_box(numerator(q), num_style(cur_style)); - z = clean_box(denominator(q), denom_style(cur_style)); + x = clean_box(numerator(q), num_style(cur_style), cur_style); + z = clean_box(denominator(q), denom_style(cur_style), cur_style); if (width(x) < width(z)) x = rebox(x, width(z)); else @@ -2782,20 +2357,19 @@ static void make_fraction(pointer q) /* Put the fraction into a box with its delimiters, and make |new_hlist(q)| point to it */ delta = fraction_del_size(cur_style); - x = var_delimiter(left_delimiter(q), cur_size, delta); + x = var_delimiter(left_delimiter(q), cur_size, delta, NULL, cur_style); left_delimiter(q) = null; vlink(x) = v; - z = var_delimiter(right_delimiter(q), cur_size, delta); + z = var_delimiter(right_delimiter(q), cur_size, delta, NULL, cur_style); right_delimiter(q) = null; vlink(v) = z; - y = hpack(x, 0, additional); + y = hpack(x, 0, additional, -1); reset_attributes(y, node_attr(q)); assign_new_hlist(q, y); } -/* -If the nucleus of an |op_noad| is a single character, it is to be +@ If the nucleus of an |op_noad| is a single character, it is to be centered vertically with respect to the axis, after first being enlarged (via a character list in the font) if we are in display style. The normal convention for placing displayed limits is to put them above and below the @@ -2809,13 +2383,13 @@ subscript and superscript. After |make_op| has acted, |subtype(q)| will be |limits| if and only if the limits have been set above and below the operator. In that case, |new_hlist(q)| will already contain the desired final box. -*/ -static scaled make_op(pointer q) +@c +static scaled make_op(pointer q, int cur_style) { scaled delta; /* offset between subscript and superscript */ pointer p, v, x, y, z; /* temporary registers for box construction */ - integer c; /* register for character examination */ + int c; /* register for character examination */ scaled shift_up, shift_down; /* dimensions for box calculation */ scaled ok_size; if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) @@ -2829,8 +2403,21 @@ static scaled make_op(pointer q) y = new_node(delim_node, 0); small_fam(y) = math_fam(nucleus(q)); small_char(y) = math_character(nucleus(q)); - x = var_delimiter(y, text_size, ok_size); - delta = 0; + x = var_delimiter(y, text_size, ok_size, &delta, cur_style); + if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { + width(x) -= delta; /* remove italic correction */ + } + /* For an OT MATH font, we may have to get rid of yet another italic + correction because |make_scripts()| will add one. + This test is somewhat more complicated because |x| can be a null + delimiter */ + if ((subscr(q) != null || supscr(q) != null) + && (subtype(q) != op_noad_type_limits) + && ((list_ptr(x) != null) + && (type(list_ptr(x)) == glyph_node) + && is_new_mathfont(font(list_ptr(x))))) { + width(x) -= delta; /* remove another italic correction */ + } } else { ok_size = height_plus_depth(cur_f, cur_c) + 1; while ((char_tag(cur_f, cur_c) == list_tag) && @@ -2842,7 +2429,7 @@ static scaled make_op(pointer q) math_character(nucleus(q)) = c; } delta = char_italic(cur_f, cur_c); - x = clean_box(nucleus(q), cur_style); + x = clean_box(nucleus(q), cur_style, cur_style); if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) width(x) = width(x) - delta; /* remove italic correction */ shift_amount(x) = @@ -2854,7 +2441,7 @@ static scaled make_op(pointer q) } else { /* normal size */ delta = char_italic(cur_f, cur_c); - x = clean_box(nucleus(q), cur_style); + x = clean_box(nucleus(q), cur_style, cur_style); if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) width(x) = width(x) - delta; /* remove italic correction */ shift_amount(x) = half(height(x) - depth(x)) - math_axis(cur_size); @@ -2868,9 +2455,9 @@ static scaled make_op(pointer q) if (subtype(q) == op_noad_type_limits) { /* The following program builds a vlist box |v| for displayed limits. The width of the box is not affected by the fact that the limits may be skewed. */ - x = clean_box(supscr(q), sup_style(cur_style)); - y = clean_box(nucleus(q), cur_style); - z = clean_box(subscr(q), sub_style(cur_style)); + x = clean_box(supscr(q), sup_style(cur_style), cur_style); + y = clean_box(nucleus(q), cur_style, cur_style); + z = clean_box(subscr(q), sub_style(cur_style), cur_style); v = new_null_box(); reset_attributes(v, node_attr(q)); type(v) = vlist_node; @@ -2945,8 +2532,7 @@ static scaled make_op(pointer q) return delta; } -/* -A ligature found in a math formula does not create a ligature, because +@ A ligature found in a math formula does not create a ligature, because there is no question of hyphenation afterwards; the ligature will simply be stored in an ordinary |glyph_node|, after residing in an |ord_noad|. @@ -2955,11 +2541,11 @@ apply an italic correction to the current character unless it belongs to a math font (i.e., a font with |space=0|). No boundary characters enter into these ligatures. -*/ +@c static void make_ord(pointer q) { - integer a; /* the left-side character for lig/kern testing */ + int a; /* the left-side character for lig/kern testing */ pointer p, r, s; /* temporary registers for list manipulation */ scaled k; /* a kern */ liginfo lig; /* a ligature */ @@ -2995,16 +2581,16 @@ static void make_ord(pointer q) switch (lig_type(lig)) { case 1: case 5: - math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:|}, \.{=:|>} */ + math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:\char`\|}, \.{=:\char`\|>} */ break; case 2: case 6: - math_character(nucleus(p)) = lig_replacement(lig); /* \.{|=:}, \.{|=:>} */ + math_character(nucleus(p)) = lig_replacement(lig); /* \.{\char`\|=:}, \.{\char`\|=:>} */ break; case 3: case 7: case 11: - r = new_noad(); /* \.{|=:|}, \.{|=:|>}, \.{|=:|>>} */ + r = new_noad(); /* \.{\char`\|=:\char`\|}, \.{\char`\|=:\char`\|>}, \.{\char`\|=:\char`\|>>} */ reset_attributes(r, node_attr(q)); s = new_node(math_char_node, 0); reset_attributes(s, node_attr(q)); @@ -3051,23 +2637,23 @@ static void make_ord(pointer q) } } -/* If the fonts for the left and right bits of a mathkern are not - * both new-style fonts, then return a sentinel value meaning: - * please use old-style italic correction placement - */ +@ If the fonts for the left and right bits of a mathkern are not +both new-style fonts, then return a sentinel value meaning: +please use old-style italic correction placement +@c #define MATH_KERN_NOT_FOUND 0x7FFFFFFF -/* This function tries to find the kern needed for proper cut-ins. +@ This function tries to find the kern needed for proper cut-ins. The left side doesn't move, but the right side does, so the first order of business is to create a staggered fence line on the left side of the right character. The microsoft spec says that there are four quadrants, but the actual images say -*/ -static scaled math_kern_at(internal_font_number f, integer c, int side, int v) +@c +static scaled math_kern_at(internal_font_number f, int c, int side, int v) { int h, k, numkerns; scaled *kerns_heights; @@ -3088,7 +2674,7 @@ static scaled math_kern_at(internal_font_number f, integer c, int side, int v) } else if (side == bottom_right_kern) { kerns_heights = co->bottom_right_math_kern_array; } else { - tconfusion("math_kern_at"); + confusion("math_kern_at"); kerns_heights = NULL; /* not reached */ } #ifdef DEBUG @@ -3110,10 +2696,10 @@ static scaled math_kern_at(internal_font_number f, integer c, int side, int v) return kern; } - +@ @c static scaled -find_math_kern(internal_font_number l_f, integer l_c, - internal_font_number r_f, integer r_c, int cmd, scaled shift) +find_math_kern(internal_font_number l_f, int l_c, + internal_font_number r_f, int r_c, int cmd, scaled shift) { scaled corr_height_top = 0, corr_height_bot = 0; scaled krn_l = 0, krn_r = 0, krn = 0; @@ -3159,12 +2745,13 @@ find_math_kern(internal_font_number l_f, integer l_c, return (krn); } else { - tconfusion("find_math_kern"); + confusion("find_math_kern"); } return 0; /* not reached */ } -/* just a small helper */ +@ just a small helper +@c static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2) { pointer y; @@ -3180,27 +2767,71 @@ static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2) return new_hlist(q); } +@ +@c +#ifdef DEBUG +void dump_simple_field(pointer q) +{ + pointer p; + printf(" [%d, type=%d, vlink=%d] ", q, type(q), vlink(q)); + switch (type(q)) { + case math_char_node: + printf("mathchar "); + break; + case math_text_char_node: + printf("texchar "); + break; + case sub_box_node: + printf("box "); + break; + case sub_mlist_node: + printf("mlist "); + p = math_list(q); + while (p != null) { + dump_simple_field(p); + p = vlink(p); + } + break; + } +} +void dump_simple_node(pointer q) +{ + printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q)); + printf("nucleus: "); + dump_simple_field(nucleus(q)); + printf("\n"); + printf("sub: "); + dump_simple_field(subscr(q)); + printf("\n"); + printf("sup: "); + dump_simple_field(supscr(q)); + printf("\n\n"); +} +#endif -/* -The purpose of |make_scripts(q,it)| is to attach the subscript and/or +@ The purpose of |make_scripts(q,it)| is to attach the subscript and/or superscript of noad |q| to the list that starts at |new_hlist(q)|, given that subscript and superscript aren't both empty. The superscript will be horizontally shifted over |delta1|, the subscript over |delta2|. We set |shift_down| and |shift_up| to the minimum amounts to shift the baseline of subscripts and superscripts based on the given nucleus. -*/ - -static void make_scripts(pointer q, pointer p, scaled it) +@c +static void make_scripts(pointer q, pointer p, scaled it, int cur_style) { pointer x, y, z; /* temporary registers for box construction */ scaled shift_up, shift_down, clr; /* dimensions in the calculation */ scaled delta1, delta2; delta1 = it; delta2 = 0; +#ifdef DEBUG + printf("it: %d\n", it); + dump_simple_node(q); + printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p)); +#endif switch (type(nucleus(q))) { case math_char_node: case math_text_char_node: @@ -3216,7 +2847,7 @@ static void make_scripts(pointer q, pointer p, scaled it) shift_up = 0; shift_down = 0; } else { - z = hpack(p, 0, additional); + z = hpack(p, 0, additional, -1); shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */ shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */ list_ptr(z) = null; @@ -3226,7 +2857,7 @@ static void make_scripts(pointer q, pointer p, scaled it) /* Construct a subscript box |x| when there is no superscript */ /* When there is a subscript without a superscript, the top of the subscript should not exceed the baseline plus four-fifths of the x-height. */ - x = clean_box(subscr(q), sub_style(cur_style)); + x = clean_box(subscr(q), sub_style(cur_style), cur_style); width(x) = width(x) + space_after_script(cur_style); if (shift_down < sub_shift_down(cur_style)) shift_down = sub_shift_down(cur_style); @@ -3253,7 +2884,7 @@ static void make_scripts(pointer q, pointer p, scaled it) /* Construct a superscript box |x| */ /*The bottom of a superscript should never descend below the baseline plus one-fourth of the x-height. */ - x = clean_box(supscr(q), sup_style(cur_style)); + x = clean_box(supscr(q), sup_style(cur_style), cur_style); width(x) = width(x) + space_after_script(cur_style); clr = sup_shift_up(cur_style); if (shift_up < clr) @@ -3285,7 +2916,7 @@ static void make_scripts(pointer q, pointer p, scaled it) both subscript and superscript move up so that the bottom of the superscript is at least as high as the baseline plus four-fifths of the x-height. */ - y = clean_box(subscr(q), sub_style(cur_style)); + y = clean_box(subscr(q), sub_style(cur_style), cur_style); width(y) = width(y) + space_after_script(cur_style); if (shift_down < sub_sup_shift_down(cur_style)) shift_down = sub_sup_shift_down(cur_style); @@ -3316,7 +2947,7 @@ static void make_scripts(pointer q, pointer p, scaled it) /* the superscript is also to be shifted by |delta1| (the italic correction) */ clr = MATH_KERN_NOT_FOUND; if (is_char_node(p) && supscr(q) != null - && type(subscr(q)) == math_char_node) { + && type(supscr(q)) == math_char_node) { fetch(supscr(q)); if (char_exists(cur_f, cur_c)) { clr = @@ -3335,8 +2966,7 @@ static void make_scripts(pointer q, pointer p, scaled it) reset_attributes(p, node_attr(q)); vlink(x) = p; vlink(p) = y; - pack_direction = math_direction; - x = vpackage(x, 0, additional, max_dimen); + x = vpackage(x, 0, additional, max_dimen, math_direction); reset_attributes(x, node_attr(q)); shift_amount(x) = shift_down; } @@ -3364,20 +2994,18 @@ static void make_scripts(pointer q, pointer p, scaled it) } -/* -The |make_left_right| function constructs a left or right delimiter of +@ The |make_left_right| function constructs a left or right delimiter of the required size and returns the value |open_noad| or |close_noad|. The |left_noad_side| and |right_noad_side| will both be based on the original |style|, so they will have consistent sizes. -*/ -static small_number make_left_right(pointer q, integer style, scaled max_d, - scaled max_hv) +@c +static small_number make_left_right(pointer q, int style, scaled max_d, + scaled max_hv) { scaled delta, delta1, delta2; /* dimensions used in the calculation */ pointer tmp; - cur_style = style; - setup_cur_size_and_mu(); + setup_cur_size(style); delta2 = max_d + math_axis(cur_size); delta1 = max_hv + max_d - delta2; if (delta2 > delta1) @@ -3386,7 +3014,7 @@ static small_number make_left_right(pointer q, integer style, scaled max_d, delta2 = delta1 + delta1 - delimiter_shortfall; if (delta < delta2) delta = delta2; - tmp = var_delimiter(delimiter(q), cur_size, delta); + tmp = var_delimiter(delimiter(q), cur_size, delta, NULL, style); delimiter(q) = null; assign_new_hlist(q, tmp); if (subtype(q) == left_noad_side) @@ -3395,7 +3023,7 @@ static small_number make_left_right(pointer q, integer style, scaled max_d, return close_noad_type; } - +@ @c #define TEXT_STYLES(A,B) do { \ def_math_param(A,display_style,(B),level_one); \ def_math_param(A,cramped_display_style,(B),level_one); \ @@ -3425,40 +3053,40 @@ void initialize_math_spacing(void) { /* *INDENT-OFF* */ ALL_STYLES (math_param_ord_ord_spacing, 0); - ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip); - SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip, 0); - SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip, 0); + ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code); + SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0); + SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip_code, 0); ALL_STYLES (math_param_ord_open_spacing, 0); ALL_STYLES (math_param_ord_close_spacing, 0); ALL_STYLES (math_param_ord_punct_spacing, 0); - SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip_code, 0); - ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip); - ALL_STYLES (math_param_op_op_spacing, thin_mu_skip); + ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip_code); + ALL_STYLES (math_param_op_op_spacing, thin_mu_skip_code); ALL_STYLES (math_param_op_bin_spacing, 0); - SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip, 0); + SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip_code, 0); ALL_STYLES (math_param_op_open_spacing, 0); ALL_STYLES (math_param_op_close_spacing, 0); ALL_STYLES (math_param_op_punct_spacing, 0); - SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip_code, 0); - SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip, 0); - SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip, 0); + SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip_code, 0); + SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip_code, 0); ALL_STYLES (math_param_bin_bin_spacing, 0); ALL_STYLES (math_param_bin_rel_spacing, 0); - SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip, 0); + SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip_code, 0); ALL_STYLES (math_param_bin_close_spacing, 0); ALL_STYLES (math_param_bin_punct_spacing, 0); - SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip, 0); + SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip_code, 0); - SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip, 0); - SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip, 0); + SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip_code, 0); + SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip_code, 0); ALL_STYLES (math_param_rel_bin_spacing, 0); ALL_STYLES (math_param_rel_rel_spacing, 0); - SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip, 0); + SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip_code, 0); ALL_STYLES (math_param_rel_close_spacing, 0); ALL_STYLES (math_param_rel_punct_spacing, 0); - SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip, 0); + SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip_code, 0); ALL_STYLES (math_param_open_ord_spacing, 0); ALL_STYLES (math_param_open_op_spacing, 0); @@ -3470,38 +3098,39 @@ void initialize_math_spacing(void) ALL_STYLES (math_param_open_inner_spacing, 0); ALL_STYLES (math_param_close_ord_spacing, 0); - ALL_STYLES (math_param_close_op_spacing, thin_mu_skip); - SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip, 0); - SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip, 0); + ALL_STYLES (math_param_close_op_spacing, thin_mu_skip_code); + SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip_code, 0); + SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip_code, 0); ALL_STYLES (math_param_close_open_spacing, 0); ALL_STYLES (math_param_close_close_spacing, 0); ALL_STYLES (math_param_close_punct_spacing, 0); - SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0); - SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip, 0); - SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip_code, 0); ALL_STYLES (math_param_punct_bin_spacing, 0); - SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip, 0); - SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip, 0); - SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip, 0); - SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip, 0); - SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip, 0); - - SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip, 0); - ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip); - SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip, 0); - SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip, 0); - SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0); + + SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip_code, 0); + ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip_code); + SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip_code, 0); + SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip_code, 0); + SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip_code, 0); ALL_STYLES (math_param_inner_close_spacing, 0); - SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip, 0); - SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0); /* *INDENT-ON* */ } +@ @c #define both_types(A,B) ((A)*16+(B)) -static pointer math_spacing_glue(int l_type, int r_type, int mstyle) +static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu) { int x = -1; pointer z = null; @@ -3521,7 +3150,9 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle) case both_types(ord_noad_type, inner_noad_type): x = get_math_param(math_param_ord_inner_spacing,mstyle); break; case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break; case both_types(op_noad_type_normal, op_noad_type_normal ): x = get_math_param(math_param_op_op_spacing,mstyle); break; - /*case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break;*/ +#if 0 + case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break; +#endif case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break; case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break; case both_types(op_noad_type_normal, close_noad_type): x = get_math_param(math_param_op_close_spacing,mstyle); break; @@ -3529,15 +3160,21 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle) case both_types(op_noad_type_normal, inner_noad_type): x = get_math_param(math_param_op_inner_spacing,mstyle); break; case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break; case both_types(bin_noad_type, op_noad_type_normal ): x = get_math_param(math_param_bin_op_spacing,mstyle); break; - /*case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break;*/ - /*case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break;*/ +#if 0 + case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break; + case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break; +#endif case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break; - /*case both_types(bin_noad_type, close_noad_type): x = get_math_param(math_param_bin_close_spacing,mstyle); break;*/ - /*case both_types(bin_noad_type, punct_noad_type): x = get_math_param(math_param_bin_punct_spacing,mstyle); break;*/ +#if 0 + case both_types(bin_noad_type, close_noad_type): x = get_math_param(math_param_bin_close_spacing,mstyle); break; + case both_types(bin_noad_type, punct_noad_type): x = get_math_param(math_param_bin_punct_spacing,mstyle); break; +#endif case both_types(bin_noad_type, inner_noad_type): x = get_math_param(math_param_bin_inner_spacing,mstyle); break; case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break; case both_types(rel_noad_type, op_noad_type_normal ): x = get_math_param(math_param_rel_op_spacing,mstyle); break; - /*case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break;*/ +#if 0 + case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break; +#endif case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break; case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break; case both_types(rel_noad_type, close_noad_type): x = get_math_param(math_param_rel_close_spacing,mstyle); break; @@ -3545,7 +3182,9 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle) case both_types(rel_noad_type, inner_noad_type): x = get_math_param(math_param_rel_inner_spacing,mstyle); break; case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break; case both_types(open_noad_type, op_noad_type_normal ): x = get_math_param(math_param_open_op_spacing,mstyle); break; - /*case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break;*/ +#if 0 + case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break; +#endif case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break; case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break; case both_types(open_noad_type, close_noad_type): x = get_math_param(math_param_open_close_spacing,mstyle); break; @@ -3561,7 +3200,9 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle) case both_types(close_noad_type,inner_noad_type): x = get_math_param(math_param_close_inner_spacing,mstyle); break; case both_types(punct_noad_type,ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break; case both_types(punct_noad_type,op_noad_type_normal ): x = get_math_param(math_param_punct_op_spacing,mstyle); break; - /*case both_types(punct_noad_type,bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break;*/ +#if 0 + case both_types(punct_noad_type,bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break; +#endif case both_types(punct_noad_type,rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break; case both_types(punct_noad_type,open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break; case both_types(punct_noad_type,close_noad_type): x = get_math_param(math_param_punct_close_spacing,mstyle); break; @@ -3578,17 +3219,17 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle) /* *INDENT-ON* */ } if (x < 0) { - tconfusion("mathspacing"); + confusion("mathspacing"); } if (x != 0) { pointer y; - if (x <= thick_mu_skip) { /* trap thin/med/thick settings cf. old TeX */ - y = math_glue(glue_par(x), cur_mu); + if (x <= thick_mu_skip_code) { /* trap thin/med/thick settings cf. old TeX */ + y = math_glue(glue_par(x), mmu); z = new_glue(y); glue_ref_count(y) = null; - subtype(z) = x + 1; /* store a symbolic subtype */ + subtype(z) = (quarterword) (x + 1); /* store a symbolic subtype */ } else { - y = math_glue(x, cur_mu); + y = math_glue(x, mmu); z = new_glue(y); glue_ref_count(y) = null; } @@ -3596,32 +3237,66 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle) return z; } +@ @c +static pointer check_nucleus_complexity(halfword q, scaled * delta, + int cur_style) +{ + pointer p = null; + switch (type(nucleus(q))) { + case math_char_node: + case math_text_char_node: + fetch(nucleus(q)); + if (char_exists(cur_f, cur_c)) { + *delta = char_italic(cur_f, cur_c); + p = new_glyph(cur_f, cur_c); + reset_attributes(p, node_attr(nucleus(q))); + if ((is_new_mathfont(cur_f) && get_char_cat_code(cur_c) == 11) || + (!is_new_mathfont(cur_f) && type(nucleus(q)) == math_text_char_node && space(cur_f)) != 0) { + *delta = 0; /* no italic correction in mid-word of text font */ + } + if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) { + pointer x = new_kern(*delta); + reset_attributes(x, node_attr(nucleus(q))); + vlink(p) = x; + *delta = 0; + } + } + break; + case sub_box_node: + p = math_list(nucleus(q)); + break; + case sub_mlist_node: + mlist_to_hlist_args(math_list(nucleus(q)), cur_style, false); /* recursive call */ + setup_cur_size(cur_style); + p = hpack(vlink(temp_head), 0, additional, -1); + reset_attributes(p, node_attr(nucleus(q))); + break; + default: + confusion("mlist2"); /* this can't happen mlist2 */ + } + return p; +} - -/* Here is the overall plan of |mlist_to_hlist|, and the list of its +@ Here is the overall plan of |mlist_to_hlist|, and the list of its local variables. -*/ -void mlist_to_hlist(void) +@c +static void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) { - pointer mlist; /* beginning of the given list */ - boolean penalties; /* should penalty nodes be inserted? */ - integer style; /* the given style */ - integer save_style; /* holds |cur_style| during recursion */ pointer q; /* runs through the mlist */ pointer r; /* the most recent noad preceding |q| */ - integer r_type; /* the |type| of noad |r|, or |op_noad| if |r=null| */ - integer r_subtype; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */ - integer t; /* the effective |type| of noad |q| during the second pass */ - integer t_subtype; /* the effective |subtype| of noad |q| during the second pass */ + int style; + int r_type; /* the |type| of noad |r|, or |op_noad| if |r=null| */ + int r_subtype; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */ + int t; /* the effective |type| of noad |q| during the second pass */ + int t_subtype; /* the effective |subtype| of noad |q| during the second pass */ pointer p, x, y, z; /* temporary registers for list construction */ - integer pen; /* a penalty to be inserted */ - integer s; /* the size of a noad to be deleted */ + int pen; /* a penalty to be inserted */ + int s; /* the size of a noad to be deleted */ scaled max_hl, max_d; /* maximum height and depth of the list translated so far */ scaled delta; /* italic correction offset for subscript and superscript */ - mlist = cur_mlist; - penalties = mlist_penalties; - style = cur_style; /* tuck global parameters away as local variables */ + scaled cur_mu; /* the math unit width corresponding to |cur_size| */ + style = cur_style; /* tuck global parameter away as local variable */ q = mlist; r = null; r_type = simple_noad; @@ -3630,7 +3305,8 @@ void mlist_to_hlist(void) max_d = 0; x = null; p = null; - setup_cur_size_and_mu(); + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad(cur_size), 18); while (q != null) { /* We use the fact that no character nodes appear in an mlist, hence the field |type(q)| is always present. */ @@ -3668,10 +3344,10 @@ void mlist_to_hlist(void) } break; case over_noad_type: - make_over(q); + make_over(q, cur_style); break; case under_noad_type: - make_under(q); + make_under(q, cur_style); break; case vcenter_noad_type: make_vcenter(q); @@ -3687,7 +3363,7 @@ void mlist_to_hlist(void) case op_noad_type_normal: case op_noad_type_limits: case op_noad_type_no_limits: - delta = make_op(q); + delta = make_op(q, cur_style); if (subtype(q) == op_noad_type_limits) goto CHECK_DIMENSIONS; break; @@ -3708,27 +3384,28 @@ void mlist_to_hlist(void) goto DONE_WITH_NOAD; break; case fraction_noad: - make_fraction(q); + make_fraction(q, cur_style); goto CHECK_DIMENSIONS; break; case radical_noad: if (subtype(q) == 4) - make_under_delimiter(q); + make_under_delimiter(q, cur_style); else if (subtype(q) == 5) - make_over_delimiter(q); + make_over_delimiter(q, cur_style); else if (subtype(q) == 6) - make_delimiter_under(q); + make_delimiter_under(q, cur_style); else if (subtype(q) == 7) - make_delimiter_over(q); + make_delimiter_over(q, cur_style); else - make_radical(q); + make_radical(q, cur_style); break; case accent_noad: - make_math_accent(q); + make_math_accent(q, cur_style); break; case style_node: cur_style = subtype(q); - setup_cur_size_and_mu(); + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad(cur_size), 18); goto DONE_WITH_NODE; break; case choice_node: @@ -3751,7 +3428,7 @@ void mlist_to_hlist(void) flush_node_list(script_mlist(q)); flush_node_list(script_script_mlist(q)); type(q) = style_node; - subtype(q) = cur_style; + subtype(q) = (quarterword) cur_style; if (p != null) { z = vlink(q); vlink(q) = p; @@ -3783,8 +3460,7 @@ void mlist_to_hlist(void) the node following will be eliminated if it is a glue or kern node and if the current size is different from |text_size|. Unconditional math glue (`\.{\\muskip}') is converted to normal glue by multiplying the dimensions - by |cur_mu|. - @:non_script_}{\.{\\nonscript} primitive@> + by |cur_mu|. */ if (subtype(q) == mu_glue) { x = glue_ptr(q); @@ -3809,7 +3485,7 @@ void mlist_to_hlist(void) goto DONE_WITH_NODE; break; default: - tconfusion("mlist1"); /* this can't happen mlist1 */ + confusion("mlist1"); /* this can't happen mlist1 */ } /* When we get to the following part of the program, we have ``fallen through'' from cases that did not lead to |check_dimensions| or |done_with_noad| or @@ -3820,59 +3496,22 @@ void mlist_to_hlist(void) If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount by which a superscript should be moved right with respect to a subscript when both are present. - @^subscripts@> - @^superscripts@> */ - switch (type(nucleus(q))) { - case math_char_node: - case math_text_char_node: - fetch(nucleus(q)); - if (char_exists(cur_f, cur_c)) { - delta = char_italic(cur_f, cur_c); - p = new_glyph(cur_f, cur_c); - reset_attributes(p, node_attr(nucleus(q))); - if ((type(nucleus(q)) == math_text_char_node) - && (space(cur_f) != 0)) - delta = 0; /* no italic correction in mid-word of text font */ - if ((subscr(q) == null) && (supscr(q) == null) && (delta != 0)) { - x = new_kern(delta); - reset_attributes(x, node_attr(nucleus(q))); - vlink(p) = x; - delta = 0; - } - } else { - p = null; - } - break; - case sub_box_node: - p = math_list(nucleus(q)); - break; - case sub_mlist_node: - cur_mlist = math_list(nucleus(q)); - save_style = cur_style; - mlist_penalties = false; - mlist_to_hlist(); /* recursive call */ - cur_style = save_style; - setup_cur_size_and_mu(); - p = hpack(vlink(temp_head), 0, additional); - reset_attributes(p, node_attr(nucleus(q))); - break; - default: - tconfusion("mlist2"); /* this can't happen mlist2 */ - } + p = check_nucleus_complexity(q, &delta, cur_style); + if ((subscr(q) == null) && (supscr(q) == null)) { assign_new_hlist(q, p); - goto CHECK_DIMENSIONS; + } else { + make_scripts(q, p, delta, cur_style); /* top, bottom */ } - make_scripts(q, p, delta); /* top, bottom */ CHECK_DIMENSIONS: - z = hpack(new_hlist(q), 0, additional); + z = hpack(new_hlist(q), 0, additional, -1); if (height(z) > max_hl) max_hl = height(z); if (depth(z) > max_d) max_d = depth(z); list_ptr(z) = null; - flush_node(z); /* only drop the \hbox */ + flush_node(z); /* only drop the \.{\\hbox} */ DONE_WITH_NOAD: r = q; r_type = type(r); @@ -3880,7 +3519,8 @@ void mlist_to_hlist(void) if (r_type == fence_noad) { r_subtype = left_noad_side; cur_style = style; - setup_cur_size_and_mu(); + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad(cur_size), 18); } DONE_WITH_NODE: q = vlink(q); @@ -3904,7 +3544,8 @@ void mlist_to_hlist(void) r_type = 0; r_subtype = 0; cur_style = style; - setup_cur_size_and_mu(); + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad(cur_size), 18); NEXT_NODE: while (q != null) { /* If node |q| is a style node, change the style and |goto delete_q|; @@ -3952,7 +3593,8 @@ void mlist_to_hlist(void) /* Change the current style and |goto delete_q| */ cur_style = subtype(q); s = style_node_size; - setup_cur_size_and_mu(); + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad(cur_size), 18); goto DELETE_Q; break; case whatsit_node: @@ -3971,11 +3613,11 @@ void mlist_to_hlist(void) goto NEXT_NODE; break; default: - tconfusion("mlist3"); /* this can't happen mlist3 */ + confusion("mlist3"); /* this can't happen mlist3 */ } /* Append inter-element spacing based on |r_type| and |t| */ if (r_type > 0) { /* not the first noad */ - z = math_spacing_glue(r_subtype, t_subtype, cur_style); + z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu); if (z != null) { reset_attributes(z, node_attr(p)); vlink(p) = z; @@ -4026,3 +3668,12 @@ void mlist_to_hlist(void) free_node(r, get_node_size(type(r), subtype(r))); } } + +@ This used to be needed when |mlist_to_hlist| communicated via global +variables. + +@c +void mlist_to_hlist_args(pointer n, int w, boolean m) +{ + mlist_to_hlist(n, m, w); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.h b/Build/source/texk/web2c/luatexdir/tex/nesting.h new file mode 100644 index 00000000000..c46875ad8c6 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/nesting.h @@ -0,0 +1,71 @@ +/* nesting.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: nesting.h 3477 2010-03-12 13:59:34Z taco $ */ + +#ifndef NESTING_H +# define NESTING_H + +# define vmode 1 /* vertical mode */ +# define hmode (vmode+max_command_cmd+1) + /* horizontal mode */ +# define mmode (hmode+max_command_cmd+1) + /* math mode */ + +extern void print_mode(int m); +extern void print_in_mode(int m); +extern int get_mode_id(void); + +# define ignore_depth -65536000 /* magic dimension value to mean `ignore me' */ + +typedef struct list_state_record_ { + int mode_field; + halfword head_field; + halfword tail_field; + halfword eTeX_aux_field; + int pg_field; + int ml_field; + halfword prev_depth_field; + halfword space_factor_field; + halfword incompleat_noad_field; + halfword dirs_field; + int math_field; + int math_style_field; +} list_state_record; + +extern list_state_record *nest; +extern int nest_ptr; +extern int max_nest_stack; +# define cur_list nest[nest_ptr] + /* the ``top'' semantic state */ +extern int shown_mode; +extern halfword save_tail; + +extern void push_nest(void); +extern void pop_nest(void); +extern void initialize_nesting(void); + +extern void tail_append(halfword p); +extern halfword pop_tail(void); + + +extern void show_activities(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.w b/Build/source/texk/web2c/luatexdir/tex/nesting.w new file mode 100644 index 00000000000..28ae086d461 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/nesting.w @@ -0,0 +1,447 @@ +% nesting.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: nesting.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/nesting.w $"; + + +@ these are for |show_activities| +@c +#define page_goal page_so_far[0] +#define pdf_ignored_dimen dimen_par(pdf_ignored_dimen_code) +#define count(A) eqtb[count_base+(A)].cint + + +@ \TeX\ is typically in the midst of building many lists at once. For example, +when a math formula is being processed, \TeX\ is in math mode and +working on an mlist; this formula has temporarily interrupted \TeX\ from +being in horizontal mode and building the hlist of a paragraph; and this +paragraph has temporarily interrupted \TeX\ from being in vertical mode +and building the vlist for the next page of a document. Similarly, when a +\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily +interrupted from working in restricted horizontal mode, and it enters +internal vertical mode. The ``semantic nest'' is a stack that +keeps track of what lists and modes are currently suspended. + +At each level of processing we are in one of six modes: + +\yskip\hang|vmode| stands for vertical mode (the page builder); + +\hang|hmode| stands for horizontal mode (the paragraph builder); + +\hang|mmode| stands for displayed formula mode; + +\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox}); + +\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox}); + +\hang|-mmode| stands for math formula mode (not displayed). + +\yskip\noindent The mode is temporarily set to zero while processing \.{\\write} +texts in the |ship_out| routine. + +Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that +\TeX's ``big semantic switch'' can select the appropriate thing to +do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current +mode and |cur_cmd| is the current command code. + +@c +const char *string_mode(int m) +{ /* prints the mode represented by |m| */ + if (m > 0) { + switch (m / (max_command_cmd + 1)) { + case 0: + return "vertical mode"; + break; + case 1: + return "horizontal mode"; + break; + case 2: + return "display math mode"; + break; + default: + break; + } + } else if (m == 0) { + return "no mode"; + } else { + switch ((-m) / (max_command_cmd + 1)) { + case 0: + return "internal vertical mode"; + break; + case 1: + return "restricted horizontal mode"; + break; + case 2: + return "math mode"; + break; + default: + break; + } + } + return "unknown mode"; +} + +@ @c +void print_mode(int m) +{ /* prints the mode represented by |m| */ + tprint(string_mode(m)); +} + +@ @c +void print_in_mode(int m) +{ /* prints the mode represented by |m| */ + tprint("' in "); + tprint(string_mode(m)); +} + +@ @c +int get_mode_id(void) +{ /* returns the mode represented by |m| */ + int m = cur_list.mode_field; + if (m > 0) { + switch (m / (max_command_cmd + 1)) { + case 0: + return 'v'; + break; + case 1: + return 'h'; + break; + case 2: + return 'm'; + break; + default: + return '\0'; + break; + } + } else if (m == 0) { + return 'n';; + } else { + switch ((-m) / (max_command_cmd + 1)) { + case 0: + return 'V'; + break; + case 1: + return 'H'; + break; + case 2: + return 'M'; + break; + default: + return '\0'; + break; + } + } +} + + +@ The state of affairs at any semantic level can be represented by +five values: + +\yskip\hang|mode| is the number representing the semantic mode, as +just explained. + +\yskip\hang|head| is a |pointer| to a list head for the list being built; +|link(head)| therefore points to the first element of the list, or +to |null| if the list is empty. + +\yskip\hang|tail| is a |pointer| to the final node of the list being +built; thus, |tail=head| if and only if the list is empty. + +\yskip\hang|prev_graf| is the number of lines of the current paragraph that +have already been put into the present vertical list. + +\yskip\hang|aux| is an auxiliary |memory_word| that gives further information +that is needed to characterize the situation. + +\yskip\noindent +In vertical mode, |aux| is also known as |prev_depth|; it is the scaled +value representing the depth of the previous box, for use in baseline +calculations, or it is |<=-1000|pt if the next box on the vertical list is to +be exempt from baseline calculations. In horizontal mode, |aux| is also +known as |space_factor|; it holds the current space factor used in +spacing calculations. In math mode, |aux| is also known as |incompleat_noad|; +if not |null|, it points to a record that represents the numerator of a +generalized fraction for which the denominator is currently being formed +in the current list. + +There is also a sixth quantity, |mode_line|, which correlates +the semantic nest with the user's input; |mode_line| contains the source +line number at which the current level of nesting was entered. The negative +of this line number is the |mode_line| at the level of the +user's output routine. + +A seventh quantity, |eTeX_aux|, is used by the extended features eTeX. +In math mode it is known as |delim_ptr| and points to the most +recent |fence_noad| of a |math_left_group|. + +In horizontal mode, the |prev_graf| field is used for initial language data. + +The semantic nest is an array called |nest| that holds the |mode|, |head|, +|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels +below the currently active one. Information about the currently active +level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|, +|aux|, and |mode_line|, which live in a struct that is ready to +be pushed onto |nest| if necessary. + +The math field is used by various bits and pieces in |texmath.w| + +@ This implementation of +\TeX\ uses two different conventions for representing sequential stacks. +@^stack conventions@>@^conventions for representing stacks@> + +\yskip\hang 1) If there is frequent access to the top entry, and if the +stack is essentially never empty, then the top entry is kept in a global +variable (even better would be a machine register), and the other entries +appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. The +semantic stack is handled this way. + +\yskip\hang 2) If there is infrequent top access, the entire stack contents +are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| +is treated this way, as we have seen. + +@c +list_state_record *nest; +int nest_ptr; /* first unused location of |nest| */ +int max_nest_stack; /* maximum of |nest_ptr| when pushing */ +int shown_mode; /* most recent mode shown by \.{\\tracingcommands} */ +halfword save_tail; /* save |tail| so we can examine whether we have an auto + kern before a glue */ + +@ We will see later that the vertical list at the bottom semantic level is split +into two parts; the ``current page'' runs from |page_head| to |page_tail|, +and the ``contribution list'' runs from |contrib_head| to |tail| of +semantic level zero. The idea is that contributions are first formed in +vertical mode, then ``contributed'' to the current page (during which time +the page-breaking decisions are made). For now, we don't need to know +any more details about the page-building process. + +@c +void initialize_nesting(void) +{ + nest_ptr = 0; + max_nest_stack = 0; + shown_mode = 0; + cur_list.mode_field = vmode; + cur_list.head_field = contrib_head; + cur_list.tail_field = contrib_head; + cur_list.eTeX_aux_field = null; + cur_list.prev_depth_field = ignore_depth; + cur_list.space_factor_field = 1000; + cur_list.incompleat_noad_field = null; + cur_list.ml_field = 0; + cur_list.pg_field = 0; + cur_list.dirs_field = null; + init_math_fields(); +} + + + +@ Here is a common way to make the current list grow: + +@c +void tail_append(halfword p) +{ + couple_nodes(cur_list.tail_field, p); + cur_list.tail_field = vlink(cur_list.tail_field); +} + + +@ @c +halfword pop_tail(void) +{ + halfword n, r; + if (cur_list.tail_field != cur_list.head_field) { + r = cur_list.tail_field; + if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) { + n = alink(cur_list.tail_field); + } else { + n = cur_list.head_field; + while (vlink(n) != cur_list.tail_field) + n = vlink(n); + } + flush_node(cur_list.tail_field); + cur_list.tail_field = n; + vlink(n) = null; + return r; + } else { + return null; + } +} + +@ When \TeX's work on one level is interrupted, the state is saved by +calling |push_nest|. This routine changes |head| and |tail| so that +a new (empty) list is begun; it does not change |mode| or |aux|. + +@c +void push_nest(void) +{ /* enter a new semantic level, save the old */ + if (nest_ptr > max_nest_stack) { + max_nest_stack = nest_ptr; + if (nest_ptr == nest_size) + overflow("semantic nest size", (unsigned) nest_size); + } + incr(nest_ptr); + cur_list.mode_field = nest[nest_ptr - 1].mode_field; + cur_list.head_field = new_node(temp_node, 0); + cur_list.tail_field = cur_list.head_field; + cur_list.eTeX_aux_field = null; + cur_list.ml_field = line; + cur_list.pg_field = 0; + cur_list.dirs_field = null; + cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field; + cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field; + cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field; + init_math_fields(); +} + + +@ Conversely, when \TeX\ is finished on the current level, the former +state is restored by calling |pop_nest|. This routine will never be +called at the lowest semantic level, nor will it be called unless |head| +is a node that should be returned to free memory. + +@c +void pop_nest(void) +{ /* leave a semantic level, re-enter the old */ + flush_node(cur_list.head_field); + decr(nest_ptr); +} + +@ Here is a procedure that displays what \TeX\ is working on, at all levels. + +@c +void show_activities(void) +{ + int p; /* index into |nest| */ + int m; /* mode */ + halfword q, r; /* for showing the current page */ + int t; /* ditto */ + tprint_nl(""); + print_ln(); + for (p = nest_ptr; p >= 0; p--) { + m = nest[p].mode_field; + tprint_nl("### "); + print_mode(m); + tprint(" entered at line "); + print_int(abs(nest[p].ml_field)); + /* we dont do this any more */ +#if 0 + + if (m == hmode) + if (nest[p].pg_field != 040600000) { + tprint(" (language"); + print_int(nest[p].pg_field % 0200000); + tprint(":hyphenmin"); + print_int(nest[p].pg_field / 020000000); + print_char(','); + print_int((nest[p].pg_field / 0200000) % 0100); + print_char(')'); + } +#endif + if (nest[p].ml_field < 0) + tprint(" (\\output routine)"); + if (p == 0) { + /* Show the status of the current page */ + if (page_head != page_tail) { + tprint_nl("### current page:"); + if (output_active) + tprint(" (held over for next output)"); + show_box(vlink(page_head)); + if (page_contents > empty) { + tprint_nl("total height "); + print_totals(); + tprint_nl(" goal height "); + print_scaled(page_goal); + r = vlink(page_ins_head); + while (r != page_ins_head) { + print_ln(); + tprint_esc("insert"); + t = subtype(r); + print_int(t); + tprint(" adds "); + if (count(t) == 1000) + t = height(r); + else + t = x_over_n(height(r), 1000) * count(t); + print_scaled(t); + if (type(r) == split_up_node) { + q = page_head; + t = 0; + do { + q = vlink(q); + if ((type(q) == ins_node) + && (subtype(q) == subtype(r))) + incr(t); + } while (q != broken_ins(r)); + tprint(", #"); + print_int(t); + tprint(" might split"); + } + r = vlink(r); + } + } + } + if (vlink(contrib_head) != null) + tprint_nl("### recent contributions:"); + } + show_box(vlink(nest[p].head_field)); + /* Show the auxiliary field, |a| */ + switch (abs(m) / (max_command_cmd + 1)) { + case 0: + tprint_nl("prevdepth "); + if (nest[p].prev_depth_field <= pdf_ignored_dimen) + tprint("ignored"); + else + print_scaled(nest[p].prev_depth_field); + if (nest[p].pg_field != 0) { + tprint(", prevgraf "); + print_int(nest[p].pg_field); + if (nest[p].pg_field != 1) + tprint(" lines"); + else + tprint(" line"); + } + break; + case 1: + tprint_nl("spacefactor "); + print_int(nest[p].space_factor_field); + /* we dont do this any more, this was aux.rh originally */ +#if 0 + if (m > 0) { + if (nest[p].current_language_field > 0) { + tprint(", current language "); + print_int(nest[p].current_language_field); + } + } +#endif + break; + case 2: + if (nest[p].incompleat_noad_field != null) { + tprint("this will be denominator of:"); + show_box(nest[p].incompleat_noad_field); + } + } /* there are no other cases */ + + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/packaging.h b/Build/source/texk/web2c/luatexdir/tex/packaging.h new file mode 100644 index 00000000000..af1f18a265f --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/packaging.h @@ -0,0 +1,150 @@ +/* nesting.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: packaging.h 3261 2009-12-18 11:38:21Z taco $ */ + +#ifndef PACKAGING_H +# define PACKAGING_H + +/* We define some constants used when calling |hpack| to deal with font expansion. */ + +typedef enum { + exactly = 0, /*a box dimension is pre-specified */ + additional, /*a box dimension is increased from the natural one */ + cal_expand_ratio, /* calculate amount for font expansion after breaking + paragraph into lines */ + subst_ex_font /* substitute fonts */ +} hpack_subtypes; + +# define substituted 3 /* |subtype| of kern nodes that should be substituted */ + +extern void scan_spec(group_code c); +extern void scan_full_spec(group_code c, int spec_direction); + +extern scaled total_stretch[5]; +extern scaled total_shrink[5]; /* glue found by |hpack| or |vpack| */ +extern int last_badness; /* badness of the most recently packaged box */ +extern halfword adjust_tail; /* tail of adjustment list */ +extern halfword pre_adjust_tail; +extern int font_expand_ratio; /* current expansion ratio */ +extern halfword last_leftmost_char; +extern halfword last_rightmost_char; +extern halfword next_char_p; /* pointer to the next char of an implicit kern */ +extern halfword prev_char_p; /* pointer to the previous char of an implicit kern */ + +extern void set_prev_char_p(halfword p); +extern scaled char_stretch(halfword p); +extern scaled char_shrink(halfword p); +extern scaled kern_stretch(halfword p); +extern scaled kern_shrink(halfword p); +extern void do_subst_font(halfword p, int ex_ratio); +extern scaled char_pw(halfword p, int side); +extern halfword new_margin_kern(scaled w, halfword p, int side); + +# define update_adjust_list(A) do { \ + vlink(A) = adjust_ptr(p); \ + while (vlink(A) != null) \ + A = vlink(A); \ + } while (0) + +extern halfword hpack(halfword p, scaled w, int m, int d); +extern halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, + int grp, int d); + +extern scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, + int g_sign, int g_order, int d); + +extern int pack_begin_line; + +# define vpack(A,B,C,D) vpackage(A,B,C,max_dimen,D) /* special case of unconstrained depth */ + +extern halfword vpackage(halfword p, scaled h, int m, scaled l, int d); +extern halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, + int grp, int d); +extern void finish_vcenter(void); +extern void package(int c); +extern void append_to_vlist(halfword b); + +extern halfword prune_page_top(halfword p, boolean s); +extern scaled active_height[10]; /* distance from first active node to~|cur_p| */ + +# define cur_height active_height[1] /* the natural height */ +# define awful_bad 07777777777 /* more than a billion demerits */ +# define deplorable 100000 /* more than |inf_bad|, but less than |awful_bad| */ + +extern scaled best_height_plus_depth; /* height of the best box, without stretching or shrinking */ + +extern halfword vert_break(halfword p, scaled h, scaled d); +extern halfword vsplit(halfword n, scaled h); /* extracts a page of height |h| from box |n| */ + +# define box_code 0 /* |chr_code| for `\.{\\box}' */ +# define copy_code 1 /* |chr_code| for `\.{\\copy}' */ +# define last_box_code 2 /* |chr_code| for `\.{\\lastbox}' */ +# define vsplit_code 3 /* |chr_code| for `\.{\\vsplit}' */ +# define vtop_code 4 /* |chr_code| for `\.{\\vtop}' */ + +# define tail_page_disc disc_ptr[copy_code] /* last item removed by page builder */ +# define page_disc disc_ptr[last_box_code] /* first item removed by page builder */ +# define split_disc disc_ptr[vsplit_code] /* first item removed by \.{\\vsplit} */ + +extern halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */ + +/* +Now let's turn to the question of how \.{\\hbox} is treated. We actually +need to consider also a slightly larger context, since constructions like +`\.{\\setbox3=}\penalty0\.{\\hbox...}' and +`\.{\\leaders}\penalty0\.{\\hbox...}' and +`\.{\\lower3.8pt\\hbox...}' +are supposed to invoke quite +different actions after the box has been packaged. Conversely, +constructions like `\.{\\setbox3=}' can be followed by a variety of +different kinds of boxes, and we would like to encode such things in an +efficient way. + +In other words, there are two problems: To represent the context of a box, +and to represent its type. + +The first problem is solved by putting a ``context code'' on the |save_stack|, +just below the two entries that give the dimensions produced by |scan_spec|. +The context code is either a (signed) shift amount, or it is a large +integer |>=box_flag|, where |box_flag=@t$2^{30}$@>|. Codes |box_flag| through +|box_flag+biggest_reg| represent `\.{\\setbox0}' through +`\.{\\setbox}|biggest_reg|'; codes |box_flag+biggest_reg+1| through +|box_flag+2*biggest_reg| represent `\.{\\global\\setbox0}' +through `\.{\\global\\setbox}|biggest_reg|'; code |box_flag+2*number_regs| +represents `\.{\\shipout}'; and codes |box_flag+2*number_regs+1| +through |box_flag+2*number_regs+3| represent `\.{\\leaders}', `\.{\\cleaders}', +and `\.{\\xleaders}'. + +The second problem is solved by giving the command code |make_box| to all +control sequences that produce a box, and by using the following |chr_code| +values to distinguish between them: |box_code|, |copy_code|, |last_box_code|, +|vsplit_code|, |vtop_code|, |vtop_code+vmode|, and |vtop_code+hmode|, +where the latter two are used denote \.{\\vbox} and \.{\\hbox}, respectively. +*/ + +# define box_flag 010000000000 /* context code for `\.{\\setbox0}' */ +# define global_box_flag (box_flag+number_regs) /* context code for `\.{\\global\\setbox0}' */ +# define max_global_box_flag (global_box_flag+number_regs) +# define ship_out_flag (max_global_box_flag+1) /* context code for `\.{\\shipout}' */ +# define leader_flag ship_out_flag+1 /* context code for `\.{\\leaders}' */ + +extern void begin_box(int box_context); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/packaging.w b/Build/source/texk/web2c/luatexdir/tex/packaging.w new file mode 100644 index 00000000000..16c030a5944 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/packaging.w @@ -0,0 +1,1789 @@ +% packaging.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: packaging.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/packaging.w $"; + +@ @c +#define scan_normal_dimen() scan_dimen(false,false,false) + +#define prev_depth cur_list.prev_depth_field +#define space_factor cur_list.space_factor_field +#define box(A) eqtb[box_base+(A)].hh.rh + +#define text_direction int_par(text_direction_code) +#define body_direction int_par(body_direction_code) +#define every_hbox equiv(every_hbox_loc) +#define every_vbox equiv(every_vbox_loc) +#define box_max_depth dimen_par(box_max_depth_code) + + +@ We're essentially done with the parts of \TeX\ that are concerned with +the input (|get_next|) and the output (|ship_out|). So it's time to +get heavily into the remaining part, which does the real work of typesetting. + +After lists are constructed, \TeX\ wraps them up and puts them into boxes. +Two major subroutines are given the responsibility for this task: |hpack| +applies to horizontal lists (hlists) and |vpack| applies to vertical lists +(vlists). The main duty of |hpack| and |vpack| is to compute the dimensions +of the resulting boxes, and to adjust the glue if one of those dimensions +is pre-specified. The computed sizes normally enclose all of the material +inside the new box; but some items may stick out if negative glue is used, +if the box is overfull, or if a \.{\\vbox} includes other boxes that have +been shifted left. + +The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node| +for a box containing the hlist that starts at |p|. Parameter |w| specifies +a width; and parameter |m| is either `|exactly|' or `|additional|'. Thus, +|hpack(p,w,exactly)| produces a box whose width is exactly |w|, while +|hpack(p,w,additional)| yields a box whose width is the natural width plus +|w|. It is convenient to define a macro called `|natural|' to cover the +most common case, so that we can say |hpack(p,natural)| to get a box that +has the natural width of list |p|. + +Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a +box containing the vlist that starts at |p|. In this case |w| represents +a height instead of a width; the parameter |m| is interpreted as in |hpack|. + + +@ The parameters to |hpack| and |vpack| correspond to \TeX's primitives +like `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note +that `\.{\\hbox}' with no dimension following it is equivalent to +`\.{\\hbox} \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such +constructions in the user's input, including the mandatory left brace that +follows them, and it puts the specification onto |save_stack| so that the +desired box can later be obtained by executing the following code: +$$\vbox{\halign{#\hfil\cr +|save_ptr:=save_ptr-1;|\cr +|hpack(p,saved_value(0),saved_level(0)).|\cr}}$$ + +@c +void scan_spec(group_code c) +{ /* scans a box specification and left brace */ + int spec_code; + if (scan_keyword("to")) { + spec_code = exactly; + scan_normal_dimen(); + } else if (scan_keyword("spread")) { + spec_code = additional; + scan_normal_dimen(); + } else { + spec_code = additional; + cur_val = 0; + } + set_saved_record(0, saved_boxspec, spec_code, cur_val); + save_ptr++; + new_save_level(c); + scan_left_brace(); +} + + +@ When scanning, special care is necessary to ensure that the special +|save_stack| codes are placed just below the new group code, because +scanning can change |save_stack| when \.{\\csname} appears. + +This coincides with the text on |dir| and |attr| keywords, as these +are exaclty the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the +input stream (the others are \.{\\vcenter}, \.{\\valign}, and +\.{\\halign}). + +@c +void scan_full_spec(group_code c, int spec_direction) +{ /* scans a box specification and left brace */ + int s; /* temporarily saved value */ + int i; + int v; + int spec_code; + halfword attr_list; + s = 0; + if (attr_list_cache == cache_disabled) + update_attribute_cache(); + attr_list = attr_list_cache; + assert(saved_type(0) == saved_boxcontext); + s = saved_value(0); /* the box context */ + CONTINUE: + while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) { + get_x_token(); + if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd) + back_input(); + } + if (scan_keyword("attr")) { + scan_register_num(); + i = cur_val; + scan_optional_equals(); + scan_int(); + v = cur_val; + if ((attr_list != null) && (attr_list == attr_list_cache)) { + attr_list = copy_attribute_list(attr_list_cache); + add_node_attr_ref(attr_list); /* will be used once */ + } + attr_list = do_set_attribute(attr_list, i, v); + goto CONTINUE; + } + if (scan_keyword("dir")) { + scan_direction(); + spec_direction = cur_val; + goto CONTINUE; + } + if (attr_list == attr_list_cache) { + add_node_attr_ref(attr_list); + } + if (scan_keyword("to")) { + spec_code = exactly; + } else if (scan_keyword("spread")) { + spec_code = additional; + } else { + spec_code = additional; + cur_val = 0; + goto FOUND; + } + scan_normal_dimen(); + FOUND: + set_saved_record(0, saved_boxcontext, 0, s); + set_saved_record(1, saved_boxspec, spec_code, cur_val); + /* DIR: Adjust |text_dir_ptr| for |scan_spec| */ + if (spec_direction != -1) { + set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr); + text_dir_ptr = new_dir(spec_direction); + } else { + set_saved_record(2, saved_boxdir, spec_direction, null); + } + set_saved_record(3, saved_boxattr, 0, attr_list); + save_ptr += 4; + new_save_level(c); + scan_left_brace(); + eq_word_define(int_base + body_direction_code, spec_direction); + eq_word_define(int_base + par_direction_code, spec_direction); + eq_word_define(int_base + text_direction_code, spec_direction); +} + + +@ To figure out the glue setting, |hpack| and |vpack| determine how much +stretchability and shrinkability are present, considering all four orders +of infinity. The highest order of infinity that has a nonzero coefficient +is then used as if no other orders were present. + +For example, suppose that the given list contains six glue nodes with +the respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill. +Then the total is essentially 2fil; and if a total additional space of 6pt +is to be achieved by stretching, the actual amounts of stretch will be +0pt, 0pt, 15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be +considered. (The `fill' glue is therefore not really stretching infinitely +with respect to `fil'; nobody would actually want that to happen.) + +The arrays |total_stretch| and |total_shrink| are used to determine how much +glue of each kind is present. A global variable |last_badness| is used +to implement \.{\\badness}. + +@c +scaled total_stretch[5]; +scaled total_shrink[5]; /* glue found by |hpack| or |vpack| */ +int last_badness; /* badness of the most recently packaged box */ + + +@ If the global variable |adjust_tail| is non-null, the |hpack| routine +also removes all occurrences of |ins_node|, |mark_node|, and |adjust_node| +items and appends the resulting material onto the list that ends at +location |adjust_tail|. + +@c +halfword adjust_tail; /* tail of adjustment list */ + + +@ Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to +|pre_adjust_tail| instead of |adjust_tail|. + +@c +halfword pre_adjust_tail; + +int font_expand_ratio; /* current expansion ratio */ +halfword last_leftmost_char; +halfword last_rightmost_char; + +halfword next_char_p; /* pointer to the next char of an implicit kern */ +halfword prev_char_p; /* pointer to the previous char of an implicit kern */ + +@ This procedure is called repeatedly from inside the line break algorithm. +@c +void set_prev_char_p(halfword p) +{ + prev_char_p = p; +} + +@ @c +scaled char_stretch(halfword p) +{ + internal_font_number k; + scaled dw; + int ef; + internal_font_number f; + int c; + f = font(p); + c = character(p); + k = font_stretch(f); + ef = get_ef_code(f, c); + if ((k != null_font) && (ef > 0)) { + dw = char_width(k, c) - char_width(f, c); + if (dw > 0) + return round_xn_over_d(dw, ef, 1000); + } + return 0; +} + +@ @c +scaled char_shrink(halfword p) +{ + internal_font_number k; + scaled dw; + int ef; + internal_font_number f; + int c; + f = font(p); + c = character(p); + k = font_shrink(f); + ef = get_ef_code(f, c); + if ((k != null_font) && (ef > 0)) { + dw = char_width(f, c) - char_width(k, c); + if (dw > 0) + return round_xn_over_d(dw, ef, 1000); + } + return 0; +} + +@ @c +scaled kern_stretch(halfword p) +{ + halfword l, r; + scaled d; + if ((prev_char_p == null) || (vlink(prev_char_p) != p) + || (vlink(p) == null)) + return 0; + l = prev_char_p; + r = vlink(p); + if (!((is_char_node(l) && is_char_node(r) && + (font(l) == font(r)) && (font_stretch(font(l)) != null_font)))) + return 0; + d = get_kern(font_stretch(font(l)), character(l), character(r)); + return round_xn_over_d(d - width(p), + get_ef_code(font(l), character(l)), 1000); +} + +@ @c +scaled kern_shrink(halfword p) +{ + halfword l, r; + scaled d; + if ((prev_char_p == null) || (vlink(prev_char_p) != p) + || (vlink(p) == null)) + return 0; + l = prev_char_p; + r = vlink(p); + if (!((is_char_node(l) && is_char_node(r) && + (font(l) == font(r)) && (font_shrink(font(l)) != null_font)))) + return 0; + d = get_kern(font_shrink(font(l)), character(l), character(r)); + return round_xn_over_d(width(p) - d, + get_ef_code(font(l), character(l)), 1000); +} + +@ @c +void do_subst_font(halfword p, int ex_ratio) +{ + internal_font_number f, k; + halfword r; + int ef; + if (type(p) == disc_node) { + r = vlink(pre_break(p)); + while (r != null) { + if (is_char_node(r)) + do_subst_font(r, ex_ratio); + r = vlink(r); + } + r = vlink(post_break(p)); + while (r != null) { + if (is_char_node(r)) + do_subst_font(r, ex_ratio); + r = vlink(r); + } + r = vlink(no_break(p)); + while (r != null) { + if (is_char_node(r)) + do_subst_font(r, ex_ratio); + r = vlink(r); + } + return; + } + if (is_char_node(p)) { + r = p; + } else { + pdf_error("font expansion", "invalid node type"); + return; + } + f = font(r); + ef = get_ef_code(f, character(r)); + if (ef == 0) + return; + if ((font_expand_ratio(f) == 0) && + (font_stretch(f) != null_font) && (ex_ratio > 0)) { + k = expand_font(f, + ext_xn_over_d(ex_ratio * ef, + font_expand_ratio(font_stretch(f)), + 1000000)); + } else if ((font_expand_ratio(f) == 0) + && (font_shrink(f) != null_font) && (ex_ratio < 0)) { + k = expand_font(f, + ext_xn_over_d(ex_ratio * ef, + -font_expand_ratio(font_shrink(f)), + 1000000)); + } else { + k = f; + } + if (k != f) { + font(r) = k; + if (!is_char_node(p)) { /* todo: this should be: |if(is_ligature())| */ + r = lig_ptr(p); + while (r != null) { + font(r) = k; + r = vlink(r); + } + } + } +} + +@ @c +scaled char_pw(halfword p, int side) +{ + internal_font_number f; + int c; + if (side == left_side) + last_leftmost_char = null; + else + last_rightmost_char = null; + if (p == null) + return 0; + if (!is_char_node(p)) + return 0; + f = font(p); + if (side == left_side) { + c = get_lp_code(f, character(p)); + last_leftmost_char = p; + } else { + c = get_rp_code(f, character(p)); + last_rightmost_char = p; + } + if (c == 0) + return 0; + return round_xn_over_d(quad(f), c, 1000); +} + +@ @c +halfword new_margin_kern(scaled w, halfword p, int side) +{ + halfword k, q; + k = new_node(margin_kern_node, side); + width(k) = w; + if (p == null) + pdf_error("margin kerning", "invalid pointer to marginal char node"); + q = new_char(font(p), character(p)); + margin_char(k) = q; + return k; +} + + +@ Here is |hpack|, which is place where we do font substituting when +font expansion is being used. + +@c +halfword hpack(halfword p, scaled w, int m, int pack_direction) +{ + halfword r; /* the box node that will be returned */ + halfword q; /* trails behind |p| */ + scaled h, d, x; /* height, depth, and natural width */ + scaled_whd whd; + scaled s; /* shift amount */ + halfword g; /* points to a glue specification */ + int o; /* order of infinity */ + internal_font_number f; /* the font in a |char_node| */ + halfword dir_ptr; /* for managing the direction stack */ + /* BEWARE: this shadows a global |dir_ptr| */ + int hpack_dir; /* the current direction */ + int disc_level; + halfword pack_interrupt[8]; + scaled font_stretch = 0; + scaled font_shrink = 0; + scaled k = 0; + last_badness = 0; + r = new_node(hlist_node, min_quarterword); + if (pack_direction == -1) { + box_dir(r) = text_direction; + } else { + box_dir(r) = pack_direction; + pack_direction = -1; + } + hpack_dir = box_dir(r); + dir_ptr = null; + push_dir(hpack_dir); + q = r + list_offset; + vlink(q) = p; + if (m == cal_expand_ratio) { + prev_char_p = null; + font_stretch = 0; + font_shrink = 0; + font_expand_ratio = 0; + } + h = 0; + /* Clear dimensions to zero */ + d = 0; + x = 0; + total_stretch[normal] = 0; + total_shrink[normal] = 0; + total_stretch[sfi] = 0; + total_shrink[sfi] = 0; + total_stretch[fil] = 0; + total_shrink[fil] = 0; + total_stretch[fill] = 0; + total_shrink[fill] = 0; + total_stretch[filll] = 0; + total_shrink[filll] = 0; + + disc_level = 0; + RESWITCH: + while ((p != null) || (disc_level > 0)) { + if (p == null) { + decr(disc_level); + p = pack_interrupt[disc_level]; + goto RESWITCH; + } + /* Examine node |p| in the hlist, taking account of its effect + on the dimensions of the new box, or moving it to the adjustment list; + then advance |p| to the next node */ + while (is_char_node(p)) { + /* Incorporate character dimensions into the dimensions of + the hbox that will contain~it, then move to the next node */ + /* The following code is part of \TeX's inner loop; i.e., adding another + character of text to the user's input will cause each of these instructions + to be exercised one more time. + */ + if (m >= cal_expand_ratio) { + prev_char_p = p; + if (m == cal_expand_ratio) { + font_stretch += char_stretch(p); + font_shrink += char_shrink(p); + } else if (m == subst_ex_font) { + do_subst_font(p, font_expand_ratio); + } + } + f = font(p); + whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true); + x += whd.wd; + if (whd.ht > h) + h = whd.ht; + if (whd.dp > d) + d = whd.dp; + p = vlink(p); + } + if (p != null) { + switch (type(p)) { + case hlist_node: + case vlist_node: + /* Incorporate box dimensions into the dimensions of the hbox that will contain~it */ + /* The code here implicitly uses the fact that running dimensions are + indicated by |null_flag|, which will be ignored in the calculations + because it is a highly negative number. */ + s = shift_amount(p); + whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false); + x += whd.wd; + if (whd.ht - s > h) + h = whd.ht - s; + if (whd.dp + s > d) + d = whd.dp + s; + break; + case rule_node: + case unset_node: + x += width(p); + if (type(p) >= rule_node) + s = 0; + else + s = shift_amount(p); + if (height(p) - s > h) + h = height(p) - s; + if (depth(p) + s > d) + d = depth(p) + s; + break; + case ins_node: + case mark_node: + case adjust_node: + if (adjust_tail != null) { + /* Transfer node |p| to the adjustment list */ + /* + Although node |q| is not necessarily the immediate predecessor of node |p|, + it always points to some node in the list preceding |p|. Thus, we can delete + nodes by moving |q| when necessary. The algorithm takes linear time, and the + extra computation does not intrude on the inner loop unless it is necessary + to make a deletion. + */ + while (vlink(q) != p) + q = vlink(q); + if (type(p) == adjust_node) { + if (adjust_pre(p) != 0) + update_adjust_list(pre_adjust_tail); + else + update_adjust_list(adjust_tail); + p = vlink(p); + adjust_ptr(vlink(q)) = null; + flush_node(vlink(q)); + } else { + vlink(adjust_tail) = p; + adjust_tail = p; + p = vlink(p); + } + vlink(q) = p; + p = q; + } + break; + case whatsit_node: + /* Incorporate a whatsit node into an hbox */ + if (subtype(p) == dir_node) { + /* DIR: Adjust the dir stack for the |hpack| routine */ + if (dir_dir(p) >= 0) { + hpack_dir = dir_dir(p); + push_dir_node(p); + } else { + pop_dir_node(); + if (dir_ptr != null) + hpack_dir = dir_dir(dir_ptr); + } + + } else { + if ((subtype(p) == pdf_refxform_node) + || (subtype(p) == pdf_refximage_node)) { + x += width(p); + s = 0; + if (height(p) - s > h) + h = height(p) - s; + if (depth(p) + s > d) + d = depth(p) + s; + } + } + break; + case glue_node: + /* Incorporate glue into the horizontal totals */ + g = glue_ptr(p); + x += width(g); + o = stretch_order(g); + total_stretch[o] = total_stretch[o] + stretch(g); + o = shrink_order(g); + total_shrink[o] = total_shrink[o] + shrink(g); + if (subtype(p) >= a_leaders) { + g = leader_ptr(p); + if (height(g) > h) + h = height(g); + if (depth(g) > d) + d = depth(g); + } + break; + case margin_kern_node: + if (m == cal_expand_ratio) { + f = font(margin_char(p)); + do_subst_font(margin_char(p), 1000); + if (f != font(margin_char(p))) + font_stretch = + font_stretch - width(p) - char_pw(margin_char(p), + subtype(p)); + font(margin_char(p)) = f; + do_subst_font(margin_char(p), -1000); + if (f != font(margin_char(p))) + font_shrink = + font_shrink - width(p) - char_pw(margin_char(p), + subtype(p)); + font(margin_char(p)) = f; + } else if (m == subst_ex_font) { + do_subst_font(margin_char(p), font_expand_ratio); + width(p) = -char_pw(margin_char(p), subtype(p)); + } + x += width(p); + break; + case kern_node: + if (subtype(p) == normal) { + if (m == cal_expand_ratio) { + font_stretch = font_stretch + kern_stretch(p); + font_shrink = font_shrink + kern_shrink(p); + } else if (m == subst_ex_font) { + if (font_expand_ratio > 0) + k = kern_stretch(p); + else if (font_expand_ratio < 0) + k = kern_shrink(p); + else + pdfassert(0); + if (k != 0) + width(p) = get_kern(font(prev_char_p), + character(prev_char_p), + character(vlink(p))); + } + } + x += width(p); + break; + case math_node: + x += surround(p); + break; + case disc_node: + if (m == subst_ex_font) + do_subst_font(p, font_expand_ratio); + if ((subtype(p) != select_disc) && (vlink(no_break(p)) != null)) { + pack_interrupt[disc_level] = vlink(p); + incr(disc_level); + p = no_break(p); + } + break; + default: + break; + } + p = vlink(p); + } + + } + + if (adjust_tail != null) + vlink(adjust_tail) = null; + if (pre_adjust_tail != null) + vlink(pre_adjust_tail) = null; + height(r) = h; + depth(r) = d; + /* Determine the value of |width(r)| and the appropriate glue setting; + then |return| or |goto common_ending| */ + /* When we get to the present part of the program, |x| is the natural width + of the box being packaged. */ + if (m == additional) + w = x + w; + width(r) = w; + x = w - x; /* now |x| is the excess to be made up */ + if (x == 0) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + goto EXIT; + } else if (x > 0) { + /* Determine horizontal glue stretch setting, then |return| + or \hbox{|goto common_ending|} */ + + /* If |hpack| is called with |m=cal_expand_ratio| we calculate + |font_expand_ratio| and return without checking for overfull or underfull box. */ + + /* Determine the stretch order */ + if (total_stretch[filll] != 0) + o = filll; + else if (total_stretch[fill] != 0) + o = fill; + else if (total_stretch[fil] != 0) + o = fil; + else if (total_stretch[sfi] != 0) + o = sfi; + else + o = normal; + + if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) { + font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0); + goto EXIT; + } + glue_order(r) = (quarterword) o; + glue_sign(r) = stretching; + if (total_stretch[o] != 0) { + glue_set(r) = unfloat((double) x / total_stretch[o]); + } else { + glue_sign(r) = normal; + set_glue_ratio_zero(glue_set(r)); /* there's nothing to stretch */ + } + if (o == normal) { + if (list_ptr(r) != null) { + /* Report an underfull hbox and |goto common_ending|, if this box + is sufficiently bad */ + last_badness = badness(x, total_stretch[normal]); + if (last_badness > int_par(hbadness_code)) { + print_ln(); + if (last_badness > 100) + tprint_nl("Underfull \\hbox (badness "); + else + tprint_nl("Loose \\hbox (badness "); + print_int(last_badness); + goto COMMON_ENDING; + } + } + } + goto EXIT; + } else { + /* Determine horizontal glue shrink setting, then |return| + or \hbox{|goto common_ending|} */ + /* Determine the shrink order */ + if (total_shrink[filll] != 0) + o = filll; + else if (total_shrink[fill] != 0) + o = fill; + else if (total_shrink[fil] != 0) + o = fil; + else if (total_shrink[sfi] != 0) + o = sfi; + else + o = normal; + + if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) { + font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0); + goto EXIT; + } + glue_order(r) = (quarterword) o; + glue_sign(r) = shrinking; + if (total_shrink[o] != 0) { + glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]); + } else { + glue_sign(r) = normal; + set_glue_ratio_zero(glue_set(r)); /* there's nothing to shrink */ + } + if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) { + last_badness = 1000000; + set_glue_ratio_one(glue_set(r)); /* use the maximum shrinkage */ + /* Report an overfull hbox and |goto common_ending|, if this box + is sufficiently bad */ + if ((-x - total_shrink[normal] > dimen_par(hfuzz_code)) + || (int_par(hbadness_code) < 100)) { + if ((dimen_par(overfull_rule_code) > 0) + && (-x - total_shrink[normal] > dimen_par(hfuzz_code))) { + while (vlink(q) != null) + q = vlink(q); + vlink(q) = new_rule(); + rule_dir(vlink(q)) = box_dir(r); + width(vlink(q)) = dimen_par(overfull_rule_code); + } + print_ln(); + tprint_nl("Overfull \\hbox ("); + print_scaled(-x - total_shrink[normal]); + tprint("pt too wide"); + goto COMMON_ENDING; + } + } else if (o == normal) { + if (list_ptr(r) != null) { + /* Report a tight hbox and |goto common_ending|, if this box is sufficiently bad */ + last_badness = badness(-x, total_shrink[normal]); + if (last_badness > int_par(hbadness_code)) { + print_ln(); + tprint_nl("Tight \\hbox (badness "); + print_int(last_badness); + goto COMMON_ENDING; + } + } + } + goto EXIT; + } + + COMMON_ENDING: + /* Finish issuing a diagnostic message for an overfull or underfull hbox */ + if (output_active) { + tprint(") has occurred while \\output is active"); + } else { + if (pack_begin_line != 0) { + if (pack_begin_line > 0) + tprint(") in paragraph at lines "); + else + tprint(") in alignment at lines "); + print_int(abs(pack_begin_line)); + tprint("--"); + } else { + tprint(") detected at line "); + } + print_int(line); + } + + print_ln(); + font_in_short_display = null_font; + short_display(list_ptr(r)); + print_ln(); + begin_diagnostic(); + show_box(r); + end_diagnostic(true); + + EXIT: + if ((m == cal_expand_ratio) && (font_expand_ratio != 0)) { + font_expand_ratio = fix_int(font_expand_ratio, -1000, 1000); + q = list_ptr(r); + list_ptr(r) = null; + flush_node(r); + r = hpack(q, w, subst_ex_font, hpack_dir); + } + while (dir_ptr != null) + pop_dir_node(); + return r; +} + +@ @c +halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, + int pac) +{ + halfword q; + new_hyphenation(p, qt); + (void) new_ligkern(p, qt); /* don't care about the tail in this case */ + q = vlink(p); + q = lua_hpack_filter(q, w, m, grp, pac); + return hpack(q, w, m, pac); +} + +@ here is a function to calculate the natural whd of a (horizontal) node list + +@c +scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, + int g_sign, int g_order, int pack_direction) +{ + scaled s; /* shift amount */ + halfword g; /* points to a glue specification */ + internal_font_number f; /* the font in a |char_node| */ + int hpack_dir; + scaled_whd xx; /* for recursion */ + scaled_whd whd, siz = { 0, 0, 0 }; + if (pack_direction == -1) { + hpack_dir = text_direction; + } else { + hpack_dir = pack_direction; + } + while (p != pp && p != null) { + while (is_char_node(p) && p != pp) { + f = font(p); + whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true); + siz.wd += whd.wd; + if (whd.ht > siz.ht) + siz.ht = whd.ht; + if (whd.dp > siz.dp) + siz.dp = whd.dp; + p = vlink(p); + } + if (p != pp && p != null) { + switch (type(p)) { + case hlist_node: + case vlist_node: + s = shift_amount(p); + whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false); + siz.wd += whd.wd; + if (whd.ht - s > siz.ht) + siz.ht = whd.ht - s; + if (whd.dp + s > siz.dp) + siz.dp = whd.dp + s; + break; + case rule_node: + case unset_node: + siz.wd += width(p); + if (type(p) >= rule_node) + s = 0; + else + s = shift_amount(p); + if (height(p) - s > siz.ht) + siz.ht = height(p) - s; + if (depth(p) + s > siz.dp) + siz.dp = depth(p) + s; + break; + case whatsit_node: + if ((subtype(p) == pdf_refxform_node) + || (subtype(p) == pdf_refximage_node)) { + siz.wd += width(p); + s = 0; + if (height(p) - s > siz.ht) + siz.ht = height(p) - s; + if (depth(p) + s > siz.dp) + siz.dp = depth(p) + s; + } + break; + case glue_node: + g = glue_ptr(p); + siz.wd += width(g); + if (g_sign != normal) { + if (g_sign == stretching) { + if (stretch_order(g) == g_order) { + siz.wd += + float_round(float_cast(g_mult) * + float_cast(stretch(g))); + } + } else if (shrink_order(g) == g_order) { + siz.wd -= + float_round(float_cast(g_mult) * + float_cast(shrink(g))); + } + } + if (subtype(p) >= a_leaders) { + g = leader_ptr(p); + if (height(g) > siz.ht) + siz.ht = height(g); + if (depth(g) > siz.dp) + siz.dp = depth(g); + } + break; + case margin_kern_node: + case kern_node: + siz.wd += width(p); + break; + case math_node: + siz.wd += surround(p); + break; + case disc_node: + xx = natural_sizes(no_break(p), null, g_mult, g_sign, g_order, + hpack_dir); + siz.wd += xx.wd; + if (xx.ht > siz.ht) + siz.ht = xx.ht; + if (xx.dp > siz.dp) + siz.dp = xx.dp; + break; + default: + break; + } + p = vlink(p); + } + + } + return siz; +} + + +@ In order to provide a decent indication of where an overfull or underfull +box originated, we use a global variable |pack_begin_line| that is +set nonzero only when |hpack| is being called by the paragraph builder +or the alignment finishing routine. + +@c +int pack_begin_line; /* source file line where the current paragraph + or alignment began; a negative value denotes alignment */ + + +@ The |vpack| subroutine is actually a special case of a slightly more +general routine called |vpackage|, which has four parameters. The fourth +parameter, which is |max_dimen| in the case of |vpack|, specifies the +maximum depth of the page box that is constructed. The depth is first +computed by the normal rules; if it exceeds this limit, the reference +point is simply moved down until the limiting depth is attained. + +@c +halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) +{ + halfword r; /* the box node that will be returned */ + scaled w, d, x; /* width, depth, and natural height */ + scaled_whd whd; + scaled s; /* shift amount */ + halfword g; /* points to a glue specification */ + int o; /* order of infinity */ + last_badness = 0; + r = new_node(vlist_node, 0); + if (pack_direction == -1) { + box_dir(r) = body_direction; + } else { + box_dir(r) = pack_direction; + } + subtype(r) = min_quarterword; + shift_amount(r) = 0; + list_ptr(r) = p; + w = 0; + /* Clear dimensions to zero */ + d = 0; + x = 0; + total_stretch[normal] = 0; + total_shrink[normal] = 0; + total_stretch[sfi] = 0; + total_shrink[sfi] = 0; + total_stretch[fil] = 0; + total_shrink[fil] = 0; + total_stretch[fill] = 0; + total_shrink[fill] = 0; + total_stretch[filll] = 0; + total_shrink[filll] = 0; + + while (p != null) { + /* Examine node |p| in the vlist, taking account of its effect + on the dimensions of the new box; then advance |p| to the next node */ + if (is_char_node(p)) { + confusion("vpack"); + } else { + switch (type(p)) { + case hlist_node: + case vlist_node: + /* Incorporate box dimensions into the dimensions of + the vbox that will contain~it */ + s = shift_amount(p); + whd = pack_width_height_depth(box_dir(r), box_dir(p), p, false); + if (whd.wd + s > w) + w = whd.wd + s; + x += d + whd.ht; + d = whd.dp; + break; + case rule_node: + case unset_node: + x += d + height(p); + d = depth(p); + if (type(p) >= rule_node) + s = 0; + else + s = shift_amount(p); + if (width(p) + s > w) + w = width(p) + s; + break; + case whatsit_node: + /* Incorporate a whatsit node into a vbox */ + if ((subtype(p) == pdf_refxform_node) + || (subtype(p) == pdf_refximage_node)) { + x += d + height(p); + d = depth(p); + s = 0; + if (width(p) + s > w) + w = width(p) + s; + } + break; + case glue_node: + /* Incorporate glue into the vertical totals */ + x += d; + d = 0; + g = glue_ptr(p); + x += width(g); + o = stretch_order(g); + total_stretch[o] = total_stretch[o] + stretch(g); + o = shrink_order(g); + total_shrink[o] = total_shrink[o] + shrink(g); + if (subtype(p) >= a_leaders) { + g = leader_ptr(p); + if (width(g) > w) + w = width(g); + } + break; + case kern_node: + x += d + width(p); + d = 0; + break; + default: + break; + } + } + p = vlink(p); + } + width(r) = w; + if (d > l) { + x += d - l; + depth(r) = l; + } else { + depth(r) = d; + } + /* Determine the value of |height(r)| and the appropriate glue setting; + then |return| or |goto common_ending| */ + /* When we get to the present part of the program, |x| is the natural height + of the box being packaged. */ + if (m == additional) + h = x + h; + height(r) = h; + x = h - x; /* now |x| is the excess to be made up */ + if (x == 0) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + return r; + } else if (x > 0) { + /* Determine vertical glue stretch setting, then |return| + or \hbox{|goto common_ending|} */ + /* Determine the stretch order */ + if (total_stretch[filll] != 0) + o = filll; + else if (total_stretch[fill] != 0) + o = fill; + else if (total_stretch[fil] != 0) + o = fil; + else if (total_stretch[sfi] != 0) + o = sfi; + else + o = normal; + + glue_order(r) = (quarterword) o; + glue_sign(r) = stretching; + if (total_stretch[o] != 0) { + glue_set(r) = unfloat((double) x / total_stretch[o]); + } else { + glue_sign(r) = normal; + set_glue_ratio_zero(glue_set(r)); /* there's nothing to stretch */ + } + if (o == normal) { + if (list_ptr(r) != null) { + /* Report an underfull vbox and |goto common_ending|, if this box + is sufficiently bad */ + last_badness = badness(x, total_stretch[normal]); + if (last_badness > int_par(vbadness_code)) { + print_ln(); + if (last_badness > 100) + tprint_nl("Underfull \\vbox (badness "); + else + tprint_nl("Loose \\vbox (badness "); + print_int(last_badness); + goto COMMON_ENDING; + } + } + } + return r; + + } else { + /* Determine vertical glue shrink setting, then |return| + or \hbox{|goto common_ending|} */ + /* Determine the shrink order */ + if (total_shrink[filll] != 0) + o = filll; + else if (total_shrink[fill] != 0) + o = fill; + else if (total_shrink[fil] != 0) + o = fil; + else if (total_shrink[sfi] != 0) + o = sfi; + else + o = normal; + + glue_order(r) = (quarterword) o; + glue_sign(r) = shrinking; + if (total_shrink[o] != 0) { + glue_set(r) = unfloat((double) (-x) / total_shrink[o]); + } else { + glue_sign(r) = normal; + set_glue_ratio_zero(glue_set(r)); /* there's nothing to shrink */ + } + if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) { + last_badness = 1000000; + set_glue_ratio_one(glue_set(r)); /* use the maximum shrinkage */ + /* Report an overfull vbox and |goto common_ending|, if this box is sufficiently bad */ + if ((-x - total_shrink[normal] > dimen_par(vfuzz_code)) + || (int_par(vbadness_code) < 100)) { + print_ln(); + tprint_nl("Overfull \\vbox ("); + print_scaled(-x - total_shrink[normal]); + tprint("pt too high"); + goto COMMON_ENDING; + } + } else if (o == normal) { + if (list_ptr(r) != null) { + /* Report a tight vbox and |goto common_ending|, if this box is sufficiently bad */ + last_badness = badness(-x, total_shrink[normal]); + if (last_badness > int_par(vbadness_code)) { + print_ln(); + tprint_nl("Tight \\vbox (badness "); + print_int(last_badness); + goto COMMON_ENDING; + } + } + } + return r; + } + + COMMON_ENDING: + /* Finish issuing a diagnostic message or an overfull or underfull vbox */ + if (output_active) { + tprint(") has occurred while \\output is active"); + } else { + if (pack_begin_line != 0) { /* it's actually negative */ + tprint(") in alignment at lines "); + print_int(abs(pack_begin_line)); + tprint("--"); + } else { + tprint(") detected at line "); + } + print_int(line); + print_ln(); + } + begin_diagnostic(); + show_box(r); + end_diagnostic(true); + return r; +} + +@ @c +halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, + int pack_direction) +{ + halfword q; + q = p; + q = lua_vpack_filter(q, h, m, l, grp, pack_direction); + return vpackage(q, h, m, l, pack_direction); +} + +@ @c +void finish_vcenter(void) +{ + halfword p; + unsave(); + save_ptr--; + p = vpack(vlink(cur_list.head_field), saved_value(0), saved_level(0), -1); + pop_nest(); + p = math_vcenter_group(p); + tail_append(p); +} + +@ @c +void package(int c) +{ + scaled h; /* height of box */ + halfword p; /* first node in a box */ + scaled d; /* max depth */ + int grp; + grp = cur_group; + d = box_max_depth; + unsave(); + save_ptr -= 4; + if (cur_list.mode_field == -hmode) { + cur_box = filtered_hpack(cur_list.head_field, + cur_list.tail_field, saved_value(1), + saved_level(1), grp, saved_level(2)); + subtype(cur_box) = HLIST_SUBTYPE_HBOX; + } else { + cur_box = filtered_vpackage(vlink(cur_list.head_field), + saved_value(1), saved_level(1), d, grp, + saved_level(2)); + if (c == vtop_code) { + /* Readjust the height and depth of |cur_box|, for \.{\\vtop} */ + /* The height of a `\.{\\vtop}' box is inherited from the first item on its list, + if that item is an |hlist_node|, |vlist_node|, or |rule_node|; otherwise + the \.{\\vtop} height is zero. + */ + + h = 0; + p = list_ptr(cur_box); + if (p != null) + if (type(p) <= rule_node) + h = height(p); + depth(cur_box) = depth(cur_box) - h + height(cur_box); + height(cur_box) = h; + + } + } + if (saved_value(2) != null) { + /* DIR: Adjust back |text_dir_ptr| for |scan_spec| */ + flush_node_list(text_dir_ptr); + text_dir_ptr = saved_value(2); + + } + replace_attribute_list(cur_box, saved_value(3)); + pop_nest(); + box_end(saved_value(0)); +} + + + +@ When a box is being appended to the current vertical list, the +baselineskip calculation is handled by the |append_to_vlist| routine. + +@c +void append_to_vlist(halfword b) +{ + scaled d; /* deficiency of space between baselines */ + halfword p; /* a new glue node */ + if (prev_depth > dimen_par(pdf_ignored_dimen_code)) { + if ((type(b) == hlist_node) && is_mirrored(box_dir(b))) { + d = width(glue_par(baseline_skip_code)) - prev_depth - depth(b); + } else { + d = width(glue_par(baseline_skip_code)) - prev_depth - height(b); + } + if (d < dimen_par(line_skip_limit_code)) { + p = new_param_glue(line_skip_code); + } else { + p = new_skip_param(baseline_skip_code); + width(temp_ptr) = d; /* |temp_ptr=glue_ptr(p)| */ + } + couple_nodes(cur_list.tail_field, p); + cur_list.tail_field = p; + } + couple_nodes(cur_list.tail_field, b); + cur_list.tail_field = b; + if ((type(b) == hlist_node) && is_mirrored(box_dir(b))) + prev_depth = height(b); + else + prev_depth = depth(b); +} + + +@ When |saving_vdiscards| is positive then the glue, kern, and penalty +nodes removed by the page builder or by \.{\\vsplit} from the top of a +vertical list are saved in special lists instead of being discarded. + +@c +#define tail_page_disc disc_ptr[copy_code] /* last item removed by page builder */ +#define page_disc disc_ptr[last_box_code] /* first item removed by page builder */ +#define split_disc disc_ptr[vsplit_code] /* first item removed by \.{\\vsplit} */ + +halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */ + + +@ The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation, +is considerably simpler than |line_break| because it doesn't have to +worry about hyphenation, and because its mission is to discover a single +break instead of an optimum sequence of breakpoints. But before we get +into the details of |vsplit|, we need to consider a few more basic things. + +A subroutine called |prune_page_top| takes a pointer to a vlist and +returns a pointer to a modified vlist in which all glue, kern, and penalty nodes +have been deleted before the first box or rule node. However, the first +box or rule is actually preceded by a newly created glue node designed so that +the topmost baseline will be at distance |split_top_skip| from the top, +whenever this is possible without backspacing. + +When the second argument |s| is |false| the deleted nodes are destroyed, +otherwise they are collected in a list starting at |split_disc|. + +@c +halfword prune_page_top(halfword p, boolean s) +{ + halfword prev_p; /* lags one step behind |p| */ + halfword q, r; /* temporary variables for list manipulation */ + prev_p = temp_head; + vlink(temp_head) = p; + r = null; + while (p != null) { + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + /* Insert glue for |split_top_skip| and set~|p:=null| */ + q = new_skip_param(split_top_skip_code); + vlink(prev_p) = q; + vlink(q) = p; /* now |temp_ptr=glue_ptr(q)| */ + if (width(temp_ptr) > height(p)) + width(temp_ptr) = width(temp_ptr) - height(p); + else + width(temp_ptr) = 0; + p = null; + break; + case whatsit_node: + case mark_node: + case ins_node: + prev_p = p; + p = vlink(prev_p); + break; + case glue_node: + case kern_node: + case penalty_node: + q = p; + p = vlink(q); + vlink(q) = null; + vlink(prev_p) = p; + if (s) { + if (split_disc == null) + split_disc = q; + else + vlink(r) = q; + r = q; + } else { + flush_node_list(q); + } + break; + default: + confusion("pruning"); + break; + } + } + return vlink(temp_head); +} + + +@ The next subroutine finds the best place to break a given vertical list +so as to obtain a box of height~|h|, with maximum depth~|d|. +A pointer to the beginning of the vertical list is given, +and a pointer to the optimum breakpoint is returned. The list is effectively +followed by a forced break, i.e., a penalty node with the |eject_penalty|; +if the best break occurs at this artificial node, the value |null| is returned. + +@c +scaled active_height[10]; /* distance from first active node to~|cur_p| */ + + +@ An array of six |scaled| distances is used to keep track of the height +from the beginning of the list to the current place, just as in |line_break|. +In fact, we use one of the same arrays, only changing its name to reflect +its new significance. + +@c +#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7) +#define set_height_zero(A) active_height[A]=0 /* initialize the height to zero */ + + +@ A global variable |best_height_plus_depth| will be set to the natural size +of the box that corresponds to the optimum breakpoint found by |vert_break|. +(This value is used by the insertion-splitting algorithm of the page builder.) + +@c +scaled best_height_plus_depth; /* height of the best box, without stretching or shrinking */ + +halfword vert_break(halfword p, scaled h, scaled d) +{ /* finds optimum page break */ + halfword prev_p; /* if |p| is a glue node, |type(prev_p)| determines + whether |p| is a legal breakpoint */ + halfword q, r; /* glue specifications */ + int pi; /* penalty value */ + int b; /* badness at a trial breakpoint */ + int least_cost; /* the smallest badness plus penalties found so far */ + halfword best_place; /* the most recent break that leads to |least_cost| */ + scaled prev_dp; /* depth of previous box in the list */ + int t; /* |type| of the node following a kern */ + prev_p = p; /* an initial glue node is not a legal breakpoint */ + least_cost = awful_bad; + do_all_six(set_height_zero); + prev_dp = 0; + best_place = null; + pi = 0; + while (1) { + /* If node |p| is a legal breakpoint, check if this break is + the best known, and |goto done| if |p| is null or + if the page-so-far is already too full to accept more stuff */ + /* A subtle point to be noted here is that the maximum depth~|d| might be + negative, so |cur_height| and |prev_dp| might need to be corrected even + after a glue or kern node. */ + + if (p == null) { + pi = eject_penalty; + } else { + /* Use node |p| to update the current height and depth measurements; + if this node is not a legal breakpoint, |goto not_found| + or |update_heights|, + otherwise set |pi| to the associated penalty at the break */ + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + cur_height = cur_height + prev_dp + height(p); + prev_dp = depth(p); + goto NOT_FOUND; + break; + case whatsit_node: + /* Process whatsit |p| in |vert_break| loop, |goto not_found| */ + if ((subtype(p) == pdf_refxform_node) + || (subtype(p) == pdf_refximage_node)) { + cur_height = cur_height + prev_dp + height(p); + prev_dp = depth(p); + } + goto NOT_FOUND; + break; + case glue_node: + if (precedes_break(prev_p)) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case kern_node: + if (vlink(p) == null) + t = penalty_node; + else + t = type(vlink(p)); + if (t == glue_node) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case penalty_node: + pi = penalty(p); + break; + case mark_node: + case ins_node: + goto NOT_FOUND; + break; + default: + confusion("vertbreak"); + break; + } + } + /* Check if node |p| is a new champion breakpoint; then |goto done| + if |p| is a forced break or if the page-so-far is already too full */ + if (pi < inf_penalty) { + /* Compute the badness, |b|, using |awful_bad| if the box is too full */ + if (cur_height < h) { + if ((active_height[3] != 0) || (active_height[4] != 0) || + (active_height[5] != 0) || (active_height[6] != 0)) + b = 0; + else + b = badness(h - cur_height, active_height[2]); + } else if (cur_height - h > active_height[7]) { + b = awful_bad; + } else { + b = badness(cur_height - h, active_height[7]); + } + + if (b < awful_bad) { + if (pi <= eject_penalty) + b = pi; + else if (b < inf_bad) + b = b + pi; + else + b = deplorable; + } + if (b <= least_cost) { + best_place = p; + least_cost = b; + best_height_plus_depth = cur_height + prev_dp; + } + if ((b == awful_bad) || (pi <= eject_penalty)) + goto DONE; + } + + if ((type(p) < glue_node) || (type(p) > kern_node)) + goto NOT_FOUND; + UPDATE_HEIGHTS: + /* Update the current height and depth measurements with + respect to a glue or kern node~|p| */ + /* Vertical lists that are subject to the |vert_break| procedure should not + contain infinite shrinkability, since that would permit any amount of + information to ``fit'' on one page. */ + + if (type(p) == kern_node) { + q = p; + } else { + q = glue_ptr(p); + active_height[2 + stretch_order(q)] += stretch(q); + active_height[7] += shrink(q); + if ((shrink_order(q) != normal) && (shrink(q) != 0)) { + print_err("Infinite glue shrinkage found in box being split"); + help4("The box you are \\vsplitting contains some infinitely", + "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.", + "Such glue doesn't belong there; but you can safely proceed,", + "since the offensive shrinkability has been made finite."); + error(); + r = new_spec(q); + shrink_order(r) = normal; + delete_glue_ref(q); + glue_ptr(p) = r; + q = r; + } + } + cur_height = cur_height + prev_dp + width(q); + prev_dp = 0; + + NOT_FOUND: + if (prev_dp > d) { + cur_height = cur_height + prev_dp - d; + prev_dp = d; + } + + prev_p = p; + p = vlink(prev_p); + } + DONE: + return best_place; +} + + +@ Now we are ready to consider |vsplit| itself. Most of +its work is accomplished by the two subroutines that we have just considered. + +Given the number of a vlist box |n|, and given a desired page height |h|, +the |vsplit| function finds the best initial segment of the vlist and +returns a box for a page of height~|h|. The remainder of the vlist, if +any, replaces the original box, after removing glue and penalties and +adjusting for |split_top_skip|. Mark nodes in the split-off box are used to +set the values of |split_first_mark| and |split_bot_mark|; we use the +fact that |split_first_mark(x)=null| if and only if |split_bot_mark(x)=null|. + +The original box becomes ``void'' if and only if it has been entirely +extracted. The extracted box is ``void'' if and only if the original +box was void (or if it was, erroneously, an hlist box). + +@c +halfword vsplit(halfword n, scaled h) +{ /* extracts a page of height |h| from box |n| */ + halfword v; /* the box to be split */ + int vdir; /* the direction of the box to be split */ + halfword p; /* runs through the vlist */ + halfword q; /* points to where the break occurs */ + halfword i; /* for traversing marks lists */ + v = box(n); + vdir = box_dir(v); + flush_node_list(split_disc); + split_disc = null; + for (i = 0; i <= biggest_used_mark; i++) { + delete_split_first_mark(i); + delete_split_bot_mark(i); + } + /* Dispense with trivial cases of void or bad boxes */ + if (v == null) { + return null; + } + if (type(v) != vlist_node) { + print_err("\\vsplit needs a \\vbox"); + help2("The box you are trying to split is an \\hbox.", + "i can't split such a box, so I''ll leave it alone."); + error(); + return null; + } + + q = vert_break(list_ptr(v), h, dimen_par(split_max_depth_code)); + /* Look at all the marks in nodes before the break, and set the final + link to |null| at the break */ + /* It's possible that the box begins with a penalty node that is the + ``best'' break, so we must be careful to handle this special case correctly. */ + + p = list_ptr(v); + if (p == q) { + list_ptr(v) = null; + } else { + while (1) { + if (type(p) == mark_node) { + if (split_first_mark(mark_class(p)) == null) { + set_split_first_mark(mark_class(p), mark_ptr(p)); + set_split_bot_mark(mark_class(p), + split_first_mark(mark_class(p))); + set_token_ref_count(split_first_mark(mark_class(p)), + token_ref_count(split_first_mark + (mark_class(p))) + 2); + } else { + delete_token_ref(split_bot_mark(mark_class(p))); + set_split_bot_mark(mark_class(p), mark_ptr(p)); + add_token_ref(split_bot_mark(mark_class(p))); + } + } + if (vlink(p) == q) { + vlink(p) = null; + break; + } + p = vlink(p); + } + } + + q = prune_page_top(q, int_par(saving_vdiscards_code) > 0); + p = list_ptr(v); + list_ptr(v) = null; + flush_node(v); + if (q == null) + box(n) = null; /* the |eq_level| of the box stays the same */ + else + box(n) = + filtered_vpackage(q, 0, additional, dimen_par(max_depth_code), + split_keep_group, vdir); + return filtered_vpackage(p, h, exactly, + dimen_par(split_max_depth_code), split_off_group, + vdir); +} + + +@ Now that we can see what eventually happens to boxes, we can consider +the first steps in their creation. The |begin_box| routine is called when +|box_context| is a context specification, |cur_chr| specifies the type of +box desired, and |cur_cmd=make_box|. + +@c +void begin_box(int box_context) +{ + halfword q; /* run through the current list */ + halfword k; /* 0 or |vmode| or |hmode| */ + int n; /* a box number */ + int spec_direction = -1; + switch (cur_chr) { + case box_code: + scan_register_num(); + cur_box = box(cur_val); + box(cur_val) = null; /* the box becomes void, at the same level */ + break; + case copy_code: + scan_register_num(); + cur_box = copy_node_list(box(cur_val)); + break; + case last_box_code: + /* If the current list ends with a box node, delete it from + the list and make |cur_box| point to it; otherwise set |cur_box:=null| */ + cur_box = null; + if (abs(cur_list.mode_field) == mmode) { + you_cant(); + help1("Sorry; this \\lastbox will be void."); + error(); + } else if ((cur_list.mode_field == vmode) + && (cur_list.head_field == cur_list.tail_field)) { + you_cant(); + help2("Sorry...I usually can't take things from the current page.", + "This \\lastbox will therefore be void."); + error(); + } else { + if (cur_list.head_field != cur_list.tail_field) { /* todo: new code, needs testing */ + if ((type(cur_list.tail_field) == hlist_node) + || (type(cur_list.tail_field) == vlist_node)) { + /* Remove the last box ... */ + q = alink(cur_list.tail_field); + if (q == null || vlink(q) != cur_list.tail_field) { + q = cur_list.head_field; + while (vlink(q) != cur_list.tail_field) + q = vlink(q); + } + uncouple_node(cur_list.tail_field); + cur_box = cur_list.tail_field; + shift_amount(cur_box) = 0; + cur_list.tail_field = q; + vlink(cur_list.tail_field) = null; + } + } + } + break; + case vsplit_code: + /* Split off part of a vertical box, make |cur_box| point to it */ + /* Here we deal with things like `\.{\\vsplit 13 to 100pt}'. */ + scan_register_num(); + n = cur_val; + if (!scan_keyword("to")) { + print_err("Missing `to' inserted"); + help2("I'm working on `\\vsplit<box number> to <dimen>';", + "will look for the <dimen> next."); + error(); + } + scan_normal_dimen(); + cur_box = vsplit(n, cur_val); + break; + default: + /* Initiate the construction of an hbox or vbox, then |return| */ + /* Here is where we enter restricted horizontal mode or internal vertical + mode, in order to make a box. */ + k = cur_chr - vtop_code; + set_saved_record(0, saved_boxcontext, 0, box_context); + switch (abs(cur_list.mode_field)) { + case vmode: + spec_direction = body_direction; + break; + case hmode: + spec_direction = text_direction; + break; + case mmode: + spec_direction = math_direction; + break; + } + if (k == hmode) { + if ((box_context < box_flag) && (abs(cur_list.mode_field) == vmode)) + scan_full_spec(adjusted_hbox_group, spec_direction); + else + scan_full_spec(hbox_group, spec_direction); + } else { + if (k == vmode) { + scan_full_spec(vbox_group, spec_direction); + } else { + scan_full_spec(vtop_group, spec_direction); + k = vmode; + } + normal_paragraph(); + } + push_nest(); + cur_list.mode_field = -k; + if (k == vmode) { + prev_depth = dimen_par(pdf_ignored_dimen_code); + if (every_vbox != null) + begin_token_list(every_vbox, every_vbox_text); + } else { + space_factor = 1000; + if (every_hbox != null) + begin_token_list(every_hbox, every_hbox_text); + } + return; + break; + } + box_end(box_context); /* in simple cases, we use the box immediately */ +} diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h new file mode 100644 index 00000000000..fb0e4d9ca6d --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h @@ -0,0 +1,59 @@ +/* postlinebreak.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: postlinebreak.h 3493 2010-03-15 11:49:49Z taco $ */ + +#ifndef POSTLINEBREAK_H +# define POSTLINEBREAK_H + +/* todo: check this macro, especially values of alink() */ + +# define append_list(A,B) do { \ + vlink(cur_list.tail_field) = vlink((A)); \ + cur_list.tail_field = (B); \ + } while (0) + + +void ext_post_line_break(int paragraph_dir, + int right_skip, + int left_skip, + int 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 widow_penalties_ptr, + int widow_penalty, + int broken_penalty, + halfword final_par_glue, + halfword best_bet, + halfword last_special_line, + scaled second_width, + scaled second_indent, + scaled first_width, + scaled first_indent, halfword best_line, + halfword pdf_ignored_dimen); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w index f27094ea5f8..908224f0b47 100644 --- a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c +++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w @@ -1,30 +1,30 @@ -/* postlinebreak.c - - Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> +% postlinebreak.w +% +% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> - This file is part of LuaTeX. +% This file is part of LuaTeX. - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. -#include "luatex-api.h" -#include <ptexlib.h> -#include "nodes.h" +@ @c +#include "ptexlib.h" static const char _svn_version[] = - "$Id: postlinebreak.c 2448 2009-06-08 07:43:50Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/postlinebreak.c $"; + "$Id: postlinebreak.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/postlinebreak.w $"; -/* So far we have gotten a little way into the |line_break| routine, having +@ 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. @@ -40,25 +40,7 @@ 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| +@ 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 @@ -71,18 +53,11 @@ The |post_line_break| must also keep an dir stack, so that it can output end direction instructions at the ends of lines and begin direction instructions at the beginnings of lines. -*/ - +@c #define next_break prev_break /*new name for |prev_break| after links are reversed */ -#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, +void ext_post_line_break(int paragraph_dir, int right_skip, int left_skip, int pdf_protrude_chars, @@ -96,9 +71,7 @@ void ext_post_line_break(boolean d, 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, @@ -120,13 +93,13 @@ void ext_post_line_break(boolean d, 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 */ + int 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@>; */ + /* 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; @@ -134,8 +107,10 @@ void ext_post_line_break(boolean d, 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 */ +#if 0 + used_discs = used_disc(best_bet); +#endif + /* |has_direction| */ cur_p = null; do { r = q; @@ -148,9 +123,9 @@ void ext_post_line_break(boolean d, cur_line = cur_list.pg_field + 1; /* prevgraf+1 */ do { - /* @<Justify the line ending at breakpoint |cur_p|, and append it to the + /* Justify the line ending at breakpoint |cur_p|, and append it to the current vertical list, together with associated penalties and other - insertions@>; */ + 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 @@ -171,8 +146,11 @@ void ext_post_line_break(boolean d, for (q = dir_ptr; q != null; q = vlink(q)) { halfword tmp = new_dir(dir_dir(q)); halfword nxt = vlink(temp_head); + delete_attribute_ref(node_attr(tmp)); + node_attr(tmp) = node_attr(temp_head); + add_node_attr_ref(node_attr(tmp)); couple_nodes(temp_head, tmp); - try_couple_nodes(tmp, nxt); /* \break\par */ + try_couple_nodes(tmp, nxt); /* \.{\\break}\.{\\par} */ } if (dir_ptr != null) { flush_node_list(dir_ptr); @@ -296,10 +274,13 @@ void ext_post_line_break(boolean d, } assert(e == cur_break(cur_p)); - /* @<DIR: Insert dir nodes at the end of the current line@>; */ + /* 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); + delete_attribute_ref(node_attr(s)); + node_attr(s) = node_attr(r); + add_node_attr_ref(node_attr(s)); couple_nodes(r, s); try_couple_nodes(s, e); r = s; @@ -334,11 +315,14 @@ void ext_post_line_break(boolean d, p = alink(q); /* get |vlink(p) = q| */ assert(vlink(p) == q); ptmp = p; - p = find_protchar_right(vlink(temp_head), 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); + delete_attribute_ref(node_attr(k)); + node_attr(k) = node_attr(p); + add_node_attr_ref(node_attr(k)); vlink(k) = vlink(ptmp); vlink(ptmp) = k; if (ptmp == q) @@ -348,17 +332,23 @@ void ext_post_line_break(boolean d, /* 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); + /* Put the \.{\\rightskip} glue after node |q|; */ + halfword r = new_glue((right_skip == null ? null : copy_node(right_skip))); + glue_ref_count(glue_ptr(r)) = null; + subtype(r) = right_skip_code; vlink(r) = vlink(q); + delete_attribute_ref(node_attr(r)); + node_attr(r) = node_attr(q); + add_node_attr_ref(node_attr(r)); vlink(q) = r; + alink(r) = q; 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; */ + /* Put the \.{\\leftskip} glue at the left and detach this line; */ /* The following code begins with |q| at the end of the list to be justified. It ends with |q| at the beginning of that list, and with |vlink(temp_head)| pointing to the remainder of the paragraph, if any. */ @@ -390,16 +380,25 @@ void ext_post_line_break(boolean d, w = char_pw(p, left_side); if (w != 0) { k = new_margin_kern(-w, last_leftmost_char, left_side); + delete_attribute_ref(node_attr(k)); + node_attr(k) = node_attr(q); + add_node_attr_ref(node_attr(k)); vlink(k) = q; q = k; } }; if (left_skip != zero_glue) { - r = new_param_glue(left_skip_code); + r = new_glue(copy_node(left_skip)); + glue_ref_count(glue_ptr(r)) = null; + subtype(r) = left_skip_code; + delete_attribute_ref(node_attr(r)); + node_attr(r) = node_attr(q); + add_node_attr_ref(node_attr(r)); vlink(r) = q; + alink(q) = r; q = r; } - /* /Put the \(l)\.{\\leftskip} glue at the left and detach this line; */ + /* /Put the \.{\\leftskip} glue at the left and detach this line; */ /* Call the packaging subroutine, setting |just_box| to the justified box; */ /* Now |q| points to the hlist that represents the current line of the @@ -417,14 +416,14 @@ void ext_post_line_break(boolean d, 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); + just_box = hpack(q, cur_width, cal_expand_ratio, paragraph_dir); } else { - just_box = hpack(q, cur_width, exactly); + just_box = hpack(q, cur_width, exactly, paragraph_dir); } shift_amount(just_box) = cur_indent; + subtype(just_box) = HLIST_SUBTYPE_LINE; /* /Call the packaging subroutine, setting |just_box| to the justified box; */ /* Append the new box to the current vertical list, followed by the list of @@ -440,12 +439,23 @@ void ext_post_line_break(boolean d, && (cur_line + 1 == best_line)) depth(just_box) = pdf_last_line_depth; - if (pre_adjust_head != pre_adjust_tail) + if ((vlink(contrib_head) != null)) + if (!output_active) + lua_node_filter_s(buildpage_filter_callback, "pre_box"); + if (pre_adjust_head != pre_adjust_tail) { append_list(pre_adjust_head, pre_adjust_tail); + if (!output_active) + lua_node_filter_s(buildpage_filter_callback, "pre_adjust"); + } pre_adjust_tail = null; append_to_vlist(just_box); - if (adjust_head != adjust_tail) + if (!output_active) + lua_node_filter_s(buildpage_filter_callback, "box"); + if (adjust_head != adjust_tail) { append_list(adjust_head, adjust_tail); + if (!output_active) + lua_node_filter_s(buildpage_filter_callback, "adjust"); + } adjust_tail = null; /* /Append the new box to the current vertical list, followed by the list of @@ -481,20 +491,14 @@ void ext_post_line_break(boolean d, } 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; + 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; + pen += widow_penalty; } if (disc_break) { if (passive_pen_broken(cur_p) != 0) { @@ -513,11 +517,11 @@ void ext_post_line_break(boolean d, /* /Justify the line ending at breakpoint |cur_p|, and append it to the current vertical list, together with associated penalties and other - insertions@>; */ + 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@>; */ + /* 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 @@ -546,8 +550,8 @@ void ext_post_line_break(boolean d, } } while (cur_p != null); if ((cur_line != best_line) || (vlink(temp_head) != null)) - tconfusion("line breaking"); + confusion("line breaking"); cur_list.pg_field = best_line - 1; /* prevgraf */ - cur_list.dirs_field = dir_ptr; /* dir_save */ + cur_list.dirs_field = dir_ptr; /* |dir_save| */ dir_ptr = null; } diff --git a/Build/source/texk/web2c/luatexdir/tex/primitive.h b/Build/source/texk/web2c/luatexdir/tex/primitive.h new file mode 100644 index 00000000000..08459aa014f --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/primitive.h @@ -0,0 +1,92 @@ +/* primitive.h + + Copyright 2008-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: primitive.h 3385 2010-01-26 05:45:55Z taco $ */ + +#ifndef LUATEX_PRIMITIVE_H +# define LUATEX_PRIMITIVE_H 1 + +/* This enum is a list of origins for primitive commands */ + +typedef enum { + tex_command = 1, + etex_command = 2, + aleph_command = 4, + omega_command = 8, + pdftex_command = 16, + luatex_command = 32, + core_command = 64, + no_command = 128, +} command_origin; + +# define hash_size 65536 /* maximum number of control sequences; it should be at most about |(fix_mem_max-fix_mem_min)/10| */ + +# define hash_prime 55711 /* a prime number equal to about 85\pct! of |hash_size| */ + +extern two_halves *hash; /* the hash table */ +extern halfword hash_used; /* allocation pointer for |hash| */ +extern int hash_extra; /* |hash_extra=hash| above |eqtb_size| */ +extern halfword hash_top; /* maximum of the hash array */ +extern halfword hash_high; /* pointer to next high hash location */ +extern boolean no_new_control_sequence; /* are new identifiers legal? */ +extern int cs_count; /* total number of known identifiers */ + +# define cs_next(a) hash[(a)].lhfield /* link for coalesced lists */ +# define cs_text(a) hash[(a)].rh + /* string number for control sequence name */ + +# define undefined_primitive 0 +# define prim_size 2100 /* maximum number of primitives */ +# define prim_prime 1777 /* about 85\pct! of |primitive_size| */ + +extern void init_primitives(void); +extern void ini_init_primitives(void); + +extern halfword compute_pool_hash(pool_pointer j, pool_pointer l, + halfword prime_number); +extern pointer prim_lookup(str_number s); + +extern boolean is_primitive(str_number csname); + +extern quarterword get_prim_eq_type(int p); +extern halfword get_prim_equiv(int p); +extern str_number get_prim_text(int p); +extern quarterword get_prim_origin(int p); + +extern void dump_primitives(void); +extern void undump_primitives(void); + +# define primitive_tex(a,b,c,d) primitive((a),(b),(c),(d),tex_command) +# define primitive_etex(a,b,c,d) primitive((a),(b),(c),(d),etex_command) +# define primitive_aleph(a,b,c,d) primitive((a),(b),(c),(d),aleph_command) +# define primitive_omega(a,b,c,d) primitive((a),(b),(c),(d),omega_command) +# define primitive_pdftex(a,b,c,d) primitive((a),(b),(c),(d),pdftex_command) +# define primitive_luatex(a,b,c,d) primitive((a),(b),(c),(d),luatex_command) +# define primitive_core(a,b,c,d) primitive((a),(b),(c),(d),core_command) +# define primitive_no(a,b,c,d) primitive((a),(b),(c),(d),no_command) + +extern void primitive(const char *ss, quarterword c, halfword o, halfword off, + int cmd_origin); +extern void primitive_def(const char *s, size_t l, quarterword c, halfword o); +extern void print_cmd_chr(quarterword cmd, halfword chr_code); + +extern pointer string_lookup(const char *s, size_t l); +extern pointer id_lookup(int j, int l); + +#endif /* LUATEX_PRIMITIVE_H */ diff --git a/Build/source/texk/web2c/luatexdir/tex/primitive.c b/Build/source/texk/web2c/luatexdir/tex/primitive.w index f98fbcd4e89..89db63fbeaa 100644 --- a/Build/source/texk/web2c/luatexdir/tex/primitive.c +++ b/Build/source/texk/web2c/luatexdir/tex/primitive.w @@ -1,72 +1,77 @@ -/* primitive.c +% primitive.w - Copyright 2008-2009 Taco Hoekwater <taco@luatex.org> +% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> - This file is part of LuaTeX. +% This file is part of LuaTeX. - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. -#include "luatex-api.h" -#include <ptexlib.h> +@ @c +#include "ptexlib.h" -#include "commands.h" -#include "primitive.h" -#include "tokens.h" static const char _svn_version[] = - "$Id: primitive.c 2448 2009-06-08 07:43:50Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/primitive.c $"; + "$Id: primitive.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/primitive.w $"; + + +@ Control sequences are stored and retrieved by means of a fairly standard hash +table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C +in {\sl The Art of Computer Programming\/}). Once a control sequence enters the +table, it is never removed, because there are complicated situations +involving \.{\\gdef} where the removal of a control sequence at the end of +a group would be a mistake preventable only by the introduction of a +complicated reference-count mechanism. + +The actual sequence of letters forming a control sequence identifier is +stored in the |str_pool| array together with all the other strings. An +auxiliary array |hash| consists of items with two halfword fields per +word. The first of these, called |next(p)|, points to the next identifier +belonging to the same coalesced list as the identifier corresponding to~|p|; +and the other, called |text(p)|, points to the |str_start| entry for +|p|'s identifier. If position~|p| of the hash table is empty, we have +|text(p)=0|; if position |p| is either empty or the end of a coalesced +hash list, we have |next(p)=0|. An auxiliary pointer variable called +|hash_used| is maintained in such a way that all locations |p>=hash_used| +are nonempty. The global variable |cs_count| tells how many multiletter +control sequences have been defined, if statistics are being kept. + +A global boolean variable called |no_new_control_sequence| is set to +|true| during the time that new hash table entries are forbidden. + +@c +two_halves *hash; /* the hash table */ +halfword hash_used; /* allocation pointer for |hash| */ +int hash_extra; /* |hash_extra=hash| above |eqtb_size| */ +halfword hash_top; /* maximum of the hash array */ +halfword hash_high; /* pointer to next high hash location */ +boolean no_new_control_sequence; /* are new identifiers legal? */ +int cs_count; /* total number of known identifiers */ -/* as usual, the file starts with a bunch of #defines that mimic pascal @ds */ - -#define level_one 1 -#define flush_string() do { decr(str_ptr); pool_ptr=str_start_macro(str_ptr); } while (0) -#define cur_length (pool_ptr - str_start_macro(str_ptr)) -#define append_char(a) str_pool[pool_ptr++]=(a) - -#define next(a) hash[(a)].lhfield /* link for coalesced lists */ -#define text(a) hash[(a)].rh /* string number for control sequence name */ #define hash_is_full (hash_used==hash_base) /* test if all positions are occupied */ -#define hash_size 65536 - -#define span_code 1114113 -#define unless_code 32 /* amount added for `\.{\\unless}' prefix */ -#define protected_token 0x1C00001 /* $2^{21}\cdot|end_match|+1$ */ -#define offset_ocp_name 1 -#define ocp_name(A) ocp_tables[(A)][offset_ocp_name] - -#define skip_base get_skip_base() -#define mu_skip_base get_mu_skip_base() -#define glue_base static_glue_base -#define toks_base get_toks_base() -#define count_base get_count_base() -#define int_base static_int_base -#define attribute_base get_attribute_base() -#define scaled_base get_scaled_base() -#define dimen_base get_dimen_base() +@ \.{\\primitive} support needs a few extra variables and definitions - -/* \primitive support needs a few extra variables and definitions */ - +@c #define prim_base 1 -/* The arrays |prim| and |prim_eqtb| are used for name -> cmd,chr lookups. - * - * The are modelled after |hash| and |eqtb|, except that primitives do not - * have an |eq_level|, that field is replaced by |origin|. - */ +@ The arrays |prim| and |prim_eqtb| are used for name -> cmd,chr lookups. + + The are modelled after |hash| and |eqtb|, except that primitives do not + have an |eq_level|, that field is replaced by |origin|. +@c #define prim_next(a) prim[(a)].lhfield /* link for coalesced lists */ #define prim_text(a) prim[(a)].rh /* string number for control sequence name */ #define prim_is_full (prim_used==prim_base) /* test if all positions are occupied */ @@ -82,9 +87,10 @@ static pointer prim_used; /* allocation pointer for |prim| */ static two_halves prim[(prim_size + 1)]; /* the primitives table */ static memory_word prim_eqtb[(prim_size + 1)]; -/* The array |prim_data| works the other way around, it is used for - cmd,chr -> name lookups. */ +@ The array |prim_data| works the other way around, it is used for + cmd,chr -> name lookups. +@c typedef struct prim_info { halfword subids; /* number of name entries */ halfword offset; /* offset to be used for |chr_code|s */ @@ -93,8 +99,8 @@ typedef struct prim_info { static prim_info prim_data[(last_cmd + 1)]; -/* initialize the memory arrays */ - +@ initialize the memory arrays +@c void init_primitives(void) { int k; @@ -111,16 +117,17 @@ void ini_init_primitives(void) } -/* The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it +@ The value of |hash_prime| should be roughly 85\%! of |hash_size|, and it should be a prime number. The theory of hashing tells us to expect fewer than two table probes, on the average, when the search is successful. [See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.] @^Vitter, Jeffrey Scott@> -*/ -static halfword compute_hash(const char *j, pool_pointer l, halfword prime_number) +@c +static halfword compute_hash(const char *j, unsigned int l, + halfword prime_number) { - pool_pointer k; + unsigned int k; halfword h = (unsigned char) *j; for (k = 1; k <= l - 1; k++) { h = h + h + (unsigned char) *(j + k); @@ -131,29 +138,27 @@ static halfword compute_hash(const char *j, pool_pointer l, halfword prime_numbe } -/* Here is the subroutine that searches the primitive table for an identifier */ - +@ Here is the subroutine that searches the primitive table for an identifier +@c pointer prim_lookup(str_number s) { - integer h; /* hash code */ + int h; /* hash code */ pointer p; /* index in |hash| array */ - pool_pointer j, l; - if (s < string_offset) { + unsigned char *j; + unsigned l; + if (s < STRING_OFFSET) { p = s; if ((p < 0) || (get_prim_eq_type(p) == undefined_cs_cmd)) { p = undefined_primitive; } } else { - j = str_start_macro(s); - if (s == str_ptr) - l = cur_length; - else - l = length(s); - h = compute_hash((char *) (str_pool + j), l, prim_prime); + j = str_string(s); + l = (unsigned) str_length(s); + h = compute_hash((char *) j, l, prim_prime); p = h + prim_base; /* we start searching here; note that |0<=h<hash_prime| */ while (1) { if (prim_text(p) > 0) - if (length(prim_text(p)) == l) + if (str_length(prim_text(p)) == l) if (str_eq_str(prim_text(p), s)) goto FOUND; if (prim_next(p) == 0) { @@ -164,7 +169,7 @@ pointer prim_lookup(str_number s) if (prim_text(p) > 0) { do { /* search for an empty location in |prim| */ if (prim_is_full) - overflow_string("primitive size", prim_size); + overflow("primitive size", prim_size); decr(prim_used); } while (prim_text(prim_used) != 0); prim_next(p) = prim_used; @@ -181,44 +186,47 @@ pointer prim_lookup(str_number s) return p; } -/* how to test a csname for primitive-ness */ - +@ how to test a csname for primitive-ness +@c boolean is_primitive(str_number csname) { - integer n, m; + int n, m; + char *ss; m = prim_lookup(csname); - n = string_lookup(makecstring(csname), length(csname)); + ss = makecstring(csname); + n = string_lookup(ss, str_length(csname)); + free(ss); return ((n != undefined_cs_cmd) && (m != undefined_primitive) && (eq_type(n) == prim_eq_type(m)) && (equiv(n) == prim_equiv(m))); } -/* a few simple accessors */ - -quarterword get_prim_eq_type(integer p) +@ a few simple accessors +@c +quarterword get_prim_eq_type(int p) { return prim_eq_type(p); } -quarterword get_prim_origin(integer p) +quarterword get_prim_origin(int p) { return prim_origin(p); } -halfword get_prim_equiv(integer p) +halfword get_prim_equiv(int p) { return prim_equiv(p); } -str_number get_prim_text(integer p) +str_number get_prim_text(int p) { return prim_text(p); } -/* dumping and undumping */ - +@ dumping and undumping +@c void dump_primitives(void) { int p, q; @@ -247,30 +255,29 @@ void undump_primitives(void) undump_int(prim_data[p].offset); undump_int(prim_data[p].subids); if (prim_data[p].subids > 0) { - prim_data[p].names = - (str_number *) xcalloc((prim_data[p].subids), - sizeof(str_number *)); - } - for (q = 0; q < prim_data[p].subids; q++) { - undump_int(prim_data[p].names[q]); + prim_data[p].names = (str_number *) + xmalloc((unsigned) + ((unsigned) prim_data[p].subids * + sizeof(str_number *))); + for (q = 0; q < prim_data[p].subids; q++) + undump_int(prim_data[p].names[q]); } } } -/* - We need to put \TeX's ``primitive'' control sequences into the hash +@ We need to put \TeX's ``primitive'' control sequences into the hash table, together with their command code (which will be the |eq_type|) and an operand (which will be the |equiv|). The |primitive| procedure does this, in a way that no \TeX\ user can. The global value |cur_val| contains the new |eqtb| pointer after |primitive| has acted. -*/ -/* Because the definitions of the actual user-accessible name of a + +@ Because the definitions of the actual user-accessible name of a primitive can be postponed until runtime, the function |primitive_def| is needed that does nothing except creating the control sequence name. -*/ -void primitive_def(char *s, size_t l, quarterword c, halfword o) +@c +void primitive_def(const char *s, size_t l, quarterword c, halfword o) { int nncs = no_new_control_sequence; no_new_control_sequence = false; @@ -281,7 +288,7 @@ void primitive_def(char *s, size_t l, quarterword c, halfword o) equiv(cur_val) = o; } -/* The function |store_primitive_name| sets up the bookkeeping for the +@ The function |store_primitive_name| sets up the bookkeeping for the reverse lookup. It is quite paranoid, because it is easy to mess this up accidentally. @@ -292,9 +299,9 @@ void primitive_def(char *s, size_t l, quarterword c, halfword o) wasted room, so |offset| is substracted from |o| because creating or accessing the array. The |assert(idx<=0xFFFF)| is not strictly needed, but it helps catch errors of this kind. -*/ -static void +@c +void store_primitive_name(str_number s, quarterword c, halfword o, halfword offset) { int idx; @@ -307,11 +314,11 @@ store_primitive_name(str_number s, quarterword c, halfword o, halfword offset) assert(idx <= 0xFFFF); if (prim_data[c].subids < (idx + 1)) { str_number *new = - (str_number *) xcalloc((idx + 1), sizeof(str_number *)); + (str_number *) xcalloc((unsigned) (idx + 1), sizeof(str_number *)); if (prim_data[c].names != NULL) { assert(prim_data[c].subids); memcpy(new, (prim_data[c].names), - (prim_data[c].subids * sizeof(str_number))); + (unsigned) (prim_data[c].subids) * sizeof(str_number)); free(prim_data[c].names); } prim_data[c].names = new; @@ -320,151 +327,149 @@ store_primitive_name(str_number s, quarterword c, halfword o, halfword offset) prim_data[c].names[idx] = s; } -/* Compared to tex82, |primitive| has two extra parameters. The |off| is an offset +@ Compared to tex82, |primitive| has two extra parameters. The |off| is an offset that will be passed on to |store_primitive_name|, the |cmd_origin| is the bit that is used to group primitives by originator. -*/ +@c void -primitive(str_number ss, quarterword c, halfword o, halfword off, +primitive(const char *thes, quarterword c, halfword o, halfword off, int cmd_origin) { - str_number s; /* actual |str_number| used */ - integer prim_val; /* needed to fill |prim_eqtb| */ - char *thes; + int prim_val; /* needed to fill |prim_eqtb| */ + str_number ss; assert(o >= off); - if (ss < string_offset) { - if (ss > 127) - tconfusion("prim"); /* should be ASCII */ - append_char(ss); - s = make_string(); - } else { - s = ss; - } - thes = makecstring(s); + ss = maketexstring(thes); if (cmd_origin == tex_command || cmd_origin == core_command) { primitive_def(thes, strlen(thes), c, o); } - prim_val = prim_lookup(s); - prim_origin(prim_val) = cmd_origin; + prim_val = prim_lookup(ss); + prim_origin(prim_val) = (quarterword) cmd_origin; prim_eq_type(prim_val) = c; prim_equiv(prim_val) = o; - store_primitive_name(s, c, o, off); + store_primitive_name(ss, c, o, off); } -/* - * Here is a helper that does the actual hash insertion. - */ -static halfword insert_id(halfword p, const unsigned char *j, pool_pointer l) +@ Here is a helper that does the actual hash insertion. + +@c +static halfword insert_id(halfword p, const unsigned char *j, unsigned int l) { - integer d; + unsigned saved_cur_length; + unsigned saved_cur_string_size; + unsigned char *saved_cur_string; const unsigned char *k; /* This code far from ideal: the existance of |hash_extra| changes all the potential (short) coalesced lists into a single (long) one. This will create a slowdown. */ - if (text(p) > 0) { + if (cs_text(p) > 0) { if (hash_high < hash_extra) { incr(hash_high); - /* can't use eqtb_top here (perhaps because that is not finalized + /* can't use |eqtb_top| here (perhaps because that is not finalized yet when called from |primitive|?) */ - next(p) = hash_high + get_eqtb_size(); - p = next(p); + cs_next(p) = hash_high + eqtb_size; + p = cs_next(p); } else { do { if (hash_is_full) - overflow_string("hash size", hash_size + hash_extra); + overflow("hash size", (unsigned) (hash_size + hash_extra)); decr(hash_used); - } while (text(hash_used) != 0); /* search for an empty location in |hash| */ - next(p) = hash_used; + } while (cs_text(hash_used) != 0); /* search for an empty location in |hash| */ + cs_next(p) = hash_used; p = hash_used; } } - check_pool_overflow((pool_ptr + l)); - d = cur_length; - while (pool_ptr > str_start_macro(str_ptr)) { - /* move current string up to make room for another */ - decr(pool_ptr); - str_pool[pool_ptr + l] = str_pool[pool_ptr]; - } + saved_cur_length = cur_length; + saved_cur_string = cur_string; + saved_cur_string_size = cur_string_size; + reset_cur_string(); for (k = j; k <= j + l - 1; k++) append_char(*k); - text(p) = make_string(); - pool_ptr = pool_ptr + d; + cs_text(p) = make_string(); + cur_length = saved_cur_length; + cur_string = saved_cur_string; + cur_string_size = saved_cur_string_size; incr(cs_count); return p; } -/* - Here is the subroutine that searches the hash table for an identifier +@ Here is the subroutine that searches the hash table for an identifier that matches a given string of length |l>1| appearing in |buffer[j.. (j+l-1)]|. If the identifier is found, the corresponding hash table address is returned. Otherwise, if the global variable |no_new_control_sequence| is |true|, the dummy address |undefined_control_sequence| is returned. Otherwise the identifier is inserted into the hash table and its location is returned. -*/ - -pointer id_lookup(integer j, integer l) +@c +pointer id_lookup(int j, int l) { /* search the hash table */ - integer h; /* hash code */ + int h; /* hash code */ pointer p; /* index in |hash| array */ - h = compute_hash((char *) (buffer + j), l, hash_prime); + h = compute_hash((char *) (buffer + j), (unsigned) l, hash_prime); +#ifdef VERBOSE + { + unsigned char *todo = xmalloc(l + 2); + strncpy(todo, (buffer + j), l); + todo[l] = '\0'; + todo[l + 1] = '\0'; + fprintf(stdout, "id_lookup(%s)\n", todo); + free(todo); + } +#endif p = h + hash_base; /* we start searching here; note that |0<=h<hash_prime| */ while (1) { - if (text(p) > 0) - if (length(text(p)) == l) - if (str_eq_buf(text(p), j)) + if (cs_text(p) > 0) + if (str_length(cs_text(p)) == (unsigned) l) + if (str_eq_buf(cs_text(p), j)) goto FOUND; - if (next(p) == 0) { + if (cs_next(p) == 0) { if (no_new_control_sequence) { - p = static_undefined_control_sequence; + p = undefined_control_sequence; } else { - p = insert_id(p, (buffer + j), l); + p = insert_id(p, (buffer + j), (unsigned) l); } goto FOUND; } - p = next(p); + p = cs_next(p); } FOUND: return p; } -/* - * Here is a similar subroutine for finding a primitive in the hash. - * This one is based on a C string. - */ - +@ Here is a similar subroutine for finding a primitive in the hash. +This one is based on a C string. +@c pointer string_lookup(const char *s, size_t l) { /* search the hash table */ - integer h; /* hash code */ + int h; /* hash code */ pointer p; /* index in |hash| array */ - h = compute_hash(s, l, hash_prime); + h = compute_hash(s, (unsigned) l, hash_prime); p = h + hash_base; /* we start searching here; note that |0<=h<hash_prime| */ while (1) { - if (text(p) > 0) - if (str_eq_cstr(text(p), s, l)) + if (cs_text(p) > 0) + if (str_eq_cstr(cs_text(p), s, l)) goto FOUND; - if (next(p) == 0) { + if (cs_next(p) == 0) { if (no_new_control_sequence) { - p = static_undefined_control_sequence; + p = undefined_control_sequence; } else { - p = insert_id(p, (const unsigned char *) s, l); + p = insert_id(p, (const unsigned char *) s, (unsigned) l); } goto FOUND; } - p = next(p); + p = cs_next(p); } FOUND: return p; } -/* The |print_cmd_chr| routine prints a symbolic interpretation of a +@ The |print_cmd_chr| routine prints a symbolic interpretation of a command code and its modifier. This is used in certain `\.{You can\'t}' error messages, and in the implementation of diagnostic routines like \.{\\show}. @@ -476,11 +481,11 @@ pointer string_lookup(const char *s, size_t l) Thanks to |prim_data|, there is no need for all that tediousness. What is left of |primt_cnd_chr| are just the exceptions to the general rule that the |cmd,chr_code| pair represents in a single primitive command. -*/ +@c #define chr_cmd(A) do { tprint(A); print(chr_code); } while (0) -static void prim_cmd_chr(quarterword cmd, halfword chr_code) +void prim_cmd_chr(quarterword cmd, halfword chr_code) { int idx = chr_code - prim_data[cmd].offset; if (cmd <= last_cmd && @@ -500,7 +505,7 @@ static void prim_cmd_chr(quarterword cmd, halfword chr_code) void print_cmd_chr(quarterword cmd, halfword chr_code) { - integer n; /* temp variable */ + int n; /* temp variable */ switch (cmd) { case left_brace_cmd: chr_cmd("begin-group character "); @@ -512,7 +517,10 @@ void print_cmd_chr(quarterword cmd, halfword chr_code) chr_cmd("math shift character "); break; case mac_param_cmd: - chr_cmd("macro parameter character "); + if (chr_code == tab_mark_cmd_code) + tprint_esc("alignmark"); + else + chr_cmd("macro parameter character "); break; case sup_mark_cmd: chr_cmd("superscript character "); @@ -535,6 +543,8 @@ void print_cmd_chr(quarterword cmd, halfword chr_code) case tab_mark_cmd: if (chr_code == span_code) tprint_esc("span"); + else if (chr_code == tab_mark_cmd_code) + tprint_esc("aligntab"); else chr_cmd("alignment tab character "); break; @@ -576,7 +586,7 @@ void print_cmd_chr(quarterword cmd, halfword chr_code) case outer_call_cmd: case long_outer_call_cmd: n = cmd - call_cmd; - if (info(link(chr_code)) == protected_token) + if (token_info(token_link(chr_code)) == protected_token) n = n + 4; if (odd(n / 4)) tprint_esc("protected"); @@ -609,7 +619,7 @@ void print_cmd_chr(quarterword cmd, halfword chr_code) case assign_glue_cmd: case assign_mu_glue_cmd: if (chr_code < skip_base) { - print_skip_param(chr_code - glue_base); + prim_cmd_chr(cmd, chr_code); } else if (chr_code < mu_skip_base) { tprint_esc("skip"); print_int(chr_code - skip_base); @@ -628,7 +638,7 @@ void print_cmd_chr(quarterword cmd, halfword chr_code) break; case assign_int_cmd: if (chr_code < count_base) { - print_param(chr_code - int_base); + prim_cmd_chr(cmd, chr_code); } else { tprint_esc("count"); print_int(chr_code - count_base); @@ -640,7 +650,7 @@ void print_cmd_chr(quarterword cmd, halfword chr_code) break; case assign_dimen_cmd: if (chr_code < scaled_base) { - print_length_param(chr_code - dimen_base); + prim_cmd_chr(cmd, chr_code); } else { tprint_esc("dimen"); print_int(chr_code - scaled_base); diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.h b/Build/source/texk/web2c/luatexdir/tex/printing.h new file mode 100644 index 00000000000..b104fd142cc --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/printing.h @@ -0,0 +1,124 @@ +/* printing.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: printing.h 3436 2010-02-21 15:34:00Z taco $ */ + +#ifndef PRINTING_H +# define PRINTING_H + +typedef enum { + no_print = 16, /* |selector| setting that makes data disappear */ + term_only = 17, /* printing is destined for the terminal only */ + log_only = 18, /* printing is destined for the transcript file only */ + term_and_log = 19, /* normal |selector| setting */ + pseudo = 20, /* special |selector| setting for |show_context| */ + new_string = 21, /* printing is deflected to the string pool */ +} selector_settings; + +# define ssup_error_line 255 +# define max_selector new_string + /* highest selector setting */ + +extern alpha_file log_file; +extern int selector; +extern int dig[23]; +extern int tally; +extern int term_offset; +extern int file_offset; +extern packed_ASCII_code trick_buf[(ssup_error_line + 1)]; +extern int trick_count; +extern int first_count; +extern boolean inhibit_par_tokens; + +/* +Macro abbreviations for output to the terminal and to the log file are +defined here for convenience. Some systems need special conventions +for terminal output, and it is possible to adhere to those conventions +by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section. +@^system dependencies@> +*/ + +# define wterm_cr() fprintf(term_out,"\n") +# define wlog_cr() fprintf(log_file,"\n") + +extern void print_ln(void); +extern void print_char(int s); +extern void print(int s); +extern void print_nl(str_number s); +extern void print_nlp(void); +extern void slow_print(int s); +extern void print_banner(const char *, int, int); +extern void log_banner(const char *, int, int); +extern void print_version_banner(void); +extern void print_esc(str_number s); +extern void print_the_digs(eight_bits k); +extern void print_int(longinteger n); +extern void print_two(int n); +extern void print_hex(int n); +extern void print_roman_int(int n); +extern void print_current_string(void); + +# define print_font_name(A) tprint(font_name(A)) + +extern void print_cs(int p); +extern void sprint_cs(pointer p); +extern void tprint(const char *s); +extern void tprint_nl(const char *s); +extern void tprint_esc(const char *s); + +extern void prompt_input(const char *s); + + +# define single_letter(A) \ + ((str_length(A)==1)|| \ + ((str_length(A)==4)&&*(str_string((A)))>=0xF0)|| \ + ((str_length(A)==3)&&*(str_string((A)))>=0xE0)|| \ + ((str_length(A)==2)&&*(str_string((A)))>=0xC0)) + +# define is_active_cs(a) (str_length(a)>3 && \ + (*str_string(a) == 0xEF) && \ + (*(str_string(a)+1) == 0xBF) && \ + (*(str_string(a)+2) == 0xBF)) + + +# define active_cs_value(A) pool_to_unichar((str_string((A))+3)) + +extern void print_glue(scaled d, int order, const char *s); /* prints a glue component */ +extern void print_spec(int p, const char *s); /* prints a glue specification */ + +extern int font_in_short_display; /* an internal font number */ +extern void print_font_identifier(internal_font_number f); +extern void short_display(int p); /* prints highlights of list |p| */ +extern void print_font_and_char(int p); /* prints |char_node| data */ +extern void print_mark(int p); /* prints token list data in braces */ +extern void print_rule_dimen(scaled d); /* prints dimension in rule node */ +extern int depth_threshold; /* maximum nesting depth in box displays */ +extern int breadth_max; /* maximum number of items shown at the same list level */ +extern void show_box(halfword p); +extern void short_display_n(int p, int m); /* prints highlights of list |p| */ + +extern void print_csnames(int hstart, int hfinish); +extern void print_file_line(void); + +extern void begin_diagnostic(void); +extern void end_diagnostic(boolean blank_line); +extern int global_old_setting; + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.w b/Build/source/texk/web2c/luatexdir/tex/printing.w new file mode 100644 index 00000000000..cb4199841e8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/printing.w @@ -0,0 +1,984 @@ +% printing.w + +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "lua/luatex-api.h" /* for ptexbanner */ +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: printing.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/printing.w $"; + +@ @c +#define font_id_text(A) cs_text(font_id_base+(A)) + +#define wlog(A) fputc(A,log_file) +#define wterm(A) fputc(A,term_out) + + +@ Messages that are sent to a user's terminal and to the transcript-log file +are produced by several `|print|' procedures. These procedures will +direct their output to a variety of places, based on the setting of +the global variable |selector|, which has the following possible +values: + +\yskip +\hang |term_and_log|, the normal setting, prints on the terminal and on the + transcript file. + +\hang |log_only|, prints only on the transcript file. + +\hang |term_only|, prints only on the terminal. + +\hang |no_print|, doesn't print at all. This is used only in rare cases + before the transcript file is open. + +\hang |pseudo|, puts output into a cyclic buffer that is used + by the |show_context| routine; when we get to that routine we shall discuss + the reasoning behind this curious mode. + +\hang |new_string|, appends the output to the current string in the + string pool. + +\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output. + +\yskip +\noindent The symbolic names `|term_and_log|', etc., have been assigned +numeric codes that satisfy the convenient relations |no_print+1=term_only|, +|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|. + +Three additional global variables, |tally| and |term_offset| and +|file_offset|, record the number of characters that have been printed +since they were most recently cleared to zero. We use |tally| to record +the length of (possibly very long) stretches of printing; |term_offset| +and |file_offset|, on the other hand, keep track of how many characters +have appeared so far on the current line that has been output to the +terminal or to the transcript file, respectively. + +@c +alpha_file log_file; /* transcript of \TeX\ session */ +int selector = term_only; /* where to print a message */ +int dig[23]; /* digits in a number being output */ +int tally = 0; /* the number of characters recently printed */ +int term_offset = 0; /* the number of characters on the current terminal line */ +int file_offset = 0; /* the number of characters on the current file line */ +packed_ASCII_code trick_buf[(ssup_error_line + 1)]; /* circular buffer for pseudoprinting */ +int trick_count; /* threshold for pseudoprinting, explained later */ +int first_count; /* another variable for pseudoprinting */ +boolean inhibit_par_tokens = false; /* for minor adjustments to |show_token_list| */ + +@ To end a line of text output, we call |print_ln| +@c +void print_ln(void) +{ /* prints an end-of-line */ + switch (selector) { + case term_and_log: + wterm_cr(); + wlog_cr(); + term_offset = 0; + file_offset = 0; + break; + case log_only: + wlog_cr(); + file_offset = 0; + break; + case term_only: + wterm_cr(); + term_offset = 0; + break; + case no_print: + case pseudo: + case new_string: + break; + default: + fprintf(write_file[selector], "\n"); + break; + } +} /* |tally| is not affected */ + + +@ The |print_char| procedure sends one byte to the desired destination. + All printing comes through |print_ln| or |print_char|. + +@c +#define wterm_char(A) do { \ + if ((A>=0x20)||(A==0x0A)||(A==0x0D)||(A==0x09)) { \ + wterm(A); \ + } else { \ + if (term_offset+2>=max_print_line) { \ + wterm_cr(); term_offset=0; \ + } \ + incr(term_offset); wterm('^'); \ + incr(term_offset); wterm('^'); \ + wterm(A+64); \ + } \ + } while (0) + +#define needs_wrapping(A,B) \ + (((A>=0xC0)&&(A<=0xDF)&&(B+2>=max_print_line))|| \ + ((A>=0xE0)&&(A<=0xEF)&&(B+3>=max_print_line))|| \ + ((A>=0xF0)&&(B+4>=max_print_line))) + +#define fix_term_offset(A) do { \ + if (needs_wrapping(A,term_offset)){ \ + wterm_cr(); term_offset=0; \ + } \ + } while (0) + +#define fix_log_offset(A) do { \ + if (needs_wrapping(A,file_offset)){ \ + wlog_cr(); file_offset=0; \ + } \ + } while (0) + +void print_char(int s) +{ /* prints a single byte */ + assert(s >= 0 && s < 256); + if (s == int_par(new_line_char_code)) { + if (selector < pseudo) { + print_ln(); + return; + } + } + switch (selector) { + case term_and_log: + fix_term_offset(s); + fix_log_offset(s); + wterm_char(s); + wlog(s); + incr(term_offset); + incr(file_offset); + if (term_offset == max_print_line) { + wterm_cr(); + term_offset = 0; + } + if (file_offset == max_print_line) { + wlog_cr(); + file_offset = 0; + } + break; + case log_only: + fix_log_offset(s); + wlog(s); + incr(file_offset); + if (file_offset == max_print_line) { + wlog_cr(); + file_offset = 0; + } + break; + case term_only: + fix_term_offset(s); + wterm_char(s); + incr(term_offset); + if (term_offset == max_print_line) { + wterm_cr(); + term_offset = 0; + } + break; + case no_print: + break; + case pseudo: + if (tally < trick_count) + trick_buf[tally % error_line] = (packed_ASCII_code) s; + break; + case new_string: + append_char(s); + break; /* we drop characters if the string space is full */ + default: + fprintf(write_file[selector], "%c", s); + } + incr(tally); +} + + +@ An entire string is output by calling |print|. Note that if we are outputting +the single standard ASCII character \.c, we could call |print("c")|, since +|"c"=99| is the number of a single-character string, as explained above. But +|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char| +routine when it knows that this is safe. (The present implementation +assumes that it is always safe to print a visible ASCII character.) +@^system dependencies@> + +The first 256 entries above the 17th unicode plane are used for a +special trick: when \TeX\ has to print items in that range, it will +instead print the character that results from substracting 0x110000 +from that value. This allows byte-oriented output to things like +\.{\\specials} and \.{\\pdfliterals}. Todo: Perhaps it would be useful +to do the same substraction while typesetting. + +@c +void print(int s) +{ /* prints string |s| */ + unsigned char *j, *l; /* current character code position */ + + if (s >= str_ptr) { + /* this can't happen */ + print_char('?'); + print_char('?'); + print_char('?'); + return; + } else if (s < STRING_OFFSET) { + if (s < 0) { + /* can't happen */ + print_char('?'); + print_char('?'); + print_char('?'); + } else { + /* TH not sure about this, disabled for now! */ + if ((false) && (selector > pseudo)) { + print_char(s); + return; /* internal strings are not expanded */ + } + if (s == int_par(new_line_char_code)) { + if (selector < pseudo) { + print_ln(); + return; + } + } + if (s <= 0x7F) { + print_char(s); + } else if (s <= 0x7FF) { + print_char(0xC0 + (s / 0x40)); + print_char(0x80 + (s % 0x40)); + } else if (s <= 0xFFFF) { + print_char(0xE0 + (s / 0x1000)); + print_char(0x80 + ((s % 0x1000) / 0x40)); + print_char(0x80 + ((s % 0x1000) % 0x40)); + } else if (s >= 0x110000) { + int c = s - 0x110000; + if (c >= 256) { + pdf_warning("print", "bad raw byte to print (c=", + true, false); + print_int(c); + tprint("), skipped."); + print_ln(); + } else { + print_char(c); + } + } else { + print_char(0xF0 + (s / 0x40000)); + print_char(0x80 + ((s % 0x40000) / 0x1000)); + print_char(0x80 + (((s % 0x40000) % 0x1000) / 0x40)); + print_char(0x80 + (((s % 0x40000) % 0x1000) % 0x40)); + } + } + return; + } + if (selector == new_string) { + append_string(str_string(s), (unsigned) str_length(s)); + return; + } + j = str_string(s); + l = j + str_length(s); + while (j < l) { + /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80 */ + /* I don't bother checking the last two bytes explicitly */ + if ((j < l - 4) && (*j == 0xF4) && (*(j + 1) == 0x90)) { + int c = (*(j + 2) - 128) * 64 + (*(j + 3) - 128); + assert(c >= 0 && c < 256); + print_char(c); + j = j + 4; + } else { + print_char(*j); + incr(j); + } + } +} + + +@ The procedure |print_nl| is like |print|, but it makes sure that the +string appears at the beginning of a new line. + +@c +void print_nlp(void) +{ /* move to beginning of a line */ + if (((term_offset > 0) && (odd(selector))) || + ((file_offset > 0) && (selector >= log_only))) + print_ln(); +} + +void print_nl(str_number s) +{ /* prints string |s| at beginning of line */ + print_nlp(); + print(s); +} + +@ |char *| versions of the same procedures + +@c +void tprint(const char *s) +{ + const unsigned char *ss = (const unsigned char *) s; + if (selector == new_string) { + append_string(ss, (unsigned) strlen(s)); + return; + } + while (*ss) + print_char(*ss++); +} + +void tprint_nl(const char *s) +{ + print_nlp(); + tprint(s); +} + +@ |slow_print| is the same as |print| nowadays, but the name is kept for now. + +@c +void slow_print(int s) +{ /* prints string |s| */ + print(s); +} + +@ Here is the very first thing that \TeX\ prints: a headline that identifies +the version number and format package. The |term_offset| variable is temporarily +incorrect, but the discrepancy is not serious since we assume that the banner +and format identifier together will occupy at most |max_print_line| +character positions. + +@c +void print_banner(const char *v, int e, int ver) +{ + boolean res; + int callback_id; + callback_id = callback_defined(start_run_callback); + if (callback_id == 0) { + if (ver < 0) + fprintf(term_out, "This is LuaTeX, Version %s-%d ", v, e); + else + fprintf(term_out, "This is LuaTeX, Version %s-%d (rev %d) ", v, e, + ver); + if (format_ident > 0) + slow_print(format_ident); + print_ln(); + if (shellenabledp) { + wterm(' '); + if (restrictedshell) + fprintf(term_out, "restricted "); + fprintf(term_out, "\\write18 enabled.\n"); + } + } else if (callback_id > 0) { + res = run_callback(callback_id, "->"); + } +} + +@ @c +void log_banner(const char *v, int e, int ver) +{ + const char *months[] = { " ", + "JAN", "FEB", "MAR", "APR", "MAY", "JUN", + "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" + }; + unsigned month = (unsigned) int_par(month_code); + if (month > 12) + month = 0; + if (ver < 0) + fprintf(log_file, "This is LuaTeX, Version %s-%d ", v, e); + else + fprintf(log_file, "This is LuaTeX, Version %s-%d (rev %d) ", v, e, ver); + slow_print(format_ident); + print_char(' '); + print_char(' '); + print_int(int_par(day_code)); + print_char(' '); + fprintf(log_file, months[month]); + print_char(' '); + print_int(int_par(year_code)); + print_char(' '); + print_two(int_par(time_code) / 60); + print_char(':'); + print_two(int_par(time_code) % 60); + if (shellenabledp) { + wlog_cr(); + wlog(' '); + if (restrictedshell) + fprintf(log_file, "restricted "); + fprintf(log_file, "\\write18 enabled."); + } + if (filelineerrorstylep) { + wlog_cr(); + fprintf(log_file, " file:line:error style messages enabled."); + } + if (parsefirstlinep) { + wlog_cr(); + fprintf(log_file, " %%&-line parsing enabled."); + } +} + +@ @c +void print_version_banner(void) +{ + fprintf(term_out, ptexbanner); +} + +@ The procedure |print_esc| prints a string that is preceded by +the user's escape character (which is usually a backslash). + +@c +void print_esc(str_number s) +{ /* prints escape character, then |s| */ + int c; /* the escape character code */ + /* Set variable |c| to the current escape character */ + c = int_par(escape_char_code); + if (c >= 0 && c < STRING_OFFSET) + print(c); + print(s); +} + +@ @c +void tprint_esc(const char *s) +{ /* prints escape character, then |s| */ + int c; /* the escape character code */ + /* Set variable |c| to the current escape character */ + c = int_par(escape_char_code); + if (c >= 0 && c < STRING_OFFSET) + print(c); + tprint(s); +} + +@ An array of digits in the range |0..15| is printed by |print_the_digs|. + +@c +void print_the_digs(eight_bits k) +{ + /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */ + while (k-- > 0) { + if (dig[k] < 10) + print_char('0' + dig[k]); + else + print_char('A' - 10 + dig[k]); + } +} + +@ The following procedure, which prints out the decimal representation of a +given integer |n|, has been written carefully so that it works properly +if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div| +to negative arguments, since such operations are not implemented consistently +by all PASCAL compilers. + +@c +void print_int(longinteger n) +{ /* prints an integer in decimal form */ + int k; /* index to current digit; we assume that $|n|<10^{23}$ */ + longinteger m; /* used to negate |n| in possibly dangerous cases */ + k = 0; + if (n < 0) { + print_char('-'); + if (n > -100000000) { + n = -n; + } else { + m = -1 - n; + n = m / 10; + m = (m % 10) + 1; + k = 1; + if (m < 10) + dig[0] = (int) m; + else { + dig[0] = 0; + incr(n); + } + } + } + do { + dig[k] = (int) (n % 10); + n = n / 10; + incr(k); + } while (n != 0); + print_the_digs((eight_bits) k); +} + + +@ Here is a trivial procedure to print two digits; it is usually called with +a parameter in the range |0<=n<=99|. + +@c +void print_two(int n) +{ /* prints two least significant digits */ + n = abs(n) % 100; + print_char('0' + (n / 10)); + print_char('0' + (n % 10)); +} + + +@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. + +@c +void print_hex(int n) +{ /* prints a positive integer in hexadecimal form */ + int k; /* index to current digit; we assume that $0\L n<16^{22}$ */ + k = 0; + print_char('"'); + do { + dig[k] = n % 16; + n = n / 16; + incr(k); + } while (n != 0); + print_the_digs((eight_bits) k); +} + + +@ Roman numerals are produced by the |print_roman_int| routine. Readers +who like puzzles might enjoy trying to figure out how this tricky code +works; therefore no explanation will be given. Notice that 1990 yields +\.{mcmxc}, not \.{mxm}. + +@c +void print_roman_int(int n) +{ + char *j, *k; /* mysterious indices */ + nonnegative_integer u, v; /* mysterious numbers */ + char mystery[] = "m2d5c2l5x2v5i"; + j = (char *) mystery; + v = 1000; + while (1) { + while (n >= (int) v) { + print_char(*j); + n = n - (int) v; + } + if (n <= 0) + return; /* nonpositive input produces no output */ + k = j + 2; + u = v / (*(k - 1) - '0'); + if (*(k - 1) == '2') { + k = k + 2; + u = u / (*(k - 1) - '0'); + } + if (n + (int) u >= (int) v) { + print_char(*k); + n = n + (int) u; + } else { + j = j + 2; + v = v / (*(j - 1) - '0'); + } + } +} + + +@ The |print| subroutine will not print a string that is still being +created. The following procedure will. + +@c +void print_current_string(void) +{ /* prints a yet-unmade string */ + unsigned j = 0; /* points to current character code */ + while (j < cur_length) + print_char(cur_string[j++]); +} + +@ The procedure |print_cs| prints the name of a control sequence, given +a pointer to its address in |eqtb|. A space is printed after the name +unless it is a single nonletter or an active character. This procedure +might be invoked with invalid data, so it is ``extra robust.'' The +individual characters must be printed one at a time using |print|, since +they may be unprintable. + +@c +void print_cs(int p) +{ /* prints a purported control sequence */ + str_number t = cs_text(p); + if (p < hash_base) { /* nullcs */ + if (p == null_cs) { + tprint_esc("csname"); + tprint_esc("endcsname"); + } else { + tprint_esc("IMPOSSIBLE."); + } + } else if ((p >= undefined_control_sequence) && + ((p <= eqtb_size) || p > eqtb_size + hash_extra)) { + tprint_esc("IMPOSSIBLE."); + } else if (t >= str_ptr) { + tprint_esc("NONEXISTENT."); + } else { + if (is_active_cs(t)) { + print(active_cs_value(t)); + } else { + print_esc(t); + if (single_letter(t)) { + if (get_cat_code(int_par(cat_code_table_code), + pool_to_unichar(str_string(t))) == letter_cmd) + print_char(' '); + } else { + print_char(' '); + } + } + } +} + + +@ Here is a similar procedure; it avoids the error checks, and it never +prints a space after the control sequence. + +@c +void sprint_cs(pointer p) +{ /* prints a control sequence */ + str_number t; + if (p == null_cs) { + tprint_esc("csname"); + tprint_esc("endcsname"); + } else { + t = cs_text(p); + if (is_active_cs(t)) + print(active_cs_value(t)); + else + print_esc(t); + } +} + + +@ This procedure is never called when |interaction<scroll_mode|. +@c +void prompt_input(const char *s) +{ + wake_up_terminal(); + tprint(s); + term_input(); +} + + +@ Then there is a subroutine that prints glue stretch and shrink, possibly +followed by the name of finite units: + +@c +void print_glue(scaled d, int order, const char *s) +{ /* prints a glue component */ + print_scaled(d); + if ((order < normal) || (order > filll)) { + tprint("foul"); + } else if (order > normal) { + tprint("fi"); + while (order > sfi) { + print_char('l'); + decr(order); + } + } else if (s != NULL) { + tprint(s); + } +} + +@ The next subroutine prints a whole glue specification +@c +void print_spec(int p, const char *s) +{ /* prints a glue specification */ + if (p < 0) { + print_char('*'); + } else { + print_scaled(width(p)); + if (s != NULL) + tprint(s); + if (stretch(p) != 0) { + tprint(" plus "); + print_glue(stretch(p), stretch_order(p), s); + } + if (shrink(p) != 0) { + tprint(" minus "); + print_glue(shrink(p), shrink_order(p), s); + } + } +} + + +@ We can reinforce our knowledge of the data structures just introduced +by considering two procedures that display a list in symbolic form. +The first of these, called |short_display|, is used in ``overfull box'' +messages to give the top-level description of a list. The other one, +called |show_node_list|, prints a detailed description of exactly what +is in the data structure. + +The philosophy of |short_display| is to ignore the fine points about exactly +what is inside boxes, except that ligatures and discretionary breaks are +expanded. As a result, |short_display| is a recursive procedure, but the +recursion is never more than one level deep. +@^recursion@> + +A global variable |font_in_short_display| keeps track of the font code that +is assumed to be present when |short_display| begins; deviations from this +font will be printed. + +@c +int font_in_short_display; /* an internal font number */ + + +@ Boxes, rules, inserts, whatsits, marks, and things in general that are +sort of ``complicated'' are indicated only by printing `\.{[]}'. + +@c +void print_font_identifier(internal_font_number f) +{ + str_number fonttext; + if (pdf_font_blink(f) == null_font) + fonttext = font_id_text(f); + else + fonttext = font_id_text(pdf_font_blink(f)); + if (fonttext > 0) { + print_esc(fonttext); + } else { + tprint_esc("FONT"); + if (pdf_font_blink(f) == null_font) + print_int(f); + else + print_int(pdf_font_blink(f)); + } + if (int_par(pdf_tracing_fonts_code) > 0) { + tprint(" ("); + print_font_name(f); + if (font_size(f) != font_dsize(f)) { + tprint("@@"); + print_scaled(font_size(f)); + tprint("pt"); + } + print_char(')'); + } else if (font_expand_ratio(f) != 0) { + tprint(" ("); + if (font_expand_ratio(f) > 0) + print_char('+'); + print_int(font_expand_ratio(f)); + print_char(')'); + } +} + +@ @c +void short_display(int p) +{ /* prints highlights of list |p| */ + while (p != null) { + if (is_char_node(p)) { + if (lig_ptr(p) != null) { + short_display(lig_ptr(p)); + } else { + if (font(p) != font_in_short_display) { + if (!is_valid_font(font(p))) + print_char('*'); + else + print_font_identifier(font(p)); + print_char(' '); + font_in_short_display = font(p); + } + print(character(p)); + } + } else { + /* Print a short indication of the contents of node |p| */ + print_short_node_contents(p); + } + p = vlink(p); + } +} + + +@ The |show_node_list| routine requires some auxiliary subroutines: one to +print a font-and-character combination, one to print a token list without +its reference count, and one to print a rule dimension. + +@c +void print_font_and_char(int p) +{ /* prints |char_node| data */ + if (!is_valid_font(font(p))) + print_char('*'); + else + print_font_identifier(font(p)); + print_char(' '); + print(character(p)); +} + +@ @c +void print_mark(int p) +{ /* prints token list data in braces */ + print_char('{'); + if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) + tprint_esc("CLOBBERED."); + else + show_token_list(token_link(p), null, max_print_line - 10); + print_char('}'); +} + +@ @c +void print_rule_dimen(scaled d) +{ /* prints dimension in rule node */ + if (is_running(d)) + print_char('*'); + else + print_scaled(d); +} + + +@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive, +@^recursion@> +up to a given maximum number of levels. The history of nesting is indicated +by the current string, which will be printed at the beginning of each line; +the length of this string, namely |cur_length|, is the depth of nesting. + +A global variable called |depth_threshold| is used to record the maximum +depth of nesting for which |show_node_list| will show information. If we +have |depth_threshold=0|, for example, only the top level information will +be given and no sublists will be traversed. Another global variable, called +|breadth_max|, tells the maximum number of items to show at each level; +|breadth_max| had better be positive, or you won't see anything. + +@c +int depth_threshold; /* maximum nesting depth in box displays */ +int breadth_max; /* maximum number of items shown at the same list level */ + + +@ The recursive machinery is started by calling |show_box|. + +@c +void show_box(halfword p) +{ + /* Assign the values |depth_threshold:=show_box_depth| and + |breadth_max:=show_box_breadth| */ + depth_threshold = int_par(show_box_depth_code); + breadth_max = int_par(show_box_breadth_code); + + if (breadth_max <= 0) + breadth_max = 5; + show_node_list(p); /* the show starts at |p| */ + print_ln(); +} + + +@ Helper for debugging purposes + +@c +void short_display_n(int p, int m) +{ /* prints highlights of list |p| */ + int i = 0; + font_in_short_display = null_font; + if (p == null) + return; + while (p != null) { + if (is_char_node(p)) { + if (p <= max_halfword) { + if (font(p) != font_in_short_display) { + if (!is_valid_font(font(p))) + print_char('*'); + else + print_font_identifier(font(p)); + print_char(' '); + font_in_short_display = font(p); + } + print(character(p)); + } + } else { + if ((type(p) == glue_node) || + (type(p) == disc_node) || + (type(p) == penalty_node) || + ((type(p) == kern_node) && (subtype(p) == explicit))) + incr(i); + if (i >= m) + return; + if (type(p) == disc_node) { + print_char('|'); + short_display(vlink(pre_break(p))); + print_char('|'); + short_display(vlink(post_break(p))); + print_char('|'); + } else { + /* Print a short indication of the contents of node |p| */ + print_short_node_contents(p); + } + } + p = vlink(p); + if (p == null) + return; + } + update_terminal(); +} + + +@ When debugging a macro package, it can be useful to see the exact +control sequence names in the format file. For example, if ten new +csnames appear, it's nice to know what they are, to help pinpoint where +they came from. (This isn't a truly ``basic'' printing procedure, but +that's a convenient module in which to put it.) + +@c +void print_csnames(int hstart, int hfinish) +{ + int h; + unsigned char *c, *l; + fprintf(stderr, "fmtdebug:csnames from %d to %d:", (int) hstart, + (int) hfinish); + for (h = hstart; h <= hfinish; h++) { + if (cs_text(h) > 0) { /* if have anything at this position */ + c = str_string(cs_text(h)); + l = c + str_length(cs_text(h)); + while (c < l) { + fputc(*c++, stderr); /* print the characters */ + } + fprintf(stderr, "|"); + } + } +} + + +@ A helper for printing file:line:error style messages. Look for a +filename in |full_source_filename_stack|, and if we fail to find +one fall back on the non-file:line:error style. + +@c +void print_file_line(void) +{ + int level; + level = in_open; + while ((level > 0) && (full_source_filename_stack[level] == 0)) + decr(level); + if (level == 0) { + tprint_nl("! "); + } else { + tprint_nl(""); + tprint(full_source_filename_stack[level]); + print_char(':'); + if (level == in_open) + print_int(line); + else + print_int(line_stack[iindex + 1 - (in_open - level)]); + tprint(": "); + } +} + + +@ \TeX\ is occasionally supposed to print diagnostic information that +goes only into the transcript file, unless |tracing_online| is positive. +Here are two routines that adjust the destination of print commands: + +@c +void begin_diagnostic(void) +{ /* prepare to do some tracing */ + global_old_setting = selector; + if ((int_par(tracing_online_code) <= 0) && (selector == term_and_log)) { + decr(selector); + if (history == spotless) + history = warning_issued; + } +} + +@ @c +void end_diagnostic(boolean blank_line) +{ /* restore proper conditions after tracing */ + tprint_nl(""); + if (blank_line) + print_ln(); + selector = global_old_setting; +} + + +@ Of course we had better declare another global variable, if the previous +routines are going to work. + +@c +int global_old_setting; diff --git a/Build/source/texk/web2c/luatexdir/tex/scanning.h b/Build/source/texk/web2c/luatexdir/tex/scanning.h new file mode 100644 index 00000000000..853a22a9a46 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/scanning.h @@ -0,0 +1,111 @@ +/* scanning.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: scanning.h 3376 2010-01-25 16:50:23Z taco $ */ + +#ifndef SCANNING_H +# define SCANNING_H + +typedef enum { + int_val_level = 0, /* integer values */ + attr_val_level, /* integer values */ + dimen_val_level, /* dimension values */ + glue_val_level, /* glue specifications */ + mu_val_level, /* math glue specifications */ + dir_val_level, /* directions */ + ident_val_level, /* font identifier */ + tok_val_level, /* token lists */ +} value_level_code; + +extern void scan_left_brace(void); +extern void scan_optional_equals(void); + +extern int cur_val; /* value returned by numeric scanners */ +extern int cur_val1; /* delcodes are sometimes 51 digits */ +extern int cur_val_level; /* the ``level'' of this value */ + +extern void scan_something_simple(halfword cmd, halfword subitem); +extern void scan_something_internal(int level, boolean negative); + + +extern void scan_limited_int(int max, const char *name); + +# define scan_register_num() scan_limited_int(65535,"register code") +# define scan_mark_num() scan_limited_int(65535,"marks code") +# define scan_char_num() scan_limited_int(biggest_char,"character code") +# define scan_four_bit_int() scan_limited_int(15,NULL) +# define scan_math_family_int() scan_limited_int(265,"math family") +# define scan_real_fifteen_bit_int() scan_limited_int(32767,"mathchar") +# define scan_big_fifteen_bit_int() scan_limited_int(0x7FFFFFF,"extended mathchar") +# define scan_twenty_seven_bit_int() scan_limited_int(0777777777,"delimiter code") + +extern void scan_fifteen_bit_int(void); +extern void scan_fifty_one_bit_int(void); +extern void scan_string_argument(void); +extern void scan_four_bit_int_or_18(void); + +# define octal_token (other_token+'\'') /* apostrophe, indicates an octal constant */ +# define hex_token (other_token+'"') /* double quote, indicates a hex constant */ +# define alpha_token (other_token+'`') /* reverse apostrophe, precedes alpha constants */ +# define point_token (other_token+'.') /* decimal point */ +# define continental_point_token (other_token+',') /* decimal point, Eurostyle */ +# define infinity 017777777777 /* the largest positive value that \TeX\ knows */ +# define zero_token (other_token+'0') /* zero, the smallest digit */ +# define A_token (letter_token+'A') /* the smallest special hex digit */ +# define other_A_token (other_token+'A') + /* special hex digit of type |other_char| */ + +extern int radix; + +extern void scan_int(void); + +extern int cur_order; + +# define scan_normal_dimen() scan_dimen(false,false,false) + +extern void scan_dimen(boolean mu, boolean inf, boolean shortcut); +extern void scan_glue(int level); +extern void scan_scaled(void); + +extern halfword the_toks(void); +extern str_number the_scanned_result(void); +extern void set_font_dimen(void); +extern void get_font_dimen(void); + +# define default_rule 26214 /* 0.4\thinspace pt */ + +extern halfword scan_rule_spec(void); + +extern void scan_font_ident(void); +extern void scan_general_text(void); +extern void get_x_or_protected(void); +extern halfword scan_toks(boolean macrodef, boolean xpand); + + +extern void scan_normal_glue(void); +extern void scan_mu_glue(void); + +extern int add_or_sub(int x, int y, int max_answer, boolean negative); +extern int quotient(int n, int d); +extern int fract(int x, int n, int d, int max_answer); +extern void scan_expr(void); + + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/scanning.w b/Build/source/texk/web2c/luatexdir/tex/scanning.w new file mode 100644 index 00000000000..43466f400d8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/scanning.w @@ -0,0 +1,2641 @@ +% scanning.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: scanning.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/scanning.w $"; + + +@ @c +#define prev_depth cur_list.prev_depth_field +#define space_factor cur_list.space_factor_field +#define par_shape_ptr equiv(par_shape_loc) +#define font_id_text(A) cs_text(font_id_base+(A)) + +#define attribute(A) eqtb[attribute_base+(A)].hh.rh +#define dimen(A) eqtb[scaled_base+(A)].hh.rh +#undef skip +#define skip(A) eqtb[skip_base+(A)].hh.rh +#define mu_skip(A) eqtb[mu_skip_base+(A)].hh.rh +#define count(A) eqtb[count_base+(A)].hh.rh +#define box(A) equiv(box_base+(A)) + +#define text_direction int_par(text_direction_code) +#define body_direction int_par(body_direction_code) + + +@ Let's turn now to some procedures that \TeX\ calls upon frequently to digest +certain kinds of patterns in the input. Most of these are quite simple; +some are quite elaborate. Almost all of the routines call |get_x_token|, +which can cause them to be invoked recursively. + +The |scan_left_brace| routine is called when a left brace is supposed to be +the next non-blank token. (The term ``left brace'' means, more precisely, +a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to +appear before the |left_brace|. + +@c +void scan_left_brace(void) +{ /* reads a mandatory |left_brace| */ + /* Get the next non-blank non-relax non-call token */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd != left_brace_cmd) { + print_err("Missing { inserted"); + help4("A left brace was mandatory here, so I've put one in.", + "You might want to delete and/or insert some corrections", + "so that I will find a matching right brace soon.", + "If you're confused by all this, try typing `I}' now."); + back_error(); + cur_tok = left_brace_token + '{'; + cur_cmd = left_brace_cmd; + cur_chr = '{'; + incr(align_state); + } +} + + +@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded +by optional spaces; `\.{\\relax}' is not ignored here. + +@c +void scan_optional_equals(void) +{ + /* Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok != other_token + '=') + back_input(); +} + + +@ Here is a procedure that sounds an alarm when mu and non-mu units +are being switched. + +@c +static void mu_error(void) +{ + print_err("Incompatible glue units"); + help1("I'm going to assume that 1mu=1pt when they're mixed."); + error(); +} + + +@ The next routine `|scan_something_internal|' is used to fetch internal +numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}' +when expanding constructions like `\.{\\the\\toks0}' and +`\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int| +procedure, which calls |scan_something_internal|; on the other hand, +|scan_something_internal| also calls |scan_int|, for constructions like +`\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we +have to declare |scan_int| as a |forward| procedure. A few other +procedures are also declared at this point. + +\TeX\ doesn't know exactly what to expect when +|scan_something_internal| begins. For example, an integer or +dimension or glue value could occur immediately after `\.{\\hskip}'; +and one can even say \.{\\the} with respect to token lists in +constructions like `\.{\\xdef\\o\{\\the\\output\}}'. On the other +hand, only integers are allowed after a construction like +`\.{\\count}'. To handle the various possibilities, +|scan_something_internal| has a |level| parameter, which tells the +``highest'' kind of quantity that |scan_something_internal| is allowed +to produce. Eight levels are distinguished, namely |int_val|, +|attr_val|, |dimen_val|, |glue_val|, |mu_val|, |dir_val|, |ident_val|, +and |tok_val|. + +The output of |scan_something_internal| (and of the other routines +|scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global +variable |cur_val|, and its level is put into |cur_val_level|. The highest +values of |cur_val_level| are special: |mu_val| is used only when +|cur_val| points to something in a ``muskip'' register, or to one of the +three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip}; +|ident_val| is used only when |cur_val| points to a font identifier; +|tok_val| is used only when |cur_val| points to |null| or to the reference +count of a token list. The last two cases are allowed only when +|scan_something_internal| is called with |level=tok_val|. + +If the output is glue, |cur_val| will point to a glue specification, and +the reference count of that glue will have been updated to reflect this +reference; if the output is a nonempty token list, |cur_val| will point to +its reference count, but in this case the count will not have been updated. +Otherwise |cur_val| will contain the integer or scaled value in question. + +@c +int cur_val; /* value returned by numeric scanners */ +int cur_val1; /* delcodes are sometimes 51 digits */ +int cur_val_level; /* the ``level'' of this value */ + +#define scanned_result(A,B) do { \ + cur_val=A; \ + cur_val_level=B; \ + } while (0) + + +@ When a |glue_val| changes to a |dimen_val|, we use the width component +of the glue; there is no need to decrease the reference count, since it +has not yet been increased. When a |dimen_val| changes to an |int_val|, +we use scaled points so that the value doesn't actually change. And when a +|mu_val| changes to a |glue_val|, the value doesn't change either. + +@c +static void downgrade_cur_val(boolean delete_glue) +{ + halfword m; + if (cur_val_level == glue_val_level) { + m = cur_val; + cur_val = width(m); + if (delete_glue) + delete_glue_ref(m); + } else if (cur_val_level == mu_val_level) { + mu_error(); + } + decr(cur_val_level); +} + +static void negate_cur_val(boolean delete_glue) +{ + halfword m; + if (cur_val_level >= glue_val_level) { + m = cur_val; + cur_val = new_spec(m); + if (delete_glue) + delete_glue_ref(m); + /* Negate all three glue components of |cur_val| */ + negate(width(cur_val)); + negate(stretch(cur_val)); + negate(shrink(cur_val)); + + } else { + negate(cur_val); + } +} + + +@ Some of the internal items can be fetched both routines, +and these have been split off into the next routine, that +returns true if the command code was understood + +@c +static boolean short_scan_something_internal(int cmd, int chr, int level, + boolean negative) +{ + halfword m; /* |chr_code| part of the operand token */ + halfword q; /* general purpose index */ + int p; /* index into |nest| */ + int save_cur_chr; + boolean succeeded = true; + m = chr; + switch (cmd) { + case assign_toks_cmd: + scanned_result(equiv(m), tok_val_level); + break; + case assign_int_cmd: + scanned_result(eqtb[m].cint, int_val_level); + break; + case assign_attr_cmd: + scanned_result(eqtb[m].cint, int_val_level); + break; + case assign_dir_cmd: + scanned_result(eqtb[m].cint, dir_val_level); + break; + case assign_dimen_cmd: + scanned_result(eqtb[m].cint, dimen_val_level); + break; + case assign_glue_cmd: + scanned_result(equiv(m), glue_val_level); + break; + case assign_mu_glue_cmd: + scanned_result(equiv(m), mu_val_level); + break; + case math_style_cmd: + scanned_result(m, int_val_level); + break; + case set_aux_cmd: + /* Fetch the |space_factor| or the |prev_depth| */ + if (abs(cur_list.mode_field) != m) { + print_err("Improper "); + print_cmd_chr(set_aux_cmd, m); + help4("You can refer to \\spacefactor only in horizontal mode;", + "you can refer to \\prevdepth only in vertical mode; and", + "neither of these is meaningful inside \\write. So", + "I'm forgetting what you said and using zero instead."); + error(); + if (level != tok_val_level) + scanned_result(0, dimen_val_level); + else + scanned_result(0, int_val_level); + } else if (m == vmode) { + scanned_result(prev_depth, dimen_val_level); + } else { + scanned_result(space_factor, int_val_level); + } + break; + case set_prev_graf_cmd: + /* Fetch the |prev_graf| */ + if (cur_list.mode_field == 0) { + scanned_result(0, int_val_level); /* |prev_graf=0| within \.{\\write} */ + } else { + p = nest_ptr; + while (abs(nest[p].mode_field) != vmode) + decr(p); + scanned_result(nest[p].pg_field, int_val_level); + } + break; + case set_page_int_cmd: + /* Fetch the |dead_cycles| or the |insert_penalties| */ + if (m == 0) + cur_val = dead_cycles; + else if (m == 2) + cur_val = interaction; /* interactionmode */ + else + cur_val = insert_penalties; + cur_val_level = int_val_level; + break; + case set_page_dimen_cmd: + /* Fetch something on the |page_so_far| */ + if ((page_contents == empty) && (!output_active)) { + if (m == 0) + cur_val = max_dimen; + else + cur_val = 0; + } else { + cur_val = page_so_far[m]; + } + cur_val_level = dimen_val_level; + break; + case set_tex_shape_cmd: + /* Fetch the |par_shape| size */ + if (par_shape_ptr == null) + cur_val = 0; + else + cur_val = vinfo(par_shape_ptr + 1); + cur_val_level = int_val_level; + break; + case set_etex_shape_cmd: + /* Fetch a penalties array element */ + scan_int(); + if ((equiv(m) == null) || (cur_val < 0)) { + cur_val = 0; + } else { + if (cur_val > penalty(equiv(m))) + cur_val = penalty(equiv(m)); + cur_val = penalty(equiv(m) + cur_val); + } + cur_val_level = int_val_level; + break; + case char_given_cmd: + case math_given_cmd: + case omath_given_cmd: + case xmath_given_cmd: + scanned_result(cur_chr, int_val_level); + break; + case last_item_cmd: + /* Because the items in this case directly refer to |cur_chr|, + it needs to be saved and restored */ + save_cur_chr = cur_chr; + cur_chr = chr; + /* Fetch an item in the current node, if appropriate */ + /* Here is where \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\lastskip} are + implemented. The reference count for \.{\\lastskip} will be updated later. + + We also handle \.{\\inputlineno} and \.{\\badness} here, because they are + legal in similar contexts. */ + + if (m >= input_line_no_code) { + if (m >= eTeX_glue) { + /* Process an expression and |return| */ + if (m < eTeX_mu) { + switch (m) { + case mu_to_glue_code: + scan_mu_glue(); + break; + }; /* there are no other cases */ + cur_val_level = glue_val_level; + } else if (m < eTeX_expr) { + switch (m) { + case glue_to_mu_code: + scan_normal_glue(); + break; + } /* there are no other cases */ + cur_val_level = mu_val_level; + } else { + cur_val_level = m - eTeX_expr + int_val_level; + scan_expr(); + } + /* This code for reducing |cur_val_level| and\slash or negating the + result is similar to the one for all the other cases of + |scan_something_internal|, with the difference that |scan_expr| has + already increased the reference count of a glue specification. + */ + while (cur_val_level > level) { + downgrade_cur_val(true); + } + if (negative) { + negate_cur_val(true); + } + return succeeded; + + } else if (m >= eTeX_dim) { + switch (m) { + case font_char_wd_code: + case font_char_ht_code: + case font_char_dp_code: + case font_char_ic_code: + scan_font_ident(); + q = cur_val; + scan_char_num(); + if (char_exists(q, cur_val)) { + switch (m) { + case font_char_wd_code: + cur_val = char_width(q, cur_val); + break; + case font_char_ht_code: + cur_val = char_height(q, cur_val); + break; + case font_char_dp_code: + cur_val = char_depth(q, cur_val); + break; + case font_char_ic_code: + cur_val = char_italic(q, cur_val); + break; + } /* there are no other cases */ + } else { + cur_val = 0; + } + break; + case par_shape_length_code: + case par_shape_indent_code: + case par_shape_dimen_code: + q = cur_chr - par_shape_length_code; + scan_int(); + if ((par_shape_ptr == null) || (cur_val <= 0)) { + cur_val = 0; + } else { + if (q == 2) { + q = cur_val % 2; + cur_val = (cur_val + q) / 2; + } + if (cur_val > vinfo(par_shape_ptr + 1)) + cur_val = vinfo(par_shape_ptr + 1); + cur_val = + varmem[par_shape_ptr + 2 * cur_val - q + 1].cint; + } + cur_val_level = dimen_val_level; + break; + case glue_stretch_code: + case glue_shrink_code: + scan_normal_glue(); + q = cur_val; + if (m == glue_stretch_code) + cur_val = stretch(q); + else + cur_val = shrink(q); + delete_glue_ref(q); + break; + } /* there are no other cases */ + cur_val_level = dimen_val_level; + } else { + switch (m) { + case input_line_no_code: + cur_val = line; + break; + case badness_code: + cur_val = last_badness; + break; + case pdftex_version_code: + cur_val = pdftex_version; + break; + case luatex_version_code: + cur_val = get_luatexversion(); + break; + case pdf_last_obj_code: + cur_val = pdf_last_obj; + break; + case pdf_last_xform_code: + cur_val = pdf_last_xform; + break; + case pdf_last_ximage_code: + cur_val = pdf_last_ximage; + break; + case pdf_last_ximage_pages_code: + cur_val = pdf_last_ximage_pages; + break; + case pdf_last_annot_code: + cur_val = pdf_last_annot; + break; + case pdf_last_x_pos_code: + cur_val = pdf_last_pos.h; + break; + case pdf_last_y_pos_code: + cur_val = pdf_last_pos.v; + break; + case pdf_retval_code: + cur_val = pdf_retval; + break; + case pdf_last_ximage_colordepth_code: + cur_val = pdf_last_ximage_colordepth; + break; + case random_seed_code: + cur_val = random_seed; + break; + case pdf_last_link_code: + cur_val = pdf_last_link; + break; + case Aleph_version_code: + cur_val = Aleph_version; + break; + case Omega_version_code: + cur_val = Omega_version; + break; + case eTeX_version_code: + cur_val = eTeX_version; + break; + case Aleph_minor_version_code: + cur_val = Aleph_minor_version; + break; + case Omega_minor_version_code: + cur_val = Omega_minor_version; + break; + case eTeX_minor_version_code: + cur_val = eTeX_minor_version; + break; + case current_group_level_code: + cur_val = cur_level - level_one; + break; + case current_group_type_code: + cur_val = cur_group; + break; + case current_if_level_code: + q = cond_ptr; + cur_val = 0; + while (q != null) { + incr(cur_val); + q = vlink(q); + } + break; + case current_if_type_code: + if (cond_ptr == null) + cur_val = 0; + else if (cur_if < unless_code) + cur_val = cur_if + 1; + else + cur_val = -(cur_if - unless_code + 1); + break; + case current_if_branch_code: + if ((if_limit == or_code) || (if_limit == else_code)) + cur_val = 1; + else if (if_limit == fi_code) + cur_val = -1; + else + cur_val = 0; + break; + case glue_stretch_order_code: + case glue_shrink_order_code: + scan_normal_glue(); + q = cur_val; + if (m == glue_stretch_order_code) + cur_val = stretch_order(q); + else + cur_val = shrink_order(q); + delete_glue_ref(q); + break; + + } /* there are no other cases */ + cur_val_level = int_val_level; + } + } else { + if (cur_chr == glue_val_level) + cur_val = zero_glue; + else + cur_val = 0; + if (cur_chr == last_node_type_code) { + cur_val_level = int_val_level; + if ((cur_list.tail_field == cur_list.head_field) + || (cur_list.mode_field == 0)) + cur_val = -1; + } else { + cur_val_level = cur_chr; /* assumes identical values */ + } + if ((cur_list.tail_field != contrib_head) && + !is_char_node(cur_list.tail_field) && + (cur_list.mode_field != 0)) { + switch (cur_chr) { + case lastpenalty_code: + if (type(cur_list.tail_field) == penalty_node) + cur_val = penalty(cur_list.tail_field); + break; + case lastkern_code: + if (type(cur_list.tail_field) == kern_node) + cur_val = width(cur_list.tail_field); + break; + case lastskip_code: + if (type(cur_list.tail_field) == glue_node) + cur_val = glue_ptr(cur_list.tail_field); + if (subtype(cur_list.tail_field) == mu_glue) + cur_val_level = mu_val_level; + break; + case last_node_type_code: + cur_val = visible_last_node_type(cur_list.tail_field); + break; + } /* there are no other cases */ + } else if ((cur_list.mode_field == vmode) + && (cur_list.tail_field == cur_list.head_field)) { + switch (cur_chr) { + case lastpenalty_code: + cur_val = last_penalty; + break; + case lastkern_code: + cur_val = last_kern; + break; + case lastskip_code: + if (last_glue != max_halfword) + cur_val = last_glue; + break; + case last_node_type_code: + cur_val = last_node_type; + break; + } /* there are no other cases */ + } + } + cur_chr = save_cur_chr; + break; + default: + succeeded = false; + } + if (succeeded) { + while (cur_val_level > level) { + /* Convert |cur_val| to a lower level */ + downgrade_cur_val(false); + } + /* Fix the reference count, if any, and negate |cur_val| if |negative| */ + /* If |cur_val| points to a glue specification at this point, the reference + count for the glue does not yet include the reference by |cur_val|. + If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|. + */ + if (negative) { + negate_cur_val(false); + } else if ((cur_val_level >= glue_val_level) + && (cur_val_level <= mu_val_level)) { + add_glue_ref(cur_val); + } + } + return succeeded; +} + +@ First, here is a short routine that is called from lua code. All +the real work is delegated to |short_scan_something_internal| that +is shared between this routine and |scan_something_internal|. + +@c +void scan_something_simple(halfword cmd, halfword subitem) +{ + /* negative is never true */ + if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) { + /* Complain that |texlib| can not do this; give zero result */ + print_err("You can't use `"); + print_cmd_chr((quarterword) cmd, subitem); + tprint("' as tex library index"); + help1("I'm forgetting what you said and using zero instead."); + error(); + scanned_result(0, int_val_level); + } +} + + + +@ OK, we're ready for |scan_something_internal| itself. A second parameter, +|negative|, is set |true| if the value that is found should be negated. +It is assumed that |cur_cmd| and |cur_chr| represent the first token of +the internal quantity to be scanned; an error will be signalled if +|cur_cmd<min_internal| or |cur_cmd>max_internal|. + +@c +void scan_something_internal(int level, boolean negative) +{ + /* fetch an internal parameter */ + halfword m; /* |chr_code| part of the operand token */ + int n, k; /* accumulators */ + RESTART: + m = cur_chr; + if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) { + switch (cur_cmd) { + case def_char_code_cmd: + /* Fetch a character code from some table */ + scan_char_num(); + if (m == math_code_base) { + cur_val1 = get_math_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == lc_code_base) { + cur_val1 = get_lc_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == uc_code_base) { + cur_val1 = get_uc_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == sf_code_base) { + cur_val1 = get_sf_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == cat_code_base) { + cur_val1 = get_cat_code(int_par(cat_code_table_code), cur_val); + scanned_result(cur_val1, int_val_level); + } else { + confusion("def_char"); + } + break; + case def_del_code_cmd: + /* Fetch a character code from some table */ + scan_char_num(); + cur_val1 = get_del_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + break; + case extdef_math_code_cmd: + /* Fetch an extended math code table value */ + scan_char_num(); + cur_val1 = get_math_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + break; + case toks_register_cmd: + case set_font_cmd: + case def_font_cmd: + case letterspace_font_cmd: + case pdf_copy_font_cmd: + /* Fetch a token list or font identifier, provided that |level=tok_val| */ + if (level != tok_val_level) { + print_err("Missing number, treated as zero"); + help3("A number should have been here; I inserted `0'.", + "(If you can't figure out why I needed to see a number,", + "look up `weird error' in the index to The TeXbook.)"); + back_error(); + scanned_result(0, dimen_val_level); + } else if (cur_cmd == toks_register_cmd) { + scan_register_num(); + m = toks_base + cur_val; + scanned_result(equiv(m), tok_val_level); + } else { + back_input(); + scan_font_ident(); + scanned_result(font_id_base + cur_val, ident_val_level); + } + break; + case def_family_cmd: + /* Fetch a math font identifier */ + scan_char_num(); + cur_val1 = fam_fnt(cur_val, m); + scanned_result(font_id_base + cur_val1, ident_val_level); + break; + case set_math_param_cmd: + /* Fetch a math param */ + cur_val1 = cur_chr; + get_token(); + if (cur_cmd != math_style_cmd) { + print_err("Missing math style, treated as \\displaystyle"); + help1 + ("A style should have been here; I inserted `\\displaystyle'."); + cur_val = display_style; + back_error(); + } else { + cur_val = cur_chr; + } + if (cur_val1 < math_param_first_mu_glue) { + if (cur_val1 == math_param_radical_degree_raise) { + cur_val1 = get_math_param(cur_val1, cur_chr); + scanned_result(cur_val1, int_val_level); + } else { + cur_val1 = get_math_param(cur_val1, cur_chr); + scanned_result(cur_val1, dimen_val_level); + } + } else { + cur_val1 = get_math_param(cur_val1, cur_chr); + if (cur_val1 == thin_mu_skip_code) + cur_val1 = glue_par(thin_mu_skip_code); + else if (cur_val1 == med_mu_skip_code) + cur_val1 = glue_par(med_mu_skip_code); + else if (cur_val1 == thick_mu_skip_code) + cur_val1 = glue_par(thick_mu_skip_code); + scanned_result(cur_val1, mu_val_level); + } + break; + case assign_box_dir_cmd: + scan_register_num(); + m = cur_val; + if (box(m) != null) + cur_val = box_dir(box(m)); + else + cur_val = 0; + cur_val_level = dir_val_level; + break; + case set_box_dimen_cmd: + /* Fetch a box dimension */ + scan_register_num(); + if (box(cur_val) == null) + cur_val = 0; + else + cur_val = varmem[box(cur_val) + m].cint; + cur_val_level = dimen_val_level; + break; + case assign_font_dimen_cmd: + /* Fetch a font dimension */ + get_font_dimen(); + break; + case assign_font_int_cmd: + /* Fetch a font integer */ + scan_font_ident(); + if (m == 0) { + scanned_result(hyphen_char(cur_val), int_val_level); + } else if (m == 1) { + scanned_result(skew_char(cur_val), int_val_level); + } else if (m == no_lig_code) { + scanned_result(test_no_ligatures(cur_val), int_val_level); + } else { + n = cur_val; + scan_char_num(); + k = cur_val; + switch (m) { + case lp_code_base: + scanned_result(get_lp_code(n, k), int_val_level); + break; + case rp_code_base: + scanned_result(get_rp_code(n, k), int_val_level); + break; + case ef_code_base: + scanned_result(get_ef_code(n, k), int_val_level); + break; + case tag_code: + scanned_result(get_tag_code(n, k), int_val_level); + break; + } + } + break; + case register_cmd: + /* Fetch a register */ + scan_register_num(); + switch (m) { + case int_val_level: + cur_val = count(cur_val); + break; + case attr_val_level: + cur_val = attribute(cur_val); + break; + case dimen_val_level: + cur_val = dimen(cur_val); + break; + case glue_val_level: + cur_val = skip(cur_val); + break; + case mu_val_level: + cur_val = mu_skip(cur_val); + break; + } /* there are no other cases */ + cur_val_level = m; + break; + case ignore_spaces_cmd: /* trap unexpandable primitives */ + if (cur_chr == 1) { + /* Reset |cur_tok| for unexpandable primitives, goto restart */ + /* This block deals with unexpandable \.{\\primitive} appearing at a spot where + an integer or an internal values should have been found. It fetches the + next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on the + primitive value of that token. No expansion takes place, because the + next token may be all sorts of things. This could trigger further + expansion creating new errors. + */ + get_token(); + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + } else { + cur_cmd = relax_cmd; + cur_chr = 0; + cur_tok = cs_token_flag + frozen_relax; + cur_cs = frozen_relax; + } + goto RESTART; + } + break; + default: + /* Complain that \.{\\the} can not do this; give zero result */ + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + tprint("' after \\the"); + help1("I'm forgetting what you said and using zero instead."); + error(); + if (level != tok_val_level) + scanned_result(0, dimen_val_level); + else + scanned_result(0, int_val_level); + break; + } + while (cur_val_level > level) { + /* Convert |cur_val| to a lower level */ + downgrade_cur_val(false); + } + /* Fix the reference count, if any, and negate |cur_val| if |negative| */ + /* If |cur_val| points to a glue specification at this point, the reference + count for the glue does not yet include the reference by |cur_val|. + If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|. + */ + if (negative) { + negate_cur_val(false); + } else if ((cur_val_level >= glue_val_level) && + (cur_val_level <= mu_val_level)) { + add_glue_ref(cur_val); + } + } +} + + +@ It is nice to have routines that say what they do, so the original +|scan_eight_bit_int| is superceded by |scan_register_num| and +|scan_mark_num|. It may become split up even further in the future. + +Many of the |restricted classes| routines are the essentially +the same except for the upper limit and the error message, so it makes +sense to combine these all into one function. + +@c +void scan_limited_int(int max, const char *name) +{ + char hlp[80]; + scan_int(); + if ((cur_val < 0) || (cur_val > max)) { + if (name == NULL) { + snprintf(hlp, 80, + "Since I expected to read a number between 0 and %d,", + max); + print_err("Bad number"); + } else { + char msg[80]; + snprintf(hlp, 80, "A %s must be between 0 and %d.", name, max); + snprintf(msg, 80, "Bad %s", name); + print_err(msg); + } + help2(hlp, "I changed this one to zero."); + int_error(cur_val); + cur_val = 0; + } +} + +@ @c +void scan_fifteen_bit_int(void) +{ + scan_real_fifteen_bit_int(); + cur_val = ((cur_val / 0x1000) * 0x1000000) + + (((cur_val % 0x1000) / 0x100) * 0x10000) + (cur_val % 0x100); +} + +@ @c +void scan_fifty_one_bit_int(void) +{ + int iiii; + scan_int(); + if ((cur_val < 0) || (cur_val > 0777777777)) { + print_err("Bad delimiter code"); + help2 + ("A numeric delimiter (first part) must be between 0 and 2^{27}-1.", + "I changed this one to zero."); + int_error(cur_val); + cur_val = 0; + } + iiii = cur_val; + scan_int(); + if ((cur_val < 0) || (cur_val > 0xFFFFFF)) { + print_err("Bad delimiter code"); + help2 + ("A numeric delimiter (second part) must be between 0 and 2^{24}-1.", + "I changed this one to zero."); + int_error(cur_val); + cur_val = 0; + } + cur_val1 = cur_val; + cur_val = iiii; +} + + +@ To be able to determine whether \.{\\write18} is enabled from within +\TeX\ we also implement \.{\\eof18}. We sort of cheat by having an +additional route |scan_four_bit_int_or_18| which is the same as +|scan_four_bit_int| except it also accepts the value 18. + +@c +void scan_four_bit_int_or_18(void) +{ + scan_int(); + if ((cur_val < 0) || ((cur_val > 15) && (cur_val != 18))) { + print_err("Bad number"); + help2("Since I expected to read a number between 0 and 15,", + "I changed this one to zero."); + int_error(cur_val); + cur_val = 0; + } +} + + +@ @c +void scan_string_argument(void) +{ + int s; + scan_left_brace(); + get_x_token(); + while ((cur_cmd != right_brace_cmd)) { + if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd)) { + str_room(1); + append_char(cur_chr); + } else if (cur_cmd == spacer_cmd) { + str_room(1); + append_char(' '); + } else { + tprint("Bad token appearing in string argument"); + } + get_x_token(); + } + s = make_string(); + /* todo: this was just conserving the string pool: */ +#if 0 + if (str_eq_str("mi",s)) s="mi"; + if (str_eq_str("mo",s)) s="mo"; + if (str_eq_str("mn",s)) s="mn"; +#endif + cur_val = s; +} + + +@ An integer number can be preceded by any number of spaces and `\.+' or +`\.-' signs. Then comes either a decimal constant (i.e., radix 10), an +octal constant (i.e., radix 8, preceded by~'), a hexadecimal constant +(radix 16, preceded by~"), an alphabetic constant (preceded by~`), or +an internal variable. After scanning is complete, +|cur_val| will contain the answer, which must be at most +$2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to +10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants, +otherwise |radix| is set to zero. An optional space follows a constant. + +@c +int radix; /* |scan_int| sets this to 8, 10, 16, or zero */ + + +@ The |scan_int| routine is used also to scan the integer part of a + fraction; for example, the `\.3' in `\.{3.14159}' will be found by + |scan_int|. The |scan_dimen| routine assumes that |cur_tok=point_token| + after the integer part of such a fraction has been scanned by |scan_int|, + and that the decimal point has been backed up to be scanned again. + +@c +void scan_int(void) +{ /* sets |cur_val| to an integer */ + boolean negative; /* should the answer be negated? */ + int m; /* |$2^{31}$ / radix|, the threshold of danger */ + int d; /* the digit just scanned */ + boolean vacuous; /* have no digits appeared? */ + boolean OK_so_far; /* has an error message been issued? */ + radix = 0; + OK_so_far = true; + /* Get the next non-blank non-sign token; set |negative| appropriately */ + negative = false; + do { + /* Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '-') { + negative = !negative; + cur_tok = other_token + '+'; + } + } while (cur_tok == other_token + '+'); + + RESTART: + if (cur_tok == alpha_token) { + /* Scan an alphabetic character code into |cur_val| */ + /* A space is ignored after an alphabetic character constant, so that + such constants behave like numeric ones. */ + get_token(); /* suppress macro expansion */ + if (cur_tok < cs_token_flag) { + cur_val = cur_chr; + if (cur_cmd <= right_brace_cmd) { + if (cur_cmd == right_brace_cmd) + incr(align_state); + else + decr(align_state); + } + } else { /* the value of a csname in this context is its name */ + str_number txt = cs_text(cur_tok - cs_token_flag); + if (is_active_cs(txt)) + cur_val = active_cs_value(txt); + else if (single_letter(txt)) + cur_val = pool_to_unichar(str_string(txt)); + else + cur_val = (biggest_char + 1); + } + if (cur_val > biggest_char) { + print_err("Improper alphabetic constant"); + help2("A one-character control sequence belongs after a ` mark.", + "So I'm essentially inserting \\0 here."); + cur_val = '0'; + back_error(); + } else { + /* Scan an optional space */ + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + } + + } else if (cur_tok == cs_token_flag + frozen_primitive) { + /* Reset |cur_tok| for unexpandable primitives, goto restart */ + /* This block deals with unexpandable \.{\\primitive} appearing at a spot where + an integer or an internal values should have been found. It fetches the + next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on the + primitive value of that token. No expansion takes place, because the + next token may be all sorts of things. This could trigger further + expansion creating new errors. + */ + get_token(); + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + } else { + cur_cmd = relax_cmd; + cur_chr = 0; + cur_tok = cs_token_flag + frozen_relax; + cur_cs = frozen_relax; + } + goto RESTART; + } else if (cur_cmd == math_style_cmd) { + cur_val = cur_chr; + } else if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + scan_something_internal(int_val_level, false); + } else { + /* Scan a numeric constant */ + radix = 10; + m = 214748364; + if (cur_tok == octal_token) { + radix = 8; + m = 02000000000; + get_x_token(); + } else if (cur_tok == hex_token) { + radix = 16; + m = 01000000000; + get_x_token(); + } + vacuous = true; + cur_val = 0; + /* Accumulate the constant until |cur_tok| is not a suitable digit */ + while (1) { + if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token) + && (cur_tok <= zero_token + 9)) { + d = cur_tok - zero_token; + } else if (radix == 16) { + if ((cur_tok <= A_token + 5) && (cur_tok >= A_token)) { + d = cur_tok - A_token + 10; + } else if ((cur_tok <= other_A_token + 5) + && (cur_tok >= other_A_token)) { + d = cur_tok - other_A_token + 10; + } else { + break; + } + } else { + break; + } + vacuous = false; + if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) { + if (OK_so_far) { + print_err("Number too big"); + help2 + ("I can only go up to 2147483647='17777777777=\"7FFFFFFF,", + "so I'm using that number instead of yours."); + error(); + cur_val = infinity; + OK_so_far = false; + } + } else { + cur_val = cur_val * radix + d; + } + get_x_token(); + } + if (vacuous) { + /* Express astonishment that no number was here */ + print_err("Missing number, treated as zero"); + help3("A number should have been here; I inserted `0'.", + "(If you can't figure out why I needed to see a number,", + "look up `weird error' in the index to The TeXbook.)"); + back_error(); + } else if (cur_cmd != spacer_cmd) { + back_input(); + } + } + if (negative) + negate(cur_val); +} + + +@ The following code is executed when |scan_something_internal| was +called asking for |mu_val|, when we really wanted a ``mudimen'' instead +of ``muglue.'' + +@c +static void coerce_glue(void) +{ + int v; + if (cur_val_level >= glue_val_level) { + v = width(cur_val); + delete_glue_ref(cur_val); + cur_val = v; + } +} + + + +@ The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to +a |scaled| value, i.e., an integral number of sp. One of its main tasks +is therefore to interpret the abbreviations for various kinds of units and +to convert measurements to scaled points. + +There are three parameters: |mu| is |true| if the finite units must be +`\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed; +|inf| is |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}' +are permitted; and |shortcut| is |true| if |cur_val| already contains +an integer and only the units need to be considered. + +The order of infinity that was found in the case of infinite glue is returned +in the global variable |cur_order|. + +@c +int cur_order; /* order of infinity found by |scan_dimen| */ + + +@ Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen| +may begin with |scan_int|. This explains why it is convenient to use +|scan_int| also for the integer part of a decimal fraction. + +Several branches of |scan_dimen| work with |cur_val| as an integer and +with an auxiliary fraction |f|, so that the actual quantity of interest is +$|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked'' +representation is put into the single word |cur_val|, which suddenly +switches significance from |integer| to |scaled|. + +@c +void scan_dimen(boolean mu, boolean inf, boolean shortcut) +/* sets |cur_val| to a dimension */ +{ + boolean negative; /* should the answer be negated? */ + int f; /* numerator of a fraction whose denominator is $2^{16}$ */ + /* Local variables for dimension calculations */ + int num, denom; /* conversion ratio for the scanned units */ + int k, kk; /* number of digits in a decimal fraction */ + halfword p, q; /* top of decimal digit stack */ + scaled v; /* an internal dimension */ + int save_cur_val; /* temporary storage of |cur_val| */ + + f = 0; + arith_error = false; + cur_order = normal; + negative = false; + if (!shortcut) { + /* Get the next non-blank non-sign... */ + negative = false; + do { + /* Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '-') { + negative = !negative; + cur_tok = other_token + '+'; + } + } while (cur_tok == other_token + '+'); + + if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + /* Fetch an internal dimension and |goto attach_sign|, + or fetch an internal integer */ + if (mu) { + scan_something_internal(mu_val_level, false); + coerce_glue(); + if (cur_val_level == mu_val_level) + goto ATTACH_SIGN; + if (cur_val_level != int_val_level) + mu_error(); + } else { + scan_something_internal(dimen_val_level, false); + if (cur_val_level == dimen_val_level) + goto ATTACH_SIGN; + } + + } else { + back_input(); + if (cur_tok == continental_point_token) { + cur_tok = point_token; + } + if (cur_tok != point_token) { + scan_int(); + } else { + radix = 10; + cur_val = 0; + } + if (cur_tok == continental_point_token) + cur_tok = point_token; + if ((radix == 10) && (cur_tok == point_token)) { + /* Scan decimal fraction */ + /* When the following code is executed, we have |cur_tok=point_token|, but this + token has been backed up using |back_input|; we must first discard it. + + It turns out that a decimal point all by itself is equivalent to `\.{0.0}'. + Let's hope people don't use that fact. */ + + k = 0; + p = null; + get_token(); /* |point_token| is being re-scanned */ + while (1) { + get_x_token(); + if ((cur_tok > zero_token + 9) || (cur_tok < zero_token)) + break; + if (k < 17) { /* digits for |k>=17| cannot affect the result */ + q = get_avail(); + set_token_link(q, p); + set_token_info(q, cur_tok - zero_token); + p = q; + incr(k); + } + } + for (kk = k; kk >= 1; kk--) { + dig[kk - 1] = token_info(p); + q = p; + p = token_link(p); + free_avail(q); + } + f = round_decimals(k); + if (cur_cmd != spacer_cmd) + back_input(); + } + } + } + if (cur_val < 0) { /* in this case |f=0| */ + negative = !negative; + negate(cur_val); + } + /* Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there + are |x| sp per unit; |goto attach_sign| if the units are internal */ + /* Now comes the harder part: At this point in the program, |cur_val| is a + nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1; + we want to multiply the sum of these two quantities by the appropriate + factor, based on the specified units, in order to produce a |scaled| + result, and we want to do the calculation with fixed point arithmetic that + does not overflow. + */ + + if (inf) { + /* Scan for (f)\.{fil} units; |goto attach_fraction| if found */ + /* In traditional TeX, a specification like `\.{filllll}' or `\.{fill L L + L}' will lead to two error messages (one for each additional keyword + \.{"l"}). + Not so for luatex, it just parses the construct in reverse. */ + if (scan_keyword("filll")) { + cur_order = filll; + goto ATTACH_FRACTION; + } else if (scan_keyword("fill")) { + cur_order = fill; + goto ATTACH_FRACTION; + } else if (scan_keyword("fil")) { + cur_order = fil; + goto ATTACH_FRACTION; + } else if (scan_keyword("fi")) { + cur_order = sfi; + goto ATTACH_FRACTION; + } + + } + /* Scan for (u)units that are internal dimensions; + |goto attach_sign| with |cur_val| set if found */ + save_cur_val = cur_val; + /* Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if ((cur_cmd < min_internal_cmd) || (cur_cmd > max_internal_cmd)) { + back_input(); + } else { + if (mu) { + scan_something_internal(mu_val_level, false); + coerce_glue(); + if (cur_val_level != mu_val_level) + mu_error(); + } else { + scan_something_internal(dimen_val_level, false); + } + v = cur_val; + goto FOUND; + } + if (mu) + goto NOT_FOUND; + if (scan_keyword("em")) { + v = (quad(get_cur_font())); + } else if (scan_keyword("ex")) { + v = (x_height(get_cur_font())); + } else if (scan_keyword("px")) { + v = dimen_par(pdf_px_dimen_code); + } else { + goto NOT_FOUND; + } + /* Scan an optional space */ + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + + FOUND: + cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000)); + goto ATTACH_SIGN; + NOT_FOUND: + + if (mu) { + /* Scan for (m)\.{mu} units and |goto attach_fraction| */ + if (scan_keyword("mu")) { + goto ATTACH_FRACTION; + } else { + print_err("Illegal unit of measure (mu inserted)"); + help4("The unit of measurement in math glue must be mu.", + "To recover gracefully from this error, it's best to", + "delete the erroneous units; e.g., type `2' to delete", + "two letters. (See Chapter 27 of The TeXbook.)"); + error(); + goto ATTACH_FRACTION; + } + } + if (scan_keyword("true")) { + /* Adjust (f)for the magnification ratio */ + prepare_mag(); + if (int_par(mag_code) != 1000) { + cur_val = xn_over_d(cur_val, 1000, int_par(mag_code)); + f = (1000 * f + 0200000 * tex_remainder) / int_par(mag_code); + cur_val = cur_val + (f / 0200000); + f = f % 0200000; + } + } + if (scan_keyword("pt")) + goto ATTACH_FRACTION; /* the easy case */ + /* Scan for (a)all other units and adjust |cur_val| and |f| accordingly; + |goto done| in the case of scaled points */ + + /* The necessary conversion factors can all be specified exactly as + fractions whose numerator and denominator add to 32768 or less. + According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$; + this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard + \^{Bosshard, Hans Rudolf} + in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). + The Didot point has been newly standardized in 1978; + it's now exactly $\rm 1\,nd=0.375\,mm$. + Conversion uses the equation $0.375=21681/20320/72.27\cdot25.4$. + The new Cicero follows the new Didot point; $\rm 1\,nc=12\,nd$. + These would lead to the ratios $21681/20320$ and $65043/5080$, + respectively. + The closest approximations supported by the algorithm would be + $11183/10481$ and $1370/107$. In order to maintain the + relation $\rm 1\,nc=12\,nd$, we pick the ratio $685/642$ for + $\rm nd$, however. + */ + +#define set_conversion(A,B) do { num=(A); denom=(B); } while(0) + + if (scan_keyword("in")) { + set_conversion(7227, 100); + } else if (scan_keyword("pc")) { + set_conversion(12, 1); + } else if (scan_keyword("cm")) { + set_conversion(7227, 254); + } else if (scan_keyword("mm")) { + set_conversion(7227, 2540); + } else if (scan_keyword("bp")) { + set_conversion(7227, 7200); + } else if (scan_keyword("dd")) { + set_conversion(1238, 1157); + } else if (scan_keyword("cc")) { + set_conversion(14856, 1157); + } else if (scan_keyword("nd")) { + set_conversion(685, 642); + } else if (scan_keyword("nc")) { + set_conversion(1370, 107); + } else if (scan_keyword("sp")) { + goto DONE; + } else { + /* Complain about unknown unit and |goto done2| */ + print_err("Illegal unit of measure (pt inserted)"); + help6("Dimensions can be in units of em, ex, in, pt, pc,", + "cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!", + "I'll assume that you meant to say pt, for printer's points.", + "To recover gracefully from this error, it's best to", + "delete the erroneous units; e.g., type `2' to delete", + "two letters. (See Chapter 27 of The TeXbook.)"); + error(); + goto DONE2; + } + cur_val = xn_over_d(cur_val, num, denom); + f = (num * f + 0200000 * tex_remainder) / denom; + cur_val = cur_val + (f / 0200000); + f = f % 0200000; + DONE2: + ATTACH_FRACTION: + if (cur_val >= 040000) + arith_error = true; + else + cur_val = cur_val * unity + f; + DONE: + /* Scan an optional space */ + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + ATTACH_SIGN: + if (arith_error || (abs(cur_val) >= 010000000000)) { + /* Report that this dimension is out of range */ + print_err("Dimension too large"); + help2("I can't work with sizes bigger than about 19 feet.", + "Continue and I'll use the largest value I can."); + error(); + cur_val = max_dimen; + arith_error = false; + } + if (negative) + negate(cur_val); +} + + +@ The final member of \TeX's value-scanning trio is |scan_glue|, which +makes |cur_val| point to a glue specification. The reference count of that +glue spec will take account of the fact that |cur_val| is pointing to~it. + +The |level| parameter should be either |glue_val| or |mu_val|. + +Since |scan_dimen| was so much more complex than |scan_int|, we might expect +|scan_glue| to be even worse. But fortunately, it is very simple, since +most of the work has already been done. + +@c +void scan_glue(int level) +{ /* sets |cur_val| to a glue spec pointer */ + boolean negative; /* should the answer be negated? */ + halfword q; /* new glue specification */ + boolean mu; /* does |level=mu_val|? */ + mu = (level == mu_val_level); + /* Get the next non-blank non-sign... */ + negative = false; + do { + /* Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '-') { + negative = !negative; + cur_tok = other_token + '+'; + } + } while (cur_tok == other_token + '+'); + + if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + scan_something_internal(level, negative); + if (cur_val_level >= glue_val_level) { + if (cur_val_level != level) + mu_error(); + return; + } + if (cur_val_level == int_val_level) + scan_dimen(mu, false, true); + else if (level == mu_val_level) + mu_error(); + } else { + back_input(); + scan_dimen(mu, false, false); + if (negative) + negate(cur_val); + } + /* Create a new glue specification whose width is |cur_val|; scan for its + stretch and shrink components */ + q = new_spec(zero_glue); + width(q) = cur_val; + if (scan_keyword("plus")) { + scan_dimen(mu, true, false); + stretch(q) = cur_val; + stretch_order(q) = (quarterword) cur_order; + } + if (scan_keyword("minus")) { + scan_dimen(mu, true, false); + shrink(q) = cur_val; + shrink_order(q) = (quarterword) cur_order; + } + cur_val = q; +} + +@ This is an omega routine +@c +void scan_scaled(void) +{ /* sets |cur_val| to a scaled value */ + boolean negative; /* should the answer be negated? */ + int f; /* numerator of a fraction whose denominator is $2^{16}$ */ + int k, kk; /* number of digits in a decimal fraction */ + halfword p, q; /* top of decimal digit stack */ + f = 0; + arith_error = false; + negative = false; + /* Get the next non-blank non-sign... */ + negative = false; + do { + /* Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '-') { + negative = !negative; + cur_tok = other_token + '+'; + } + } while (cur_tok == other_token + '+'); + + back_input(); + if (cur_tok == continental_point_token) + cur_tok = point_token; + if (cur_tok != point_token) { + scan_int(); + } else { + radix = 10; + cur_val = 0; + } + if (cur_tok == continental_point_token) + cur_tok = point_token; + if ((radix == 10) && (cur_tok == point_token)) { + /* Scan decimal fraction */ + /* TODO: merge this with the same block in |scan_dimen| */ + /* When the following code is executed, we have |cur_tok=point_token|, but this + token has been backed up using |back_input|; we must first discard it. + + It turns out that a decimal point all by itself is equivalent to `\.{0.0}'. + Let's hope people don't use that fact. */ + + k = 0; + p = null; + get_token(); /* |point_token| is being re-scanned */ + while (1) { + get_x_token(); + if ((cur_tok > zero_token + 9) || (cur_tok < zero_token)) + break; + if (k < 17) { /* digits for |k>=17| cannot affect the result */ + q = get_avail(); + set_token_link(q, p); + set_token_info(q, cur_tok - zero_token); + p = q; + incr(k); + } + } + for (kk = k; kk >= 1; kk--) { + dig[kk - 1] = token_info(p); + q = p; + p = token_link(p); + free_avail(q); + } + f = round_decimals(k); + if (cur_cmd != spacer_cmd) + back_input(); + + } + if (cur_val < 0) { /* in this case |f=0| */ + negative = !negative; + negate(cur_val); + } + if (cur_val > 040000) + arith_error = true; + else + cur_val = cur_val * unity + f; + if (arith_error || (abs(cur_val) >= 010000000000)) { + print_err("Stack number too large"); + error(); + } + if (negative) + negate(cur_val); +} + + +@ This procedure is supposed to scan something like `\.{\\skip\\count12}', +i.e., whatever can follow `\.{\\the}', and it constructs a token list +containing something like `\.{-3.0pt minus 0.5fill}'. + +@c +halfword the_toks(void) +{ + int old_setting; /* holds |selector| setting */ + halfword p, q, r; /* used for copying a token list */ + int c; /* value of |cur_chr| */ + str_number s; + halfword retval; + /* Handle \.{\\unexpanded} or \.{\\detokenize} and |return| */ + if (odd(cur_chr)) { + c = cur_chr; + scan_general_text(); + if (c == 1) { + return cur_val; + } else { + old_setting = selector; + selector = new_string; + p = get_avail(); + set_token_link(p, token_link(temp_token_head)); + token_show(p); + flush_list(p); + selector = old_setting; + s = make_string(); + retval = str_toks(str_lstring(s)); + flush_str(s); + return retval; + } + } + get_x_token(); + scan_something_internal(tok_val_level, false); + if (cur_val_level >= ident_val_level) { + /* Copy the token list */ + p = temp_token_head; + set_token_link(p, null); + if (cur_val_level == ident_val_level) { + store_new_token(cs_token_flag + cur_val); + } else if (cur_val != null) { + r = token_link(cur_val); /* do not copy the reference count */ + while (r != null) { + fast_store_new_token(token_info(r)); + r = token_link(r); + } + } + return p; + } else { + old_setting = selector; + selector = new_string; + switch (cur_val_level) { + case int_val_level: + print_int(cur_val); + break; + case attr_val_level: + print_int(cur_val); + break; + case dir_val_level: + print_dir(cur_val); + break; + case dimen_val_level: + print_scaled(cur_val); + tprint("pt"); + break; + case glue_val_level: + print_spec(cur_val, "pt"); + delete_glue_ref(cur_val); + break; + case mu_val_level: + print_spec(cur_val, "mu"); + delete_glue_ref(cur_val); + break; + } /* there are no other cases */ + selector = old_setting; + s = make_string(); + retval = str_toks(str_lstring(s)); + flush_str(s); + return retval; + } +} + +@ @c +str_number the_scanned_result(void) +{ + int old_setting; /* holds |selector| setting */ + str_number r; /* return value * */ + old_setting = selector; + selector = new_string; + if (cur_val_level >= ident_val_level) { + if (cur_val != null) { + show_token_list(token_link(cur_val), null, -1); + r = make_string(); + } else { + r = get_nullstr(); + } + } else { + switch (cur_val_level) { + case int_val_level: + print_int(cur_val); + break; + case attr_val_level: + print_int(cur_val); + break; + case dir_val_level: + print_dir(cur_val); + break; + case dimen_val_level: + print_scaled(cur_val); + tprint("pt"); + break; + case glue_val_level: + print_spec(cur_val, "pt"); + delete_glue_ref(cur_val); + break; + case mu_val_level: + print_spec(cur_val, "mu"); + delete_glue_ref(cur_val); + break; + } /* there are no other cases */ + r = make_string(); + } + selector = old_setting; + return r; +} + + + +@ The following routine is used to implement `\.{\\fontdimen} |n| |f|'. +The boolean parameter |writing| is set |true| if the calling program +intends to change the parameter value. + +@c +static void font_param_error(int f) +{ + print_err("Font "); + print_esc(font_id_text(f)); + tprint(" has only "); + print_int(font_params(f)); + tprint(" fontdimen parameters"); + help2("To increase the number of font parameters, you must", + "use \\fontdimen immediately after the \\font is loaded."); + error(); +} + +void set_font_dimen(void) +{ + internal_font_number f; + int n; /* the parameter number */ + scan_int(); + n = cur_val; + scan_font_ident(); + f = cur_val; + if (n <= 0) { + font_param_error(f); + } else { + if (n > font_params(f)) { + if (font_touched(f)) { + font_param_error(f); + } else { + /* Increase the number of parameters in the font */ + do { + set_font_param(f, (font_params(f) + 1), 0); + } while (n != font_params(f)); + } + } + } + scan_optional_equals(); + scan_normal_dimen(); + set_font_param(f, n, cur_val); +} + +void get_font_dimen(void) +{ + internal_font_number f; + int n; /* the parameter number */ + scan_int(); + n = cur_val; + scan_font_ident(); + f = cur_val; + cur_val = 0; /* initialize return value */ + if (n <= 0) { + font_param_error(f); + goto EXIT; + } else { + if (n > font_params(f)) { + if (font_touched(f)) { + font_param_error(f); + goto EXIT; + } else { + /* Increase the number of parameters in the font */ + do { + set_font_param(f, (font_params(f) + 1), 0); + } while (n != font_params(f)); + + } + } + } + cur_val = font_param(f, n); + EXIT: + scanned_result(cur_val, dimen_val_level); +} + + +@ Here's a similar procedure that returns a pointer to a rule node. This +routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule}; +therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store +the default rule dimensions in the node, then to override them if +`\.{height}' or `\.{width}' or `\.{depth}' specifications are +found (in any order). + +@c +halfword scan_rule_spec(void) +{ + halfword q; /* the rule node being created */ + q = new_rule(); /* |width|, |depth|, and |height| all equal |null_flag| now */ + if (cur_cmd == vrule_cmd) { + width(q) = default_rule; + rule_dir(q) = body_direction; + } else { + height(q) = default_rule; + depth(q) = 0; + rule_dir(q) = text_direction; + } + RESWITCH: + if (scan_keyword("width")) { + scan_normal_dimen(); + width(q) = cur_val; + goto RESWITCH; + } + if (scan_keyword("height")) { + scan_normal_dimen(); + height(q) = cur_val; + goto RESWITCH; + } + if (scan_keyword("depth")) { + scan_normal_dimen(); + depth(q) = cur_val; + goto RESWITCH; + } + return q; +} + + +@ Declare procedures that scan font-related stuff + +@c +void scan_font_ident(void) +{ + internal_font_number f; + halfword m; + /* Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if ((cur_cmd == def_font_cmd) || (cur_cmd == letterspace_font_cmd) + || (cur_cmd == pdf_copy_font_cmd)) { + f = get_cur_font(); + } else if (cur_cmd == set_font_cmd) { + f = cur_chr; + set_font_touched(f, 1); + } else if (cur_cmd == def_family_cmd) { + m = cur_chr; + scan_math_family_int(); + f = fam_fnt(cur_val, m); + set_font_touched(f, 1); + } else { + print_err("Missing font identifier"); + help2("I was looking for a control sequence whose", + "current meaning has been defined by \\font."); + back_error(); + f = null_font; + } + cur_val = f; +} + +@ The |scan_general_text| procedure is much like |scan_toks(false,false)|, +but will be invoked via |expand|, i.e., recursively. + +The token list (balanced text) created by |scan_general_text| begins +at |link(temp_token_head)| and ends at |cur_val|. (If |cur_val=temp_token_head|, +the list is empty.) + +@c +void scan_general_text(void) +{ + int s; /* to save |scanner_status| */ + halfword w; /* to save |warning_index| */ + halfword d; /* to save |def_ref| */ + halfword p; /* tail of the token list being built */ + halfword q; /* new node being added to the token list via |store_new_token| */ + halfword unbalance; /* number of unmatched left braces */ + s = scanner_status; + w = warning_index; + d = def_ref; + scanner_status = absorbing; + warning_index = cur_cs; + p = get_avail(); + def_ref = p; + set_token_ref_count(def_ref, 0); + p = def_ref; + scan_left_brace(); /* remove the compulsory left brace */ + unbalance = 1; + while (1) { + get_token(); + if (cur_tok < right_brace_limit) { + if (cur_cmd < right_brace_cmd) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + break; + } + } + store_new_token(cur_tok); + } + q = token_link(def_ref); + free_avail(def_ref); /* discard reference count */ + if (q == null) + cur_val = temp_token_head; + else + cur_val = p; + set_token_link(temp_token_head, q); + scanner_status = s; + warning_index = w; + def_ref = d; +} + + +@ The |get_x_or_protected| procedure is like |get_x_token| except that +protected macros are not expanded. + +@c +void get_x_or_protected(void) +{ /* sets |cur_cmd|, |cur_chr|, |cur_tok|, + and expands non-protected macros */ + while (1) { + get_token(); + if (cur_cmd <= max_command_cmd) + return; + if ((cur_cmd >= call_cmd) && (cur_cmd < end_template_cmd)) { + if (token_info(token_link(cur_chr)) == protected_token) + return; + } + expand(); + } +} + + +@ |scan_toks|. This function returns a pointer to the tail of a new token +list, and it also makes |def_ref| point to the reference count at the +head of that list. + +There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| +is true, the goal is to create the token list for a macro definition; +otherwise the goal is to create the token list for some other \TeX\ +primitive: \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase}, +\.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or +\.{\\special}. In the latter cases a left brace must be scanned next; this +left brace will not be part of the token list, nor will the matching right +brace that comes at the end. If |xpand| is false, the token list will +simply be copied from the input using |get_token|. Otherwise all expandable +tokens will be expanded until unexpandable tokens are left, except that +the results of expanding `\.{\\the}' are not expanded further. +If both |macro_def| and |xpand| are true, the expansion applies +only to the macro body (i.e., to the material following the first +|left_brace| character). + +The value of |cur_cs| when |scan_toks| begins should be the |eqtb| +address of the control sequence to display in ``runaway'' error +messages. + +@c +halfword scan_toks(boolean macro_def, boolean xpand) +{ + halfword t; /* token representing the highest parameter number */ + halfword s; /* saved token */ + halfword p; /* tail of the token list being built */ + halfword q; /* new node being added to the token list via |store_new_token| */ + halfword unbalance; /* number of unmatched left braces */ + halfword hash_brace; /* possible `\.{\#\{}' token */ + if (macro_def) + scanner_status = defining; + else + scanner_status = absorbing; + warning_index = cur_cs; + p = get_avail(); + def_ref = p; + set_token_ref_count(def_ref, 0); + p = def_ref; + hash_brace = 0; + t = zero_token; + if (macro_def) { + /* Scan and build the parameter part of the macro definition */ + while (1) { + get_token(); /* set |cur_cmd|, |cur_chr|, |cur_tok| */ + if (cur_tok < right_brace_limit) + break; + if (cur_cmd == mac_param_cmd) { + /* If the next character is a parameter number, make |cur_tok| + a |match| token; but if it is a left brace, store + `|left_brace|, |end_match|', set |hash_brace|, and |goto done|; + */ + s = match_token + cur_chr; + get_token(); + if (cur_cmd == left_brace_cmd) { + hash_brace = cur_tok; + store_new_token(cur_tok); + store_new_token(end_match_token); + goto DONE; + } + if (t == zero_token + 9) { + print_err("You already have nine parameters"); + help1("I'm going to ignore the # sign you just used."); + error(); + } else { + incr(t); + if (cur_tok != t) { + print_err("Parameters must be numbered consecutively"); + help2 + ("I've inserted the digit you should have used after the #.", + "Type `1' to delete what you did use."); + back_error(); + } + cur_tok = s; + } + } + store_new_token(cur_tok); + } + store_new_token(end_match_token); + if (cur_cmd == right_brace_cmd) { + /* Express shock at the missing left brace; |goto found| */ + print_err("Missing { inserted"); + incr(align_state); + help2 + ("Where was the left brace? You said something like `\\def\\a}',", + "which I'm going to interpret as `\\def\\a{}'."); + error(); + goto FOUND; + } + + } else { + scan_left_brace(); /* remove the compulsory left brace */ + } + DONE: + /* Scan and build the body of the token list; |goto found| when finished */ + unbalance = 1; + while (1) { + if (xpand) { + /* Expand the next part of the input */ + /* Here we insert an entire token list created by |the_toks| without + expanding it further. */ + while (1) { + get_token_lua(); + if (cur_cmd >= call_cmd) { + if (token_info(token_link(cur_chr)) == protected_token) { + cur_cmd = relax_cmd; + cur_chr = no_expand_flag; + } + } + if (cur_cmd <= max_command_cmd) + break; + if (cur_cmd != the_cmd) { + expand(); + } else { + q = the_toks(); + if (token_link(temp_token_head) != null) { + set_token_link(p, token_link(temp_token_head)); + p = q; + } + } + } + x_token(); + + } else { + get_token(); + } + if (cur_tok < right_brace_limit) { + if (cur_cmd < right_brace_cmd) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + goto FOUND; + } + } else if (cur_cmd == mac_param_cmd) { + if (macro_def) { + /* Look for parameter number or \.{\#\#} */ + s = cur_tok; + if (xpand) + get_x_token(); + else + get_token(); + if (cur_cmd != mac_param_cmd) { + if ((cur_tok <= zero_token) || (cur_tok > t)) { + print_err("Illegal parameter number in definition of "); + sprint_cs(warning_index); + help3("You meant to type ## instead of #, right?", + "Or maybe a } was forgotten somewhere earlier, and things", + "are all screwed up? I''m going to assume that you meant ##."); + back_error(); + cur_tok = s; + } else { + cur_tok = out_param_token - '0' + cur_chr; + } + } + } + } + store_new_token(cur_tok); + } + FOUND: + scanner_status = normal; + if (hash_brace != 0) + store_new_token(hash_brace); + return p; +} + + +@ Here we declare two trivial procedures in order to avoid mutually +recursive procedures with parameters. + +@c +void scan_normal_glue(void) +{ + scan_glue(glue_val_level); +} + +void scan_mu_glue(void) +{ + scan_glue(mu_val_level); +} + +@ The |scan_expr| procedure scans and evaluates an expression. + +@ Evaluating an expression is a recursive process: When the left +parenthesis of a subexpression is scanned we descend to the next level +of recursion; the previous level is resumed with the matching right +parenthesis. + +@c +typedef enum { + expr_none = 0, /* \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */ + expr_add = 1, /* \.( $\langle\it expr\rangle$ \.+ seen */ + expr_sub = 2, /* \.( $\langle\it expr\rangle$ \.- seen */ + expr_mult = 3, /* $\langle\it term\rangle$ \.* seen */ + expr_div = 4, /* $\langle\it term\rangle$ \./ seen */ + expr_scale = 5, /* $\langle\it term\rangle$ \.* $\langle\it factor\rangle$ \./ seen */ +} expression_states; + + +@ We want to make sure that each term and (intermediate) result is in + the proper range. Integer values must not exceed |infinity| + ($2^{31}-1$) in absolute value, dimensions must not exceed |max_dimen| + ($2^{30}-1$). We avoid the absolute value of an integer, because this + might fail for the value $-2^{31}$ using 32-bit arithmetic. + +@ clear a number or dimension and set |arith_error| + +@c +#define num_error(A) do { \ + arith_error=true; \ + A=0; \ + } while (0) + + +@ clear a glue spec and set |arith_error| + +@c +#define glue_error(A) do { \ + arith_error=true; \ + delete_glue_ref(A); \ + A=new_spec(zero_glue); \ + } while (0) + + +#define normalize_glue(A) do { \ + if (stretch(A)==0) stretch_order(A)=normal; \ + if (shrink(A)==0) shrink_order(A)=normal; \ + } while (0) + + +@ Parenthesized subexpressions can be inside expressions, and this +nesting has a stack. Seven local variables represent the top of the +expression stack: |p| points to pushed-down entries, if any; |l| +specifies the type of expression currently beeing evaluated; |e| is the +expression so far and |r| is the state of its evaluation; |t| is the +term so far and |s| is the state of its evaluation; finally |n| is the +numerator for a combined multiplication and division, if any. + +@c +#define expr_type(A) type((A)+1) +#define expr_state(A) subtype((A)+1) +#define expr_e_field(A) vlink((A)+1) /* saved expression so far */ +#define expr_t_field(A) vlink((A)+2) /* saved term so far */ +#define expr_n_field(A) vinfo((A)+2) /* saved numerator */ + + +#define expr_add_sub(A,B,C) add_or_sub((A),(B),(C),(r==expr_sub)) +#define expr_a(A,B) expr_add_sub((A),(B),max_dimen) + +@ + The function |add_or_sub(x,y,max_answer,negative)| computes the sum + (for |negative=false|) or difference (for |negative=true|) of |x| and + |y|, provided the absolute value of the result does not exceed + |max_answer|. + +@c +int add_or_sub(int x, int y, int max_answer, boolean negative) +{ + int a; /* the answer */ + if (negative) + negate(y); + if (x >= 0) { + if (y <= max_answer - x) + a = x + y; + else + num_error(a); + } else if (y >= -max_answer - x) { + a = x + y; + } else { + num_error(a); + } + return a; +} + + +#define expr_m(A) A = nx_plus_y((A),f,0) + +#define expr_d(A) A=quotient((A),f) + + +@ The function |quotient(n,d)| computes the rounded quotient +$q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive. + +@c +int quotient(int n, int d) +{ + boolean negative; /* should the answer be negated? */ + int a; /* the answer */ + if (d == 0) { + num_error(a); + } else { + if (d > 0) { + negative = false; + } else { + negate(d); + negative = true; + } + if (n < 0) { + negate(n); + negative = !negative; + } + a = n / d; + n = n - a * d; + d = n - d; /* avoid certain compiler optimizations! */ + if (d + n >= 0) + incr(a); + if (negative) + negate(a); + } + return a; +} + +#define expr_s(A) A=fract((A),n,f,max_dimen) + + +@ Finally, the function |fract(x,n,d,max_answer)| computes the integer +$q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive +and the result does not exceed |max_answer|. We can't use floating +point arithmetic since the routine must produce identical results in all +cases; and it would be too dangerous to multiply by~|n| and then divide +by~|d|, in separate operations, since overflow might well occur. Hence +this subroutine simulates double precision arithmetic, somewhat +analogous to Metafont's |make_fraction| and |take_fraction| routines. + +@c +int fract(int x, int n, int d, int max_answer) +{ + boolean negative; /* should the answer be negated? */ + int a; /* the answer */ + int f; /* a proper fraction */ + int h; /* smallest integer such that |2*h>=d| */ + int r; /* intermediate remainder */ + int t; /* temp variable */ + if (d == 0) + goto TOO_BIG; + a = 0; + if (d > 0) { + negative = false; + } else { + negate(d); + negative = true; + } + if (x < 0) { + negate(x); + negative = !negative; + } else if (x == 0) { + goto DONE; + } + if (n < 0) { + negate(n); + negative = !negative; + } + t = n / d; + if (t > max_answer / x) + goto TOO_BIG; + a = t * x; + n = n - t * d; + if (n == 0) + goto FOUND; + t = x / d; + if (t > (max_answer - a) / n) + goto TOO_BIG; + a = a + t * n; + x = x - t * d; + if (x == 0) + goto FOUND; + if (x < n) { + t = x; + x = n; + n = t; + } + + + /* now |0<n<=x<d| */ + /* Compute $f=\lfloor xn/d+{1\over2}\rfloor$; */ + /* The loop here preserves the following invariant relations + between |f|, |x|, |n|, and~|r|: + (i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor x_0n_0/d+{1\over2}\rfloor$; + (ii)~|-d<=r<0<n<=x<d|, where $x_0$, $n_0$ are the original values of~$x$ + and $n$. */ + /* Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|, + because the latter could overflow. */ + f = 0; + r = (d / 2) - d; + h = -r; + while (1) { + if (odd(n)) { + r = r + x; + if (r >= 0) { + r = r - d; + incr(f); + } + } + n = n / 2; + if (n == 0) + break; + if (x < h) { + x = x + x; + } else { + t = x - d; + x = t + x; + f = f + n; + if (x < n) { + if (x == 0) + break; + t = x; + x = n; + n = t; + } + } + } + + if (f > (max_answer - a)) + goto TOO_BIG; + a = a + f; + FOUND: + if (negative) + negate(a); + goto DONE; + TOO_BIG: + num_error(a); + DONE: + return a; +} + +@ @c +void scan_expr(void) +{ /* scans and evaluates an expression */ + boolean a, b; /* saved values of |arith_error| */ + int l; /* type of expression */ + int r; /* state of expression so far */ + int s; /* state of term so far */ + int o; /* next operation or type of next factor */ + int e; /* expression so far */ + int t; /* term so far */ + int f; /* current factor */ + int n; /* numerator of combined multiplication and division */ + halfword p; /* top of expression stack */ + halfword q; /* for stack manipulations */ + l = cur_val_level; + a = arith_error; + b = false; + p = null; + /* Scan and evaluate an expression |e| of type |l| */ + RESTART: + r = expr_none; + e = 0; + s = expr_none; + t = 0; + n = 0; + CONTINUE: + if (s == expr_none) + o = l; + else + o = int_val_level; + /* Scan a factor |f| of type |o| or start a subexpression */ + /* Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if (cur_tok == other_token + '(') { + /* Push the expression stack and |goto restart| */ + q = new_node(expr_node, 0); + vlink(q) = p; + expr_type(q) = (quarterword) l; + expr_state(q) = (quarterword) (4 * s + r); + expr_e_field(q) = e; + expr_t_field(q) = t; + expr_n_field(q) = n; + p = q; + l = o; + goto RESTART; + } + back_input(); + if ((o == int_val_level) || (o == attr_val_level)) + scan_int(); + else if (o == dimen_val_level) + scan_normal_dimen(); + else if (o == glue_val_level) + scan_normal_glue(); + else + scan_mu_glue(); + f = cur_val; + + FOUND: + /* Scan the next operator and set |o| */ + /* Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if (cur_tok == other_token + '+') { + o = expr_add; + } else if (cur_tok == other_token + '-') { + o = expr_sub; + } else if (cur_tok == other_token + '*') { + o = expr_mult; + } else if (cur_tok == other_token + '/') { + o = expr_div; + } else { + o = expr_none; + if (p == null) { + if (cur_cmd != relax_cmd) + back_input(); + } else if (cur_tok != other_token + ')') { + print_err("Missing ) inserted for expression"); + help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't."); + back_error(); + } + } + + arith_error = b; + /* Make sure that |f| is in the proper range */ + if (((l == int_val_level) || (l == attr_val_level)) || (s > expr_sub)) { + if ((f > infinity) || (f < -infinity)) + num_error(f); + } else if (l == dimen_val_level) { + if (abs(f) > max_dimen) + num_error(f); + } else { + if ((abs(width(f)) > max_dimen) || + (abs(stretch(f)) > max_dimen) || (abs(shrink(f)) > max_dimen)) + glue_error(f); + } + + switch (s) { + /* Cases for evaluation of the current term */ + case expr_none: + /* + Applying the factor |f| to the partial term |t| (with the operator + |s|) is delayed until the next operator |o| has been scanned. Here we + handle the first factor of a partial term. A glue spec has to be copied + unless the next operator is a right parenthesis; this allows us later on + to simply modify the glue components. + */ + if ((l >= glue_val_level) && (o != expr_none)) { + t = new_spec(f); + delete_glue_ref(f); + normalize_glue(t); + } else { + t = f; + } + break; + case expr_mult: + /* If a multiplication is followed by a division, the two operations are + combined into a `scaling' operation. Otherwise the term |t| is + multiplied by the factor |f|. */ + if (o == expr_div) { + n = f; + o = expr_scale; + } else if ((l == int_val_level) || (l == attr_val_level)) { + t = mult_integers(t, f); + } else if (l == dimen_val_level) { + expr_m(t); + } else { + expr_m(width(t)); + expr_m(stretch(t)); + expr_m(shrink(t)); + } + break; + case expr_div: + /* Here we divide the term |t| by the factor |f| */ + if (l < glue_val_level) { + expr_d(t); + } else { + expr_d(width(t)); + expr_d(stretch(t)); + expr_d(shrink(t)); + } + break; + case expr_scale: + /* Here the term |t| is multiplied by the quotient $n/f$. */ + if ((l == int_val_level) || (l == attr_val_level)) { + t = fract(t, n, f, infinity); + } else if (l == dimen_val_level) { + expr_s(t); + } else { + expr_s(width(t)); + expr_s(stretch(t)); + expr_s(shrink(t)); + } + break; + + } /* there are no other cases */ + if (o > expr_sub) { + s = o; + } else { + /* Evaluate the current expression */ + /* When a term |t| has been completed it is copied to, added to, or + subtracted from the expression |e|. */ + s = expr_none; + if (r == expr_none) { + e = t; + } else if ((l == int_val_level) || (l == attr_val_level)) { + e = expr_add_sub(e, t, infinity); + } else if (l == dimen_val_level) { + e = expr_a(e, t); + } else { + /* Compute the sum or difference of two glue specs */ + /* We know that |stretch_order(e)>normal| implies |stretch(e)<>0| and + |shrink_order(e)>normal| implies |shrink(e)<>0|. */ + width(e) = expr_a(width(e), width(t)); + if (stretch_order(e) == stretch_order(t)) { + stretch(e) = expr_a(stretch(e), stretch(t)); + } else if ((stretch_order(e) < stretch_order(t)) + && (stretch(t) != 0)) { + stretch(e) = stretch(t); + stretch_order(e) = stretch_order(t); + } + if (shrink_order(e) == shrink_order(t)) { + shrink(e) = expr_a(shrink(e), shrink(t)); + } else if ((shrink_order(e) < shrink_order(t)) && (shrink(t) != 0)) { + shrink(e) = shrink(t); + shrink_order(e) = shrink_order(t); + } + delete_glue_ref(t); + normalize_glue(e); + } + r = o; + } + b = arith_error; + if (o != expr_none) + goto CONTINUE; + if (p != null) { + /* Pop the expression stack and |goto found| */ + f = e; + q = p; + e = expr_e_field(q); + t = expr_t_field(q); + n = expr_n_field(q); + s = expr_state(q) / 4; + r = expr_state(q) % 4; + l = expr_type(q); + p = vlink(q); + flush_node(q); + goto FOUND; + } + + if (b) { + print_err("Arithmetic overflow"); + help2("I can't evaluate this expression,", + "since the result is out of range."); + error(); + if (l >= glue_val_level) { + delete_glue_ref(e); + e = zero_glue; + add_glue_ref(e); + } else { + e = 0; + } + } + arith_error = a; + cur_val = e; + cur_val_level = l; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/stringpool.h b/Build/source/texk/web2c/luatexdir/tex/stringpool.h new file mode 100644 index 00000000000..51eb4fd7195 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/stringpool.h @@ -0,0 +1,146 @@ +/* stringpool.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: stringpool.h 3395 2010-01-27 11:40:08Z taco $ */ + +#ifndef STRINGPOOL_H +# define STRINGPOOL_H + +/* Both lua and tex strings can contains null, but C strings cannot, so: */ + +typedef struct { + unsigned char *s; + size_t l; +} lstring; + +typedef struct { + const char *s; + size_t l; +} const_lstring; + +extern lstring *string_pool; + +extern str_number str_ptr; +extern str_number init_str_ptr; + +# define STRING_OFFSET 0x200000 +# define STRING_OFFSET_BITS 21 + +# define get_nullstr() STRING_OFFSET + +# define biggest_char 1114111 +# define number_chars 1114112 +# define special_char 1114113 /* |biggest_char+2| */ + +/* + Several of the elementary string operations are performed using + macros instead of procedures, because many of the + operations are done quite frequently and we want to avoid the + overhead of procedure calls. For example, here is + a simple macro that computes the length of a string. +*/ + +# define utf8_size(a) (a>0xFFFF ? 4 : (a>0x7FF ? 3 : (a>0x7F? 2 : 1))) + +# define str_length(a) string_pool[(a)-STRING_OFFSET].l +# define str_string(a) string_pool[(a)-STRING_OFFSET].s +# define str_lstring(a) string_pool[(a)-STRING_OFFSET] + +/* Strings are created by appending character codes to |str_pool|. + The |append_char| macro, defined here, does not check to see if the + value of |pool_ptr| has gotten too high; this test is supposed to be + made before |append_char| is used. There is also a |flush_char| + macro, which erases the last character appended. + + To test if there is room to append |l| more characters to |str_pool|, + we shall write |str_room(l)|, which aborts \TeX\ and gives an + apologetic error message if there isn't enough room. +*/ + +/* The length of the current string is called |cur_length|: */ + +extern unsigned char *cur_string; +extern unsigned cur_length; +extern unsigned cur_string_size; +extern unsigned pool_size; + +# define EXTRA_STRING 500 + +/* put |ASCII_code| \# at the end of |str_pool| */ +# define append_char(A) do { \ + if (cur_string==NULL) reset_cur_string(); \ + else str_room(1); \ + cur_string[cur_length++]=(unsigned char)(A); \ + } while (0) + +# define str_room(wsize) do { \ + unsigned nsize; \ + if ((cur_length+wsize) > cur_string_size) { \ + nsize = cur_string_size + cur_string_size / 5 + EXTRA_STRING; \ + if (nsize < (unsigned)(wsize)) { \ + nsize = wsize + EXTRA_STRING; \ + } \ + cur_string = (unsigned char *) xreallocarray(cur_string, unsigned char, nsize); \ + memset (cur_string+cur_length,0,nsize-cur_length); \ + cur_string_size = nsize; \ + } \ + } while (0) + +# define flush_char() --cur_length /* forget the last character in the pool */ + +extern str_number make_string(void); +extern boolean str_eq_buf(str_number s, int k); +extern boolean str_eq_str(str_number s, str_number t); +extern boolean str_eq_cstr(str_number, const char *, size_t); +extern boolean get_strings_started(void); +extern void reset_cur_string(void); + +# define save_cur_string() (cur_length>0 ? make_string() : 0) + +# define restore_cur_string(u) if (u!=0) { \ + unsigned l = (unsigned)str_length(u); \ + reset_cur_string(); \ + str_room(l); \ + memcpy(cur_string, str_string(u),l); \ + cur_length = l; \ + flush_str(u); \ + u=0; \ + } + + +extern str_number search_string(str_number search); +extern int pool_to_unichar(unsigned char *t); + +extern unsigned char *uni2str(unsigned); +extern unsigned str2uni(const unsigned char *); + +extern str_number maketexstring(const char *); +extern str_number maketexlstring(const char *, size_t); +extern void append_string(const unsigned char *s, unsigned l); + +extern char *makecstring(int); +extern char *makeclstring(int, size_t *); + +extern int dump_string_pool(void); +extern int undump_string_pool(void); + +extern void init_string_pool_array(unsigned s); +extern void flush_str(str_number s); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/stringpool.w b/Build/source/texk/web2c/luatexdir/tex/stringpool.w new file mode 100644 index 00000000000..dbea33554ac --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/stringpool.w @@ -0,0 +1,492 @@ +% stringpool.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: stringpool.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/stringpool.w $"; + +@ Control sequence names and diagnostic messages are variable-length strings +of eight-bit characters. Since PASCAL did not have a well-developed string +mechanism, \TeX\ did all of its string processing by homegrown methods. + +Elaborate facilities for dynamic strings are not needed, so all of the +necessary operations can be handled with a simple data structure. +The array |str_pool| contains all of the (eight-bit) bytes off all +of the strings, and the array |str_start| contains indices of the starting +points of each string. Strings are referred to by integer numbers, so that +string number |s| comprises the characters |str_pool[j]| for +|str_start_macro(s)<=j<str_start_macro(s+1)|. Additional integer variables +|pool_ptr| and |str_ptr| indicate the number of entries used so far +in |str_pool| and |str_start|, respectively; locations +|str_pool[pool_ptr]| and |str_start_macro(str_ptr)| are +ready for the next string to be allocated. + +String numbers 0 to |biggest_char| are reserved for strings that correspond to +single UNICODE characters. This is in accordance with the conventions of \.{WEB} +which converts single-character strings into the ASCII code number of the +single character involved. + +@c +lstring *string_pool; /* the array of strings */ +lstring *_string_pool; /* this variable lives |STRING_OFFSET| below |string_pool| + (handy for debugging: + |_string_pool[str_ptr] == str_string(str_ptr)| */ + +str_number str_ptr = (STRING_OFFSET + 1); /* number of the current string being created */ +str_number init_str_ptr; /* the starting value of |str_ptr| */ + +unsigned char *cur_string; /* current string buffer */ +unsigned cur_length; /* current index in that buffer */ +unsigned cur_string_size; /* malloced size of |cur_string| */ +unsigned pool_size; /* occupied byte count */ + + +@ Once a sequence of characters has been appended to |cur_string|, it +officially becomes a string when the function |make_string| is called. +This function returns the identification number of the new string as its +value. + +@c +void reset_cur_string(void) +{ + cur_length = 0; + cur_string_size = 255; + cur_string = (unsigned char *) xmalloc(256); + memset(cur_string, 0, 256); +} + +@ current string enters the pool +@c +str_number make_string(void) +{ + if (str_ptr == (max_strings + STRING_OFFSET)) + overflow("number of strings", + (unsigned) (max_strings - init_str_ptr + STRING_OFFSET)); + str_room(1); + cur_string[cur_length] = '\0'; /* now |lstring.s| is always a valid C string */ + str_string(str_ptr) = (unsigned char *) cur_string; + str_length(str_ptr) = cur_length; + pool_size += cur_length; + reset_cur_string(); +#if 0 + printf("Made a string: %s (s=%d)\n", (char *)str_string(str_ptr), (int)str_ptr); +#endif + str_ptr++; + return (str_ptr - 1); +} + +@ @c +static void utf_error(void) +{ + const char *hlp[] = + { "A funny symbol that I can't read has just been (re)read.", + "Just continue, I'll change it to 0xFFFD.", + NULL + }; + deletions_allowed = false; + tex_error("String contains an invalid utf-8 sequence", hlp); + deletions_allowed = true; +} + +@ @c +unsigned str2uni(const unsigned char *k) +{ + register int ch; + unsigned val = 0xFFFD; + const unsigned char *text = k; + if ((ch = *text++) < 0x80) { + val = (unsigned) ch; + } else if (ch <= 0xbf) { /* error */ + } else if (ch <= 0xdf) { + if (*text >= 0x80 && *text < 0xc0) + val = (unsigned) (((ch & 0x1f) << 6) | (*text++ & 0x3f)); + } else if (ch <= 0xef) { + if (*text >= 0x80 && *text < 0xc0 && text[1] >= 0x80 && text[1] < 0xc0) { + val = (unsigned) + (((ch & 0xf) << 12) | ((text[0] & 0x3f) << 6) | + (text[1] & 0x3f)); + } + } 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 = (unsigned) (w * 0x400 + w2 + 0x10000); + if (*text < 0x80 || text[1] < 0x80 || text[2] < 0x80 || + *text >= 0xc0 || text[1] >= 0xc0 || text[2] >= 0xc0) + val = 0xFFFD; + } + if (val == 0xFFFD) + utf_error(); + return (val); +} + +@ This is a very basic helper +@c +unsigned char *uni2str(unsigned unic) +{ + unsigned char *buf = xmalloc(5); + unsigned char *pt = buf; + if (unic < 0x80) + *pt++ = (unsigned char) unic; + else if (unic < 0x800) { + *pt++ = (unsigned char) (0xc0 | (unic >> 6)); + *pt++ = (unsigned char) (0x80 | (unic & 0x3f)); + } else if (unic >= 0x110000) { + *pt++ = (unsigned char) (unic - 0x110000); + } else if (unic < 0x10000) { + *pt++ = (unsigned char) (0xe0 | (unic >> 12)); + *pt++ = (unsigned char) (0x80 | ((unic >> 6) & 0x3f)); + *pt++ = (unsigned char) (0x80 | (unic & 0x3f)); + } else { + int u, z, y, x; + unsigned val = unic - 0x10000; + u = (int) (((val & 0xf0000) >> 16) + 1); + z = (int) ((val & 0x0f000) >> 12); + y = (int) ((val & 0x00fc0) >> 6); + x = (int) (val & 0x0003f); + *pt++ = (unsigned char) (0xf0 | (u >> 2)); + *pt++ = (unsigned char) (0x80 | ((u & 3) << 4) | z); + *pt++ = (unsigned char) (0x80 | y); + *pt++ = (unsigned char) (0x80 | x); + } + *pt = '\0'; + return buf; +} + + +@ |buffer_to_unichar| converts a sequence of bytes in the |buffer| +into a unicode character value. It does not check for overflow +of the |buffer|, but it is careful to check the validity of the +UTF-8 encoding. + +@c +#define test_sequence_byte(A) do { \ + if (((A)<0x80) || ((A)>=0xC0)) { \ + utf_error(); \ + return 0xFFFD; \ + } \ + } while (0) + + +static int buffer_to_unichar(int k) +{ + int a; /* a utf char */ + int b; /* a utf nibble */ + b = buffer[k]; + if (b < 0x80) { + a = b; + } else if (b >= 0xF8) { + /* the 5- and 6-byte UTF-8 sequences generate integers + that are outside of the valid UCS range, and therefore + unsupported + */ + test_sequence_byte(-1); + } else if (b >= 0xF0) { + a = (b - 0xF0) * 64; + b = buffer[k + 1]; + test_sequence_byte(b); + a = (a + (b - 128)) * 64; + b = buffer[k + 2]; + test_sequence_byte(b); + a = (a + (b - 128)) * 64; + b = buffer[k + 3]; + test_sequence_byte(b); + a = a + (b - 128); + } else if (b >= 0xE0) { + a = (b - 0xE0) * 64; + b = buffer[k + 1]; + test_sequence_byte(b); + a = (a + (b - 128)) * 64; + b = buffer[k + 2]; + test_sequence_byte(b); + a = a + (b - 128); + } else if (b >= 0xC0) { + a = (b - 0xC0) * 64; + b = buffer[k + 1]; + test_sequence_byte(b); + a = a + (b - 128); + } else { + /* This is an encoding error */ + test_sequence_byte(-1); + } + return a; +} + +@ @c +int pool_to_unichar(unsigned char *t) +{ + return (int) str2uni(t); +} + + + +@ The following subroutine compares string |s| with another string of the +same length that appears in |buffer| starting at position |k|; +the result is |true| if and only if the strings are equal. +Empirical tests indicate that |str_eq_buf| is used in such a way that +it tends to return |true| about 80 percent of the time. + +@c +boolean str_eq_buf(str_number s, int k) +{ /* test equality of strings */ + int a; /* a unicode character */ + if (s < STRING_OFFSET) { + a = buffer_to_unichar(k); + if (a != s) + return false; + } else { + unsigned char *j = str_string(s); + unsigned char *l = j + str_length(s); + while (j < l) { + if (*j++ != buffer[k++]) + return false; + } + } + return true; +} + + +@ Here is a similar routine, but it compares two strings in the string pool, +and it does not assume that they have the same length. + +@c +boolean str_eq_str(str_number s, str_number t) +{ /* test equality of strings */ + int a = 0; /* a utf char */ + unsigned char *j, *k, *l; /* running indices */ + if (s < STRING_OFFSET) { + if (t >= STRING_OFFSET) { + k = str_string(t); + if (s <= 0x7F && (str_length(t) == 1) && *k == s) + return true; + a = pool_to_unichar(k); + if (a != s) + return false; + } else { + if (t != s) + return false; + } + } else if (t < STRING_OFFSET) { + j = str_string(s); + if (t <= 0x7F && (str_length(s) == 1) && *j == t) + return true; + a = pool_to_unichar(j); + if (a != t) + return false; + } else { + if (str_length(s) != str_length(t)) + return false; + k = str_string(t); + j = str_string(s); + l = j + str_length(s); + while (j < l) { + if (*j++ != *k++) + return false; + } + } + return true; +} + +@ string compare +@c +boolean str_eq_cstr(str_number r, const char *s, size_t l) +{ + if (l != (size_t) str_length(r)) + return false; + return (strncmp((const char *) (str_string(r)), s, l) == 0); +} + + +@ The initial values of |str_pool|, |str_start|, |pool_ptr|, +and |str_ptr| are computed by the \.{INITEX} program, based in part +on the information that \.{WEB} has output while processing \TeX. + +The first |string_offset| strings are single-characters strings matching +Unicode. There is no point in generating all of these. But |str_ptr| has +initialized properly, otherwise |print_char| cannot see the difference +between characters and strings. + + +@ initializes the string pool, but returns |false| if something goes wrong +@c +boolean get_strings_started(void) +{ + reset_cur_string(); + return true; +} + +@ The string recycling routines. \TeX{} uses 2 + upto 4 {\it new\/} strings when scanning a filename in an \.{\\input}, + \.{\\openin}, or \.{\\openout} operation. These strings are normally + lost because the reference to them are not saved after finishing the + operation. |search_string| searches through the string pool for the + given string and returns either 0 or the found string number. + +@c +str_number search_string(str_number search) +{ + str_number s; /* running index */ + size_t len; /* length of searched string */ + len = str_length(search); + if (len == 0) { + return get_nullstr(); + } else { + s = search - 1; /* start search with newest string below |s|; |search>1|! */ + while (s >= STRING_OFFSET) { + /* first |string_offset| strings depend on implementation!! */ + if (str_length(s) == len) + if (str_eq_str(s, search)) + return s; + s--; + } + } + return 0; +} + +@ @c +str_number maketexstring(const char *s) +{ + if (s == NULL || *s == 0) + return get_nullstr(); + return maketexlstring(s, strlen(s)); +} + +@ @c +str_number maketexlstring(const char *s, size_t l) +{ + if (s == NULL || l == 0) + return get_nullstr(); + str_string(str_ptr) = xmalloc((unsigned) (l + 1)); + memcpy(str_string(str_ptr), s, (l + 1)); + str_length(str_ptr) = (unsigned) l; + str_ptr++; + return (str_ptr - 1); +} + +@ append a C string to a TeX string +@c +void append_string(const unsigned char *s, unsigned l) +{ + if (s == NULL || *s == 0) + return; + l = (unsigned) strlen((const char *) s); + str_room(l); + memcpy(cur_string + cur_length, s, l); + cur_length += l; + return; +} + +@ @c +char *makecstring(int s) +{ + size_t l; + return makeclstring(s, &l); +} + +@ @c +char *makeclstring(int s, size_t * len) +{ + if (s < STRING_OFFSET) { + *len = (size_t) utf8_size(s); + return (char *) uni2str((unsigned) s); + } else { + unsigned l = (unsigned) str_length(s); + char *cstrbuf = xmalloc(l + 1); + memcpy(cstrbuf, str_string(s), l); + cstrbuf[l] = '\0'; + *len = (size_t) l; + return cstrbuf; + } +} + +@ @c +int dump_string_pool(void) +{ + int j; + int l; + int k = str_ptr; + dump_int(k - STRING_OFFSET); + for (j = STRING_OFFSET + 1; j < k; j++) { + l = (int) str_length(j); + if (str_string(j) == NULL) + l = -1; + dump_int(l); + if (l > 0) + dump_things(*str_string(j), str_length(j)); + } + return (k - STRING_OFFSET); +} + +@ @c +int undump_string_pool(void) +{ + int j; + int x; + undump_int(str_ptr); + if (max_strings < str_ptr + strings_free) + max_strings = str_ptr + strings_free; + str_ptr += STRING_OFFSET; + if (ini_version) + libcfree(string_pool); + init_string_pool_array((unsigned) max_strings); + for (j = STRING_OFFSET + 1; j < str_ptr; j++) { + undump_int(x); + if (x >= 0) { + str_length(j) = (unsigned) x; + pool_size += (unsigned) x; + str_string(j) = xmallocarray(unsigned char, (unsigned) (x + 1)); + undump_things(*str_string(j), (unsigned) x); + *(str_string(j) + str_length(j)) = '\0'; + } else { + str_length(j) = 0; + } + } + init_str_ptr = str_ptr; + return str_ptr; +} + +@ @c +void init_string_pool_array(unsigned s) +{ + string_pool = xmallocarray(lstring, s); + _string_pool = string_pool - STRING_OFFSET; + memset(string_pool, 0, s * sizeof(lstring)); + /* seed the null string */ + string_pool[0].s = xmalloc(1); + string_pool[0].s[0] = '\0'; +} + +@ To destroy an already made string, we say |flush_str|. +@c +void flush_str(str_number s) +{ +#if 0 + printf("Flushing a string: %s (s=%d,str_ptr=%d)\n", (char *)str_string(s), (int)s, (int)str_ptr); +#endif + if (s > STRING_OFFSET) { /* don't ever delete the null string */ + pool_size -= (unsigned) str_length(s); + str_length(s) = 0; + xfree(str_string(s)); + } + while (str_string((str_ptr - 1)) == NULL) + str_ptr--; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.h b/Build/source/texk/web2c/luatexdir/tex/texdeffont.h new file mode 100644 index 00000000000..e2ce6f91cc0 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.h @@ -0,0 +1,31 @@ +/* texdeffont.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: texdeffont.h 3261 2009-12-18 11:38:21Z taco $ */ + +#ifndef TEXDEFFONT_H +# define TEXDEFFONT_H + +extern int font_bytes; + +extern void set_cur_font(internal_font_number f); + +extern void tex_def_font(small_number a); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.c b/Build/source/texk/web2c/luatexdir/tex/texdeffont.w index 016f12ab200..ecb4f9fe29f 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texdeffont.c +++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.w @@ -1,37 +1,46 @@ -/* texdeffont.c +% texdeffont.w - Copyright 2009 Taco Hoekwater <taco@luatex.org> +% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> - This file is part of LuaTeX. +% This file is part of LuaTeX. - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. -#include "luatex-api.h" -#include <ptexlib.h> - -#include "tokens.h" -#include "nodes.h" -#include "commands.h" +@ @c +#include "ptexlib.h" static const char _svn_version[] = - "$Id: texdeffont.c 2271 2009-04-12 23:42:21Z oneiros $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/texdeffont.c $"; + "$Id: texdeffont.w 3587 2010-04-03 14:32:25Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/texdeffont.w $"; + + + +@ When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number +to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the +|eqtb| location of a ``frozen'' control sequence that will always select +the font. -#define text(a) hash[(a)].rh /* string number for control sequence name */ -#define null_cs 1 /* equivalent of \.{\\csname\\endcsname} */ -#define scan_normal_dimen() scan_dimen(false,false,false) +@c +int font_bytes; -static char *scaled_to_string(scaled s) +void set_cur_font(internal_font_number f) +{ + int a = 0; /* never global */ + define(cur_font_loc, data_cmd, f); +} + +@ @c +char *scaled_to_string(scaled s) { /* prints scaled real, rounded to five digits */ static char result[16]; int n, k; @@ -47,11 +56,11 @@ static char *scaled_to_string(scaled s) n = s / unity; /* process the integer part */ do { - dig[l++] = n % 10; + dig[l++] = (char) (n % 10); n = n / 10;; } while (n > 0); while (l > 0) { - result[k++] = (dig[--l] + '0'); + result[k++] = (char) (dig[--l] + '0'); } } result[k++] = '.'; @@ -60,7 +69,7 @@ static char *scaled_to_string(scaled s) do { if (delta > unity) s = s + 0100000 - 050000; /* round the last digit */ - result[k++] = '0' + (s / unity); + result[k++] = (char) ('0' + (s / unity)); s = 10 * (s % unity); delta = delta * 10; } while (s > delta); @@ -69,21 +78,23 @@ static char *scaled_to_string(scaled s) return (char *) result; } +@ @c void tex_def_font(small_number a) { pointer u; /* user's font identifier */ internal_font_number f; /* runs through existing fonts */ str_number t; /* name for the frozen font identifier */ int old_setting; /* holds |selector| setting */ - integer offset = 0; + int offset = 0; scaled s = -1000; /* stated ``at'' size, or negative of scaled magnification */ - integer natural_dir = -1; /* the natural direction of the font */ + int natural_dir = -1; /* the natural direction of the font */ + char *fn; if (job_name == 0) open_log_file(); /* avoid confusing \.{texput} with the font name */ get_r_token(); u = cur_cs; if (u >= null_cs) - t = text(u); + t = cs_text(u); else t = maketexstring("FONT"); if (a >= 4) { @@ -92,7 +103,7 @@ void tex_def_font(small_number a) eq_define(u, set_font_cmd, null_font); } scan_optional_equals(); - /* @<Get the next non-blank non-call token@>; */ + /* Get the next non-blank non-call token; */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); @@ -127,15 +138,15 @@ void tex_def_font(small_number a) token_show(def_ref); selector = old_setting; flush_list(def_ref); - /* str_room(1); *//* what did that do ? */ + /* |str_room(1)|; *//* what did that do ? */ cur_name = make_string(); cur_ext = get_nullstr(); cur_area = get_nullstr(); } - /* @<Scan the font size specification@>; */ + /* Scan the font size specification; */ name_in_progress = true; /* this keeps |cur_name| from being changed */ if (scan_keyword("at")) { - /* @<Put the \(p)(positive) `at' size into |s|@> */ + /* Put the positive `at' size into |s| */ scan_normal_dimen(); s = cur_val; if ((s <= 0) || (s >= 01000000000)) { @@ -171,7 +182,8 @@ void tex_def_font(small_number a) offset = cur_val; if (cur_val < 0) { char err[256]; - const char *errhelp[] = { "The offset must be bigger than 0.", NULL }; + const char *errhelp[] = + { "The offset must be bigger than 0.", NULL }; snprintf(err, 255, "Illegal offset has been changed to 0 (%d)", (int) cur_val); tex_error(err, errhelp); @@ -183,8 +195,10 @@ void tex_def_font(small_number a) natural_dir = cur_val; } name_in_progress = false; - f = read_font_info(u, cur_name, s, natural_dir); + fn = makecstring(cur_name); + f = read_font_info(u, fn, s, natural_dir); + xfree(fn); equiv(u) = f; - zeqtb[get_font_id_base() + f] = zeqtb[u]; - text(get_font_id_base() + f) = t; + eqtb[font_id_base + f] = eqtb[u]; + cs_text(font_id_base + f) = t; } diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.h b/Build/source/texk/web2c/luatexdir/tex/texfileio.h new file mode 100644 index 00000000000..77b7a6dd5b5 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texfileio.h @@ -0,0 +1,144 @@ +/* texfileio.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: texfileio.h 3395 2010-01-27 11:40:08Z taco $ */ + +#ifndef TEXFILEIO_H +# define TEXFILEIO_H + +extern int *input_file_callback_id; +extern int read_file_callback_id[17]; + +extern char *luatex_find_file(const char *s, int callback_index); +extern char *luatex_find_read_file(const char *s, int n, int callback_index); +extern boolean luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, + const_string fopen_mode, boolean must_exist); +extern boolean luatex_open_output(FILE ** f_ptr, const char *fn, + const_string fopen_mode); + +extern boolean lua_a_open_in(alpha_file * f, char *fn, int n); +extern boolean lua_a_open_out(alpha_file * f, char *fn, int n); +extern boolean lua_b_open_out(alpha_file * f, char *fn); +extern void lua_a_close_in(alpha_file f, int n); +extern void lua_a_close_out(alpha_file f); + +extern packed_ASCII_code *buffer; +extern int first; +extern int last; +extern int max_buf_stack; + +extern boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln); + +/* +The user's terminal acts essentially like other files of text, except +that it is used both for input and for output. When the terminal is +considered an input file, the file variable is called |term_in|, and when it +is considered an output file the file variable is |term_out|. +@^system dependencies@> +*/ + +# define term_in stdin /* the terminal as an input file */ +# define term_out stdout /* the terminal as an output file */ + + +/* +Here is how to open the terminal files. |t_open_out| does nothing. +|t_open_in|, on the other hand, does the work of ``rescanning,'' or getting +any command line arguments the user has provided. It's defined in C. +*/ + +# define t_open_out() /* output already open for text output */ + +/* +Sometimes it is necessary to synchronize the input/output mixture that +happens on the user's terminal, and three system-dependent +procedures are used for this +purpose. The first of these, |update_terminal|, is called when we want +to make sure that everything we have output to the terminal so far has +actually left the computer's internal buffers and been sent. +The second, |clear_terminal|, is called when we wish to cancel any +input that the user may have typed ahead (since we are about to +issue an unexpected error message). The third, |wake_up_terminal|, +is supposed to revive the terminal if the user has disabled it by +some instruction to the operating system. The following macros show how +these operations can be specified with {\mc UNIX}. |update_terminal| +does an |fflush|. |clear_terminal| is redefined +to do nothing, since the user should control the terminal. +@^system dependencies@> +*/ + +# define update_terminal() fflush (term_out) +# define clear_terminal() do { ; } while (0) +# define wake_up_terminal() do { ; } while (0) /* cancel the user's cancellation of output */ + +extern boolean init_terminal(void); +extern void term_input(void); + +extern str_number cur_name; +extern str_number cur_area; +extern str_number cur_ext; +extern pool_pointer area_delimiter; +extern pool_pointer ext_delimiter; + +extern char *pack_file_name(str_number n, str_number a, str_number e); + +# define file_name_size 512 + +# define format_area_length 0 /* length of its area part */ +# define format_ext_length 4 /* length of its `\.{.fmt}' part */ +# define format_extension ".fmt" + /* the extension, as a constant */ + +extern char *TEX_format_default; + +extern char *open_fmt_file(void); + +extern boolean name_in_progress; /* is a file name being scanned? */ +extern str_number job_name; /* principal file name */ +extern boolean log_opened; /* has the transcript file been opened? */ + +extern unsigned char *texmf_log_name; /* full name of the log file */ + +extern void open_log_file(void); +extern void start_input(void); + +extern int open_outfile(FILE ** f, const char *name, const char *mode); + +extern boolean zopen_w_input(FILE **, const char *, int, + const_string fopen_mode); +extern boolean zopen_w_output(FILE **, const char *, const_string fopen_mode); +extern void zwclose(FILE *); + +extern int readbinfile(FILE * f, unsigned char **b, int *s); + +# define read_tfm_file readbinfile +# define read_vf_file readbinfile +# define read_ocp_file readbinfile +# define read_data_file readbinfile + +extern boolean openinnameok(const_string); +extern boolean openoutnameok(const_string); + +extern boolean open_in_or_pipe(FILE **, char *, int, const_string fopen_mode, + boolean must_exist); +extern boolean open_out_or_pipe(FILE **, char *, const_string fopen_mode); +extern void close_file_or_pipe(FILE *); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.w b/Build/source/texk/web2c/luatexdir/tex/texfileio.w new file mode 100644 index 00000000000..c577dbad17a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texfileio.w @@ -0,0 +1,1368 @@ +% texfileio.w +% +% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include <string.h> +#include "ptexlib.h" +#include <kpathsea/absolute.h> + +static const char _svn_version[] = + "$Id: texfileio.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/texfileio.w $"; + +@ @c +#define end_line_char int_par(end_line_char_code) + +@ The bane of portability is the fact that different operating systems treat +input and output quite differently, perhaps because computer scientists +have not given sufficient attention to this problem. People have felt somehow +that input and output are not part of ``real'' programming. Well, it is true +that some kinds of programming are more fun than others. With existing +input/output conventions being so diverse and so messy, the only sources of +joy in such parts of the code are the rare occasions when one can find a +way to make the program a little less bad than it might have been. We have +two choices, either to attack I/O now and get it over with, or to postpone +I/O until near the end. Neither prospect is very attractive, so let's +get it over with. + +The basic operations we need to do are (1)~inputting and outputting of +text, to or from a file or the user's terminal; (2)~inputting and +outputting of eight-bit bytes, to or from a file; (3)~instructing the +operating system to initiate (``open'') or to terminate (``close'') input or +output from a specified file; (4)~testing whether the end of an input +file has been reached. + +\TeX\ needs to deal with two kinds of files. +We shall use the term |alpha_file| for a file that contains textual data, +and the term |byte_file| for a file that contains eight-bit binary information. +These two types turn out to be the same on many computers, but +sometimes there is a significant distinction, so we shall be careful to +distinguish between them. Standard protocols for transferring +such files from computer to computer, via high-speed networks, are +now becoming available to more and more communities of users. + +The program actually makes use also of a third kind of file, called a +|word_file|, when dumping and reloading base information for its own +initialization. We shall define a word file later; but it will be possible +for us to specify simple operations on word files before they are defined. + +@ We finally did away with |nameoffile| and |namelength|, but the variables +have to be kept otherwise there will be link errors from |openclose.c| in +the web2c library + +@c +char *nameoffile; +int namelength; + + +@ When input files are opened via a callback, they will also be read using +callbacks. for that purpose, the |open_read_file_callback| returns an +integer to uniquely identify a callback table. This id replaces the file +point |f| in this case, because the input does not have to be a file +in the traditional sense. + +Signalling this fact is achieved by having two arrays of integers. + +@c +int *input_file_callback_id; +int read_file_callback_id[17]; + +@ Handle -output-directory. + +We assume that it is OK to look here first. Possibly it +would be better to replace lookups in "." with lookups in the +|output_directory| followed by "." but to do this requires much more +invasive surgery in libkpathsea. + +@c +static char *find_in_output_directory(const char *s) +{ + if (output_directory && !kpse_absolute_p(s, false)) { + FILE *f_ptr; + char *ftemp = concat3(output_directory, DIR_SEP_STRING, s); + f_ptr = fopen(ftemp, "rb"); /* this code is used for input files only */ + if (f_ptr) { + fclose(f_ptr); + return ftemp; + } else { + free(ftemp); + + } + } + return NULL; +} + +@ find an \.{\\input} or \.{\\read} file. |n| differentiates between those case. + +@c +char *luatex_find_read_file(const char *s, int n, int callback_index) +{ + char *ftemp = NULL; + int callback_id = callback_defined(callback_index); + if (callback_id > 0) { + (void) run_callback(callback_id, "dS->S", n, s, &ftemp); + } else { + /* use kpathsea here */ + ftemp = find_in_output_directory(s); + if (!ftemp) + ftemp = kpse_find_file(s, kpse_tex_format, 1); + } + if (ftemp) { + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = xstrdup(ftemp); + } + return ftemp; +} + +@ find other files types +@c +char *luatex_find_file(const char *s, int callback_index) +{ + char *ftemp = NULL; + int callback_id = callback_defined(callback_index); + if (callback_id > 0) { + (void) run_callback(callback_id, "S->S", s, &ftemp); + + } else { + /* use kpathsea here */ + switch (callback_index) { + case find_enc_file_callback: + ftemp = kpse_find_file(s, kpse_enc_format, 0); + break; + case find_sfd_file_callback: + ftemp = kpse_find_file(s, kpse_sfd_format, 0); + break; + case find_map_file_callback: + ftemp = kpse_find_file(s, kpse_fontmap_format, 0); + break; + case find_type1_file_callback: + ftemp = kpse_find_file(s, kpse_type1_format, 0); + break; + case find_truetype_file_callback: + ftemp = kpse_find_file(s, kpse_truetype_format, 0); + break; + case find_opentype_file_callback: + ftemp = kpse_find_file(s, kpse_opentype_format, 0); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_truetype_format, 0); + break; + case find_ocp_file_callback: + ftemp = kpse_find_file(s, kpse_ocp_format, 0); + break; + case find_data_file_callback: + ftemp = find_in_output_directory(s); + if (!ftemp) + ftemp = kpse_find_file(s, kpse_tex_format, 0); + break; + case find_font_file_callback: + ftemp = kpse_find_file(s, kpse_ofm_format, 0); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_tfm_format, 0); + break; + case find_vf_file_callback: + ftemp = kpse_find_file(s, kpse_ovf_format, 0); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_vf_format, 0); + break; + default: + printf + ("luatex_find_file(): do not know how to handle file %s of type %d\n", + s, callback_index); + break; + } + } + return ftemp; +} + + +@ Open an input file F, using the kpathsea format FILEFMT and passing + |FOPEN_MODE| to fopen. The filename is in `fn'. We return whether or + not the open succeeded. + +@c +boolean +luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, + const_string fopen_mode, boolean must_exist) +{ + string fname = NULL; + /* We havent found anything yet. */ + *f_ptr = NULL; + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = NULL; + fname = kpse_find_file(fn, (kpse_file_format_type) filefmt, must_exist); + if (fname) { + fullnameoffile = xstrdup(fname); + /* If we found the file in the current directory, don't leave + the `./' at the beginning of `fn', since it looks + dumb when `tex foo' says `(./foo.tex ... )'. On the other + hand, if the user said `tex ./foo', and that's what we + opened, then keep it -- the user specified it, so we + shouldn't remove it. */ + if (fname[0] == '.' && IS_DIR_SEP(fname[1]) + && (fn[0] != '.' || !IS_DIR_SEP(fn[1]))) { + unsigned i = 0; + while (fname[i + 2] != 0) { + fname[i] = fname[i + 2]; + i++; + } + fname[i] = 0; + } + /* This fopen is not allowed to fail. */ + *f_ptr = xfopen(fname, fopen_mode); + } + if (*f_ptr) { + recorder_record_input(fname); + } + return *f_ptr != NULL; +} + +@ @c +boolean luatex_open_output(FILE ** f_ptr, const char *fn, + const_string fopen_mode) +{ + char *fname; + boolean absolute = kpse_absolute_p(fn, false); + + /* If we have an explicit output directory, use it. */ + if (output_directory && !absolute) { + fname = concat3(output_directory, DIR_SEP_STRING, fn); + } else { + fname = xstrdup(fn); + } + + /* Is the filename openable as given? */ + *f_ptr = fopen(fname, fopen_mode); + + if (!*f_ptr) { + /* Can't open as given. Try the envvar. */ + string texmfoutput = kpse_var_value("TEXMFOUTPUT"); + + if (texmfoutput && *texmfoutput && !absolute) { + fname = concat3(texmfoutput, DIR_SEP_STRING, fn); + *f_ptr = fopen(fname, fopen_mode); + } + } + if (*f_ptr) { + recorder_record_output(fname); + } + free(fname); + return *f_ptr != NULL; +} + + +@ @c +boolean lua_a_open_in(alpha_file * f, char *fn, int n) +{ + int k; + char *fnam; /* string returned by find callback */ + int callback_id; + boolean ret = true; /* return value */ + boolean file_ok = true; /* the status so far */ + if (n == 0) { + input_file_callback_id[iindex] = 0; + } else { + read_file_callback_id[n] = 0; + } + fnam = luatex_find_read_file(fn, n, find_read_file_callback); + if (!fnam) + return false; + callback_id = callback_defined(open_read_file_callback); + if (callback_id > 0) { + k = run_and_save_callback(callback_id, "S->", fnam); + if (k > 0) { + ret = true; + if (n == 0) + input_file_callback_id[iindex] = k; + else + read_file_callback_id[n] = k; + } else { + file_ok = false; /* read failed */ + } + } else { /* no read callback */ + if (openinnameok(fnam)) { + ret = + open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, + (n == 0 ? true : false)); + } else { + file_ok = false; /* open failed */ + } + } + if (!file_ok) { + ret = false; + } + return ret; +} + +@ @c +boolean lua_a_open_out(alpha_file * f, char *fn, int n) +{ + boolean test; + str_number fnam; + int callback_id; + boolean ret = false; + callback_id = callback_defined(find_write_file_callback); + if (callback_id > 0) { + fnam = 0; + test = run_callback(callback_id, "dS->s", n, fn, &fnam); + if ((test) && (fnam != 0) && (str_length(fnam) > 0)) { + ret = open_outfile(f, fn, FOPEN_W_MODE); + } + } else { + if (openoutnameok(fn)) { + ret = open_out_or_pipe(f, fn, FOPEN_W_MODE); + } + } + return ret; +} + +@ @c +boolean lua_b_open_out(alpha_file * f, char *fn) +{ + boolean test; + str_number fnam; + int callback_id; + boolean ret = false; + callback_id = callback_defined(find_output_file_callback); + if (callback_id > 0) { + fnam = 0; + test = run_callback(callback_id, "S->s", fn, &fnam); + if ((test) && (fnam != 0) && (str_length(fnam) > 0)) { + ret = open_outfile(f, fn, FOPEN_WBIN_MODE); + } + } else { + if (openoutnameok(fn)) { + ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE); + } + } + return ret; +} + +@ @c +void lua_a_close_in(alpha_file f, int n) +{ /* close a text file */ + boolean ret; + int callback_id; + if (n == 0) + callback_id = input_file_callback_id[iindex]; + else + callback_id = read_file_callback_id[n]; + if (callback_id > 0) { + ret = run_saved_callback(callback_id, "close", "->"); + destroy_saved_callback(callback_id); + if (n == 0) + input_file_callback_id[iindex] = 0; + else + read_file_callback_id[n] = 0; + } else { + close_file_or_pipe(f); + } +} + +@ @c +void lua_a_close_out(alpha_file f) +{ /* close a text file */ + close_file_or_pipe(f); +} + + +@ Binary input and output are done with C's ordinary +procedures, so we don't have to make any other special arrangements for +binary~I/O. Text output is also easy to do with standard routines. +The treatment of text input is more difficult, however, because +of the necessary translation to |ASCII_code| values. +\TeX's conventions should be efficient, and they should +blend nicely with the user's operating environment. + +Input from text files is read one line at a time, using a routine called +|lua_input_ln|. This function is defined in terms of global variables called +|buffer|, |first|, and |last| that will be described in detail later; for +now, it suffices for us to know that |buffer| is an array of |ASCII_code| +values, and that |first| and |last| are indices into this array +representing the beginning and ending of a line of text. + +@c +packed_ASCII_code *buffer; /* lines of characters being read */ +int first; /* the first unused position in |buffer| */ +int last; /* end of the line just input to |buffer| */ +int max_buf_stack; /* largest index used in |buffer| */ + + +@ The |lua_input_ln| function brings the next line of input from the specified +file into available positions of the buffer array and returns the value +|true|, unless the file has already been entirely read, in which case it +returns |false| and sets |last:=first|. In general, the |ASCII_code| +numbers that represent the next line of the file are input into +|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the +global variable |last| is set equal to |first| plus the length of the +line. Trailing blanks are removed from the line; thus, either |last=first| +(in which case the line was entirely blank) or |buffer[last-1]<>" "|. + +An overflow error is given, however, if the normal actions of |lua_input_ln| +would make |last>=buf_size|; this is done so that other parts of \TeX\ +can safely look at the contents of |buffer[last+1]| without overstepping +the bounds of the |buffer| array. Upon entry to |lua_input_ln|, the condition +|first<buf_size| will always hold, so that there is always room for an +``empty'' line. + +The variable |max_buf_stack|, which is used to keep track of how large +the |buf_size| parameter must be to accommodate the present job, is +also kept up to date by |lua_input_ln|. + +If the |bypass_eoln| parameter is |true|, |lua_input_ln| will do a |get| +before looking at the first character of the line; this skips over +an |eoln| that was in |f^|. The procedure does not do a |get| when it +reaches the end of the line; therefore it can be used to acquire input +from the user's terminal as well as from ordinary text files. + +Since the inner loop of |lua_input_ln| is part of \TeX's ``inner loop''---each +character of input comes in at this place---it is wise to reduce system +overhead by making use of special routines that read in an entire array +of characters at once, if such routines are available. +@^inner loop@> + +@c +boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln) +{ + boolean lua_result; + int last_ptr; + int callback_id; + (void) bypass_eoln; /* todo: variable can be removed */ + if (n == 0) + callback_id = input_file_callback_id[iindex]; + else + callback_id = read_file_callback_id[n]; + if (callback_id > 0) { + last = first; + last_ptr = first; + lua_result = + run_saved_callback(callback_id, "reader", "->l", &last_ptr); + if ((lua_result == true) && (last_ptr != 0)) { + last = last_ptr; + if (last > max_buf_stack) + max_buf_stack = last; + } else { + lua_result = false; + } + } else { + lua_result = input_ln(f, bypass_eoln); + } + if (lua_result == true) { + /* Fix up the input buffer using callbacks */ + if (last >= first) { + callback_id = callback_defined(process_input_buffer_callback); + if (callback_id > 0) { + last_ptr = first; + lua_result = + run_callback(callback_id, "l->l", (last - first), + &last_ptr); + if ((lua_result == true) && (last_ptr != 0)) { + last = last_ptr; + if (last > max_buf_stack) + max_buf_stack = last; + } + } + } + return true; + } + return false; +} + + +@ We need a special routine to read the first line of \TeX\ input from +the user's terminal. This line is different because it is read before we +have opened the transcript file; there is sort of a ``chicken and +egg'' problem here. If the user types `\.{\\input paper}' on the first +line, or if some macro invoked by that line does such an \.{\\input}, +the transcript file will be named `\.{paper.log}'; but if no \.{\\input} +commands are performed during the first line of terminal input, the transcript +file will acquire its default name `\.{texput.log}'. (The transcript file +will not contain error messages generated by the first line before the +first \.{\\input} command.) +@.texput@> + +The first line is special also because it may be read before \TeX\ has +input a format file. In such cases, normal error messages cannot yet +be given. The following code uses concepts that will be explained later. + +@ Different systems have different ways to get started. But regardless of +what conventions are adopted, the routine that initializes the terminal +should satisfy the following specifications: + +\yskip\textindent{1)}It should open file |term_in| for input from the + terminal. (The file |term_out| will already be open for output to the + terminal.) + +\textindent{2)}If the user has given a command line, this line should be + considered the first line of terminal input. Otherwise the + user should be prompted with `\.{**}', and the first line of input + should be whatever is typed in response. + +\textindent{3)}The first line of input, which might or might not be a + command line, should appear in locations |first| to |last-1| of the + |buffer| array. + +\textindent{4)}The global variable |loc| should be set so that the + character to be read next by \TeX\ is in |buffer[loc]|. This + character should not be blank, and we should have |loc<last|. + +\yskip\noindent(It may be necessary to prompt the user several times +before a non-blank line comes in. The prompt is `\.{**}' instead of the +later `\.*' because the meaning is slightly different: `\.{\\input}' need +not be typed immediately after~`\.{**}'.) + +The following program does the required initialization. +Iff anything has been specified on the command line, then |t_open_in| +will return with |last > first|. +@^system dependencies@> + +@c +boolean init_terminal(void) +{ /* gets the terminal input started */ + t_open_in(); + if (last > first) { + iloc = first; + while ((iloc < last) && (buffer[iloc] == ' ')) + incr(iloc); + if (iloc < last) { + return true; + } + } + while (1) { + wake_up_terminal(); + fputs("**", term_out); + update_terminal(); + if (!input_ln(term_in, true)) { + /* this shouldn't happen */ + fputs("\n! End of file on the terminal... why?\n", term_out); + return false; + } + iloc = first; + while ((iloc < last) && (buffer[iloc] == ' ')) + incr(iloc); + if (iloc < last) { + return true; /* return unless the line was all blank */ + } + fputs("Please type the name of your input file.\n", term_out); + } +} + + +@ Here is a procedure that asks the user to type a line of input, +assuming that the |selector| setting is either |term_only| or |term_and_log|. +The input is placed into locations |first| through |last-1| of the +|buffer| array, and echoed on the transcript file if appropriate. + +@c +void term_input(void) +{ /* gets a line from the terminal */ + int k; /* index into |buffer| */ + update_terminal(); /* now the user sees the prompt for sure */ + if (!input_ln(term_in, true)) + fatal_error("End of file on the terminal!"); + term_offset = 0; /* the user's line ended with \.{<return>} */ + decr(selector); /* prepare to echo the input */ + if (last != first) { + for (k = first; k <= last - 1; k++) + print_char(buffer[k]); + } + print_ln(); + incr(selector); /* restore previous status */ +} + + +@ It's time now to fret about file names. Besides the fact that different +operating systems treat files in different ways, we must cope with the +fact that completely different naming conventions are used by different +groups of people. The following programs show what is required for one +particular operating system; similar routines for other systems are not +difficult to devise. +@^fingers@> +@^system dependencies@> + +\TeX\ assumes that a file name has three parts: the name proper; its +``extension''; and a ``file area'' where it is found in an external file +system. The extension of an input file or a write file is assumed to be +`\.{.tex}' unless otherwise specified; it is `\.{.log}' on the +transcript file that records each run of \TeX; it is `\.{.tfm}' on the font +metric files that describe characters in the fonts \TeX\ uses; it is +`\.{.dvi}' on the output files that specify typesetting information; and it +is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX. +The file area can be arbitrary on input files, but files are usually +output to the user's current area. If an input file cannot be +found on the specified area, \TeX\ will look for it on a special system +area; this special area is intended for commonly used input files like +\.{webmac.tex}. + +Simple uses of \TeX\ refer only to file names that have no explicit +extension or area. For example, a person usually says `\.{\\input} \.{paper}' +or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input} +\.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file +names are best, because they make the \TeX\ source files portable; +whenever a file name consists entirely of letters and digits, it should be +treated in the same way by all implementations of \TeX. However, users +need the ability to refer to other files in their environment, especially +when responding to error messages concerning unopenable files; therefore +we want to let them use the syntax that appears in their favorite +operating system. + +The following procedures don't allow spaces to be part of +file names; but some users seem to like names that are spaced-out. +System-dependent changes to allow such things should probably +be made with reluctance, and only when an entire file name that +includes spaces is ``quoted'' somehow. + +Here are the global values that file names will be scanned into. + +@c +str_number cur_name; /* name of file just scanned */ +str_number cur_area; /* file area just scanned, or \.{""} */ +str_number cur_ext; /* file extension just scanned, or \.{""} */ + + +@ The file names we shall deal with have the +following structure: If the name contains `\./' or `\.:' +(for Amiga only), the file area +consists of all characters up to and including the final such character; +otherwise the file area is null. If the remaining file name contains +`\..', the file extension consists of all such characters from the last +`\..' to the end, otherwise the file extension is null. + +We can scan such file names easily by using two global variables that keep track +of the occurrences of area and extension delimiters: + +@c +pool_pointer area_delimiter; /* the most recent `\./', if any */ +pool_pointer ext_delimiter; /* the relevant `\..', if any */ + + +@ Input files that can't be found in the user's area may appear in a standard +system area called |TEX_area|. Font metric files whose areas are not given +explicitly are assumed to appear in a standard system area called +|TEX_font_area|. $\Omega$'s compiled translation process files whose areas +are not given explicitly are assumed to appear in a standard system area. +These system area names will, of course, vary from place to place. + +@c +#define append_to_fn(A) do { \ + c=(A); \ + if (c!='"') { \ + if (k<file_name_size) fn[k++]=(unsigned char)(c); \ + } \ + } while (0) + + +char *pack_file_name(str_number n, str_number a, str_number e) +{ + ASCII_code c; /* character being packed */ + unsigned char *j; /* index into |str_pool| */ + int k = 0; /* number of positions filled in |fn| */ + unsigned char *fn = xmallocarray(packed_ASCII_code, + str_length(a) + str_length(n) + + str_length(e) + 1); + for (j = str_string(a); j < str_string(a) + str_length(a); j++) + append_to_fn(*j); + for (j = str_string(n); j < str_string(n) + str_length(n); j++) + append_to_fn(*j); + for (j = str_string(e); j < str_string(e) + str_length(e); j++) + append_to_fn(*j); + fn[k] = 0; + return (char *) fn; +} + + + +@ A messier routine is also needed, since format file names must be scanned +before \TeX's string mechanism has been initialized. We shall use the +global variable |TEX_format_default| to supply the text for default system areas +and extensions related to format files. +@^system dependencies@> + +Under {\mc UNIX} we don't give the area part, instead depending +on the path searching that will happen during file opening. Also, the +length will be set in the main program. + +@c +char *TEX_format_default; + + +@ This part of the program becomes active when a ``virgin'' \TeX\ is trying to get going, +just after the preliminary initialization, or when the user is substituting another +format file by typing `\.\&' after the initial `\.{**}' prompt. The buffer +contains the first line of input in |buffer[loc..(last-1)]|, where +|loc<last| and |buffer[loc]<>" "|. + +@c +char *open_fmt_file(void) +{ + int j; /* the first space after the format file name */ + char *fmt = NULL; + int dist; + j = iloc; + if (buffer[iloc] == '&') { + incr(iloc); + j = iloc; + buffer[last] = ' '; + while (buffer[j] != ' ') + incr(j); + fmt = xmalloc((unsigned) (j - iloc + 1)); + strncpy(fmt, (char *) (buffer + iloc), (size_t) (j - iloc)); + fmt[j - iloc] = 0; + dist = (int) (strlen(fmt) - strlen(DUMP_EXT)); + if (!(strstr(fmt, DUMP_EXT) == fmt + dist)) + fmt = concat(fmt, DUMP_EXT); + if (zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) + goto FOUND; + wake_up_terminal(); + fprintf(stdout, "Sorry, I can't find the format `%s'; will try `%s'.\n", + fmt, TEX_format_default); + update_terminal(); + } + /* now pull out all the stops: try for the system \.{plain} file */ + fmt = TEX_format_default; + if (!zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) { + wake_up_terminal(); + fprintf(stdout, "I can't find the format file `%s'!\n", + TEX_format_default); + return NULL; + } + FOUND: + iloc = j; + return fmt; +} + + +@ The global variable |name_in_progress| is used to prevent recursive +use of |scan_file_name|, since the |begin_name| and other procedures +communicate via global variables. Recursion would arise only by +devious tricks like `\.{\\input\\input f}'; such attempts at sabotage +must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input} +@^recursion@> +from being initiated when a font size specification is being scanned. + +Another global variable, |job_name|, contains the file name that was first +\.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}' +and `\.{.fmt}' in the names of \TeX's output files. + +@c +boolean name_in_progress; /* is a file name being scanned? */ +str_number job_name; /* principal file name */ +boolean log_opened; /* has the transcript file been opened? */ + + +@ Initially |job_name=0|; it becomes nonzero as soon as the true name is known. +We have |job_name=0| if and only if the `\.{log}' file has not been opened, +except of course for a short time just after |job_name| has become nonzero. + +@c +unsigned char *texmf_log_name; /* full name of the log file */ + + +@ The |open_log_file| routine is used to open the transcript file and to help +it catch up to what has previously been printed on the terminal. + +@c +void open_log_file(void) +{ + int old_setting; /* previous |selector| setting */ + int k; /* index into |buffer| */ + int l; /* end of first input line */ + char *fn; + old_setting = selector; + if (job_name == 0) + job_name = getjobname(maketexstring("texput")); /* TODO */ + fn = pack_job_name(".fls"); + recorder_change_filename(fn); + fn = pack_job_name(".log"); + while (!lua_a_open_out(&log_file, fn, 0)) { + /* Try to get a different log file name */ + /* Sometimes |open_log_file| is called at awkward moments when \TeX\ is + unable to print error messages or even to |show_context|. + The |prompt_file_name| routine can result in a |fatal_error|, but the |error| + routine will not be invoked because |log_opened| will be false. + + The normal idea of |batch_mode| is that nothing at all should be written + on the terminal. However, in the unusual case that + no log file could be opened, we make an exception and allow + an explanatory message to be seen. + + Incidentally, the program always refers to the log file as a `\.{transcript + file}', because some systems cannot use the extension `\.{.log}' for + this file. + */ + selector = term_only; + fn = prompt_file_name("transcript file name", ".log"); + } + texmf_log_name = (unsigned char *) xstrdup(fn); + selector = log_only; + log_opened = true; + if (callback_defined(start_run_callback) == 0) { + /* Print the banner line, including the date and time */ + log_banner(luatex_version_string, luatex_date_info, luatex_svn); + + input_stack[input_ptr] = cur_input; /* make sure bottom level is in memory */ + tprint_nl("**"); + l = input_stack[0].limit_field; /* last position of first line */ + if (buffer[l] == end_line_char) + decr(l); /* TODO: multichar endlinechar */ + for (k = 1; k <= l; k++) + print_char(buffer[k]); + print_ln(); /* now the transcript file contains the first line of input */ + } + flush_loggable_info(); /* should be done always */ + selector = old_setting + 2; /* |log_only| or |term_and_log| */ +} + + +@ Let's turn now to the procedure that is used to initiate file reading +when an `\.{\\input}' command is being processed. + +@c +void start_input(void) +{ /* \TeX\ will \.{\\input} something */ + str_number temp_str; + char *fn; + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + back_input(); + if (cur_cmd != left_brace_cmd) { + scan_file_name(); /* set |cur_name| to desired file name */ + } else { + scan_file_name_toks(); + } + fn = pack_file_name(cur_name, cur_area, cur_ext); + while (1) { + begin_file_reading(); /* set up |cur_file| and new level of input */ + if (lua_a_open_in(&cur_file, fn, 0)) + break; + end_file_reading(); /* remove the level that didn't work */ + fn = prompt_file_name("input file name", ""); + } + iname = maketexstring(fullnameoffile); + source_filename_stack[in_open] = iname; + full_source_filename_stack[in_open] = xstrdup(fullnameoffile); + /* we can try to conserve string pool space now */ + temp_str = search_string(iname); + if (temp_str > 0) { + flush_str(iname); + iname = temp_str; + } + if (job_name == 0) { + job_name = getjobname(cur_name); + open_log_file(); + } + /* |open_log_file| doesn't |show_context|, so |limit| + and |loc| needn't be set to meaningful values yet */ + if (tracefilenames) { + if (term_offset + (int) str_length(iname) > max_print_line - 2) + print_ln(); + else if ((term_offset > 0) || (file_offset > 0)) + print_char(' '); + print_char('('); + tprint_file_name(NULL, (unsigned char *) fullnameoffile, NULL); + } + incr(open_parens); + update_terminal(); + istate = new_line; + /* Prepare new file {\sl Sync\TeX} information */ + synctexstartinput(); /* Give control to the {\sl Sync\TeX} controller */ + + /* Read the first line of the new file */ + /* Here we have to remember to tell the |lua_input_ln| routine not to + start with a |get|. If the file is empty, it is considered to + contain a single blank line. */ + + line = 1; + if (lua_input_ln(cur_file, 0, false)) { + ; + } + firm_up_the_line(); + if (end_line_char_inactive()) + decr(ilimit); + else + buffer[ilimit] = (packed_ASCII_code) end_line_char; + first = ilimit + 1; + iloc = istart; +} + +@ Read and write dump files through zlib + +@ Earlier versions recast |*f| from |FILE *| to |gzFile|, but there is +no guarantee that these have the same size, so a static variable +is needed. + +@c +static gzFile gz_fmtfile = NULL; + +@ @c +void do_zdump(char *p, int item_size, int nitems, FILE * out_file) +{ + int err; + (void) out_file; + if (nitems == 0) + return; + if (gzwrite(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) != + item_size * nitems) { + fprintf(stderr, "! Could not write %d %d-byte item(s): %s.\n", nitems, + item_size, gzerror(gz_fmtfile, &err)); + uexit(1); + } +} + +@ @c +void do_zundump(char *p, int item_size, int nitems, FILE * in_file) +{ + int err; + (void) in_file; + if (nitems == 0) + return; + if (gzread(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) <= 0) { + fprintf(stderr, "Could not undump %d %d-byte item(s): %s.\n", + nitems, item_size, gzerror(gz_fmtfile, &err)); + uexit(1); + } +} + +@ @c +#define COMPRESSION "R3" + +boolean zopen_w_input(FILE ** f, const char *fname, int format, + const_string fopen_mode) +{ + int callbackid; + int res; + char *fnam; + callbackid = callback_defined(find_format_file_callback); + if (callbackid > 0) { + res = run_callback(callbackid, "S->S", fname, &fnam); + if (res && fnam && strlen(fnam) > 0) { + *f = xfopen(fnam, fopen_mode); + if (*f == NULL) { + return 0; + } + } else { + return 0; + } + } else { + res = luatex_open_input(f, fname, format, fopen_mode, true); + } + if (res) { + gz_fmtfile = gzdopen(fileno(*f), "rb" COMPRESSION); + } + return res; +} + +@ @c +boolean zopen_w_output(FILE ** f, const char *s, const_string fopen_mode) +{ + int res = 1; + if (luainit) { + *f = fopen(s, fopen_mode); + if (*f == NULL) { + return 0; + } + } else { + res = luatex_open_output(f, s, fopen_mode); + } + if (res) { + gz_fmtfile = gzdopen(fileno(*f), "wb" COMPRESSION); + } + return res; +} + +@ @c +void zwclose(FILE * f) +{ + (void) f; + gzclose(gz_fmtfile); +} + +@ create the dvi or pdf file +@c +int open_outfile(FILE ** f, const char *name, const char *mode) +{ + FILE *res; + res = fopen(name, mode); + if (res != NULL) { + *f = res; + return 1; + } + return 0; +} + + +@ the caller should set |tfm_buffer=NULL| and |tfm_size=0| +@c +int readbinfile(FILE * f, unsigned char **tfm_buffer, int *tfm_size) +{ + void *buf; + int size; + if (fseek(f, 0, SEEK_END) == 0) { + size = (int) ftell(f); + if (size > 0) { + buf = xmalloc((unsigned) size); + if (fseek(f, 0, SEEK_SET) == 0) { + if (fread((void *) buf, (size_t) size, 1, f) == 1) { + *tfm_buffer = (unsigned char *) buf; + *tfm_size = size; + return 1; + } + } + } else { + *tfm_buffer = NULL; + *tfm_size = 0; + return 1; + } + } /* seek failed, or zero-sized file */ + return 0; +} + + +@ Like |runsystem()|, the |runpopen()| function is called only when + |shellenabledp == 1|. Unlike |runsystem()|, here we write errors to + stderr, since we have nowhere better to use; and of course we return + a file handle (or NULL) instead of a status indicator. + +@c +static FILE *runpopen(char *cmd, const char *mode) +{ + FILE *f = NULL; + char *safecmd = NULL; + char *cmdname = NULL; + int allow; + + /* If restrictedshell == 0, any command is allowed. */ + if (restrictedshell == 0) { + allow = 1; + } else { + const char *thecmd = cmd; + allow = shell_cmd_is_allowed(&thecmd, &safecmd, &cmdname); + } + if (allow == 1) + f = popen(cmd, mode); + else if (allow == 2) + f = popen(safecmd, mode); + else if (allow == -1) + fprintf(stderr, "\nrunpopen quotation error in command line: %s\n", + cmd); + else + fprintf(stderr, "\nrunpopen command not allowed: %s\n", cmdname); + + if (safecmd) + free(safecmd); + if (cmdname) + free(cmdname); + return f; +} + +@ Return true if FNAME is acceptable as a name for \.{\\openout}, \.{\\openin}, or + \.{\\input}. + +@c +typedef enum ok_type { + ok_reading, + ok_writing +} ok_type; + +static const_string ok_type_name[] = { + "reading", + "writing" +}; + +static boolean +opennameok(const_string fname, const_string check_var, + const_string default_choice, ok_type action) +{ + /* We distinguish three cases: + 'a' (any) allows any file to be opened. + 'r' (restricted) means disallowing special file names. + 'p' (paranoid) means being really paranoid: disallowing special file + names and restricting output files to be in or below + the working directory or $TEXMFOUTPUT, while input files + must be below the current directory, $TEXMFOUTPUT, or + (implicitly) in the system areas. + We default to "paranoid". The error messages from TeX will be somewhat + puzzling... + This function contains several return statements... */ + + const_string open_choice = kpse_var_value(check_var); + + if (!open_choice) + open_choice = default_choice; + + if (*open_choice == 'a' || *open_choice == 'y' || *open_choice == '1') + return true; + +#if defined (unix) && !defined (MSDOS) + { + const_string base = xbasename(fname); + /* Disallow .rhosts, .login, etc. Allow .tex (for LaTeX). */ + if (base[0] == 0 || + (base[0] == '.' && !IS_DIR_SEP(base[1]) && !STREQ(base, ".tex"))) { + fprintf(stderr, "%s: Not %s to %s (%s = %s).\n", + program_invocation_name, ok_type_name[action], fname, + check_var, open_choice); + return false; + } + } +#else + /* Other OSs don't have special names? */ +#endif + + if (*open_choice == 'r' || *open_choice == 'n' || *open_choice == '0') + return true; + + /* Paranoia supplied by Charles Karney... */ + if (kpse_absolute_p(fname, false)) { + const_string texmfoutput = kpse_var_value("TEXMFOUTPUT"); + /* Absolute pathname is only OK if TEXMFOUTPUT is set, it's not empty, + fname begins the TEXMFOUTPUT, and is followed by / */ + if (!texmfoutput || *texmfoutput == '\0' + || fname != strstr(fname, texmfoutput) + || !IS_DIR_SEP(fname[strlen(texmfoutput)])) { + fprintf(stderr, "%s: Not %s to %s (%s = %s).\n", + program_invocation_name, ok_type_name[action], fname, + check_var, open_choice); + return false; + } + } + /* For all pathnames, we disallow "../" at the beginning or "/../" + anywhere. */ + if (fname[0] == '.' && fname[1] == '.' && IS_DIR_SEP(fname[2])) { + fprintf(stderr, "%s: Not %s to %s (%s = %s).\n", + program_invocation_name, ok_type_name[action], fname, + check_var, open_choice); + return false; + } else { + /* Check for "/../". Since more than one characted can be matched + by |IS_DIR_SEP|, we cannot use "/../" itself. */ + const_string dotpair = strstr(fname, ".."); + while (dotpair) { + /* If dotpair[2] == |DIR_SEP|, then dotpair[-1] is well-defined, + because the "../" case was handled above. */ + if (IS_DIR_SEP(dotpair[2]) && IS_DIR_SEP(dotpair[-1])) { + fprintf(stderr, "%s: Not %s to %s (%s = %s).\n", + program_invocation_name, ok_type_name[action], fname, + check_var, open_choice); + return false; + } + /* Continue after the dotpair. */ + dotpair = strstr(dotpair + 2, ".."); + } + } + + /* We passed all tests. */ + return true; +} + +boolean openinnameok(const_string fname) +{ + /* For input default to all. */ + return opennameok(fname, "openin_any", "a", ok_reading); +} + +#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) + +static int Isspace(char c) +{ + return (c == ' ' || c == '\t'); +} + +static boolean executable_filep(const_string fname) +{ + string p, q, base; + string *pp; + + /* check |openout_any| */ + p = kpse_var_value("openout_any"); + if (p && *p == 'p') { + free(p); +/* get base name + we cannot use xbasename() for abnormal names. +*/ + base = xstrdup(fname); + p = strrchr(fname, '/'); + if (p) { + p++; + strcpy(base, p); + } + p = strrchr(base, '\\'); + if (p) { + p++; + strcpy(base, p); + } +# if defined(__CYGWIN__) + for (p = base; *p; p++) + *p = tolower(*p); + p = base; +# else + p = (char *) strlwr(base); +# endif + for (q = p + strlen(p) - 1; + (q >= p) && ((*q == '.') || (Isspace(*q))); q--) { + *q = '\0'; /* remove trailing '.' , ' ' and '\t' */ + } + q = strrchr(p, '.'); /* get extension part */ + pp = suffixlist; + if (pp && q) { + while (*pp) { + if (strchr(fname, ':') || !strcmp(q, *pp)) { + fprintf(stderr, + "\nThe name %s is forbidden to open for writing.\n", + fname); + free(base); + return true; + } + pp++; + } + } + free(base); + } else if (p) { + free(p); + } + return false; +} +#endif + +boolean openoutnameok(const_string fname) +{ +#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) + /* Output of an executable file is restricted on Windows */ + if (executable_filep(fname)) + return false; +#endif + /* For output, default to paranoid. */ + return opennameok(fname, "openout_any", "p", ok_writing); +} + + +@ piped I/O + + +@ The code that implements |popen()| needs an array for tracking + possible pipe file pointers, because these need to be + closed using |pclose()|. + +@c +static FILE *pipes[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +boolean open_in_or_pipe(FILE ** f_ptr, char *fn, int filefmt, + const_string fopen_mode, boolean must_exist) +{ + string fname = NULL; + int i; /* iterator */ + + /* opening a read pipe is straightforward, only have to + skip past the pipe symbol in the file name. filename + quoting is assumed to happen elsewhere (it does :-)) */ + + if (shellenabledp && *fn == '|') { + /* the user requested a pipe */ + *f_ptr = NULL; + fname = (string) xmalloc((unsigned) (strlen(fn) + 1)); + strcpy(fname, fn); + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = xstrdup(fname); + recorder_record_input(fname + 1); + *f_ptr = runpopen(fname + 1, "r"); + free(fname); + for (i = 0; i <= 15; i++) { + if (pipes[i] == NULL) { + pipes[i] = *f_ptr; + break; + } + } + if (*f_ptr) + setvbuf(*f_ptr, (char *) NULL, _IOLBF, 0); + + return *f_ptr != NULL; + } + + return luatex_open_input(f_ptr, fn, filefmt, fopen_mode, must_exist); +} + + +boolean open_out_or_pipe(FILE ** f_ptr, char *fn, const_string fopen_mode) +{ + string fname; + int i; /* iterator */ + + /* opening a write pipe takes a little bit more work, because TeX + will perhaps have appended ".tex". To avoid user confusion as + much as possible, this extension is stripped only when the command + is a bare word. Some small string trickery is needed to make + sure the correct number of bytes is free()-d afterwards */ + + if (shellenabledp && *fn == '|') { + /* the user requested a pipe */ + fname = (string) xmalloc((unsigned) (strlen(fn) + 1)); + strcpy(fname, fn); + if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) { + /* mp and mf currently do not use this code, but it + is better to be prepared */ + if (STREQ((fname + strlen(fname) - 3), "tex")) + *(fname + strlen(fname) - 4) = 0; + *f_ptr = runpopen(fname + 1, "w"); + *(fname + strlen(fname)) = '.'; + } else { + *f_ptr = runpopen(fname + 1, "w"); + } + recorder_record_output(fname + 1); + free(fname); + + for (i = 0; i <= 15; i++) { + if (pipes[i] == NULL) { + pipes[i] = *f_ptr; + break; + } + } + + if (*f_ptr) + setvbuf(*f_ptr, (char *) NULL, _IOLBF, 0); + + return *f_ptr != NULL; + } + + return luatex_open_output(f_ptr, fn, fopen_mode); +} + + +void close_file_or_pipe(FILE * f) +{ + int i; /* iterator */ + + if (shellenabledp) { + for (i = 0; i <= 15; i++) { + /* if this file was a pipe, |pclose()| it and return */ + if (pipes[i] == f) { + if (f) + pclose(f); + pipes[i] = NULL; + return; + } + } + } + close_file(f); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.h b/Build/source/texk/web2c/luatexdir/tex/texmath.h new file mode 100644 index 00000000000..75a595e545f --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texmath.h @@ -0,0 +1,329 @@ +/* texmath.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: texmath.h 3461 2010-03-09 10:20:54Z taco $ */ + +#ifndef TEXMATH_H +# define TEXMATH_H 1 + +# define empty 0 + +extern pointer new_noad(void); + +extern void show_math_node(halfword); +extern void flush_math(void); +extern void math_left_brace(void); +extern void finish_display_alignment(halfword, halfword, halfword); +extern halfword new_sub_box(halfword); + +# define math_reset(p) do { if (p!=null) flush_node(p); p = null; } while (0) + +# define scripts_allowed(A) ((type((A))>=simple_noad)&&(type((A))<fence_noad)) + +# define default_code 010000000000 /* denotes |default_rule_thickness| */ + +typedef enum { + ord_noad_type = 0, + op_noad_type_normal, + op_noad_type_limits, + op_noad_type_no_limits, + bin_noad_type, + rel_noad_type, + open_noad_type, + close_noad_type, + punct_noad_type, + inner_noad_type, + under_noad_type, + over_noad_type, + vcenter_noad_type, +} noad_types; + +extern void initialize_math(void); +extern void initialize_math_spacing(void); +extern halfword math_vcenter_group(halfword); +extern void build_choices(void); +extern void close_math_group(halfword); +extern void init_math(void); +extern void start_eq_no(void); +extern void set_math_char(mathcodeval); +extern void math_char_in_text(mathcodeval); +extern void math_math_comp(void); +extern void math_limit_switch(void); +extern void math_radical(void); +extern void math_ac(void); +extern pointer new_style(small_number); +extern void append_choices(void); +extern void sub_sup(void); +extern void math_fraction(void); +extern void math_left_right(void); +extern void after_math(void); + +extern void scan_extdef_del_code(int level, int extcode); +extern void scan_extdef_math_code(int level, int extcode); + +extern int fam_fnt(int fam_id, int size_id); +extern void def_fam_fnt(int fam_id, int size_id, int f, int lvl); +extern void dump_math_data(void); +extern void undump_math_data(void); +void unsave_math_data(int lvl); + +/* + @ We also need to compute the change in style between mlists and their + subsidiaries. The following macros define the subsidiary style for + an overlined nucleus (|cramped_style|), for a subscript or a superscript + (|sub_style| or |sup_style|), or for a numerator or denominator (|num_style| + or |denom_style|). +*/ + +typedef enum { + display_style = 0, /* |subtype| for \.{\\displaystyle} */ + cramped_display_style, /* |subtype| for \.{\\crampeddisplaystyle} */ + text_style, /* |subtype| for \.{\\textstyle} */ + cramped_text_style, /* |subtype| for \.{\\crampedtextstyle} */ + script_style, /* |subtype| for \.{\\scriptstyle} */ + cramped_script_style, /* |subtype| for \.{\\crampedscriptstyle} */ + script_script_style, /* |subtype| for \.{\\scriptscriptstyle} */ + cramped_script_script_style, /* |subtype| for \.{\\crampedscriptscriptstyle} */ +} math_style_subtypes; + +extern const char *math_style_names[]; + +# define cramped 1 /* add this to an uncramped style if you want to cramp it */ + +# define cramped_style(A) 2*((A)/2)+cramped /* cramp the style */ +# define sub_style(A) 2*((A)/4)+script_style+cramped /* smaller and cramped */ +# define sup_style(A) 2*((A)/4)+script_style+((A)%2) /* smaller */ +# define num_style(A) (A)+2-2*((A)/6) /* smaller unless already script-script */ +# define denom_style(A) 2*((A)/2)+cramped+2-2*((A)/6) /* smaller, cramped */ +# define sup_sup_style(A) sup_style(sup_style((A))) /* smaller */ + +void setup_math_style(void); +void print_math_style(void); + +# define text_size 0 +# define script_size 1 +# define script_script_size 2 + +# define math_direction int_par(math_direction_code) + +# define dir_math_save cur_list.math_field +# define m_style cur_list.math_style_field +# define init_math_fields() do { \ + dir_math_save=false; \ + m_style=-1; \ + } while (0) + + +# define null_font 0 +# define min_quarterword 0 + +# define undefined_math_parameter max_dimen + +typedef enum { + math_param_quad, + math_param_axis, + math_param_operator_size, + math_param_overbar_kern, + math_param_overbar_rule, + math_param_overbar_vgap, + math_param_underbar_kern, + math_param_underbar_rule, + math_param_underbar_vgap, + math_param_radical_kern, + math_param_radical_rule, + math_param_radical_vgap, + math_param_radical_degree_before, + math_param_radical_degree_after, + math_param_radical_degree_raise, + math_param_stack_vgap, + math_param_stack_num_up, + math_param_stack_denom_down, + math_param_fraction_rule, + math_param_fraction_num_vgap, + math_param_fraction_num_up, + math_param_fraction_denom_vgap, + math_param_fraction_denom_down, + math_param_fraction_del_size, + math_param_limit_above_vgap, + math_param_limit_above_bgap, + math_param_limit_above_kern, + math_param_limit_below_vgap, + math_param_limit_below_bgap, + math_param_limit_below_kern, + math_param_under_delimiter_vgap, + math_param_under_delimiter_bgap, + math_param_over_delimiter_vgap, + math_param_over_delimiter_bgap, + math_param_sub_shift_drop, + math_param_sup_shift_drop, + math_param_sub_shift_down, + math_param_sub_sup_shift_down, + math_param_sub_top_max, + math_param_sup_shift_up, + math_param_sup_bottom_min, + math_param_sup_sub_bottom_max, + math_param_subsup_vgap, + math_param_space_after_script, + math_param_connector_overlap_min, + math_param_ord_ord_spacing, + math_param_ord_op_spacing, + math_param_ord_bin_spacing, + math_param_ord_rel_spacing, + math_param_ord_open_spacing, + math_param_ord_close_spacing, + math_param_ord_punct_spacing, + math_param_ord_inner_spacing, + math_param_op_ord_spacing, + math_param_op_op_spacing, + math_param_op_bin_spacing, + math_param_op_rel_spacing, + math_param_op_open_spacing, + math_param_op_close_spacing, + math_param_op_punct_spacing, + math_param_op_inner_spacing, + math_param_bin_ord_spacing, + math_param_bin_op_spacing, + math_param_bin_bin_spacing, + math_param_bin_rel_spacing, + math_param_bin_open_spacing, + math_param_bin_close_spacing, + math_param_bin_punct_spacing, + math_param_bin_inner_spacing, + math_param_rel_ord_spacing, + math_param_rel_op_spacing, + math_param_rel_bin_spacing, + math_param_rel_rel_spacing, + math_param_rel_open_spacing, + math_param_rel_close_spacing, + math_param_rel_punct_spacing, + math_param_rel_inner_spacing, + math_param_open_ord_spacing, + math_param_open_op_spacing, + math_param_open_bin_spacing, + math_param_open_rel_spacing, + math_param_open_open_spacing, + math_param_open_close_spacing, + math_param_open_punct_spacing, + math_param_open_inner_spacing, + math_param_close_ord_spacing, + math_param_close_op_spacing, + math_param_close_bin_spacing, + math_param_close_rel_spacing, + math_param_close_open_spacing, + math_param_close_close_spacing, + math_param_close_punct_spacing, + math_param_close_inner_spacing, + math_param_punct_ord_spacing, + math_param_punct_op_spacing, + math_param_punct_bin_spacing, + math_param_punct_rel_spacing, + math_param_punct_open_spacing, + math_param_punct_close_spacing, + math_param_punct_punct_spacing, + math_param_punct_inner_spacing, + math_param_inner_ord_spacing, + math_param_inner_op_spacing, + math_param_inner_bin_spacing, + math_param_inner_rel_spacing, + math_param_inner_open_spacing, + math_param_inner_close_spacing, + math_param_inner_punct_spacing, + math_param_inner_inner_spacing, + math_param_last +} math_parameters; + +extern const char *math_param_names[]; + +# define math_param_first_mu_glue math_param_ord_ord_spacing + +extern void def_math_param(int param_code, int style_code, scaled value, + int lvl); +extern scaled get_math_param(int param_code, int style_code); + + +typedef enum { + ScriptPercentScaleDown = 1, + ScriptScriptPercentScaleDown, + DelimitedSubFormulaMinHeight, + DisplayOperatorMinHeight, + MathLeading, + AxisHeight, + AccentBaseHeight, + FlattenedAccentBaseHeight, + SubscriptShiftDown, + SubscriptTopMax, + SubscriptBaselineDropMin, + SuperscriptShiftUp, + SuperscriptShiftUpCramped, + SuperscriptBottomMin, + SuperscriptBaselineDropMax, + SubSuperscriptGapMin, + SuperscriptBottomMaxWithSubscript, + SpaceAfterScript, + UpperLimitGapMin, + UpperLimitBaselineRiseMin, + LowerLimitGapMin, + LowerLimitBaselineDropMin, + StackTopShiftUp, + StackTopDisplayStyleShiftUp, + StackBottomShiftDown, + StackBottomDisplayStyleShiftDown, + StackGapMin, + StackDisplayStyleGapMin, + StretchStackTopShiftUp, + StretchStackBottomShiftDown, + StretchStackGapAboveMin, + StretchStackGapBelowMin, + FractionNumeratorShiftUp, + FractionNumeratorDisplayStyleShiftUp, + FractionDenominatorShiftDown, + FractionDenominatorDisplayStyleShiftDown, + FractionNumeratorGapMin, + FractionNumeratorDisplayStyleGapMin, + FractionRuleThickness, + FractionDenominatorGapMin, + FractionDenominatorDisplayStyleGapMin, + SkewedFractionHorizontalGap, + SkewedFractionVerticalGap, + OverbarVerticalGap, + OverbarRuleThickness, + OverbarExtraAscender, + UnderbarVerticalGap, + UnderbarRuleThickness, + UnderbarExtraDescender, + RadicalVerticalGap, + RadicalDisplayStyleVerticalGap, + RadicalRuleThickness, + RadicalExtraAscender, + RadicalKernBeforeDegree, + RadicalKernAfterDegree, + RadicalDegreeBottomRaisePercent, + MinConnectorOverlap, + SubscriptShiftDownWithSuperscript, + FractionDelimiterSize, + FractionDelimiterDisplayStyleSize, + MATH_param_last, +} MATH_param_codes; + +# define MATH_param_max MATH_param_last + +extern const char *MATH_param_names[]; + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/math.c b/Build/source/texk/web2c/luatexdir/tex/texmath.w index a3a2a154f7a..2e76aec61dc 100644 --- a/Build/source/texk/web2c/luatexdir/tex/math.c +++ b/Build/source/texk/web2c/luatexdir/tex/texmath.w @@ -1,66 +1,46 @@ -/* math.c +% texmath.w - Copyright 2008-2009 Taco Hoekwater <taco@luatex.org> +% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> - This file is part of LuaTeX. +% This file is part of LuaTeX. - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. -#include "luatex-api.h" -#include <ptexlib.h> - -#include "nodes.h" -#include "commands.h" -#include "managed-sa.h" - -#include "tokens.h" +@ @c +#include "ptexlib.h" static const char _svn_version[] = - "$Id: math.c 2594 2009-06-25 09:24:57Z taco $ " - "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/math.c $"; + "$Id: texmath.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/texmath.w $"; +@ @c #define mode cur_list.mode_field #define head cur_list.head_field #define tail cur_list.tail_field #define prev_graf cur_list.pg_field #define eTeX_aux cur_list.eTeX_aux_field #define delim_ptr eTeX_aux -#define aux cur_list.aux_field -#define prev_depth aux.cint -#define space_factor aux.hh.lhfield -#define incompleat_noad aux.cint - -#define stretching 1 -#define shrinking 2 - -#define append_char(A) str_pool[pool_ptr++]=(A) -#define flush_char pool_ptr-- /* forget the last character in the pool */ -#define cur_length (pool_ptr - str_start_macro(str_ptr)) -#define saved(A) save_stack[save_ptr+(A)].cint +#define space_factor cur_list.space_factor_field +#define incompleat_noad cur_list.incompleat_noad_field -#define vmode 1 -#define hmode (vmode+max_command_cmd+1) -#define mmode (hmode+max_command_cmd+1) - -#define cur_fam int_par(param_cur_fam_code) -#define text_direction zeqtb[static_int_base + param_text_direction_code].cint - -#define scan_normal_dimen() scan_dimen(false,false,false) +#define cur_fam int_par(cur_fam_code) +#define text_direction int_par(text_direction_code) #define var_code 7 -/* TODO: not sure if this is the right order */ +@ TODO: not sure if this is the right order +@c #define back_error(A,B) do { \ OK_to_interrupt=false; \ back_input(); \ @@ -68,22 +48,23 @@ static const char _svn_version[] = tex_error(A,B); \ } while (0) +@ @c int scan_math(pointer, int); pointer fin_mlist(pointer); -#define pre_display_size dimen_par(param_pre_display_size_code) -#define hsize dimen_par(param_hsize_code) -#define display_width dimen_par(param_display_width_code) -#define display_indent dimen_par(param_display_indent_code) -#define math_surround dimen_par(param_math_surround_code) -#define hang_indent dimen_par(param_hang_indent_code) -#define hang_after int_par(param_hang_after_code) -#define every_math loc_par(param_every_math_code) -#define every_display loc_par(param_every_display_code) -#define par_shape_ptr loc_par(param_par_shape_code) - -/* -When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an +#define pre_display_size dimen_par(pre_display_size_code) +#define hsize dimen_par(hsize_code) +#define display_width dimen_par(display_width_code) +#define display_indent dimen_par(display_indent_code) +#define math_surround dimen_par(math_surround_code) +#define hang_indent dimen_par(hang_indent_code) +#define hang_after int_par(hang_after_code) +#define every_math equiv(every_math_loc) +#define every_display equiv(every_display_loc) +#define par_shape_ptr equiv(par_shape_loc) + + +@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an {\sl mlist}, which is essentially a tree structure representing that formula. An mlist is a linear sequence of items, but we can regard it as a tree structure because mlists can appear within mlists. For example, many @@ -118,7 +99,7 @@ that of a node. An mlist does not contain character nodes, hlist nodes, vlist nodes, math nodes or unset nodes; in particular, each mlist item appears in the variable-size part of |mem|, so the |type| field is always present. -@ Each noad is five or more words long. The first word contains the +Each noad is five or more words long. The first word contains the |type| and |subtype| and |link| fields that are already so familiar to us; the second contains the attribute list pointer, and the third, fourth an fifth words are called the noad's |nucleus|, |subscr|, and @@ -161,23 +142,21 @@ and `\.{\$P\$}' produce different results (the former will not have the ``italic correction'' added to the width of |P|, but the ``script skip'' will be added). -*/ - - -static void unsave_math(void) +@c +void unsave_math(void) { unsave(); decr(save_ptr); flush_node_list(text_dir_ptr); - text_dir_ptr = saved(0); + assert(saved_type(0) == saved_textdir); + text_dir_ptr = saved_value(0); } -/* -Sometimes it is necessary to destroy an mlist. The following +@ Sometimes it is necessary to destroy an mlist. The following subroutine empties the current list, assuming that |abs(mode)=mmode|. -*/ +@c void flush_math(void) { flush_node_list(vlink(head)); @@ -187,25 +166,27 @@ void flush_math(void) incompleat_noad = null; } -/* Before we can do anything in math mode, we need fonts. */ +@ Before we can do anything in math mode, we need fonts. +@c #define MATHFONTSTACK 8 #define MATHFONTDEFAULT 0 /* == nullfont */ static sa_tree math_fam_head = NULL; -integer fam_fnt(integer fam_id, integer size_id) +@ @c +int fam_fnt(int fam_id, int size_id) { - integer n = fam_id + (256 * size_id); - return (integer) get_sa_item(math_fam_head, n); + int n = fam_id + (256 * size_id); + return (int) get_sa_item(math_fam_head, n); } -void def_fam_fnt(integer fam_id, integer size_id, integer f, integer lvl) +void def_fam_fnt(int fam_id, int size_id, int f, int lvl) { - integer n = fam_id + (256 * size_id); - set_sa_item(math_fam_head, n, f, lvl); + int n = fam_id + (256 * size_id); + set_sa_item(math_fam_head, n, (sa_tree_item) f, lvl); fixup_math_parameters(fam_id, size_id, f, lvl); - if (int_par(param_tracing_assigns_code) > 0) { + if (int_par(tracing_assigns_code) > 0) { begin_diagnostic(); tprint("{assigning"); print_char(' '); @@ -218,19 +199,20 @@ void def_fam_fnt(integer fam_id, integer size_id, integer f, integer lvl) } } -static void unsave_math_fam_data(integer gl) +@ @c +void unsave_math_fam_data(int gl) { sa_stack_item st; if (math_fam_head->stack == NULL) return; while (math_fam_head->stack_ptr > 0 && abs(math_fam_head->stack[math_fam_head->stack_ptr].level) - >= (integer) gl) { + >= (int) gl) { st = math_fam_head->stack[math_fam_head->stack_ptr]; if (st.level > 0) { rawset_sa_item(math_fam_head, st.code, st.value); /* now do a trace message, if requested */ - if (int_par(param_tracing_restores_code) > 0) { + if (int_par(tracing_restores_code) > 0) { int size_id = st.code / 256; int fam_id = st.code % 256; begin_diagnostic(); @@ -250,18 +232,20 @@ static void unsave_math_fam_data(integer gl) -/* and parameters */ +@ and parameters +@c #define MATHPARAMSTACK 8 #define MATHPARAMDEFAULT undefined_math_parameter static sa_tree math_param_head = NULL; +@ @c void def_math_param(int param_id, int style_id, scaled value, int lvl) { - integer n = param_id + (256 * style_id); - set_sa_item(math_param_head, n, value, lvl); - if (int_par(param_tracing_assigns_code) > 0) { + int n = param_id + (256 * style_id); + set_sa_item(math_param_head, n, (sa_tree_item) value, lvl); + if (int_par(tracing_assigns_code) > 0) { begin_diagnostic(); tprint("{assigning"); print_char(' '); @@ -276,24 +260,25 @@ void def_math_param(int param_id, int style_id, scaled value, int lvl) scaled get_math_param(int param_id, int style_id) { - integer n = param_id + (256 * style_id); + int n = param_id + (256 * style_id); return (scaled) get_sa_item(math_param_head, n); } -static void unsave_math_param_data(integer gl) +@ @c +void unsave_math_param_data(int gl) { sa_stack_item st; if (math_param_head->stack == NULL) return; while (math_param_head->stack_ptr > 0 && abs(math_param_head->stack[math_param_head->stack_ptr].level) - >= (integer) gl) { + >= (int) gl) { st = math_param_head->stack[math_param_head->stack_ptr]; if (st.level > 0) { rawset_sa_item(math_param_head, st.code, st.value); /* now do a trace message, if requested */ - if (int_par(param_tracing_restores_code) > 0) { + if (int_par(tracing_restores_code) > 0) { int param_id = st.code % 256; int style_id = st.code / 256; begin_diagnostic(); @@ -312,14 +297,17 @@ static void unsave_math_param_data(integer gl) } -/* saving and unsaving of both */ +@ saving and unsaving of both -void unsave_math_data(integer gl) +@c +void unsave_math_data(int gl) { unsave_math_fam_data(gl); unsave_math_param_data(gl); } +@ Dumping and undumping +@c void dump_math_data(void) { if (math_fam_head == NULL) @@ -336,8 +324,7 @@ void undump_math_data(void) math_param_head = undump_sa_tree(); } -/* */ - +@ @c void initialize_math(void) { if (math_fam_head == NULL) @@ -350,14 +337,14 @@ void initialize_math(void) } -/* + @ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, Clo, Pun, or Inn, for purposes of spacing and line breaking. An |ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, |punct_noad|, or |inner_noad| is used to represent portions of the various types. For example, an `\.=' sign in a formula leads to the creation of a |rel_noad| whose |nucleus| field is a representation of an equals sign -(usually |fam=0|, |character=@'75|). A formula preceded by \.{\\mathrel} +(usually |fam=0|, |character=075|). A formula preceded by \.{\\mathrel} also results in a |rel_noad|. When a |rel_noad| is followed by an |op_noad|, say, and possibly separated by one or more ordinary nodes (not noads), \TeX\ will insert a penalty node (with the current |rel_penalty|) @@ -394,11 +381,12 @@ be placed at the left and right of the fraction. In this way, a |fraction_noad| is able to represent all of \TeX's operators \.{\\over}, \.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and \.{\\abovewithdelims}. -*/ -/* The |new_noad| function creates an |ord_noad| that is completely null */ +@ The |new_noad| function creates an |ord_noad| that is completely null + +@c pointer new_noad(void) { pointer p; @@ -407,6 +395,7 @@ pointer new_noad(void) return p; } +@ @c pointer new_sub_box(pointer cur_box) { pointer p, q; @@ -417,7 +406,7 @@ pointer new_sub_box(pointer cur_box) return p; } -/* + @ A few more kinds of noads will complete the set: An |under_noad| has its nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places an accent over its nucleus; the accent character appears as @@ -426,7 +415,7 @@ centers its nucleus vertically with respect to the axis of the formula; in such noads we always have |type(nucleus(p))=sub_box|. And finally, we have the |fence_noad| type, to implement -\TeX's \.{\\left} and \.{\\right} as well as \eTeX's \.{\\middle}. +\TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}. The |nucleus| of such noads is replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces a |fence_noad| such that |delimiter(p)| holds the family and character @@ -435,9 +424,9 @@ never appears in an mlist except as the first element, and a |fence_noad| with subtype |right_noad_side| never appears in an mlist except as the last element; furthermore, we either have both a |left_noad_side| and a |right_noad_side|, or neither one is present. -*/ -/* + + @ Math formulas can also contain instructions like \.{\\textstyle} that override \TeX's normal style rules. A |style_node| is inserted into the data structure to record such instructions; it is three words long, so it @@ -452,8 +441,8 @@ present because a |choice_node| is converted to a |style_node|. is backwards from the convention of Appendix~G in {\sl The \TeX book\/}; i.e., a smaller style has a larger numerical value. @:TeXbook}{\sl The \TeX book@> -*/ +@c const char *math_style_names[] = { "display", "crampeddisplay", "text", "crampedtext", @@ -489,37 +478,37 @@ const char *math_param_names[] = { "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing", "openordspacing", "openopspacing", "openbinspacing", "openrelspacing", "openopenspacing", "openclosespacing", "openpunctspacing", - "openinnerspacing", + "openinnerspacing", "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing", "closeopenspacing", "closeclosespacing", "closepunctspacing", - "closeinnerspacing", + "closeinnerspacing", "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing", "punctopenspacing", "punctclosespacing", "punctpunctspacing", - "punctinnerspacing", + "punctinnerspacing", "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing", "inneropenspacing", "innerclosespacing", "innerpunctspacing", - "innerinnerspacing", + "innerinnerspacing", NULL }; +@ @c pointer new_style(small_number s) { /* create a style node */ m_style = s; return new_node(style_node, s); } -/* Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which +@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which has special subfields |display_mlist|, |text_mlist|, |script_mlist|, and |script_script_mlist| pointing to the mlists for each style. -*/ - -static pointer new_choice(void) +@c +pointer new_choice(void) { /* create a choice node */ return new_node(choice_node, 0); /* the |subtype| is not used */ } -/* + @ Let's consider now the previously unwritten part of |show_node_list| that displays the things that can only be present in mlists; this program illustrates how to access the data structures just defined. @@ -534,9 +523,8 @@ or |supscr| or |denominator| or |numerator| fields of noads. For example, the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for |x| in the (ridiculous) formula `\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'. -*/ - +@c void display_normal_noad(pointer p); /* forward */ void display_fence_noad(pointer p); /* forward */ void display_fraction_noad(pointer p); /* forward */ @@ -551,16 +539,16 @@ void show_math_node(pointer p) tprint_esc("mathchoice"); append_char('D'); show_node_list(display_mlist(p)); - flush_char; + flush_char(); append_char('T'); show_node_list(text_mlist(p)); - flush_char; + flush_char(); append_char('S'); show_node_list(script_mlist(p)); - flush_char; + flush_char(); append_char('s'); show_node_list(script_script_mlist(p)); - flush_char; + flush_char(); break; case simple_noad: case radical_noad: @@ -580,9 +568,10 @@ void show_math_node(pointer p) } -/* Here are some simple routines used in the display of noads. */ +@ Here are some simple routines used in the display of noads. -static void print_fam_and_char(pointer p) +@c +void print_fam_and_char(pointer p) { /* prints family and character */ tprint_esc("fam"); print_int(math_fam(p)); @@ -590,9 +579,10 @@ static void print_fam_and_char(pointer p) print(math_character(p)); } -static void print_delimiter(pointer p) +@ @c +void print_delimiter(pointer p) { - integer a; + int a; if (small_fam(p) < 0) { print_int(-1); /* this should never happen */ } else if (small_fam(p) < 16 && large_fam(p) < 16 && @@ -615,19 +605,18 @@ static void print_delimiter(pointer p) } } -/* + @ The next subroutine will descend to another level of recursion when a subsidiary mlist needs to be displayed. The parameter |c| indicates what character is to become part of the recursion history. An empty mlist is distinguished from a missing field, because these are not equivalent (as explained above). @^recursion@> -*/ - -static void print_subsidiary_data(pointer p, ASCII_code c) +@c +void print_subsidiary_data(pointer p, ASCII_code c) { /* display a noad field */ - if (cur_length >= depth_threshold) { + if ((int) cur_length >= depth_threshold) { if (p != null) tprint(" []"); } else { @@ -653,10 +642,11 @@ static void print_subsidiary_data(pointer p, ASCII_code c) break; } } - flush_char; /* remove |c| from the recursion history */ + flush_char(); /* remove |c| from the recursion history */ } } +@ @c void display_normal_noad(pointer p) { switch (type(p)) { @@ -745,6 +735,7 @@ void display_normal_noad(pointer p) print_subsidiary_data(subscr(p), '_'); } +@ @c void display_fence_noad(pointer p) { if (subtype(p) == right_noad_side) @@ -756,6 +747,7 @@ void display_fence_noad(pointer p) print_delimiter(delimiter(p)); } +@ @c void display_fraction_noad(pointer p) { tprint_esc("fraction, thickness "); @@ -783,8 +775,8 @@ void display_fraction_noad(pointer p) print_subsidiary_data(denominator(p), '/'); } -/* -The routines that \TeX\ uses to create mlists are similar to those we have + +@ The routines that \TeX\ uses to create mlists are similar to those we have just seen for the generation of hlists and vlists. But it is necessary to make ``noads'' as well as nodes, so the reader should review the discussion of math mode data structures before trying to make sense out of @@ -792,21 +784,21 @@ the following program. Here is a little routine that needs to be done whenever a subformula is about to be processed. The parameter is a code like |math_group|. -*/ -static void new_save_level_math(group_code c) +@c +void new_save_level_math(group_code c) { - saved(0) = text_dir_ptr; + set_saved_record(0, saved_textdir, 0, text_dir_ptr); text_dir_ptr = new_dir(math_direction); incr(save_ptr); new_save_level(c); - eq_word_define(static_int_base + param_body_direction_code, math_direction); - eq_word_define(static_int_base + param_par_direction_code, math_direction); - eq_word_define(static_int_base + param_text_direction_code, math_direction); - eq_word_define(static_int_base + param_level_local_dir_code, cur_level); + eq_word_define(int_base + body_direction_code, math_direction); + eq_word_define(int_base + par_direction_code, math_direction); + eq_word_define(int_base + text_direction_code, math_direction); } -static void push_math(group_code c, int mstyle) +@ @c +void push_math(group_code c, int mstyle) { if (math_direction != text_direction) dir_math_save = true; @@ -817,22 +809,24 @@ static void push_math(group_code c, int mstyle) new_save_level_math(c); } -static void enter_ordinary_math(void) +@ @c +void enter_ordinary_math(void) { push_math(math_shift_group, text_style); - eq_word_define(static_int_base + param_cur_fam_code, -1); + eq_word_define(int_base + cur_fam_code, -1); if (every_math != null) begin_token_list(every_math, every_math_text); } -static void enter_display_math(void); +@ @c +void enter_display_math(void); -/* We get into math mode from horizontal mode when a `\.\$' (i.e., a +@ We get into math mode from horizontal mode when a `\.\$' (i.e., a |math_shift| character) is scanned. We must check to see whether this `\.\$' is immediately followed by another, in case display math mode is called for. -*/ +@c void init_math(void) { if (cur_cmd == math_shift_cmd) { @@ -852,28 +846,25 @@ void init_math(void) } } -/* -We get into ordinary math mode from display math mode when `\.{\\eqno}' or + +@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or `\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; the value of |cur_chr| is placed onto |save_stack| for safe keeping. -*/ -/* -When \TeX\ is in display math mode, |cur_group=math_shift_group|, +@ When \TeX\ is in display math mode, |cur_group=math_shift_group|, so it is not necessary for the |start_eq_no| procedure to test for this condition. -*/ +@c void start_eq_no(void) { - saved(0) = cur_chr; + set_saved_record(0, saved_eqno, 0, cur_chr); incr(save_ptr); enter_ordinary_math(); } -/* -Subformulas of math formulas cause a new level of math mode to be entered, +@ Subformulas of math formulas cause a new level of math mode to be entered, on the semantic nest as well as the save stack. These subformulas arise in several ways: (1)~A left brace by itself indicates the beginning of a subformula that will be put into a box, thereby freezing its glue and @@ -886,8 +877,8 @@ The group codes placed on |save_stack| in these three cases are Here is the code that handles case (1); the other cases are not quite as trivial, so we shall consider them later. -*/ +@c void math_left_brace(void) { pointer q; @@ -898,26 +889,34 @@ void math_left_brace(void) (void) scan_math(nucleus(tail), m_style); } -/* -When we enter display math mode, we need to call |line_break| to + +@ When we enter display math mode, we need to call |line_break| to process the partial paragraph that has just been interrupted by the display. Then we can set the proper values of |display_width| and |display_indent| and |pre_display_size|. -*/ - -static void enter_display_math(void) +@c +void enter_display_math(void) { scaled w; /* new or partial |pre_display_size| */ scaled l; /* new |display_width| */ scaled s; /* new |display_indent| */ pointer p; - integer n; /* scope of paragraph shape specification */ - if (head == tail) { /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ + int n; /* scope of paragraph shape specification */ + if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ + (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */ + type(tail) == whatsit_node && + subtype(tail) == local_par_node && vlink(tail) == null)) { + if (vlink(head) == tail) { + /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if + there is another display immediately following, we have to get rid + of that node */ + flush_node(tail); + } pop_nest(); w = -max_dimen; } else { - line_break(true); + line_break(true, math_shift_group); w = actual_box_width(just_box, (2 * quad(get_cur_font()))); } /* now we are in vertical mode, working on the list that will contain the display */ @@ -948,10 +947,10 @@ static void enter_display_math(void) push_math(math_shift_group, display_style); mode = mmode; - eq_word_define(static_int_base + param_cur_fam_code, -1); - eq_word_define(static_dimen_base + param_pre_display_size_code, w); - eq_word_define(static_dimen_base + param_display_width_code, l); - eq_word_define(static_dimen_base + param_display_indent_code, s); + eq_word_define(int_base + cur_fam_code, -1); + eq_word_define(dimen_base + pre_display_size_code, w); + eq_word_define(dimen_base + display_width_code, l); + eq_word_define(dimen_base + display_indent_code, s); if (every_display != null) begin_token_list(every_display, every_display_text); if (nest_ptr == 1) { @@ -961,25 +960,25 @@ static void enter_display_math(void) } } -/* The next routine parses all variations of a delimiter code. The |extcode| - * tells what syntax form to use (\TeX, \Aleph, \XeTeX, \XeTeXnum, ...) , the - * |doclass| tells whether or not read a math class also (for \delimiter c.s.). - * (the class is passed on for conversion to |\mathchar|). - */ +@ The next routine parses all variations of a delimiter code. The |extcode| + tells what syntax form to use (\TeX, Aleph, XeTeX, XeTeXnum, ...) , the + |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.). + (the class is passed on for conversion to \.{\\mathchar}). +@c #define fam_in_range ((cur_fam>=0)&&(cur_fam<256)) -static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) +delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) { const char *hlp[] = { "I'm going to use 0 instead of that illegal code value.", NULL }; delcodeval d; - integer cur_val1; /* and the global |cur_val| */ - integer mcls, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; + int cur_val1; /* and the global |cur_val| */ + int mcls, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; mcls = 0; - if (extcode == tex_mathcode) { /* \delcode, this is the easiest */ + if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */ scan_int(); /* "MFCCFCC or "FCCFCC */ if (doclass) { @@ -994,7 +993,7 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) mschr = (cur_val % 0x100000) / 0x1000; mlfam = (cur_val & 0xFFF) / 0x100; mlchr = (cur_val % 0x100); - } else if (extcode == aleph_mathcode) { /* \odelcode */ + } else if (extcode == aleph_mathcode) { /* \.{\\odelcode} */ /* "MFFCCCC"FFCCCC or "FFCCCC"FFCCCC */ scan_int(); if (doclass) { @@ -1012,7 +1011,7 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) mschr = (cur_val1 % 0x10000); mlfam = (cur_val / 0x10000); mlchr = (cur_val % 0x10000); - } else if (extcode == xetex_mathcode) { /* \Udelcode */ + } else if (extcode == xetex_mathcode) { /* \.{\\Udelcode} */ /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */ if (doclass) { scan_int(); @@ -1029,13 +1028,13 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) } mlfam = 0; mlchr = 0; - } else if (extcode == xetexnum_mathcode) { /* \Udelcodenum */ + } else if (extcode == xetexnum_mathcode) { /* \.{\\Udelcodenum} */ /* "FF<21bits> */ /* the largest numeric value is $2^29-1$, but the top of bit 21 can't be used as it contains invalid USV's */ if (doclass) { /* such a primitive doesn't exist */ - tconfusion("xetexnum_mathcode"); + confusion("xetexnum_mathcode"); } scan_int(); msfam = (cur_val / 0x200000); @@ -1049,7 +1048,7 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) mlchr = 0; } else { /* something's gone wrong */ - tconfusion("unknown_extcode"); + confusion("unknown_extcode"); } d.origin_value = extcode; d.class_value = mcls; @@ -1060,18 +1059,21 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) return d; } +@ @c void scan_extdef_del_code(int level, int extcode) { delcodeval d; - integer p; + int p; scan_char_num(); p = cur_val; scan_optional_equals(); d = do_scan_extdef_del_code(extcode, false); set_del_code(p, extcode, d.small_family_value, d.small_character_value, - d.large_family_value, d.large_character_value, level); + d.large_family_value, d.large_character_value, + (quarterword) (level)); } +@ @c mathcodeval scan_mathchar(int extcode) { const char *hlp[] = { @@ -1079,8 +1081,8 @@ mathcodeval scan_mathchar(int extcode) NULL }; mathcodeval d; - integer mcls = 0, mfam = 0, mchr = 0; - if (extcode == tex_mathcode) { /* \mathcode */ + int mcls = 0, mfam = 0, mchr = 0; + if (extcode == tex_mathcode) { /* \.{\\mathcode} */ /* "TFCC */ scan_int(); if (cur_val > 0x8000) { @@ -1090,7 +1092,7 @@ mathcodeval scan_mathchar(int extcode) mcls = (cur_val / 0x1000); mfam = ((cur_val % 0x1000) / 0x100); mchr = (cur_val % 0x100); - } else if (extcode == aleph_mathcode) { /* \omathcode */ + } else if (extcode == aleph_mathcode) { /* \.{\\omathcode} */ /* "TFFCCCC */ scan_int(); if (cur_val > 0x8000000) { @@ -1133,7 +1135,7 @@ mathcodeval scan_mathchar(int extcode) } } else { /* something's gone wrong */ - tconfusion("unknown_extcode"); + confusion("unknown_extcode"); } d.class_value = mcls; d.family_value = mfam; @@ -1142,22 +1144,22 @@ mathcodeval scan_mathchar(int extcode) return d; } - +@ @c void scan_extdef_math_code(int level, int extcode) { mathcodeval d; - integer p; + int p; scan_char_num(); p = cur_val; scan_optional_equals(); d = scan_mathchar(extcode); set_math_code(p, extcode, d.class_value, - d.family_value, d.character_value, level); + d.family_value, d.character_value, (quarterword) (level)); } -/* this reads in a delcode when actually a mathcode is needed */ - +@ this reads in a delcode when actually a mathcode is needed +@c mathcodeval scan_delimiter_as_mathchar(int extcode) { delcodeval dval; @@ -1170,11 +1172,11 @@ mathcodeval scan_delimiter_as_mathchar(int extcode) return mval; } -/* this has to match the inverse routine in the pascal code - * where the |\Umathchardef| is executed - */ - -mathcodeval mathchar_from_integer(integer value, int extcode) +@ this has to match the inverse routine in the pascal code + where the \.{\\Umathchardef} is executed + +@c +mathcodeval mathchar_from_integer(int value, int extcode) { mathcodeval mval; mval.origin_value = extcode; @@ -1195,22 +1197,21 @@ mathcodeval mathchar_from_integer(integer value, int extcode) return mval; } -/* Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad +@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are broken down into subfields called |type| and either |math_list| or |(math_fam,math_character)|. The job of |scan_math| is to figure out what to place in one of these principal fields; it looks at the subformula that comes next in the input, and places an encoding of that subformula into a given word of |mem|. -*/ - +@c #define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) int scan_math(pointer p, int mstyle) { /* label restart,reswitch,exit; */ - mathcodeval mval; + mathcodeval mval = { 0, 0, 0, 0 }; assert(p != null); RESTART: get_next_nb_nr(); @@ -1246,7 +1247,7 @@ int scan_math(pointer p, int mstyle) else if (cur_chr == 3) mval = scan_mathchar(xetexnum_mathcode); else - tconfusion("scan_math"); + confusion("scan_math"); break; case math_given_cmd: mval = mathchar_from_integer(cur_chr, tex_mathcode); @@ -1265,14 +1266,14 @@ int scan_math(pointer p, int mstyle) else if (cur_chr == 2) mval = scan_delimiter_as_mathchar(xetex_mathcode); else - tconfusion("scan_math"); + confusion("scan_math"); break; default: /* The pointer |p| is placed on |save_stack| while a complex subformula is being scanned. */ back_input(); scan_left_brace(); - saved(0) = p; + set_saved_record(0, saved_math, 0, p); incr(save_ptr); push_math(math_group, mstyle); return 1; @@ -1287,13 +1288,13 @@ int scan_math(pointer p, int mstyle) } -/* - The |set_math_char| procedure creates a new noad appropriate to a given + +@ The |set_math_char| procedure creates a new noad appropriate to a given math code, and appends it to the current mlist. However, if the math code is sufficiently large, the |cur_chr| is treated as an active character and nothing is appended. -*/ +@c void set_math_char(mathcodeval mval) { pointer p; /* the new noad */ @@ -1334,13 +1335,13 @@ void set_math_char(mathcodeval mval) } -/* - The |math_char_in_text| procedure creates a new node representing a math char + +@ The |math_char_in_text| procedure creates a new node representing a math char in text code, and appends it to the current list. However, if the math code is sufficiently large, the |cur_chr| is treated as an active character and nothing is appended. -*/ +@c void math_char_in_text(mathcodeval mval) { pointer p; /* the new node */ @@ -1360,11 +1361,12 @@ void math_char_in_text(mathcodeval mval) } +@ @c void math_math_comp(void) { pointer q; tail_append(new_noad()); - subtype(tail) = cur_chr; + subtype(tail) = (quarterword) cur_chr; q = new_node(math_char_node, 0); nucleus(tail) = q; if (cur_chr == over_noad_type) @@ -1374,6 +1376,7 @@ void math_math_comp(void) } +@ @c void math_limit_switch(void) { const char *hlp[] = { @@ -1382,29 +1385,28 @@ void math_limit_switch(void) }; if (head != tail) { if (type(tail) == simple_noad) { - subtype(tail) = cur_chr; + subtype(tail) = (quarterword) cur_chr; return; } } tex_error("Limit controls must follow a math operator", hlp); } -/* - Delimiter fields of noads are filled in by the |scan_delimiter| routine. + +@ Delimiter fields of noads are filled in by the |scan_delimiter| routine. The first parameter of this procedure is the |mem| address where the delimiter is to be placed; the second tells if this delimiter follows \.{\\radical} or not. -*/ - -static void scan_delimiter(pointer p, integer r) +@c +void scan_delimiter(pointer p, int r) { - delcodeval dval; - if (r == tex_mathcode) { /* \radical */ + delcodeval dval = { 0, 0, 0, 0, 0, 0 }; + if (r == tex_mathcode) { /* \.{\\radical} */ dval = do_scan_extdef_del_code(tex_mathcode, true); - } else if (r == aleph_mathcode) { /* \oradical */ + } else if (r == aleph_mathcode) { /* \.{\\oradical} */ dval = do_scan_extdef_del_code(aleph_mathcode, true); - } else if (r == xetex_mathcode) { /* \Uradical */ + } else if (r == xetex_mathcode) { /* \.{\\Uradical} */ dval = do_scan_extdef_del_code(xetex_mathcode, false); } else if (r == no_mathcode) { get_next_nb_nr(); @@ -1414,21 +1416,21 @@ static void scan_delimiter(pointer p, integer r) dval = get_del_code(cur_chr); break; case delim_num_cmd: - if (cur_chr == 0) /* \delimiter */ + if (cur_chr == 0) /* \.{\\delimiter} */ dval = do_scan_extdef_del_code(tex_mathcode, true); - else if (cur_chr == 1) /* \odelimiter */ + else if (cur_chr == 1) /* \.{\\odelimiter} */ dval = do_scan_extdef_del_code(aleph_mathcode, true); - else if (cur_chr == 2) /* \Udelimiter */ + else if (cur_chr == 2) /* \.{\\Udelimiter} */ dval = do_scan_extdef_del_code(xetex_mathcode, true); else - tconfusion("scan_delimiter1"); + confusion("scan_delimiter1"); break; default: dval.small_family_value = -1; break; } } else { - tconfusion("scan_delimiter2"); + confusion("scan_delimiter2"); } if (p == null) return; @@ -1457,6 +1459,7 @@ static void scan_delimiter(pointer p, integer r) } +@ @c void math_radical(void) { halfword q; @@ -1464,24 +1467,24 @@ void math_radical(void) tail_append(new_node(radical_noad, chr_code)); q = new_node(delim_node, 0); left_delimiter(tail) = q; - if (chr_code == 0) /* \radical */ + if (chr_code == 0) /* \.{\\radical} */ scan_delimiter(left_delimiter(tail), tex_mathcode); - else if (chr_code == 1) /* \oradical */ + else if (chr_code == 1) /* \.{\\oradical} */ scan_delimiter(left_delimiter(tail), aleph_mathcode); - else if (chr_code == 2) /* \Uradical */ + else if (chr_code == 2) /* \.{\\Uradical} */ scan_delimiter(left_delimiter(tail), xetex_mathcode); - else if (chr_code == 3) /* \Uroot */ + else if (chr_code == 3) /* \.{\\Uroot} */ scan_delimiter(left_delimiter(tail), xetex_mathcode); - else if (chr_code == 4) /* \Uunderdelimiter */ + else if (chr_code == 4) /* \.{\\Uunderdelimiter} */ scan_delimiter(left_delimiter(tail), xetex_mathcode); - else if (chr_code == 5) /* \Uoverdelimiter */ + else if (chr_code == 5) /* \.{\\Uoverdelimiter} */ scan_delimiter(left_delimiter(tail), xetex_mathcode); - else if (chr_code == 6) /* \Udelimiterunder */ + else if (chr_code == 6) /* \.{\\Udelimiterunder} */ scan_delimiter(left_delimiter(tail), xetex_mathcode); - else if (chr_code == 7) /* \Udelimiterover */ + else if (chr_code == 7) /* \.{\\Udelimiterover} */ scan_delimiter(left_delimiter(tail), xetex_mathcode); else - tconfusion("math_radical"); + confusion("math_radical"); if (chr_code == 3) { /* the trick with the |vlink(q)| is used by |scan_math| to decide whether it needs to go on */ @@ -1501,14 +1504,12 @@ void math_radical(void) } } +@ @c void math_ac(void) { halfword q; - mathcodeval t, b; - t.character_value = 0; - t.family_value = 0; - b.character_value = 0; - b.family_value = 0; + mathcodeval t = { 0, 0, 0, 0 }, b = { + 0, 0, 0, 0}; if (cur_cmd == accent_cmd) { const char *hlp[] = { "I'm changing \\accent to \\mathaccent here; wish me luck.", @@ -1518,19 +1519,19 @@ void math_ac(void) tex_error("Please use \\mathaccent for accents in math mode", hlp); } tail_append(new_node(accent_noad, 0)); - if (cur_chr == 0) { /* \mathaccent */ + if (cur_chr == 0) { /* \.{\\mathaccent} */ t = scan_mathchar(tex_mathcode); - } else if (cur_chr == 1) { /* \omathaccent */ + } else if (cur_chr == 1) { /* \.{\\omathaccent} */ t = scan_mathchar(aleph_mathcode); - } else if (cur_chr == 2) { /* \Umathaccent */ + } else if (cur_chr == 2) { /* \.{\\Umathaccent} */ t = scan_mathchar(xetex_mathcode); - } else if (cur_chr == 3) { /* \Umathbotaccent */ + } else if (cur_chr == 3) { /* \.{\\Umathbotaccent} */ b = scan_mathchar(xetex_mathcode); - } else if (cur_chr == 4) { /* \Umathaccents */ + } else if (cur_chr == 4) { /* \.{\\Umathaccents} */ t = scan_mathchar(xetex_mathcode); b = scan_mathchar(xetex_mathcode); } else { - tconfusion("math_ac"); + confusion("math_ac"); } if (!(t.character_value == 0 && t.family_value == 0)) { q = new_node(math_char_node, 0); @@ -1555,6 +1556,7 @@ void math_ac(void) (void) scan_math(nucleus(tail), cramped_style(m_style)); } +@ @c pointer math_vcenter_group(pointer p) { pointer q, r; @@ -1566,19 +1568,21 @@ pointer math_vcenter_group(pointer p) return q; } -/* -The routine that scans the four mlists of a \.{\\mathchoice} is very + +@ The routine that scans the four mlists of a \.{\\mathchoice} is very much like the routine that builds discretionary nodes. -*/ +@c void append_choices(void) { tail_append(new_choice()); incr(save_ptr); - saved(-1) = 0; + set_saved_record(-1, saved_choices, 0, 0); push_math(math_choice_group, display_style); scan_left_brace(); } + +@ @c void build_choices(void) { pointer p; /* the current mlist */ @@ -1586,7 +1590,8 @@ void build_choices(void) prev_style = m_style; unsave_math(); p = fin_mlist(null); - switch (saved(-1)) { + assert(saved_type(-1) == saved_choices); + switch (saved_value(-1)) { case 0: display_mlist(tail) = p; break; @@ -1602,16 +1607,16 @@ void build_choices(void) return; break; } /* there are no other cases */ - incr(saved(-1)); + set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1)); push_math(math_choice_group, (prev_style + 2)); scan_left_brace(); } -/* - Subscripts and superscripts are attached to the previous nucleus by the - action procedure called |sub_sup|. -*/ +@ Subscripts and superscripts are attached to the previous nucleus by the +action procedure called |sub_sup|. + +@c void sub_sup(void) { pointer q; @@ -1620,7 +1625,7 @@ void sub_sup(void) q = new_node(sub_mlist_node, 0); nucleus(tail) = q; } - if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* super_sub_script */ + if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */ if (supscr(tail) != null) { const char *hlp[] = { "I treat `x^1^2' essentially like `x^1{}^2'.", NULL @@ -1649,19 +1654,19 @@ void sub_sup(void) } } -/* -An operation like `\.{\\over}' causes the current mlist to go into a + +@ An operation like `\.{\\over}' causes the current mlist to go into a state of suspended animation: |incompleat_noad| points to a |fraction_noad| that contains the mlist-so-far as its numerator, while the denominator is yet to come. Finally when the mlist is finished, the denominator will go into the incompleat fraction noad, and that noad will become the whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' delimiters. -*/ +@c void math_fraction(void) { - small_number c; /* the type of generalized fraction we are scanning */ + halfword c; /* the type of generalized fraction we are scanning */ pointer q; c = cur_chr; if (incompleat_noad != null) { @@ -1711,14 +1716,13 @@ void math_fraction(void) -/* At the end of a math formula or subformula, the |fin_mlist| routine is +@ At the end of a math formula or subformula, the |fin_mlist| routine is called upon to return a pointer to the newly completed mlist, and to pop the nest back to the enclosing semantic level. The parameter to |fin_mlist|, if not null, points to a |fence_noad| that ends the current mlist; this |fence_noad| has not yet been appended. -*/ - +@c pointer fin_mlist(pointer p) { pointer q; /* the mlist to return */ @@ -1736,7 +1740,7 @@ pointer fin_mlist(pointer p) q = math_list(numerator(incompleat_noad)); if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) || (delim_ptr == null)) - tconfusion("right"); /* this can't happen */ + confusion("right"); /* this can't happen */ math_list(numerator(incompleat_noad)) = vlink(delim_ptr); vlink(delim_ptr) = incompleat_noad; vlink(incompleat_noad) = p; @@ -1750,12 +1754,12 @@ pointer fin_mlist(pointer p) } -/* Now at last we're ready to see what happens when a right brace occurs +@ Now at last we're ready to see what happens when a right brace occurs in a math formula. Two special cases are simplified here: Braces are effectively removed when they surround a single Ord without sub/superscripts, or when they surround an accent that is the nucleus of an Ord atom. -*/ +@c void close_math_group(pointer p) { pointer q; @@ -1763,28 +1767,30 @@ void close_math_group(pointer p) unsave_math(); decr(save_ptr); - type(saved(0)) = sub_mlist_node; + assert(saved_type(0) == saved_math); + type(saved_value(0)) = sub_mlist_node; p = fin_mlist(null); - math_list(saved(0)) = p; + math_list(saved_value(0)) = p; if (p != null) { if (vlink(p) == null) { if (type(p) == simple_noad && subtype(p) == ord_noad_type) { if (subscr(p) == null && supscr(p) == null) { - type(saved(0)) = type(nucleus(p)); + type(saved_value(0)) = type(nucleus(p)); if (type(nucleus(p)) == math_char_node) { - math_fam(saved(0)) = math_fam(nucleus(p)); - math_character(saved(0)) = math_character(nucleus(p)); + math_fam(saved_value(0)) = math_fam(nucleus(p)); + math_character(saved_value(0)) = + math_character(nucleus(p)); } else { - math_list(saved(0)) = math_list(nucleus(p)); + math_list(saved_value(0)) = math_list(nucleus(p)); math_list(nucleus(p)) = null; } - delete_attribute_ref(node_attr(saved(0))); - node_attr(saved(0)) = node_attr(nucleus(p)); + delete_attribute_ref(node_attr(saved_value(0))); + node_attr(saved_value(0)) = node_attr(nucleus(p)); node_attr(nucleus(p)) = null; flush_node(p); } } else if (type(p) == accent_noad) { - if (saved(0) == nucleus(tail)) { + if (saved_value(0) == nucleus(tail)) { if (type(tail) == simple_noad && subtype(tail) == ord_noad_type) { q = head; @@ -1804,27 +1810,27 @@ void close_math_group(pointer p) } } } - if (vlink(saved(0)) > 0) { + if (vlink(saved_value(0)) > 0) { pointer q; q = new_node(math_char_node, 0); - nucleus(vlink(saved(0))) = q; - vlink(saved(0)) = null; - saved(0) = q; - (void) scan_math(saved(0), old_style); + nucleus(vlink(saved_value(0))) = q; + vlink(saved_value(0)) = null; + saved_value(0) = q; + (void) scan_math(saved_value(0), old_style); /* restart */ } } -/* -We have dealt with all constructions of math mode except `\.{\\left}' and + +@ We have dealt with all constructions of math mode except `\.{\\left}' and `\.{\\right}', so the picture is completed by the following sections of -the program. The |middle| feature of \eTeX\ allows one ore several \.{\\middle} +the program. The |middle| feature of eTeX allows one ore several \.{\\middle} delimiters to appear between \.{\\left} and \.{\\right}. -*/ +@c void math_left_right(void) { - small_number t; /* |left_noad_side| .. |right_noad_side| */ + halfword t; /* |left_noad_side| .. |right_noad_side| */ pointer p; /* new noad */ pointer q; /* resulting mlist */ pointer r; /* temporary */ @@ -1851,7 +1857,7 @@ void math_left_right(void) } else { p = new_noad(); type(p) = fence_noad; - subtype(p) = t; + subtype(p) = (quarterword) t; r = new_node(delim_node, 0); delimiter(p) = r; scan_delimiter(delimiter(p), no_mathcode); @@ -1862,7 +1868,7 @@ void math_left_right(void) unsave_math(); } if (t != right_noad_side) { - push_math(math_left_group, cur_style); + push_math(math_left_group, m_style); vlink(head) = q; tail = p; delim_ptr = p; @@ -1877,10 +1883,10 @@ void math_left_right(void) } -/* \TeX\ gets to the following part of the program when +@ \TeX\ gets to the following part of the program when the first `\.\$' ending a display has been scanned. -*/ +@c static void check_second_math_shift(void) { get_x_token(); @@ -1916,89 +1922,18 @@ static void check_inline_math_end(void) } } - - -void finish_displayed_math(boolean l, boolean danger, pointer a); - -void after_math(void) -{ - boolean danger; /* not enough symbol fonts are present */ - integer m; /* |mmode| or |-mmode| */ - pointer p; /* the formula */ - pointer a = null; /* box containing equation number */ - boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ - danger = check_necessary_fonts(); - m = mode; - p = fin_mlist(null); /* this pops the nest */ - if (cur_cmd == math_shift_cs_cmd && - (cur_chr == text_style || cur_chr == display_style)) { - you_cant(); - } - if (mode == -m) { /* end of equation number */ - if (cur_cmd == math_shift_cmd) { - check_second_math_shift(); - } else { - check_display_math_end(); - } - run_mlist_to_hlist(p, text_style, false); - a = hpack(vlink(temp_head), 0, additional); - unsave_math(); - decr(save_ptr); /* now |cur_group=math_shift_group| */ - if (saved(0) == 1) - l = true; - danger = check_necessary_fonts(); - m = mode; - p = fin_mlist(null); - } - if (m < 0) { - /* The |unsave| is done after everything else here; hence an appearance of - `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these - particular \.\$'s. This is consistent with the conventions of - `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the - space above that display. - */ - if (cur_cmd == math_shift_cs_cmd) { - check_inline_math_end(); - } - tail_append(new_math(math_surround, before)); - if (dir_math_save) { - tail_append(new_dir(math_direction)); - } - run_mlist_to_hlist(p, text_style, (mode > 0)); - vlink(tail) = vlink(temp_head); - while (vlink(tail) != null) - tail = vlink(tail); - if (dir_math_save) { - tail_append(new_dir(math_direction - 64)); - } - dir_math_save = false; - tail_append(new_math(math_surround, after)); - space_factor = 1000; - unsave_math(); - } else { - if (a == null) { - if (cur_cmd == math_shift_cmd) { - check_second_math_shift(); - } else { - check_display_math_end(); - } - } - run_mlist_to_hlist(p, display_style, false); - finish_displayed_math(l, danger, a); - } -} - - -static void resume_after_display(void) +@ @c +void resume_after_display(void) { if (cur_group != math_shift_group) - tconfusion("display"); + confusion("display"); unsave_math(); prev_graf = prev_graf + 3; push_nest(); mode = hmode; space_factor = 1000; - tail_append(make_local_par_node()); + tail_append(make_local_par_node()); /* this needs to be intercepted in + the display math start ! */ get_x_token(); if (cur_cmd != spacer_cmd) back_input(); @@ -2009,68 +1944,102 @@ static void resume_after_display(void) } -/* -We have saved the worst for last: The fussiest part of math mode processing -occurs when a displayed formula is being centered and placed with an optional -equation number. -*/ -/* At this time |p| points to the mlist for the formula; |a| is either - |null| or it points to a box containing the equation number; and we are in - vertical mode (or internal vertical mode). -*/ +@ If the inline directions of \.{\\pardir} and \.{\\mathdir} are +opposite, then this function will return true. Discovering that fact +is somewhat odd because it needs traversal of the |save_stack|. +The occurance of displayed equations is weird enough that this is +probably still better than having yet another field in the |input_stack| +structures. + +None of this makes much sense if the inline direction of either one of +\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current +math machinery is ill suited anyway so I do not bother to test that. + +@c +static boolean math_and_text_reversed_p(void) +{ + int i = save_ptr - 1; + while (save_type(i) != level_boundary) + i--; + while (i < save_ptr) { + if (save_type(i) == restore_old_value && + save_value(i) == int_base + par_direction_code) { + if (textdir_opposite(math_direction, save_value(i - 1))) + return true; + } + i++; + } + return false; +} + + -void finish_displayed_math(boolean l, boolean danger, pointer a) +@ The fussiest part of math mode processing occurs when a displayed formula is +being centered and placed with an optional equation number. + + +At this time we are in vertical mode (or internal vertical mode). + + |p| points to the mlist for the formula. + |a| is either |null| or it points to a box containing the equation number. + |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box). + + +@c +static void finish_displayed_math(boolean l, pointer a, pointer p) { - pointer b; /* box containing the equation */ - scaled w; /* width of the equation */ - scaled z; /* width of the line */ - scaled e; /* width of equation number */ - scaled q; /* width of equation number plus space to separate from equation */ + pointer eq_box; /* box containing the equation */ + scaled eq_w; /* width of the equation */ + scaled line_w; /* width of the line */ + scaled eqno_w; /* width of equation number */ + scaled eqno_w2; /* width of equation number plus space to separate from equation */ + scaled line_s; /* move the line right this much */ scaled d; /* displacement of equation in the line */ - scaled s; /* move the line right this much */ small_number g1, g2; /* glue parameter codes for before and after */ pointer r; /* kern node used to position the display */ pointer t; /* tail of adjustment list */ pointer pre_t; /* tail of pre-adjustment list */ - pointer p; - p = vlink(temp_head); + boolean swap_dir; /* true if the math and surrounding text dirs are opposed */ + swap_dir = math_and_text_reversed_p(); + adjust_tail = adjust_head; pre_adjust_tail = pre_adjust_head; - b = hpack(p, 0, additional); - p = list_ptr(b); + eq_box = hpack(p, 0, additional, -1); + p = list_ptr(eq_box); t = adjust_tail; adjust_tail = null; pre_t = pre_adjust_tail; pre_adjust_tail = null; - w = width(b); - z = display_width; - s = display_indent; - if ((a == null) || danger) { - e = 0; - q = 0; + eq_w = width(eq_box); + line_w = display_width; + line_s = display_indent; + if (a == null) { + eqno_w = 0; + eqno_w2 = 0; } else { - e = width(a); - q = e + get_math_quad(text_size); + eqno_w = width(a); + eqno_w2 = eqno_w + get_math_quad(text_size); } - if (w + q > z) { + if (eq_w + eqno_w2 > line_w) { /* The user can force the equation number to go on a separate line by causing its width to be zero. */ - if ((e != 0) && ((w - total_shrink[normal] + q <= z) || - (total_shrink[sfi] != 0) || (total_shrink[fil] != 0) || - (total_shrink[fill] != 0) - || (total_shrink[filll] != 0))) { - list_ptr(b) = null; - flush_node(b); - b = hpack(p, z - q, exactly); + if ((eqno_w != 0) + && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w) + || (total_shrink[sfi] != 0) || (total_shrink[fil] != 0) + || (total_shrink[fill] != 0) + || (total_shrink[filll] != 0))) { + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w - eqno_w2, exactly, -1); } else { - e = 0; - if (w > z) { - list_ptr(b) = null; - flush_node(b); - b = hpack(p, z, exactly); + eqno_w = 0; + if (eq_w > line_w) { + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w, exactly, -1); } } - w = width(b); + eq_w = width(eq_box); } /* We try first to center the display without regard to the existence of the equation number. If that would make it too close (where ``too close'' @@ -2080,9 +2049,9 @@ void finish_displayed_math(boolean l, boolean danger, pointer a) is taken only if the display begins with glue, since we assume that the user put glue there to control the spacing precisely. */ - d = half(z - w); - if ((e > 0) && (d < 2 * e)) { /* too close */ - d = half(z - w - e); + d = half(line_w - eq_w); + if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */ + d = half(line_w - eq_w - eqno_w); if (p != null) if (!is_char_node(p)) if (type(p) == glue_node) @@ -2095,41 +2064,62 @@ void finish_displayed_math(boolean l, boolean danger, pointer a) displacement for all three potential lines of the display, even though `\.{\\parshape}' may specify them differently. */ - tail_append(new_penalty(int_par(param_pre_display_penalty_code))); - if ((d + s <= pre_display_size) || l) { /* not enough clearance */ - g1 = param_above_display_skip_code; - g2 = param_below_display_skip_code; + tail_append(new_penalty(int_par(pre_display_penalty_code))); + if ((d + line_s <= pre_display_size) || l) { /* not enough clearance */ + g1 = above_display_skip_code; + g2 = below_display_skip_code; } else { - g1 = param_above_display_short_skip_code; - g2 = param_below_display_short_skip_code; + g1 = above_display_short_skip_code; + g2 = below_display_short_skip_code; } - if (l && (e == 0)) { /* it follows that |type(a)=hlist_node| */ - shift_amount(a) = s; + + if (l && (eqno_w == 0)) { /* \.{\\leqno} on a forced single line due to |width=0| */ + /* it follows that |type(a)=hlist_node| */ + if (swap_dir) { + shift_amount(a) = line_w + line_s; + } else { + shift_amount(a) = line_s; + } append_to_vlist(a); tail_append(new_penalty(inf_penalty)); } else { tail_append(new_param_glue(g1)); } - if (e != 0) { - r = new_kern(z - w - e - d); + if (eqno_w != 0) { + r = new_kern(line_w - eq_w - eqno_w - d); if (l) { vlink(a) = r; - vlink(r) = b; - b = a; + vlink(r) = eq_box; + eq_box = a; d = 0; } else { - vlink(b) = r; + vlink(eq_box) = r; vlink(r) = a; } - b = hpack(b, 0, additional); + eq_box = hpack(eq_box, 0, additional, -1); + } + if (swap_dir) { + /* |d = line_w - d;| */ + if (eqno_w != 0) { + if (l) + d = line_w - width(eq_box); + else + d = 0; + } else { + d = line_w - eq_w - eqno_w - d; + } } - shift_amount(b) = s + d; - append_to_vlist(b); + shift_amount(eq_box) = line_s + d; + append_to_vlist(eq_box); - if ((a != null) && (e == 0) && !l) { + if ((a != null) && (eqno_w == 0) && !l) { tail_append(new_penalty(inf_penalty)); - shift_amount(a) = s + z - width(a); + if (!swap_dir) { + shift_amount(a) = line_s + line_w - width(a); + } else { + shift_amount(a) = line_s; + } append_to_vlist(a); g2 = 0; } @@ -2141,23 +2131,88 @@ void finish_displayed_math(boolean l, boolean danger, pointer a) vlink(tail) = vlink(pre_adjust_head); tail = pre_t; } - tail_append(new_penalty(int_par(param_post_display_penalty_code))); + tail_append(new_penalty(int_par(post_display_penalty_code))); if (g2 > 0) tail_append(new_param_glue(g2)); resume_after_display(); } +@ @c +void after_math(void) +{ + int m; /* |mmode| or |-mmode| */ + pointer p; /* the formula */ + pointer a = null; /* box containing equation number */ + boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ + m = mode; + p = fin_mlist(null); /* this pops the nest */ + if (cur_cmd == math_shift_cs_cmd && + (cur_chr == text_style || cur_chr == display_style)) { + you_cant(); + } + if (mode == -m) { /* end of equation number */ + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + run_mlist_to_hlist(p, text_style, false); + a = hpack(vlink(temp_head), 0, additional, -1); + unsave_math(); + decr(save_ptr); /* now |cur_group=math_shift_group| */ + assert(saved_type(0) == saved_eqno); + if (saved_value(0) == 1) + l = true; + m = mode; + p = fin_mlist(null); + } + if (m < 0) { + /* The |unsave| is done after everything else here; hence an appearance of + `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these + particular \.\$'s. This is consistent with the conventions of + `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the + space above that display. + */ + if (cur_cmd == math_shift_cs_cmd) { + check_inline_math_end(); + } + tail_append(new_math(math_surround, before)); + if (dir_math_save) { + tail_append(new_dir(math_direction)); + } + run_mlist_to_hlist(p, text_style, (mode > 0)); + vlink(tail) = vlink(temp_head); + while (vlink(tail) != null) + tail = vlink(tail); + if (dir_math_save) { + tail_append(new_dir(math_direction - 64)); + } + dir_math_save = false; + tail_append(new_math(math_surround, after)); + space_factor = 1000; + unsave_math(); + } else { + if (a == null) { + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + } + run_mlist_to_hlist(p, display_style, false); + finish_displayed_math(l, a, vlink(temp_head)); + } +} + -/* - When \.{\\halign} appears in a display, the alignment routines operate +@ When \.{\\halign} appears in a display, the alignment routines operate essentially as they do in vertical mode. Then the following program is activated, with |p| and |q| pointing to the beginning and end of the resulting list, and with |aux_save| holding the |prev_depth| value. -*/ - -void finish_display_alignment(pointer p, pointer q, memory_word aux_save) +@c +void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) { do_assignments(); if (cur_cmd != math_shift_cmd) { @@ -2171,19 +2226,20 @@ void finish_display_alignment(pointer p, pointer q, memory_word aux_save) check_second_math_shift(); } pop_nest(); - tail_append(new_penalty(int_par(param_pre_display_penalty_code))); - tail_append(new_param_glue(param_above_display_skip_code)); + tail_append(new_penalty(int_par(pre_display_penalty_code))); + tail_append(new_param_glue(above_display_skip_code)); vlink(tail) = p; if (p != null) tail = q; - tail_append(new_penalty(int_par(param_post_display_penalty_code))); - tail_append(new_param_glue(param_below_display_skip_code)); - prev_depth = aux_save.cint; + tail_append(new_penalty(int_par(post_display_penalty_code))); + tail_append(new_param_glue(below_display_skip_code)); + cur_list.prev_depth_field = saved_prevdepth; resume_after_display(); } -/* Interface to \.{\\Umath} and \.{\\mathstyle} */ +@ Interface to \.{\\Umath} and \.{\\mathstyle} +@c void setup_math_style(void) { pointer q; @@ -2194,6 +2250,7 @@ void setup_math_style(void) } +@ @c void print_math_style(void) { if (abs(mode) == mmode) diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.h b/Build/source/texk/web2c/luatexdir/tex/texnodes.h new file mode 100644 index 00000000000..556f8fb2c72 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.h @@ -0,0 +1,780 @@ +/* nodes.h + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: texnodes.h 3552 2010-03-26 15:30:08Z taco $ */ + +#include <stdarg.h> + +#ifndef __NODES_H__ +# define __NODES_H__ + +/* these are in texlang.c */ + +# define set_vlink(a,b) vlink(a)=b +# define get_vlink(a) vlink(a) +# define get_character(a) character(a) + +extern halfword insert_discretionary(halfword t, halfword pre, halfword post, + halfword replace); +extern halfword insert_syllable_discretionary(halfword t, lang_variables * lan); +extern halfword insert_word_discretionary(halfword t, lang_variables * lan); +extern halfword insert_complex_discretionary(halfword t, lang_variables * lan, + halfword pre, halfword post, + halfword replace); +extern halfword insert_character(halfword t, int n); +extern void set_disc_field(halfword f, halfword t); + +# define varmemcast(a) (memory_word *)(a) +extern memory_word *volatile varmem; +extern halfword var_mem_max; + +extern halfword get_node(int s); +extern void free_node(halfword p, int s); +extern void init_node_mem(int s); +extern void dump_node_mem(void); +extern void undump_node_mem(void); + + +# define max_halfword 0x3FFFFFFF +# define max_dimen 0x3FFFFFFF +# ifndef null +# define null 0 +# endif +# define null_flag -0x40000000 +# define zero_glue 0 +# define normal 0 + +# define vinfo(a) varmem[(a)].hh.v.LH +# define vlink(a) varmem[(a)].hh.v.RH +# define type(a) varmem[(a)].hh.u.B0 +# define subtype(a) varmem[(a)].hh.u.B1 +# define node_attr(a) vinfo((a)+1) +# define alink(a) vlink((a)+1) + +# define node_size(a) varmem[(a)].hh.v.LH + +# define rlink(a) vlink((a)+1) + /* aka alink() */ +# define llink(a) vinfo((a)+1) + /* overlaps with node_attr() */ + +# define add_glue_ref(a) glue_ref_count(a)++ /* new reference to a glue spec */ + +/* really special head node pointers that only need links */ + +# define temp_node_size 2 + +/* attribute lists */ + +# define UNUSED_ATTRIBUTE -0x7FFFFFFF /* as low as it goes */ + +/* it is convenient to have attribute list nodes and attribute node + * be the same size + */ + +# define attribute_node_size 2 + +# define attr_list_ref(a) vinfo((a)+1) + /* the reference count */ +# define attribute_id(a) vinfo((a)+1) +# define attribute_value(a) vlink((a)+1) + +# define cache_disabled max_halfword +# define add_node_attr_ref(a) { if (a!=null) attr_list_ref((a))++; } + +# define replace_attribute_list(a,b) do { \ + delete_attribute_ref(node_attr(a)); \ + node_attr(a)=b; \ + } while (0) + +extern void update_attribute_cache(void); +extern halfword copy_attribute_list(halfword n); +extern halfword do_set_attribute(halfword p, int i, int val); + + +/* a glue spec */ +# define glue_spec_size 4 +# define stretch(a) vlink((a)+1) +/* width == a+2 */ +# define shrink(a) vinfo((a)+1) +# define stretch_order(a) type((a)+3) +# define shrink_order(a) subtype((a)+3) +# define glue_ref_count(a) vlink((a)+3) + +# define width_offset 2 +# define depth_offset 3 +# define height_offset 4 +# define list_offset 6 + +typedef enum { + cond_math_glue = 98, /* special |subtype| to suppress glue in the next node */ + mu_glue, /* |subtype| for math glue */ + a_leaders, /* |subtype| for aligned leaders */ + c_leaders, /* |subtype| for centered leaders */ + x_leaders, /* |subtype| for expanded leaders */ + g_leaders /* |subtype| for global (page) leaders */ +} glue_subtype_codes; + +/* normal nodes */ + +# define inf_bad 10000 /* infinitely bad value */ +# define inf_penalty inf_bad /*``infinite'' penalty value */ +# define eject_penalty -(inf_penalty) /*``negatively infinite'' penalty value */ + +# define penalty_node_size 3 +# define penalty(a) vlink((a)+2) + +# define glue_node_size 4 +# define glue_ptr(a) vinfo((a)+2) +# define leader_ptr(a) vlink((a)+2) +# define synctex_tag_glue(a) vinfo((a)+3) +# define synctex_line_glue(a) vlink((a)+3) + +/* disc nodes could eventually be smaller, because the indirect + pointers are not really needed (8 instead of 10). + */ + +# define disc_node_size 10 + +typedef enum { + discretionary_disc = 0, + explicit_disc, + automatic_disc, + syllable_disc, + init_disc, /* first of a duo of syllable_discs */ + select_disc, /* second of a duo of syllable_discs */ +} discretionary_types; + +# define pre_break_head(a) ((a)+4) +# define post_break_head(a) ((a)+6) +# define no_break_head(a) ((a)+8) + +# define pre_break(a) vinfo((a)+2) +# define post_break(a) vlink((a)+2) +# define no_break(a) vlink((a)+3) +# define tlink llink + +# define vlink_pre_break(a) vlink(pre_break_head(a)) +# define vlink_post_break(a) vlink(post_break_head(a)) +# define vlink_no_break(a) vlink(no_break_head(a)) + +# define tlink_pre_break(a) tlink(pre_break_head(a)) +# define tlink_post_break(a) tlink(post_break_head(a)) +# define tlink_no_break(a) tlink(no_break_head(a)) + +# define kern_node_size 5 +# define explicit 1 /*|subtype| of kern nodes from \.{\\kern} and \.{\\/} */ +# define acc_kern 2 /*|subtype| of kern nodes from accents */ +# define synctex_tag_kern(a) vinfo((a)+3) +# define synctex_line_kern(a) vlink((a)+3) +# define ex_kern(a) vinfo((a)+4) /* expansion factor (hz) */ + +# define box_node_size 9 + +# define HLIST_SUBTYPE_UNKNOWN 0 +# define HLIST_SUBTYPE_LINE 1 +# define HLIST_SUBTYPE_HBOX 2 + +# define width(a) varmem[(a)+2].cint +# define depth(a) varmem[(a)+3].cint +# define height(a) varmem[(a)+4].cint +# define shift_amount(a) vlink((a)+5) +# define box_dir(a) vinfo((a)+5) +# define list_ptr(a) vlink((a)+6) +# define glue_order(a) subtype((a)+6) +# define glue_sign(a) type((a)+6) +# define glue_set(a) varmem[(a)+7].gr +# define synctex_tag_box(a) vinfo((a)+8) +# define synctex_line_box(a) vlink((a)+8) + + +/* unset nodes */ +# define glue_stretch(a) varmem[(a)+7].cint +# define glue_shrink shift_amount +# define span_count subtype + +# define rule_node_size 7 +# define rule_dir(a) vlink((a)+5) +# define synctex_tag_rule(a) vinfo((a)+6) +# define synctex_line_rule(a) vlink((a)+6) + +# define mark_node_size 3 +# define mark_ptr(a) vlink((a)+2) +# define mark_class(a) vinfo((a)+2) + +# define adjust_node_size 3 +# define adjust_pre subtype +# define adjust_ptr(a) vlink(a+2) + +# define glyph_node_size 6 + +# define character(a) vinfo((a)+2) +# define font(a) vlink((a)+2) +# define lang_data(a) vinfo((a)+3) +# define lig_ptr(a) vlink((a)+3) +# define x_displace(a) vinfo((a)+4) +# define y_displace(a) vlink((a)+4) +# define ex_glyph(a) vinfo((a)+5) /* expansion factor (hz) */ +# define is_char_node(a) (a!=null && type(a)==glyph_node) + +# define char_lang(a) ((const int)(signed short)(((signed int)((unsigned)lang_data(a)&0x7FFF0000)<<1)>>17)) +# define char_lhmin(a) ((const int)(((unsigned)lang_data(a) & 0x0000FF00)>>8)) +# define char_rhmin(a) ((const int)(((unsigned)lang_data(a) & 0x000000FF))) +# define char_uchyph(a) ((const int)(((unsigned)lang_data(a) & 0x80000000)>>31)) + +# define make_lang_data(a,b,c,d) (a>0 ? (1<<31): 0)+ \ + (b<<16)+ (((c>0 && c<256) ? c : 255)<<8)+(((d>0 && d<256) ? d : 255)) + +# define init_lang_data(a) lang_data(a)=256+1 + +# define set_char_lang(a,b) lang_data(a)=make_lang_data(char_uchyph(a),b,char_lhmin(a),char_rhmin(a)) +# define set_char_lhmin(a,b) lang_data(a)=make_lang_data(char_uchyph(a),char_lang(a),b,char_rhmin(a)) +# define set_char_rhmin(a,b) lang_data(a)=make_lang_data(char_uchyph(a),char_lang(a),char_lhmin(a),b) +# define set_char_uchyph(a,b) lang_data(a)=make_lang_data(b,char_lang(a),char_lhmin(a),char_rhmin(a)) + +# define margin_kern_node_size 4 +# define margin_char(a) vlink((a)+3) + +/*@# {|subtype| of marginal kerns}*/ +# define left_side 0 +# define right_side 1 + +# define math_node_size 4 +# define surround(a) vlink((a)+2) +# define before 0 /*|subtype| for math node that introduces a formula */ +# define after 1 /*|subtype| for math node that winds up a formula */ +# define synctex_tag_math(a) vinfo((a)+3) +# define synctex_line_math(a) vlink((a)+3) + +# define ins_node_size 6 +# define float_cost(a) varmem[(a)+2].cint +# define ins_ptr(a) vinfo((a)+5) +# define split_top_ptr(a) vlink((a)+5) + + +# define page_ins_node_size 5 + +/* height = 4 */ + +typedef enum { + hlist_node = 0, + vlist_node = 1, + rule_node, + ins_node, + mark_node, + adjust_node, + /* 6 used to be ligatures */ + disc_node = 7, + whatsit_node, + math_node, + glue_node, /* 10 */ + kern_node, + penalty_node, + unset_node, + style_node, + choice_node, /* 15 */ + simple_noad, + old_op_noad, + old_bin_noad, + old_rel_noad, + old_open_noad, /* 20 */ + old_close_noad, + old_punct_noad, + old_inner_noad, + radical_noad, + fraction_noad, /* 25 */ + old_under_noad, + old_over_noad, + accent_noad, + old_vcenter_noad, + fence_noad, /* 30 */ + math_char_node, /* kernel fields */ + sub_box_node, + sub_mlist_node, + math_text_char_node, + delim_node, /* shield fields */ + margin_kern_node, + glyph_node, + align_record_node, + pseudo_file_node, + pseudo_line_node, /* 40 */ + inserting_node, + split_up_node, + expr_node, + nesting_node, + span_node, /* 45 */ + attribute_node, + glue_spec_node, + attribute_list_node, + action_node, + temp_node, /* 50 */ + align_stack_node, + movement_node, + if_node, + unhyphenated_node, + hyphenated_node, /* 55 */ + delta_node, + passive_node, + shape_node, +} node_types; + +# define MAX_NODE_TYPE 58 + +# define last_known_node temp_node /* used by \lastnodetype */ + +# define movement_node_size 3 +# define expr_node_size 3 +# define if_node_size 2 +# define align_stack_node_size 6 +# define nesting_node_size 2 + +# define span_node_size 3 +# define span_span(a) vlink((a)+1) +# define span_link(a) vinfo((a)+1) + +# define pseudo_file_node_size 2 +# define pseudo_lines(a) vlink((a)+1) + +# define nodetype_has_attributes(t) (((t)<=glyph_node) && ((t)!=unset_node)) + +/* style and choice nodes */ +/* style nodes can be smaller, the information is encoded in |subtype|, + but choice nodes are on-the-spot converted to style nodes */ + +# define style_node_size 4 /* number of words in a style node */ + +# define display_mlist(a) vinfo((a)+2) /* mlist to be used in display style */ +# define text_mlist(a) vlink((a)+2) /* mlist to be used in text style */ +# define script_mlist(a) vinfo((a)+3) /* mlist to be used in script style */ +# define script_script_mlist(a) vlink((a)+3) /* mlist to be used in scriptscript style */ + +/* regular noads */ + +# define noad_size 4 /* number of words in a normal noad */ +# define new_hlist(a) vlink((a)+2) /* the translation of an mlist */ +# define nucleus(a) vinfo((a)+2) /* the |nucleus| field of a noad */ +# define supscr(a) vlink((a)+3) /* the |supscr| field of a noad */ +# define subscr(a) vinfo((a)+3) /* the |subscr| field of a noad */ + +/* accent noads */ +/* like a regular noad, but with two extra fields. */ + +# define accent_noad_size 5 /*number of |mem| words in an accent noad */ +# define accent_chr(a) vinfo((a)+4) /* the |accent_chr| field of an accent noad */ +# define bot_accent_chr(a) vlink((a)+4) /* the |bot_accent_chr| field of an accent noad */ + +/* left and right noads */ + +# define fence_noad_size 3 +# define delimiter(a) vlink((a)+2) /* |delimiter| field in left and right noads */ + +/* subtype of fence noads */ + +# define left_noad_side 1 +# define middle_noad_side 2 +# define right_noad_side 3 + +/* fraction noads */ + +# define fraction_noad_size 5 /*number of |mem| words in a fraction noad */ +# define thickness(a) vlink((a)+2) /* |thickness| field in a fraction noad */ +# define numerator(a) vlink((a)+3) /*|numerator| field in a fraction noad */ +# define denominator(a) vinfo((a)+3) /*|denominator| field in a fraction noad */ +# define left_delimiter(a) vlink((a)+4) /* first delimiter field of a noad */ +# define right_delimiter(a) vinfo((a)+4) /* second delimiter field of a fraction noad */ + +/* radical noads */ +/* this is like a fraction, but it only stores a |left_delimiter| */ +# define radical_noad_size 5 /*number of |mem| words in a radical noad */ + +# define degree(a) vinfo((a)+4) /* the root degree in a radical noad */ + +# define math_kernel_node_size 3 + +/* accessors for the |nucleus|-style node fields */ +# define math_fam(a) vinfo((a)+2) +# define math_character(a) vlink((a)+2) +# define math_list(a) vlink((a)+2) + +/* accessors for the |delimiter|-style two-word subnode fields */ + +# define math_shield_node_size 4 + /* not used yet */ + +# define small_fam(A) vinfo((A)+2) /* |fam| for ``small'' delimiter */ +# define small_char(A) vlink((A)+2) /* |character| for ``small'' delimiter */ +# define large_fam(A) vinfo((A)+3) /* |fam| for ``large'' delimiter */ +# define large_char(A) vlink((A)+3) /* |character| for ``large'' delimiter */ + +typedef enum { + open_node = 0, + write_node, + close_node, + special_node, + language_node, + set_language_code, + local_par_node, + dir_node, + pdf_literal_node, + pdf_obj_code, + pdf_refobj_node, /* 10 */ + pdf_xform_code, + pdf_refxform_node, + pdf_ximage_code, + pdf_refximage_node, + pdf_annot_node, + pdf_start_link_node, + pdf_end_link_node, + pdf_outline_code, + pdf_dest_node, + pdf_thread_node, /* 20 */ + pdf_start_thread_node, + pdf_end_thread_node, + pdf_save_pos_node, + pdf_thread_data_node, + pdf_link_data_node, + pdf_names_code, + pdf_font_attr_code, + pdf_include_chars_code, + pdf_map_file_code, + pdf_map_line_code, /* 30 */ + pdf_trailer_code, + pdf_font_expand_code, + set_random_seed_code, + pdf_glyph_to_unicode_code, + late_lua_node, /* 35 */ + close_lua_node, + save_cat_code_table_code, + init_cat_code_table_code, + pdf_colorstack_node, + pdf_setmatrix_node, /* 40 */ + pdf_save_node, + pdf_restore_node, + cancel_boundary_node, + user_defined_node /* 44 */ +} whatsit_types; + +# define MAX_WHATSIT_TYPE 44 + +# define get_node_size(i,j) (i!=whatsit_node ? node_data[i].size : whatsit_node_data[j].size) +# define get_node_name(i,j) (i!=whatsit_node ? node_data[i].name : whatsit_node_data[j].name) + + +# define pdf_info_code pdf_thread_data_node +# define pdf_catalog_code pdf_link_data_node + +# define GLYPH_CHARACTER (1 << 0) +# define GLYPH_LIGATURE (1 << 1) +# define GLYPH_GHOST (1 << 2) +# define GLYPH_LEFT (1 << 3) +# define GLYPH_RIGHT (1 << 4) + +# define is_character(p) ((subtype(p)) & GLYPH_CHARACTER) +# define is_ligature(p) ((subtype(p)) & GLYPH_LIGATURE ) +# define is_ghost(p) ((subtype(p)) & GLYPH_GHOST ) + +# define is_simple_character(p) (is_character(p) && !is_ligature(p) && !is_ghost(p)) + +# define is_leftboundary(p) (is_ligature(p) && ((subtype(p)) & GLYPH_LEFT )) +# define is_rightboundary(p) (is_ligature(p) && ((subtype(p)) & GLYPH_RIGHT )) +# define is_leftghost(p) (is_ghost(p) && ((subtype(p)) & GLYPH_LEFT )) +# define is_rightghost(p) (is_ghost(p) && ((subtype(p)) & GLYPH_RIGHT )) + +# define set_is_glyph(p) subtype(p) = (quarterword) (subtype(p) & ~GLYPH_CHARACTER) +# define set_is_character(p) subtype(p) = (quarterword) (subtype(p) | GLYPH_CHARACTER) +# define set_is_ligature(p) subtype(p) = (quarterword) (subtype(p) | GLYPH_LIGATURE) +# define set_is_ghost(p) subtype(p) = (quarterword) (subtype(p) |GLYPH_GHOST) + +# define set_to_glyph(p) subtype(p) = (quarterword)(subtype(p) & 0xFF00) +# define set_to_character(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_CHARACTER) +# define set_to_ligature(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_LIGATURE) +# define set_to_ghost(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_GHOST) + +# define set_is_leftboundary(p) { set_to_ligature(p); subtype(p) |= GLYPH_LEFT; } +# define set_is_rightboundary(p) { set_to_ligature(p); subtype(p) |= GLYPH_RIGHT; } +# define set_is_leftghost(p) { set_to_ghost(p); subtype(p) |= GLYPH_LEFT; } +# define set_is_rightghost(p) { set_to_ghost(p); subtype(p) |= GLYPH_RIGHT; } + + +# define special_node_size 3 + +# define dir_node_size 6 +# define dir_dir(a) vinfo((a)+2) +# define dir_level(a) vlink((a)+2) +# define dir_dvi_ptr(a) vinfo((a)+3) +# define dir_dvi_h(a) vlink((a)+3) /* obsolete */ +# define dir_refpos_h(a) vinfo((a)+4) +# define dir_refpos_v(a) vlink((a)+4) +# define dir_cur_h(a) vinfo((a)+5) +# define dir_cur_v(a) vlink((a)+5) + +# define write_node_size 3 +# define close_node_size 3 +# define write_tokens(a) vlink(a+2) +# define write_stream(a) vinfo(a+2) + +# define open_node_size 4 +# define open_name(a) vlink((a)+2) +# define open_area(a) vinfo((a)+3) +# define open_ext(a) vlink((a)+3) + +# define late_lua_node_size 4 +# define late_lua_data(a) vlink((a)+2) +# define late_lua_reg(a) vinfo((a)+2) +# define late_lua_name(a) vlink((a)+3) + +# define local_par_size 6 + +# define local_pen_inter(a) vinfo((a)+2) +# define local_pen_broken(a) vlink((a)+2) +# define local_box_left(a) vlink((a)+3) +# define local_box_left_width(a) vinfo((a)+3) +# define local_box_right(a) vlink((a)+4) +# define local_box_right_width(a) vinfo((a)+4) +# define local_par_dir(a) vinfo((a)+5) + + +/* type of literal data */ +# define lua_refid_literal 1 /* not a |normal| string */ + +/* literal ctm types */ + +typedef enum { + set_origin = 0, + direct_page, + direct_always, + scan_special, +} ctm_transform_modes; + + +# define pdf_refobj_node_size 3 + +# define pdf_obj_objnum(a) vinfo((a) + 2) + +# define pdf_refxform_node_size 6 +# define pdf_refximage_node_size 6 +# define pdf_annot_node_size 8 +# define pdf_dest_node_size 8 +# define pdf_thread_node_size 8 + +/* +when a whatsit node representing annotation is created, words |1..3| are +width, height and depth of this annotation; after shipping out words |1..4| +are rectangle specification of annotation. For whatsit node representing +destination |pdf_ann_left| and |pdf_ann_top| are used for some types of destinations +*/ + +/* coordinates of destinations/threads/annotations (in whatsit node) */ +# define pdf_ann_left(a) varmem[(a) + 2].cint +# define pdf_ann_top(a) varmem[(a) + 3].cint +# define pdf_ann_right(a) varmem[(a) + 4].cint +# define pdf_ann_bottom(a) varmem[(a) + 5].cint + +# define pdf_literal_data(a) vlink((a)+2) +# define pdf_literal_mode(a) type((a)+2) +# define pdf_literal_type(a) subtype((a)+2) + +# define pdf_ximage_index(a) vinfo((a) + 5) /* /Im* number and image index in array */ +# define pdf_ximage_transform(a) vlink((a) + 5) +# define pdf_xform_objnum(a) vinfo((a) + 5) +# define pdf_xform_transform(a) vlink((a) + 5) + +# define pdf_annot_data(a) vinfo((a) + 6) +# define pdf_link_attr(a) vinfo((a) + 6) +# define pdf_link_action(a) vlink((a) + 6) +# define pdf_annot_objnum(a) varmem[(a) + 7].cint +# define pdf_link_objnum(a) varmem[(a) + 7].cint + +# define pdf_dest_type(a) type((a) + 6) +# define pdf_dest_named_id(a) subtype((a) + 6) +# define pdf_dest_id(a) vlink((a) + 6) +# define pdf_dest_xyz_zoom(a) vinfo((a) + 7) +# define pdf_dest_objnum(a) vlink((a) + 7) + +# define pdf_thread_named_id(a) subtype((a) + 6) +# define pdf_thread_id(a) vlink((a) + 6) +# define pdf_thread_attr(a) vinfo((a) + 7) + +# define pdf_end_link_node_size 3 +# define pdf_end_thread_node_size 3 +# define pdf_save_pos_node_size 3 + +# define pdf_colorstack_node_size 4 +# define pdf_setmatrix_node_size 3 + +# define pdf_colorstack_stack(a) vlink((a)+2) +# define pdf_colorstack_cmd(a) vinfo((a)+2) +# define pdf_colorstack_data(a) vlink((a)+3) +# define pdf_setmatrix_data(a) vlink((a)+2) + +# define pdf_save_node_size 3 +# define pdf_restore_node_size 3 + +typedef enum { + colorstack_set = 0, + colorstack_push, + colorstack_pop, + colorstack_current +} colorstack_commands; + +# define colorstack_data colorstack_push /* last value where data field is set */ + +# define user_defined_node_size 4 +# define user_node_type(a) vinfo((a)+2) +# define user_node_id(a) vlink((a)+2) +# define user_node_value(a) vinfo((a)+3) + +# define cancel_boundary_size 3 + +# define active_node_size 4 /*number of words in extended active nodes */ +# define fitness subtype /*|very_loose_fit..tight_fit| on final line for this break */ +# define break_node(a) vlink((a)+1) /*pointer to the corresponding passive node */ +# define line_number(a) vinfo((a)+1) /*line that begins at this breakpoint */ +# define total_demerits(a) varmem[(a)+2].cint /* the quantity that \TeX\ minimizes */ +# define active_short(a) vinfo(a+3) /* |shortfall| of this line */ +# define active_glue(a) vlink(a+3) /*corresponding glue stretch or shrink */ + +# define passive_node_size 7 +# define cur_break(a) vlink((a)+1) /*in passive node, points to position of this breakpoint */ +# define prev_break(a) vinfo((a)+1) /*points to passive node that should precede this one */ +# define passive_pen_inter(a) vinfo((a)+2) +# define passive_pen_broken(a) vlink((a)+2) +# define passive_left_box(a) vlink((a)+3) +# define passive_left_box_width(a) vinfo((a)+3) +# define passive_last_left_box(a) vlink((a)+4) +# define passive_last_left_box_width(a) vinfo((a)+4) +# define passive_right_box(a) vlink((a)+5) +# define passive_right_box_width(a) vinfo((a)+5) +# define serial(a) vlink((a)+6) /* serial number for symbolic identification */ + +# define delta_node_size 10 /* 8 fields, stored in a+1..9 */ + +# define couple_nodes(a,b) {assert(b!=null);vlink(a)=b;alink(b)=a;} +# define try_couple_nodes(a,b) if (b==null) vlink(a)=b; else {couple_nodes(a,b);} +# define uncouple_node(a) {assert(a!=null);vlink(a)=null;alink(a)=null;} + +# define cache_disabled max_halfword + +extern void delete_attribute_ref(halfword b); +extern void build_attribute_list(halfword b); + +extern int unset_attribute(halfword n, int c, int w); +extern void set_attribute(halfword n, int c, int w); +extern int has_attribute(halfword n, int c, int w); + +extern halfword new_span_node(halfword n, int c, scaled w); + +extern void print_short_node_contents(halfword n); +extern void show_node_list(int i); +extern pointer actual_box_width(pointer r, scaled base_width); + +/* TH: these two defines still need checking. The node ordering in luatex is not + quite the same as in tex82 */ + +# define precedes_break(a) (type((a))<math_node && (type(a)!=whatsit_node || subtype(a)!=dir_node)) +# define non_discardable(a) (type((a))<math_node) + +/* from luanode.c */ + +typedef struct _node_info { + int id; + int size; + const char **fields; + const char *name; +} node_info; + +extern node_info node_data[]; +extern node_info whatsit_node_data[]; +extern halfword new_node(int i, int j); +extern void flush_node_list(halfword); +extern void flush_node(halfword); +extern halfword do_copy_node_list(halfword, halfword); +extern halfword copy_node_list(halfword); +extern halfword copy_node(const halfword); +extern void check_node(halfword); +extern void check_node_mem(void); +extern void fix_node_list(halfword); +extern int fix_node_lists; +extern char *sprint_node_mem_usage(void); +extern halfword raw_glyph_node(void); +extern halfword new_glyph_node(void); +extern int valid_node(halfword); + +typedef enum { + normal_g = 0, + sfi, + fil, + fill, + filll +} glue_orders; + +# define zero_glue 0 +# define sfi_glue zero_glue+glue_spec_size +# define fil_glue sfi_glue+glue_spec_size +# define fill_glue fil_glue+glue_spec_size +# define ss_glue fill_glue+glue_spec_size +# define fil_neg_glue ss_glue+glue_spec_size +# define page_ins_head fil_neg_glue+glue_spec_size +# define contrib_head page_ins_head+temp_node_size +# define page_head contrib_head+temp_node_size +# define temp_head page_head+temp_node_size +# define hold_head temp_head+temp_node_size +# define adjust_head hold_head+temp_node_size +# define pre_adjust_head adjust_head+temp_node_size +# define active pre_adjust_head+temp_node_size +# define align_head active+active_node_size +# define end_span align_head+temp_node_size +# define begin_point end_span+span_node_size +# define end_point begin_point+glyph_node_size +# define var_mem_stat_max (end_point+glyph_node_size-1) + +# define stretching 1 +# define shrinking 2 + +# define is_running(A) ((A)==null_flag) /* tests for a running dimension */ + +extern halfword tail_of_list(halfword p); +extern void delete_glue_ref(halfword p); + +extern int var_used; +extern halfword temp_ptr; + +# define cache_disabled max_halfword + +extern int max_used_attr; +extern halfword attr_list_cache; + +extern halfword new_null_box(void); +extern halfword new_rule(void); +extern halfword new_glyph(int f, int c); +extern quarterword norm_min(int h); +extern halfword new_char(int f, int c); +extern scaled glyph_width(halfword p); +extern scaled glyph_height(halfword p); +extern scaled glyph_depth(halfword p); +extern halfword new_disc(void); +extern halfword new_math(scaled w, int s); +extern halfword new_spec(halfword p); +extern halfword new_param_glue(int n); +extern halfword new_glue(halfword q); +extern halfword new_skip_param(int n); +extern halfword new_kern(scaled w); +extern halfword new_penalty(int m); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.c b/Build/source/texk/web2c/luatexdir/tex/texnodes.w index ca6af731ef1..45241b0ac29 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texnodes.c +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.w @@ -1,68 +1,49 @@ -/* texnodes.c - - Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> +% texnodes.w - This file is part of LuaTeX. +% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. +% This file is part of LuaTeX. - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. -#include "luatex-api.h" -#include <ptexlib.h> -#include "nodes.h" -#include "commands.h" - -#include "tokens.h" -#undef name - -#define noDEBUG +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. +@ @c static const char _svn_version[] = - "$Id: texnodes.c 2448 2009-06-08 07:43:50Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/texnodes.c $"; - -#define append_char(A) str_pool[pool_ptr++]=(A) -#define cur_length (pool_ptr - str_start_macro(str_ptr)) -#define flush_char() pool_ptr-- + "$Id: texnodes.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/texnodes.w $"; -#define stretching 1 -#define shrinking 2 +#include "ptexlib.h" +#include "lua/luatex-api.h" -#define adjust_pre subtype +@ @c +#undef name -typedef enum { - pdf_dest_xyz = 0, - pdf_dest_fit, - pdf_dest_fith, - pdf_dest_fitv, - pdf_dest_fitb, - pdf_dest_fitbh, - pdf_dest_fitbv, - pdf_dest_fitr, -} pdf_destination_types; +#define noDEBUG +#define DEBUG_OUT stdout +#define adjust_pre subtype +#define attribute(A) eqtb[attribute_base+(A)].cint -#define obj_obj_data(A) pdf_mem[obj_data_ptr((A)) + 0] -#define obj_obj_is_stream(A) pdf_mem[obj_data_ptr((A)) + 1] -#define obj_obj_stream_attr(A) pdf_mem[obj_data_ptr((A)) + 2] -#define obj_obj_is_file(A) pdf_mem[obj_data_ptr((A)) + 3] -#define obj_xform_width(A) pdf_mem[obj_data_ptr((A)) + 0] -#define obj_xform_height(A) pdf_mem[obj_data_ptr((A)) + 1] -#define obj_xform_depth(A) pdf_mem[obj_data_ptr((A)) + 2] +#define uc_hyph int_par(uc_hyph_code) +#define cur_lang int_par(cur_lang_code) +#define left_hyphen_min int_par(left_hyphen_min_code) +#define right_hyphen_min int_par(right_hyphen_min_code) +#define text_direction int_par(text_direction_code) #define MAX_CHAIN_SIZE 13 -volatile memory_word *varmem = NULL; +memory_word *volatile varmem = NULL; #ifndef NDEBUG char *varmem_sizes = NULL; @@ -73,31 +54,28 @@ halfword rover = 0; halfword free_chain[MAX_CHAIN_SIZE] = { null }; -static integer my_prealloc = 0; +static int my_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 */ +halfword slow_get_node(int 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 - -const char *node_fields_list[] = { "attr", "width", "depth", "height", "dir", "shift", +const char *node_fields_list[] = + { "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign", "glue_set", "list", NULL }; -const char *node_fields_rule[] = { "attr", "width", "depth", "height", "dir", NULL }; +const char *node_fields_rule[] = + { "attr", "width", "depth", "height", "dir", NULL }; const char *node_fields_insert[] = { "attr", "cost", "depth", "height", "spec", "list", NULL }; const char *node_fields_mark[] = { "attr", "class", "mark", NULL }; @@ -105,16 +83,18 @@ const char *node_fields_adjust[] = { "attr", "list", NULL }; const char *node_fields_disc[] = { "attr", "pre", "post", "replace", NULL }; const char *node_fields_math[] = { "attr", "surround", NULL }; const char *node_fields_glue[] = { "attr", "spec", "leader", NULL }; -const char *node_fields_kern[] = { "attr", "kern", NULL }; +const char *node_fields_kern[] = { "attr", "kern", "expansion_factor", NULL }; const char *node_fields_penalty[] = { "attr", "penalty", NULL }; + const char *node_fields_unset[] = { "attr", "width", "depth", "height", "dir", "shrink", "glue_order", "glue_sign", "stretch", "span", "list", NULL }; const char *node_fields_margin_kern[] = { "attr", "width", "glyph", NULL }; + const char *node_fields_glyph[] = { "attr", "char", "font", "lang", "left", "right", "uchyph", - "components", "xoffset", "yoffset", NULL + "components", "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL }; const char *node_fields_style[] = { "attr", "style", NULL }; const char *node_fields_choice[] = @@ -155,8 +135,9 @@ const char *node_fields_action[] = { "action_type", "named_id", "action_id", "file", "new_window", "data", "ref_count", NULL }; const char *node_fields_attribute[] = { "number", "value", NULL }; + const char *node_fields_glue_spec[] = { "width", "stretch", "shrink", - "stretch_order", "shrink_order", "ref_count", NULL + "stretch_order", "shrink_order", "ref_count", "writable", NULL }; const char *node_fields_attribute_list[] = { NULL }; @@ -173,32 +154,39 @@ const char *node_fields_whatsit_local_par[] = const char *node_fields_whatsit_dir[] = { "attr", "dir", "level", "dvi_ptr", "dvi_h", NULL }; -const char *node_fields_whatsit_pdf_literal[] = { "attr", "mode", "data", NULL }; +const char *node_fields_whatsit_pdf_literal[] = + { "attr", "mode", "data", NULL }; const char *node_fields_whatsit_pdf_refobj[] = { "attr", "objnum", NULL }; const char *node_fields_whatsit_pdf_refxform[] = - { "attr", "width", "height", "depth", "objnum", NULL }; + { "attr", "width", "depth", "height", "objnum", NULL }; const char *node_fields_whatsit_pdf_refximage[] = - { "attr", "width", "height", "depth", "objnum", NULL }; + { "attr", "width", "depth", "height", "transform", "index", NULL }; const char *node_fields_whatsit_pdf_annot[] = - { "attr", "width", "height", "depth", "objnum", "data", NULL }; + { "attr", "width", "depth", "height", "objnum", "data", NULL }; const char *node_fields_whatsit_pdf_start_link[] = - { "attr", "width", "height", "depth", + { "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL }; const char *node_fields_whatsit_pdf_end_link[] = { "attr", NULL }; -const char *node_fields_whatsit_pdf_dest[] = { "attr", "width", "height", "depth", + +const char *node_fields_whatsit_pdf_dest[] = + { "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type", "xyz_zoom", "objnum", NULL }; -const char *node_fields_whatsit_pdf_thread[] = { "attr", "width", "height", "depth", + +const char *node_fields_whatsit_pdf_thread[] = + { "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL }; + const char *node_fields_whatsit_pdf_start_thread[] = - { "attr", "width", "height", "depth", + { "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL }; const char *node_fields_whatsit_pdf_end_thread[] = { "attr", NULL }; const char *node_fields_whatsit_pdf_save_pos[] = { "attr", NULL }; -const char *node_fields_whatsit_late_lua[] = { "attr", "reg", "data", "name", NULL }; +const char *node_fields_whatsit_late_lua[] = + { "attr", "reg", "data", "name", NULL }; const char *node_fields_whatsit_close_lua[] = { "attr", "reg", NULL }; const char *node_fields_whatsit_pdf_colorstack[] = { "attr", "stack", "cmd", "data", NULL }; @@ -349,18 +337,19 @@ node_info whatsit_node_data[] = { #define last_whatsit_node user_defined_node +@ @c halfword new_node(int i, int j) { - register int s; - register halfword n; + int s; + 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 + /* 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))); + (sizeof(memory_word) * ((unsigned) s - 1))); switch (i) { case glyph_node: init_lang_data(n); @@ -397,14 +386,14 @@ halfword new_node(int i, int j) 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(). + /* 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))); + (sizeof(memory_word) * ((unsigned) j - 1))); break; default: break; @@ -441,10 +430,10 @@ halfword new_node(int i, int j) if (nodetype_has_attributes(i)) { build_attribute_list(n); } - type(n) = i; - subtype(n) = j; + type(n) = (quarterword) i; + subtype(n) = (quarterword) j; #ifdef DEBUG - fprintf(stderr, "Alloc-ing %s node %d\n", + fprintf(DEBUG_OUT, "Alloc-ing %s node %d\n", get_node_name(type(n), subtype(n)), (int) n); #endif return n; @@ -474,15 +463,15 @@ halfword new_glyph_node(void) } -/* 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) +@ makes a duplicate of the node list that starts at |p| and returns a + pointer to the new list +@c +halfword do_copy_node_list(halfword p, halfword end) { halfword q = null; /* previous position in new list */ halfword h = null; /* head of the list */ copy_error_seen = 0; - while (p != null) { + while (p != end) { register halfword s = copy_node(p); if (h == null) { h = s; @@ -495,7 +484,13 @@ halfword copy_node_list(halfword p) return h; } - /* make a dupe of a single node */ +halfword copy_node_list(halfword p) +{ + return do_copy_node_list(p, null); +} + +@ make a dupe of a single node +@c halfword copy_node(const halfword p) { halfword r; /* current node being fabricated for new list */ @@ -509,7 +504,7 @@ halfword copy_node(const halfword p) r = get_node(i); (void) memcpy((void *) (varmem + r), (void *) (varmem + p), - (sizeof(memory_word) * i)); + (sizeof(memory_word) * (unsigned) i)); /* handle synctex extension */ switch (type(p)) { @@ -627,13 +622,6 @@ halfword copy_node(const halfword p) s = copy_node(delimiter(p)); delimiter(r) = s; break; - /* - case style_node: - case delim_node: - case math_char_node: - case math_text_char_node: - break; - */ case sub_box_node: case sub_mlist_node: s = copy_node_list(math_list(p)); @@ -712,18 +700,27 @@ halfword copy_node(const halfword p) break; } break; +#if 0 + case style_node: + case delim_node: + case math_char_node: + case math_text_char_node: + break; +#else default: +#endif break; } break; } #ifdef DEBUG - fprintf(stderr, "Alloc-ing %s node %d (copy of %d)\n", + fprintf(DEBUG_OUT, "Alloc-ing %s node %d (copy of %d)\n", get_node_name(type(r), subtype(r)), (int) r, (int) p); #endif return r; } +@ @c int valid_node(halfword p) { if (p > my_prealloc) { @@ -734,11 +731,12 @@ int valid_node(halfword p) return 1; } } else { - return 1; + return 0; } return 0; } +@ @c static void do_free_error(halfword p) { halfword r; @@ -830,7 +828,7 @@ static void do_free_error(halfword p) #endif } -static int free_error(halfword p) +int free_error(halfword p) { assert(p > my_prealloc); assert(p < var_mem_max); @@ -844,6 +842,7 @@ static int free_error(halfword p) } +@ @c static void do_copy_error(halfword p) { char errstr[255] = { 0 }; @@ -885,7 +884,7 @@ int copy_error(halfword p) } - +@ @c void flush_node(halfword p) { @@ -893,8 +892,8 @@ void flush_node(halfword p) return; #ifdef DEBUG - fprintf(stderr, "Free-ing %s node %d\n", get_node_name(type(p), subtype(p)), - (int) p); + fprintf(DEBUG_OUT, "Free-ing %s node %d\n", + get_node_name(type(p), subtype(p)), (int) p); #endif if (free_error(p)) return; @@ -1021,7 +1020,7 @@ void flush_node(halfword p) break; default: - tconfusion("ext3"); + confusion("ext3"); return; } @@ -1116,6 +1115,7 @@ void flush_node(halfword p) return; } +@ @c void flush_node_list(halfword pp) { /* erase list of nodes starting at |p| */ register halfword p = pp; @@ -1132,20 +1132,21 @@ void flush_node_list(halfword pp) } } +@ @c static int test_count = 1; #define dorangetest(a,b,c) do { \ if (!(b>=0 && b<c)) { \ fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \ (int)a, (int)b, (int)c, __LINE__,test_count); \ - tconfusion("dorangetest"); \ + confusion("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"); \ + confusion("dotest"); \ } } while (0) #define check_action_ref(a) { dorangetest(p,a,var_mem_max); } @@ -1229,7 +1230,7 @@ void check_node(halfword p) case 'd': break; default: - tconfusion("extuser"); + confusion("extuser"); break; } break; @@ -1250,7 +1251,7 @@ void check_node(halfword p) case local_par_node: break; default: - tconfusion("ext3"); + confusion("ext3"); } break; case margin_kern_node: @@ -1330,7 +1331,8 @@ void check_node(halfword p) } } -static void check_static_node_mem(void) +@ @c +void check_static_node_mem(void) { dotest(zero_glue, width(zero_glue), 0); dotest(zero_glue, type(zero_glue), glue_spec_node); @@ -1381,6 +1383,7 @@ static void check_static_node_mem(void) dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal); } +@ @c void check_node_mem(void) { int i; @@ -1395,6 +1398,7 @@ void check_node_mem(void) test_count++; } +@ @c void fix_node_list(halfword head) { halfword p, q; @@ -1409,18 +1413,20 @@ void fix_node_list(halfword head) } } -halfword get_node(integer s) +@ @c +halfword get_node(int s) { register halfword r; - - /*check_static_node_mem(); */ +#if 0 + check_static_node_mem(); +#endif assert(s < MAX_CHAIN_SIZE); r = free_chain[s]; if (r != null) { free_chain[s] = vlink(r); #ifndef NDEBUG - varmem_sizes[r] = s; + varmem_sizes[r] = (char) s; #endif vlink(r) = null; var_used += s; /* maintain usage statistics */ @@ -1430,8 +1436,8 @@ halfword get_node(integer s) return slow_get_node(s); } -#if 0 /* unused */ -static void print_free_chain(int c) +@ @c +void print_free_chain(int c) { halfword p = free_chain[c]; fprintf(stdout, "\nfree chain[%d] =\n ", c); @@ -1441,9 +1447,9 @@ static void print_free_chain(int c) } fprintf(stdout, "null;\n"); } -#endif -void free_node(halfword p, integer s) +@ @c +void free_node(halfword p, int s) { if (p <= my_prealloc) { @@ -1469,7 +1475,8 @@ void free_node(halfword p, integer s) var_used -= s; /* maintain statistics */ } -static void free_node_chain(halfword q, integer s) +@ @c +void free_node_chain(halfword q, int s) { register halfword p = q; while (vlink(p) != null) { @@ -1488,24 +1495,27 @@ static void free_node_chain(halfword q, integer s) } -void init_node_mem(halfword t) +@ @c +void init_node_mem(int t) { my_prealloc = var_mem_stat_max; 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); + varmem = + (memory_word *) realloc((void *) varmem, + sizeof(memory_word) * (unsigned) t); if (varmem == NULL) { - overflow_string("node memory size", var_mem_max); + overflow("node memory size", (unsigned) var_mem_max); } - memset((void *) (varmem), 0, t * sizeof(memory_word)); + memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word)); #ifndef NDEBUG - varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * t); + varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t); if (varmem_sizes == NULL) { - overflow_string("node memory size", var_mem_max); + overflow("node memory size", (unsigned) var_mem_max); } - memset((void *) varmem_sizes, 0, sizeof(char) * t); + memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t); #endif var_mem_max = t; rover = var_mem_stat_max + 1; @@ -1616,6 +1626,7 @@ void init_node_mem(halfword t) vlink(end_point + 4) = 0; } +@ @c void dump_node_mem(void) { dump_int(var_mem_max); @@ -1629,20 +1640,22 @@ void dump_node_mem(void) dump_int(my_prealloc); } -/* it makes sense to enlarge the varmem array immediately */ - +@ it makes sense to enlarge the varmem array immediately +@c void undump_node_mem(void) { - integer x; + 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)); */ + varmem = xmallocarray(memory_word, (unsigned) var_mem_max); +#if 0 + memset ((void *)varmem,0,x*sizeof(memory_word)); +#endif undump_things(varmem[0], x); #ifndef NDEBUG - varmem_sizes = xmallocarray(char, var_mem_max); - memset((void *) varmem_sizes, 0, var_mem_max * sizeof(char)); + varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); + memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char)); undump_things(varmem_sizes[0], x); #endif undump_things(free_chain[0], MAX_CHAIN_SIZE); @@ -1677,7 +1690,8 @@ void test_rovers(char *s) # define test_rovers(a) #endif -halfword slow_get_node(integer s) +@ @c +halfword slow_get_node(int s) { register int t; @@ -1705,7 +1719,7 @@ halfword slow_get_node(integer s) test_rovers("taken"); assert(vlink(rover) < var_mem_max); #ifndef NDEBUG - varmem_sizes[r] = (s > 127 ? 127 : s); + varmem_sizes[r] = (char) (s > 127 ? 127 : s); #endif vlink(r) = null; var_used += s; /* maintain usage statistics */ @@ -1739,19 +1753,23 @@ halfword slow_get_node(integer s) x = (var_mem_max >> 2) + s; varmem = (memory_word *) realloc((void *) varmem, - sizeof(memory_word) * (var_mem_max + x)); + sizeof(memory_word) * + (unsigned) (var_mem_max + x)); if (varmem == NULL) { - overflow_string("node memory size", var_mem_max); + overflow("node memory size", (unsigned) var_mem_max); } - memset((void *) (varmem + var_mem_max), 0, x * sizeof(memory_word)); + memset((void *) (varmem + var_mem_max), 0, + (unsigned) x * sizeof(memory_word)); #ifndef NDEBUG varmem_sizes = - (char *) realloc(varmem_sizes, sizeof(char) * (var_mem_max + x)); + (char *) realloc(varmem_sizes, + sizeof(char) * (unsigned) (var_mem_max + x)); if (varmem_sizes == NULL) { - overflow_string("node memory size", var_mem_max); + overflow("node memory size", (unsigned) var_mem_max); } - memset((void *) (varmem_sizes + var_mem_max), 0, x * sizeof(char)); + memset((void *) (varmem_sizes + var_mem_max), 0, + (unsigned) (x) * sizeof(char)); #endif /* todo ? it is perhaps possible to merge the new memory with an existing rover */ @@ -1768,6 +1786,7 @@ halfword slow_get_node(integer s) } } +@ @c char *sprint_node_mem_usage(void) { int i, b; @@ -1797,7 +1816,7 @@ char *sprint_node_mem_usage(void) snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], get_node_name((i > last_normal_node ? whatsit_node : i), j)); - ss = xmalloc(strlen(s) + strlen(msg) + 1); + ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1)); strcpy(ss, s); strcat(ss, msg); free(s); @@ -1811,13 +1830,14 @@ char *sprint_node_mem_usage(void) return s; } +@ @c halfword list_node_mem_usage(void) { halfword i, j; halfword p = null, q = null; #ifndef NDEBUG - char *saved_varmem_sizes = xmallocarray(char, var_mem_max); - memcpy(saved_varmem_sizes, varmem_sizes, var_mem_max); + char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); + memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max); for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { if (saved_varmem_sizes[i] > 0) { j = copy_node(i); @@ -1834,34 +1854,34 @@ halfword list_node_mem_usage(void) return q; } +@ @c void print_node_mem_stats(void) { 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 + my_prealloc), (int) var_mem_max); + int free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; + snprintf(msg, 255, " %d words of node memory still in use:", + (int) (var_used + my_prealloc)); tprint_nl(msg); - tprint_nl("rapidly available: "); + s = sprint_node_mem_usage(); + tprint_nl(" "); + tprint(s); + free(s); + tprint(" nodes"); + tprint_nl(" avail lists: "); b = 0; for (i = 1; i < MAX_CHAIN_SIZE; i++) { for (j = free_chain[i]; j != null; j = vlink(j)) free_chain_counts[i]++; if (free_chain_counts[i] > 0) { - snprintf(msg, 255, "%s%d:%d", (b ? ", " : ""), i, + 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 */ } @@ -1876,57 +1896,19 @@ halfword new_span_node(halfword n, int s, scaled 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 */ +@* Attribute stuff. +@c static halfword new_attribute_node(unsigned int i, int v) { register halfword r = get_node(attribute_node_size); type(r) = attribute_node; - attribute_id(r) = i; + attribute_id(r) = (halfword) i; attribute_value(r) = v; return r; } +@ @c halfword copy_attribute_list(halfword n) { halfword q = get_node(attribute_node_size); @@ -1946,6 +1928,7 @@ halfword copy_attribute_list(halfword n) return q; } +@ @c void update_attribute_cache(void) { halfword p; @@ -1955,9 +1938,9 @@ void update_attribute_cache(void) 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); + register int v = attribute(i); if (v > UNUSED_ATTRIBUTE) { - register halfword r = new_attribute_node(i, v); + register halfword r = new_attribute_node((unsigned) i, v); vlink(p) = r; p = r; } @@ -1969,6 +1952,7 @@ void update_attribute_cache(void) return; } +@ @c void build_attribute_list(halfword b) { if (max_used_attr >= 0) { @@ -1980,19 +1964,20 @@ void build_attribute_list(halfword b) attr_list_ref(attr_list_cache)++; node_attr(b) = attr_list_cache; #ifdef DEBUG - fprintf(stderr, "Added attrlist (%d) to node %d (count=%d)\n", + fprintf(DEBUG_OUT, "Added attrlist (%d) to node %d (count=%d)\n", node_attr(b), b, attr_list_ref(attr_list_cache)); #endif } } +@ @c void delete_attribute_ref(halfword b) { if (b != null) { assert(type(b) == attribute_list_node); attr_list_ref(b)--; #ifdef DEBUG - fprintf(stderr, "Removed attrlistref (%d) (count=%d)\n", b, + fprintf(DEBUG_OUT, "Removed attrlistref (%d) (count=%d)\n", b, attr_list_ref(b)); #endif if (attr_list_ref(b) == 0) { @@ -2006,17 +1991,17 @@ void delete_attribute_ref(halfword b) } } -/* |p| is an attr list head, or zero */ - +@ |p| is an attr list head, or zero +@c halfword do_set_attribute(halfword p, int i, int val) { register halfword q; register int j = 0; - if (p == null) { /* add a new head & node */ + 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); + p = new_attribute_node((unsigned) i, val); vlink(q) = p; return q; } @@ -2038,13 +2023,14 @@ halfword do_set_attribute(halfword p, int i, int val) if (attribute_id(vlink(p)) == i) { attribute_value(vlink(p)) = val; } else { /* add a new node */ - halfword r = new_attribute_node(i, val); + halfword r = new_attribute_node((unsigned) i, val); vlink(r) = vlink(p); vlink(p) = r; } return q; } +@ @c void set_attribute(halfword n, int i, int val) { register halfword p; @@ -2052,12 +2038,12 @@ void set_attribute(halfword n, int i, int val) if (!nodetype_has_attributes(type(n))) return; p = node_attr(n); - if (p == null) { /* add a new head & node */ + 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); + p = new_attribute_node((unsigned) i, val); vlink(node_attr(n)) = p; return; } @@ -2090,7 +2076,7 @@ void set_attribute(halfword n, int i, int val) if (attribute_id(vlink(p)) == i) { attribute_value(vlink(p)) = val; } else { /* add a new node */ - halfword r = new_attribute_node(i, val); + halfword r = new_attribute_node((unsigned) i, val); vlink(r) = vlink(p); vlink(p) = r; } @@ -2098,6 +2084,7 @@ void set_attribute(halfword n, int i, int val) } +@ @c int unset_attribute(halfword n, int i, int val) { register halfword p; @@ -2109,6 +2096,12 @@ int unset_attribute(halfword n, int i, int val) p = node_attr(n); if (p == null) return UNUSED_ATTRIBUTE; + if (attr_list_ref(p) == 0) { + fprintf(stdout, + "Node %d has an attribute list that is free already\n", + (int) n); + return UNUSED_ATTRIBUTE; + } assert(vlink(p) != null); while (vlink(p) != null) { t = attribute_id(vlink(p)); @@ -2125,19 +2118,15 @@ int unset_attribute(halfword n, int i, int val) return UNUSED_ATTRIBUTE; /* 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 == attr_list_cache) { + halfword q = copy_attribute_list(p); 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; + attr_list_ref(q) = 1; + node_attr(n) = q; } - p = vlink(p); + p = vlink(node_attr(n)); while (j-- > 0) p = vlink(p); t = attribute_value(p); @@ -2147,6 +2136,7 @@ int unset_attribute(halfword n, int i, int val) return t; } +@ @c int has_attribute(halfword n, int i, int val) { register halfword p; @@ -2170,6 +2160,7 @@ int has_attribute(halfword n, int i, int val) return UNUSED_ATTRIBUTE; } +@ @c void print_short_node_contents(halfword p) { switch (type(p)) { @@ -2204,25 +2195,25 @@ void print_short_node_contents(halfword p) } -static void show_pdftex_whatsit_rule_spec(integer p) +@ @c +void show_pdftex_whatsit_rule_spec(int p) { tprint("("); - print_rule_dimen(pdf_height(p)); + print_rule_dimen(height(p)); print_char('+'); - print_rule_dimen(pdf_depth(p)); + print_rule_dimen(depth(p)); tprint(")x"); - print_rule_dimen(pdf_width(p)); + print_rule_dimen(width(p)); } -/* -Each new type of node that appears in our data structure must be capable + +@ Each new type of node that appears in our data structure must be capable of being displayed, copied, destroyed, and so on. The routines that we need for write-oriented whatsits are somewhat like those for mark nodes; other extensions might, of course, involve more subtlety here. -*/ - +@c static void print_write_whatsit(const char *s, pointer p) { tprint_esc(s); @@ -2235,7 +2226,8 @@ static void print_write_whatsit(const char *s, pointer p) } -static void show_whatsit_node(integer p) +@ @c +void show_whatsit_node(int p) { switch (subtype(p)) { case open_node: @@ -2264,6 +2256,7 @@ static void show_whatsit_node(integer p) print_char(' '); print_dir(dir_dir(p)); } + break; case local_par_node: tprint_esc("whatsit"); append_char('.'); @@ -2285,7 +2278,7 @@ static void show_whatsit_node(integer p) } else { append_char('.'); show_node_list(local_box_left(p)); - decr(pool_ptr); + decr(cur_length); } print_ln(); print_current_string(); @@ -2295,9 +2288,9 @@ static void show_whatsit_node(integer p) } else { append_char('.'); show_node_list(local_box_right(p)); - decr(pool_ptr); + decr(cur_length); } - decr(pool_ptr); + decr(cur_length); break; case pdf_literal_node: show_pdf_literal(p); @@ -2319,7 +2312,7 @@ static void show_whatsit_node(integer p) tprint(" current"); break; default: - tconfusion("pdfcolorstack"); + confusion("pdfcolorstack"); break; } if (pdf_colorstack_cmd(p) <= colorstack_data) @@ -2349,34 +2342,39 @@ static void show_whatsit_node(integer p) break; case pdf_refobj_node: tprint_esc("pdfrefobj"); - if (obj_obj_is_stream(pdf_obj_objnum(p)) > 0) { - if (obj_obj_stream_attr(pdf_obj_objnum(p)) != null) { + if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) { + if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { tprint(" attr"); - print_mark(obj_obj_stream_attr(pdf_obj_objnum(p))); + lua_rawgeti(Luas, LUA_REGISTRYINDEX, + obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p))); + print_char(' '); + tprint((const char *) lua_tostring(Luas, -1)); + lua_pop(Luas, 1); } tprint(" stream"); } - if (obj_obj_is_file(pdf_obj_objnum(p)) > 0) + if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p))) tprint(" file"); - print_mark(obj_obj_data(pdf_obj_objnum(p))); + if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { + lua_rawgeti(Luas, LUA_REGISTRYINDEX, + obj_obj_data(static_pdf, pdf_obj_objnum(p))); + print_char(' '); + tprint((const char *) lua_tostring(Luas, -1)); + lua_pop(Luas, 1); + } break; case pdf_refxform_node: - tprint_esc("pdfrefxform"); - print_char('('); - print_scaled(obj_xform_height(pdf_xform_objnum(p))); - print_char('+'); - print_scaled(obj_xform_depth(pdf_xform_objnum(p))); - tprint(")x"); - print_scaled(obj_xform_width(pdf_xform_objnum(p))); - break; case pdf_refximage_node: - tprint_esc("pdfrefximage"); + if (subtype(p) == pdf_refxform_node) + tprint_esc("pdfrefxform"); + else + tprint_esc("pdfrefximage"); tprint("("); - print_scaled(pdf_height(p)); + print_scaled(height(p)); print_char('+'); - print_scaled(pdf_depth(p)); + print_scaled(depth(p)); tprint(")x"); - print_scaled(pdf_width(p)); + print_scaled(width(p)); break; case pdf_annot_node: tprint_esc("pdfannot"); @@ -2425,8 +2423,7 @@ static void show_whatsit_node(integer p) } break; default: - pdf_error(maketexstring("displaying"), - maketexstring("unknown action type")); + pdf_error("displaying", "unknown action type"); break; } break; @@ -2485,11 +2482,11 @@ static void show_whatsit_node(integer p) else tprint_esc("pdfstartthread"); tprint("("); - print_rule_dimen(pdf_height(p)); + print_rule_dimen(height(p)); print_char('+'); - print_rule_dimen(pdf_depth(p)); + print_rule_dimen(depth(p)); tprint(")x"); - print_rule_dimen(pdf_width(p)); + print_rule_dimen(width(p)); if (pdf_thread_attr(p) != null) { tprint(" attr"); print_mark(pdf_thread_attr(p)); @@ -2541,8 +2538,8 @@ static void show_whatsit_node(integer p) } -/* - Now we are ready for |show_node_list| itself. This procedure has been + +@ Now we are ready for |show_node_list| itself. This procedure has been written to be ``extra robust'' in the sense that it should not crash or get into a loop even if the data structures have been messed up by bugs in the rest of the program. You can safely call its parent routine @@ -2552,23 +2549,23 @@ static void show_whatsit_node(integer p) for example, it might try to read |mem[p].hh| when |mem[p]| contains a scaled integer, if |p| is a pointer that has been clobbered or chosen at random. -*/ -/* |str_room| need not be checked; see |show_box| */ -/* Recursive calls on |show_node_list| therefore use the following pattern: */ +@ |str_room| need not be checked; see |show_box| +@ Recursive calls on |show_node_list| therefore use the following pattern: +@c #define node_list_display(A) do { \ append_char('.'); \ show_node_list(A); \ flush_char(); \ } while (0) -void show_node_list(integer p) +void show_node_list(int p) { /* prints a node list symbolically */ - integer n; /* the number of items already printed at this level */ + int n; /* the number of items already printed at this level */ real g; /* a glue ratio, as a floating point number */ - if (cur_length > depth_threshold) { + if ((int) cur_length > depth_threshold) { if (p > null) tprint(" []"); /* indicate that there's been some truncation */ return; @@ -2577,18 +2574,18 @@ void show_node_list(integer p) while (p != null) { print_ln(); print_current_string(); /* display the nesting history */ - if (int_par(param_tracing_online_code) < -2) + if (int_par(tracing_online_code) < -2) print_int(p); incr(n); if (n > breadth_max) { /* time to stop */ tprint("etc."); return; } - /* @<Display node |p|@> */ + /* Display node |p| */ if (is_char_node(p)) { print_font_and_char(p); if (is_ligature(p)) { - /* @<Display ligature |p|@>; */ + /* Display ligature |p|; */ tprint(" (ligature "); if (is_leftboundary(p)) print_char('|'); @@ -2603,7 +2600,7 @@ void show_node_list(integer p) case hlist_node: case vlist_node: case unset_node: - /* @<Display box |p|@>; */ + /* Display box |p|; */ if (type(p) == hlist_node) tprint_esc("h"); else if (type(p) == vlist_node) @@ -2617,7 +2614,7 @@ void show_node_list(integer p) tprint(")x"); print_scaled(width(p)); if (type(p) == unset_node) { - /* @<Display special fields of the unset node |p|@>; */ + /* Display special fields of the unset node |p|; */ if (span_count(p) != min_quarterword) { tprint(" ("); print_int(span_count(p) + 1); @@ -2625,14 +2622,14 @@ void show_node_list(integer p) } if (glue_stretch(p) != 0) { tprint(", stretch "); - print_glue(glue_stretch(p), glue_order(p), 0); + print_glue(glue_stretch(p), glue_order(p), NULL); } if (glue_shrink(p) != 0) { tprint(", shrink "); - print_glue(glue_shrink(p), glue_sign(p), 0); + print_glue(glue_shrink(p), glue_sign(p), NULL); } } else { - /*<Display the value of |glue_set(p)|@> */ + /* Display the value of |glue_set(p)| */ /* The code will have to change in this place if |glue_ratio| is a structured type instead of an ordinary |real|. Note that this routine should avoid arithmetic errors even if the |glue_set| field holds an @@ -2652,9 +2649,9 @@ void show_node_list(integer p) print_char('>'); else tprint("< -"); - print_glue(20000 * unity, glue_order(p), 0); + print_glue(20000 * unity, glue_order(p), NULL); } else { - print_glue(round(unity * g), glue_order(p), 0); + print_glue(round(unity * g), glue_order(p), NULL); } } @@ -2668,7 +2665,7 @@ void show_node_list(integer p) node_list_display(list_ptr(p)); /* recursive call */ break; case rule_node: - /* @<Display rule |p|@>; */ + /* Display rule |p|; */ tprint_esc("rule("); print_rule_dimen(height(p)); print_char('+'); @@ -2677,13 +2674,13 @@ void show_node_list(integer p) print_rule_dimen(width(p)); break; case ins_node: - /* @<Display insertion |p|@>; */ + /* Display insertion |p|; */ tprint_esc("insert"); print_int(subtype(p)); tprint(", natural size "); print_scaled(height(p)); tprint("; split("); - print_spec(split_top_ptr(p), 0); + print_spec(split_top_ptr(p), NULL); print_char(','); print_scaled(depth(p)); tprint("); float cost "); @@ -2694,35 +2691,51 @@ void show_node_list(integer p) show_whatsit_node(p); break; case glue_node: - /* @<Display glue |p|@>; */ + /* Display glue |p|; */ if (subtype(p) >= a_leaders) { - /* @<Display leaders |p|@>; */ + /* Display leaders |p|; */ tprint_esc(""); - if (subtype(p) == c_leaders) + switch (subtype(p)) { + case a_leaders: + break; + case c_leaders: print_char('c'); - else if (subtype(p) == x_leaders) + break; + case x_leaders: print_char('x'); + break; + case g_leaders: + print_char('g'); + break; + default: + assert(0); + } tprint("leaders "); - print_spec(glue_ptr(p), 0); + print_spec(glue_ptr(p), NULL); node_list_display(leader_ptr(p)); /* recursive call */ } else { tprint_esc("glue"); if (subtype(p) != normal) { print_char('('); - if (subtype(p) < cond_math_glue) - print_skip_param(subtype(p) - 1); - else if (subtype(p) == cond_math_glue) + if ((subtype(p) - 1) < thin_mu_skip_code) { + print_cmd_chr(assign_glue_cmd, + glue_base + (subtype(p) - 1)); + } else if (subtype(p) < cond_math_glue) { + print_cmd_chr(assign_mu_glue_cmd, + glue_base + (subtype(p) - 1)); + } else if (subtype(p) == cond_math_glue) { tprint_esc("nonscript"); - else + } else { tprint_esc("mskip"); + } print_char(')'); } if (subtype(p) != cond_math_glue) { print_char(' '); if (subtype(p) < cond_math_glue) - print_spec(glue_ptr(p), 0); + print_spec(glue_ptr(p), NULL); else - print_spec(glue_ptr(p), maketexstring("mu")); + print_spec(glue_ptr(p), "mu"); } } break; @@ -2735,7 +2748,7 @@ void show_node_list(integer p) tprint(" (right margin)"); break; case kern_node: - /* @<Display kern |p|@>; */ + /* Display kern |p|; */ /* An ``explicit'' kern value is indicated implicitly by an explicit space. */ if (subtype(p) != mu_glue) { tprint_esc("kern"); @@ -2751,7 +2764,7 @@ void show_node_list(integer p) } break; case math_node: - /* @<Display math node |p|@>; */ + /* Display math node |p|; */ tprint_esc("math"); if (subtype(p) == before) tprint("on"); @@ -2763,12 +2776,12 @@ void show_node_list(integer p) } break; case penalty_node: - /* @<Display penalty |p|@>; */ + /* Display penalty |p|; */ tprint_esc("penalty "); print_int(penalty(p)); break; case disc_node: - /* @<Display discretionary |p|@>; */ + /* Display discretionary |p|; */ /* The |post_break| list of a discretionary node is indicated by a prefixed `\.{\char'174}' instead of the `\..' before the |pre_break| list. */ tprint_esc("discretionary"); @@ -2782,7 +2795,7 @@ void show_node_list(integer p) flush_char(); /* recursive call */ break; case mark_node: - /* @<Display mark |p|@>; */ + /* Display mark |p|; */ tprint_esc("mark"); if (mark_class(p) != 0) { print_char('s'); @@ -2791,7 +2804,7 @@ void show_node_list(integer p) print_mark(mark_ptr(p)); break; case adjust_node: - /* @<Display adjustment |p|@>; */ + /* Display adjustment |p|; */ tprint_esc("vadjust"); if (adjust_pre(p) != 0) tprint(" pre "); @@ -2806,9 +2819,10 @@ void show_node_list(integer p) } } -/* This routine finds the 'base' width of a horizontal box, using the same logic +@ This routine finds the 'base' width of a horizontal box, using the same logic that \TeX82 used for \.{\\predisplaywidth} */ +@c pointer actual_box_width(pointer r, scaled base_width) { scaled w; /* calculated |size| */ @@ -2866,7 +2880,7 @@ pointer actual_box_width(pointer r, scaled base_width) case whatsit_node: if ((subtype(p) == pdf_refxform_node) || (subtype(p) == pdf_refximage_node)) - d = pdf_width(p); + d = width(p); else d = 0; break; @@ -2890,3 +2904,488 @@ pointer actual_box_width(pointer r, scaled base_width) } return w; } + + +@ @c +halfword tail_of_list(halfword p) +{ + halfword q = p; + while (vlink(q) != null) + q = vlink(q); + return q; +} + + +@ |delete_glue_ref| is called when a pointer to a glue + specification is being withdrawn. + +@c +#define fast_delete_glue_ref(A) do { \ + if (glue_ref_count(A)==null) { \ + flush_node(A); \ + } else { \ + decr(glue_ref_count(A)); \ + } \ + } while (0) + +void delete_glue_ref(halfword p) +{ /* |p| points to a glue specification */ + assert(type(p) == glue_spec_node); + fast_delete_glue_ref(p); +} + +@ @c +int var_used; +halfword temp_ptr; /* a pointer variable for occasional emergency use */ + + +@ Attribute lists need two extra globals to increase processing efficiency. +|max_used_attr| limits the test loop that checks for set attributes, and +|attr_list_cache| contains a pointer to an already created attribute list. It is +set to the special value |cache_disabled| when the current value can no longer be +trusted: after an assignment to an attribute register, and after a group has +ended. + +@c +int max_used_attr; /* maximum assigned attribute id */ +halfword attr_list_cache; + +@ From the computer's standpoint, \TeX's chief mission is to create +horizontal and vertical lists. We shall now investigate how the elements +of these lists are represented internally as nodes in the dynamic memory. + +A horizontal or vertical list is linked together by |link| fields in +the first word of each node. Individual nodes represent boxes, glue, +penalties, or special things like discretionary hyphens; because of this +variety, some nodes are longer than others, and we must distinguish different +kinds of nodes. We do this by putting a `|type|' field in the first word, +together with the link and an optional `|subtype|'. + + +@ Character nodes appear only in horizontal lists, never in vertical lists. + +An |hlist_node| stands for a box that was made from a horizontal list. +Each |hlist_node| is seven words long, and contains the following fields +(in addition to the mandatory |type| and |link|, which we shall not +mention explicitly when discussing the other node types): The |height| and +|width| and |depth| are scaled integers denoting the dimensions of the +box. There is also a |shift_amount| field, a scaled integer indicating +how much this box should be lowered (if it appears in a horizontal list), +or how much it should be moved to the right (if it appears in a vertical +list). There is a |list_ptr| field, which points to the beginning of the +list from which this box was fabricated; if |list_ptr| is |null|, the box +is empty. Finally, there are three fields that represent the setting of +the glue: |glue_set(p)| is a word of type |glue_ratio| that represents +the proportionality constant for glue setting; |glue_sign(p)| is +|stretching| or |shrinking| or |normal| depending on whether or not the +glue should stretch or shrink or remain rigid; and |glue_order(p)| +specifies the order of infinity to which glue setting applies (|normal|, +|sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used. + +@ The |new_null_box| function returns a pointer to an |hlist_node| in +which all subfields have the values corresponding to `\.{\\hbox\{\}}'. +The |subtype| field is set to |min_quarterword|, since that's the desired +|span_count| value if this |hlist_node| is changed to an |unset_node|. + +@c +halfword new_null_box(void) +{ /* creates a new box node */ + halfword p; /* the new node */ + p = new_node(hlist_node, min_quarterword); + box_dir(p) = text_direction; + return p; +} + + +@ A |vlist_node| is like an |hlist_node| in all respects except that it +contains a vertical list. + + +@ A |rule_node| stands for a solid black rectangle; it has |width|, +|depth|, and |height| fields just as in an |hlist_node|. However, if +any of these dimensions is $-2^{30}$, the actual value will be determined +by running the rule up to the boundary of the innermost enclosing box. +This is called a ``running dimension.'' The |width| is never running in +an hlist; the |height| and |depth| are never running in a~vlist. + +@ A new rule node is delivered by the |new_rule| function. It +makes all the dimensions ``running,'' so you have to change the +ones that are not allowed to run. + +@c +halfword new_rule(void) +{ + halfword p; /* the new node */ + p = new_node(rule_node, 0); /* the |subtype| is not used */ + return p; +} + + +@ Insertions are represented by |ins_node| records, where the |subtype| +indicates the corresponding box number. For example, `\.{\\insert 250}' +leads to an |ins_node| whose |subtype| is |250+min_quarterword|. +The |height| field of an |ins_node| is slightly misnamed; it actually holds +the natural height plus depth of the vertical list being inserted. +The |depth| field holds the |split_max_depth| to be used in case this +insertion is split, and the |split_top_ptr| points to the corresponding +|split_top_skip|. The |float_cost| field holds the |floating_penalty| that +will be used if this insertion floats to a subsequent page after a +split insertion of the same class. There is one more field, the +|ins_ptr|, which points to the beginning of the vlist for the insertion. + +@ A |mark_node| has a |mark_ptr| field that points to the reference count +of a token list that contains the user's \.{\\mark} text. +In addition there is a |mark_class| field that contains the mark class. + +@ An |adjust_node|, which occurs only in horizontal lists, +specifies material that will be moved out into the surrounding +vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}' +operation. The |adjust_ptr| field points to the vlist containing this +material. + +@ A |glyph_node|, which occurs only in horizontal lists, specifies a +glyph in a particular font, along with its attribute list. Older +versions of \TeX\ could use token memory for characters, because the +font,char combination would fit in a single word (both values were +required to be strictly less than $2^{16}$). In LuaTeX, room is +needed for characters that are larger than that, as well as a pointer +to a potential attribute list, and the two displacement values. + +In turn, that made the node so large that it made sense to merge +ligature glyphs as well, as that requires only one extra pointer. A +few extra classes of glyph nodes will be introduced later. The +unification of all those types makes it easier to manipulate lists of +glyphs. The subtype differentiates various glyph kinds. + +First, here is a function that returns a pointer to a glyph node for a given +glyph in a given font. If that glyph doesn't exist, |null| is returned +instead. Nodes of this subtype are directly created only for accents +and their base (through |make_accent|), and math nucleus items (in the +conversion from |mlist| to |hlist|). + +@c +halfword new_glyph(int f, int c) +{ + halfword p = null; /* the new node */ + if ((f == 0) || (char_exists(f, c))) { + p = new_glyph_node(); + set_to_glyph(p); + font(p) = f; + character(p) = c; + } + return p; +} + + +@ A subset of the glyphs nodes represent ligatures: characters +fabricated from the interaction of two or more actual characters. The +characters that generated the ligature have not been forgotten, since +they are needed for diagnostic messages; the |lig_ptr| field points to +a linked list of character nodes for all original characters that have +been deleted. (This list might be empty if the characters that +generated the ligature were retained in other nodes.) + +The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if +the original source of the ligature included implicit left and/or +right boundaries. These nodes are created by the C function |new_ligkern|. + +A third general type of glyphs could be called a character, as it +only appears in lists that are not yet processed by the ligaturing and +kerning steps of the program. + +|main_control| inserts these, and they are later converted to +|subtype_normal| by |new_ligkern|. + +@c +quarterword norm_min(int h) +{ + if (h <= 0) + return 1; + else if (h >= 255) + return 255; + else + return (quarterword) h; +} + +halfword new_char(int f, int c) +{ + halfword p; /* the new node */ + p = new_glyph_node(); + set_to_character(p); + font(p) = f; + character(p) = c; + lang_data(p) = + make_lang_data(uc_hyph, cur_lang, left_hyphen_min, right_hyphen_min); + return p; +} + + +@ Left and right ghost glyph nodes are the result of \.{\\leftghost} +and \.{\\rightghost}, respectively. They are going to be removed by +|new_ligkern|, at the end of which they are no longer needed. + +@ Here are a few handy helpers used by the list output routines. + +@c +scaled glyph_width(halfword p) +{ + scaled w; + w = char_width(font(p), character(p)); + return w; +} + +scaled glyph_height(halfword p) +{ + scaled w; + w = char_height(font(p), character(p)) + y_displace(p); + if (w < 0) + w = 0; + return w; +} + +scaled glyph_depth(halfword p) +{ + scaled w; + w = char_depth(font(p), character(p)); + if (y_displace(p) > 0) + w = w - y_displace(p); + if (w < 0) + w = 0; + return w; +} + + +@ A |disc_node|, which occurs only in horizontal lists, specifies a +``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text +that starts at |pre_break(p)| will precede the break, the text that starts at +|post_break(p)| will follow the break, and text that appears in +|no_break(p)| nodes will be ignored. For example, an ordinary +discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with +|pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|, +and |no_break=null|. + +{TODO: Knuth said: All three of the discretionary texts must be lists +that consist entirely of character, kern, box and rule nodes.} + +If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this +break. Otherwise the |hyphen_penalty| will be charged. The texts will +actually be substituted into the list by the line-breaking algorithm if it +decides to make the break, and the discretionary node will disappear at +that time; thus, the output routine sees only discretionaries that were +not chosen. + +@c +halfword new_disc(void) +{ /* creates an empty |disc_node| */ + halfword p; /* the new node */ + p = new_node(disc_node, 0); + return p; +} + +@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The +|subtype| field in its first word says what `\\{whatsit}' it is, and +implicitly determines the node size (which must be 2 or more) and the +format of the remaining words. When a |whatsit_node| is encountered +in a list, special actions are invoked; knowledgeable people who are +careful not to mess up the rest of \TeX\ are able to make \TeX\ do new +things by adding code at the end of the program. For example, there +might be a `\TeX nicolor' extension to specify different colors of ink, +@^extensions to \TeX@> +and the whatsit node might contain the desired parameters. + +The present implementation of \TeX\ treats the features associated with +`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to +illustrate how such routines might be coded. We shall defer further +discussion of extensions until the end of this program. + +@ A |math_node|, which occurs only in horizontal lists, appears before and +after mathematical formulas. The |subtype| field is |before| before the +formula and |after| after it. There is a |surround| field, which represents +the amount of surrounding space inserted by \.{\\mathsurround}. + +@c +halfword new_math(scaled w, int s) +{ + halfword p; /* the new node */ + p = new_node(math_node, s); + surround(p) = w; + return p; +} + +@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, +|rule_node|, |ins_node|, |mark_node|, |adjust_node|, +|disc_node|, |whatsit_node|, and |math_node| are at the low end of the +type codes, by permitting a break at glue in a list if and only if the +|type| of the previous node is less than |math_node|. Furthermore, a +node is discarded after a break if its type is |math_node| or~more. + +@ A |glue_node| represents glue in a list. However, it is really only +a pointer to a separate glue specification, since \TeX\ makes use of the +fact that many essentially identical nodes of glue are usually present. +If |p| points to a |glue_node|, |glue_ptr(p)| points to +another packet of words that specify the stretch and shrink components, etc. + +Glue nodes also serve to represent leaders; the |subtype| is used to +distinguish between ordinary glue (which is called |normal|) and the three +kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|). +The |leader_ptr| field points to a rule node or to a box node containing the +leaders; it is set to |null| in ordinary glue nodes. + +Many kinds of glue are computed from \TeX's ``skip'' parameters, and +it is helpful to know which parameter has led to a particular glue node. +Therefore the |subtype| is set to indicate the source of glue, whenever +it originated as a parameter. We will be defining symbolic names for the +parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|, +etc.); it suffices for now to say that the |subtype| of parametric glue +will be the same as the parameter number, plus~one. + +@ In math formulas there are two more possibilities for the |subtype| in a +glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu} +instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}' +feature that cancels the glue node immediately following if it appears +in a subscript. + +@ A glue specification has a halfword reference count in its first word, +@^reference counts@> +representing |null| plus the number of glue nodes that point to it (less one). +Note that the reference count appears in the same position as +the |link| field in list nodes; this is the field that is initialized +to |null| when a node is allocated, and it is also the field that is flagged +by |empty_flag| in empty nodes. + +Glue specifications also contain three |scaled| fields, for the |width|, +|stretch|, and |shrink| dimensions. Finally, there are two one-byte +fields called |stretch_order| and |shrink_order|; these contain the +orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|) +corresponding to the stretch and shrink values. + +@ Here is a function that returns a pointer to a copy of a glue spec. +The reference count in the copy is |null|, because there is assumed +to be exactly one reference to the new specification. + +@c +halfword new_spec(halfword p) +{ /* duplicates a glue specification */ + halfword q; /* the new spec */ + q = copy_node(p); + glue_ref_count(q) = null; + return q; +} + +@ And here's a function that creates a glue node for a given parameter +identified by its code number; for example, +|new_param_glue(line_skip_code)| returns a pointer to a glue node for the +current \.{\\lineskip}. + +@c +halfword new_param_glue(int n) +{ + halfword p; /* the new node */ + halfword q; /* the glue specification */ + p = new_node(glue_node, n + 1); + q = glue_par(n); + glue_ptr(p) = q; + incr(glue_ref_count(q)); + return p; +} + +@ Glue nodes that are more or less anonymous are created by |new_glue|, +whose argument points to a glue specification. + +@c +halfword new_glue(halfword q) +{ + halfword p; /* the new node */ + p = new_node(glue_node, normal); + glue_ptr(p) = q; + incr(glue_ref_count(q)); + return p; +} + +@ Still another subroutine is needed: This one is sort of a combination +of |new_param_glue| and |new_glue|. It creates a glue node for one of +the current glue parameters, but it makes a fresh copy of the glue +specification, since that specification will probably be subject to change, +while the parameter will stay put. The global variable |temp_ptr| is +set to the address of the new spec. + +@c +halfword new_skip_param(int n) +{ + halfword p; /* the new node */ + temp_ptr = new_spec(glue_par(n)); + p = new_glue(temp_ptr); + glue_ref_count(temp_ptr) = null; + subtype(p) = (quarterword) (n + 1); + return p; +} + +@ A |kern_node| has a |width| field to specify a (normally negative) +amount of spacing. This spacing correction appears in horizontal lists +between letters like A and V when the font designer said that it looks +better to move them closer together or further apart. A kern node can +also appear in a vertical list, when its `|width|' denotes additional +spacing in the vertical direction. The |subtype| is either |normal| (for +kerns inserted from font information or math mode calculations) or |explicit| +(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern| +(for kerns inserted from non-math accents) or |mu_glue| (for kerns +inserted from \.{\\mkern} specifications in math formulas). + +@ The |new_kern| function creates a kern node having a given width. + +@c +halfword new_kern(scaled w) +{ + halfword p; /* the new node */ + p = new_node(kern_node, normal); + width(p) = w; + return p; +} + +@ A |penalty_node| specifies the penalty associated with line or page +breaking, in its |penalty| field. This field is a fullword integer, but +the full range of integer values is not used: Any penalty |>=10000| is +treated as infinity, and no break will be allowed for such high values. +Similarly, any penalty |<=-10000| is treated as negative infinity, and a +break will be forced. + +@ Anyone who has been reading the last few sections of the program will +be able to guess what comes next. + +@c +halfword new_penalty(int m) +{ + halfword p; /* the new node */ + p = new_node(penalty_node, 0); /* the |subtype| is not used */ + penalty(p) = m; + return p; +} + +@ You might think that we have introduced enough node types by now. Well, +almost, but there is one more: An |unset_node| has nearly the same format +as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} +or \.{\\valign} that are not yet in their final form, since the box +dimensions are their ``natural'' sizes before any glue adjustment has been +made. The |glue_set| word is not present; instead, we have a |glue_stretch| +field, which contains the total stretch of order |glue_order| that is +present in the hlist or vlist being boxed. +Similarly, the |shift_amount| field is replaced by a |glue_shrink| field, +containing the total shrink of order |glue_sign| that is present. +The |subtype| field is called |span_count|; an unset box typically +contains the data for |qo(span_count)+1| columns. +Unset nodes will be changed to box nodes when alignment is completed. + +In fact, there are still more types coming. When we get to math formula +processing we will see that a |style_node| has |type=14|; and a number +of larger type codes will also be defined, for use in math mode only. + +Warning: If any changes are made to these data structure layouts, such as +changing any of the node sizes or even reordering the words of nodes, +the |copy_node_list| procedure and the memory initialization code +below may have to be changed. Such potentially dangerous parts of the +program are listed in the index under `data structure assumptions'. +@!@^data structure assumptions@> +However, other references to the nodes are made symbolically in terms of +the \.{WEB} macro definitions above, so that format changes will leave +\TeX's other algorithms intact. +@^system dependencies@> diff --git a/Build/source/texk/web2c/luatexdir/tex/texpdf.c b/Build/source/texk/web2c/luatexdir/tex/texpdf.c deleted file mode 100644 index 2494e71442b..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/texpdf.c +++ /dev/null @@ -1,186 +0,0 @@ -/* texpdf.c - - Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> - - This file is part of LuaTeX. - - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. - - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ - -#include "luatex-api.h" -#include <ptexlib.h> -#include <ctype.h> - -static const char _svn_version[] = - "$Id: texpdf.c 2271 2009-04-12 23:42:21Z oneiros $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/texpdf.c $"; - -#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 - -/* 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(longinteger 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 longinteger 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/textcodes.h b/Build/source/texk/web2c/luatexdir/tex/textcodes.h new file mode 100644 index 00000000000..f81db2dd0a6 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/textcodes.h @@ -0,0 +1,46 @@ +/* textcodes.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: textcodes.h 3261 2009-12-18 11:38:21Z taco $ */ + +#ifndef TEXTCODES_H +# define TEXTCODES_H + + +void set_lc_code(int n, halfword v, quarterword gl); +halfword get_lc_code(int n); +void set_uc_code(int n, halfword v, quarterword gl); +halfword get_uc_code(int n); +void set_sf_code(int n, halfword v, quarterword gl); +halfword get_sf_code(int n); +void set_cat_code(int h, int n, halfword v, quarterword gl); +halfword get_cat_code(int h, int n); +void unsave_cat_codes(int h, quarterword gl); +int valid_catcode_table(int h); +void initex_cat_codes(int h); +void unsave_text_codes(quarterword grouplevel); +void initialize_text_codes(void); +void dump_text_codes(void); +void undump_text_codes(void); +void copy_cat_codes(int from, int to); +void free_math_codes(void); +void free_text_codes(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/textcodes.w b/Build/source/texk/web2c/luatexdir/tex/textcodes.w new file mode 100644 index 00000000000..ea2f1f73698 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/textcodes.w @@ -0,0 +1,319 @@ +% textcodes.w +% +% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +#include "ptexlib.h" + +static const char _svn_version[] = + "$Id: textcodes.w 3587 2010-04-03 14:32:25Z taco $ " + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/textcodes.w $"; + +@ @c +#define LCCODESTACK 8 +#define LCCODEDEFAULT 0 + +static sa_tree lccode_head = NULL; + +#define UCCODESTACK 8 +#define UCCODEDEFAULT 0 + +static sa_tree uccode_head = NULL; + +#define SFCODESTACK 8 +#define SFCODEDEFAULT 1000 + +static sa_tree sfcode_head = NULL; + +#define CATCODESTACK 8 +#define CATCODEDEFAULT 12 + + +void set_lc_code(int n, halfword v, quarterword gl) +{ + set_sa_item(lccode_head, n, (sa_tree_item) v, gl); +} + +halfword get_lc_code(int n) +{ + return (halfword) get_sa_item(lccode_head, n); +} + +static void unsavelccodes(quarterword gl) +{ + restore_sa_stack(lccode_head, gl); +} + +static void initializelccodes(void) +{ + lccode_head = new_sa_tree(LCCODESTACK, LCCODEDEFAULT); +} + +static void dumplccodes(void) +{ + dump_sa_tree(lccode_head); +} + +static void undumplccodes(void) +{ + lccode_head = undump_sa_tree(); +} + +void set_uc_code(int n, halfword v, quarterword gl) +{ + set_sa_item(uccode_head, n, (sa_tree_item) v, gl); +} + +halfword get_uc_code(int n) +{ + return (halfword) get_sa_item(uccode_head, n); +} + +static void unsaveuccodes(quarterword gl) +{ + restore_sa_stack(uccode_head, gl); +} + +static void initializeuccodes(void) +{ + uccode_head = new_sa_tree(UCCODESTACK, UCCODEDEFAULT); +} + +static void dumpuccodes(void) +{ + dump_sa_tree(uccode_head); +} + +static void undumpuccodes(void) +{ + uccode_head = undump_sa_tree(); +} + +void set_sf_code(int n, halfword v, quarterword gl) +{ + set_sa_item(sfcode_head, n, (sa_tree_item) v, gl); +} + +halfword get_sf_code(int n) +{ + return (halfword) get_sa_item(sfcode_head, n); +} + +static void unsavesfcodes(quarterword gl) +{ + restore_sa_stack(sfcode_head, gl); +} + +static void initializesfcodes(void) +{ + sfcode_head = new_sa_tree(SFCODESTACK, SFCODEDEFAULT); +} + +static void dumpsfcodes(void) +{ + dump_sa_tree(sfcode_head); +} + +static void undumpsfcodes(void) +{ + sfcode_head = undump_sa_tree(); +} + + +static sa_tree *catcode_heads = NULL; +static int catcode_max = 0; +static unsigned char *catcode_valid = NULL; + +#define CATCODE_MAX 65535 + +#define update_catcode_max(h) if (h > catcode_max) catcode_max = h + +void set_cat_code(int h, int n, halfword v, quarterword gl) +{ + sa_tree s = catcode_heads[h]; + update_catcode_max(h); + if (s == NULL) { + s = new_sa_tree(CATCODESTACK, CATCODEDEFAULT); + catcode_heads[h] = s; + } + set_sa_item(s, n, (sa_tree_item) v, gl); +} + +halfword get_cat_code(int h, int n) +{ + sa_tree s = catcode_heads[h]; + update_catcode_max(h); + if (s == NULL) { + s = new_sa_tree(CATCODESTACK, CATCODEDEFAULT); + catcode_heads[h] = s; + } + return (halfword) get_sa_item(s, n); +} + +void unsave_cat_codes(int h, quarterword gl) +{ + int k; + update_catcode_max(h); + for (k = 0; k <= catcode_max; k++) { + if (catcode_heads[k] != NULL) + restore_sa_stack(catcode_heads[k], gl); + } +} + +void clearcatcodestack(int h) +{ + clear_sa_stack(catcode_heads[h]); +} + +static void initializecatcodes(void) +{ + catcode_max = 0; +#if 0 + xfree(catcode_heads); /* not needed */ + xfree(catcode_valid); +#endif + catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1)); + catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1)); + memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1)); + memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1)); + catcode_valid[0] = 1; + catcode_heads[0] = new_sa_tree(CATCODESTACK, CATCODEDEFAULT); +} + +static void dumpcatcodes(void) +{ + int k, total; + dump_int(catcode_max); + total = 0; + for (k = 0; k <= catcode_max; k++) { + if (catcode_valid[k]) { + total++; + } + } + dump_int(total); + for (k = 0; k <= catcode_max; k++) { + if (catcode_valid[k]) { + dump_int(k); + dump_sa_tree(catcode_heads[k]); + } + } +} + +static void undumpcatcodes(void) +{ + int total, k, x; + xfree(catcode_heads); + xfree(catcode_valid); + catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1)); + catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1)); + memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1)); + memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1)); + undump_int(catcode_max); + undump_int(total); + for (k = 0; k < total; k++) { + undump_int(x); + catcode_heads[x] = undump_sa_tree(); + catcode_valid[x] = 1; + } +} + +int valid_catcode_table(int h) +{ + if (h <= CATCODE_MAX && h >= 0 && catcode_valid[h]) { + return 1; + } + return 0; +} + +void copy_cat_codes(int from, int to) +{ + if (from < 0 || from > CATCODE_MAX || catcode_valid[from] == 0) { + uexit(1); + } + update_catcode_max(to); + destroy_sa_tree(catcode_heads[to]); + catcode_heads[to] = copy_sa_tree(catcode_heads[from]); + catcode_valid[to] = 1; +} + +void initex_cat_codes(int h) +{ + int k; + update_catcode_max(h); + destroy_sa_tree(catcode_heads[h]); + catcode_heads[h] = NULL; + set_cat_code(h, '\r', car_ret_cmd, 1); + set_cat_code(h, ' ', spacer_cmd, 1); + set_cat_code(h, '\\', escape_cmd, 1); + set_cat_code(h, '%', comment_cmd, 1); + set_cat_code(h, 127, invalid_char_cmd, 1); + set_cat_code(h, 0, ignore_cmd, 1); + set_cat_code(h, 0xFEFF, ignore_cmd, 1); + for (k = 'A'; k <= 'Z'; k++) { + set_cat_code(h, k, letter_cmd, 1); + set_cat_code(h, k + 'a' - 'A', letter_cmd, 1); + } + catcode_valid[h] = 1; +} + +void unsave_text_codes(quarterword grouplevel) +{ + unsavesfcodes(grouplevel); + unsaveuccodes(grouplevel); + unsavelccodes(grouplevel); +} + +void initialize_text_codes(void) +{ + initializesfcodes(); + initializeuccodes(); + initializecatcodes(); + initializelccodes(); +} + +void free_text_codes(void) +{ + int k; + destroy_sa_tree(lccode_head); + destroy_sa_tree(uccode_head); + destroy_sa_tree(sfcode_head); + for (k = 0; k <= catcode_max; k++) { + if (catcode_valid[k]) { + destroy_sa_tree(catcode_heads[k]); + } + } + xfree(catcode_heads); + xfree(catcode_valid); +} + + +void dump_text_codes(void) +{ + dumpsfcodes(); + dumpuccodes(); + dumplccodes(); + dumpcatcodes(); +} + +void undump_text_codes(void) +{ + undumpsfcodes(); + undumpuccodes(); + undumplccodes(); + undumpcatcodes(); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.c b/Build/source/texk/web2c/luatexdir/tex/textoken.c deleted file mode 100644 index a961966a1a1..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/textoken.c +++ /dev/null @@ -1,1173 +0,0 @@ -/* textoken.c - - Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> - - This file is part of LuaTeX. - - LuaTeX is free software; you can redistribute it and/or modify it under - the terms of the GNU General Public License as published by the Free - Software Foundation; either version 2 of the License, or (at your - option) any later version. - - LuaTeX is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - License for more details. - - You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ - -#include "luatex-api.h" -#include <ptexlib.h> - -#include "tokens.h" -#include "commands.h" - -static const char _svn_version[] = - "$Id: textoken.c 2448 2009-06-08 07:43:50Z taco $ $URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.40.6/source/texk/web2c/luatexdir/tex/textoken.c $"; - -#define skipping 1 /* |scanner_status| when passing conditional text */ -#define defining 2 /* |scanner_status| when reading a macro definition */ -#define matching 3 /* |scanner_status| when reading macro arguments */ -#define aligning 4 /* |scanner_status| when reading an alignment preamble */ -#define absorbing 5 /* |scanner_status| when reading a balanced text */ - -#define right_brace_token 0x400000 /* $2^{21}\cdot|right_brace|$ */ - -#define cat_code_table int_par(param_cat_code_table_code) -#define tracing_nesting int_par(param_tracing_nesting_code) -#define end_line_char int_par(param_end_line_char_code) -#define suppress_outer_error int_par(param_suppress_outer_error_code) - -#define every_eof get_every_eof() - -#define null_cs 1 /* 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 - -#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| */ - -#define detokenized_line() (line_catcode_table==NO_CAT_TABLE) - -#define do_get_cat_code(a) do { \ - if (line_catcode_table!=DEFAULT_CAT_TABLE) \ - a=get_cat_code(line_catcode_table,cur_chr); \ - else \ - a=get_cat_code(cat_code_table,cur_chr); \ - } while (0) - - - -/* string compare */ - -boolean str_eq_cstr(str_number r, const char *s, size_t l) -{ - if (l != (size_t) length(r)) - return false; - return (strncmp((const char *) (str_pool + str_start_macro(r)), s, l) == 0); -} - - - -int get_char_cat_code(int cur_chr) -{ - int a; - do_get_cat_code(a); - return a; -} - -static void invalid_character_error(void) -{ - const char *hlp[] = { "A funny symbol that I can't read has just been input.", - "Continue, and I'll forget that it ever happened.", - NULL - }; - deletions_allowed = false; - tex_error("Text line contains an invalid character", hlp); - deletions_allowed = true; -} - -static boolean process_sup_mark(void); /* 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) -{ - const 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; -} - -#define do_buffer_to_unichar(a,b) a = buffer[b] < 0x80 ? buffer[b++] : qbuffer_to_unichar(&b) - -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); -} - -/* This is a very basic helper */ - -static char *u2s(unsigned unic) -{ - char *buf = xmalloc(5); - char *pt = buf; - if (unic < 0x80) - *pt++ = unic; - else if (unic < 0x800) { - *pt++ = 0xc0 | (unic >> 6); - *pt++ = 0x80 | (unic & 0x3f); - } else if (unic >= 0x110000) { - *pt++ = unic - 0x110000; - } else if (unic < 0x10000) { - *pt++ = 0xe0 | (unic >> 12); - *pt++ = 0x80 | ((unic >> 6) & 0x3f); - *pt++ = 0x80 | (unic & 0x3f); - } else { - int u, z, y, x; - unsigned val = unic - 0x10000; - u = ((val & 0xf0000) >> 16) + 1; - z = (val & 0x0f000) >> 12; - y = (val & 0x00fc0) >> 6; - x = val & 0x0003f; - *pt++ = 0xf0 | (u >> 2); - *pt++ = 0x80 | ((u & 3) << 4) | z; - *pt++ = 0x80 | y; - *pt++ = 0x80 | x; - } - *pt = '\0'; - return buf; -} - - -/* - In case you are getting bored, here is a slightly less trivial routine: - Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or - `\.{width}', the |scan_keyword| routine checks to see whether the next - tokens of input match this string. The match must be exact, except that - uppercase letters will match their lowercase counterparts; uppercase - equivalents are determined by subtracting |"a"-"A"|, rather than using the - |uc_code| table, since \TeX\ uses this routine only for its own limited - set of keywords. - - If a match is found, the characters are effectively removed from the input - and |true| is returned. Otherwise |false| is returned, and the input - is left essentially unchanged (except for the fact that some macros - may have been expanded, etc.). - @^inner loop@> -*/ - -boolean scan_keyword(const char *s) -{ /* look for a given string */ - pointer p; /* tail of the backup list */ - pointer q; /* new node being added to the token list via |store_new_token| */ - const char *k; /* index into |str_pool| */ - pointer save_cur_cs = cur_cs; - if (strlen(s) == 1) { - /* @<Get the next non-blank non-call token@>; */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - if ((cur_cs == 0) && ((cur_chr == *s) || (cur_chr == *s - 'a' + 'A'))) { - return true; - } else { - cur_cs = save_cur_cs; - back_input(); - return false; - } - } else { - p = backup_head; - link(p) = null; - k = s; - while (*k) { - get_x_token(); /* recursion is possible here */ - if ((cur_cs == 0) && - ((cur_chr == *k) || (cur_chr == *k - 'a' + 'A'))) { - store_new_token(cur_tok); - k++; - } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) { - if (p != backup_head) { - q = get_avail(); - info(q) = cur_tok; - link(q) = null; - link(p) = q; - begin_token_list(link(backup_head), backed_up); - } else { - back_input(); - } - cur_cs = save_cur_cs; - return false; - } - } - flush_list(link(backup_head)); - } - return true; -} - -/* |scan_direction| has to be defined here because luatangle will output - a character constant when it sees a string literal of length 1 */ - -#define dir_T 0 -#define dir_L 1 -#define dir_B 2 -#define dir_R 3 - -#define scan_single_dir(A) do { \ - if (scan_keyword("T")) A=dir_T; \ - else if (scan_keyword("L")) A=dir_L; \ - else if (scan_keyword("B")) A=dir_B; \ - else if (scan_keyword("R")) A=dir_R; \ - else { \ - tex_error("Bad direction", NULL); \ - cur_val=0; \ - return; \ - } \ - } while (0) - -void scan_direction(void) -{ - integer d1, d2, d3; - get_x_token(); - if (cur_cmd == assign_dir_cmd) { - cur_val = zeqtb[cur_chr].cint; - return; - } else { - back_input(); - } - scan_single_dir(d1); - scan_single_dir(d2); - if (dir_parallel(d1, d2)) { - tex_error("Bad direction", NULL); - cur_val = 0; - return; - } - scan_single_dir(d3); - get_x_token(); - if (cur_cmd != spacer_cmd) - back_input(); - cur_val = d1 * 8 + dir_rearrange[d2] * 4 + d3; -} - - -/* We can not return |undefined_control_sequence| under some conditions - * (inside |shift_case|, for example). This needs thinking. - */ - -halfword active_to_cs(int curchr, int force) -{ - halfword curcs; - char *a, *b; - char *utfbytes = xmalloc(10); - int nncs = no_new_control_sequence; - a = u2s(0xFFFF); - utfbytes = strcpy(utfbytes, a); - if (force) - no_new_control_sequence = false; - if (curchr > 0) { - b = u2s(curchr); - utfbytes = strcat(utfbytes, b); - free(b); - curcs = string_lookup(utfbytes, strlen(utfbytes)); - } else { - utfbytes[3] = '\0'; - curcs = string_lookup(utfbytes, 4); - } - no_new_control_sequence = nncs; - free(a); - free(utfbytes); - return curcs; -} - -/* TODO this function should listen to \.{\\escapechar} */ - -#define is_active_cs(a) (length(a)>3 && \ - (str_pool[str_start_macro(a)] == 0xEF) && \ - (str_pool[str_start_macro(a)+1] == 0xBF) && \ - (str_pool[str_start_macro(a)+2] == 0xBF)) - - -static char *cs_to_string(pointer p) -{ /* prints a control sequence */ - const char *s; - int k = 0; - static char ret[256] = { 0 }; - if (p == null_cs) { - ret[k++] = '\\'; - s = "csname"; - while (*s) { - ret[k++] = *s++; - } - ret[k++] = '\\'; - s = "endcsname"; - while (*s) { - ret[k++] = *s++; - } - ret[k] = 0; - - } else { - str_number txt = zget_cs_text(p); - s = makecstring(txt); - if (is_active_cs(txt)) { - s = s + 3; - while (*s) { - ret[k++] = *s++; - } - ret[k] = 0; - } else { - ret[k++] = '\\'; - while (*s) { - ret[k++] = *s++; - } - ret[k] = 0; - } - } - return (char *) ret; -} - -/* TODO this is a quick hack, will be solved differently soon */ - -static char *cmd_chr_to_string(int cmd, int chr) -{ - char *s; - str_number str; - int sel = selector; - selector = new_string; - print_cmd_chr(cmd, chr); - str = make_string(); - s = makecstring(str); - selector = sel; - flush_str(str); - return s; -} - -/* Before getting into |get_next|, let's consider the subroutine that - is called when an `\.{\\outer}' control sequence has been scanned or - when the end of a file has been reached. These two cases are distinguished - by |cur_cs|, which is zero at the end of a file. -*/ - -static int frozen_control_sequence = 0; - -#define frozen_cr (frozen_control_sequence+1) /* permanent `\.{\\cr}' */ -#define frozen_fi (frozen_control_sequence+4) /* permanent `\.{\\fi}' */ - -void check_outer_validity(void) -{ - pointer p; /* points to inserted token list */ - pointer q; /* auxiliary pointer */ - if (suppress_outer_error) - return; - if (frozen_control_sequence == 0) { - frozen_control_sequence = get_nullcs() + 1 + get_hash_size(); /* hashbase=nullcs+1 */ - } - if (scanner_status != normal) { - deletions_allowed = false; - /* @<Back up an outer control sequence so that it can be reread@>; */ - /* An outer control sequence that occurs in a \.{\\read} will not be reread, - since the error recovery for \.{\\read} is not very powerful. */ - if (cur_cs != 0) { - if ((state == token_list) || (name < 1) || (name > 17)) { - p = get_avail(); - info(p) = cs_token_flag + cur_cs; - begin_token_list(p, backed_up); /* prepare to read the control sequence again */ - } - cur_cmd = spacer_cmd; - cur_chr = ' '; /* replace it by a space */ - } - if (scanner_status > skipping) { - const char *errhlp[] = { "I suspect you have forgotten a `}', causing me", - "to read past where you wanted me to stop.", - "I'll try to recover; but if the error is serious,", - "you'd better type `E' or `X' now and fix your file.", - NULL - }; - char errmsg[256]; - const char *startmsg, *scannermsg; - /* @<Tell the user what has run away and try to recover@> */ - runaway(); /* print a definition, argument, or preamble */ - if (cur_cs == 0) { - startmsg = "File ended"; - } else { - cur_cs = 0; - startmsg = "Forbidden control sequence found"; - } - /* @<Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}', - and insert tokens that should lead to recovery@>; */ - /* The recovery procedure can't be fully understood without knowing more - about the \TeX\ routines that should be aborted, but we can sketch the - ideas here: For a runaway definition we will insert a right brace; for a - runaway preamble, we will insert a special \.{\\cr} token and a right - brace; and for a runaway argument, we will set |long_state| to - |outer_call| and insert \.{\\par}. */ - p = get_avail(); - switch (scanner_status) { - case defining: - scannermsg = "definition"; - info(p) = right_brace_token + '}'; - break; - case matching: - scannermsg = "use"; - info(p) = par_token; - long_state = outer_call_cmd; - break; - case aligning: - scannermsg = "preamble"; - info(p) = right_brace_token + '}'; - q = p; - p = get_avail(); - link(p) = q; - info(p) = cs_token_flag + frozen_cr; - align_state = -1000000; - break; - case absorbing: - scannermsg = "text"; - info(p) = right_brace_token + '}'; - break; - default: /* can't happen */ - scannermsg = "unknown"; - break; - } /*there are no other cases */ - begin_token_list(p, inserted); - snprintf(errmsg, 255, "%s while scanning %s of %s", - startmsg, scannermsg, cs_to_string(warning_index)); - tex_error(errmsg, errhlp); - } else { - char errmsg[256]; - const char *errhlp_no[] = - { "The file ended while I was skipping conditional text.", - "This kind of error happens when you say `\\if...' and forget", - "the matching `\\fi'. I've inserted a `\\fi'; this might work.", - NULL - }; - const char *errhlp_cs[] = - { "A forbidden control sequence occurred in skipped text.", - "This kind of error happens when you say `\\if...' and forget", - "the matching `\\fi'. I've inserted a `\\fi'; this might work.", - NULL - }; - const char **errhlp = (const char **) errhlp_no; - if (cur_cs != 0) { - errhlp = errhlp_cs; - cur_cs = 0; - } - snprintf(errmsg, 255, - "Incomplete %s; all text was ignored after line %d", - cmd_chr_to_string(if_test_cmd, cur_if), (int) skip_line); - /* @.Incomplete \\if...@> */ - cur_tok = cs_token_flag + frozen_fi; - /* back up one inserted token and call |error| */ - { - OK_to_interrupt = false; - back_input(); - token_type = inserted; - OK_to_interrupt = true; - tex_error(errmsg, errhlp); - } - } - deletions_allowed = true; - } -} - -static boolean get_next_file(void) -{ - SWITCH: - if (loc <= limit) { /* current line not yet finished */ - do_buffer_to_unichar(cur_chr, 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_cmd: - case skip_blanks + ignore_cmd: - case new_line + ignore_cmd: - case skip_blanks + spacer_cmd: - case new_line + spacer_cmd: /* @<Cases where character is ignored@> */ - goto SWITCH; - break; - case mid_line + escape_cmd: - case new_line + escape_cmd: - case skip_blanks + escape_cmd: /* @<Scan a control sequence ...@>; */ - state = scan_control_sequence(); - if (cur_cmd >= outer_call_cmd) - check_outer_validity(); - break; - case mid_line + active_char_cmd: - case new_line + active_char_cmd: - case skip_blanks + active_char_cmd: /* @<Process an active-character */ - cur_cs = active_to_cs(cur_chr, false); - cur_cmd = eq_type(cur_cs); - cur_chr = equiv(cur_cs); - state = mid_line; - if (cur_cmd >= outer_call_cmd) - check_outer_validity(); - break; - case mid_line + sup_mark_cmd: - case new_line + sup_mark_cmd: - case skip_blanks + sup_mark_cmd: /* @<If this |sup_mark| starts */ - if (process_sup_mark()) - goto RESWITCH; - else - state = mid_line; - break; - case mid_line + invalid_char_cmd: - case new_line + invalid_char_cmd: - case skip_blanks + invalid_char_cmd: /* @<Decry the invalid character and |goto restart|@>; */ - invalid_character_error(); - return false; /* because state may be token_list now */ - break; - case mid_line + spacer_cmd: /* @<Enter |skip_blanks| state, emit a space@>; */ - state = skip_blanks; - cur_chr = ' '; - break; - case mid_line + car_ret_cmd: /* @<Finish line, emit a space@>; */ - /* When a character of type |spacer| gets through, its character code is - changed to $\.{"\ "}=@'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_cmd; - cur_chr = ' '; - break; - case skip_blanks + car_ret_cmd: - case mid_line + comment_cmd: - case new_line + comment_cmd: - case skip_blanks + comment_cmd: /* @<Finish line, |goto switch|@>; */ - loc = limit + 1; - goto SWITCH; - break; - case new_line + car_ret_cmd: /* @<Finish line, emit a \.{\\par}@>; */ - loc = limit + 1; - cur_cs = par_loc; - cur_cmd = eq_type(cur_cs); - cur_chr = equiv(cur_cs); - if (cur_cmd >= outer_call_cmd) - check_outer_validity(); - break; - case skip_blanks + left_brace_cmd: - case new_line + left_brace_cmd: - state = mid_line; /* fall through */ - case mid_line + left_brace_cmd: - align_state++; - break; - case skip_blanks + right_brace_cmd: - case new_line + right_brace_cmd: - state = mid_line; /* fall through */ - case mid_line + right_brace_cmd: - align_state--; - break; - case mid_line + math_shift_cmd: - case mid_line + tab_mark_cmd: - case mid_line + mac_param_cmd: - case mid_line + sub_mark_cmd: - case mid_line + letter_cmd: - case mid_line + other_char_cmd: - break; - 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; - do_buffer_to_unichar(cur_chr, k); - do_get_cat_code(cat); - if (cat != letter_cmd || k > limit) { - retval = (cat == spacer_cmd ? skip_blanks : mid_line); - if (cat == sup_mark_cmd && check_expanded_code(&k)) /* @<If an expanded...@>; */ - continue; - } else { - retval = skip_blanks; - do { - do_buffer_to_unichar(cur_chr, k); - do_get_cat_code(cat); - } while (cat == letter_cmd && k <= limit); - - if (cat == sup_mark_cmd && check_expanded_code(&k)) /* @<If an expanded...@>; */ - continue; - if (cat != letter_cmd) { - 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) -{ - boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */ - 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| */ - line_catcode_table = DEFAULT_CAT_TABLE; - 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(); - line_catcode_table = luacstring_cattable(); - line_partial = luacstring_partial(); - if (luacstring_final_line() || line_partial - || line_catcode_table == NO_CAT_TABLE) - inhibit_eol = true; - if (!line_partial) - state = new_line; - } else { - force_eof = true; - } - } else { - if (lua_input_ln(cur_file, 0, true)) { /* not end of file */ - firm_up_the_line(); /* this sets |limit| */ - line_catcode_table = DEFAULT_CAT_TABLE; - } 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; - if (name == 21 || /* lua input */ - name == 19) { /* \scantextokens */ - end_file_reading(); - } else { - end_file_reading(); - check_outer_validity(); - } - 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 >= outer_call_cmd) { - if (cur_cmd == dont_expand_cmd) { /* @<Get the next token, suppressing expansion@> */ - /* The present point in the program is reached only when the |expand| - routine has inserted a special marker into the input. In this special - case, |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_cmd) { - cur_cmd = relax_cmd; - cur_chr = no_expand_flag; - return true; - } - } else { - check_outer_validity(); - } - } - 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_cmd: - align_state++; - break; - case right_brace_cmd: - align_state--; - break; - case out_param_cmd: /* @<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_cmd || cur_cmd == car_ret_cmd) && 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(); -} diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.h b/Build/source/texk/web2c/luatexdir/tex/textoken.h new file mode 100644 index 00000000000..1cb4533a5d2 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.h @@ -0,0 +1,179 @@ +/* textoken.h + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +/* $Id: textoken.h 3376 2010-01-25 16:50:23Z taco $ */ + +#ifndef TEXTOKEN_H +# define TEXTOKEN_H + +# define token_list 0 +# define null 0 +# define cs_token_flag 0x1FFFFFFF + +# define left_brace_token 0x200000 /* $2^{21}\cdot|left_brace|$ */ +# define right_brace_token 0x400000 /* $2^{21}\cdot|right_brace|$ */ +# define left_brace_token 0x200000 /* $2^{21}\cdot|left_brace|$ */ +# define left_brace_limit 0x400000 /* $2^{21}\cdot(|left_brace|+1)$ */ +# define right_brace_token 0x400000 /* $2^{21}\cdot|right_brace|$ */ +# define right_brace_limit 0x600000 /* $2^{21}\cdot(|right_brace|+1)$ */ +# define math_shift_token 0x600000 /* $2^{21}\cdot|math_shift|$ */ +# define tab_token 0x800000 /* $2^{21}\cdot|tab_mark|$ */ +# define out_param_token 0xA00000 /* $2^{21}\cdot|out_param|$ */ +# define space_token 0x1400020 /* $2^{21}\cdot|spacer|+|" "|$ */ +# define letter_token 0x1600000 /* $2^{21}\cdot|letter|$ */ +# define other_token 0x1800000 /* $2^{21}\cdot|other_char|$ */ +# define match_token 0x1A00000 /* $2^{21}\cdot|match|$ */ +# define end_match_token 0x1C00000 /* $2^{21}\cdot|end_match|$ */ +# define protected_token 0x1C00001 /* $2^{21}\cdot|end_match|+1$ */ + +# include "tex/stringpool.h" + +typedef struct smemory_word_ { + halfword hhrh; + halfword hhlh; +} smemory_word; + +# define fix_mem_init 10000 + +extern smemory_word *fixmem; +extern unsigned fix_mem_min; +extern unsigned fix_mem_max; + +extern halfword garbage; /* head of a junk list, write only */ +extern halfword temp_token_head; /* head of a temporary list of some kind */ +extern halfword hold_token_head; /* head of a temporary list of another kind */ +extern halfword omit_template; /* a constant token list */ +extern halfword null_list; /* permanently empty list */ +extern halfword backup_head; /* head of token list built by |scan_keyword| */ + +extern void initialize_tokens(void); + +extern int dyn_used; + +# define token_info(a) fixmem[(a)].hhlh +# define token_link(a) fixmem[(a)].hhrh +# define set_token_info(a,b) fixmem[(a)].hhlh=(b) +# define set_token_link(a,b) fixmem[(a)].hhrh=(b) + +extern halfword avail; /* head of the list of available one-word nodes */ +extern unsigned fix_mem_end; /* the last one-word node used in |mem| */ + +extern halfword get_avail(void); + +/* A one-word node is recycled by calling |free_avail|. +This routine is part of \TeX's ``inner loop,'' so we want it to be fast. +*/ + +# define free_avail(A) do { /* single-word node liberation */ \ + token_link(A)=avail; avail=(A); decr(dyn_used); \ + } while (0) + +/* +There's also a |fast_get_avail| routine, which saves the procedure-call +overhead at the expense of extra programming. This routine is used in +the places that would otherwise account for the most calls of |get_avail|. +*/ + +# define fast_get_avail(A) do { \ + (A)=avail; /* avoid |get_avail| if possible, to save time */ \ + if ((A)==null) { (A)=get_avail(); } \ + else { avail=token_link((A)); token_link((A))=null; incr(dyn_used); } \ + } while (0) + +extern void print_meaning(void); + +extern void flush_list(halfword p); +extern void show_token_list(int p, int q, int l); +extern void token_show(halfword p); + +# define token_ref_count(a) token_info((a)) /* reference count preceding a token list */ +# define set_token_ref_count(a,b) token_info((a))=b +# define add_token_ref(a) token_ref_count(a)++ /* new reference to a token list */ + +# define store_new_token(a) do { \ + q=get_avail(); token_link(p)=q; token_info(q)=(a); p=q; \ + } while (0) + +# define fast_store_new_token(a) do { \ + fast_get_avail(q); token_link(p)=q; token_info(q)=(a); p=q; \ + } while (0) + +extern void delete_token_ref(halfword p); + +extern void make_token_table(lua_State * L, int cmd, int chr, int cs); + +# define NO_CAT_TABLE -2 +# define DEFAULT_CAT_TABLE -1 + +extern void get_next(void); +extern void check_outer_validity(void); +extern boolean scan_keyword(const char *); +extern halfword active_to_cs(int, int); +extern void get_token_lua(void); +halfword string_to_toks(char *); +extern int get_char_cat_code(int); + +/* +|no_expand_flag| is a special character value that is inserted by +|get_next| if it wants to suppress expansion. +*/ + +# define no_expand_flag special_char + +extern boolean end_line_char_inactive(void); + +extern halfword par_loc; +extern halfword par_token; +extern boolean force_eof; +extern int luacstrings; + +extern void firm_up_the_line(void); +extern void get_token(void); + +extern halfword str_toks(lstring b); +extern void ins_the_toks(void); + +extern int scan_lua_state(void); +extern void conv_toks(void); + +extern boolean in_lua_escape; +extern boolean is_convert(halfword c); +extern str_number the_convert_string(halfword c, int i); + +# define closed 2 /* not open, or at end of file */ +# define just_open 1 /* newly opened, first line not yet read */ + +extern FILE *read_file[16]; /* used for \.{\\read} */ +extern int read_open[17]; /* state of |read_file[n]| */ + +extern void initialize_read(void); + +extern void read_toks(int n, halfword r, halfword j); + +extern str_number tokens_to_string(halfword p); /* return a string from tokens list */ + +extern char *tokenlist_to_cstring(int p, int inhibit_par, int *siz); +extern lstring *tokenlist_to_lstring(int pp, int inhibit_par); +extern void free_lstring(lstring * ls); + +# define token_cmd(A) ((A) >> STRING_OFFSET_BITS) +# define token_chr(A) ((A) & (STRING_OFFSET - 1)) +# define token_val(A,B) (((A)<<STRING_OFFSET_BITS)+(B)) + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.w b/Build/source/texk/web2c/luatexdir/tex/textoken.w new file mode 100644 index 00000000000..ac9ad1e1013 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.w @@ -0,0 +1,2410 @@ +% textoken.w +% +% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +% This file is part of LuaTeX. + +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. + +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. + +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +@ @c +static const char _svn_version[] = + "$Id: textoken.w 3587 2010-04-03 14:32:25Z taco $" + "$URL: http://foundry.supelec.fr/svn/luatex/tags/beta-0.60.0/source/texk/web2c/luatexdir/tex/textoken.w $"; + +#include "ptexlib.h" + +@ @c +#define pausing int_par(pausing_code) +#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 suppress_outer_error int_par(suppress_outer_error_code) + +#define every_eof equiv(every_eof_loc) +#define box(A) equiv(box_base+(A)) + +#define detokenized_line() (line_catcode_table==NO_CAT_TABLE) + +#define do_get_cat_code(a) do { \ + if (line_catcode_table!=DEFAULT_CAT_TABLE) \ + a=get_cat_code(line_catcode_table,cur_chr); \ + else \ + a=get_cat_code(cat_code_table,cur_chr); \ + } while (0) + + +@ The \TeX\ system does nearly all of its own memory allocation, so that it +can readily be transported into environments that do not have automatic +facilities for strings, garbage collection, etc., and so that it can be in +control of what error messages the user receives. The dynamic storage +requirements of \TeX\ are handled by providing two large arrays called +|fixmem| and |varmem| in which consecutive blocks of words are used as +nodes by the \TeX\ routines. + +Pointer variables are indices into this array, or into another array +called |eqtb| that will be explained later. A pointer variable might +also be a special flag that lies outside the bounds of |mem|, so we +allow pointers to assume any |halfword| value. The minimum halfword +value represents a null pointer. \TeX\ does not assume that |mem[null]| exists. + + + +@ Locations in |fixmem| are used for storing one-word records; a conventional +\.{AVAIL} stack is used for allocation in this array. + +@c +smemory_word *fixmem; /* the big dynamic storage area */ +unsigned fix_mem_min; /* the smallest location of one-word memory in use */ +unsigned fix_mem_max; /* the largest location of one-word memory in use */ + + +@ In order to study the memory requirements of particular applications, it +is possible to prepare a version of \TeX\ that keeps track of current and +maximum memory usage. When code between the delimiters |@!stat| $\ldots$ +|tats| is not ``commented out,'' \TeX\ will run a bit slower but it will +report these statistics when |tracing_stats| is sufficiently large. + +@c +int var_used, dyn_used; /* how much memory is in use */ + +halfword avail; /* head of the list of available one-word nodes */ +unsigned fix_mem_end; /* the last one-word node used in |mem| */ + +halfword garbage; /* head of a junk list, write only */ +halfword temp_token_head; /* head of a temporary list of some kind */ +halfword hold_token_head; /* head of a temporary list of another kind */ +halfword omit_template; /* a constant token list */ +halfword null_list; /* permanently empty list */ +halfword backup_head; /* head of token list built by |scan_keyword| */ + +@ @c +void initialize_tokens(void) +{ + halfword p; + avail = null; + fix_mem_end = 0; + p = get_avail(); + temp_token_head = p; + set_token_info(temp_token_head, 0); + p = get_avail(); + hold_token_head = p; + set_token_info(hold_token_head, 0); + p = get_avail(); + omit_template = p; + set_token_info(omit_template, 0); + p = get_avail(); + null_list = p; + set_token_info(null_list, 0); + p = get_avail(); + backup_head = p; + set_token_info(backup_head, 0); + p = get_avail(); + garbage = p; + set_token_info(garbage, 0); + dyn_used = 0; /* initialize statistics */ +} + +@ The function |get_avail| returns a pointer to a new one-word node whose +|link| field is null. However, \TeX\ will halt if there is no more room left. +@^inner loop@> + +If the available-space list is empty, i.e., if |avail=null|, +we try first to increase |fix_mem_end|. If that cannot be done, i.e., if +|fix_mem_end=fix_mem_max|, we try to reallocate array |fixmem|. +If, that doesn't work, we have to quit. + +@c +halfword get_avail(void) +{ /* single-word node allocation */ + unsigned p; /* the new node being got */ + unsigned t; + p = (unsigned) avail; /* get top location in the |avail| stack */ + if (p != null) { + avail = token_link(avail); /* and pop it off */ + } else if (fix_mem_end < fix_mem_max) { /* or go into virgin territory */ + incr(fix_mem_end); + p = fix_mem_end; + } else { + smemory_word *new_fixmem; /* the big dynamic storage area */ + t = (fix_mem_max / 5); + new_fixmem = + fixmemcast(realloc + (fixmem, sizeof(smemory_word) * (fix_mem_max + t + 1))); + if (new_fixmem == NULL) { + runaway(); /* if memory is exhausted, display possible runaway text */ + overflow("token memory size", fix_mem_max); + } else { + fixmem = new_fixmem; + } + memset(voidcast(fixmem + fix_mem_max + 1), 0, t * sizeof(smemory_word)); + fix_mem_max += t; + p = ++fix_mem_end; + } + token_link(p) = null; /* provide an oft-desired initialization of the new node */ + incr(dyn_used); /* maintain statistics */ + return (halfword) p; +} + + +@ The procedure |flush_list(p)| frees an entire linked list of +one-word nodes that starts at position |p|. +@^inner loop@> + +@c +void flush_list(halfword p) +{ /* makes list of single-word nodes available */ + halfword q, r; /* list traversers */ + if (p != null) { + r = p; + do { + q = r; + r = token_link(r); + decr(dyn_used); + } while (r != null); /* now |q| is the last node on the list */ + token_link(q) = avail; + avail = p; + } +} + +@ A \TeX\ token is either a character or a control sequence, and it is +@^token@> +represented internally in one of two ways: (1)~A character whose ASCII +code number is |c| and whose command code is |m| is represented as the +number $2^{21}m+c$; the command code is in the range |1<=m<=14|. (2)~A control +sequence whose |eqtb| address is |p| is represented as the number +|cs_token_flag+p|. Here |cs_token_flag=@t$2^{25}-1$@>| is larger than +$2^{21}m+c$, yet it is small enough that |cs_token_flag+p< max_halfword|; +thus, a token fits comfortably in a halfword. + +A token |t| represents a |left_brace| command if and only if +|t<left_brace_limit|; it represents a |right_brace| command if and only if +we have |left_brace_limit<=t<right_brace_limit|; and it represents a |match| or +|end_match| command if and only if |match_token<=t<=end_match_token|. +The following definitions take care of these token-oriented constants +and a few others. + +@ A token list is a singly linked list of one-word nodes in |mem|, where +each word contains a token and a link. Macro definitions, output-routine +definitions, marks, \.{\\write} texts, and a few other things +are remembered by \TeX\ in the form +of token lists, usually preceded by a node with a reference count in its +|token_ref_count| field. The token stored in location |p| is called +|info(p)|. + +Three special commands appear in the token lists of macro definitions. +When |m=match|, it means that \TeX\ should scan a parameter +for the current macro; when |m=end_match|, it means that parameter +matching should end and \TeX\ should start reading the macro text; and +when |m=out_param|, it means that \TeX\ should insert parameter +number |c| into the text at this point. + +The enclosing \.{\char'173} and \.{\char'175} characters of a macro +definition are omitted, but the final right brace of an output routine +is included at the end of its token list. + +Here is an example macro definition that illustrates these conventions. +After \TeX\ processes the text +$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$ +the definition of \.{\\mac} is represented as a token list containing +$$\def\,{\hskip2pt} +\vbox{\halign{\hfil#\hfil\cr +(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ , +\.{\\b}, |end_match|,\cr +|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#, +|other_char|\,\.1,\cr +|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$ +The procedure |scan_toks| builds such token lists, and |macro_call| +does the parameter matching. +@^reference counts@> + +Examples such as +$$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$ +explain why reference counts would be needed even if \TeX\ had no \.{\\let} +operation: When the token list for \.{\\m} is being read, the redefinition of +\.{\\m} changes the |eqtb| entry before the token list has been fully +consumed, so we dare not simply destroy a token list when its +control sequence is being redefined. + +If the parameter-matching part of a definition ends with `\.{\#\{}', +the corresponding token list will have `\.\{' just before the `|end_match|' +and also at the very end. The first `\.\{' is used to delimit the parameter; the +second one keeps the first from disappearing. + +The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in +symbolic form, including the expansion of a macro or mark. + +@c +void print_meaning(void) +{ + print_cmd_chr((quarterword) cur_cmd, cur_chr); + if (cur_cmd >= call_cmd) { + print_char(':'); + print_ln(); + token_show(cur_chr); + } else { + /* Show the meaning of a mark node */ + if ((cur_cmd == top_bot_mark_cmd) && (cur_chr < marks_code)) { + print_char(':'); + print_ln(); + switch (cur_chr) { + case first_mark_code: + token_show(first_mark(0)); + break; + case bot_mark_code: + token_show(bot_mark(0)); + break; + case split_first_mark_code: + token_show(split_first_mark(0)); + break; + case split_bot_mark_code: + token_show(split_bot_mark(0)); + break; + default: + token_show(top_mark(0)); + break; + } + } + } +} + + +@ The procedure |show_token_list|, which prints a symbolic form of +the token list that starts at a given node |p|, illustrates these +conventions. The token list being displayed should not begin with a reference +count. However, the procedure is intended to be robust, so that if the +memory links are awry or if |p| is not really a pointer to a token list, +nothing catastrophic will happen. + +An additional parameter |q| is also given; this parameter is either null +or it points to a node in the token list where a certain magic computation +takes place that will be explained later. (Basically, |q| is non-null when +we are printing the two-line context information at the time of an error +message; |q| marks the place corresponding to where the second line +should begin.) + +For example, if |p| points to the node containing the first \.a in the +token list above, then |show_token_list| will print the string +$$\hbox{`\.{a\#1\#2\ \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$ +and if |q| points to the node containing the second \.a, +the magic computation will be performed just before the second \.a is printed. + +The generation will stop, and `\.{\\ETC.}' will be printed, if the length +of printing exceeds a given limit~|l|. Anomalous entries are printed in the +form of control sequences that are not followed by a blank space, e.g., +`\.{\\BAD.}'; this cannot be confused with actual control sequences because +a real control sequence named \.{BAD} would come out `\.{\\BAD\ }'. + +@c +void show_token_list(int p, int q, int l) +{ + int m, c; /* pieces of a token */ + ASCII_code match_chr; /* character used in a `|match|' */ + ASCII_code n; /* the highest parameter number, as an ASCII digit */ + match_chr = '#'; + n = '0'; + tally = 0; + if (l < 0) + l = 0x3FFFFFFF; + while ((p != null) && (tally < l)) { + if (p == q) { + /* Do magic computation */ + set_trick_count(); + } + /* Display token |p|, and |return| if there are problems */ + if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) { + tprint_esc("CLOBBERED."); + return; + } + if (token_info(p) >= cs_token_flag) { + if (!((inhibit_par_tokens) && (token_info(p) == par_token))) + print_cs(token_info(p) - cs_token_flag); + } else { + m = token_cmd(token_info(p)); + c = token_chr(token_info(p)); + if (token_info(p) < 0) { + tprint_esc("BAD."); + } else { + /* Display the token $(|m|,|c|)$ */ + /* The procedure usually ``learns'' the character code used for macro + parameters by seeing one in a |match| command before it runs into any + |out_param| commands. */ + switch (m) { + case left_brace_cmd: + case right_brace_cmd: + case math_shift_cmd: + case tab_mark_cmd: + case sup_mark_cmd: + case sub_mark_cmd: + case spacer_cmd: + case letter_cmd: + case other_char_cmd: + print(c); + break; + case mac_param_cmd: + if (!in_lua_escape) + print(c); + print(c); + break; + case out_param_cmd: + print(match_chr); + if (c <= 9) { + print_char(c + '0'); + } else { + print_char('!'); + return; + } + break; + case match_cmd: + match_chr = c; + print(c); + incr(n); + print_char(n); + if (n > '9') + return; + break; + case end_match_cmd: + if (c == 0) + tprint("->"); + break; + default: + tprint_esc("BAD."); + break; + } + } + } + p = token_link(p); + } + if (p != null) + tprint_esc("ETC."); +} + +@ @c +#define do_buffer_to_unichar(a,b) do { \ + a = (halfword)str2uni(buffer+b); \ + b += utf8_size(a); \ + } while (0) + + +@ Here's the way we sometimes want to display a token list, given a pointer +to its reference count; the pointer may be null. + +@c +void token_show(halfword p) +{ + if (p != null) + show_token_list(token_link(p), null, 10000000); +} + + + +@ |delete_token_ref|, is called when +a pointer to a token list's reference count is being removed. This means +that the token list should disappear if the reference count was |null|, +otherwise the count should be decreased by one. +@^reference counts@> + +@c +void delete_token_ref(halfword p) +{ /* |p| points to the reference count + of a token list that is losing one reference */ + assert(token_ref_count(p) >= 0); + if (token_ref_count(p) == 0) + flush_list(p); + else + decr(token_ref_count(p)); +} + +@ @c +int get_char_cat_code(int cur_chr) +{ + int a; + do_get_cat_code(a); + return a; +} + +@ @c +static void invalid_character_error(void) +{ + const char *hlp[] = + { "A funny symbol that I can't read has just been input.", + "Continue, and I'll forget that it ever happened.", + NULL + }; + deletions_allowed = false; + tex_error("Text line contains an invalid character", hlp); + deletions_allowed = true; +} + +@ @c +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 */ + + +@ In case you are getting bored, here is a slightly less trivial routine: + Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or + `\.{width}', the |scan_keyword| routine checks to see whether the next + tokens of input match this string. The match must be exact, except that + uppercase letters will match their lowercase counterparts; uppercase + equivalents are determined by subtracting |"a"-"A"|, rather than using the + |uc_code| table, since \TeX\ uses this routine only for its own limited + set of keywords. + + If a match is found, the characters are effectively removed from the input + and |true| is returned. Otherwise |false| is returned, and the input + is left essentially unchanged (except for the fact that some macros + may have been expanded, etc.). + @^inner loop@> + +@c +boolean scan_keyword(const char *s) +{ /* look for a given string */ + halfword p; /* tail of the backup list */ + halfword q; /* new node being added to the token list via |store_new_token| */ + const char *k; /* index into |str_pool| */ + halfword save_cur_cs = cur_cs; + if (strlen(s) == 1) { + /* Get the next non-blank non-call token; */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if ((cur_cs == 0) && ((cur_chr == *s) || (cur_chr == *s - 'a' + 'A'))) { + return true; + } else { + cur_cs = save_cur_cs; + back_input(); + return false; + } + } else { + p = backup_head; + token_link(p) = null; + k = s; + while (*k) { + get_x_token(); /* recursion is possible here */ + if ((cur_cs == 0) && + ((cur_chr == *k) || (cur_chr == *k - 'a' + 'A'))) { + store_new_token(cur_tok); + k++; + } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) { + if (p != backup_head) { + q = get_avail(); + token_info(q) = cur_tok; + token_link(q) = null; + token_link(p) = q; + begin_token_list(token_link(backup_head), backed_up); + } else { + back_input(); + } + cur_cs = save_cur_cs; + return false; + } + } + flush_list(token_link(backup_head)); + } + cur_cs = save_cur_cs; + return true; +} + +@ We can not return |undefined_control_sequence| under some conditions + (inside |shift_case|, for example). This needs thinking. + +@c +halfword active_to_cs(int curchr, int force) +{ + halfword curcs; + char *a, *b; + char *utfbytes = xmalloc(10); + int nncs = no_new_control_sequence; + a = (char *) uni2str(0xFFFF); + utfbytes = strcpy(utfbytes, a); + if (force) + no_new_control_sequence = false; + if (curchr > 0) { + b = (char *) uni2str((unsigned) curchr); + utfbytes = strcat(utfbytes, b); + free(b); + curcs = string_lookup(utfbytes, strlen(utfbytes)); + } else { + utfbytes[3] = '\0'; + curcs = string_lookup(utfbytes, 4); + } + no_new_control_sequence = nncs; + free(a); + free(utfbytes); + return curcs; +} + +@ TODO this function should listen to \.{\\escapechar} + +@c +char *cs_to_string(halfword p) +{ /* prints a control sequence */ + const char *s; + char *sh; + int k = 0; + static char ret[256] = { 0 }; + if (p == 0 || p == null_cs) { + ret[k++] = '\\'; + s = "csname"; + while (*s) { + ret[k++] = *s++; + } + ret[k++] = '\\'; + s = "endcsname"; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + + } else { + str_number txt = cs_text(p); + sh = makecstring(txt); + s = sh; + if (is_active_cs(txt)) { + s = s + 3; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + } else { + ret[k++] = '\\'; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + } + free(sh); + } + return (char *) ret; +} + +@ TODO this is a quick hack, will be solved differently soon + +@c +char *cmd_chr_to_string(int cmd, int chr) +{ + char *s; + str_number str; + int sel = selector; + selector = new_string; + print_cmd_chr((quarterword) cmd, chr); + str = make_string(); + s = makecstring(str); + selector = sel; + flush_str(str); + return s; +} + +@ The heart of \TeX's input mechanism is the |get_next| procedure, which +we shall develop in the next few sections of the program. Perhaps we +shouldn't actually call it the ``heart,'' however, because it really acts +as \TeX's eyes and mouth, reading the source files and gobbling them up. +And it also helps \TeX\ to regurgitate stored token lists that are to be +processed again. +@^eyes and mouth@> + +The main duty of |get_next| is to input one token and to set |cur_cmd| +and |cur_chr| to that token's command code and modifier. Furthermore, if +the input token is a control sequence, the |eqtb| location of that control +sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero. + +Underlying this simple description is a certain amount of complexity +because of all the cases that need to be handled. +However, the inner loop of |get_next| is reasonably short and fast. + +When |get_next| is asked to get the next token of a \.{\\read} line, +it sets |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens +appear on that line. (There might not be any tokens at all, if the +|end_line_char| has |ignore| as its catcode.) + + +@ The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity +is needed because a blank line of input is supposed to be exactly equivalent +to the appearance of \.{\\par}; we must set |cur_cs:=par_loc| +when detecting a blank line. + +@c +halfword par_loc; /* location of `\.{\\par}' in |eqtb| */ +halfword par_token; /* token representing `\.{\\par}' */ + + +@ Parts |get_next| are executed more often than any other instructions of \TeX. +@^mastication@>@^inner loop@> + + + +@ The global variable |force_eof| is normally |false|; it is set |true| +by an \.{\\endinput} command. |luacstrings| is the number of lua print +statements waiting to be input, it is changed by |luatokencall|. + +@c +boolean force_eof; /* should the next \.{\\input} be aborted early? */ +int luacstrings; /* how many lua strings are waiting to be input? */ + + +@ If the user has set the |pausing| parameter to some positive value, +and if nonstop mode has not been selected, each line of input is displayed +on the terminal and the transcript file, followed by `\.{=>}'. +\TeX\ waits for a response. If the response is simply |carriage_return|, the +line is accepted as it stands, otherwise the line typed is +used instead of the line in the file. + +@c +void firm_up_the_line(void) +{ + int k; /* an index into |buffer| */ + ilimit = last; + if (pausing > 0) { + if (interaction > nonstop_mode) { + wake_up_terminal(); + print_ln(); + if (istart < ilimit) { + for (k = istart; k <= ilimit - 1; k++) + print_char(buffer[k]); + } + first = ilimit; + prompt_input("=>"); /* wait for user response */ + if (last > first) { + for (k = first; k < +last - 1; k++) /* move line down in buffer */ + buffer[k + istart - first] = buffer[k]; + ilimit = istart + last - first; + } + } + } +} + + + +@ Before getting into |get_next|, let's consider the subroutine that + is called when an `\.{\\outer}' control sequence has been scanned or + when the end of a file has been reached. These two cases are distinguished + by |cur_cs|, which is zero at the end of a file. + +@c +void check_outer_validity(void) +{ + halfword p; /* points to inserted token list */ + halfword q; /* auxiliary pointer */ + if (suppress_outer_error) + return; + if (scanner_status != normal) { + deletions_allowed = false; + /* Back up an outer control sequence so that it can be reread; */ + /* An outer control sequence that occurs in a \.{\\read} will not be reread, + since the error recovery for \.{\\read} is not very powerful. */ + if (cur_cs != 0) { + if ((istate == token_list) || (iname < 1) || (iname > 17)) { + p = get_avail(); + token_info(p) = cs_token_flag + cur_cs; + begin_token_list(p, backed_up); /* prepare to read the control sequence again */ + } + cur_cmd = spacer_cmd; + cur_chr = ' '; /* replace it by a space */ + } + if (scanner_status > skipping) { + const char *errhlp[] = + { "I suspect you have forgotten a `}', causing me", + "to read past where you wanted me to stop.", + "I'll try to recover; but if the error is serious,", + "you'd better type `E' or `X' now and fix your file.", + NULL + }; + char errmsg[256]; + const char *startmsg; + const char *scannermsg; + /* Tell the user what has run away and try to recover */ + runaway(); /* print a definition, argument, or preamble */ + if (cur_cs == 0) { + startmsg = "File ended"; + } else { + cur_cs = 0; + startmsg = "Forbidden control sequence found"; + } + /* Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}', + and insert tokens that should lead to recovery; */ + /* The recovery procedure can't be fully understood without knowing more + about the \TeX\ routines that should be aborted, but we can sketch the + ideas here: For a runaway definition we will insert a right brace; for a + runaway preamble, we will insert a special \.{\\cr} token and a right + brace; and for a runaway argument, we will set |long_state| to + |outer_call| and insert \.{\\par}. */ + p = get_avail(); + switch (scanner_status) { + case defining: + scannermsg = "definition"; + token_info(p) = right_brace_token + '}'; + break; + case matching: + scannermsg = "use"; + token_info(p) = par_token; + long_state = outer_call_cmd; + break; + case aligning: + scannermsg = "preamble"; + token_info(p) = right_brace_token + '}'; + q = p; + p = get_avail(); + token_link(p) = q; + token_info(p) = cs_token_flag + frozen_cr; + align_state = -1000000; + break; + case absorbing: + scannermsg = "text"; + token_info(p) = right_brace_token + '}'; + break; + default: /* can't happen */ + scannermsg = "unknown"; + break; + } /*there are no other cases */ + begin_token_list(p, inserted); + snprintf(errmsg, 255, "%s while scanning %s of %s", + startmsg, scannermsg, cs_to_string(warning_index)); + tex_error(errmsg, errhlp); + } else { + char errmsg[256]; + const char *errhlp_no[] = + { "The file ended while I was skipping conditional text.", + "This kind of error happens when you say `\\if...' and forget", + "the matching `\\fi'. I've inserted a `\\fi'; this might work.", + NULL + }; + const char *errhlp_cs[] = + { "A forbidden control sequence occurred in skipped text.", + "This kind of error happens when you say `\\if...' and forget", + "the matching `\\fi'. I've inserted a `\\fi'; this might work.", + NULL + }; + const char **errhlp = (const char **) errhlp_no; + char *ss; + if (cur_cs != 0) { + errhlp = errhlp_cs; + cur_cs = 0; + } + ss = cmd_chr_to_string(if_test_cmd, cur_if); + snprintf(errmsg, 255, + "Incomplete %s; all text was ignored after line %d", + ss, (int) skip_line); + free(ss); + /* Incomplete \\if... */ + cur_tok = cs_token_flag + frozen_fi; + /* back up one inserted token and call |error| */ + { + OK_to_interrupt = false; + back_input(); + token_type = inserted; + OK_to_interrupt = true; + tex_error(errmsg, errhlp); + } + } + deletions_allowed = true; + } +} + +@ @c +static boolean get_next_file(void) +{ + SWITCH: + if (iloc <= ilimit) { /* current line not yet finished */ + do_buffer_to_unichar(cur_chr, iloc); + + RESWITCH: + if (detokenized_line()) { + cur_cmd = (cur_chr == ' ' ? 10 : 12); + } else { + do_get_cat_code(cur_cmd); + } + /* + Change state if necessary, and |goto switch| if the current + character should be ignored, or |goto reswitch| if the current + character changes to another; + */ + /* The following 48-way switch accomplishes the scanning quickly, assuming + that a decent C compiler has translated the code. Note that the numeric + values for |mid_line|, |skip_blanks|, and |new_line| are spaced + apart from each other by |max_char_code+1|, so we can add a character's + command code to the state to get a single number that characterizes both. + */ + switch (istate + cur_cmd) { + case mid_line + ignore_cmd: + case skip_blanks + ignore_cmd: + case new_line + ignore_cmd: + case skip_blanks + spacer_cmd: + case new_line + spacer_cmd: /* Cases where character is ignored */ + goto SWITCH; + break; + case mid_line + escape_cmd: + case new_line + escape_cmd: + case skip_blanks + escape_cmd: /* Scan a control sequence ...; */ + istate = (unsigned char) scan_control_sequence(); + if (cur_cmd >= outer_call_cmd) + check_outer_validity(); + break; + case mid_line + active_char_cmd: + case new_line + active_char_cmd: + case skip_blanks + active_char_cmd: /* Process an active-character */ + cur_cs = active_to_cs(cur_chr, false); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + istate = mid_line; + if (cur_cmd >= outer_call_cmd) + check_outer_validity(); + break; + case mid_line + sup_mark_cmd: + case new_line + sup_mark_cmd: + case skip_blanks + sup_mark_cmd: /* If this |sup_mark| starts */ + if (process_sup_mark()) + goto RESWITCH; + else + istate = mid_line; + break; + case mid_line + invalid_char_cmd: + case new_line + invalid_char_cmd: + case skip_blanks + invalid_char_cmd: /* Decry the invalid character and |goto restart|; */ + invalid_character_error(); + return false; /* because state may be |token_list| now */ + break; + case mid_line + spacer_cmd: /* Enter |skip_blanks| state, emit a space; */ + istate = skip_blanks; + cur_chr = ' '; + break; + case mid_line + car_ret_cmd: /* Finish line, emit a space; */ + /* When a character of type |spacer| gets through, its character code is + changed to $\.{"\ "}=040$. This means that the ASCII codes for tab and space, + and for the space inserted at the end of a line, will + be treated alike when macro parameters are being matched. We do this + since such characters are indistinguishable on most computer terminal displays. + */ + iloc = ilimit + 1; + cur_cmd = spacer_cmd; + cur_chr = ' '; + break; + case skip_blanks + car_ret_cmd: + case mid_line + comment_cmd: + case new_line + comment_cmd: + case skip_blanks + comment_cmd: /* Finish line, |goto switch|; */ + iloc = ilimit + 1; + goto SWITCH; + break; + case new_line + car_ret_cmd: /* Finish line, emit a \.{\\par}; */ + iloc = ilimit + 1; + cur_cs = par_loc; + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + if (cur_cmd >= outer_call_cmd) + check_outer_validity(); + break; + case skip_blanks + left_brace_cmd: + case new_line + left_brace_cmd: + istate = mid_line; /* fall through */ + case mid_line + left_brace_cmd: + align_state++; + break; + case skip_blanks + right_brace_cmd: + case new_line + right_brace_cmd: + istate = mid_line; /* fall through */ + case mid_line + right_brace_cmd: + align_state--; + break; + case mid_line + math_shift_cmd: + case mid_line + tab_mark_cmd: + case mid_line + mac_param_cmd: + case mid_line + sub_mark_cmd: + case mid_line + letter_cmd: + case mid_line + other_char_cmd: + break; +#if 0 + 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: +#else + default: +#endif + istate = mid_line; + break; + } + } else { + if (current_ocp_lstack > 0) { + pop_input(); + return false; + } + if (iname != 21) + istate = new_line; + + /* + Move to next line of file, + or |goto restart| if there is no next line, + or |return| if a \.{\\read} line has finished; + */ + 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; +} + +@ @c +#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. + +@c +static boolean process_sup_mark(void) +{ + if (cur_chr == buffer[iloc]) { + int c, cc; + if (iloc < ilimit) { + if ((cur_chr == buffer[iloc + 1]) && (cur_chr == buffer[iloc + 2]) + && (cur_chr == buffer[iloc + 3]) + && (cur_chr == buffer[iloc + 4]) + && ((iloc + 10) <= ilimit)) { + int ccc, cccc, ccccc, cccccc; /* constituents of a possible expanded code */ + c = buffer[iloc + 5]; + cc = buffer[iloc + 6]; + ccc = buffer[iloc + 7]; + cccc = buffer[iloc + 8]; + ccccc = buffer[iloc + 9]; + cccccc = buffer[iloc + 10]; + if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) + && (is_hex(cccc)) + && (is_hex(ccccc)) && (is_hex(cccccc))) { + iloc = iloc + 11; + six_hex_to_cur_chr; + return true; + } + } + if ((cur_chr == buffer[iloc + 1]) && (cur_chr == buffer[iloc + 2]) + && (cur_chr == buffer[iloc + 3]) && ((iloc + 8) <= ilimit)) { + int ccc, cccc, ccccc; /* constituents of a possible expanded code */ + c = buffer[iloc + 4]; + cc = buffer[iloc + 5]; + ccc = buffer[iloc + 6]; + cccc = buffer[iloc + 7]; + ccccc = buffer[iloc + 8]; + if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) + && (is_hex(cccc)) && (is_hex(ccccc))) { + iloc = iloc + 9; + five_hex_to_cur_chr; + return true; + } + } + if ((cur_chr == buffer[iloc + 1]) && (cur_chr == buffer[iloc + 2]) + && ((iloc + 6) <= ilimit)) { + int ccc, cccc; /* constituents of a possible expanded code */ + c = buffer[iloc + 3]; + cc = buffer[iloc + 4]; + ccc = buffer[iloc + 5]; + cccc = buffer[iloc + 6]; + if ((is_hex(c)) && (is_hex(cc)) && (is_hex(ccc)) + && (is_hex(cccc))) { + iloc = iloc + 7; + four_hex_to_cur_chr; + return true; + } + } + c = buffer[iloc + 1]; + if (c < 0200) { /* yes we have an expanded char */ + iloc = iloc + 2; + if (is_hex(c) && iloc <= ilimit) { + cc = buffer[iloc]; + if (is_hex(cc)) { + incr(iloc); + 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[ilimit+1]|, but it never looks at |buffer[ilimit+2]|. + + If expanded characters like `\.{\^\^A}' or `\.{\^\^df}' + appear in or just following + a control sequence name, they are converted to single characters in the + buffer and the process is repeated, slowly but surely. + +@c +static boolean check_expanded_code(int *kk); /* below */ + +static int scan_control_sequence(void) +{ + int retval = mid_line; + if (iloc > ilimit) { + cur_cs = null_cs; /* |state| is irrelevant in this case */ + } else { + register int cat; /* |cat_code(cur_chr)|, usually */ + while (1) { + int k = iloc; + do_buffer_to_unichar(cur_chr, k); + do_get_cat_code(cat); + if (cat != letter_cmd || k > ilimit) { + retval = (cat == spacer_cmd ? skip_blanks : mid_line); + if (cat == sup_mark_cmd && check_expanded_code(&k)) /* If an expanded...; */ + continue; + } else { + retval = skip_blanks; + do { + do_buffer_to_unichar(cur_chr, k); + do_get_cat_code(cat); + } while (cat == letter_cmd && k <= ilimit); + + if (cat == sup_mark_cmd && check_expanded_code(&k)) /* If an expanded...; */ + continue; + if (cat != letter_cmd) { + 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(iloc, k - iloc); + iloc = 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<=ilimit+1| and |cat=get_cat_code(cat_code_table,cur_chr)|. If an + expanded code like \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)..(k+1)]| + or |buffer[(k-1)..(k+2)]|, we + will store the corresponding code in |buffer[k-1]| and shift the rest of + the buffer left two or three places. + +@c +static boolean check_expanded_code(int *kk) +{ + int l; + 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 < ilimit) { + if ((cur_chr == buffer[k + 1]) && (cur_chr == buffer[k + 2]) + && ((k + 6) <= ilimit)) { + d = 4; + if ((cur_chr == buffer[k + 3]) && ((k + 8) <= ilimit)) + d = 5; + if ((cur_chr == buffer[k + 4]) && ((k + 10) <= ilimit)) + 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) <= ilimit) { + 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] = (packed_ASCII_code) cur_chr; + } else if (cur_chr <= 0x7FF) { + buffer[k - 1] = (packed_ASCII_code) (0xC0 + cur_chr / 0x40); + k++; + d--; + buffer[k - 1] = (packed_ASCII_code) (0x80 + cur_chr % 0x40); + } else if (cur_chr <= 0xFFFF) { + buffer[k - 1] = (packed_ASCII_code) (0xE0 + cur_chr / 0x1000); + k++; + d--; + buffer[k - 1] = + (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) / 0x40); + k++; + d--; + buffer[k - 1] = + (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) % 0x40); + } else { + buffer[k - 1] = (packed_ASCII_code) (0xF0 + cur_chr / 0x40000); + k++; + d--; + buffer[k - 1] = + (packed_ASCII_code) (0x80 + (cur_chr % 0x40000) / 0x1000); + k++; + d--; + buffer[k - 1] = + (packed_ASCII_code) (0x80 + + ((cur_chr % 0x40000) % 0x1000) / 0x40); + k++; + d--; + buffer[k - 1] = + (packed_ASCII_code) (0x80 + + ((cur_chr % 0x40000) % 0x1000) % 0x40); + } + l = k; + ilimit = ilimit - d; + while (l <= ilimit) { + buffer[l] = buffer[l + d]; + l++; + } + *kk = k; + return true; + } + return false; +} + +@ todo: this is a function because it is still used from the converted pascal. + once that is gone, it can be a \#define again + +@c +boolean end_line_char_inactive(void) +{ + return ((end_line_char < 0) || (end_line_char > 127)); +} + +@ All of the easy branches of |get_next| have now been taken care of. + There is one more branch. + +@c +static next_line_retval next_line(void) +{ + boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */ + if (iname > 17) { + /* Read next line of file into |buffer|, or |goto restart| if the file has ended */ + incr(line); + first = istart; + if (!force_eof) { + if (iname <= 20) { + if (pseudo_input()) { /* not end of file */ + firm_up_the_line(); /* this sets |ilimit| */ + line_catcode_table = DEFAULT_CAT_TABLE; + if ((iname == 19) && (pseudo_lines(pseudo_files) == null)) + inhibit_eol = true; + } else if ((every_eof != null) && !eof_seen[iindex]) { + ilimit = first - 1; + eof_seen[iindex] = true; /* fake one empty line */ + if (iname != 19) + begin_token_list(every_eof, every_eof_text); + return next_line_restart; + } else { + force_eof = true; + } + } else { + if (iname == 21) { + if (luacstring_input()) { /* not end of strings */ + firm_up_the_line(); + line_catcode_table = (short) luacstring_cattable(); + line_partial = (signed char) luacstring_partial(); + if (luacstring_final_line() || line_partial + || line_catcode_table == NO_CAT_TABLE) + inhibit_eol = true; + if (!line_partial) + istate = new_line; + } else { + force_eof = true; + } + } else { + if (lua_input_ln(cur_file, 0, true)) { /* not end of file */ + firm_up_the_line(); /* this sets |ilimit| */ + line_catcode_table = DEFAULT_CAT_TABLE; + } else if ((every_eof != null) && (!eof_seen[iindex])) { + ilimit = first - 1; + eof_seen[iindex] = 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 (!((iname == 19) || (iname == 21))) + file_warning(); /* give warning for some unfinished groups and/or conditionals */ + if ((iname > 21) || (iname == 20)) { + if (tracefilenames) + print_char(')'); + open_parens--; +#if 0 + update_terminal(); /* show user that file has been read */ +#endif + } + force_eof = false; + if (iname == 21 || /* lua input */ + iname == 19) { /* \.{\\scantextokens} */ + end_file_reading(); + } else { + end_file_reading(); + check_outer_validity(); + } + return next_line_restart; + } + if (inhibit_eol || end_line_char_inactive()) + ilimit--; + else + buffer[ilimit] = (packed_ASCII_code) end_line_char; + first = ilimit + 1; + iloc = istart; /* 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()) + ilimit++; + if (ilimit == istart) { /* previous line was empty */ + tprint_nl("(Please type a command or say `\\end')"); + } + print_ln(); + first = istart; + prompt_input("*"); /* input on-line into |buffer| */ + ilimit = last; + if (end_line_char_inactive()) + ilimit--; + else + buffer[ilimit] = (packed_ASCII_code) end_line_char; + first = ilimit + 1; + iloc = istart; + } else { + fatal_error("*** (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. + +@c +static boolean get_next_tokenlist(void) +{ + register halfword t; /* a token */ + t = token_info(iloc); + iloc = token_link(iloc); /* 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 >= outer_call_cmd) { + if (cur_cmd == dont_expand_cmd) { /* Get the next token, suppressing expansion */ + /* The present point in the program is reached only when the |expand| + routine has inserted a special marker into the input. In this special + case, |token_info(iloc)| is known to be a control sequence token, and |token_link(iloc)=null|. + */ + cur_cs = token_info(iloc) - cs_token_flag; + iloc = null; + cur_cmd = eq_type(cur_cs); + if (cur_cmd > max_command_cmd) { + cur_cmd = relax_cmd; + cur_chr = no_expand_flag; + return true; + } + } else { + check_outer_validity(); + } + } + cur_chr = equiv(cur_cs); + } else { + cur_cmd = token_cmd(t); + cur_chr = token_chr(t); + switch (cur_cmd) { + case left_brace_cmd: + align_state++; + break; + case right_brace_cmd: + align_state--; + break; + case out_param_cmd: /* 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 + +@c +void get_next(void) +{ + RESTART: + cur_cs = 0; + if (istate != token_list) { + /* Input from external file, |goto restart| if no input found */ + if (!get_next_file()) + goto RESTART; + } else { + if (iloc == null) { + end_token_list(); + goto RESTART; /* list exhausted, resume previous level */ + } 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_cmd || cur_cmd == car_ret_cmd) && align_state == 0) { + insert_vj_template(); + goto RESTART; + } +} + + +@ Since |get_next| is used so frequently in \TeX, it is convenient +to define three related procedures that do a little more: + +\yskip\hang|get_token| not only sets |cur_cmd| and |cur_chr|, it +also sets |cur_tok|, a packed halfword version of the current token. + +\yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like +|get_token|, but if the current token turns out to be a user-defined +control sequence (i.e., a macro call), or a conditional, +or something like \.{\\topmark} or \.{\\expandafter} or \.{\\csname}, +it is eliminated from the input by beginning the expansion of the macro +or the evaluation of the conditional. + +\yskip\hang|x_token| is like |get_x_token| except that it assumes that +|get_next| has already been called. + +\yskip\noindent +In fact, these three procedures account for almost every use of |get_next|. + +No new control sequences will be defined except during a call of +|get_token|, or when \.{\\csname} compresses a token list, because +|no_new_control_sequence| is always |true| at other times. + +@c +void get_token(void) +{ /* sets |cur_cmd|, |cur_chr|, |cur_tok| */ + no_new_control_sequence = false; + get_token_lua(); + no_new_control_sequence = true; + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + +@ @c +void get_token_lua(void) +{ + register int callback_id; + callback_id = callback_defined(token_filter_callback); + if (callback_id > 0) { + while (istate == token_list && iloc == null && iindex != v_template) + end_token_list(); + /* there is some stuff we don't want to see inside the callback */ + if (!(istate == token_list && + ((nofilter == true) || (iindex == backed_up && iloc != null)))) { + do_get_token_lua(callback_id); + return; + } + } + get_next(); +} + + +@ changes the string |s| to a token list +@c +halfword string_to_toks(char *ss) +{ + halfword p; /* tail of the token list */ + halfword q; /* new node being added to the token list via |store_new_token| */ + halfword t; /* token being appended */ + char *s = ss, *se = ss + strlen(s); + p = temp_token_head; + set_token_link(p, null); + while (s < se) { + t = (halfword) str2uni((unsigned char *) s); + s += utf8_size(t); + if (t == ' ') + t = space_token; + else + t = other_token + t; + fast_store_new_token(t); + } + return token_link(temp_token_head); +} + +@ The token lists for macros and for other things like \.{\\mark} and \.{\\output} +and \.{\\write} are produced by a procedure called |scan_toks|. + +Before we get into the details of |scan_toks|, let's consider a much +simpler task, that of converting the current string into a token list. +The |str_toks| function does this; it classifies spaces as type |spacer| +and everything else as type |other_char|. + +The token list created by |str_toks| begins at |link(temp_token_head)| and ends +at the value |p| that is returned. (If |p=temp_token_head|, the list is empty.) + +|lua_str_toks| is almost identical, but it also escapes the three +symbols that |lua| considers special while scanning a literal string + +@c +static halfword lua_str_toks(lstring b) +{ /* changes the string |str_pool[b..pool_ptr]| to a token list */ + halfword p; /* tail of the token list */ + halfword q; /* new node being added to the token list via |store_new_token| */ + halfword t; /* token being appended */ + unsigned char *k; /* index into string */ + p = temp_token_head; + set_token_link(p, null); + k = (unsigned char *) b.s; + while (k < (unsigned char *) b.s + b.l) { + t = pool_to_unichar(k); + k += utf8_size(t); + if (t == ' ') { + t = space_token; + } else { + if ((t == '\\') || (t == '"') || (t == '\'') || (t == 10) + || (t == 13)) + fast_store_new_token(other_token + '\\'); + if (t == 10) + t = 'n'; + if (t == 13) + t = 'r'; + t = other_token + t; + } + fast_store_new_token(t); + } + return p; +} + + +@ Incidentally, the main reason for wanting |str_toks| is the function |the_toks|, +which has similar input/output characteristics. + +@c +halfword str_toks(lstring s) +{ /* changes the string |str_pool[b..pool_ptr]| to a token list */ + halfword p; /* tail of the token list */ + halfword q; /* new node being added to the token list via |store_new_token| */ + halfword t; /* token being appended */ + unsigned char *k, *l; /* index into string */ + p = temp_token_head; + set_token_link(p, null); + k = s.s; + l = k + s.l; + while (k < l) { + t = pool_to_unichar(k); + k += utf8_size(t); + if (t == ' ') + t = space_token; + else + t = other_token + t; + fast_store_new_token(t); + } + return p; +} + +@ Here's part of the |expand| subroutine that we are now ready to complete: +@c +void ins_the_toks(void) +{ + (void) the_toks(); + ins_list(token_link(temp_token_head)); +} + + +@ Here is a routine that print the result of a convert command, using + the argument |i|. It returns |false | if it does not know to print + the code |c|. The function exists because lua code and tex code can + both call it to convert something. + +@c +static boolean print_convert_string(halfword c, int i) +{ + int ff; /* for use with |set_ff| */ + boolean ret = true; + switch (c) { + case number_code: + print_int(i); + break; + case roman_numeral_code: + print_roman_int(i); + break; + case etex_code: + tprint(eTeX_version_string); + break; + case omega_code: + tprint(Omega_version_string); + break; + case aleph_code: + tprint(Aleph_version_string); + break; + case pdftex_revision_code: + tprint(pdftex_revision); + break; + case luatex_revision_code: + print(get_luatexrevision()); + break; + case luatex_date_code: + print_int(get_luatex_date_info()); + break; + case pdftex_banner_code: + tprint(pdftex_banner); + break; + case uniform_deviate_code: + print_int(unif_rand(i)); + break; + case normal_deviate_code: + print_int(norm_rand()); + break; + case format_name_code: + print(format_name); + break; + case job_name_code: + print(job_name); + break; + case font_name_code: + append_string((unsigned char *) font_name(i), + (unsigned) strlen(font_name(i))); + if (font_size(i) != font_dsize(i)) { + tprint(" at "); + print_scaled(font_size(i)); + tprint("pt"); + } + break; + case math_style_code: + print_math_style(); + break; + case pdf_font_name_code: + case pdf_font_objnum_code: + set_ff(i); + if (c == pdf_font_name_code) + print_int(obj_info(static_pdf, pdf_font_num(ff))); + else + print_int(pdf_font_num(ff)); + break; + case pdf_font_size_code: + print_scaled(font_size(i)); + tprint("pt"); + break; + case pdf_page_ref_code: + print_int(get_obj(static_pdf, obj_type_page, i, false)); + break; + case pdf_xform_name_code: + print_int(obj_info(static_pdf, i)); + break; + case Aleph_revision_code: + tprint(Aleph_revision); + break; + case Omega_revision_code: + tprint(Omega_revision); + break; + case eTeX_revision_code: + tprint(eTeX_revision); + break; + default: + ret = false; + break; + } + return ret; +} + +@ @c +int scan_lua_state(void) +{ + int sn = 0; + if (scan_keyword("name")) { + scan_pdf_ext_toks(); + sn = def_ref; + } + /* Parse optional lua state integer, or an instance name to be stored in |sn| */ + /* Get the next non-blank non-relax non-call token */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + back_input(); /* have to push it back, whatever it is */ + if (cur_cmd != left_brace_cmd) { + scan_register_num(); + if (get_lua_name(cur_val)) + sn = (cur_val - 65536); + } + return sn; +} + + +@ The procedure |conv_toks| uses |str_toks| to insert the token list +for |convert| functions into the scanner; `\.{\\outer}' control sequences +are allowed to follow `\.{\\string}' and `\.{\\meaning}'. + +The extra temp string |u| is needed because |pdf_scan_ext_toks| incorporates +any pending string in its output. In order to save such a pending string, +we have to create a temporary string that is destroyed immediately after. + +@c +void conv_toks(void) +{ + int old_setting; /* holds |selector| setting */ + halfword p, q; + int save_scanner_status; /* |scanner_status| upon entry */ + halfword save_def_ref; /* |def_ref| upon entry, important if inside `\.{\\message}' */ + halfword save_warning_index; + boolean bool; /* temp boolean */ + str_number s; /* first temp string */ + int sn; /* lua chunk name */ + str_number u = 0; /* third temp string, will become non-nil if a string is already being built */ + int i = 0; /* first temp integer */ + int j = 0; /* second temp integer */ + int c = cur_chr; /* desired type of conversion */ + str_number str; + /* Scan the argument for command |c| */ + switch (c) { + case number_code: + case roman_numeral_code: + scan_int(); + break; + case string_code: + case meaning_code: + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + break; + case etex_code: + case omega_code: + case aleph_code: + break; + case font_name_code: + scan_font_ident(); + break; + case pdftex_revision_code: + case luatex_revision_code: + case luatex_date_code: + case pdftex_banner_code: + break; + case pdf_font_name_code: + case pdf_font_objnum_code: + case pdf_font_size_code: + scan_font_ident(); + if (cur_val == null_font) + pdf_error("font", "invalid font identifier"); + if (c != pdf_font_size_code) { + pdf_check_vf(cur_val); + if (!font_used(cur_val)) + pdf_init_font(static_pdf, cur_val); + } + break; + case pdf_page_ref_code: + scan_int(); + if (cur_val <= 0) + pdf_error("pageref", "invalid page number"); + break; + case left_margin_kern_code: + case right_margin_kern_code: + scan_int(); + if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node)) + pdf_error("marginkern", "a non-empty hbox expected"); + break; + case pdf_xform_name_code: + scan_int(); + check_obj_exists(static_pdf, obj_type_xform, cur_val); + break; + case pdf_creation_date_code: + ins_list(string_to_toks(getcreationdate(static_pdf))); + return; + break; + case format_name_code: + case job_name_code: + if (job_name == 0) + open_log_file(); + break; + case pdf_colorstack_init_code: + bool = scan_keyword("page"); + if (scan_keyword("direct")) + cur_val = direct_always; + else if (scan_keyword("page")) + cur_val = direct_page; + else + cur_val = set_origin; + save_scanner_status = scanner_status; + save_warning_index = warning_index; + save_def_ref = def_ref; + u = save_cur_string(); + scan_pdf_ext_toks(); + s = tokens_to_string(def_ref); + delete_token_ref(def_ref); + def_ref = save_def_ref; + warning_index = save_warning_index; + scanner_status = save_scanner_status; + cur_val = newcolorstack(s, cur_val, bool); + flush_str(s); + cur_val_level = int_val_level; + if (cur_val < 0) { + print_err("Too many color stacks"); + help2("The number of color stacks is limited to 32768.", + "I'll use the default color stack 0 here."); + error(); + cur_val = 0; + restore_cur_string(u); + } + break; + case uniform_deviate_code: + scan_int(); + break; + case normal_deviate_code: + break; + case lua_escape_string_code: + { + lstring str; + int l = 0; + save_scanner_status = scanner_status; + save_def_ref = def_ref; + save_warning_index = warning_index; + scan_pdf_ext_toks(); + bool = in_lua_escape; + in_lua_escape = true; + str.s = (unsigned char *) tokenlist_to_cstring(def_ref, false, &l); + str.l = (unsigned) l; + in_lua_escape = bool; + delete_token_ref(def_ref); + def_ref = save_def_ref; + warning_index = save_warning_index; + scanner_status = save_scanner_status; + (void) lua_str_toks(str); + ins_list(token_link(temp_token_head)); + free(str.s); + return; + } + break; + case math_style_code: + break; + case expanded_code: + save_scanner_status = scanner_status; + save_warning_index = warning_index; + save_def_ref = def_ref; + u = save_cur_string(); + scan_pdf_ext_toks(); + warning_index = save_warning_index; + scanner_status = save_scanner_status; + ins_list(token_link(def_ref)); + def_ref = save_def_ref; + restore_cur_string(u); + return; + break; + case lua_code: + u = save_cur_string(); + save_scanner_status = scanner_status; + save_def_ref = def_ref; + save_warning_index = warning_index; + sn = scan_lua_state(); + scan_pdf_ext_toks(); + s = def_ref; + warning_index = save_warning_index; + def_ref = save_def_ref; + scanner_status = save_scanner_status; + luacstrings = 0; + luatokencall(s, sn); + delete_token_ref(s); + restore_cur_string(u); /* TODO: check this, was different */ + if (luacstrings > 0) + lua_string_start(); + return; + break; + case pdf_insert_ht_code: + scan_register_num(); + break; + case pdf_ximage_bbox_code: + scan_int(); + check_obj_exists(static_pdf, obj_type_ximage, cur_val); + i = obj_data_ptr(static_pdf, cur_val); + scan_int(); + j = cur_val; + if ((j < 1) || (j > 4)) + pdf_error("pdfximagebbox", "invalid parameter"); + break; + /* Cases of 'Scan the argument for command |c|' */ + case Aleph_revision_code: + case Omega_revision_code: + case eTeX_revision_code: + break; + default: + confusion("convert"); + break; + } + + old_setting = selector; + selector = new_string; + + /* Print the result of command |c| */ + if (!print_convert_string(c, cur_val)) { + switch (c) { + case string_code: + if (cur_cs != 0) + sprint_cs(cur_cs); + else + print(cur_chr); + break; + case meaning_code: + print_meaning(); + break; + case left_margin_kern_code: + p = list_ptr(box(cur_val)); + if ((p != null) && (!is_char_node(p)) && + (type(p) == glue_node) && (subtype(p) == left_skip_code + 1)) + p = vlink(p); + if ((p != null) && (!is_char_node(p)) && + (type(p) == margin_kern_node) && (subtype(p) == left_side)) + print_scaled(width(p)); + else + print_char('0'); + tprint("pt"); + break; + case right_margin_kern_code: + q = list_ptr(box(cur_val)); + p = null; + if (q != null) { + p = prev_rightmost(q, null); + if ((p != null) && (!is_char_node(p)) && (type(p) == glue_node) + && (subtype(p) == right_skip_code + 1)) + p = prev_rightmost(q, p); + } + if ((p != null) && (!is_char_node(p)) && + (type(p) == margin_kern_node) && (subtype(p) == right_side)) + print_scaled(width(p)); + else + print_char('0'); + tprint("pt"); + break; + case pdf_colorstack_init_code: + print_int(cur_val); + break; + case pdf_insert_ht_code: + i = cur_val; + p = page_ins_head; + while (i >= subtype(vlink(p))) + p = vlink(p); + if (subtype(p) == i) + print_scaled(height(p)); + else + print_char('0'); + tprint("pt"); + break; + case pdf_ximage_bbox_code: + if (is_pdf_image(i)) { + switch (j) { + case 1: + print_scaled(epdf_orig_x(i)); + break; + case 2: + print_scaled(epdf_orig_y(i)); + break; + case 3: + print_scaled(epdf_orig_x(i) + epdf_xsize(i)); + break; + case 4: + print_scaled(epdf_orig_y(i) + epdf_ysize(i)); + break; + } + } else { + print_scaled(0); + } + tprint("pt"); + break; + case pdf_creation_date_code: + case lua_escape_string_code: + case lua_code: + case expanded_code: + break; + default: + confusion("convert"); + break; + } + } + + selector = old_setting; + str = make_string(); + (void) str_toks(str_lstring(str)); + flush_str(str); + ins_list(token_link(temp_token_head)); +} + +@ This boolean is keeping track of the lua string escape state +@c +boolean in_lua_escape; + +@ probably not needed anymore +@c +boolean is_convert(halfword c) +{ + return (c == convert_cmd); +} + +str_number the_convert_string(halfword c, int i) +{ + int old_setting; /* saved |selector| setting */ + str_number ret = 0; + old_setting = selector; + selector = new_string; + if (print_convert_string(c, i)) { + ret = make_string(); + } else if (c == font_identifier_code) { + print_font_identifier(i); + ret = make_string(); + } + selector = old_setting; + return ret; +} + +@ Another way to create a token list is via the \.{\\read} command. The +sixteen files potentially usable for reading appear in the following +global variables. The value of |read_open[n]| will be |closed| if +stream number |n| has not been opened or if it has been fully read; +|just_open| if an \.{\\openin} but not a \.{\\read} has been done; +and |normal| if it is open and ready to read the next line. + +@c +FILE *read_file[16]; /* used for \.{\\read} */ +int read_open[17]; /* state of |read_file[n]| */ + +void initialize_read(void) +{ + int k; + for (k = 0; k <= 16; k++) + read_open[k] = closed; +} + +@ The |read_toks| procedure constructs a token list like that for any +macro definition, and makes |cur_val| point to it. Parameter |r| points +to the control sequence that will receive this token list. + +@c +void read_toks(int n, halfword r, halfword j) +{ + halfword p; /* tail of the token list */ + halfword q; /* new node being added to the token list via |store_new_token| */ + int s; /* saved value of |align_state| */ + int m; /* stream number */ + scanner_status = defining; + warning_index = r; + p = get_avail(); + def_ref = p; + set_token_ref_count(def_ref, 0); + p = def_ref; /* the reference count */ + store_new_token(end_match_token); + if ((n < 0) || (n > 15)) + m = 16; + else + m = n; + s = align_state; + align_state = 1000000; /* disable tab marks, etc. */ + do { + /* Input and store tokens from the next line of the file */ + begin_file_reading(); + iname = m + 1; + if (read_open[m] == closed) { + /* Input for \.{\\read} from the terminal */ + /* Here we input on-line into the |buffer| array, prompting the user explicitly + if |n>=0|. The value of |n| is set negative so that additional prompts + will not be given in the case of multi-line input. */ + if (interaction > nonstop_mode) { + if (n < 0) { + prompt_input(""); + } else { + wake_up_terminal(); + print_ln(); + sprint_cs(r); + prompt_input(" ="); + n = -1; + } + } else { + fatal_error + ("*** (cannot \\read from terminal in nonstop modes)"); + } + + } else if (read_open[m] == just_open) { + /* Input the first line of |read_file[m]| */ + /* The first line of a file must be treated specially, since |lua_input_ln| + must be told not to start with |get|. */ + if (lua_input_ln(read_file[m], (m + 1), false)) { + read_open[m] = normal; + } else { + lua_a_close_in(read_file[m], (m + 1)); + read_open[m] = closed; + } + + } else { + /* Input the next line of |read_file[m]| */ + /* An empty line is appended at the end of a |read_file|. */ + if (!lua_input_ln(read_file[m], (m + 1), true)) { + lua_a_close_in(read_file[m], (m + 1)); + read_open[m] = closed; + if (align_state != 1000000) { + runaway(); + print_err("File ended within \\read"); + help1("This \\read has unbalanced braces."); + align_state = 1000000; + error(); + } + } + + } + ilimit = last; + if (end_line_char_inactive()) + decr(ilimit); + else + buffer[ilimit] = (packed_ASCII_code) int_par(end_line_char_code); + first = ilimit + 1; + iloc = istart; + istate = new_line; + /* Handle \.{\\readline} and |goto done|; */ + if (j == 1) { + while (iloc <= ilimit) { /* current line not yet finished */ + cur_chr = buffer[iloc]; + incr(iloc); + if (cur_chr == ' ') + cur_tok = space_token; + else + cur_tok = cur_chr + other_token; + store_new_token(cur_tok); + } + } else { + while (1) { + get_token(); + if (cur_tok == 0) + break; /* |cur_cmd=cur_chr=0| will occur at the end of the line */ + if (align_state < 1000000) { /* unmatched `\.\}' aborts the line */ + do { + get_token(); + } while (cur_tok != 0); + align_state = 1000000; + break; + } + store_new_token(cur_tok); + } + } + end_file_reading(); + + } while (align_state != 1000000); + cur_val = def_ref; + scanner_status = normal; + align_state = s; +} + +@ @c +str_number tokens_to_string(halfword p) +{ /* return a string from tokens list */ + int old_setting; + if (selector == new_string) + pdf_error("tokens", + "tokens_to_string() called while selector = new_string"); + old_setting = selector; + selector = new_string; + show_token_list(token_link(p), null, -1); + selector = old_setting; + return make_string(); +} + +@ @c +#define make_room(a) \ + if ((unsigned)i+a+1>alloci) { \ + ret = xrealloc(ret,(alloci+64)); \ + alloci = alloci + 64; \ + } + + +#define append_i_byte(a) ret[i++] = (char)(a) + +#define Print_char(a) make_room(1); append_i_byte(a) + +#define Print_uchar(s) { \ + make_room(4); \ + if (s<=0x7F) { \ + append_i_byte(s); \ + } else if (s<=0x7FF) { \ + append_i_byte(0xC0 + (s / 0x40)); \ + append_i_byte(0x80 + (s % 0x40)); \ + } else if (s<=0xFFFF) { \ + append_i_byte(0xE0 + (s / 0x1000)); \ + append_i_byte(0x80 + ((s % 0x1000) / 0x40)); \ + append_i_byte(0x80 + ((s % 0x1000) % 0x40)); \ + } else if (s>=0x110000) { \ + append_i_byte(s-0x11000); \ + } else { \ + append_i_byte(0xF0 + (s / 0x40000)); \ + append_i_byte(0x80 + ((s % 0x40000) / 0x1000)); \ + append_i_byte(0x80 + (((s % 0x40000) % 0x1000) / 0x40)); \ + append_i_byte(0x80 + (((s % 0x40000) % 0x1000) % 0x40)); \ + } } + + +#define Print_esc(b) { \ + const char *v = b; \ + if (e>0 && e<STRING_OFFSET) { \ + Print_uchar (e); \ + } \ + make_room(strlen(v)); \ + while (*v) { append_i_byte(*v); v++; } \ + } + +#define is_cat_letter(a) \ + (get_char_cat_code(pool_to_unichar(str_string((a)))) == 11) + +@ the actual token conversion in this function is now functionally + equivalent to |show_token_list|, except that it always prints the + whole token list. + TODO: check whether this causes problems in the lua library. + +@c +char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) +{ + register int p, c, m; + int q; + int infop; + char *s, *sh; + int e = 0; + char *ret; + int match_chr = '#'; + int n = '0'; + unsigned alloci = 1024; + int i = 0; + p = pp; + if (p == null) { + if (siz != NULL) + *siz = 0; + return NULL; + } + ret = xmalloc(alloci); + p = token_link(p); /* skip refcount */ + if (p != null) { + e = int_par(escape_char_code); + } + while (p != null) { + if (p < (int) fix_mem_min || p > (int) fix_mem_end) { + Print_esc("CLOBBERED."); + break; + } + infop = token_info(p); + if (infop >= cs_token_flag) { + if (!(inhibit_par && infop == par_token)) { + q = infop - cs_token_flag; + if (q < hash_base) { + if (q == null_cs) { + Print_esc("csname"); + Print_esc("endcsname"); + } else { + Print_esc("IMPOSSIBLE."); + } + } else if ((q >= undefined_control_sequence) + && ((q <= eqtb_size) + || (q > eqtb_size + hash_extra))) { + Print_esc("IMPOSSIBLE."); + } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) { + Print_esc("NONEXISTENT."); + } else { + str_number txt = cs_text(q); + sh = makecstring(txt); + s = sh; + if (is_active_cs(txt)) { + s = s + 3; + while (*s) { + Print_char(*s); + s++; + } + } else { + Print_uchar(e); + while (*s) { + Print_char(*s); + s++; + } + if ((!single_letter(txt)) || is_cat_letter(txt)) { + Print_char(' '); + } + } + free(sh); + } + } + } else { + if (infop < 0) { + Print_esc("BAD."); + } else { + m = token_cmd(infop); + c = token_chr(infop); + switch (m) { + case left_brace_cmd: + case right_brace_cmd: + case math_shift_cmd: + case tab_mark_cmd: + case sup_mark_cmd: + case sub_mark_cmd: + case spacer_cmd: + case letter_cmd: + case other_char_cmd: + Print_uchar(c); + break; + case mac_param_cmd: + if (!in_lua_escape) + Print_uchar(c); + Print_uchar(c); + break; + case out_param_cmd: + Print_uchar(match_chr); + if (c <= 9) { + Print_char(c + '0'); + } else { + Print_char('!'); + goto EXIT; + } + break; + case match_cmd: + match_chr = c; + Print_uchar(c); + n++; + Print_char(n); + if (n > '9') + goto EXIT; + break; + case end_match_cmd: + if (c == 0) { + Print_char('-'); + Print_char('>'); + } + break; + default: + Print_esc("BAD."); + break; + } + } + } + p = token_link(p); + } + EXIT: + ret[i] = '\0'; + if (siz != NULL) + *siz = i; + return ret; +} + +@ @c +lstring *tokenlist_to_lstring(int pp, int inhibit_par) +{ + int siz; + lstring *ret = xmalloc(sizeof(lstring)); + ret->s = (unsigned char *) tokenlist_to_cstring(pp, inhibit_par, &siz); + ret->l = (size_t) siz; + return ret; +} + +@ @c +void free_lstring(lstring * ls) +{ + if (ls == NULL) + return; + if (ls->s != NULL) + free(ls->s); + free(ls); +} |