diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/web2c/luatexdir/tex | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex')
66 files changed, 52659 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/align.c b/Build/source/texk/web2c/luatexdir/tex/align.c new file mode 100644 index 00000000000..bb721856fac --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/align.c @@ -0,0 +1,1299 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +void fin_align(void); +void init_row(void); +void init_col(void); + +#define noDEBUG + +/*tex + + It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because + they cut across so many of the control structures of \TeX. Therefore the + present page is probably not the best place for a beginner to start reading + this program; it is better to master everything else first. + + Let us focus our thoughts on an example of what the input might be, in order + to get some idea about how the alignment miracle happens. The example doesn't + do anything useful, but it is sufficiently general to indicate all of the + special cases that must be dealt with; please do not be disturbed by its + apparent complexity and meaninglessness. + + \starttyping + \tabskip 2pt plus 3pt + \halign to 300pt{u1#v1& + \hskip 50pt \tabskip 1pt plus 1fil u2#v2& + \hskip 50pt u3#v3\cr + \hskip 25pt a1&\omit a2&\vrule\cr + \hskip 25pt \noalign\{\vskip 3pt} + \hskip 25pt b1\span b2\cr + \hskip 25pt \omit&c2\span\omit\cr} + \stoptyping + + Here's what happens: + + \startitemize + + \startitem + When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine + places the 300pt dimension onto the |save_stack|, and an + |align_group| code is placed above it. This will make it possible to + complete the alignment when the matching `\.\}' is found. + \stopitem + + \startitem + The preamble is scanned next. Macros in the preamble are not + expanded, except as part of a tabskip specification. For example, if + \.{u2} had been a macro in the preamble above, it would have been + expanded, since \TeX\ must look for `\.{minus...}' as part of the + tabskip glue. A ``preamble list'' is constructed based on the user's + preamble; in our case it contains the following seven items: + + \starttabulate + \NC \type{\glue 2pt plus 3pt} \NC the tabskip preceding column 1 \NC \NR + \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 1 \NC \NR + \NC \type{\glue 2pt plus 3pt} \NC the tabskip between columns 1 and 2 \NC \NR + \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 2 \NC \NR + \NC \type{\glue 1pt plus 1fil} \NC the tabskip between columns 2 and 3 \NC \NR + \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 3 \NC \NR + \NC \type{\glue 1pt plus 1fil} \NC the tabskip following column 3 \NC \NR + \stoptabulate + + These ``alignrecord'' entries have the same size as an |unset_node|, + since they will later be converted into such nodes. These alignrecord + nodes have no |depth| field; this is split into |u_part| and + |v_part|, and they point to token lists for the templates of the + alignment. For example, the |u_part| field in the first alignrecord + points to the token list `\.{u1}', i.e., the template preceding the + `\.\#' for column~1. Furthermore, They have a |span_ptr| instead of a + |node_attr| field, and these |span_ptr| fields are initially set to + the value |end_span|, for reasons explained below. + \stopitem + + \startitem + \TeX\ now looks at what follows the \.{\\cr} that ended the preamble. + It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back + to be read again, and the template `\.{u1}' is fed to the scanner. + Just before reading `\.{u1}', \TeX\ goes into restricted horizontal + mode. Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then + (when the {\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans + an |endv| token, indicating the end of a column. At this point an + |unset_node| is created, containing the contents of the current hlist + (i.e., `\.{u1a1v1}'). The natural width of this unset node replaces + the |width| field of the alignrecord for column~1; in general, the + alignrecords will record the maximum natural width that has occurred + so far in a given column. + \stopitem + + \startitem + Since `\.{\\omit}' follows the `\.\&', the templates for column~2 are + now bypassed. Again \TeX\ goes into restricted horizontal mode and + makes an |unset_node| from the resulting hlist; but this time the + hlist contains simply `\.{a2}'. The natural width of the new unset + box is remembered in the |width| field of the alignrecord for + column~2. + \stopitem + + \startitem + A third |unset_node| is created for column 3, using essentially the + mechanism that worked for column~1; this unset box contains + `\.{u3\\vrule v3}'. The vertical rule in this case has running + dimensions that will later extend to the height and depth of the + whole first row, since each |unset_node| in a row will eventually + inherit the height and depth of its enclosing box. + \stopitem + + \startitem + The first row has now ended; it is made into a single unset box + comprising the following seven items: + + \starttyping + \glue 2pt plus 3pt + \unsetbox for 1 column: u1a1v1 + \glue 2pt plus 3pt + \unsetbox for 1 column: a2 + \glue 1pt plus 1fil + \unsetbox for 1 column: u3\vrule v3 + \glue 1pt plus 1fil + \stoptyping + + The width of this unset row is unimportant, but it has the correct + height and depth, so the correct baselineskip glue will be computed + as the row is inserted into a vertical list. + \stopitem + + \startitem + Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends + additional material (in this case \.{\\vskip 3pt}) to the vertical + list. While processing this material, \TeX\ will be in internal + vertical mode, and |no_align_group| will be on |save_stack|. + \stopitem + + \startitem + The next row produces an unset box that looks like this: + + \starttyping + \glue 2pt plus 3pt + \unsetbox for 2 columns: u1b1v1u2b2v2 + \glue 1pt plus 1fil + \unsetbox for 1 column: {(empty)} + \glue 1pt plus 1fil + \stoptyping + + The natural width of the unset box that spans columns 1~and~2 is + stored in a ``span node,'' which we will explain later; the + |span_ptr| field of the alignrecord for column~1 now points to the + new span node, and the |span_ptr| of the span node points to + |end_span|. + \stopitem + + \startitem + + The final row produces the unset box + + \starttyping + \glue 2pt plus 3pt\cr + \unsetbox for 1 column: {(empty)} + \glue 2pt plus 3pt\cr + \unsetbox for 2 columns: u2c2v2 + \glue 1pt plus 1fil + \stoptyping + + A new span node is attached to the alignrecord for column 2. + \stopitem + + \startitem + The last step is to compute the true column widths and to change all + the unset boxes to hboxes, appending the whole works to the vertical + list that encloses the \.{\\halign}. The rules for deciding on the + final widths of each unset column box will be explained below. + \stopitem + + \stopitemize + + Note that as \.{\\halign} is being processed, we fearlessly give up control + to the rest of \TeX. At critical junctures, an alignment routine is called + upon to step in and do some little action, but most of the time these + routines just lurk in the background. It's something like post-hypnotic + suggestion. + + We have mentioned that alignrecords contain no |height| or |depth| fields. + Their |glue_sign| and |glue_order| are pre-empted as well, since it is + necessary to store information about what to do when a template ends. This + information is called the |extra_info| field. + +*/ + +/*tex The pointer to \<u_j> token list: */ + +#define u_part(A) vlink((A)+depth_offset) + +/*tex The pointer to \<v_j> token list */ + +#define v_part(A) vinfo((A)+depth_offset) + +/*tex A column spanning list */ + +#define span_ptr(A) vinfo((A)+1) + +/*tex Info to remember during template */ + +#define extra_info(A) vinfo((A)+list_offset) + +/*tex + + Alignments can occur within alignments, so a small stack is used to access + the alignrecord information. At each level we have a |preamble| pointer, + indicating the beginning of the preamble list; a |cur_align| pointer, + indicating the current position in the preamble list; a |cur_span| pointer, + indicating the value of |cur_align| at the beginning of a sequence of spanned + columns; a |cur_loop| pointer, indicating the tabskip glue before an + alignrecord that should be copied next if the current list is extended; and + the |align_state| variable, which indicates the nesting of braces so that + \.{\\cr} and \.{\\span} and tab marks are properly intercepted. There also + are pointers |cur_head| and |cur_tail| to the head and tail of a list of + adjustments being moved out from horizontal mode to vertical~mode, and alike + |cur_pre_head| and |cur_pre_tail| for pre-adjust lists. + + The current values of these nine quantities appear in global variables; when + they have to be pushed down, they are stored in 6-word nodes, and |align_ptr| + points to the topmost such node. + +*/ + +/*tex This could be in |texnodes.h| but it's documented here. */ + +/*tex The current preamble list: */ + +#define preamble vlink(align_head) + +/*tex The current position in the preamble list: */ + +pointer cur_align = null; + +/*tex The start of the currently spanned columns in the preamble list: */ + +pointer cur_span = null; + +/*tex A place to copy when extending a periodic preamble: */ + +pointer cur_loop = null; + +/*tex The most recently pushed-down alignment stack node: */ + +pointer align_ptr = null; + +/*tex Adjustment list pointers: */ + +pointer cur_head = null, cur_tail = null; + +/*tex Pre-adjustment list pointers: */ + +pointer cur_pre_head = null, cur_pre_tail = null; + +/*tex + + The |align_state| and |preamble| variables are initialized elsewhere. + + Alignment stack maintenance is handled by a pair of trivial routines called + |push_alignment| and |pop_alignment|. + + (HH:) It makes not much sense to add support for an \.{attr} keyword to + \.{\\halign} and \.{\\valign} because then we need to decide if we tag rows + or cells or both or come up with \.{cellattr} and \.{rowattr} and such. But + then it even makes sense to have explicit commands (in addition to the + seperator) to tags individual cells. Too muss hassle for now and the + advantages are not that large. + +*/ + +static void push_alignment(void) +{ + /*tex The new alignment stack node: */ + pointer p; + p = new_node(align_stack_node, 0); + vinfo(p + 1) = align_ptr; + vlink(p + 1) = cur_align; + vinfo(p + 2) = preamble; + vlink(p + 2) = cur_span; + vinfo(p + 3) = cur_loop; + vlink(p + 3) = align_state; + vinfo(p + 4) = cur_head; + vlink(p + 4) = cur_tail; + vinfo(p + 5) = cur_pre_head; + vlink(p + 5) = cur_pre_tail; + align_ptr = p; + cur_head = new_node(temp_node, 0); + cur_pre_head = new_node(temp_node, 0); +} + +static void pop_alignment(void) +{ + /*tex The top alignment stack node: */ + pointer p; + flush_node(cur_head); + flush_node(cur_pre_head); + p = align_ptr; + cur_pre_tail = vlink(p + 5); + cur_pre_head = vinfo(p + 5); + cur_tail = vlink(p + 4); + cur_head = vinfo(p + 4); + align_state = vlink(p + 3); + cur_loop = vinfo(p + 3); + cur_span = vlink(p + 2); + preamble = vinfo(p + 2); + cur_align = vlink(p + 1); + align_ptr = vinfo(p + 1); + flush_node(p); +} + +/*tex + + \TeX\ has eight procedures that govern alignments: |init_align| and + |fin_align| are used at the very beginning and the very end; |init_row| and + |fin_row| are used at the beginning and end of individual rows; |init_span| + is used at the beginning of a sequence of spanned columns (possibly involving + only one column); |init_col| and |fin_col| are used at the beginning and end + of individual columns; and |align_peek| is used after \.{\\cr} to see whether + the next item is \.{\\noalign}. + + We shall consider these routines in the order they are first used during the + course of a complete \.{\\halign}, namely |init_align|, |align_peek|, + |init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|. + + The preamble is copied directly, except that \.{\\tabskip} causes a change to + the tabskip glue, thereby possibly expanding macros that immediately follow + it. An appearance of \.{\\span} also causes such an expansion. + + Note that if the preamble contains `\.{\\global\\tabskip}', the + `\.{\\global}' token survives in the preamble and the `\.{\\tabskip}' defines + new tabskip glue (locally). + +*/ + +static void get_preamble_token(void) +{ + RESTART: + get_token(); + while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) { + /*tex This token will be expanded once. */ + get_token(); + if (cur_cmd > max_command_cmd) { + expand(); + get_token(); + } + } + if (cur_cmd == endv_cmd) + fatal_error("(interwoven alignment preambles are not allowed)"); + if ((cur_cmd == assign_glue_cmd) + && (cur_chr == glue_base + tab_skip_code)) { + scan_optional_equals(); + scan_glue(glue_val_level); + if (global_defs_par > 0) + geq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val); + else + eq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val); + goto RESTART; + } +} + +/*tex + + When \.{\\halign} or \.{\\valign} has been scanned in an appropriate mode, + \TeX\ calls |init_align|, whose task is to get everything off to a good + start. This mostly involves scanning the preamble and putting its information + into the preamble list. + +*/ + +void init_align(void) +{ + /*tex |warning_index| value for error messages */ + pointer save_cs_ptr; + /*tex for short-term temporary use */ + pointer p, r; + /*tex \.{\\halign} or \.{\\valign}, usually */ + save_cs_ptr = cur_cs; + push_alignment(); + /*tex enter a new alignment level */ + align_state = -1000000; + /*tex + + When \.{\\halign} is used as a displayed formula, there should be no + other pieces of mlists present. + + */ + if ((cur_list.mode_field == mmode) && ((cur_list.tail_field != cur_list.head_field) || (incompleat_noad_par != null))) { + const char *hlp[] = { + "Displays can use special alignments (like \\eqalignno)", + "only if nothing but the alignment itself is between $$'s.", + "So I've deleted the formulas that preceded this alignment.", + NULL + }; + tex_error("Improper \\halign inside $$'s", hlp); + flush_math(); + } + /*tex Enter a new semantic level. */ + push_nest(); + /*tex + + In vertical modes, |prev_depth| already has the correct value. But if we + are in |mmode| (displayed formula mode), we reach out to the enclosing + vertical mode for the |prev_depth| value that produces the correct + baseline calculations. + */ + if (cur_list.mode_field == mmode) { + cur_list.mode_field = -vmode; + prev_depth_par = nest[nest_ptr - 2].prev_depth_field; + } else if (cur_list.mode_field > 0) { + cur_list.mode_field = -(cur_list.mode_field); + } + scan_spec(align_group); + /*tex Scan the preamble. */ + preamble = null; + cur_align = align_head; + cur_loop = null; + scanner_status = aligning; + warning_index = save_cs_ptr; + align_state = -1000000; + /*tex At this point, |cur_cmd=left_brace|. */ + while (true) { + /*tex Append the current tabskip glue to the preamble list. */ + r = new_param_glue(tab_skip_code); + vlink(cur_align) = r; + cur_align = vlink(cur_align); + if (cur_cmd == car_ret_cmd) { + /*tex \.{\\cr} ends the preamble. */ + break; + } + /*tex + + Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| and + then scan the template \<u_j>, putting the resulting token list in + |hold_token_head|. Spaces are eliminated from the beginning of a + template. + + */ + p = hold_token_head; + token_link(p) = null; + while (1) { + get_preamble_token(); + if (cur_cmd == mac_param_cmd) + break; + if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) + && (align_state == -1000000)) { + if ((p == hold_token_head) && (cur_loop == null) && (cur_cmd == tab_mark_cmd)) { + cur_loop = cur_align; + } else { + const char *hlp[] = { + "There should be exactly one # between &'s, when an", + "\\halign or \\valign is being set up. In this case you had", + "none, so I've put one in; maybe that will work.", + NULL + }; + back_input(); + tex_error("Missing # inserted in alignment preamble", hlp); + break; + } + } else if ((cur_cmd != spacer_cmd) || (p != hold_token_head)) { + r = get_avail(); + token_link(p) = r; + p = token_link(p); + token_info(p) = cur_tok; + } + } + r = new_node(align_record_node, 0); + vlink(cur_align) = r; + /*tex A new align record: */ + cur_align = vlink(cur_align); + span_ptr(cur_align) = end_span; + width(cur_align) = null_flag; + u_part(cur_align) = token_link(hold_token_head); + /*tex + + Scan the template \<v_j>, putting the resulting token list in + |hold_token_head|. + + */ + p = hold_token_head; + token_link(p) = null; + while (1) { + CONTINUE: + get_preamble_token(); + if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) && (align_state == -1000000)) + break; + if (cur_cmd == mac_param_cmd) { + const char *hlp[] = { + "There should be exactly one # between &'s, when an", + "\\halign or \\valign is being set up. In this case you had", + "more than one, so I'm ignoring all but the first.", + NULL + }; + tex_error("Only one # is allowed per tab", hlp); + goto CONTINUE; + } + r = get_avail(); + token_link(p) = r; + p = token_link(p); + token_info(p) = cur_tok; + } + r = get_avail(); + token_link(p) = r; + p = token_link(p); + /*tex Put \.{\\endtemplate} at the end: */ + token_info(p) = end_template_token; + v_part(cur_align) = token_link(hold_token_head); + } + scanner_status = normal; + new_save_level(align_group); + if (every_cr_par != null) + begin_token_list(every_cr_par, every_cr_text); + /*tex Look for \.{\\noalign} or \.{\\omit}. */ + align_peek(); +} + +/*tex + + The tricky part about alignments is getting the templates into the scanner at + the right time, and recovering control when a row or column is finished. + + We usually begin a row after each \.{\\cr} has been sensed, unless that + \.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates + the alignment. The |align_peek| routine is used to look ahead and do the + right thing; it either gets a new row started, or gets a \.{\\noalign} + started, or finishes off the alignment. + +*/ + +void align_peek(void) +{ + RESTART: + align_state = 1000000; + do { + get_x_or_protected(); + } while (cur_cmd == spacer_cmd); + if (cur_cmd == no_align_cmd) { + scan_left_brace(); + new_save_level(no_align_group); + if (cur_list.mode_field == -vmode) + normal_paragraph(); + } else if (cur_cmd == right_brace_cmd) { + fin_align(); + } else if ((cur_cmd == car_ret_cmd) && (cur_chr == cr_cr_code)) { + /*tex Ignore \.{\\crcr}. */ + goto RESTART; + } else { + /*tex Start a new row. */ + init_row(); + /*tex Start a new column and replace what we peeked at. */ + init_col(); + } +} + + +/*tex + + The parameter to |init_span| is a pointer to the alignrecord where the next + column or group of columns will begin. A new semantic level is entered, so + that the columns will generate a list for subsequent packaging. + +*/ + +static void init_span(pointer p) +{ + push_nest(); + if (cur_list.mode_field == -hmode) { + space_factor_par = 1000; + } else { + prev_depth_par = ignore_depth; + normal_paragraph(); + } + cur_span = p; +} + +/*tex + + To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'), + we enter a new semantic level, copy the first tabskip glue, and change from + internal vertical mode to restricted horizontal mode or vice versa. The + |space_factor| and |prev_depth| are not used on this semantic level, but we + clear them to zero just to be tidy. + +*/ + +void init_row(void) +{ + push_nest(); + cur_list.mode_field = (-hmode - vmode) - cur_list.mode_field; + if (cur_list.mode_field == -hmode) + space_factor_par = 0; + else + prev_depth_par = 0; + tail_append(new_glue(preamble)); + subtype(cur_list.tail_field) = tab_skip_code + 1; + cur_align = vlink(preamble); + cur_tail = cur_head; + cur_pre_tail = cur_pre_head; + init_span(cur_align); +} + +/*tex + + When a column begins, we assume that |cur_cmd| is either |omit| or else the + current token should be put back into the input until the \<u_j> template has + been scanned. (Note that |cur_cmd| might be |tab_mark| or |car_ret|.) We also + assume that |align_state| is approximately 1000000 at this time. We remain in + the same mode, and start the template if it is called for. + +*/ + +void init_col(void) +{ + extra_info(cur_align) = cur_cmd; + if (cur_cmd == omit_cmd) + align_state = 0; + else { + back_input(); + begin_token_list(u_part(cur_align), u_template); + } + /*tex now |align_state=1000000| */ +} + + +/*tex + + The scanner sets |align_state| to zero when the \<u_j> template ends. When a + subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|, + the scanner activates the following code, which fires up the \<v_j> template. + We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|, + |span_code|, or a character code, depending on how the column text has ended. + + This part of the program had better not be activated when the preamble to + another alignment is being scanned, or when no alignment preamble is active. + +*/ + +void insert_vj_template(void) +{ + if ((scanner_status == aligning) || (cur_align == null)) + fatal_error("(interwoven alignment preambles are not allowed)"); + cur_cmd = extra_info(cur_align); + extra_info(cur_align) = cur_chr; + if (cur_cmd == omit_cmd) + begin_token_list(omit_template, v_template); + else + begin_token_list(v_part(cur_align), v_template); + align_state = 1000000; +} + +/*tex Determine the stretch order */ + +#define determine_stretch_order() do { \ + if (total_stretch[filll]!= 0) o = filll; \ + else if (total_stretch[fill] != 0) o = fill; \ + else if (total_stretch[fil] != 0) o = fil; \ + else if (total_stretch[sfi] != 0) o = sfi; \ + else o=normal; \ +} while (0) + +/*tex Determine the shrink order */ + +#define determine_shrink_order() do { \ + if (total_shrink[filll] != 0) o = filll; \ + else if (total_shrink[fill] != 0) o = fill; \ + else if (total_shrink[fil] != 0) o = fil; \ + else if (total_shrink[sfi] != 0) o = sfi; \ + else o=normal; \ +} while (0) + +/*tex + + When the |endv| command at the end of a \<v_j> template comes through the + scanner, things really start to happen; and it is the |fin_col| routine that + makes them happen. This routine returns |true| if a row as well as a column + has been finished. + +*/ + +boolean fin_col(void) +{ + /*tex the alignrecord after the current one */ + pointer p; + /*tex temporary pointers for list manipulation */ + pointer q, r; + /*tex a new span node */ + pointer s; + /*tex a new unset box */ + pointer u; + /*tex natural width */ + scaled w; + /*tex order of infinity */ + unsigned char o; + /*tex span counter */ + halfword n; + if (cur_align == null) + confusion("endv"); + q = vlink(cur_align); + if (q == null) + confusion("endv"); + if (align_state < 500000) + fatal_error("(interwoven alignment preambles are not allowed)"); + p = vlink(q); + /*tex If the preamble list has been traversed, check that the row has ended. */ + if ((p == null) && (extra_info(cur_align) < cr_code)) { + if (cur_loop != null) { + /*tex Lengthen the preamble periodically: */ + r = new_node(align_record_node, 0); + vlink(q) = r; + /*tex A new align record: */ + p = vlink(q); + span_ptr(p) = end_span; + width(p) = null_flag; + cur_loop = vlink(cur_loop); + /*tex Copy the templates from node |cur_loop| into node |p|. */ + q = hold_token_head; + r = u_part(cur_loop); + while (r != null) { + s = get_avail(); + token_link(q) = s; + q = token_link(q); + token_info(q) = token_info(r); + r = token_link(r); + } + token_link(q) = null; + u_part(p) = token_link(hold_token_head); + q = hold_token_head; + r = v_part(cur_loop); + while (r != null) { + s = get_avail(); + token_link(q) = s; + q = token_link(q); + token_info(q) = token_info(r); + r = token_link(r); + } + token_link(q) = null; + v_part(p) = token_link(hold_token_head); + cur_loop = vlink(cur_loop); + r = new_glue(cur_loop); + vlink(p) = r; + } else { + const char *hlp[] = { + "You have given more \\span or & marks than there were", + "in the preamble to the \\halign or \\valign now in progress.", + "So I'll assume that you meant to type \\cr instead.", + NULL + }; + extra_info(cur_align) = cr_code; + tex_error("Extra alignment tab has been changed to \\cr", hlp); + } + } + if (extra_info(cur_align) != span_code) { + unsave(); + new_save_level(align_group); + /*tex Package an unset box for the current column and record its width. */ + if (cur_list.mode_field == -hmode) { + adjust_tail = cur_tail; + pre_adjust_tail = cur_pre_tail; + u = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, additional, align_set_group, -1, 0, 0); + w = width(u); + cur_tail = adjust_tail; + adjust_tail = null; + cur_pre_tail = pre_adjust_tail; + pre_adjust_tail = null; + } else { + u = filtered_vpackage(vlink(cur_list.head_field), 0, additional, 0, align_set_group, -1, 0, 0); + w = height(u); + } + /*tex This represents a span count of 1: */ + n = min_quarterword; + if (cur_span != cur_align) { + /*tex Update width entry for spanned columns. */ + q = cur_span; + do { + incr(n); + q = vlink(vlink(q)); + } while (q != cur_align); + if (n > max_quarterword) { + /*tex This can happen, but won't. */ + confusion("too many spans"); + } + q = cur_span; + while (span_span(span_ptr(q)) < n) { + q = span_ptr(q); + } + if (span_span(span_ptr(q)) > n) { + s = new_span_node(span_ptr(q), n, w); + span_ptr(q) = s; + } else if (width(span_ptr(q)) < w) { + width(span_ptr(q)) = w; + } + } else if (w > width(cur_align)) { + width(cur_align) = w; + } + type(u) = unset_node; + span_count(u) = (quarterword) n; + determine_stretch_order(); + glue_order(u) = o; + glue_stretch(u) = total_stretch[o]; + determine_shrink_order(); + glue_sign(u) = o; + glue_shrink(u) = total_shrink[o]; + pop_nest(); + vlink(cur_list.tail_field) = u; + cur_list.tail_field = u; + /*tex Copy the tabskip glue between columns. */ + tail_append(new_glue(vlink(cur_align))); + subtype(cur_list.tail_field) = tab_skip_code + 1; + if (extra_info(cur_align) >= cr_code) { + return true; + } + init_span(p); + } + align_state = 1000000; + do { + get_x_or_protected(); + } while (cur_cmd == spacer_cmd); + cur_align = p; + init_col(); + return false; +} + +/*tex + + A span node is a 3-word record containing |width|, |span_span|, and + |span_ptr| fields. The |span_span| field indicates the number of spanned + columns; the |span_ptr| field points to a span node for the same starting + column, having a greater extent of spanning, or to |end_span|, which has the + largest possible |span_span| field; the |width| field holds the largest + natural width corresponding to a particular set of spanned columns. + + A list of the maximum widths so far, for spanned columns starting at a given + column, begins with the |span_ptr| field of the alignrecord for that column. + The code has to make sure that there is room for |span_ptr| in both the + alignrecord and the span nodes, which is why |span_ptr| replaces |node_attr|. + + The |new_span_node| function is defined in |texnodes.c|. + +*/ + +/*tex This is normally |alink|: */ + +#ifndef span_span +# define span_span(A) vlink((A)+1) +#endif + +/*tex + + At the end of a row, we append an unset box to the current vlist (for + \.{\\halign}) or the current hlist (for \.{\\valign}). This unset box + contains the unset boxes for the columns, separated by the tabskip glue. + Everything will be set later. + +*/ + +void fin_row(void) +{ + /*tex The new unset box: */ + pointer p; + if (cur_list.mode_field == -hmode) { + p = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, + additional, fin_row_group, -1, 0, 0); + pop_nest(); + if (cur_pre_head != cur_pre_tail) + append_list(cur_pre_head, cur_pre_tail); + append_to_vlist(p,lua_key_index(alignment)); + if (cur_head != cur_tail) + append_list(cur_head, cur_tail); + } else { + p = filtered_vpackage(vlink(cur_list.head_field), + 0, additional, max_depth_par, fin_row_group, -1, 0, 0); + pop_nest(); + vlink(cur_list.tail_field) = p; + cur_list.tail_field = p; + space_factor_par = 1000; + } + type(p) = unset_node; + glue_stretch(p) = 0; + if (every_cr_par != null) + begin_token_list(every_cr_par, every_cr_text); + align_peek(); + /*tex Note that |glue_shrink(p)=0| since |glue_shrink==shift_amount|. */ +} + +/*tex + + Finally, we will reach the end of the alignment, and we can breathe a sigh of + relief that memory hasn't overflowed. All the unset boxes will now be set so + that the columns line up, taking due account of spanned columns. + +*/ + +void fin_align(void) +{ + /*tex registers for the list operations */ + pointer p, q, r, s, u, rr; + /*tex width of column */ + scaled t, w; + /*tex shift offset for unset boxes */ + scaled o; + /*tex matching span amount */ + halfword n; + /*tex temporary storage for |overfull_rule| */ + scaled rule_save; + /*tex temporary storage for |prev_depth| */ + halfword pd; + /*tex temporary storage for |new_glue| */ + halfword ng; + /*tex The |align_group| was for individual entries: */ + if (cur_group != align_group) + confusion("align1"); + unsave(); + /*tex The |align_group| was for the whole alignment: */ + if (cur_group != align_group) + confusion("align0"); + unsave(); + if (nest[nest_ptr - 1].mode_field == mmode) { + o = display_indent_par; + } else { + o = 0; + } + /*tex + + Go through the preamble list, determining the column widths and + changing the alignrecords to dummy unset boxes. + + It's time now to dismantle the preamble list and to compute the + column widths. Let $w_{ij}$ be the maximum of the natural widths of + all entries that span columns $i$ through $j$, inclusive. The + alignrecord for column~$i$ contains $w_{ii}$ in its |width| field, + and there is also a linked list of the nonzero $w_{ij}$ for + increasing $j$, accessible via the |info| field; these span nodes + contain the value $j-i+|min_quarterword|$ in their |link| fields. The + values of $w_{ii}$ were initialized to |null_flag|, which we regard + as $-\infty$. + + The final column widths are defined by the formula $$w_j=\max_{1\L + i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$ where $t_k$ is + the natural width of the tabskip glue between columns $k$ and~$k+1$. + However, if $w_{ij}=-\infty$ for all |i| in the range |1<=i<=j| + (i.e., if every entry that involved column~|j| also involved + column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after + column~|j|. + + \TeX\ computes these values by using the following scheme: First + $w_1=w_{11}$. Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, + for all $j>1$. Then $w_2=w_{22}$. Then replace $w_{3j}$ by + $\max(w_{3j},w_{2j}-t_2-w_2)$ for all $j>2$; and so on. If any $w_j$ + turns out to be $-\infty$, its value is changed to zero and so is the + next tabskip. + + */ + q = vlink(preamble); + do { + flush_list(u_part(q)); + flush_list(v_part(q)); + p = vlink(vlink(q)); + if (width(q) == null_flag) { + /*tex Nullify |width(q)| and the tabskip glue following this column. */ + width(q) = 0; + r = vlink(q); + reset_glue_to_zero(r); + } + if (span_ptr(q) != end_span) { + /*tex + + Merge the widths in the span nodes of |q| with those of |p|, + destroying the span nodes of |q|. + + Merging of two span-node lists is a typical exercise in the + manipulation of linearly linked data structures. The essential + invariant in the following |repeat| loop is that we want to + dispense with node |r|, in |q|'s list, and |u| is its successor; + all nodes of |p|'s list up to and including |s| have been + processed, and the successor of |s| matches |r| or precedes |r| + or follows |r|, according as |link(r)=n| or |link(r)>n| or + |link(r)<n|. + + */ + t = width(q) + width(vlink(q)); + r = span_ptr(q); + s = end_span; + span_ptr(s) = p; + n = min_quarterword + 1; + do { + width(r) = width(r) - t; + u = span_ptr(r); + while (span_span(r) > n) { + s = span_ptr(s); + n = span_span(span_ptr(s)) + 1; + } + if (span_span(r) < n) { + span_ptr(r) = span_ptr(s); + span_ptr(s) = r; + decr(span_span(r)); + s = r; + } else { + if (width(r) > width(span_ptr(s))) + width(span_ptr(s)) = width(r); + flush_node(r); + } + r = u; + } while (r != end_span); + } + type(q) = unset_node; + span_count(q) = min_quarterword; + height(q) = 0; + depth(q) = 0; + glue_order(q) = normal; + glue_sign(q) = normal; + glue_stretch(q) = 0; + glue_shrink(q) = 0; + q = p; + } while (q != null); + /*tex + + Package the preamble list, to determine the actual tabskip glue amounts, + and let |p| point to this prototype box. + + Now the preamble list has been converted to a list of alternating unset + boxes and tabskip glue, where the box widths are equal to the final + column sizes. In case of \.{\\valign}, we change the widths to heights, + so that a correct error message will be produced if the alignment is + overfull or underfull. + + */ + decr(save_ptr); + pack_begin_line = -cur_list.ml_field; + if (cur_list.mode_field == -vmode) { + rule_save = overfull_rule_par; + /*tex Prevent the rule from being packaged. */ + overfull_rule_par = 0; + p = hpack(preamble, saved_value(0), saved_level(0), -1); + overfull_rule_par = rule_save; + } else { + q = vlink(preamble); + do { + height(q) = width(q); + width(q) = 0; + q = vlink(vlink(q)); + } while (q != null); + p = filtered_vpackage(preamble, + saved_value(0), saved_level(0), max_depth_par, preamble_group, -1, 0, 0); + q = vlink(preamble); + do { + width(q) = height(q); + height(q) = 0; + q = vlink(vlink(q)); + } while (q != null); + } + pack_begin_line = 0; + /*tex Set the glue in all the unset boxes of the current list. */ + q = vlink(cur_list.head_field); + s = cur_list.head_field; + while (q != null) { + if (!is_char_node(q)) { + if (type(q) == unset_node) { + /*tex + + We set the unset box |q| and the unset boxes in it. The unset + box |q| represents a row that contains one or more unset + boxes, depending on how soon \.{\\cr} occurred in that row. + + */ + if (cur_list.mode_field == -vmode) { + type(q) = hlist_node; + subtype(q) = align_row_list; + width(q) = width(p); + } else { + type(q) = vlist_node; + subtype(q) = align_row_list; + height(q) = height(p); + } + glue_order(q) = glue_order(p); + glue_sign(q) = glue_sign(p); + glue_set(q) = glue_set(p); + shift_amount(q) = o; + r = vlink(list_ptr(q)); + if (type(r) != unset_node) { + formatted_error("alignment","bad box"); + } + s = vlink(list_ptr(p)); + do { + /*tex + + We set the glue in node |r| and change it from an unset + node. A box made from spanned columns will be followed by + tabskip glue nodes and by empty boxes as if there were no + spanning. This permits perfect alignment of subsequent + entries, and it prevents values that depend on floating + point arithmetic from entering into the dimensions of any + boxes. + + */ + n = span_count(r); + t = width(s); + w = t; + u = hold_head; + while (n > min_quarterword) { + decr(n); + /*tex + + Append tabskip glue and an empty box to list |u|, and + update |s| and |t| as the prototype nodes are passed. + + */ + s = vlink(s); + ng = new_glue(s); + vlink(u) = ng; + u = vlink(u); + subtype(u) = tab_skip_code + 1; + t = t + width(s); + if (glue_sign(p) == stretching) { + if (stretch_order(s) == glue_order(p)) + t = t + round(float_cast(glue_set(p)) * float_cast(stretch(s))); + } else if (glue_sign(p) == shrinking) { + if (shrink_order(s) == glue_order(p)) + t = t - round(float_cast(glue_set(p)) * float_cast(shrink(s))); + } + s = vlink(s); + rr = new_null_box(); + vlink(u) = rr; + u = vlink(u); + t = t + width(s); + subtype(u) = align_cell_list; + if (cur_list.mode_field == -vmode) { + width(u) = width(s); + } else { + type(u) = vlist_node; + height(u) = width(s); + } + } + if (cur_list.mode_field == -vmode) { + /*tex + + Make the unset node |r| into an |hlist_node| of width + |w|, setting the glue as if the width were |t|. + + */ + height(r) = height(q); + depth(r) = depth(q); + if (t == width(r)) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } else if (t > width(r)) { + glue_sign(r) = stretching; + if (glue_stretch(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else + glue_set(r) = unfloat((double) (t - width(r)) / glue_stretch(r)); + } else { + glue_order(r) = glue_sign(r); + glue_sign(r) = shrinking; + if (glue_shrink(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else if ((glue_order(r) == normal) && (width(r) - t > glue_shrink(r))) + set_glue_ratio_one(glue_set(r)); + else + glue_set(r) = unfloat((double) (width(r) - t) / glue_shrink(r)); + } + width(r) = w; + type(r) = hlist_node; + subtype(r) = align_cell_list; + + } else { + /*tex + + Make the unset node |r| into a |vlist_node| of height + |w|, setting the glue as if the height were |t|. + + */ + width(r) = width(q); + if (t == height(r)) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } else if (t > height(r)) { + glue_sign(r) = stretching; + if (glue_stretch(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else + glue_set(r) = unfloat((t - height(r)) / glue_stretch(r)); + } else { + glue_order(r) = glue_sign(r); + glue_sign(r) = shrinking; + if (glue_shrink(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else if ((glue_order(r) == normal) && (height(r) - t > glue_shrink(r))) + set_glue_ratio_one(glue_set(r)); + else + glue_set(r) = unfloat((height(r) - t) / glue_shrink(r)); + } + height(r) = w; + type(r) = vlist_node; + subtype(r) = align_cell_list; + } + shift_amount(r) = 0; + if (u != hold_head) { + /*tex Append blank boxes to account for spanned nodes. */ + vlink(u) = vlink(r); + vlink(r) = vlink(hold_head); + r = u; + } + + r = vlink(vlink(r)); + s = vlink(vlink(s)); + } while (r != null); + + } else if (type(q) == rule_node) { + /*tex + + Make the running dimensions in rule |q| extend to the + boundaries of the alignment. + + */ + if (is_running(width(q))) + width(q) = width(p); + if (is_running(height(q))) + height(q) = height(p); + if (is_running(depth(q))) + depth(q) = depth(p); + if (o != 0) { + r = vlink(q); + vlink(q) = null; + q = hpack(q, 0, additional, -1); + shift_amount(q) = o; + subtype(q) = align_cell_list; + vlink(q) = r; + vlink(s) = q; + } + } + } + s = q; + q = vlink(q); + } + flush_node_list(p); + pop_alignment(); + /*tex + + We now have a completed alignment, in the list that starts at + |cur_list.head_field| and ends at |cur_list.tail_field|. This list will + be merged with the one that encloses it. (In case the enclosing mode is + |mmode|, for displayed formulas, we will need to insert glue before and + after the display; that part of the program will be deferred until we're + more familiar with such operations.) + + */ + pd = prev_depth_par; + p = vlink(cur_list.head_field); + q = cur_list.tail_field; + pop_nest(); + if (cur_list.mode_field == mmode) { + finish_display_alignment(p, q, pd); + } else { + prev_depth_par = pd; + vlink(cur_list.tail_field) = p; + if (p != null) + cur_list.tail_field = q; + if (cur_list.mode_field == vmode) { + if (!output_active) + lua_node_filter_s(buildpage_filter_callback,lua_key_index(alignment)); + build_page(); + } + } +} + +/*tex + + The token list |omit_template| just referred to is a constant token list that + contains the special control sequence \.{\\endtemplate} only. + +*/ + +void initialize_alignments(void) +{ + token_info(omit_template) = end_template_token; + span_span(end_span) = max_quarterword + 1; + span_ptr(end_span) = null; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/align.h b/Build/source/texk/web2c/luatexdir/tex/align.h new file mode 100644 index 00000000000..a93539f0dc5 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/align.h @@ -0,0 +1,54 @@ +/* align.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef ALIGN_H +# define ALIGN_H 1 + +/* We enter `\.{\\span}' into |eqtb| with |tab_mark| as its command code, +and with |span_code| as the command modifier. This makes \TeX\ interpret it +essentially the same as an alignment delimiter like `\.\&', yet it is +recognizably different when we need to distinguish it from a normal delimiter. +It also turns out to be useful to give a special |cr_code| to `\.{\\cr}', +and an even larger |cr_cr_code| to `\.{\\crcr}'. + +The end of a template is represented by two ``frozen'' control sequences +called \.{\\endtemplate}. The first has the command code |end_template|, which +is |>outer_call|, so it will not easily disappear in the presence of errors. +The |get_x_token| routine converts the first into the second, which has |endv| +as its command code. +*/ + +# define tab_mark_cmd_code 1114113 /* {|biggest_char+2|} */ +# define span_code 1114114 /* {|biggest_char+3|} */ +# define cr_code (span_code+1) /* distinct from |span_code| and from any character */ +# define cr_cr_code (cr_code+1) /* this distinguishes \.{\\crcr} from \.{\\cr} */ + + +extern void init_align(void); +extern void initialize_alignments(void); + +extern boolean fin_col(void); +extern void fin_row(void); + +extern void align_peek(void); +extern void insert_vj_template(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/arithmetic.c b/Build/source/texk/web2c/luatexdir/tex/arithmetic.c new file mode 100644 index 00000000000..2782c253400 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/arithmetic.c @@ -0,0 +1,815 @@ +/* + +arithmetic.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +The principal computations performed by \TeX\ are done entirely in terms of +integers less than $2^{31}$ in magnitude; and divisions are done only when both +dividend and divisor are nonnegative. Thus, the arithmetic specified in this +program can be carried out in exactly the same way on a wide variety of +computers, including some small ones. Why? Because the arithmetic calculations +need to be spelled out precisely in order to guarantee that \TeX\ will produce +identical output on different machines. If some quantities were rounded +differently in different implementations, we would find that line breaks and even +page breaks might occur in different places. Hence the arithmetic of \TeX\ has +been designed with care, and systems that claim to be implementations of \TeX82 +should follow precisely the @:TeX82}{\TeX82@> calculations as they appear in the +present program. + +Actually there are three places where \TeX\ uses |div| with a possibly negative +numerator. These are harmless; see |div| in the index. Also if the user sets the +\.{\\time} or the \.{\\year} to a negative value, some diagnostic information +will involve negative-numerator division. The same remarks apply for |mod| as +well as for |div|. + +Here is a routine that calculates half of an integer, using an unambiguous +convention with respect to signed odd numbers. + +*/ + +int half(int x) +{ + if (odd(x)) + return ((x + 1) / 2); + else + return (x / 2); +} + +/*tex + +The following function is used to create a scaled integer from a given decimal +fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is +given in |dig[i]|, and the calculation produces a correctly rounded result. + +*/ + +scaled round_decimals(int k) +{ + int a = 0; + while (k-- > 0) { + a = (a + dig[k] * two) / 10; + } + return ((a + 1) / 2); +} + +/*tex + +Conversely, here is a procedure analogous to |print_int|. If the output of this +procedure is subsequently read by \TeX\ and converted by the |round_decimals| +routine above, it turns out that the original value will be reproduced exactly; +the ``simplest'' such decimal number is output, but there is always at least one +digit following the decimal point. + +The invariant relation in the \&{repeat} loop is that a sequence of decimal +digits yet to be printed will yield the original number if and only if they form +a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$. We can stop if and only +if $f=0$ satisfies this condition; the loop will terminate before $s$ can +possibly become zero. + +The next one prints a scaled real, rounded to five digits. + +*/ + +void print_scaled(scaled s) +{ + /*tex The amount of allowable inaccuracy: */ + scaled delta; + char buffer[20]; + int i = 0; + if (s < 0) { + /*tex Print the sign, if negative. */ + print_char('-'); + negate(s); + } + /*tex Print the integer part. */ + print_int(s / unity); + buffer[i++] = '.'; + s = 10 * (s % unity) + 5; + delta = 10; + do { + if (delta > unity) { + /*tex Round the last digit. */ + s = s + 0100000 - 50000; + } + buffer[i++] = '0' + (s / unity); + s = 10 * (s % unity); + delta = delta * 10; + } while (s > delta); + buffer[i++] = '\0'; + tprint(buffer); +} + +/*tex + +Physical sizes that a \TeX\ user specifies for portions of documents are +represented internally as scaled points. Thus, if we define an `sp' (scaled +@^sp@> point) as a unit equal to $2^{-16}$ printer's points, every dimension +inside of \TeX\ is an integer number of sp. There are exactly 4,736,286.72 sp per +inch. Users are not allowed to specify dimensions larger than $2^{30}-1$ sp, +which is a distance of about 18.892 feet (5.7583 meters); two such quantities can +be added without overflow on a 32-bit computer. + +The present implementation of \TeX\ does not check for overflow when @^overflow +in arithmetic@> dimensions are added or subtracted. This could be done by +inserting a few dozen tests of the form `\ignorespaces|if x>=010000000000 then +@t\\{report\_overflow}@>|', but the chance of overflow is so remote that such +tests do not seem worthwhile. + +\TeX\ needs to do only a few arithmetic operations on scaled quantities, other +than addition and subtraction, and the following subroutines do most of the work. +A single computation might use several subroutine calls, and it is desirable to +avoid producing multiple error messages in case of arithmetic overflow; so the +routines set the global variable |arith_error| to |true| instead of reporting +errors directly to the user. Another global variable, |tex_remainder|, holds the +remainder after a division. + +*/ + +/*tex Has arithmetic overflow occurred recently? */ + +boolean arith_error; + +/*tex The amount subtracted to get an exact division. */ + +scaled tex_remainder; + +/*tex + + The first arithmetical subroutine we need computes $nx+y$, where |x| +and~|y| are |scaled| and |n| is an integer. We will also use it to +multiply integers. + +*/ + +scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer) +{ + if (n == 0) + return y; + if (n < 0) { + negate(x); + negate(n); + } + if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) { + return (n * x + y); + } else { + arith_error = true; + return 0; + } +} + +/*tex + +We also need to divide scaled dimensions by integers. + +*/ + +scaled x_over_n(scaled x, int n) +{ + /*tex Should |tex_remainder| be negated? */ + boolean negative = false; + if (n == 0) { + arith_error = true; + tex_remainder = x; + return 0; + } else { + if (n < 0) { + negate(x); + negate(n); + negative = true; + } + if (x >= 0) { + tex_remainder = x % n; + if (negative) + negate(tex_remainder); + return (x / n); + } else { + tex_remainder = -((-x) % n); + if (negative) + negate(tex_remainder); + return (-((-x) / n)); + } + } +} + +/*tex + +Then comes the multiplication of a scaled number by a fraction |n/d|, where |n| +and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is positive. It would +be too dangerous to multiply by~|n| and then divide by~|d|, in separate +operations, since overflow might well occur; and it would be too inaccurate to +divide by |d| and then multiply by |n|. Hence this subroutine simulates +1.5-precision arithmetic. + +*/ + +scaled xn_over_d(scaled x, int n, int d) +{ + nonnegative_integer t, u, v, xx, dd; + boolean positive = true; + if (x < 0) { + negate(x); + positive = false; + } + xx = (nonnegative_integer) x; + dd = (nonnegative_integer) d; + t = ((xx % 0100000) * (nonnegative_integer) n); + u = ((xx / 0100000) * (nonnegative_integer) n + (t / 0100000)); + v = (u % dd) * 0100000 + (t % 0100000); + if (u / dd >= 0100000) + arith_error = true; + else + u = 0100000 * (u / dd) + (v / dd); + if (positive) { + tex_remainder = (int) (v % dd); + return (scaled) u; + } else { + /*tex The casts are for ms cl. */ + tex_remainder = -(int) (v % dd); + return -(scaled) (u); + } +} + +/*tex + +The next subroutine is used to compute the ``badness'' of glue, when a total~|t| +is supposed to be made from amounts that sum to~|s|. According to {\sl The \TeX +book}, the badness of this situation is $100(t/s)^3$; however, badness is simply +a heuristic, so we need not squeeze out the last drop of accuracy when computing +it. All we really want is an approximation that has similar properties. +@:TeXbook}{\sl The \TeX book@> + +The actual method used to compute the badness is easier to read from the program +than to describe in words. It produces an integer value that is a reasonably +close approximation to $100(t/s)^3$, and all implementations of \TeX\ should use +precisely this method. Any badness of $2^{13}$ or more is treated as infinitely +bad, and represented by 10000. + +It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s) +>= badness(t,s+1)|}.$$ The badness function defined here is capable of computing +at most 1095 distinct values, but that is plenty. + +*/ + +halfword badness(scaled t, scaled s) +{ + /*tex Approximation to $\alpha t/s$, where $\alpha^3\approx 100\cdot2^{18}$ */ + int r; + if (t == 0) { + return 0; + } else if (s <= 0) { + return inf_bad; + } else { + /*tex $297^3=99.94\times2^{18}$ */ + if (t <= 7230584) { + r = (t * 297) / s; + } else if (s >= 1663497) { + r = t / (s / 297); + } else { + r = t; + } + if (r > 1290) { + /*tex $1290^3<2^{31}<1291^3$ */ + return inf_bad; + } else { + /*tex This is $r^3/2^{18}$, rounded to the nearest integer. */ + return ((r * r * r + 0400000) / 01000000); + } + } +} + +/*tex + +When \TeX\ ``packages'' a list into a box, it needs to calculate the +proportionality ratio by which the glue inside the box should stretch or shrink. +This calculation does not affect \TeX's decision making, so the precise details +of rounding, etc., in the glue calculation are not of critical importance for the +consistency of results on different computers. + +We shall use the type |glue_ratio| for such proportionality ratios. A glue ratio +should take the same amount of memory as an |integer| (usually 32 bits) if it is +to blend smoothly with \TeX's other data structures. Thus |glue_ratio| should be +equivalent to |short_real| in some implementations of PASCAL. Alternatively, it +is possible to deal with glue ratios using nothing but fixed-point arithmetic; +see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the routines cited there must +be modified to allow negative glue ratios.) @^system dependencies@> + +*/ + +/* + +This section is (almost) straight from MetaPost. I (Taco) had to change the types +(use |integer| instead of |fraction|), but that should not have any influence on +the actual calculations (the original comments refer to quantities like +|fraction_four| ($2^{30}$), and that is the same as the numeric representation of +|max_dimen|). + +I've copied the low-level variables and routines that are needed, but only those +(e.g. |m_log|), not the accompanying ones like |m_exp|. Most of the following +low-level numeric routines are only needed within the calculation of |norm_rand|. +I've been forced to rename |make_fraction| to |make_frac| because TeX already has +a routine by that name with a wholly different function (it creates a +|fraction_noad| for math typesetting) + +And now let's complete our collection of numeric utility routines by considering +random number generation. \MP{} generates pseudo-random numbers with the additive +scheme recommended in Section 3.6 of {\sl The Art of Computer Programming}; +however, the results are random fractions between 0 and |fraction_one-1|, +inclusive. + +There's an auxiliary array |randoms| that contains 55 pseudo-random fractions. +Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$, we generate batches +of 55 new $x_n$'s at a time by calling |new_randoms|. The global variable +|j_random| tells which element has most recently been consumed. + +*/ + +/*tex The last 55 random values generated: */ + +static int randoms[55]; + +/*tex The number of unused |randoms|: */ + +static int j_random; + +/*tex The default random seed: */ + +scaled random_seed; + +/*tex A small bit of \METAPOST\ is needed. */ + +#define fraction_half 01000000000 /* $2^{27} $, represents 0.50000000 */ +#define fraction_one 02000000000 /* $2^{28} $, represents 1.00000000 */ +#define fraction_four 010000000000 /* $2^{30} $, represents 4.00000000 */ +#define el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */ + +/*tex + +The |make_frac| routine produces the |fraction| equivalent of |p/q|, given +integers |p| and~|q|; it computes the integer +$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are positive. If |p| and +|q| are both of the same scaled type |t|, the ``type relation'' +|make_frac(t,t)=fraction| is valid; and it's also possible to use the subroutine +``backwards,'' using the relation |make_frac(t,fraction)=t| between scaled types. + +If the result would have magnitude $2^{31}$ or more, |make_frac| sets +|arith_error:=true|. Most of \MP's internal computations have been designed to +avoid this sort of error. + +If this subroutine were programmed in assembly language on a typical machine, we +could simply compute |(@t$2^{28}$@>*p)div q|, since a double-precision product +can often be input to a fixed-point division instruction. But when we are +restricted to PASCAL arithmetic it is necessary either to resort to +multiple-precision maneuvering or to use a simple but slow iteration. The +multiple-precision technique would be about three times faster than the code +adopted here, but it would be comparatively long and tricky, involving about +sixteen additional multiplications and divisions. + +This operation is part of \MP's ``inner loop''; indeed, it will consume nearly +10\%! of the running time (exclusive of input and output) if the code below is +left unchanged. A machine-dependent recoding will therefore make \MP\ run faster. +The present implementation is highly portable, but slow; it avoids multiplication +and division except in the initial stage. System wizards should be careful to +replace it with a routine that is guaranteed to produce identical results in all +cases. @^system dependencies@> + +As noted below, a few more routines should also be replaced by machine-dependent +code, for efficiency. But when a procedure is not part of the ``inner loop,'' +such changes aren't advisable; simplicity and robustness are preferable to +trickery, unless the cost is too high. + +*/ + +static int make_frac(int p, int q) +{ + /*tex The fraction bits, with a leading 1 bit: */ + int f; + /*tex The integer part of $\vert p/q\vert$: */ + int n; + /*tex Disables certain compiler optimizations: */ + register int be_careful; + /*tex Should the result be negated? */ + boolean negative = false; + if (p < 0) { + negate(p); + negative = true; + } + if (q <= 0) { + negate(q); + negative = !negative; + } + n = p / q; + p = p % q; + if (n >= 8) { + arith_error = true; + if (negative) + return (-el_gordo); + else + return el_gordo; + } else { + n = (n - 1) * fraction_one; + /*tex_remainder + + Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$. The |repeat| loop + here preserves the following invariant relations between |f|, |p|, + and~|q|: (i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an + integer and $p_0$ is the original value of~$p$. + + Notice that the computation specifies |(p-q)+p| instead of |(p+p)-q|, + because the latter could overflow. Let us hope that optimizing + compilers do not miss this point; a special variable |be_careful| is + used to emphasize the necessary order of computation. Optimizing + compilers should keep |be_careful| in a register, not store it in + memory. + + */ + f = 1; + do { + be_careful = p - q; + p = be_careful + p; + if (p >= 0) + f = f + f + 1; + else { + f += f; + p = p + q; + } + } while (f < fraction_one); + be_careful = p - q; + if (be_careful + p >= 0) + incr(f); + + if (negative) + return (-(f + n)); + else + return (f + n); + } +} + +static int take_frac(int q, int f) +{ + /*tex The fraction so far: */ + int p; + /*tex Additional multiple of $q$: */ + int n; + /*tex Disables certain compiler optimizations. */ + register int be_careful; + /*tex Should the result be negated? */ + boolean negative = false; + /*tex Reduce to the case that |f>=0| and |q>0|. */ + if (f < 0) { + negate(f); + negative = true; + } + if (q < 0) { + negate(q); + negative = !negative; + } + if (f < fraction_one) { + n = 0; + } else { + n = f / fraction_one; + f = f % fraction_one; + if (q <= el_gordo / n) { + n = n * q; + } else { + arith_error = true; + n = el_gordo; + } + } + f = f + fraction_one; + /*tex + + Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$. The invariant relations + in this case are (i)~$\lfloor(qf+p)/2^k\rfloor =\lfloor + qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and $f_0$ is the + original value of~$f$; (ii)~$2^k\L f<2^{k+1}$. + + Here |p| becomes $2^{27}$; the invariants hold now with $k=28$: + + */ + p = fraction_half; + if (q < fraction_four) { + do { + if (odd(f)) + p = halfp(p + q); + else + p = halfp(p); + f = halfp(f); + } while (f != 1); + } else { + do { + if (odd(f)) + p = p + halfp(q - p); + else + p = halfp(p); + f = halfp(f); + } while (f != 1); + } + be_careful = n - el_gordo; + if (be_careful + p > 0) { + arith_error = true; + n = el_gordo - p; + } + if (negative) + return (-(n + p)); + else + return (n + p); +} + +/*tex + +The subroutines for logarithm and exponential involve two tables. The first is +simple: |two_to_the[k]| equals $2^k$. The second involves a bit more calculation, +which the author claims to have done correctly: |spec_log[k]| is $2^{27}$ times +$\ln\bigl(1/(1-2^{-k})\bigr)= 2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, +rounded to the nearest integer. + +*/ + +/*tex The powers of two: */ + +static int two_to_the[31]; + +/*tex Special logarithms: */ + +static int spec_log[29]; + +void initialize_arithmetic(void) +{ + int k; + two_to_the[0] = 1; + for (k = 1; k <= 30; k++) { + two_to_the[k] = 2 * two_to_the[k - 1]; + } + spec_log [1] = 93032640; + spec_log [2] = 38612034; + spec_log [3] = 17922280; + spec_log [4] = 8662214; + spec_log [5] = 4261238; + spec_log [6] = 2113709; + spec_log [7] = 1052693; + spec_log [8] = 525315; + spec_log [9] = 262400; + spec_log[10] = 131136; + spec_log[11] = 65552; + spec_log[12] = 32772; + spec_log[13] = 16385; + for (k = 14; k <= 27; k++) { + spec_log[k] = two_to_the[27 - k]; + } + spec_log[28] = 1; +} + +static int m_log(int x) +{ + /*tex Auxiliary registers: */ + int y, z; + /*tex Iteration counter: */ + int k; + if (x <= 0) { + /*tex Handle non-positive logarithm. */ + print_err("Logarithm of "); + print_scaled(x); + tprint(" has been replaced by 0"); + help2( + "Since I don't take logs of non-positive numbers,", + "I'm zeroing this one. Proceed, with fingers crossed." + ); + error(); + return 0; + } else { + /*tex $14\times2^{27}\ln2\approx1302456956.421063$ */ + y = 1302456956 + 4 - 100; + /*tex $2^{16}\times .421063\approx 27595$ */ + z = 27595 + 6553600; + while (x < fraction_four) { + x += x; + /*tex $2^{27}\ln2\approx 93032639.74436163$ */ + y = y - 93032639; + /*tex $2^{16}\times.74436163\approx 48782$ */ + z = z - 48782; + } + + y = y + (z / unity); + k = 2; + while (x > fraction_four + 4) { + /*tex + Increase |k| until |x| can be multiplied by a factor of $2^{-k}$, + and adjust $y$ accordingly. Here $z=\lceil x/2^k\rceil$. + */ + z = ((x - 1) / two_to_the[k]) + 1; + while (x < fraction_four + z) { + z = halfp(z + 1); + k = k + 1; + } + y = y + spec_log[k]; + x = x - z; + } + return (y / 8); + } +} + +/*tex + +The following somewhat different subroutine tests rigorously if $ab$ is greater +than, equal to, or less than~$cd$, given integers $(a,b,c,d)$. In most cases a +quick decision is reached. The result is $+1$, 0, or~$-1$ in the three respective +cases. + +*/ + +static int ab_vs_cd(int a, int b, int c, int d) +{ + int q, r; + /*tex Reduce to the case that |a,c>=0| and |b,d>0|. */ + if (a < 0) { + negate(a); + negate(b); + } + if (c < 0) { + negate(c); + negate(d); + } + if (d <= 0) { + if (b >= 0) + return (((a == 0 || b == 0) && (c == 0 || d == 0)) ? 0 : 1); + if (d == 0) + return (a == 0 ? 0 : -1); + q = a; + a = c; + c = q; + q = -b; + b = -d; + d = q; + } else if (b <= 0) { + if (b < 0 && a > 0) + return -1; + return (c == 0 ? 0 : -1); + } + while (1) { + q = a / d; + r = c / b; + if (q != r) + return (q > r ? 1 : -1); + q = a % d; + r = c % b; + if (r == 0) + return (q == 0 ? 0 : 1); + if (q == 0) + return -1; + a = b; + b = q; + c = d; + d = r; + /*tex Now |a>d>0| and |c>b>0|. */ + } +} + +/*tex + +To consume a random integer, the program below will say `|next_random|' and then +it will fetch |randoms[j_random]|. + +*/ + +#define next_random() do { \ + if (j_random==0) \ + new_randoms(); \ + else \ + decr(j_random); \ +} while (0) + +static void new_randoms(void) +{ + /*tex The index into |randoms|. */ + int k; + /*tex The accumulator. */ + int x; + for (k = 0; k <= 23; k++) { + x = randoms[k] - randoms[k + 31]; + if (x < 0) + x = x + fraction_one; + randoms[k] = x; + } + for (k = 24; k <= 54; k++) { + x = randoms[k] - randoms[k - 24]; + if (x < 0) + x = x + fraction_one; + randoms[k] = x; + } + j_random = 54; +} + +/*tex + +To initialize the |randoms| table, we call the following routine. + +*/ + +void init_randoms(int seed) +{ + /*tex Three more or less random integers. */ + int j, jj, k; + /*tex The index into |randoms|. */ + int i; + j = abs(seed); + while (j >= fraction_one) + j = halfp(j); + k = 1; + for (i = 0; i <= 54; i++) { + jj = k; + k = j - k; + j = jj; + if (k < 0) + k = k + fraction_one; + randoms[(i * 21) % 55] = j; + } + /*tex We ``warm up'' the array. */ + new_randoms(); + new_randoms(); + new_randoms(); +} + +/*tex + +To produce a uniform random number in the range |0<=u<x| or |0>=u>x| or |0=u=x|, +given a |scaled| value~|x|, we proceed as shown here. + +Note that the call of |take_frac| will produce the values 0 and~|x| with about +half the probability that it will produce any other particular values between 0 +and~|x|, because it rounds its answers. + +*/ + +int unif_rand(int x) +{ + int y; + next_random(); + y = take_frac(abs(x), randoms[j_random]); + if (y == abs(x)) + return 0; + else if (x > 0) + return y; + else + return -y; +} + +/*tex + +Finally, a normal deviate with mean zero and unit standard deviation can readily +be obtained with the ratio method (Algorithm 3.4.1R in {\sl The Art of Computer +Programming\/}. + +*/ + +int norm_rand(void) +{ + /*tex What the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$. */ + int x, u, l; + do { + do { + next_random(); + x = take_frac(112429, randoms[j_random] - fraction_half); + /*tex Which is $2^{16}\sqrt{8/e}\approx 112428.82793$. */ + next_random(); + u = randoms[j_random]; + } while (abs(x) >= u); + x = make_frac(x, u); + /*tex More fuzzyness: $2^{24}\cdot12\ln2\approx139548959.6165$. */ + l = 139548960 - m_log(u); + } while (ab_vs_cd(1024, l, x, x) < 0); + return x; +} + +/*tex + +This function could also be expressed as a macro, but it is a useful breakpoint +for debugging. + +*/ + +int fix_int(int val, int min, int max) +{ + return (val < min ? min : (val > max ? max : val)); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/arithmetic.h b/Build/source/texk/web2c/luatexdir/tex/arithmetic.h new file mode 100644 index 00000000000..e5638990e0c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/arithmetic.h @@ -0,0 +1,80 @@ +/* arithmetic.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef ARITHMETIC_H +# define ARITHMETIC_H + +/* |incr| and |decr| are in texmfmp.h */ + + /* #define incr(A) (A)++ *//* increase a variable by unity */ + /* #define decr(A) (A)-- *//* decrease a variable by unity */ + +# define negate(A) (A)=-(A) /* change the sign of a variable */ + +# undef half +extern int half(int x); + +/* +Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples +of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit +positions from the right end of a binary computer word. +*/ + +# define unity 0200000 /* $2^{16}$, represents 1.00000 */ +# define two 0400000 /* $2^{17}$, represents 2.00000 */ + +typedef unsigned int nonnegative_integer; /* $0\L x<2^{31}$ */ + +extern scaled round_decimals(int k); +extern void print_scaled(scaled s); + +extern boolean arith_error; +extern scaled tex_remainder; + +extern scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer); + +# define nx_plus_y(A,B,C) mult_and_add((A),(B),(C),07777777777) +# define mult_integers(A,B) mult_and_add((A),(B),0,017777777777) + +extern scaled x_over_n(scaled x, int n); +extern scaled xn_over_d(scaled x, int n, int d); + +# define inf_bad 10000 /* infinitely bad value */ + +extern halfword badness(scaled t, scaled s); + +# define set_glue_ratio_zero(A) (A)=0.0 /* store the representation of zero ratio */ +# define set_glue_ratio_one(A) (A)=1.0 /* store the representation of unit ratio */ +# define float_cast(A) (float)(A) /* convert from |glue_ratio| to type |real| */ +# define unfloat(A) (glue_ratio)(A) /* convert from |real| to type |glue_ratio| */ +# define float_constant(A) (float)A /* convert |integer| constant to |real| */ +# define float_round round + +extern void initialize_arithmetic(void); + +extern scaled random_seed; /* the default random seed */ + +extern void init_randoms(int seed); +extern int unif_rand(int x); +extern int norm_rand(void); + +extern int fix_int(int val, int min, int max); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/backend.c b/Build/source/texk/web2c/luatexdir/tex/backend.c new file mode 100644 index 00000000000..3115a88a7e3 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/backend.c @@ -0,0 +1,124 @@ +/* to fill */ + +#include "ptexlib.h" + +scaled max_v = 0; /* maximum height-plus-depth of pages shipped so far */ +scaled max_h = 0; /* maximum width of pages shipped so far */ +boolean doing_leaders = false; /* are we inside a leader box? */ +int cur_s = -1; /* current depth of output box nesting, initially $-1$ */ + +static backend_struct *backend = NULL; +backend_function *backend_out, *backend_out_whatsit, *backend_out_control; + +static void missing_backend_function(PDF pdf, halfword p) +{ + const char *n = get_node_name(type(p), subtype(p)); + if (type(p) == whatsit_node) + formatted_error("pdf backend","no output function for whatsit %s",n); + else + formatted_error("pdf backend","no output function for node %s",n); +} + +static void init_none_backend_functions(void) +{ + backend_struct *p = &backend[OMODE_NONE]; + p->name = strdup("NONE"); +} + +static void init_pdf_backend_functions(void) +{ + backend_struct *p = &backend[OMODE_PDF]; + p->name = strdup("PDF"); + p->node_fu[rule_node] = &pdf_place_rule; + p->node_fu[glyph_node] = &pdf_place_glyph; + p->whatsit_fu[special_node] = &pdf_special; + p->whatsit_fu[pdf_literal_node] = &pdf_out_literal; + p->whatsit_fu[pdf_refobj_node] = &pdf_ref_obj; + p->whatsit_fu[pdf_annot_node] = &do_annot; + p->whatsit_fu[pdf_start_link_node] = &do_link; + p->whatsit_fu[pdf_end_link_node] = &end_link; + p->whatsit_fu[pdf_dest_node] = &do_dest; + p->whatsit_fu[pdf_thread_node] = &do_thread; + p->whatsit_fu[pdf_end_thread_node] = &end_thread; + p->whatsit_fu[late_lua_node] = &late_lua; + p->whatsit_fu[pdf_colorstack_node] = &pdf_out_colorstack; + p->whatsit_fu[pdf_setmatrix_node] = &pdf_out_setmatrix; + p->whatsit_fu[pdf_save_node] = &pdf_out_save; + p->whatsit_fu[pdf_restore_node] = &pdf_out_restore; + p->whatsit_fu[pdf_link_state_node] = &pdf_out_link_state; + p->control_fu[backend_control_push_list] = &pdf_push_list; + p->control_fu[backend_control_pop_list] = &pdf_pop_list; + p->control_fu[backend_control_begin_page] = &pdf_begin_page; + p->control_fu[backend_control_end_page] = &pdf_end_page; + p->control_fu[backend_control_open_file] = &pdf_open_file; + p->control_fu[backend_control_write_header] = &pdf_write_header; + p->control_fu[backend_control_finish_file] = &pdf_finish_file; + p->control_fu[backend_control_set_reference_point] = &pdf_set_reference_point; +} + +static void init_dvi_backend_functions(void) +{ + backend_struct *p = &backend[OMODE_DVI]; + p->name = strdup("DVI"); + p->node_fu[rule_node] = &dvi_place_rule; + p->node_fu[glyph_node] = &dvi_place_glyph; + p->whatsit_fu[special_node] = &dvi_special; + p->whatsit_fu[late_lua_node] = &late_lua; + p->control_fu[backend_control_push_list] = &dvi_push_list; + p->control_fu[backend_control_pop_list] = &dvi_pop_list; + p->control_fu[backend_control_begin_page] = &dvi_begin_page; + p->control_fu[backend_control_end_page] = &dvi_end_page; + p->control_fu[backend_control_open_file] = &dvi_open_file; + p->control_fu[backend_control_write_header] = &dvi_write_header; + p->control_fu[backend_control_finish_file] = &dvi_finish_file; + p->control_fu[backend_control_set_reference_point] = &dvi_set_reference_point; +} + + +void init_backend_functionpointers(output_mode o_mode) +{ + int i, j; + if (backend == NULL) { + backend = xmalloc((MAX_OMODE + 1) * sizeof(backend_struct)); + for (i = 0; i <= MAX_OMODE; i++) { + backend[i].node_fu = xmalloc((MAX_NODE_TYPE + 1) * sizeof(backend_function)); + backend[i].whatsit_fu = xmalloc((MAX_WHATSIT_TYPE + 1) * sizeof(backend_function)); + backend[i].control_fu = xmalloc((MAX_CONTROL_TYPE + 1) * sizeof(backend_function)); + for (j = 0; j < MAX_NODE_TYPE + 1; j++) + backend[i].node_fu[j] = &missing_backend_function; + for (j = 0; j < MAX_WHATSIT_TYPE + 1; j++) + backend[i].whatsit_fu[j] = &missing_backend_function; + for (j = 0; j < MAX_CONTROL_TYPE + 1; j++) + backend[i].control_fu[j] = &missing_backend_function; + } + init_none_backend_functions(); + init_dvi_backend_functions(); + init_pdf_backend_functions(); + } + backend_out = backend[o_mode].node_fu; + backend_out_whatsit = backend[o_mode].whatsit_fu; + backend_out_control = backend[o_mode].control_fu; +} + +output_mode get_o_mode(void) +{ + output_mode o_mode; + if (output_mode_par > 0) { + o_mode = OMODE_PDF; + } else + o_mode = OMODE_DVI; + return o_mode; +} + +void fix_o_mode(void) +{ + output_mode o_mode = get_o_mode(); + if (output_mode_used == OMODE_NONE) { + output_mode_used = o_mode; + /*tex Used by synctex, we need to use output_mode_used there: */ + static_pdf->o_mode = output_mode_used; + } else if (output_mode_used != o_mode) { + normal_error("pdf backend", "\\outputmode can only be changed before anything is written to the output"); + } + init_backend_functionpointers(output_mode_used); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/backend.h b/Build/source/texk/web2c/luatexdir/tex/backend.h new file mode 100644 index 00000000000..5cfbd06fb90 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/backend.h @@ -0,0 +1,54 @@ +/* to fill */ + +#ifndef BACKEND_H +# define BACKEND_H + +#include "ptexlib.h" + +extern scaled max_v; +extern scaled max_h; +extern boolean doing_leaders; +extern int cur_s; + +# define MAX_CONTROL_TYPE 7 + +typedef enum { + backend_control_push_list = 0, + backend_control_pop_list, + backend_control_begin_page, + backend_control_end_page, + backend_control_open_file, + backend_control_write_header, + backend_control_finish_file, + backend_control_set_reference_point +} backend_control_types ; + +typedef void (*backend_function) (); /* variadic arguments */ + +typedef struct { + char *name; /* name of the backend */ + backend_function *node_fu; /* array of node output functions */ + backend_function *whatsit_fu; /* array of whatsit output functions */ + backend_function *control_fu; /* array of whatsit output functions */ +} backend_struct; + +/* extern pos_info_structure pos_info; */ + +extern backend_function *backend_out; +extern backend_function *backend_out_whatsit; +extern backend_function *backend_out_control; + +/* get_o_mode translates from output_mode to output_mode_used */ +/* fix_o_mode freezes output_mode as soon as anything goes through the backend */ + +/* + extern void check_o_mode(PDF pdf, const char *s, int o_mode, boolean errorflag); + extern void ensure_output_file_open(PDF pdf, const char *ext); +*/ + +extern void fix_o_mode(void); +extern output_mode get_o_mode(void); + +extern void init_backend_functionpointers(output_mode o_mode); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/buildpage.c b/Build/source/texk/web2c/luatexdir/tex/buildpage.c new file mode 100644 index 00000000000..81e69dc3b2a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/buildpage.c @@ -0,0 +1,1164 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + + +#define mode mode_par +#define head head_par +#define tail tail_par + +/*tex + + When \TeX\ appends new material to its main vlist in vertical mode, it uses a + method something like |vsplit| to decide where a page ends, except that the + calculations are done ``on line'' as new items come in. The main complication + in this process is that insertions must be put into their boxes and removed + from the vlist, in a more-or-less optimum manner. + + We shall use the term ``current page'' for that part of the main vlist that + is being considered as a candidate for being broken off and sent to the + user's output routine. The current page starts at |vlink(page_head)|, and it + ends at |page_tail|. We have |page_head=page_tail| if this list is empty. + + Utter chaos would reign if the user kept changing page specifications while a + page is being constructed, so the page builder keeps the pertinent + specifications frozen as soon as the page receives its first box or + insertion. The global variable |page_contents| is |empty| when the current + page contains only mark nodes and content-less whatsit nodes; it is + |inserts_only| if the page contains only insertion nodes in addition to marks + and whatsits. Glue nodes, kern nodes, and penalty nodes are discarded until a + box or rule node appears, at which time |page_contents| changes to + |box_there|. As soon as |page_contents| becomes non-|empty|, the current + |vsize| and |max_depth| are squirreled away into |page_goal| and + |page_max_depth|; the latter values will be used until the page has been + forwarded to the user's output routine. The \.{\\topskip} adjustment is made + when |page_contents| changes to |box_there|. + + Although |page_goal| starts out equal to |vsize|, it is decreased by the + scaled natural height-plus-depth of the insertions considered so far, and by + the \.{\\skip} corrections for those insertions. Therefore it represents the + size into which the non-inserted material should fit, assuming that all + insertions in the current page have been made. + + The global variables |best_page_break| and |least_page_cost| correspond + respectively to the local variables |best_place| and |least_cost| in the + |vert_break| routine that we have already studied; i.e., they record the + location and value of the best place currently known for breaking the current + page. The value of |page_goal| at the time of the best break is stored in + |best_size|. + +*/ + +/*tex the final node on the current page */ + +halfword page_tail; + +/*tex what is on the current page so far? */ + +int page_contents; + +/*tex maximum box depth on page being built */ + +scaled page_max_depth; + +/*tex break here to get the best page known so far */ + +halfword best_page_break; + +/*tex the score for this currently best page */ + +int least_page_cost; + +/*tex its |page_goal| */ + +scaled best_size; + +/*tex + + The page builder has another data structure to keep track of insertions. This + is a list of four-word nodes, starting and ending at |page_ins_head|. That + is, the first element of the list is node |t$_1$=vlink(page_ins_head)|; + node $r_j$ is followed by |t$_{j+1}$=vlink(t$_j$)|; and if there are + |n| items we have |$_{n+1}$>=page_ins_head|. The |subtype| field of each + node in this list refers to an insertion number; for example, `\.{\\insert + 250}' would correspond to a node whose |subtype| is |qi(250)| (the same as + the |subtype| field of the relevant |ins_node|). These |subtype| fields are + in increasing order, and |subtype(page_ins_head)=65535|, so |page_ins_head| + serves as a convenient sentinel at the end of the list. A record is present + for each insertion number that appears in the current page. + + The |type| field in these nodes distinguishes two possibilities that might + occur as we look ahead before deciding on the optimum page break. If + |type(r)=inserting_node|, then |height(r)| contains the total of the + height-plus-depth dimensions of the box and all its inserts seen so far. + |type(r)=split_up_node|, then no more insertions will be made into this box, + because at least one previous insertion was too big to fit on the current + page; |broken_ptr(r)| points to the node where that insertion will be split, + if \TeX\ decides to split it, |broken_ins(r)| points to the insertion node + that was tentatively split, and |height(r)| includes also the natural height + plus depth of the part that would be split off. + + In both cases, |last_ins_ptr(r)| points to the last |ins_node| encountered + for box |qo(subtype(r))| that would be at least partially inserted on the + next page; and |best_ins_ptr(r)| points to the last such |ins_node| that + should actually be inserted, to get the page with minimum badness among all + page breaks considered so far. We have |best_ins_ptr(r)=null| if and only if + no insertion for this box should be made to produce this optimum page. + + Pages are built by appending nodes to the current list in \TeX's vertical + mode, which is at the outermost level of the semantic nest. This vlist is + split into two parts; the ``current page'' that we have been talking so much + about already, and the ``contribution list'' that receives new nodes as they + are created. The current page contains everything that the page builder has + accounted for in its data structures, as described above, while the + contribution list contains other things that have been generated by other + parts of \TeX\ but have not yet been seen by the page builder. The + contribution list starts at |vlink(contrib_head)|, and it ends at the current + node in \TeX's vertical mode. + + When \TeX\ has appended new material in vertical mode, it calls the procedure + |build_page|, which tries to catch up by moving nodes from the contribution + list to the current page. This procedure will succeed in its goal of emptying + the contribution list, unless a page break is discovered, i.e., unless the + current page has grown to the point where the optimum next page break has + been determined. In the latter case, the nodes after the optimum break will + go back onto the contribution list, and control will effectively pass to the + user's output routine. + + We make |type(page_head)=glue_node|, so that an initial glue node on the + current page will not be considered a valid breakpoint. + +*/ + +void initialize_buildpage(void) +{ + subtype(page_ins_head) = 65535; + type(page_ins_head) = split_up_node; + vlink(page_ins_head) = page_ins_head; + + type(page_head) = glue_node; + subtype(page_head) = normal; +} + +/*tex + + An array |page_so_far| records the heights and depths of everything on the + current page. This array contains six |scaled| numbers, like the similar + arrays already considered in |line_break| and |vert_break|; and it also + contains |page_goal| and |page_depth|, since these values are all accessible + to the user via |set_page_dimen| commands. The value of |page_so_far[1]| is + also called |page_total|. The stretch and shrink components of the \.{\\skip} + corrections for each insertion are included in |page_so_far|, but the natural + space components of these corrections are not, since they have been + subtracted from |page_goal|. + + The variable |page_depth| records the depth of the current page; it has been + adjusted so that it is at most |page_max_depth|. The variable |last_glue| + points to the glue specification of the most recent node contributed from the + contribution list, if this was a glue node; otherwise + |last_glue=max_halfword|. (If the contribution list is nonempty, however, the + value of |last_glue| is not necessarily accurate.) The variables + |last_penalty|, |last_kern|, and |last_node_type| are similar. And finally, + |insert_penalties| holds the sum of the penalties associated with all split + and floating insertions. + +*/ + +/*tex height and glue of the current page */ + +scaled page_so_far[8]; + +/*tex used to implement \.{\\lastskip} */ + +halfword last_glue; + +/*tex used to implement \.{\\lastpenalty} */ + +int last_penalty; + +/*tex used to implement \.{\\lastkern} */ + +scaled last_kern; + +/*tex used to implement \.{\\lastnodetype} */ + +int last_node_type; + +/*tex sum of the penalties for held-over insertions */ + +int insert_penalties; + +#define print_plus(A,B) do { \ + if (page_so_far[(A)]!=0) { \ + tprint(" plus "); \ + print_scaled(page_so_far[(A)]); \ + tprint((B)); \ + } \ +} while (0) + +void print_totals(void) +{ + print_scaled(page_total); + print_plus(2, ""); + print_plus(3, "fil"); + print_plus(4, "fill"); + print_plus(5, "filll"); + if (page_shrink != 0) { + tprint(" minus "); + print_scaled(page_shrink); + } +} + +/*tex + + Here is a procedure that is called when the |page_contents| is changing from + |empty| to |inserts_only| or |box_there|. + +*/ + +#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7) +#define set_page_so_far_zero(A) page_so_far[(A)]=0 + +void freeze_page_specs(int s) +{ + page_contents = s; + page_goal = vsize_par; + page_max_depth = max_depth_par; + page_depth = 0; + do_all_six(set_page_so_far_zero); + least_page_cost = awful_bad; + if (tracing_pages_par > 0) { + begin_diagnostic(); + tprint_nl("%% goal height="); + print_scaled(page_goal); + tprint(", max depth="); + print_scaled(page_max_depth); + end_diagnostic(false); + } +} + +/*tex + + The global variable |output_active| is true during the time the user's output + routine is driving \TeX. + +*/ + +/*tex Are we in the midst of an output routine? */ + +boolean output_active; + +/*tex + + The page builder is ready to start a fresh page if we initialize the + following state variables. (However, the page insertion list is initialized + elsewhere.) + +*/ + +void start_new_page(void) +{ + page_contents = empty; + page_tail = page_head; + vlink(page_head) = null; + last_glue = max_halfword; + last_penalty = 0; + last_kern = 0; + last_node_type = -1; + page_depth = 0; + page_max_depth = 0; +} + +/*tex + + At certain times box \.{\\outputbox} is supposed to be void (i.e., |null|), + or an insertion box is supposed to be ready to accept a vertical list. If + not, an error message is printed, and the following subroutine flushes the + unwanted contents, reporting them to the user. + +*/ + +static void box_error(int n) +{ + error(); + begin_diagnostic(); + tprint_nl("The following box has been deleted:"); + show_box(box(n)); + end_diagnostic(true); + flush_node_list(box(n)); + box(n) = null; +} + +/*tex + + The following procedure guarantees that a given box register does not contain + an \.{\\hbox}. + +*/ + +static void ensure_vbox(int n) +{ + halfword p = box(n); + if (p != null && type(p) == hlist_node) { + print_err("Insertions can only be added to a vbox"); + help3( + "Tut tut: You're trying to \\insert into a", + "\\box register that now contains an \\hbox.", + "Proceed, and I'll discard its present contents." + ); + box_error(n); + } +} + +/*tex + + \TeX\ is not always in vertical mode at the time |build_page| is called; the + current mode reflects what \TeX\ should return to, after the contribution + list has been emptied. A call on |build_page| should be immediately followed + by `|goto big_switch|', which is \TeX's central control point. + +*/ + +/*tex Append contributions to the current page. */ + +void build_page(void) +{ + /*tex the node being appended */ + halfword p; + /*tex nodes being examined */ + halfword q, r; + /*tex badness and cost of current page */ + int b, c; + /*tex penalty to be added to the badness */ + int pi = 0; + /*tex insertion box number */ + int n; + /*tex sizes used for insertion calculations */ + scaled delta, h, w; + int id, sk, i; + if ((vlink(contrib_head) == null) || output_active) + return; + do { + CONTINUE: + p = vlink(contrib_head); + /*tex Update the values of |last_glue|, |last_penalty|, and |last_kern|. */ + if (last_glue != max_halfword) { + flush_node(last_glue); + last_glue = max_halfword; + } + last_penalty = 0; + last_kern = 0; + last_node_type = type(p) + 1; + if (type(p) == glue_node) { + last_glue = new_glue(p); + } else if (type(p) == penalty_node) { + last_penalty = penalty(p); + } else if (type(p) == kern_node) { + last_kern = width(p); + } + /*tex + + Move node |p| to the current page; if it is time for a page break, + put the nodes following the break back onto the contribution list, + and |return| to the users output routine if there is one. + + The code here is an example of a many-way switch into routines that + merge together in different places. Some people call this + unstructured programming, but the author doesn't see much wrong with + it, as long as the various labels have a well-understood meaning. + + If the current page is empty and node |p| is to be deleted, |goto + done1|; otherwise use node |p| to update the state of the current + page; if this node is an insertion, |goto contribute|; otherwise if + this node is not a legal breakpoint, |goto contribute| or + |update_heights|; otherwise set |pi| to the penalty associated with + this breakpoint. + + The title of this section is already so long, it seems best to avoid + making it more accurate but still longer, by mentioning the fact that + a kern node at the end of the contribution list will not be + contributed until we know its successor. + + */ + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + if (page_contents < box_there) { + /*tex + + Initialize the current page, insert the \.{\\topskip} + glue ahead of |p|, and |goto continue|. + + */ + if (page_contents == empty) + freeze_page_specs(box_there); + else + page_contents = box_there; + q = new_skip_param(top_skip_code); + if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) { + if (width(q) > depth(p)) + width(q) = width(q) - depth(p); + else + width(q) = 0; + } else { + if (width(q) > height(p)) + width(q) = width(q) - height(p); + else + width(q) = 0; + } + couple_nodes(q, p); + couple_nodes(contrib_head, q); + goto CONTINUE; + } else { + /*tex + + Prepare to move a box or rule node to the current page, + then |goto contribute|. + + */ + if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) { + page_total = page_total + page_depth + depth(p); + page_depth = height(p); + } else { + page_total = page_total + page_depth + height(p); + page_depth = depth(p); + } + goto CONTRIBUTE; + + } + break; + case boundary_node: + case whatsit_node: + goto CONTRIBUTE; + break; + case glue_node: + if (page_contents < box_there) + goto DONE1; + else if (precedes_break(page_tail)) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case kern_node: + if (page_contents < box_there) + goto DONE1; + else if (vlink(p) == null) + goto EXIT; + else if (type(vlink(p)) == glue_node) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case penalty_node: + if (page_contents < box_there) + goto DONE1; + else + pi = penalty(p); + break; + case mark_node: + goto CONTRIBUTE; + break; + case ins_node: + /*tex Append an insertion to the current page and |goto contribute|. */ + if (page_contents == empty) + freeze_page_specs(inserts_only); + n = subtype(p); + r = page_ins_head; + i = 1 ; + while (n >= subtype(vlink(r))) { + r = vlink(r); + i = i + 1 ; + } + if (subtype(r) != n) { + /*tex + + Create a page insertion node with |subtype(r)=qi(n)|, and + include the glue correction for box |n| in the current + page state. + + We take note of the value of \.{\\skip} |n| and the + height plus depth of \.{\\box}~|n| only when the first + \.{\\insert}~|n| node is encountered for a new page. A + user who changes the contents of \.{\\box}~|n| after that + first \.{\\insert}~|n| had better be either extremely + careful or extremely lucky, or both. + + */ + id = callback_defined(build_page_insert_callback); + if (id != 0) { + run_callback(id, "dd->d",n,i,&sk); + } else { + sk = n; + } + q = new_node(inserting_node, n); + try_couple_nodes(q, vlink(r)); + couple_nodes(r, q); + r = q; + ensure_vbox(n); + if (box(n) == null) + height(r) = 0; + else + height(r) = height(box(n)) + depth(box(n)); + best_ins_ptr(r) = null; + q = skip(sk); + if (count(n) == 1000) + h = height(r); + else + h = x_over_n(height(r), 1000) * count(n); + page_goal = page_goal - h - width(q); + if (stretch_order(q) > 1) + page_so_far[1 + stretch_order(q)] = page_so_far[1 + stretch_order(q)] + stretch(q); + else + page_so_far[2 + stretch_order(q)] = page_so_far[2 + stretch_order(q)] + stretch(q); + page_shrink = page_shrink + shrink(q); + if ((shrink_order(q) != normal) && (shrink(q) != 0)) { + print_err("Infinite glue shrinkage inserted from \\skip"); + print_int(n); + help3( + "The correction glue for page breaking with insertions", + "must have finite shrinkability. But you may proceed,", + "since the offensive shrinkability has been made finite." + ); + error(); + } + + } + if (type(r) == split_up_node) { + insert_penalties = insert_penalties + float_cost(p); + } else { + last_ins_ptr(r) = p; + delta = page_goal - page_total - page_depth + page_shrink; + /*tex This much room is left if we shrink the maximum. */ + if (count(n) == 1000) { + h = height(p); + } else { + /*tex This much room is needed. */ + h = x_over_n(height(p), 1000) * count(n); + } + if (((h <= 0) || (h <= delta)) + && (height(p) + height(r) <= dimen(n))) { + page_goal = page_goal - h; + height(r) = height(r) + height(p); + } else { + /*tex + + Find the best way to split the insertion, and change + |type(r)| to |split_up_node|. + + Here is the code that will split a long footnote + between pages, in an emergency. The current situation + deserves to be recapitulated: Node |p| is an + insertion into box |n|; the insertion will not fit, + in its entirety, either because it would make the + total contents of box |n| greater than \.{\\dimen} + |n|, or because it would make the incremental amount + of growth |h| greater than the available space + |delta|, or both. (This amount |h| has been weighted + by the insertion scaling factor, i.e., by \.{\\count} + |n| over 1000.) Now we will choose the best way to + break the vlist of the insertion, using the same + criteria as in the \.{\\vsplit} operation. + + */ + if (count(n) <= 0) { + w = max_dimen; + } else { + w = page_goal - page_total - page_depth; + if (count(n) != 1000) + w = x_over_n(w, count(n)) * 1000; + } + if (w > dimen(n) - height(r)) + w = dimen(n) - height(r); + q = vert_break(ins_ptr(p), w, depth(p)); + height(r) = height(r) + best_height_plus_depth; + if (tracing_pages_par > 0) { + /*tex Display the insertion split cost. */ + begin_diagnostic(); + tprint_nl("% split"); + print_int(n); + tprint(" to "); + print_scaled(w); + print_char(','); + print_scaled(best_height_plus_depth); + tprint(" p="); + if (q == null) + print_int(eject_penalty); + else if (type(q) == penalty_node) + print_int(penalty(q)); + else + print_char('0'); + end_diagnostic(false); + } + if (count(n) != 1000) + best_height_plus_depth = x_over_n(best_height_plus_depth, 1000) * count(n); + page_goal = page_goal - best_height_plus_depth; + type(r) = split_up_node; + broken_ptr(r) = q; + broken_ins(r) = p; + if (q == null) + insert_penalties = insert_penalties + eject_penalty; + else if (type(q) == penalty_node) + insert_penalties = insert_penalties + penalty(q); + } + } + goto CONTRIBUTE; + + break; + default: + formatted_error("pagebuilder","invalid node of type %d in vertical mode", type(p)); + break; + } + /*tex + + Check if node |p| is a new champion breakpoint; then if it is time + for a page break, prepare for output, and either fire up the users + output routine and |return| or ship out the page and |goto done|. + + */ + if (pi < inf_penalty) { + /*tex + + Compute the badness, |b|, of the current page, using |awful_bad| + if the box is too full. + + */ + if (page_total < page_goal) { + if ((page_so_far[3] != 0) || (page_so_far[4] != 0) || + (page_so_far[5] != 0)) + b = 0; + else + b = badness(page_goal - page_total, page_so_far[2]); + } else if (page_total - page_goal > page_shrink) { + b = awful_bad; + } else { + b = badness(page_total - page_goal, page_shrink); + } + if (b < awful_bad) { + if (pi <= eject_penalty) + c = pi; + else if (b < inf_bad) + c = b + pi + insert_penalties; + else + c = deplorable; + } else { + c = b; + } + if (insert_penalties >= 10000) + c = awful_bad; + if (tracing_pages_par > 0) { + /*tex Display the page break cost. */ + begin_diagnostic(); + tprint_nl("%"); + tprint(" t="); + print_totals(); + tprint(" g="); + print_scaled(page_goal); + tprint(" b="); + if (b == awful_bad) + print_char('*'); + else + print_int(b); + tprint(" p="); + print_int(pi); + tprint(" c="); + if (c == awful_bad) + print_char('*'); + else + print_int(c); + if (c <= least_page_cost) + print_char('#'); + end_diagnostic(false); + } + if (c <= least_page_cost) { + best_page_break = p; + best_size = page_goal; + least_page_cost = c; + r = vlink(page_ins_head); + while (r != page_ins_head) { + best_ins_ptr(r) = last_ins_ptr(r); + r = vlink(r); + } + } + if ((c == awful_bad) || (pi <= eject_penalty)) { + /*tex Output the current page at the best place. */ + fire_up(p); + if (output_active) { + /*tex User's output routine will act. */ + goto EXIT; + } + /*tex The page has been shipped out by default output routine. */ + goto DONE; + } + } + if ((type(p) < glue_node) || (type(p) > kern_node)) + goto CONTRIBUTE; + UPDATE_HEIGHTS: + /*tex + + Go here to record glue in the |active_height| table. Update the + current page measurements with respect to the glue or kern specified + by node~|p|. + + */ + if (type(p) != kern_node) { + if (stretch_order(p) > 1) + page_so_far[1 + stretch_order(p)] = page_so_far[1 + stretch_order(p)] + stretch(p); + else + page_so_far[2 + stretch_order(p)] = page_so_far[2 + stretch_order(p)] + stretch(p); + page_shrink = page_shrink + shrink(p); + if ((shrink_order(p) != normal) && (shrink(p) != 0)) { + print_err("Infinite glue shrinkage found on current page"); + help4( + "The page about to be output contains some infinitely", + "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.", + "Such glue doesn't belong there; but you can safely proceed,", + "since the offensive shrinkability has been made finite." + ); + error(); + reset_glue_to_zero(p); + shrink_order(p) = normal; + } + } + page_total = page_total + page_depth + width(p); + page_depth = 0; + CONTRIBUTE: + /*tex + + Go here to link a node into the current page. Make sure that + |page_max_depth| is not exceeded. + + */ + if (page_depth > page_max_depth) { + page_total = page_total + page_depth - page_max_depth; + page_depth = page_max_depth; + } + /*tex Link node |p| into the current page and |goto done|. */ + couple_nodes(page_tail, p); + page_tail = p; + try_couple_nodes(contrib_head,vlink(p)); + vlink(p) = null; + goto DONE; + DONE1: + /*tex Recycle node |p|. */ + try_couple_nodes(contrib_head,vlink(p)); + vlink(p) = null; + if (saving_vdiscards_par > 0) { + if (page_disc == null) { + page_disc = p; + } else { + couple_nodes(tail_page_disc, p); + } + tail_page_disc = p; + } else { + flush_node_list(p); + } + DONE: + ; + } while (vlink(contrib_head) != null); + /*tex Make the contribution list empty by setting its tail to |contrib_head|. */ + contrib_tail = contrib_head; + EXIT: + ; +} + +/*tex + + When the page builder has looked at as much material as could appear before + the next page break, it makes its decision. The break that gave minimum + badness will be used to put a completed ``page'' into box \.{\\outputbox}, + with insertions appended to their other boxes. + + We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The + program uses the fact that |bot_mark(x)<>null| implies |first_mark(x)<>null|; + it also knows that |bot_mark(x)=null| implies + |top_mark(x)=first_mark(x)=null|. + + The |fire_up| subroutine prepares to output the current page at the best + place; then it fires up the user's output routine, if there is one, or it + simply ships out the page. There is one parameter, |c|, which represents the + node that was being contributed to the page when the decision to force an + output was made. + +*/ + +void fire_up(halfword c) +{ + /*tex nodes being examined and/or changed */ + halfword p, q, r, s; + /*tex predecessor of |p| */ + halfword prev_p; + /*tex insertion box number */ + int n; + /*tex should the present insertion be held over? */ + boolean wait; + /*tex saved value of |vbadness| */ + int save_vbadness; + /*tex saved value of |vfuzz| */ + scaled save_vfuzz; + /*tex saved value of |split_top_skip| */ + halfword save_split_top_skip; + /*tex for looping through the marks */ + halfword i; + /*tex Set the value of |output_penalty|. */ + if (type(best_page_break) == penalty_node) { + geq_word_define(int_base + output_penalty_code, + penalty(best_page_break)); + penalty(best_page_break) = inf_penalty; + } else { + geq_word_define(int_base + output_penalty_code, inf_penalty); + } + + for (i = 0; i <= biggest_used_mark; i++) { + if (bot_mark(i) != null) { + if (top_mark(i) != null) + delete_token_ref(top_mark(i)); + set_top_mark(i, bot_mark(i)); + add_token_ref(top_mark(i)); + delete_first_mark(i); + } + } + /*tex + + Put the optimal current page into box |output_box|, update |first_mark| + and |bot_mark|, append insertions to their boxes, and put the remaining + nodes back on the contribution list. + + As the page is finally being prepared for output, pointer |p| runs + through the vlist, with |prev_p| trailing behind; pointer |q| is the tail + of a list of insertions that are being held over for a subsequent page. + + */ + if (c == best_page_break) { + /*tex |c| not yet linked in */ + best_page_break = null; + } + /*tex Ensure that box |output_box| is empty before output. */ + if (box(output_box_par) != null) { + print_err("\\box"); + print_int(output_box_par); + tprint(" is not void"); + help2( + "You shouldn't use \\box\\outputbox except in \\output routines.", + "Proceed, and I'll discard its present contents." + ); + box_error(output_box_par); + } + /*tex This will count the number of insertions held over. */ + insert_penalties = 0; + save_split_top_skip = split_top_skip_par; + if (holding_inserts_par <= 0) { + /*tex + + Prepare all the boxes involved in insertions to act as queues. If + many insertions are supposed to go into the same box, we want to know + the position of the last node in that box, so that we don't need to + waste time when linking further information into it. The + |last_ins_ptr| fields of the page insertion nodes are therefore used + for this purpose during the packaging phase. + + */ + r = vlink(page_ins_head); + while (r != page_ins_head) { + if (best_ins_ptr(r) != null) { + n = subtype(r); + ensure_vbox(n); + if (box(n) == null) + box(n) = new_null_box(); + p = box(n) + list_offset; + while (vlink(p) != null) + p = vlink(p); + last_ins_ptr(r) = p; + } + r = vlink(r); + } + + } + q = hold_head; + vlink(q) = null; + prev_p = page_head; + p = vlink(prev_p); + while (p != best_page_break) { + if (type(p) == ins_node) { + if (holding_inserts_par <= 0) { + /*tex + + Either insert the material specified by node |p| into the + appropriate box, or hold it for the next page; also delete + node |p| from the current page. + + We will set |best_ins_ptr:=null| and package the box + corresponding to insertion node~|r|, just after making the + final insertion into that box. If this final insertion is + `|split_up_node|', the remainder after splitting and pruning + (if any) will be carried over to the next page. + + */ + r = vlink(page_ins_head); + while (subtype(r) != subtype(p)) + r = vlink(r); + if (best_ins_ptr(r) == null) { + wait = true; + } else { + wait = false; + s = last_ins_ptr(r); + vlink(s) = ins_ptr(p); + if (best_ins_ptr(r) == p) { + halfword t; + /*tex + + Wrap up the box specified by node |r|, splitting node + |p| if called for; set |wait:=true| if node |p| holds + a remainder after splitting. + + */ + if (type(r) == split_up_node) { + if ((broken_ins(r) == p) && (broken_ptr(r) != null)) { + while (vlink(s) != broken_ptr(r)) + s = vlink(s); + vlink(s) = null; + split_top_skip_par = split_top_ptr(p); + ins_ptr(p) = + prune_page_top(broken_ptr(r), false); + if (ins_ptr(p) != null) { + t = vpack(ins_ptr(p), 0, additional, -1); + height(p) = height(t) + depth(t); + list_ptr(t) = null; + flush_node(t); + wait = true; + } + } + } + best_ins_ptr(r) = null; + n = subtype(r); + t = list_ptr(box(n)); + list_ptr(box(n)) = null; + flush_node(box(n)); + box(n) = vpack(t, 0, additional, body_direction_par); + + } else { + while (vlink(s) != null) + s = vlink(s); + last_ins_ptr(r) = s; + } + } + /*tex + + Either append the insertion node |p| after node |q|, and + remove it from the current page, or delete |node(p)|. + + */ + try_couple_nodes(prev_p, vlink(p)); + vlink(p) = null; + if (wait) { + couple_nodes(q, p); + q = p; + incr(insert_penalties); + } else { + ins_ptr(p) = null; + flush_node(p); + } + p = prev_p; + + } + } else if (type(p) == mark_node) { + /*tex Update the values of |first_mark| and |bot_mark|. */ + if (first_mark(mark_class(p)) == null) { + set_first_mark(mark_class(p), mark_ptr(p)); + add_token_ref(first_mark(mark_class(p))); + } + if (bot_mark(mark_class(p)) != null) + delete_token_ref(bot_mark(mark_class(p))); + set_bot_mark(mark_class(p), mark_ptr(p)); + add_token_ref(bot_mark(mark_class(p))); + + } + prev_p = p; + p = vlink(prev_p); + } + split_top_skip_par = save_split_top_skip; + /*tex + + Break the current page at node |p|, put it in box~|output_box|, and put + the remaining nodes on the contribution list. + + When the following code is executed, the current page runs from node + |vlink(page_head)| to node |prev_p|, and the nodes from |p| to + |page_tail| are to be placed back at the front of the contribution list. + Furthermore the heldover insertions appear in a list from + |vlink(hold_head)| to |q|; we will put them into the current page list + for safekeeping while the user's output routine is active. We might have + |q=hold_head|; and |p=null| if and only if |prev_p=page_tail|. Error + messages are suppressed within |vpackage|, since the box might appear to + be overfull or underfull simply because the stretch and shrink from the + \.{\\skip} registers for inserts are not actually present in the box. + + */ + if (p != null) { + if (vlink(contrib_head) == null) { + contrib_tail = page_tail; + } + couple_nodes(page_tail,vlink(contrib_head)); + couple_nodes(contrib_head, p); + vlink(prev_p) = null; + } + save_vbadness = vbadness_par; + vbadness_par = inf_bad; + save_vfuzz = vfuzz_par; + /*tex Inhibit error messages. */ + vfuzz_par = max_dimen; + box(output_box_par) = filtered_vpackage(vlink(page_head), + best_size, exactly, page_max_depth, output_group, body_direction_par, 0, 0); + vbadness_par = save_vbadness; + vfuzz_par = save_vfuzz; + if (last_glue != max_halfword) + flush_node(last_glue); + /*tex Start a new current page. This sets |last_glue:=max_halfword|. */ + start_new_page(); + if (q != hold_head) { + vlink(page_head) = vlink(hold_head); + page_tail = q; + } + /*tex Delete the page-insertion nodes. */ + r = vlink(page_ins_head); + while (r != page_ins_head) { + /*tex Todo: couple. */ + q = vlink(r); + flush_node(r); + r = q; + } + vlink(page_ins_head) = page_ins_head; + for (i = 0; i <= biggest_used_mark; i++) { + if ((top_mark(i) != null) && (first_mark(i) == null)) { + set_first_mark(i, top_mark(i)); + add_token_ref(top_mark(i)); + } + } + if (output_routine_par != null) { + if (dead_cycles >= max_dead_cycles_par) { + /*tex Explain that too many dead cycles have occurred in a row. */ + print_err("Output loop---"); + print_int(dead_cycles); + tprint(" consecutive dead cycles"); + help3( + "I've concluded that your \\output is awry; it never does a", + "\\shipout, so I'm shipping \\box\\outputbox out myself. Next time", + "increase \\maxdeadcycles if you want me to be more patient!" + ); + error(); + } else { + /*tex Fire up the users output routine and |return|. */ + output_active = true; + incr(dead_cycles); + push_nest(); + mode = -vmode; + prev_depth_par = ignore_depth; + mode_line_par = -line; + begin_token_list(output_routine_par, output_text); + new_save_level(output_group); + normal_paragraph(); + scan_left_brace(); + return; + } + } + /*tex + + Perform the default output routine. The list of heldover insertions, + running from |vlink(page_head)| to |page_tail|, must be moved to the + contribution list when the user has specified no output routine. + + */ + if (vlink(page_head) != null) { + if (vlink(contrib_head) == null) { + contrib_tail = page_tail; + } else { + vlink(page_tail) = vlink(contrib_head); + } + vlink(contrib_head) = vlink(page_head); + vlink(page_head) = null; + page_tail = page_head; + } + flush_node_list(page_disc); + page_disc = null; + ship_out(static_pdf, box(output_box_par), SHIPPING_PAGE); + box(output_box_par) = null; +} + +/*tex + + When the user's output routine finishes, it has constructed a vlist in + internal vertical mode, and \TeX\ will do the following: + +*/ + +void resume_after_output(void) +{ + if ((iloc != null) || ((token_type != output_text) && (token_type != backed_up))) { + /*tex Recover from an unbalanced output routine */ + print_err("Unbalanced output routine"); + help2( + "Your sneaky output routine has problematic {'s and/or }'s.", + "I can't handle that very well; good luck." + ); + error(); + /*tex Loops forever if reading from a file, since |null=min_halfword<=0|. */ + do { + get_token(); + } while (iloc != null); + } + /*tex Conserve stack space in case more outputs are triggered. */ + end_token_list(); + end_graf(bottom_level); + unsave(); + output_active = false; + insert_penalties = 0; + /*tex Ensure that box |output_box| is empty after output. */ + if (box(output_box_par) != null) { + print_err("Output routine didn't use all of \\box"); + print_int(output_box_par); + help3( + "Your \\output commands should empty \\box\\outputbox,", + "e.g., by saying `\\shipout\\box\\outputbox'.", + "Proceed; I'll discard its present contents." + ); + box_error(output_box_par); + } + if (tail != head) { + /*tex Current list goes after heldover insertions. */ + try_couple_nodes(page_tail, vlink(head)); + page_tail = tail; + } + if (vlink(page_head) != null) { + /* Both go before heldover contributions. */ + if (vlink(contrib_head) == null) + contrib_tail = page_tail; + try_couple_nodes(page_tail, vlink(contrib_head)); + try_couple_nodes(contrib_head, vlink(page_head)); + vlink(page_head) = null; + page_tail = page_head; + } + flush_node_list(page_disc); + page_disc = null; + pop_nest(); + normal_page_filter(after_output); + build_page(); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/buildpage.h b/Build/source/texk/web2c/luatexdir/tex/buildpage.h new file mode 100644 index 00000000000..9c013514efa --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/buildpage.h @@ -0,0 +1,74 @@ +/* buildpage.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef BUILDPAGE_H +# define BUILDPAGE_H + +/* empty = 0 */ +# define inserts_only 1 /* |page_contents| when an insert node has been contributed, but no boxes */ +# define box_there 2 /* |page_contents| when a box or rule has been contributed */ + +extern halfword page_tail; /* the final node on the current page */ +extern int page_contents; /* what is on the current page so far? */ +extern scaled page_max_depth; /* maximum box depth on page being built */ +extern halfword best_page_break; /* break here to get the best page known so far */ +extern int least_page_cost; /* the score for this currently best page */ +extern scaled best_size; /* its |page_goal| */ + +/* +The data structure definitions here use the fact that the |@!height| field +appears in the fourth word of a box node. +@^data structure assumptions@> +*/ + +# define broken_ptr(A) vlink((A)+2) /* an insertion for this class will break here if anywhere */ +# define broken_ins(A) vinfo((A)+2) /* this insertion might break at |broken_ptr| */ +# define last_ins_ptr(A) vlink((A)+3) /* the most recent insertion for this |subtype| */ +# define best_ins_ptr(A) vinfo((A)+3) /* the optimum most recent insertion */ + +extern void initialize_buildpage(void); + +# define page_goal page_so_far[0] /* desired height of information on page being built */ +# define page_total page_so_far[1] /* height of the current page */ +# define page_shrink page_so_far[6] /* shrinkability of the current page */ +# define page_depth page_so_far[7] /* depth of the current page */ + +extern scaled page_so_far[8]; /* height and glue of the current page */ +extern halfword last_glue; /* used to implement \.{\\lastskip */ +extern int last_penalty; /* used to implement \.{\\lastpenalty} */ +extern scaled last_kern; /* used to implement \.{\\lastkern} */ +extern int last_node_type; /* used to implement \.{\\lastnodetype} */ +extern int insert_penalties; /* sum of the penalties for held-over insertions */ + +extern void print_totals(void); +extern void freeze_page_specs(int s); + +extern boolean output_active; + +extern void start_new_page(void); + +# define contrib_tail nest[0].tail_field + /* tail of the contribution list */ + +extern void build_page(void); /* append contributions to the current page */ +extern void fire_up(halfword c); +extern void resume_after_output(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.c b/Build/source/texk/web2c/luatexdir/tex/commands.c new file mode 100644 index 00000000000..261f23f4a83 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/commands.c @@ -0,0 +1,936 @@ +/* + +commands.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +The symbolic names for glue parameters are put into \TeX's hash table by using +the routine called |primitive|, defined below. Let us enter them now, so that we +don't have to list all those parameter names anywhere else. + +*/ + +void initialize_commands(void) +{ + + primitive_tex("lineskip", assign_glue_cmd, glue_base + line_skip_code, glue_base); + primitive_tex("baselineskip", assign_glue_cmd, glue_base + baseline_skip_code, glue_base); + primitive_tex("parskip", assign_glue_cmd, glue_base + par_skip_code, glue_base); + primitive_tex("abovedisplayskip", assign_glue_cmd, glue_base + above_display_skip_code, glue_base); + primitive_tex("belowdisplayskip", assign_glue_cmd, glue_base + below_display_skip_code, glue_base); + primitive_tex("abovedisplayshortskip", assign_glue_cmd, glue_base + above_display_short_skip_code, glue_base); + primitive_tex("belowdisplayshortskip", assign_glue_cmd, glue_base + below_display_short_skip_code, glue_base); + primitive_tex("leftskip", assign_glue_cmd, glue_base + left_skip_code, glue_base); + primitive_tex("rightskip", assign_glue_cmd, glue_base + right_skip_code, glue_base); + primitive_tex("topskip", assign_glue_cmd, glue_base + top_skip_code, glue_base); + primitive_tex("splittopskip", assign_glue_cmd, glue_base + split_top_skip_code, glue_base); + primitive_tex("tabskip", assign_glue_cmd, glue_base + tab_skip_code, glue_base); + primitive_tex("spaceskip", assign_glue_cmd, glue_base + space_skip_code, glue_base); + primitive_tex("xspaceskip", assign_glue_cmd, glue_base + xspace_skip_code, glue_base); + primitive_tex("parfillskip", assign_glue_cmd, glue_base + par_fill_skip_code, glue_base); + primitive_tex("thinmuskip", assign_mu_glue_cmd, glue_base + thin_mu_skip_code, glue_base + thin_mu_skip_code); + primitive_tex("medmuskip", assign_mu_glue_cmd, glue_base + med_mu_skip_code, glue_base + thin_mu_skip_code); + primitive_tex("thickmuskip", assign_mu_glue_cmd, glue_base + thick_mu_skip_code, glue_base + thin_mu_skip_code); + primitive_luatex("mathsurroundskip", assign_glue_cmd, glue_base + math_skip_code, glue_base); + primitive_luatex("mathsurroundmode", assign_int_cmd, int_base + math_skip_mode_code, int_base); + primitive_luatex("mathscriptboxmode", assign_int_cmd, int_base + math_script_box_mode_code, int_base); + primitive_luatex("mathscriptcharmode", assign_int_cmd, int_base + math_script_char_mode_code, int_base); + primitive_luatex("mathrulethicknessmode", assign_int_cmd, int_base + math_rule_thickness_mode_code, int_base); + primitive_luatex("mathflattenmode", assign_int_cmd, int_base + math_flatten_mode_code, int_base); + primitive_tex("output", assign_toks_cmd, output_routine_loc, local_base); + primitive_tex("everypar", assign_toks_cmd, every_par_loc, local_base); + primitive_tex("everymath", assign_toks_cmd, every_math_loc, local_base); + primitive_tex("everydisplay", assign_toks_cmd, every_display_loc, local_base); + primitive_tex("everyhbox", assign_toks_cmd, every_hbox_loc, local_base); + primitive_tex("everyvbox", assign_toks_cmd, every_vbox_loc, local_base); + primitive_tex("everyjob", assign_toks_cmd, every_job_loc, local_base); + primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base); + primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base); + + /*tex The integer parameter names must be entered into the hash table */ + + primitive_tex("pretolerance", assign_int_cmd, int_base + pretolerance_code, int_base); + primitive_tex("tolerance", assign_int_cmd, int_base + tolerance_code, int_base); + primitive_tex("linepenalty", assign_int_cmd, int_base + line_penalty_code, int_base); + primitive_tex("hyphenpenalty", assign_int_cmd, int_base + hyphen_penalty_code, int_base); + primitive_tex("exhyphenpenalty", assign_int_cmd, int_base + ex_hyphen_penalty_code, int_base); + primitive_tex("clubpenalty", assign_int_cmd, int_base + club_penalty_code, int_base); + primitive_tex("widowpenalty", assign_int_cmd, int_base + widow_penalty_code, int_base); + primitive_tex("displaywidowpenalty", assign_int_cmd, int_base + display_widow_penalty_code, int_base); + primitive_tex("brokenpenalty", assign_int_cmd, int_base + broken_penalty_code, int_base); + primitive_tex("binoppenalty", assign_int_cmd, int_base + bin_op_penalty_code, int_base); + primitive_luatex("prerelpenalty", assign_int_cmd, int_base + pre_rel_penalty_code, int_base); + primitive_luatex("prebinoppenalty", assign_int_cmd, int_base + pre_bin_op_penalty_code, int_base); + primitive_tex("relpenalty", assign_int_cmd, int_base + rel_penalty_code, int_base); + primitive_tex("predisplaypenalty", assign_int_cmd, int_base + pre_display_penalty_code, int_base); + primitive_tex("postdisplaypenalty", assign_int_cmd, int_base + post_display_penalty_code, int_base); + primitive_luatex("mathpenaltiesmode", assign_int_cmd, int_base + math_penalties_mode_code, int_base); + primitive_luatex("mathdelimitersmode", assign_int_cmd, int_base + math_delimiters_mode_code, int_base); + primitive_tex("interlinepenalty", assign_int_cmd, int_base + inter_line_penalty_code, int_base); + primitive_tex("doublehyphendemerits", assign_int_cmd, int_base + double_hyphen_demerits_code, int_base); + primitive_tex("finalhyphendemerits", assign_int_cmd, int_base + final_hyphen_demerits_code, int_base); + primitive_tex("adjdemerits", assign_int_cmd, int_base + adj_demerits_code, int_base); + primitive_tex("mag", assign_int_cmd, int_base + mag_code, int_base); + primitive_tex("delimiterfactor", assign_int_cmd, int_base + delimiter_factor_code, int_base); + primitive_tex("looseness", assign_int_cmd, int_base + looseness_code, int_base); + primitive_tex("time", assign_int_cmd, int_base + time_code, int_base); + primitive_tex("day", assign_int_cmd, int_base + day_code, int_base); + primitive_tex("month", assign_int_cmd, int_base + month_code, int_base); + primitive_tex("year", assign_int_cmd, int_base + year_code, int_base); + primitive_tex("showboxbreadth", assign_int_cmd, int_base + show_box_breadth_code, int_base); + primitive_tex("showboxdepth", assign_int_cmd, int_base + show_box_depth_code, int_base); + primitive_tex("hbadness", assign_int_cmd, int_base + hbadness_code, int_base); + primitive_tex("vbadness", assign_int_cmd, int_base + vbadness_code, int_base); + primitive_tex("pausing", assign_int_cmd, int_base + pausing_code, int_base); + primitive_tex("tracingonline", assign_int_cmd, int_base + tracing_online_code, int_base); + primitive_tex("tracingmacros", assign_int_cmd, int_base + tracing_macros_code, int_base); + primitive_tex("tracingstats", assign_int_cmd, int_base + tracing_stats_code, int_base); + primitive_tex("tracingparagraphs", assign_int_cmd, int_base + tracing_paragraphs_code, int_base); + primitive_tex("tracingpages", assign_int_cmd, int_base + tracing_pages_code, int_base); + primitive_tex("tracingoutput", assign_int_cmd, int_base + tracing_output_code, int_base); + primitive_tex("tracinglostchars", assign_int_cmd, int_base + tracing_lost_chars_code, int_base); + primitive_tex("tracingcommands", assign_int_cmd, int_base + tracing_commands_code, int_base); + primitive_tex("tracingrestores", assign_int_cmd, int_base + tracing_restores_code, int_base); + primitive_tex("uchyph", assign_int_cmd, int_base + uc_hyph_code, int_base); + primitive_tex("outputpenalty", assign_int_cmd, int_base + output_penalty_code, int_base); + primitive_tex("maxdeadcycles", assign_int_cmd, int_base + max_dead_cycles_code, int_base); + primitive_tex("hangafter", assign_int_cmd, int_base + hang_after_code, int_base); + primitive_tex("floatingpenalty", assign_int_cmd, int_base + floating_penalty_code, int_base); + primitive_tex("globaldefs", assign_int_cmd, int_base + global_defs_code, int_base); + primitive_tex("fam", assign_int_cmd, int_base + cur_fam_code, int_base); + primitive_tex("escapechar", assign_int_cmd, int_base + escape_char_code, int_base); + primitive_tex("defaulthyphenchar", assign_int_cmd, int_base + default_hyphen_char_code, int_base); + primitive_tex("defaultskewchar", assign_int_cmd, int_base + default_skew_char_code, int_base); + primitive_tex("endlinechar", assign_int_cmd, int_base + end_line_char_code, int_base); + primitive_tex("newlinechar", assign_int_cmd, int_base + new_line_char_code, int_base); + primitive_tex("language", assign_int_cmd, int_base + language_code, int_base); + primitive_tex("setlanguage", assign_int_cmd, int_base + cur_lang_code, int_base); + primitive_tex("firstvalidlanguage", assign_int_cmd, int_base + first_valid_language_code, int_base); + primitive_tex("exhyphenchar", assign_int_cmd, int_base + ex_hyphen_char_code, int_base); + primitive_tex("lefthyphenmin", assign_int_cmd, int_base + left_hyphen_min_code, int_base); + primitive_tex("righthyphenmin", assign_int_cmd, int_base + right_hyphen_min_code, int_base); + primitive_tex("holdinginserts", assign_int_cmd, int_base + holding_inserts_code, int_base); + primitive_tex("errorcontextlines", assign_int_cmd, int_base + error_context_lines_code, int_base); + primitive_luatex("nokerns", assign_int_cmd, int_base + disable_kern_code, int_base); + primitive_luatex("noligs", assign_int_cmd, int_base + disable_lig_code, int_base); + primitive_luatex("nospaces", assign_int_cmd, int_base + disable_space_code, int_base); + primitive_luatex("catcodetable", assign_int_cmd, int_base + cat_code_table_code, int_base); + primitive_luatex("outputbox", assign_int_cmd, int_base + output_box_code, int_base); + primitive_luatex("outputmode", assign_int_cmd, int_base + output_mode_code, int_base); + primitive_luatex("adjustspacing", assign_int_cmd, int_base + adjust_spacing_code, int_base); + primitive_luatex("protrudechars", assign_int_cmd, int_base + protrude_chars_code, int_base); + primitive_luatex("tracingfonts", assign_int_cmd, int_base + tracing_fonts_code, int_base); + primitive_luatex("draftmode", assign_int_cmd, int_base + draft_mode_code, int_base); + primitive_tex("parindent", assign_dimen_cmd, dimen_base + par_indent_code, dimen_base); + primitive_tex("mathsurround", assign_dimen_cmd, dimen_base + math_surround_code, dimen_base); + primitive_tex("lineskiplimit", assign_dimen_cmd, dimen_base + line_skip_limit_code, dimen_base); + primitive_tex("hsize", assign_dimen_cmd, dimen_base + hsize_code, dimen_base); + primitive_tex("vsize", assign_dimen_cmd, dimen_base + vsize_code, dimen_base); + primitive_tex("maxdepth", assign_dimen_cmd, dimen_base + max_depth_code, dimen_base); + primitive_tex("splitmaxdepth", assign_dimen_cmd, dimen_base + split_max_depth_code, dimen_base); + primitive_tex("boxmaxdepth", assign_dimen_cmd, dimen_base + box_max_depth_code, dimen_base); + primitive_tex("hfuzz", assign_dimen_cmd, dimen_base + hfuzz_code, dimen_base); + primitive_tex("vfuzz", assign_dimen_cmd, dimen_base + vfuzz_code, dimen_base); + primitive_tex("delimitershortfall", assign_dimen_cmd, dimen_base + delimiter_shortfall_code, dimen_base); + primitive_tex("nulldelimiterspace", assign_dimen_cmd, dimen_base + null_delimiter_space_code, dimen_base); + primitive_tex("scriptspace", assign_dimen_cmd, dimen_base + script_space_code, dimen_base); + primitive_tex("predisplaysize", assign_dimen_cmd, dimen_base + pre_display_size_code, dimen_base); + primitive_tex("displaywidth", assign_dimen_cmd, dimen_base + display_width_code, dimen_base); + primitive_tex("displayindent", assign_dimen_cmd, dimen_base + display_indent_code, dimen_base); + primitive_tex("overfullrule", assign_dimen_cmd, dimen_base + overfull_rule_code, dimen_base); + primitive_tex("hangindent", assign_dimen_cmd, dimen_base + hang_indent_code, dimen_base); + primitive_tex("hoffset", assign_dimen_cmd, dimen_base + h_offset_code, dimen_base); + primitive_tex("voffset", assign_dimen_cmd, dimen_base + v_offset_code, dimen_base); + primitive_tex("emergencystretch", assign_dimen_cmd, dimen_base + emergency_stretch_code, dimen_base); + primitive_luatex("pagewidth", assign_dimen_cmd, dimen_base + page_width_code, dimen_base); + primitive_luatex("pageheight", assign_dimen_cmd, dimen_base + page_height_code, dimen_base); + primitive_luatex("pxdimen", assign_dimen_cmd, dimen_base + px_dimen_code, dimen_base); + primitive_luatex("predisplaygapfactor", assign_int_cmd, int_base + math_pre_display_gap_factor_code, int_base); + primitive_luatex("hyphenpenaltymode", assign_int_cmd, int_base + hyphen_penalty_mode_code, int_base); + primitive_luatex("automatichyphenpenalty", assign_int_cmd, int_base + automatic_hyphen_penalty_code, int_base); + primitive_luatex("explicithyphenpenalty", assign_int_cmd, int_base + explicit_hyphen_penalty_code, int_base); + primitive_luatex("automatichyphenmode", assign_int_cmd, int_base + automatic_hyphen_mode_code, int_base); + primitive_luatex("compoundhyphenmode", assign_int_cmd, int_base + compound_hyphen_mode_code, int_base); + primitive_luatex("breakafterdirmode", assign_int_cmd, int_base + break_after_dir_mode_code, int_base); + primitive_luatex("exceptionpenalty", assign_int_cmd, int_base + exception_penalty_code, int_base); + primitive_luatex("fixupboxesmode", assign_int_cmd, int_base + fixup_boxes_code, int_base); + primitive_luatex("glyphdimensionsmode", assign_int_cmd, int_base + glyph_dimensions_code, int_base); + + /*tex + + Many of \TeX's primitives need no |equiv|, since they are identifiable by + their |eq_type| alone. These primitives are loaded into the hash table as + follows: + + */ + + primitive_tex(" ", ex_space_cmd, 0, 0); + primitive_tex("/", ital_corr_cmd, 0, 0); + primitive_tex("accent", accent_cmd, 0, 0); + primitive_tex("advance", advance_cmd, 0, 0); + primitive_tex("afterassignment", after_assignment_cmd, 0, 0); + primitive_tex("aftergroup", after_group_cmd, 0, 0); + primitive_tex("begingroup", begin_group_cmd, 0, 0); + primitive_tex("char", char_num_cmd, 0, 0); + primitive_tex("csname", cs_name_cmd, 0, 0); + primitive_luatex("lastnamedcs", cs_name_cmd, 1, 0); + primitive_luatex("begincsname", cs_name_cmd, 2, 0); + primitive_tex("delimiter", delim_num_cmd, 0, 0); + primitive_luatex("Udelimiter", delim_num_cmd, 1, 0); + primitive_tex("divide", divide_cmd, 0, 0); + primitive_tex("endcsname", end_cs_name_cmd, 0, 0); + primitive_tex("endgroup", end_group_cmd, 0, 0); + cs_text(frozen_end_group) = maketexstring("endgroup"); + eqtb[frozen_end_group] = eqtb[cur_val]; + primitive_tex("expandafter", expand_after_cmd, 0, 0); + primitive_tex("font", def_font_cmd, 0, 0); + primitive_luatex("letterspacefont", letterspace_font_cmd, 0, 0); + primitive_luatex("expandglyphsinfont", normal_cmd, expand_font_code, 0); + primitive_luatex("copyfont", copy_font_cmd, 0, 0); + primitive_luatex("setfontid", set_font_id_cmd, 0, 0); + primitive_tex("fontdimen", assign_font_dimen_cmd, 0, 0); + primitive_tex("halign", halign_cmd, 0, 0); + primitive_tex("hrule", hrule_cmd, 0, 0); + primitive_luatex("nohrule", no_hrule_cmd, 0, 0); + primitive_tex("ignorespaces", ignore_spaces_cmd, 0, 0); + primitive_tex("insert", insert_cmd, 0, 0); + primitive_luatex("leftghost", char_ghost_cmd, 0, 0); + primitive_tex("mark", mark_cmd, 0, 0); + primitive_tex("mathaccent", math_accent_cmd, 0, 0); + primitive_luatex("Umathaccent", math_accent_cmd, 1, 0); + primitive_tex("mathchar", math_char_num_cmd, 0, 0); + primitive_luatex("Umathchar", math_char_num_cmd, 1, 0); + primitive_luatex("Umathcharnum", math_char_num_cmd, 2, 0); + primitive_tex("mathchoice", math_choice_cmd, 0, 0); + primitive_luatex("Ustack", math_choice_cmd, 1, 0); + primitive_tex("multiply", multiply_cmd, 0, 0); + primitive_tex("noalign", no_align_cmd, 0, 0); + primitive_tex("noboundary", boundary_cmd, 0, 0); + primitive_tex("boundary", boundary_cmd, 1, 0); + primitive_tex("protrusionboundary", boundary_cmd, 2, 0); + primitive_tex("wordboundary", boundary_cmd, 3, 0); + primitive_tex("noexpand", no_expand_cmd, 0, 0); + primitive_luatex("primitive", no_expand_cmd, 1, 0); + primitive_tex("nonscript", non_script_cmd, 0, 0); + primitive_tex("omit", omit_cmd, 0, 0); + primitive_tex("parshape", set_tex_shape_cmd, par_shape_loc, par_shape_loc); + primitive_tex("penalty", break_penalty_cmd, 0, 0); + primitive_tex("prevgraf", set_prev_graf_cmd, 0, 0); + primitive_tex("radical", radical_cmd, 0, 0); + primitive_luatex("Uradical", radical_cmd, 1, 0); + primitive_luatex("Uroot", radical_cmd, 2, 0); + primitive_luatex("Uunderdelimiter", radical_cmd, 3, 0); + primitive_luatex("Uoverdelimiter", radical_cmd, 4, 0); + primitive_luatex("Udelimiterunder", radical_cmd, 5, 0); + primitive_luatex("Udelimiterover", radical_cmd, 6, 0); + primitive_luatex("Uhextensible", radical_cmd, 7, 0); + primitive_tex("read", read_to_cs_cmd, 0, 0); + primitive_tex("relax", relax_cmd, too_big_char, too_big_char); + cs_text(frozen_relax) = maketexstring("relax"); + eqtb[frozen_relax] = eqtb[cur_val]; + primitive_luatex("rightghost", char_ghost_cmd, 1, 0); + primitive_tex("setbox", set_box_cmd, 0, 0); + primitive_tex("the", the_cmd, 0, 0); + primitive_luatex("toksapp", combine_toks_cmd, 0, 0); + primitive_luatex("etoksapp", combine_toks_cmd, 1, 0); + primitive_luatex("tokspre", combine_toks_cmd, 2, 0); + primitive_luatex("etokspre", combine_toks_cmd, 3, 0); + primitive_luatex("gtoksapp", combine_toks_cmd, 4, 0); + primitive_luatex("xtoksapp", combine_toks_cmd, 5, 0); + primitive_luatex("gtokspre", combine_toks_cmd, 6, 0); + primitive_luatex("xtokspre", combine_toks_cmd, 7, 0); + primitive_tex("toks", toks_register_cmd, 0, 0); + primitive_tex("vadjust", vadjust_cmd, 0, 0); + primitive_tex("valign", valign_cmd, 0, 0); + primitive_tex("vcenter", vcenter_cmd, 0, 0); + primitive_tex("vrule", vrule_cmd, 0, 0); + primitive_luatex("novrule", no_vrule_cmd, 0, 0); + primitive_luatex("luafunctioncall", lua_function_call_cmd, 0, 0); + primitive_luatex("luabytecodecall", lua_bytecode_call_cmd, 0, 0); + primitive_luatex("luadef", def_lua_call_cmd, 0, 0); + primitive_tex("par", par_end_cmd, too_big_char, too_big_char); + par_loc = cur_val; + par_token = cs_token_flag + par_loc; + + /*tex + The processing of \.{\\input} involves the |start_input| subroutine, + which will be declared later; the processing of \.{\\endinput} is trivial. + */ + + primitive_tex("input", input_cmd, 0, 0); + primitive_tex("endinput", input_cmd, 1, 0); + primitive_tex("topmark", top_bot_mark_cmd, top_mark_code, 0); + primitive_tex("firstmark", top_bot_mark_cmd, first_mark_code, 0); + primitive_tex("botmark", top_bot_mark_cmd, bot_mark_code, 0); + primitive_tex("splitfirstmark", top_bot_mark_cmd, split_first_mark_code, 0); + primitive_tex("splitbotmark", top_bot_mark_cmd, split_bot_mark_code, 0); + primitive_luatex("clearmarks", mark_cmd, clear_marks_code, 0); + primitive_etex("marks", mark_cmd, marks_code, 0); + primitive_etex("topmarks", top_bot_mark_cmd, top_mark_code + marks_code, 0); + primitive_etex("firstmarks", top_bot_mark_cmd, first_mark_code + marks_code, 0); + primitive_etex("botmarks", top_bot_mark_cmd, bot_mark_code + marks_code, 0); + primitive_etex("splitfirstmarks", top_bot_mark_cmd, split_first_mark_code + marks_code, 0); + primitive_etex("splitbotmarks", top_bot_mark_cmd, split_bot_mark_code + marks_code, 0); + + /* + The hash table is initialized with `\.{\\count}', `\.{\\attribute}', + `\.{\\dimen}', `\.{\\skip}', and `\.{\\muskip}' all having |register| + as their command code; they are distinguished by the |chr_code|, which + is either |int_val|, |attr_val|, |dimen_val|, |glue_val|, or |mu_val|. + */ + + primitive_tex("count", register_cmd, int_val_level, 0); + primitive_luatex("attribute", register_cmd, attr_val_level, 0); + primitive_tex("dimen", register_cmd, dimen_val_level, 0); + primitive_tex("skip", register_cmd, glue_val_level, 0); + primitive_tex("muskip", register_cmd, mu_val_level, 0); + + primitive_tex("spacefactor", set_aux_cmd, hmode, 0); + primitive_tex("prevdepth", set_aux_cmd, vmode, 0); + primitive_tex("deadcycles", set_page_int_cmd, 0, 0); + primitive_tex("insertpenalties", set_page_int_cmd, 1, 0); + primitive_tex("wd", set_box_dimen_cmd, width_offset, 0); + primitive_tex("ht", set_box_dimen_cmd, height_offset, 0); + primitive_tex("dp", set_box_dimen_cmd, depth_offset, 0); + primitive_tex("lastpenalty", last_item_cmd, lastpenalty_code, 0); + primitive_tex("lastkern", last_item_cmd, lastkern_code, 0); + primitive_tex("lastskip", last_item_cmd, lastskip_code, 0); + primitive_tex("inputlineno", last_item_cmd, input_line_no_code, 0); + primitive_tex("badness", last_item_cmd, badness_code, 0); + primitive_luatex("luatexversion", last_item_cmd, luatex_version_code, 0); + primitive_luatex("lastsavedboxresourceindex", last_item_cmd, last_saved_box_resource_index_code, 0); + primitive_luatex("lastsavedimageresourceindex", last_item_cmd, last_saved_image_resource_index_code, 0); + primitive_luatex("lastsavedimageresourcepages", last_item_cmd, last_saved_image_resource_pages_code, 0); + primitive_luatex("lastxpos", last_item_cmd, last_x_pos_code, 0); + primitive_luatex("lastypos", last_item_cmd, last_y_pos_code, 0); + primitive_luatex("randomseed", last_item_cmd, random_seed_code, 0); + + primitive_tex("number", convert_cmd, number_code, 0); + primitive_tex("romannumeral", convert_cmd, roman_numeral_code, 0); + primitive_tex("string", convert_cmd, string_code, 0); + primitive_luatex("csstring", convert_cmd, cs_string_code, 0); + primitive_tex("meaning", convert_cmd, meaning_code, 0); + primitive_etex("eTeXVersion", convert_cmd, etex_code, 0); + primitive_tex("fontname", convert_cmd, font_name_code, 0); + primitive_luatex("fontid", convert_cmd, font_id_code, 0); + primitive_luatex("luatexrevision", convert_cmd, luatex_revision_code, 0); + primitive_luatex("luatexbanner", convert_cmd, luatex_banner_code, 0); + primitive_luatex("leftmarginkern", convert_cmd, left_margin_kern_code, 0); + primitive_luatex("rightmarginkern", convert_cmd, right_margin_kern_code, 0); + primitive_luatex("uniformdeviate", convert_cmd, uniform_deviate_code, 0); + primitive_luatex("normaldeviate", convert_cmd, normal_deviate_code, 0); + primitive_core("directlua", convert_cmd, lua_code, 0); + primitive_luatex("luafunction", convert_cmd, lua_function_code, 0); + primitive_luatex("luabytecode", convert_cmd, lua_bytecode_code, 0); + primitive_luatex("luaescapestring", convert_cmd, lua_escape_string_code, 0); + primitive_luatex("mathstyle", convert_cmd, math_style_code, 0); + primitive_luatex("expanded", convert_cmd, expanded_code, 0); + primitive_luatex("immediateassignment", convert_cmd, immediate_assignment_code, 0); + primitive_luatex("immediateassigned", convert_cmd, immediate_assigned_code, 0); + primitive_tex("jobname", convert_cmd, job_name_code, 0); + primitive_luatex("formatname", convert_cmd, format_name_code, 0); + primitive_luatex("Uchar", convert_cmd, uchar_code, 0); + + primitive_luatex("Umathcharclass", convert_cmd, math_char_class_code, 0); + primitive_luatex("Umathcharfam", convert_cmd, math_char_fam_code, 0); + primitive_luatex("Umathcharslot", convert_cmd, math_char_slot_code, 0); + + primitive_tex("if", if_test_cmd, if_char_code, 0); + primitive_tex("ifcat", if_test_cmd, if_cat_code, 0); + primitive_tex("ifnum", if_test_cmd, if_int_code, 0); + primitive_tex("ifdim", if_test_cmd, if_dim_code, 0); + primitive_tex("ifodd", if_test_cmd, if_odd_code, 0); + primitive_tex("ifvmode", if_test_cmd, if_vmode_code, 0); + primitive_tex("ifhmode", if_test_cmd, if_hmode_code, 0); + primitive_tex("ifmmode", if_test_cmd, if_mmode_code, 0); + primitive_tex("ifinner", if_test_cmd, if_inner_code, 0); + primitive_tex("ifvoid", if_test_cmd, if_void_code, 0); + primitive_tex("ifhbox", if_test_cmd, if_hbox_code, 0); + primitive_tex("ifvbox", if_test_cmd, if_vbox_code, 0); + primitive_tex("ifx", if_test_cmd, if_x_code, 0); + primitive_tex("ifeof", if_test_cmd, if_eof_code, 0); + primitive_tex("iftrue", if_test_cmd, if_true_code, 0); + primitive_tex("iffalse", if_test_cmd, if_false_code, 0); + primitive_tex("ifcase", if_test_cmd, if_case_code, 0); + primitive_luatex("ifprimitive", if_test_cmd, if_primitive_code, 0); + primitive_luatex("ifcondition", if_test_cmd, if_condition_code, 0); + primitive_tex("fi", fi_or_else_cmd, fi_code, 0); + cs_text(frozen_fi) = maketexstring("fi"); + eqtb[frozen_fi] = eqtb[cur_val]; + primitive_tex("or", fi_or_else_cmd, or_code, 0); + primitive_tex("else", fi_or_else_cmd, else_code, 0); + + /* + \TeX\ always knows at least one font, namely the null font. It has no + characters, and its seven parameters are all equal to zero. + */ + + primitive_tex("nullfont", set_font_cmd, null_font, 0); + cs_text(frozen_null_font) = maketexstring("nullfont"); + eqtb[frozen_null_font] = eqtb[cur_val]; + + primitive_tex("span", tab_mark_cmd, span_code, tab_mark_cmd_code); + primitive_luatex("aligntab", tab_mark_cmd, tab_mark_cmd_code, tab_mark_cmd_code); + primitive_luatex("alignmark", mac_param_cmd, tab_mark_cmd_code, tab_mark_cmd_code); + primitive_tex("cr", car_ret_cmd, cr_code, cr_code); + cs_text(frozen_cr) = maketexstring("cr"); + eqtb[frozen_cr] = eqtb[cur_val]; + primitive_tex("crcr", car_ret_cmd, cr_cr_code, cr_code); + cs_text(frozen_end_template) = maketexstring("endtemplate"); + cs_text(frozen_endv) = maketexstring("endtemplate"); + set_eq_type(frozen_endv, endv_cmd); + set_equiv(frozen_endv, null_list); + set_eq_level(frozen_endv, level_one); + eqtb[frozen_end_template] = eqtb[frozen_endv]; + set_eq_type(frozen_end_template, end_template_cmd); + + primitive_tex("pagegoal", set_page_dimen_cmd, 0, 0); + primitive_tex("pagetotal", set_page_dimen_cmd, 1, 0); + primitive_tex("pagestretch", set_page_dimen_cmd, 2, 0); + primitive_tex("pagefilstretch", set_page_dimen_cmd, 3, 0); + primitive_tex("pagefillstretch", set_page_dimen_cmd, 4, 0); + primitive_tex("pagefilllstretch", set_page_dimen_cmd, 5, 0); + primitive_tex("pageshrink", set_page_dimen_cmd, 6, 0); + primitive_tex("pagedepth", set_page_dimen_cmd, 7, 0); + + /* + Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the + endgame, since both of them have `|stop|' as their command code. + */ + + primitive_tex("end", stop_cmd, 0, 0); + primitive_tex("dump", stop_cmd, 1, 0); + + primitive_tex("hskip", hskip_cmd, skip_code, 0); + primitive_tex("hfil", hskip_cmd, fil_code, 0); + primitive_tex("hfill", hskip_cmd, fill_code, 0); + primitive_tex("hss", hskip_cmd, ss_code, 0); + primitive_tex("hfilneg", hskip_cmd, fil_neg_code, 0); + primitive_tex("vskip", vskip_cmd, skip_code, 0); + primitive_tex("vfil", vskip_cmd, fil_code, 0); + primitive_tex("vfill", vskip_cmd, fill_code, 0); + primitive_tex("vss", vskip_cmd, ss_code, 0); + primitive_tex("vfilneg", vskip_cmd, fil_neg_code, 0); + primitive_tex("mskip", mskip_cmd, mskip_code, 0); + primitive_tex("kern", kern_cmd, explicit_kern, 0); + primitive_tex("mkern", mkern_cmd, mu_glue, 0); + primitive_tex("moveleft", hmove_cmd, 1, 0); + primitive_tex("moveright", hmove_cmd, 0, 0); + primitive_tex("raise", vmove_cmd, 1, 0); + primitive_tex("lower", vmove_cmd, 0, 0); + primitive_tex("box", make_box_cmd, box_code, 0); + primitive_tex("copy", make_box_cmd, copy_code, 0); + primitive_tex("lastbox", make_box_cmd, last_box_code, 0); + primitive_tex("vsplit", make_box_cmd, vsplit_code, 0); + primitive_tex("tpack", make_box_cmd, tpack_code, 0); + primitive_tex("vpack", make_box_cmd, vpack_code, 0); + primitive_tex("hpack", make_box_cmd, hpack_code, 0); + primitive_tex("vtop", make_box_cmd, vtop_code, 0); + primitive_tex("vbox", make_box_cmd, vtop_code + vmode, 0); + primitive_tex("hbox", make_box_cmd, vtop_code + hmode, 0); + primitive_tex("shipout", leader_ship_cmd, a_leaders - 2, 0); /* |ship_out_flag=leader_flag-2| */ + primitive_tex("leaders", leader_ship_cmd, a_leaders, 0); + primitive_tex("cleaders", leader_ship_cmd, c_leaders, 0); + primitive_tex("xleaders", leader_ship_cmd, x_leaders, 0); + primitive_luatex("gleaders", leader_ship_cmd, g_leaders, 0); + primitive_luatex("boxdir", assign_box_dir_cmd, 0, 0); + primitive_luatex("boxdirection", assign_box_direction_cmd, 0, 0); + primitive_tex("indent", start_par_cmd, 1, 0); + primitive_tex("noindent", start_par_cmd, 0, 0); + primitive_luatex("quitvmode", start_par_cmd, 2, 0); + primitive_tex("unpenalty", remove_item_cmd, penalty_node, 0); + primitive_tex("unkern", remove_item_cmd, kern_node, 0); + primitive_tex("unskip", remove_item_cmd, glue_node, 0); + primitive_tex("unhbox", un_hbox_cmd, box_code, 0); + primitive_tex("unhcopy", un_hbox_cmd, copy_code, 0); + primitive_tex("unvbox", un_vbox_cmd, box_code, 0); + primitive_tex("unvcopy", un_vbox_cmd, copy_code, 0); + primitive_tex("-", discretionary_cmd, explicit_disc, 0); /* good old tex */ + primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0); + primitive_luatex("explicitdiscretionary", discretionary_cmd, explicit_disc, 0); + primitive_luatex("automaticdiscretionary", discretionary_cmd, automatic_disc, 0); + primitive_luatex("localleftbox", assign_local_box_cmd, 0, 0); + primitive_luatex("localrightbox", assign_local_box_cmd, 1, 0); + + primitive_luatex("Ustartmath", math_shift_cs_cmd, text_style, 0); + primitive_luatex("Ustopmath", math_shift_cs_cmd, cramped_text_style, 0); + primitive_luatex("Ustartdisplaymath", math_shift_cs_cmd, display_style, 0); + primitive_luatex("Ustopdisplaymath", math_shift_cs_cmd, cramped_display_style, 0); + primitive_tex("eqno", eq_no_cmd, 0, 0); + primitive_tex("leqno", eq_no_cmd, 1, 0); + primitive_tex("mathord", math_comp_cmd, ord_noad_type, 0); + primitive_tex("mathop", math_comp_cmd, op_noad_type_normal, 0); + primitive_tex("mathbin", math_comp_cmd, bin_noad_type, 0); + primitive_tex("mathrel", math_comp_cmd, rel_noad_type, 0); + primitive_tex("mathopen", math_comp_cmd, open_noad_type, 0); + primitive_tex("mathclose", math_comp_cmd, close_noad_type, 0); + primitive_tex("mathpunct", math_comp_cmd, punct_noad_type, 0); + primitive_tex("mathinner", math_comp_cmd, inner_noad_type, 0); + primitive_tex("underline", math_comp_cmd, under_noad_type, 0); + primitive_tex("overline", math_comp_cmd, over_noad_type, 0); + primitive_tex("displaylimits", limit_switch_cmd, op_noad_type_normal, 0); + primitive_tex("limits", limit_switch_cmd, op_noad_type_limits, 0); + primitive_tex("nolimits", limit_switch_cmd, op_noad_type_no_limits, 0); + primitive_tex("displaystyle", math_style_cmd, display_style, 0); + primitive_tex("textstyle", math_style_cmd, text_style, 0); + primitive_tex("scriptstyle", math_style_cmd, script_style, 0); + primitive_tex("scriptscriptstyle", math_style_cmd, script_script_style, 0); + primitive_luatex("crampeddisplaystyle", math_style_cmd, cramped_display_style, 0); + primitive_luatex("crampedtextstyle", math_style_cmd, cramped_text_style, 0); + primitive_luatex("crampedscriptstyle", math_style_cmd, cramped_script_style, 0); + primitive_luatex("crampedscriptscriptstyle", math_style_cmd, cramped_script_script_style, 0); + primitive_luatex("Usuperscript", super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd); + primitive_luatex("Usubscript", super_sub_script_cmd, sub_mark_cmd, sup_mark_cmd); + primitive_luatex("Unosuperscript", no_super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd); + primitive_luatex("Unosubscript", no_super_sub_script_cmd, sub_mark_cmd, sup_mark_cmd); + primitive_tex("above", above_cmd, above_code, 0); + primitive_tex("over", above_cmd, over_code, 0); + primitive_tex("atop", above_cmd, atop_code, 0); + primitive_luatex("Uskewed", above_cmd, skewed_code, 0); + primitive_tex("abovewithdelims", above_cmd, delimited_code + above_code, 0); + primitive_tex("overwithdelims", above_cmd, delimited_code + over_code, 0); + primitive_tex("atopwithdelims", above_cmd, delimited_code + atop_code, 0); + primitive_luatex("Uskewedwithdelims", above_cmd, delimited_code + skewed_code, 0); + primitive_tex("left", left_right_cmd, left_noad_side, 0); + primitive_tex("right", left_right_cmd, right_noad_side, 0); + primitive_tex("middle", left_right_cmd, middle_noad_side, 0); + primitive_tex("Uleft", left_right_cmd, 10+left_noad_side, 0); + primitive_tex("Uright", left_right_cmd, 10+right_noad_side, 0); + primitive_tex("Umiddle", left_right_cmd, 10+middle_noad_side, 0); + primitive_luatex("Uvextensible", left_right_cmd, 10+no_noad_side, 0); + cs_text(frozen_right) = maketexstring("right"); + eqtb[frozen_right] = eqtb[cur_val]; + + primitive_tex("long", prefix_cmd, 1, 0); + primitive_tex("outer", prefix_cmd, 2, 0); + primitive_tex("global", prefix_cmd, 4, 0); + primitive_tex("def", def_cmd, 0, 0); + primitive_tex("gdef", def_cmd, 1, 0); + primitive_tex("edef", def_cmd, 2, 0); + primitive_tex("xdef", def_cmd, 3, 0); + primitive_tex("glet", let_cmd, 0, 0); + primitive_tex("let", let_cmd, 1, 0); + primitive_tex("futurelet", let_cmd, 2, 0); + primitive_luatex("letcharcode", let_cmd, 3, 0); + primitive_tex("chardef", shorthand_def_cmd, char_def_code, 0); + primitive_tex("mathchardef", shorthand_def_cmd, math_char_def_code, 0); + primitive_luatex("Umathchardef", shorthand_def_cmd, xmath_char_def_code, 0); + primitive_luatex("Umathcharnumdef", shorthand_def_cmd, umath_char_def_code, 0); + primitive_tex("countdef", shorthand_def_cmd, count_def_code, 0); + primitive_luatex("attributedef", shorthand_def_cmd, attribute_def_code, 0); + primitive_tex("dimendef", shorthand_def_cmd, dimen_def_code, 0); + primitive_tex("skipdef", shorthand_def_cmd, skip_def_code, 0); + primitive_tex("muskipdef", shorthand_def_cmd, mu_skip_def_code, 0); + primitive_tex("toksdef", shorthand_def_cmd, toks_def_code, 0); + primitive_tex("catcode", def_char_code_cmd, cat_code_base, cat_code_base); + primitive_tex("mathcode", def_char_code_cmd, math_code_base, cat_code_base); + primitive_tex("lccode", def_char_code_cmd, lc_code_base, cat_code_base); + primitive_tex("uccode", def_char_code_cmd, uc_code_base, cat_code_base); + primitive_tex("sfcode", def_char_code_cmd, sf_code_base, cat_code_base); + primitive_tex("delcode", def_del_code_cmd, del_code_base, del_code_base); + primitive_tex("textfont", def_family_cmd, text_size, 0); + primitive_tex("scriptfont", def_family_cmd, script_size, 0); + primitive_tex("scriptscriptfont", def_family_cmd, script_script_size, 0); + primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0); + primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0); + + primitive_luatex("Umathoperatorsize", set_math_param_cmd, math_param_operator_size, 0); + primitive_luatex("Umathoverbarkern", set_math_param_cmd, math_param_overbar_kern, 0); + primitive_luatex("Umathoverbarrule", set_math_param_cmd, math_param_overbar_rule, 0); + primitive_luatex("Umathoverbarvgap", set_math_param_cmd, math_param_overbar_vgap, 0); + primitive_luatex("Umathunderbarkern", set_math_param_cmd, math_param_underbar_kern, 0); + primitive_luatex("Umathunderbarrule", set_math_param_cmd, math_param_underbar_rule, 0); + primitive_luatex("Umathunderbarvgap", set_math_param_cmd, math_param_underbar_vgap, 0); + primitive_luatex("Umathradicalkern", set_math_param_cmd, math_param_radical_kern, 0); + primitive_luatex("Umathradicalrule", set_math_param_cmd, math_param_radical_rule, 0); + primitive_luatex("Umathradicalvgap", set_math_param_cmd, math_param_radical_vgap, 0); + primitive_luatex("Umathradicaldegreebefore", set_math_param_cmd, math_param_radical_degree_before, 0); + primitive_luatex("Umathradicaldegreeafter", set_math_param_cmd, math_param_radical_degree_after, 0); + primitive_luatex("Umathradicaldegreeraise", set_math_param_cmd, math_param_radical_degree_raise, 0); + primitive_luatex("Umathstackvgap", set_math_param_cmd, math_param_stack_vgap, 0); + primitive_luatex("Umathstacknumup", set_math_param_cmd, math_param_stack_num_up, 0); + primitive_luatex("Umathstackdenomdown", set_math_param_cmd, math_param_stack_denom_down, 0); + primitive_luatex("Umathfractionrule", set_math_param_cmd, math_param_fraction_rule, 0); + primitive_luatex("Umathfractionnumvgap", set_math_param_cmd, math_param_fraction_num_vgap, 0); + primitive_luatex("Umathfractionnumup", set_math_param_cmd, math_param_fraction_num_up, 0); + primitive_luatex("Umathfractiondenomvgap", set_math_param_cmd, math_param_fraction_denom_vgap, 0); + primitive_luatex("Umathfractiondenomdown", set_math_param_cmd, math_param_fraction_denom_down, 0); + primitive_luatex("Umathfractiondelsize", set_math_param_cmd, math_param_fraction_del_size, 0); + primitive_luatex("Umathskewedfractionvgap", set_math_param_cmd, math_param_skewed_fraction_vgap, 0); + primitive_luatex("Umathskewedfractionhgap", set_math_param_cmd, math_param_skewed_fraction_hgap, 0); + primitive_luatex("Umathlimitabovevgap", set_math_param_cmd, math_param_limit_above_vgap, 0); + primitive_luatex("Umathlimitabovebgap", set_math_param_cmd, math_param_limit_above_bgap, 0); + primitive_luatex("Umathlimitabovekern", set_math_param_cmd, math_param_limit_above_kern, 0); + primitive_luatex("Umathlimitbelowvgap", set_math_param_cmd, math_param_limit_below_vgap, 0); + primitive_luatex("Umathlimitbelowbgap", set_math_param_cmd, math_param_limit_below_bgap, 0); + primitive_luatex("Umathlimitbelowkern", set_math_param_cmd, math_param_limit_below_kern, 0); + primitive_luatex("Umathnolimitsubfactor", set_math_param_cmd, math_param_nolimit_sub_factor, 0); /* bonus */ + primitive_luatex("Umathnolimitsupfactor", set_math_param_cmd, math_param_nolimit_sup_factor, 0); /* bonus */ + primitive_luatex("Umathunderdelimitervgap", set_math_param_cmd, math_param_under_delimiter_vgap, 0); + primitive_luatex("Umathunderdelimiterbgap", set_math_param_cmd, math_param_under_delimiter_bgap, 0); + primitive_luatex("Umathoverdelimitervgap", set_math_param_cmd, math_param_over_delimiter_vgap, 0); + primitive_luatex("Umathoverdelimiterbgap", set_math_param_cmd, math_param_over_delimiter_bgap, 0); + primitive_luatex("Umathsubshiftdrop", set_math_param_cmd, math_param_sub_shift_drop, 0); + primitive_luatex("Umathsupshiftdrop", set_math_param_cmd, math_param_sup_shift_drop, 0); + primitive_luatex("Umathsubshiftdown", set_math_param_cmd, math_param_sub_shift_down, 0); + primitive_luatex("Umathsubsupshiftdown", set_math_param_cmd, math_param_sub_sup_shift_down, 0); + primitive_luatex("Umathsubtopmax", set_math_param_cmd, math_param_sub_top_max, 0); + primitive_luatex("Umathsupshiftup", set_math_param_cmd, math_param_sup_shift_up, 0); + primitive_luatex("Umathsupbottommin", set_math_param_cmd, math_param_sup_bottom_min, 0); + primitive_luatex("Umathsupsubbottommax", set_math_param_cmd, math_param_sup_sub_bottom_max, 0); + primitive_luatex("Umathsubsupvgap", set_math_param_cmd, math_param_subsup_vgap, 0); + primitive_luatex("Umathspaceafterscript", set_math_param_cmd, math_param_space_after_script, 0); + primitive_luatex("Umathconnectoroverlapmin", set_math_param_cmd, math_param_connector_overlap_min, 0); + primitive_luatex("Umathordordspacing", set_math_param_cmd, math_param_ord_ord_spacing, 0); + primitive_luatex("Umathordopspacing", set_math_param_cmd, math_param_ord_op_spacing, 0); + primitive_luatex("Umathordbinspacing", set_math_param_cmd, math_param_ord_bin_spacing, 0); + primitive_luatex("Umathordrelspacing", set_math_param_cmd, math_param_ord_rel_spacing, 0); + primitive_luatex("Umathordopenspacing", set_math_param_cmd, math_param_ord_open_spacing, 0); + primitive_luatex("Umathordclosespacing", set_math_param_cmd, math_param_ord_close_spacing, 0); + primitive_luatex("Umathordpunctspacing", set_math_param_cmd, math_param_ord_punct_spacing, 0); + primitive_luatex("Umathordinnerspacing", set_math_param_cmd, math_param_ord_inner_spacing, 0); + primitive_luatex("Umathopordspacing", set_math_param_cmd, math_param_op_ord_spacing, 0); + primitive_luatex("Umathopopspacing", set_math_param_cmd, math_param_op_op_spacing, 0); + primitive_luatex("Umathopbinspacing", set_math_param_cmd, math_param_op_bin_spacing, 0); + primitive_luatex("Umathoprelspacing", set_math_param_cmd, math_param_op_rel_spacing, 0); + primitive_luatex("Umathopopenspacing", set_math_param_cmd, math_param_op_open_spacing, 0); + primitive_luatex("Umathopclosespacing", set_math_param_cmd, math_param_op_close_spacing, 0); + primitive_luatex("Umathoppunctspacing", set_math_param_cmd, math_param_op_punct_spacing, 0); + primitive_luatex("Umathopinnerspacing", set_math_param_cmd, math_param_op_inner_spacing, 0); + primitive_luatex("Umathbinordspacing", set_math_param_cmd, math_param_bin_ord_spacing, 0); + primitive_luatex("Umathbinopspacing", set_math_param_cmd, math_param_bin_op_spacing, 0); + primitive_luatex("Umathbinbinspacing", set_math_param_cmd, math_param_bin_bin_spacing, 0); + primitive_luatex("Umathbinrelspacing", set_math_param_cmd, math_param_bin_rel_spacing, 0); + primitive_luatex("Umathbinopenspacing", set_math_param_cmd, math_param_bin_open_spacing, 0); + primitive_luatex("Umathbinclosespacing", set_math_param_cmd, math_param_bin_close_spacing, 0); + primitive_luatex("Umathbinpunctspacing", set_math_param_cmd, math_param_bin_punct_spacing, 0); + primitive_luatex("Umathbininnerspacing", set_math_param_cmd, math_param_bin_inner_spacing, 0); + primitive_luatex("Umathrelordspacing", set_math_param_cmd, math_param_rel_ord_spacing, 0); + primitive_luatex("Umathrelopspacing", set_math_param_cmd, math_param_rel_op_spacing, 0); + primitive_luatex("Umathrelbinspacing", set_math_param_cmd, math_param_rel_bin_spacing, 0); + primitive_luatex("Umathrelrelspacing", set_math_param_cmd, math_param_rel_rel_spacing, 0); + primitive_luatex("Umathrelopenspacing", set_math_param_cmd, math_param_rel_open_spacing, 0); + primitive_luatex("Umathrelclosespacing", set_math_param_cmd, math_param_rel_close_spacing, 0); + primitive_luatex("Umathrelpunctspacing", set_math_param_cmd, math_param_rel_punct_spacing, 0); + primitive_luatex("Umathrelinnerspacing", set_math_param_cmd, math_param_rel_inner_spacing, 0); + primitive_luatex("Umathopenordspacing", set_math_param_cmd, math_param_open_ord_spacing, 0); + primitive_luatex("Umathopenopspacing", set_math_param_cmd, math_param_open_op_spacing, 0); + primitive_luatex("Umathopenbinspacing", set_math_param_cmd, math_param_open_bin_spacing, 0); + primitive_luatex("Umathopenrelspacing", set_math_param_cmd, math_param_open_rel_spacing, 0); + primitive_luatex("Umathopenopenspacing", set_math_param_cmd, math_param_open_open_spacing, 0); + primitive_luatex("Umathopenclosespacing", set_math_param_cmd, math_param_open_close_spacing, 0); + primitive_luatex("Umathopenpunctspacing", set_math_param_cmd, math_param_open_punct_spacing, 0); + primitive_luatex("Umathopeninnerspacing", set_math_param_cmd, math_param_open_inner_spacing, 0); + primitive_luatex("Umathcloseordspacing", set_math_param_cmd, math_param_close_ord_spacing, 0); + primitive_luatex("Umathcloseopspacing", set_math_param_cmd, math_param_close_op_spacing, 0); + primitive_luatex("Umathclosebinspacing", set_math_param_cmd, math_param_close_bin_spacing, 0); + primitive_luatex("Umathcloserelspacing", set_math_param_cmd, math_param_close_rel_spacing, 0); + primitive_luatex("Umathcloseopenspacing", set_math_param_cmd, math_param_close_open_spacing, 0); + primitive_luatex("Umathcloseclosespacing", set_math_param_cmd, math_param_close_close_spacing, 0); + primitive_luatex("Umathclosepunctspacing", set_math_param_cmd, math_param_close_punct_spacing, 0); + primitive_luatex("Umathcloseinnerspacing", set_math_param_cmd, math_param_close_inner_spacing, 0); + primitive_luatex("Umathpunctordspacing", set_math_param_cmd, math_param_punct_ord_spacing, 0); + primitive_luatex("Umathpunctopspacing", set_math_param_cmd, math_param_punct_op_spacing, 0); + primitive_luatex("Umathpunctbinspacing", set_math_param_cmd, math_param_punct_bin_spacing, 0); + primitive_luatex("Umathpunctrelspacing", set_math_param_cmd, math_param_punct_rel_spacing, 0); + primitive_luatex("Umathpunctopenspacing", set_math_param_cmd, math_param_punct_open_spacing, 0); + primitive_luatex("Umathpunctclosespacing", set_math_param_cmd, math_param_punct_close_spacing, 0); + primitive_luatex("Umathpunctpunctspacing", set_math_param_cmd, math_param_punct_punct_spacing, 0); + primitive_luatex("Umathpunctinnerspacing", set_math_param_cmd, math_param_punct_inner_spacing, 0); + primitive_luatex("Umathinnerordspacing", set_math_param_cmd, math_param_inner_ord_spacing, 0); + primitive_luatex("Umathinneropspacing", set_math_param_cmd, math_param_inner_op_spacing, 0); + primitive_luatex("Umathinnerbinspacing", set_math_param_cmd, math_param_inner_bin_spacing, 0); + primitive_luatex("Umathinnerrelspacing", set_math_param_cmd, math_param_inner_rel_spacing, 0); + primitive_luatex("Umathinneropenspacing", set_math_param_cmd, math_param_inner_open_spacing, 0); + primitive_luatex("Umathinnerclosespacing", set_math_param_cmd, math_param_inner_close_spacing, 0); + primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, math_param_inner_punct_spacing, 0); + primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, math_param_inner_inner_spacing, 0); + + primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base, math_code_base); + primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base, del_code_base); + primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 1, math_code_base); + primitive_luatex("Udelcodenum", extdef_del_code_cmd, del_code_base + 1, del_code_base); + primitive_tex("hyphenation", hyph_data_cmd, 0, 0); + primitive_tex("patterns", hyph_data_cmd, 1, 0); + primitive_luatex("prehyphenchar", hyph_data_cmd, 2, 0); + primitive_luatex("posthyphenchar", hyph_data_cmd, 3, 0); + primitive_luatex("preexhyphenchar", hyph_data_cmd, 4, 0); + primitive_luatex("postexhyphenchar", hyph_data_cmd, 5, 0); + primitive_luatex("hyphenationmin", hyph_data_cmd, 6, 0); + primitive_luatex("hjcode", hyph_data_cmd, 7, 0); + primitive_tex("hyphenchar", assign_font_int_cmd, 0, 0); + primitive_tex("skewchar", assign_font_int_cmd, 1, 0); + primitive_luatex("lpcode", assign_font_int_cmd, lp_code_base, 0); + primitive_luatex("rpcode", assign_font_int_cmd, rp_code_base, 0); + primitive_luatex("efcode", assign_font_int_cmd, ef_code_base, 0); + primitive_luatex("tagcode", assign_font_int_cmd, tag_code, 0); + primitive_luatex("ignoreligaturesinfont", assign_font_int_cmd, no_lig_code, 0); + primitive_tex("batchmode", set_interaction_cmd, batch_mode, 0); + primitive_tex("nonstopmode", set_interaction_cmd, nonstop_mode, 0); + primitive_tex("scrollmode", set_interaction_cmd, scroll_mode, 0); + primitive_tex("errorstopmode", set_interaction_cmd, error_stop_mode, 0); + primitive_tex("openin", in_stream_cmd, 1, 0); + primitive_tex("closein", in_stream_cmd, 0, 0); + primitive_tex("message", message_cmd, 0, 0); + primitive_tex("errmessage", message_cmd, 1, 0); + primitive_tex("lowercase", case_shift_cmd, lc_code_base, lc_code_base); + primitive_tex("uppercase", case_shift_cmd, uc_code_base, lc_code_base); + primitive_tex("show", xray_cmd, show_code, 0); + primitive_tex("showbox", xray_cmd, show_box_code, 0); + primitive_tex("showthe", xray_cmd, show_the_code, 0); + primitive_tex("showlists", xray_cmd, show_lists, 0); + + primitive_tex("openout", extension_cmd, open_code, 0); + primitive_tex("write", extension_cmd, write_code, 0); + write_loc = cur_val; + primitive_tex("closeout", extension_cmd, close_code, 0); + primitive_luatex("endlocalcontrol", extension_cmd, end_local_code, 0); + primitive_tex("special", extension_cmd, special_code, 0); + cs_text(frozen_special) = maketexstring("special"); + eqtb[frozen_special] = eqtb[cur_val]; + primitive_tex("immediate", extension_cmd, immediate_code, 0); + primitive_luatex("localinterlinepenalty", assign_int_cmd, int_base + local_inter_line_penalty_code, int_base); + primitive_luatex("localbrokenpenalty", assign_int_cmd, int_base + local_broken_penalty_code, int_base); + primitive_luatex("pagedir", assign_dir_cmd, int_base + page_direction_code, dir_base); + primitive_luatex("bodydir", assign_dir_cmd, int_base + body_direction_code, dir_base); + primitive_luatex("pardir", assign_dir_cmd, int_base + par_direction_code, dir_base); + primitive_luatex("textdir", assign_dir_cmd, int_base + text_direction_code, dir_base); + primitive_luatex("mathdir", assign_dir_cmd, int_base + math_direction_code, dir_base); + primitive_luatex("linedir", assign_dir_cmd, int_base + line_direction_code, dir_base); + primitive_luatex("pageleftoffset", assign_dimen_cmd, dimen_base + page_left_offset_code, dimen_base); + primitive_luatex("pagetopoffset", assign_dimen_cmd, dimen_base + page_top_offset_code, dimen_base); + primitive_luatex("pagerightoffset", assign_dimen_cmd, dimen_base + page_right_offset_code, dimen_base); + primitive_luatex("pagebottomoffset", assign_dimen_cmd, dimen_base + page_bottom_offset_code, dimen_base); + primitive_luatex("saveboxresource", extension_cmd, save_box_resource_code, 0); + primitive_luatex("useboxresource", extension_cmd, use_box_resource_code, 0); + primitive_luatex("saveimageresource", extension_cmd, save_image_resource_code, 0); + primitive_luatex("useimageresource", extension_cmd, use_image_resource_code, 0); + primitive_luatex("savepos", normal_cmd, save_pos_code, 0); + primitive_luatex("savecatcodetable", normal_cmd, save_cat_code_table_code, 0); + primitive_luatex("initcatcodetable", normal_cmd, init_cat_code_table_code, 0); + primitive_luatex("setrandomseed", normal_cmd, set_random_seed_code, 0); + primitive_luatex("latelua", normal_cmd, late_lua_code, 0); + primitive_luatex("lateluafunction", normal_cmd, late_lua_call_code, 0); + primitive_luatex("insertht", convert_cmd, insert_ht_code, 0); + primitive_luatex("dviextension", extension_cmd, dvi_extension_code, 0); + primitive_luatex("dvifeedback", feedback_cmd, dvi_feedback_code, 0); + primitive_luatex("dvivariable", variable_cmd, dvi_variable_code, 0); + primitive_luatex("pdfextension", extension_cmd, pdf_extension_code, 0); + primitive_luatex("pdffeedback", feedback_cmd, pdf_feedback_code, 0); + primitive_luatex("pdfvariable", variable_cmd, pdf_variable_code, 0); + primitive_luatex("mathoption", option_cmd, math_option_code, 0); + + primitive_luatex("luacopyinputnodes", assign_int_cmd, int_base + copy_lua_input_nodes_code, int_base); + + primitive_luatex("pagedirection", assign_direction_cmd, int_base + page_direction_code, dir_base); + primitive_luatex("bodydirection", assign_direction_cmd, int_base + body_direction_code, dir_base); + primitive_luatex("pardirection", assign_direction_cmd, int_base + par_direction_code, dir_base); + primitive_luatex("textdirection", assign_direction_cmd, int_base + text_direction_code, dir_base); + primitive_luatex("mathdirection", assign_direction_cmd, int_base + math_direction_code, dir_base); + primitive_luatex("linedirection", assign_direction_cmd, int_base + line_direction_code, dir_base); + + /* + some of the internal integer parameters are not associated with actual + primitives at all. + */ + + primitive_no("nolocalwhatsits", assign_int_cmd, int_base + no_local_whatsits_code, int_base); + primitive_no("nolocaldirs", assign_int_cmd, int_base + no_local_dirs_code, int_base); + +} + +void initialize_etex_commands(void) +{ + primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0); + primitive_etex("eTeXversion", last_item_cmd, eTeX_version_code, 0); + primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, 0); + primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0); + + /*tex + + First we implement the additional \eTeX\ parameters in the table of + equivalents. + */ + + primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base); + primitive_etex("tracingassigns", assign_int_cmd, int_base + tracing_assigns_code, int_base); + primitive_etex("tracinggroups", assign_int_cmd, int_base + tracing_groups_code, int_base); + primitive_etex("tracingifs", assign_int_cmd, int_base + tracing_ifs_code, int_base); + primitive_etex("tracingscantokens", assign_int_cmd, int_base + tracing_scan_tokens_code, int_base); + primitive_etex("tracingnesting", assign_int_cmd, int_base + tracing_nesting_code, int_base); + primitive_etex("predisplaydirection", assign_int_cmd, int_base + pre_display_direction_code, int_base); + primitive_etex("lastlinefit", assign_int_cmd, int_base + last_line_fit_code, int_base); + primitive_etex("savingvdiscards", assign_int_cmd, int_base + saving_vdiscards_code, int_base); + primitive_etex("savinghyphcodes", assign_int_cmd, int_base + saving_hyph_codes_code, int_base); + primitive_luatex("suppressfontnotfounderror", assign_int_cmd, int_base + suppress_fontnotfound_error_code, int_base); + primitive_luatex("suppresslongerror", assign_int_cmd, int_base + suppress_long_error_code, int_base); + primitive_luatex("suppressprimitiveerror", assign_int_cmd, int_base + suppress_primitive_error_code, int_base); + primitive_luatex("suppressmathparerror", assign_int_cmd, int_base + suppress_mathpar_error_code, int_base); + primitive_luatex("suppressifcsnameerror", assign_int_cmd, int_base + suppress_ifcsname_error_code, int_base); + primitive_luatex("suppressoutererror", assign_int_cmd, int_base + suppress_outer_error_code, int_base); + primitive_luatex("matheqnogapstep", assign_int_cmd, int_base + math_eqno_gap_step_code, int_base); + primitive_luatex("mathdisplayskipmode", assign_int_cmd, int_base + math_display_skip_mode_code, int_base); + primitive_luatex("mathscriptsmode", assign_int_cmd, int_base + math_scripts_mode_code, int_base); + primitive_luatex("mathnolimitsmode", assign_int_cmd, int_base + math_nolimits_mode_code, int_base); + primitive_luatex("mathitalicsmode", assign_int_cmd, int_base + math_italics_mode_code, int_base); + primitive_luatex("mathrulesmode", assign_int_cmd, int_base + math_rules_mode_code, int_base); + primitive_luatex("mathrulesfam", assign_int_cmd, int_base + math_rules_fam_code, int_base); + primitive_luatex("synctex", assign_int_cmd, int_base + synctex_code, int_base); + + primitive_etex("currentgrouplevel", last_item_cmd, current_group_level_code, 0); + primitive_etex("currentgrouptype", last_item_cmd, current_group_type_code, 0); + + primitive_etex("currentiflevel", last_item_cmd, current_if_level_code, 0); + primitive_etex("currentiftype", last_item_cmd, current_if_type_code, 0); + primitive_etex("currentifbranch", last_item_cmd, current_if_branch_code, 0); + primitive_etex("fontcharwd", last_item_cmd, font_char_wd_code, 0); + primitive_etex("fontcharht", last_item_cmd, font_char_ht_code, 0); + primitive_etex("fontchardp", last_item_cmd, font_char_dp_code, 0); + primitive_etex("fontcharic", last_item_cmd, font_char_ic_code, 0); + + primitive_etex("parshapelength", last_item_cmd, par_shape_length_code, 0); + primitive_etex("parshapeindent", last_item_cmd, par_shape_indent_code, 0); + primitive_etex("parshapedimen", last_item_cmd, par_shape_dimen_code, 0); + + primitive_luatex("shapemode", assign_int_cmd, int_base + shape_mode_code, int_base); + primitive_luatex("hyphenationbounds", assign_int_cmd, int_base + hyphenation_bounds_code, int_base); + + primitive_etex("showgroups", xray_cmd, show_groups, 0); + + /*tex + + The \.{\\showtokens} command displays a token list. + + */ + + primitive_etex("showtokens", xray_cmd, show_tokens, 0); + + /*tex + + The \.{\\unexpanded} primitive prevents expansion of tokens much as the + result from \.{\\the} applied to a token variable. The \.{\\detokenize} + primitive converts a token list into a list of character tokens much as + if the token list were written to a file. We use the fact that the + command modifiers for \.{\\unexpanded} and \.{\\detokenize} are odd + whereas those for \.{\\the} and \.{\\showthe} are even. + + */ + + primitive_etex("unexpanded", the_cmd, 1, 0); + primitive_etex("detokenize", the_cmd, show_tokens, 0); + + /*tex + + The \.{\\showifs} command displays all currently active conditionals. + + */ + + primitive_etex("showifs", xray_cmd, show_ifs, 0); + + /*tex + + The \.{\\interactionmode} primitive allows to query and set the interaction mode. + + */ + + primitive_etex("interactionmode", set_page_int_cmd, 2, 0); + + /*tex + + The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive. + + */ + + primitive_etex("scantokens", input_cmd, 2, 0); + primitive_luatex("scantextokens", input_cmd, 3, 0); + + primitive_etex("readline", read_to_cs_cmd, 1, 0); + + primitive_etex("unless", expand_after_cmd, 1, 0); + primitive_etex("ifdefined", if_test_cmd, if_def_code, 0); + primitive_etex("ifcsname", if_test_cmd, if_cs_code, 0); + primitive_etex("iffontchar", if_test_cmd, if_font_char_code, 0); + primitive_luatex("ifincsname", if_test_cmd, if_in_csname_code, 0); + primitive_luatex("ifabsnum", if_test_cmd, if_abs_num_code, 0); + primitive_luatex("ifabsdim", if_test_cmd, if_abs_dim_code, 0); + + /*tex + + The |protected| feature of \eTeX\ defines the \.{\\protected} prefix + command for macro definitions. Such macros are protected against + expansions when lists of expanded tokens are built, e.g., for \.{\\edef} + or during \.{\\write}. + */ + + primitive_etex("protected", prefix_cmd, 8, 0); + + /*tex + + Here are the additional \eTeX\ primitives for expressions. + + */ + + primitive_etex("numexpr", last_item_cmd, eTeX_expr - int_val_level + int_val_level, 0); + primitive_etex("dimexpr", last_item_cmd, eTeX_expr - int_val_level + dimen_val_level, 0); + primitive_etex("glueexpr", last_item_cmd, eTeX_expr - int_val_level + glue_val_level, 0); + primitive_etex("muexpr", last_item_cmd, eTeX_expr - int_val_level + mu_val_level, 0); + + primitive_etex("gluestretchorder", last_item_cmd, glue_stretch_order_code, 0); + primitive_etex("glueshrinkorder", last_item_cmd, glue_shrink_order_code, 0); + primitive_etex("gluestretch", last_item_cmd, glue_stretch_code, 0); + primitive_etex("glueshrink", last_item_cmd, glue_shrink_code, 0); + + primitive_luatex("eTeXgluestretchorder", last_item_cmd, eTeX_glue_stretch_order_code, 0); + primitive_luatex("eTeXglueshrinkorder", last_item_cmd, eTeX_glue_shrink_order_code, 0); + + primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0); + primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0); + + /*tex + + The \.{\\pagediscards} and \.{\\splitdiscards} commands share the command + code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are + distinguished by their |chr_code| values |last_box_code| and + |vsplit_code|. These |chr_code| values are larger than |box_code| and + |copy_code|. + + */ + + primitive_etex("pagediscards", un_vbox_cmd, last_box_code, 0); + primitive_etex("splitdiscards", un_vbox_cmd, vsplit_code, 0); + + /*tex + + The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties}, + and \.{\\displaywidowpenalties} commands allow to define arrays of + penalty values to be used instead of the corresponding single values. + + */ + + primitive_etex("interlinepenalties", set_etex_shape_cmd, inter_line_penalties_loc, etex_pen_base); + primitive_etex("clubpenalties", set_etex_shape_cmd, club_penalties_loc, etex_pen_base); + primitive_etex("widowpenalties", set_etex_shape_cmd, widow_penalties_loc, etex_pen_base); + primitive_etex("displaywidowpenalties", set_etex_shape_cmd, display_widow_penalties_loc, etex_pen_base); + +} diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.h b/Build/source/texk/web2c/luatexdir/tex/commands.h new file mode 100644 index 00000000000..7d7a7056564 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/commands.h @@ -0,0 +1,359 @@ +/* commands.h + + Copyright 2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef COMMANDS_H +# define COMMANDS_H + +/* +Before we can go any further, we need to define symbolic names for the internal +code numbers that represent the various commands obeyed by \TeX. These codes +are somewhat arbitrary, but not completely so. For example, the command +codes for character types are fixed by the language, since a user says, +e.g., `\.{\\catcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter, +and the command code |math_shift| is equal to~3. Some other codes have +been made adjacent so that |case| statements in the program need not consider +cases that are widely spaced, or so that |case| statements can be replaced +by |if| statements. + +At any rate, here is the list, for future reference. First come the +``catcode'' commands, several of which share their numeric codes with +ordinary commands when the catcode cannot emerge from \TeX's scanning routine. + +@ Next are the ordinary run-of-the-mill command codes. Codes that are +|min_internal| or more represent internal quantities that might be +expanded by `\.{\\the}'. + + +@ The next codes are special; they all relate to mode-independent +assignment of values to \TeX's internal registers or tables. +Codes that are |max_internal| or less represent internal quantities +that might be expanded by `\.{\\the}'. + +There is no matching primitive to go with |assign_attr|, but even if +there was no \.{\\attributedef}, a reserved number would still be +needed because there is an implied correspondence between the +|assign_xxx| commands and |xxx_val| expression values. That would +break down otherwise. + +@ The remaining command codes are extra special, since they cannot get through +\TeX's scanner to the main control routine. They have been given values higher +than |max_command| so that their special nature is easily discernible. +The ``expandable'' commands come first. + + +*/ + +typedef enum { + relax_cmd = 0, /* do nothing ( \.{\\relax} ) */ +# define escape_cmd relax_cmd /* escape delimiter (called \.\\ in {\sl The \TeX book\/}) */ + left_brace_cmd, /* beginning of a group ( \.\{ ) */ + right_brace_cmd, /* ending of a group ( \.\} ) */ + math_shift_cmd, /* mathematics shift character ( \.\$ ) */ + tab_mark_cmd, /* alignment delimiter ( \.\&, \.{\\span} ) */ + car_ret_cmd, /* end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} ) */ +# define out_param_cmd car_ret_cmd /* output a macro parameter */ + mac_param_cmd, /* macro parameter symbol ( \.\# ) */ + sup_mark_cmd, /* superscript ( \.{\char'136} ) */ + sub_mark_cmd, /* subscript ( \.{\char'137} ) */ + endv_cmd, /* end of \<v_j> list in alignment template */ +# define ignore_cmd endv_cmd /* characters to ignore ( \.{\^\^@@} ) */ + spacer_cmd, /* characters equivalent to blank space ( \.{\ } ) */ + letter_cmd, /* characters regarded as letters ( \.{A..Z}, \.{a..z} ) */ + other_char_cmd, /* none of the special character types */ + par_end_cmd, /* end of paragraph ( \.{\\par} ) */ +# define active_char_cmd par_end_cmd /* characters that invoke macros ( \.{\char`\~} ) */ +# define match_cmd par_end_cmd /* match a macro parameter */ + stop_cmd, /* end of job ( \.{\\end}, \.{\\dump} ) */ +# define comment_cmd stop_cmd /* characters that introduce comments ( \.\% ) */ +# define end_match_cmd stop_cmd /* end of parameters to macro */ + delim_num_cmd, /* specify delimiter numerically ( \.{\\delimiter} ) */ +# define invalid_char_cmd delim_num_cmd /* characters that shouldn't appear ( \.{\^\^?} ) */ +# define max_char_code_cmd delim_num_cmd /* largest catcode for individual characters */ + char_num_cmd, /* character specified numerically ( \.{\\char} ) */ + math_char_num_cmd, /* explicit math code ( \.{\\mathchar} ) */ + mark_cmd, /* mark definition ( \.{\\mark} ) */ + node_cmd, + xray_cmd, /* peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~) */ + make_box_cmd, /* make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~) */ + hmove_cmd, /* horizontal motion ( \.{\\moveleft}, \.{\\moveright} ) */ + vmove_cmd, /* vertical motion ( \.{\\raise}, \.{\\lower} ) */ + un_hbox_cmd, /* unglue a box ( \.{\\unhbox}, \.{\\unhcopy} ) */ + un_vbox_cmd, /* unglue a box ( \.{\\unvbox}, \.{\\unvcopy} or \.{\\pagediscards}, \.{\\splitdiscards} ) */ + remove_item_cmd, /* nullify last item ( \.{\\unpenalty}, \.{\\unkern}, \.{\\unskip} ) */ + hskip_cmd, /* horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~) */ + vskip_cmd, /* vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~) */ + mskip_cmd, /* math glue ( \.{\\mskip} ) */ + kern_cmd, /* fixed space ( \.{\\kern}) */ + mkern_cmd, /* math kern ( \.{\\mkern} ) */ + leader_ship_cmd, /* use a box ( \.{\\shipout}, \.{\\leaders}, etc.~) */ + halign_cmd, /* horizontal table alignment ( \.{\\halign} ) */ + valign_cmd, /* vertical table alignment ( \.{\\valign} ) */ + no_align_cmd, /* temporary escape from alignment ( \.{\\noalign} ) */ + vrule_cmd, /* vertical rule ( \.{\\vrule} ) */ + hrule_cmd, /* horizontal rule ( \.{\\hrule} ) */ + no_vrule_cmd, /* no rule, just reserve space ( \.{\\novrule} )*/ + no_hrule_cmd, /* no rule, just reserve space ( \.{\\nohrule} )*/ + insert_cmd, /* vlist inserted in box ( \.{\\insert} ) */ + vadjust_cmd, /* vlist inserted in enclosing paragraph ( \.{\\vadjust} ) */ + ignore_spaces_cmd, /* gobble |spacer| tokens ( \.{\\ignorespaces} ) */ + after_assignment_cmd, /* save till assignment is done ( \.{\\afterassignment} ) */ + after_group_cmd, /* save till group is done ( \.{\\aftergroup} ) */ + break_penalty_cmd, /* additional badness ( \.{\\penalty} ) */ + start_par_cmd, /* begin paragraph ( \.{\\indent}, \.{\\noindent} ) */ + ital_corr_cmd, /* italic correction ( \.{\\/} ) */ + accent_cmd, /* attach accent in text ( \.{\\accent} ) */ + math_accent_cmd, /* attach accent in math ( \.{\\mathaccent} ) */ + discretionary_cmd, /* discretionary texts ( \.{\\-}, \.{\\discretionary} ) */ + eq_no_cmd, /* equation number ( \.{\\eqno}, \.{\\leqno} ) */ + left_right_cmd, /* variable delimiter ( \.{\\left}, \.{\\right} or \.{\\middle} ) */ + math_comp_cmd, /* component of formula ( \.{\\mathbin}, etc.~) */ + limit_switch_cmd, /* diddle limit conventions ( \.{\\displaylimits}, etc.~) */ + above_cmd, /* generalized fraction ( \.{\\above}, \.{\\atop}, etc.~) */ + math_style_cmd, /* style specification ( \.{\\displaystyle}, etc.~) */ + math_choice_cmd, /* choice specification ( \.{\\mathchoice} ) */ + non_script_cmd, /* conditional math glue ( \.{\\nonscript} ) */ + vcenter_cmd, /* vertically center a vbox ( \.{\\vcenter} ) */ + case_shift_cmd, /* force specific case ( \.{\\lowercase}, \.{\\uppercase}~) */ + message_cmd, /* send to user ( \.{\\message}, \.{\\errmessage} ) */ + normal_cmd, /* general extensions to \TeX\ that don't fit into a category */ + extension_cmd, /* extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~) */ + option_cmd, + lua_function_call_cmd, + lua_bytecode_call_cmd, + lua_call_cmd, + in_stream_cmd, /* files for reading ( \.{\\openin}, \.{\\closein} ) */ + begin_group_cmd, /* begin local grouping ( \.{\\begingroup} ) */ + end_group_cmd, /* end local grouping ( \.{\\endgroup} ) */ + omit_cmd, /* omit alignment template ( \.{\\omit} ) */ + ex_space_cmd, /* explicit space ( \.{\\\ } ) */ + boundary_cmd, /* insert boundry node with value ( \.{\\*boundary} ) */ + radical_cmd, /* square root and similar signs ( \.{\\radical} ) */ + super_sub_script_cmd, /* explicit super- or subscript */ + no_super_sub_script_cmd, /* explicit no super- or subscript */ + math_shift_cs_cmd, /* start- and endmath */ + end_cs_name_cmd, /* end control sequence ( \.{\\endcsname} ) */ + char_ghost_cmd, /* \.{\\leftghost}, \.{\\rightghost} character for kerning */ + assign_local_box_cmd, /* box for guillemets \.{\\localleftbox} or \.{\\localrightbox} */ + char_given_cmd, /* character code defined by \.{\\chardef} */ +# define min_internal_cmd char_given_cmd /* the smallest code that can follow \.{\\the} */ + math_given_cmd, /* math code defined by \.{\\mathchardef} */ + xmath_given_cmd, /* math code defined by \.{\\Umathchardef} or \.{\\Umathcharnumdef} */ + last_item_cmd, /* most recent item ( \.{\\lastpenalty}, \.{\\lastkern}, \.{\\lastskip} ) */ +# define max_non_prefixed_command_cmd last_item_cmd /* largest command code that can't be \.{\\global} */ + toks_register_cmd, /* token list register ( \.{\\toks} ) */ + assign_toks_cmd, /* special token list ( \.{\\output}, \.{\\everypar}, etc.~) */ + assign_int_cmd, /* user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~) */ + assign_attr_cmd, /* user-defined attributes */ + assign_dimen_cmd, /* user-defined length ( \.{\\hsize}, etc.~) */ + assign_glue_cmd, /* user-defined glue ( \.{\\baselineskip}, etc.~) */ + assign_mu_glue_cmd, /* user-defined muglue ( \.{\\thinmuskip}, etc.~) */ + assign_font_dimen_cmd, /* user-defined font dimension ( \.{\\fontdimen} ) */ + assign_font_int_cmd, /* user-defined font integer ( \.{\\hyphenchar}, \.{\\skewchar} ) */ + assign_hang_indent_cmd, + set_aux_cmd, /* specify state info ( \.{\\spacefactor}, \.{\\prevdepth} ) */ + set_prev_graf_cmd, /* specify state info ( \.{\\prevgraf} ) */ + set_page_dimen_cmd, /* specify state info ( \.{\\pagegoal}, etc.~) */ + set_page_int_cmd, /* specify state info ( \.{\\deadcycles}, \.{\\insertpenalties} ) */ + set_box_dimen_cmd, /* change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} ) */ + set_tex_shape_cmd, /* specify fancy paragraph shape ( \.{\\parshape} ) */ + set_etex_shape_cmd, /* specify etex extended list ( \.{\\interlinepenalties}, etc.~) */ + def_char_code_cmd, /* define a character code ( \.{\\catcode}, etc.~) */ + def_del_code_cmd, /* define a delimiter code ( \.{\\delcode}) */ + extdef_math_code_cmd, /* define an extended character code ( \.{\\Umathcode}, etc.~) */ + extdef_del_code_cmd, /* define an extended delimiter code ( \.{\\Udelcode}, etc.~) */ + def_family_cmd, /* declare math fonts ( \.{\\textfont}, etc.~) */ + set_math_param_cmd, /* set math parameters ( \.{\\mathquad}, etc.~) */ + set_font_cmd, /* set current font ( font identifiers ) */ + def_font_cmd, /* define a font file ( \.{\\font} ) */ + register_cmd, /* internal register ( \.{\\count}, \.{\\dimen}, etc.~) */ + assign_box_direction_cmd, /* (\.{\\boxdirection}) */ + assign_box_dir_cmd, /* (\.{\\boxdir}) */ + assign_direction_cmd, /* (\.{\\pagedirection}, \.{\\textdirection}) */ + assign_dir_cmd, /* (\.{\\pagedir}, \.{\\textdir}) */ + combine_toks_cmd, +# define max_internal_cmd assign_dir_cmd /* the largest code that can follow \.{\\the} */ + advance_cmd, /* advance a register or parameter ( \.{\\advance} ) */ + multiply_cmd, /* multiply a register or parameter ( \.{\\multiply} ) */ + divide_cmd, /* divide a register or parameter ( \.{\\divide} ) */ + prefix_cmd, /* qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} ) */ + let_cmd, /* assign a command code ( \.{\\let}, \.{\\futurelet} ) */ + shorthand_def_cmd, /* code definition ( \.{\\chardef}, \.{\\countdef}, etc.~) */ + def_lua_call_cmd, + read_to_cs_cmd, /* read into a control sequence ( \.{\\read} ) */ + def_cmd, /* macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} ) */ + set_box_cmd, /* set a box ( \.{\\setbox} ) */ + hyph_data_cmd, /* hyphenation data ( \.{\\hyphenation}, \.{\\patterns} ) */ + set_interaction_cmd, /* define level of interaction ( \.{\\batchmode}, etc.~) */ + letterspace_font_cmd, /* letterspace a font ( \.{\\letterspacefont} ) */ + expand_font_cmd, /* expand glyphs ( \.{\\expandglyphsinfont} ) */ + copy_font_cmd, /* create a new font instance ( \.{\\copyfont} ) */ + set_font_id_cmd, + undefined_cs_cmd, /* initial state of most |eq_type| fields */ + expand_after_cmd, /* special expansion ( \.{\\expandafter} ) */ + no_expand_cmd, /* special nonexpansion ( \.{\\noexpand} ) */ + input_cmd, /* input a source file ( \.{\\input}, \.{\\endinput} or \.{\\scantokens} or \.{\\scantextokens} ) */ + lua_expandable_call_cmd, + lua_local_call_cmd, + if_test_cmd, /* conditional text ( \.{\\if}, \.{\\ifcase}, etc.~) */ + fi_or_else_cmd, /* delimiters for conditionals ( \.{\\else}, etc.~) */ + cs_name_cmd, /* make a control sequence from tokens ( \.{\\csname} ) */ + convert_cmd, /* convert to text ( \.{\\number}, \.{\\string}, etc.~) */ + variable_cmd, + feedback_cmd, + the_cmd, /* expand an internal quantity ( \.{\\the} or \.{\\unexpanded}, \.{\\detokenize} ) */ + top_bot_mark_cmd, /* inserted mark ( \.{\\topmark}, etc.~) */ + call_cmd, /* non-long, non-outer control sequence */ + long_call_cmd, /* long, non-outer control sequence */ + outer_call_cmd, /* non-long, outer control sequence */ + long_outer_call_cmd, /* long, outer control sequence */ + end_template_cmd, /* end of an alignment template */ + dont_expand_cmd, /* the following token was marked by \.{\\noexpand} */ + glue_ref_cmd, /* the equivalent points to a glue specification */ + shape_ref_cmd, /* the equivalent points to a parshape specification */ + box_ref_cmd, /* the equivalent points to a box node, or is |null| */ + data_cmd, /* the equivalent is simply a halfword number */ +} tex_command_code; + +# define max_command_cmd set_font_id_cmd /* the largest command code seen at |big_switch| */ +# define last_cmd data_cmd +# define max_non_prefixed_command last_item_cmd + +typedef enum { + above_code = 0, + over_code = 1, + atop_code = 2, + skewed_code = 3, + delimited_code = 4, +} fraction_codes; + +typedef enum { + number_code = 0, /* command code for \.{\\number} */ + lua_code, /* command code for \.{\\directlua} */ + lua_function_code, /* command code for \.{\\luafunction} */ + lua_bytecode_code, /* command code for \.{\\luabytecode} */ + expanded_code, /* command code for \.{\\expanded} */ + immediate_assignment_code, /* command code for \.{\\immediateassignment} */ + immediate_assigned_code, /* command code for \.{\\assigned} */ + math_style_code, /* command code for \.{\\mathstyle} */ + string_code, /* command code for \.{\\string} */ + cs_string_code, /* command code for \.{\\csstring} */ + roman_numeral_code, /* command code for \.{\\romannumeral} */ + meaning_code, /* command code for \.{\\meaning} */ + uchar_code, /* command code for \.{\\Uchar} */ + lua_escape_string_code, /* command code for \.{\\luaescapestring} */ + font_id_code, /* command code for \.{\\fontid} */ + font_name_code, /* command code for \.{\\fontname} */ + left_margin_kern_code, /* command code for \.{\\leftmarginkern} */ + right_margin_kern_code, /* command code for \.{\\rightmarginkern} */ + uniform_deviate_code, /* command code for \.{\\uniformdeviate} */ + normal_deviate_code, /* command code for \.{\\normaldeviate} */ + math_char_class_code, + math_char_fam_code, + math_char_slot_code, + insert_ht_code, /* command code for \.{\\insertht} */ + job_name_code, /* command code for \.{\\jobname} */ + format_name_code, /* command code for \.{\\AlephVersion} */ + luatex_banner_code, /* command code for \.{\\luatexbanner}: */ + luatex_revision_code, /* command code for \.{\\luatexrevision} */ + etex_code, /* command code for \.{\\eTeXVersion} */ + eTeX_revision_code, /* command code for \.{\\eTeXrevision} */ + font_identifier_code, /* command code for \.{tex.fontidentifier} (virtual) */ + /* backend */ + dvi_feedback_code, + pdf_feedback_code, + dvi_variable_code, + pdf_variable_code, +} convert_codes; + +typedef enum { + lastpenalty_code = 0, /* code for \.{\\lastpenalty} */ + lastattr_code, /* not used */ + lastkern_code, /* code for \.{\\lastkern} */ + lastskip_code, /* code for \.{\\lastskip} */ + last_node_type_code, /* code for \.{\\lastnodetype} */ + input_line_no_code, /* code for \.{\\inputlineno} */ + badness_code, /* code for \.{\\badness} */ + last_saved_box_resource_index_code, /* code for \.{\\lastsavedboxresourceindex} */ + last_saved_image_resource_index_code, /* code for \.{\\lastsavedimageresourceindex} */ + last_saved_image_resource_pages_code, /* code for \.{\\lastsavedimageresourcepages} */ + last_x_pos_code, /* code for \.{\\lastxpos} */ + last_y_pos_code, /* code for \.{\\lastypos} */ + random_seed_code, /* code for \.{\\randomseed} */ + luatex_version_code, /* code for \.{\\luatexversion} */ + eTeX_minor_version_code, /* code for \.{\\eTeXminorversion} */ + eTeX_version_code, /* code for \.{\\eTeXversion} */ +# define eTeX_int eTeX_version_code /* first of \eTeX\ codes for integers */ + current_group_level_code, /* code for \.{\\currentgrouplevel} */ + current_group_type_code, /* code for \.{\\currentgrouptype} */ + current_if_level_code, /* code for \.{\\currentiflevel} */ + current_if_type_code, /* code for \.{\\currentiftype} */ + current_if_branch_code, /* code for \.{\\currentifbranch} */ + glue_stretch_order_code, /* code for \.{\\gluestretchorder} */ + glue_shrink_order_code, /* code for \.{\\glueshrinkorder} */ + eTeX_glue_stretch_order_code, /* code for \.{\\eTeXgluestretchorder} */ + eTeX_glue_shrink_order_code, /* code for \.{\\eTeXglueshrinkorder} */ + font_char_wd_code, /* code for \.{\\fontcharwd} */ +# define eTeX_dim font_char_wd_code /* first of \eTeX\ codes for dimensions */ + font_char_ht_code, /* code for \.{\\fontcharht} */ + font_char_dp_code, /* code for \.{\\fontchardp} */ + font_char_ic_code, /* code for \.{\\fontcharic} */ + par_shape_length_code, /* code for \.{\\parshapelength} */ + par_shape_indent_code, /* code for \.{\\parshapeindent} */ + par_shape_dimen_code, /* code for \.{\\parshapedimen} */ + glue_stretch_code, /* code for \.{\\gluestretch} */ + glue_shrink_code, /* code for \.{\\glueshrink} */ + mu_to_glue_code, /* code for \.{\\mutoglue} */ +# define eTeX_glue mu_to_glue_code /* first of \eTeX\ codes for glue */ + glue_to_mu_code, /* code for \.{\\gluetomu} */ +# define eTeX_mu glue_to_mu_code /* first of \eTeX\ codes for muglue */ + numexpr_code, /* code for \.{\\numexpr} */ +# define eTeX_expr numexpr_code /* first of \eTeX\ codes for expressions */ + attrexpr_code, /* not used */ + dimexpr_code, /* code for \.{\\dimexpr} */ + glueexpr_code, /* code for \.{\\glueexpr} */ + muexpr_code, /* code for \.{\\muexpr} */ +} last_item_codes; + + +typedef enum { + save_cat_code_table_code=0, + init_cat_code_table_code, + set_random_seed_code, + save_pos_code, + late_lua_code, + late_lua_call_code, + expand_font_code, +} normal_codes; + +typedef enum { + lp_code_base = 2, + rp_code_base = 3, + ef_code_base = 4, + tag_code = 5, + no_lig_code = 6, + gp_code_base = 7 +} font_codes ; + +# define immediate_code 4 /* command modifier for \.{\\immediate} */ + +extern void initialize_commands(void); +extern void initialize_etex_commands(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.c b/Build/source/texk/web2c/luatexdir/tex/conditional.c new file mode 100644 index 00000000000..948db580340 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/conditional.c @@ -0,0 +1,568 @@ +/* + +conditional.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +@* We consider now the way \TeX\ handles various kinds of \.{\\if} commands. + +Conditions can be inside conditions, and this nesting has a stack that is +independent of the |save_stack|. + +Four global variables represent the top of the condition stack: |cond_ptr| points +to pushed-down entries, if any; |if_limit| specifies the largest code of a +|fi_or_else| command that is syntactically legal; |cur_if| is the name of the +current type of conditional; and |if_line| is the line number at which it began. + +If no conditions are currently in progress, the condition stack has the special +state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|. Otherwise +|cond_ptr| points to a two-word node; the |type|, |subtype|, and |link| fields of +the first word contain |if_limit|, |cur_if|, and |cond_ptr| at the next level, +and the second word contains the corresponding |if_line|. + +In |cond_ptr| we keep track of the top of the condition stack while |if_limit| +holds the upper bound on |fi_or_else| codes. The type of conditional being worked +on is stored in cur_if and |if_line| keeps track of the line where that +conditional began. When we skip conditional text, |skip_line| keeps track of the +line number where skipping began, for use in error messages. + +*/ + +halfword cond_ptr; +int if_limit, cur_if, if_line, skip_line; + +/*tex + +Here is a procedure that ignores text until coming to an \.{\\or}, \.{\\else}, or +\.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$ nesting. After it has acted, +|cur_chr| will indicate the token that was found, but |cur_tok| will not be set +(because this makes the procedure run faster). + +With |l| we keep track of the level of $\.{\\if}\ldots\.{\\fi}$ nesting and +|scanner_status| let us return to the entry status. + +*/ + +void pass_text(void) +{ + int l = 0; + int save_scanner_status = scanner_status; + scanner_status = skipping; + skip_line = line; + while (1) { + get_next(); + if (cur_cmd == fi_or_else_cmd) { + if (l == 0) + break; + if (cur_chr == fi_code) + decr(l); + } else if (cur_cmd == if_test_cmd) { + incr(l); + } + } + scanner_status = save_scanner_status; + if (tracing_ifs_par > 0) + show_cur_cmd_chr(); +} + +/*tex + +When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then if\/ +\.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if} condition +has been evaluated, \.{\\relax} will be inserted. For example, a sequence of +commands like `\.{\\ifvoid1\\else...\\fi}' would otherwise require something +after the `\.1'. + +*/ + +void push_condition_stack(void) +{ + halfword p = new_node(if_node, 0); + vlink(p) = cond_ptr; + if_limit_type(p) = (quarterword) if_limit; + if_limit_subtype(p) = (quarterword) cur_if; + if_line_field(p) = if_line; + cond_ptr = p; + cur_if = cur_chr; + if_limit = if_code; + if_line = line; +} + +void pop_condition_stack(void) +{ + halfword p; + if (if_stack[in_open] == cond_ptr) { + /*tex Conditionals are possibly not properly nested with files. */ + if_warning(); + } + p = cond_ptr; + if_line = if_line_field(p); + cur_if = if_limit_subtype(p); + if_limit = if_limit_type(p); + cond_ptr = vlink(p); + flush_node(p); +} + +/*tex + +Here's a procedure that changes the |if_limit| code corresponding to a given +value of |cond_ptr|. + +*/ + +void change_if_limit(int l, halfword p) +{ + if (p == cond_ptr) { + if_limit = l; + } else { + halfword q = cond_ptr; + while (1) { + if (q == null) + confusion("if"); + if (vlink(q) == p) { + if_limit_type(q) = (quarterword) l; + return; + } + q = vlink(q); + } + } +} + +/*tex + +The conditional \.{\\ifcsname} is equivalent to \.{\\expandafter} +\.{\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new control +sequence will be entered into the hash table (once all tokens preceding the +mandatory \.{\\endcsname} have been expanded). + +*/ + +static halfword last_tested_cs ; + +static boolean test_for_cs(void) +{ + /*tex Is the condition true? */ + boolean b = false; + /*tex To be tested against the second operand: */ + int m, s; + /*tex For traversing token lists in \.{\\ifx} tests: */ + halfword q; + halfword n = get_avail(); + /*tex Head of the list of characters: */ + halfword p = n; + is_in_csname += 1; + while (1) { + get_x_token(); + if (cur_cs != 0) + break; + store_new_token(cur_tok); + } + if (cur_cmd != end_cs_name_cmd) { + last_tested_cs = null_cs; + if (suppress_ifcsname_error_par) { + do { + get_x_token(); + } while (cur_cmd != end_cs_name_cmd); + flush_list(n); + is_in_csname -= 1; + return b; + } else { + complain_missing_csname(); + } + } + /*tex Look up the characters of list |n| in the hash table, and set |cur_cs|. */ + m = first; + p = token_link(n); + while (p != null) { + if (m >= max_buf_stack) { + max_buf_stack = m + 4; + if (max_buf_stack >= buf_size) + check_buffer_overflow(max_buf_stack); + } + s = token_chr(token_info(p)); + if (s <= 0x7F) { + buffer[m++] = (packed_ASCII_code) s; + } else if (s <= 0x7FF) { + buffer[m++] = (packed_ASCII_code) (0xC0 + s / 0x40); + buffer[m++] = (packed_ASCII_code) (0x80 + s % 0x40); + } else if (s <= 0xFFFF) { + buffer[m++] = (packed_ASCII_code) (0xE0 + s / 0x1000); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) / 0x40); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x1000) % 0x40); + } else { + buffer[m++] = (packed_ASCII_code) (0xF0 + s / 0x40000); + buffer[m++] = (packed_ASCII_code) (0x80 + (s % 0x40000) / 0x1000); + buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) / 0x40); + buffer[m++] = (packed_ASCII_code) (0x80 + ((s % 0x40000) % 0x1000) % 0x40); + } + p = token_link(p); + } + if (m > first) { + /*tex |no_new_control_sequence| is |true| */ + cur_cs = id_lookup(first, m - first); + } else if (m == first) { + /*tex the list is empty */ + cur_cs = null_cs; + } + b = (eq_type(cur_cs) != undefined_cs_cmd); + flush_list(n); + last_cs_name = cur_cs; + is_in_csname -= 1; + return b; +} + +/*tex + +An active character will be treated as category 13 following \.{\\if\\noexpand} +or following \.{\\ifcat\\noexpand}. + +*/ + +#define get_x_token_or_active_char() do { \ + get_x_token(); \ + if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \ + if (is_active_cs(cs_text(cur_cs))) { \ + cur_cmd=active_char_cmd; \ + cur_chr=active_cs_value(cs_text(cur_tok-cs_token_flag)); \ + } \ + } \ +} while (0) + +/*tex + +A condition is started when the |expand| procedure encounters an |if_test| +command; in that case |expand| reduces to |conditional|, which is a recursive +procedure. @^recursion@> + +*/ + +void conditional(void) +{ + /*tex Is the condition true? */ + boolean b = false; + /*tex The relation to be evaluated: */ + int r; + /*tex To be tested against the second operand: */ + int m, n; + /*tex For traversing token lists in \.{\\ifx} tests: */ + halfword p, q; + /*tex The |scanner_status| upon entry: */ + int save_scanner_status; + /*tex The |cond_ptr| corresponding to this conditional: */ + halfword save_cond_ptr; + /*tex The type of this conditional: */ + int this_if; + /*tex Was this \.{\\if} preceded by \.{\\unless}? */ + boolean is_unless; + if ((tracing_ifs_par > 0) && (tracing_commands_par <= 1)) { + show_cur_cmd_chr(); + } + push_condition_stack(); + save_cond_ptr = cond_ptr; + is_unless = (cur_chr >= unless_code); + this_if = cur_chr % unless_code; + /*tex Either process \.{\\ifcase} or set |b| to the value of a boolean condition. */ + switch (this_if) { + case if_char_code: + case if_cat_code: + /*tex Test if two characters match. */ + get_x_token_or_active_char(); + if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) { + /*tex It's not a character. */ + m = relax_cmd; + n = too_big_char; + } else { + m = cur_cmd; + n = cur_chr; + } + get_x_token_or_active_char(); + if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) { + cur_cmd = relax_cmd; + cur_chr = too_big_char; + } + if (this_if == if_char_code) + b = (n == cur_chr); + else + b = (m == cur_cmd); + break; + case if_int_code: + case if_dim_code: + case if_abs_dim_code: + case if_abs_num_code: + /*tex + Test the relation between integers or dimensions. Here we use the fact + that |<|, |=|, and |>| are consecutive ASCII codes. + */ + if (this_if == if_int_code || this_if == if_abs_num_code) + scan_int(); + else + scan_normal_dimen(); + n = cur_val; + if ((n < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code)) + negate(n); + /*tex Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + r = cur_tok - other_token; + if ((r < '<') || (r > '>')) { + print_err("Missing = inserted for "); + print_cmd_chr(if_test_cmd, this_if); + help1("I was expecting to see `<', `=', or `>'. Didn't."); + back_error(); + r = '='; + } + if (this_if == if_int_code || this_if == if_abs_num_code) + scan_int(); + else + scan_normal_dimen(); + if ((cur_val < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code)) + negate(cur_val); + switch (r) { + case '<': + b = (n < cur_val); + break; + case '=': + b = (n == cur_val); + break; + case '>': + b = (n > cur_val); + break; + default: + /*tex This can't happen. */ + b = false; + break; + } + break; + case if_odd_code: + /*tex Test if an integer is odd. */ + scan_int(); + b = odd(cur_val); + break; + case if_vmode_code: + b = (abs(cur_list.mode_field) == vmode); + break; + case if_hmode_code: + b = (abs(cur_list.mode_field) == hmode); + break; + case if_mmode_code: + b = (abs(cur_list.mode_field) == mmode); + break; + case if_inner_code: + b = (cur_list.mode_field < 0); + break; + case if_void_code: + scan_register_num(); + p = box(cur_val); + b = (p == null); + break; + case if_hbox_code: + scan_register_num(); + p = box(cur_val); + b = (p != null) && (type(p) == hlist_node); + break; + case if_vbox_code: + scan_register_num(); + p = box(cur_val); + b = (p != null) && (type(p) == vlist_node); + break; + case if_x_code: + /*tex + Test if two tokens match. Note that `\.{\\ifx}' will declare two + macros different if one is \\{long} or \\{outer} and the other + isn't, even though the texts of the macros are the same. + + We need to reset |scanner_status|, since \.{\\outer} control + sequences are allowed, but we might be scanning a macro + definition or preamble. + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_next(); + n = cur_cs; + p = cur_cmd; + q = cur_chr; + get_next(); + if (cur_cmd != p) { + b = false; + } else if (cur_cmd < call_cmd) { + b = (cur_chr == q); + } else { + /*tex + Test if two macro texts match. Note also that `\.{\\ifx}' + decides that macros \.{\\a} and \.{\\b} are different in + examples like this: + + $$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr + {}\\def\\a\{\\c\}& + {}\\def\\c\{\}\cr + {}\\def\\b\{\\d\}& + {}\\def\\d\{\}\cr}}$$ + */ + p = token_link(cur_chr); + /*tex Omit reference counts. */ + q = token_link(equiv(n)); + if (p == q) { + b = true; + } else { + while ((p != null) && (q != null)) { + if (token_info(p) != token_info(q)) { + p = null; + break; + } else { + p = token_link(p); + q = token_link(q); + } + } + b = ((p == null) && (q == null)); + } + } + scanner_status = save_scanner_status; + break; + case if_eof_code: + scan_four_bit_int(); + b = (read_open[cur_val] == closed); + break; + case if_true_code: + b = true; + break; + case if_false_code: + b = false; + break; + case if_case_code: + /*tex Select the appropriate case and |return| or |goto common_ending|. */ + scan_int(); + /*tex |n| is the number of cases to pass. */ + n = cur_val; + if (tracing_commands_par > 1) { + begin_diagnostic(); + tprint("{case "); + print_int(n); + print_char('}'); + end_diagnostic(false); + } + while (n != 0) { + pass_text(); + if (cond_ptr == save_cond_ptr) { + if (cur_chr == or_code) + decr(n); + else + goto COMMON_ENDING; + } else if (cur_chr == fi_code) { + pop_condition_stack(); + } + } + change_if_limit(or_code, save_cond_ptr); + /*tex Wait for \.{\\or}, \.{\\else}, or \.{\\fi}. */ + return; + break; + case if_primitive_code: + save_scanner_status = scanner_status; + scanner_status = normal; + get_next(); + scanner_status = save_scanner_status; + m = prim_lookup(cs_text(cur_cs)); + b = ((cur_cmd != undefined_cs_cmd) && + (m != undefined_primitive) && + (cur_cmd == get_prim_eq_type(m)) && + (cur_chr == get_prim_equiv(m))); + break; + case if_def_code: + /*tex + The conditional \.{\\ifdefined} tests if a control sequence is + defined. We need to reset |scanner_status|, since \.{\\outer} + control sequences are allowed, but we might be scanning a macro + definition or preamble. + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_next(); + b = (cur_cmd != undefined_cs_cmd); + scanner_status = save_scanner_status; + break; + case if_cs_code: + b = test_for_cs(); + break; + case if_in_csname_code: + b = is_in_csname; + break; + case if_font_char_code: + /*tex + The conditional \.{\\iffontchar} tests the existence of a + character in a font. + */ + scan_font_ident(); + n = cur_val; + scan_char_num(); + b = char_exists(n, cur_val); + break; + default: + /*tex there are no other cases, but we need to please |-Wall|. */ + b = false; + } + if (is_unless) + b = !b; + if (tracing_commands_par > 1) { + /*tex Display the value of |b|. */ + begin_diagnostic(); + if (b) + tprint("{true}"); + else + tprint("{false}"); + end_diagnostic(false); + } + if (b) { + change_if_limit(else_code, save_cond_ptr); + /*tex Wait for \.{\\else} or \.{\\fi}. */ + return; + } + /*tex + Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|. In a + construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first + \.{\\else} that we come to after learning that the \.{\\if} is false is + not the \.{\\else} we're looking for. Hence the following curious logic + is needed. + */ + while (1) { + pass_text(); + if (cond_ptr == save_cond_ptr) { + if (cur_chr != or_code) + goto COMMON_ENDING; + print_err("Extra \\or"); + help1( + "I'm ignoring this; it doesn't match any \\if." + ); + error(); + } else if (cur_chr == fi_code) { + pop_condition_stack(); + } + } + COMMON_ENDING: + if (cur_chr == fi_code) { + pop_condition_stack(); + } else { + /*tex Wait for \.{\\fi}. */ + if_limit = fi_code; + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.h b/Build/source/texk/web2c/luatexdir/tex/conditional.h new file mode 100644 index 00000000000..483496eece2 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/conditional.h @@ -0,0 +1,78 @@ +/* conditional.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef CONDITIONAL_H +# define CONDITIONAL_H + +# define unless_code 32 /* amount added for `\.{\\unless}' prefix */ + +typedef enum { + if_char_code = 0, /* \.{\\if} */ + if_cat_code = 1, /* \.{\\ifcat} */ + if_int_code = 2, /* \.{\\ifnum} */ + if_dim_code = 3, /* \.{\\ifdim} */ + if_odd_code = 4, /* \.{\\ifodd} */ + if_vmode_code = 5, /* \.{\\ifvmode} */ + if_hmode_code = 6, /* \.{\\ifhmode} */ + if_mmode_code = 7, /* \.{\\ifmmode} */ + if_inner_code = 8, /* \.{\\ifinner} */ + if_void_code = 9, /* \.{\\ifvoid} */ + if_hbox_code = 10, /* \.{\\ifhbox} */ + if_vbox_code = 11, /* \.{\\ifvbox} */ + if_x_code = 12, /* \.{\\ifx} */ + if_eof_code = 13, /* \.{\\ifeof} */ + if_true_code = 14, /* \.{\\iftrue} */ + if_false_code = 15, /* \.{\\iffalse} */ + if_case_code = 16, /* \.{\\ifcase} */ + if_def_code = 17, /* \.{\\ifdefined} */ + if_cs_code = 18, /* \.{\\ifcsname} */ + if_font_char_code = 19, /* \.{\\iffontchar} */ + if_in_csname_code = 20, /* \.{\\ifincsname} */ + if_primitive_code = 21, /* \.{\\ifprimitive} */ + if_abs_num_code = 22, /* \.{\\ifabsnum} */ + if_abs_dim_code = 23, /* \.{\\ifabsdim} */ + if_condition_code = 24, /* \.{\\ifcondition} */ +} if_type_codes; + +# define if_limit_subtype(A) subtype((A)+1) +# define if_limit_type(A) type((A)+1) +# define if_line_field(A) vlink((A)+1) + +typedef enum { + if_code = 1, /* \.{\\if...} */ + fi_code = 2, /* \.{\\fi} */ + else_code = 3, /* \.{\\else} */ + or_code = 4, /* \.{\\or} */ +} else_type_codes; + +extern halfword cond_ptr; /* top of the condition stack */ +extern int if_limit; /* upper bound on |fi_or_else| codes */ +extern int cur_if; /* type of conditional being worked on */ +extern int if_line; /* line where that conditional began */ +extern int skip_line; /* skipping began here */ + +extern void pass_text(void); +extern void push_condition_stack(void); +extern void pop_condition_stack(void); +extern void change_if_limit(int l, halfword p); + +extern void conditional(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/directions.c b/Build/source/texk/web2c/luatexdir/tex/directions.c new file mode 100644 index 00000000000..59b2f7dcd85 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/directions.c @@ -0,0 +1,196 @@ +/* + +directions.w + +Copyright 2009-2014 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +void scan_direction(void) +{ + int save_cur_cmd = cur_cmd; + int save_cur_chr = cur_chr; + get_x_token(); + if (cur_cmd == assign_dir_cmd) { + cur_val = eqtb[cur_chr].cint; + goto EXIT; + } else { + back_input(); + } + if (scan_keyword("tlt")) { + cur_val = dir_TLT; + } else if (scan_keyword("trt")) { + cur_val = dir_TRT; + } else if (scan_keyword("ltl")) { + cur_val = dir_LTL; + } else if (scan_keyword("rtt")) { + cur_val = dir_RTT; + } else { + tex_error("Bad direction", NULL); + cur_val = 0; + } + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + EXIT: + cur_cmd = save_cur_cmd; + cur_chr = save_cur_chr; +} + +/*tex + + The next two are used by |postlinebreak.c|: + +*/ + +halfword do_push_dir_node(halfword p, halfword a) +{ + halfword n = copy_node(a); + vlink(n) = p; + return n; +} + +halfword do_pop_dir_node(halfword p) +{ + halfword n = vlink(p); + flush_node(p); + return n; +} + +halfword dir_ptr; +halfword text_dir_ptr; + +void initialize_directions(void) +{ + /*tex There is no need to do anything here at the moment. */ +} + +halfword new_dir(int s) +{ + halfword p = new_node(dir_node, 0); + dir_dir(p) = s; + dir_level(p) = cur_level; + return p; +} + +const char *dir_strings_par[4] = { [0] = + "TLT","TRT","LTL","RTT", +}; + +const char *dir_strings_text_normal[4] = { [0] = + "+TLT","+TRT","+LTL","+RTT", +}; + +const char *dir_strings_text_cancel[4] = { [0] = + "-TLT","-TRT","-LTL","-RTT", +}; + +void print_dir_par(int d) +{ + tprint(dir_strings_par[d]); +} + +void print_dir_text(halfword d) +{ + if (subtype(d) == cancel_dir) { + tprint(dir_strings_text_cancel[dir_dir(d)]); + } else { + tprint(dir_strings_text_normal[dir_dir(d)]); + } +} + +scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph) +{ + scaled wd = 0; + if (isglyph) { + if (textdir_parallel(curdir, pdir) == textglyphdir_orthogonal(pdir)) { + wd = glyph_width(p); + if (ex_glyph(p) != 0) { + /* wd = round_xn_over_d(wd, 1000 + ex_glyph(p)/1000, 1000); */ + wd = ext_xn_over_d(wd, 1000000+ex_glyph(p), 1000000); + + } + } else { + wd = glyph_depth(p) + glyph_height(p); + } + } else { + if (textdir_parallel(pdir, curdir)) + wd = width(p); + else + wd = depth(p) + height(p); + } + return wd; +} + +scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph) +{ + scaled_whd whd = { 0, 0, 0 }; + whd.wd = pack_width(curdir, pdir, p, isglyph); + if (isglyph) { + if (is_rotated(curdir)) { + if (textdir_parallel(curdir, pdir)) + whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2; + else + whd.ht = whd.dp = glyph_width(p) / 2; + } else if (is_rotated(pdir)) { + if (textdir_parallel(curdir, pdir)) + whd.ht = whd.dp = (glyph_height(p) + glyph_depth(p)) / 2; + else + whd.ht = glyph_width(p); + } else { + if (glyphdir_eq(curdir, pdir)) { + whd.ht = glyph_height(p); + whd.dp = glyph_depth(p); + } else if (glyphdir_opposite(curdir, pdir)) { + whd.ht = glyph_depth(p); + whd.dp = glyph_height(p); + } else + whd.ht = glyph_width(p); + } + } else { + if (is_rotated(curdir)) { + if (textdir_parallel(curdir, pdir)) + whd.ht = whd.dp = (height(p) + depth(p)) / 2; + else + whd.ht = whd.dp = width(p) / 2; + } else if (pardir_eq(curdir, pdir)) { + whd.ht = height(p); + whd.dp = depth(p); + } else if (pardir_opposite(curdir, pdir)) { + whd.ht = depth(p); + whd.dp = height(p); + } else + whd.ht = width(p); + } + return whd; +} + +void update_text_dir_ptr(int val) +{ + if (dir_level(text_dir_ptr) == cur_level) { + /* update */ + dir_dir(text_dir_ptr) = val; + } else { + /* addition */ + halfword text_dir_tmp = new_dir(val); + vlink(text_dir_tmp) = text_dir_ptr; + text_dir_ptr = text_dir_tmp; + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/directions.h b/Build/source/texk/web2c/luatexdir/tex/directions.h new file mode 100644 index 00000000000..92d6cc18f12 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/directions.h @@ -0,0 +1,168 @@ +/* directions.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef DIRECTIONS_H +# define DIRECTIONS_H + +/* +#define dir_swap 4 +*/ + +#define dir_min_value 0 +#define dir_max_value 3 + +#define check_dir_value(d) \ + if ((d < dir_min_value) || (d > dir_max_value)) \ + d = dir_min_value; + +#define is_mirrored(a) 0 + +#define is_rotated(a) (a == dir_RTT) + +/* + + # define textdir_parallel(a,b) ( \ + ((a == dir_TLT || a == dir_TRT) && (b == dir_TLT || b == dir_TRT)) || \ + ((a == dir_LTL || a == dir_RTT) && (b == dir_LTL || b == dir_RTT)) \ + ) + + # define pardir_parallel(a,b) ( \ + ((a == dir_TLT || a == dir_TRT) && (b == dir_TLT || b == dir_TRT)) || \ + ((a == dir_LTL || a == dir_RTT) && (b == dir_LTL || b == dir_RTT)) \ + ) + + # define pardir_eq(a,b) ( \ + ((a == dir_TLT || a == dir_TRT) && (b == dir_TLT || b == dir_TRT)) || \ + (a == dir_LTL && b == dir_LTL) || \ + (a == dir_RTT && b == dir_RTT) \ + ) + + # define textdir_eq(a,b) ( \ + (a == dir_TLT && b == dir_TLT) || \ + (a == dir_TRT && b == dir_TRT) || \ + (a == dir_LTL && (b == dir_LTL || b == dir_RTT)) || \ + (a == dir_RTT && (b == dir_LTL || b == dir_RTT)) + + # define partextdir_eq(a,b) ( \ + ((a == dir_TLT || a == dir_TRT) && (b == dir_LTL || b == dir_RTT)) || \ + (a == dir_LTL && b == dir_TLT) || \ + (a == dir_RTT && b == dir_TRT) \ + ) + + # define dir_TLT_or_TRT(a) (a == dir_TLT || a == dir_TRT) + # define dir_LTL_or_RTT(a) (a == dir_LTL || a == dir_RTT) + +*/ + +/* TLT TRT LTL RTT */ + +# define dir_TLT_or_TRT(a) (a < 2) +# define dir_LTL_or_RTT(a) (a > 1) + +# define textdir_parallel(a,b) ( \ + (dir_TLT_or_TRT(a) && dir_TLT_or_TRT(b)) || \ + (dir_LTL_or_RTT(a) && dir_LTL_or_RTT(b)) \ +) + +# define pardir_parallel(a,b) ( \ + (dir_TLT_or_TRT(a) && dir_TLT_or_TRT(b)) || \ + (dir_LTL_or_RTT(a) && dir_LTL_or_RTT(b)) \ +) + +# define pardir_opposite(a,b) ( \ + (a == dir_LTL && b == dir_RTT) || \ + (a == dir_RTT && b == dir_LTL) \ +) + +# define textdir_opposite(a,b) ( \ + (a == dir_TLT && b == dir_TRT) || \ + (a == dir_TRT && b == dir_TLT) \ +) + +# define glyphdir_opposite(a,b) 0 + +# define pardir_eq(a,b) ( \ + (dir_TLT_or_TRT(a) && dir_TLT_or_TRT(b)) || \ + (a == dir_LTL && b == dir_LTL) || \ + (a == dir_RTT && b == dir_RTT) \ +) + +# define textdir_eq(a,b) ( \ + (a == dir_TLT && b == dir_TLT) || \ + (a == dir_TRT && b == dir_TRT) || \ + (a == dir_LTL && dir_LTL_or_RTT(b)) || \ + (a == dir_RTT && dir_LTL_or_RTT(b)) \ +) + +# define glyphdir_eq(a,b) ( \ + (a != dir_LTL && b != dir_LTL) || \ + (a == dir_LTL && b == dir_LTL) \ +) + +# define partextdir_eq(a,b) ( \ + (dir_TLT_or_TRT(a) && dir_LTL_or_RTT(b)) || \ + (a == dir_LTL && b == dir_TLT) || \ + (a == dir_RTT && b == dir_TRT) \ +) + +# define textglyphdir_orthogonal(a) (a != dir_RTT) + +# define textdir_is_L(a) (a == dir_TLT) + +# define push_dir(p,a) { \ + halfword dir_tmp=new_dir((a)); \ + vlink(dir_tmp)=p; \ + p=dir_tmp; \ +} + +# define push_dir_node(p,a) { \ + halfword dir_tmp=copy_node((a)); \ + vlink(dir_tmp)=p; \ + p=dir_tmp; \ +} + +# define pop_dir_node(p) { \ + halfword dir_tmp=p; \ + p=vlink(dir_tmp); \ + flush_node(dir_tmp); \ +} + +extern halfword dir_ptr; + +extern halfword text_dir_ptr; + +extern void initialize_directions(void); +extern halfword new_dir(int s); + +extern const char *string_dir(int d); +extern void print_dir_par(int d); +extern void print_dir_text(halfword d); + +extern void scan_direction(void); + +extern halfword do_push_dir_node(halfword p, halfword a); +extern halfword do_pop_dir_node(halfword p); + +scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph); +scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph); + +void update_text_dir_ptr(int val); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.c b/Build/source/texk/web2c/luatexdir/tex/dumpdata.c new file mode 100644 index 00000000000..e7d53050b29 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.c @@ -0,0 +1,539 @@ +/* + +dumpdata.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + + We use a magic number to register the version of the format. Normally this + number only increments when we add a new primitive of change command codes. + We start with 907 which is the sum of the values of the bytes of \quote + {don knuth}. + +*/ + +#define FORMAT_ID (907+54) +#if ((FORMAT_ID>=0) && (FORMAT_ID<=256)) +#error Wrong value for FORMAT_ID. +#endif + +/*tex + +After \.{INITEX} has seen a collection of fonts and macros, it can write all the +necessary information on an auxiliary file so that production versions of \TeX\ +are able to initialize their memory at high speed. The present section of the +program takes care of such output and input. We shall consider simultaneously the +processes of storing and restoring, so that the inverse relation between them is +clear. @.INITEX@> + +The global variable |format_ident| is a string that is printed right after the +|banner| line when \TeX\ is ready to start. For \.{INITEX} this string says +simply `\.{(INITEX)}'; for other versions of \TeX\ it says, for example, +`\.{(preloaded format=plain 1982.11.19)}', showing the year, month, and day that +the format file was created. We have |format_ident=0| before \TeX's tables are +loaded. |FORMAT_ID| is a new field of type int suitable for the identification of +a format: values between 0 and 256 (included) can not be used because in the +previous format they are used for the length of the name of the engine. + +*/ + +str_number format_ident; +str_number format_name; + + +/*tex + +Format files consist of |memory_word| items, and we use the following macros to +dump words of different types: + +*/ + +FILE *fmt_file; + +void store_fmt_file(void) +{ + int j, k, l, x; + halfword p; + char *format_engine; + int callback_id; + char *fmtname = NULL; + /*tex + If dumping is not allowed, abort. The user is not allowed to dump a + format file unless |save_ptr=0|. This condition implies that + |cur_level=level_one|, hence the |xeq_level| array is constant and it + need not be dumped. + */ + if (save_ptr != 0) { + print_err("You can't dump inside a group"); + help1("`{...\\dump}' is a no-no."); + succumb(); + } + /*tex + Create the |format_ident|, open the format file, and inform the user that + dumping has begun. + */ + callback_id = callback_defined(pre_dump_callback); + if (callback_id > 0) { + (void) run_callback(callback_id, "->"); + } + selector = new_string; + tprint(" (format="); + print(job_name); + print_char(' '); + print_int(year_par); + print_char('.'); + print_int(month_par); + print_char('.'); + print_int(day_par); + print_char(')'); + str_room(2); + format_ident = make_string(); + print(job_name); + format_name = make_string(); + if (interaction == batch_mode) { + selector = log_only; + } else { + selector = term_and_log; + } + fmtname = pack_job_name(format_extension); + while (!zopen_w_output(&fmt_file, fmtname, FOPEN_WBIN_MODE)) { + fmtname = prompt_file_name("format file name", format_extension); + } + tprint_nl("Beginning to dump on file "); + tprint(fmtname); + free(fmtname); + tprint_nl(""); + print(format_ident); + /*tex + Dump constants for consistency check. The next few sections of the + program should make it clear how we use the dump/undump macros. First + comes Web2C \TeX's magic constant: "W2TX" + */ + dump_int(0x57325458); + dump_int(FORMAT_ID); + /*tex + We align |engine_name| to 4 bytes with one or more trailing |NUL|. + */ + x = (int) strlen(engine_name); + format_engine = xmalloc((unsigned) (x + 4)); + strcpy(format_engine, engine_name); + for (k = x; k <= x + 3; k++) + format_engine[k] = 0; + x = x + 4 - (x % 4); + dump_int(x); + dump_things(format_engine[0], x); + xfree(format_engine); + dump_int(0x57325458); + dump_int(max_halfword); + dump_int(hash_high); + dump_int(eqtb_size); + dump_int(hash_prime); + /*tex Dump the string pool. */ + k = dump_string_pool(); + print_ln(); + print_int(k); + tprint(" strings using "); + print_int((longinteger) pool_size); + tprint(" bytes"); + /*tex + Dump the dynamic memory. By sorting the list of available spaces in the + variable-size portion of |mem|, we are usually able to get by without + having to dump very much of the dynamic memory. + + We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid + information even when it has not been gathering statistics. + */ + dump_node_mem(); + dump_int(temp_token_head); + dump_int(hold_token_head); + dump_int(omit_template); + dump_int(null_list); + dump_int(backup_head); + dump_int(garbage); + x = (int) fix_mem_min; + dump_int(x); + x = (int) fix_mem_max; + dump_int(x); + x = (int) fix_mem_end; + dump_int(x); + dump_int(avail); + dyn_used = (int) fix_mem_end + 1; + dump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); + x = x + (int) (fix_mem_end + 1 - fix_mem_min); + p = avail; + while (p != null) { + decr(dyn_used); + p = token_link(p); + } + dump_int(dyn_used); + print_ln(); + print_int(x); + tprint(" memory locations dumped; current usage is "); + print_int(var_used); + print_char('&'); + print_int(dyn_used); + /*tex + Dump regions 1 to 4 of |eqtb|, the table of equivalents. The table of + equivalents usually contains repeated information, so we dump it in + compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in + the format file represents $n+m$ consecutive entries of |eqtb|, with |m| + extra copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. + */ + k = null_cs; + do { + j = k; + while (j < int_base - 1) { + if ((equiv(j) == equiv(j + 1)) && (eq_type(j) == eq_type(j + 1)) && + (eq_level(j) == eq_level(j + 1))) + goto FOUND1; + incr(j); + } + l = int_base; + /*tex |j=int_base-1| */ + goto DONE1; + FOUND1: + incr(j); + l = j; + while (j < int_base - 1) { + if ((equiv(j) != equiv(j + 1)) || (eq_type(j) != eq_type(j + 1)) || + (eq_level(j) != eq_level(j + 1))) + goto DONE1; + incr(j); + } + DONE1: + dump_int(l - k); + dump_things(eqtb[k], l - k); + k = j + 1; + dump_int(k - l); + } while (k != int_base); + /*tex Dump regions 5 and 6 of |eqtb|. */ + do { + j = k; + while (j < eqtb_size) { + if (eqtb[j].cint == eqtb[j + 1].cint) + goto FOUND2; + incr(j); + } + l = eqtb_size + 1; + /*tex |j=eqtb_size| */ + goto DONE2; + FOUND2: + incr(j); + l = j; + while (j < eqtb_size) { + if (eqtb[j].cint != eqtb[j + 1].cint) + goto DONE2; + incr(j); + } + DONE2: + dump_int(l - k); + dump_things(eqtb[k], l - k); + k = j + 1; + dump_int(k - l); + } while (k <= eqtb_size); + if (hash_high > 0) { + /*tex Dump the |hash_extra| part: */ + dump_things(eqtb[eqtb_size + 1], hash_high); + } + dump_int(par_loc); + dump_int(write_loc); + dump_math_codes(); + dump_text_codes(); + /*tex + Dump the hash table, A different scheme is used to compress the hash + table, since its lower region is usually sparse. When |text(p)<>0| for + |p<=hash_used|, we output two words, |p| and |hash[p]|. The hash table + is, of course, densely packed for |p>=hash_used|, so the remaining + entries are output in a~block. + */ + dump_primitives(); + dump_int(hash_used); + cs_count = frozen_control_sequence - 1 - hash_used + hash_high; + for (p = hash_base; p <= hash_used; p++) { + if (cs_text(p) != 0) { + dump_int(p); + dump_hh(hash[p]); + incr(cs_count); + } + } + dump_things(hash[hash_used + 1],undefined_control_sequence - 1 - hash_used); + if (hash_high > 0) { + dump_things(hash[eqtb_size + 1], hash_high); + } + dump_int(cs_count); + print_ln(); + print_int(cs_count); + tprint(" multiletter control sequences"); + /*tex Dump the font information. */ + dump_int(max_font_id()); + for (k = 0; k <= max_font_id(); k++) { + /*tex Dump the array info for internal font number |k|. */ + dump_font(k); + tprint_nl("\\font"); + print_esc(font_id_text(k)); + print_char('='); + tprint_file_name((unsigned char *) font_name(k), + (unsigned char *) font_area(k), NULL); + if (font_size(k) != font_dsize(k)) { + tprint(" at "); + print_scaled(font_size(k)); + tprint("pt"); + } + } + print_ln(); + print_int(max_font_id()); + tprint(" preloaded font"); + if (max_font_id() != 1) + print_char('s'); + dump_math_data(); + /*tex Dump the hyphenation tables. */ + dump_language_data(); + /*tex Dump a couple more things and the closing check word. */ + dump_int(interaction); + dump_int(format_ident); + dump_int(format_name); + dump_int(69069); + /*tex + We have already printed a lot of statistics, so we set |tracing_stats:=0| + to prevent them from appearing again. + */ + tracing_stats_par = 0; + /*tex Dump the \LUA\ bytecodes. */ + dump_luac_registers(); + /*tex Close the format file. */ + zwclose(fmt_file); +} + +/*tex + +Corresponding to the procedure that dumps a format file, we have a function that +reads one in. The function returns |false| if the dumped format is incompatible +with the present \TeX\ table sizes, etc. + +*/ + +#define too_small(A) do { \ + wake_up_terminal(); \ + wterm_cr(); \ + fprintf(term_out,"---! Must increase the %s",(A)); \ + goto BAD_FMT; \ +} while (0) + +/*tex + + The inverse macros are slightly more complicated, since we need to check the + range of the values we are reading in. We say `|undump(a)(b)(x)|' to read an + integer value |x| that is supposed to be in the range |a<=x<=b|. + +*/ + +#define undump(A,B,C) do { \ + undump_int(x); \ + if (x<(A) || x>(B)) \ + goto BAD_FMT; \ + else \ + (C) = x; \ +} while (0) + +#define format_debug(A,B) do { \ + if (debug_format_file) { \ + fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \ + } \ +} while (0) + +#define undump_size(A,B,C,D) do { \ + undump_int(x); \ + if (x<(A)) \ + goto BAD_FMT; \ + if (x>(B)) \ + too_small(C); \ + else \ + format_debug (C,x); \ + (D) = x; \ +} while (0) + +boolean load_fmt_file(const char *fmtname) +{ + int j, k, x; + halfword p; + char *format_engine; + /*tex Undump constants for consistency check .*/ + if (ini_version) { + libcfree(hash); + libcfree(eqtb); + libcfree(fixmem); + libcfree(varmem); + } + undump_int(x); + format_debug("format magic number", x); + if (x != 0x57325458) { + /*tex it's not a format file. */ + goto BAD_FMT; + } + undump_int(x); + format_debug("format id", x); + if (x != FORMAT_ID) { + /*tex We have a |FORMAT_ID| mismatch. */ + goto BAD_FMT; + } + undump_int(x); + format_debug("engine name size", x); + if ((x < 0) || (x > 256)) { + /*tex The format file is corrupt. */ + goto BAD_FMT; + } + format_engine = xmalloc((unsigned) x); + undump_things(format_engine[0], x); + format_engine[x - 1] = 0; + if (strcmp(engine_name, format_engine)) { + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "---! %s was written by %s", fmtname, format_engine); + xfree(format_engine); + goto BAD_FMT; + } + xfree(format_engine); + undump_int(x); + format_debug("string pool checksum", x); + if (x != 0x57325458) { + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "---! %s was written by a different version", + fmtname); + goto BAD_FMT; + } + undump_int(x); + if (x != max_halfword) + goto BAD_FMT; + undump_int(hash_high); + if ((hash_high < 0) || (hash_high > sup_hash_extra)) + goto BAD_FMT; + if (hash_extra < hash_high) + hash_extra = hash_high; + eqtb_top = eqtb_size + hash_extra; + if (hash_extra == 0) + hash_top = undefined_control_sequence; + else + hash_top = eqtb_top; + hash = xmallocarray(two_halves, (unsigned) (1 + hash_top)); + memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); + eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); + set_eq_type(undefined_control_sequence, undefined_cs_cmd); + set_equiv(undefined_control_sequence, null); + set_eq_level(undefined_control_sequence, level_zero); + for (x = eqtb_size + 1; x <= eqtb_top; x++) + eqtb[x] = eqtb[undefined_control_sequence]; + undump_int(x); + if (x != eqtb_size) + goto BAD_FMT; + undump_int(x); + if (x != hash_prime) + goto BAD_FMT; + /*tex Undump the string pool */ + str_ptr = undump_string_pool(); + /*tex Undump the dynamic memory */ + undump_node_mem(); + undump_int(temp_token_head); + undump_int(hold_token_head); + undump_int(omit_template); + undump_int(null_list); + undump_int(backup_head); + undump_int(garbage); + undump_int(fix_mem_min); + undump_int(fix_mem_max); + fixmem = xmallocarray(smemory_word, fix_mem_max + 1); + memset(voidcast(fixmem), 0, (fix_mem_max + 1) * sizeof(smemory_word)); + undump_int(fix_mem_end); + undump_int(avail); + undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); + undump_int(dyn_used); + /*tex Undump regions 1 to 6 of the table of equivalents |eqtb|. */ + k = null_cs; + do { + undump_int(x); + if ((x < 1) || (k + x > eqtb_size + 1)) + goto BAD_FMT; + undump_things(eqtb[k], x); + k = k + x; + undump_int(x); + if ((x < 0) || (k + x > eqtb_size + 1)) + goto BAD_FMT; + for (j = k; j <= k + x - 1; j++) + eqtb[j] = eqtb[k - 1]; + k = k + x; + } while (k <= eqtb_size); + if (hash_high > 0) { + /*tex undump |hash_extra| part */ + undump_things(eqtb[eqtb_size + 1], hash_high); + } + undump(hash_base, hash_top, par_loc); + par_token = cs_token_flag + par_loc; + undump(hash_base, hash_top, write_loc); + undump_math_codes(); + undump_text_codes(); + /*tex Undump the hash table */ + undump_primitives(); + undump(hash_base, frozen_control_sequence, hash_used); + p = hash_base - 1; + do { + undump(p + 1, hash_used, p); + undump_hh(hash[p]); + } while (p != hash_used); + undump_things(hash[hash_used + 1], undefined_control_sequence - 1 - hash_used); + if (debug_format_file) + print_csnames(hash_base, undefined_control_sequence - 1); + if (hash_high > 0) { + undump_things(hash[eqtb_size + 1], hash_high); + if (debug_format_file) + print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1)); + } + undump_int(cs_count); + /*tex Undump the font information */ + undump_int(x); + set_max_font_id(x); + for (k = 0; k <= max_font_id(); k++) { + /*tex Undump the array info for internal font number |k| */ + undump_font(k); + } + undump_math_data(); + /*tex Undump the hyphenation tables */ + undump_language_data(); + /*tex Undump a couple more things and the closing check word */ + undump(batch_mode, error_stop_mode, interaction); + if (interactionoption != unspecified_mode) + interaction = interactionoption; + undump(0, str_ptr, format_ident); + undump(0, str_ptr, format_name); + undump_int(x); + if (x != 69069) + goto BAD_FMT; + /*tex Undump the lua bytecodes. */ + undump_luac_registers(); + prev_depth_par = ignore_depth; + return true; + BAD_FMT: + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "(Fatal format file error; I'm stymied)"); + return false; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.h b/Build/source/texk/web2c/luatexdir/tex/dumpdata.h new file mode 100644 index 00000000000..933dcfa1f0d --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.h @@ -0,0 +1,114 @@ +/* dumpdata.h + + Copyright 2009, 2012 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef DUMPDATA_H +# define DUMPDATA_H + +extern str_number format_ident; +extern str_number format_name; /* principal file name */ +extern FILE *fmt_file; /* for input or output of format information */ + +extern void store_fmt_file(void); +extern boolean load_fmt_file(const char *); + +/* (Un)dumping. These are called from the change file. */ +# define dump_things(base, len) \ + do_zdump ((char *) &(base), sizeof (base), (int) (len), DUMP_FILE) +# define undump_things(base, len) \ + do_zundump ((char *) &(base), sizeof (base), (int) (len), DUMP_FILE) + +extern void do_zdump(char *, int, int, FILE *); +extern void do_zundump(char *, int, int, FILE *); + +/* Like do_undump, but check each value against LOW and HIGH. The + slowdown isn't significant, and this improves the chances of + detecting incompatible format files. In fact, Knuth himself noted + this problem with Web2c some years ago, so it seems worth fixing. We + can't make this a subroutine because then we lose the type of BASE. */ +# define undump_checked_things(low, high, base, len) \ + do { \ + unsigned i; \ + undump_things (base, len); \ + for (i = 0; i < (len); i++) { \ + if ((&(base))[i] < (low) || (&(base))[i] > (high)) { \ + FATAL5 ("Item %u (=%ld) of .fmt array at %lx <%ld or >%ld", \ + i, (unsigned long) (&(base))[i], (unsigned long) &(base), \ + (unsigned long) low, (unsigned long) high); \ + } \ + } \ + } while (0) + +/* Like undump_checked_things, but only check the upper value. We use + this when the base type is unsigned, and thus all the values will be + greater than zero by definition. */ +# define undump_upper_check_things(high, base, len) \ + do { \ + unsigned i; \ + undump_things (base, len); \ + for (i = 0; i < (len); i++) { \ + if ((&(base))[i] > (high)) { \ + FATAL4 ("Item %u (=%ld) of .fmt array at %lx >%ld", \ + i, (unsigned long) (&(base))[i], (unsigned long) &(base), \ + (unsigned long) high); \ + } \ + } \ + } while (0) + +/* Use the above for all the other dumping and undumping. */ +# define generic_dump(x) dump_things (x, 1) +# define generic_undump(x) undump_things (x, 1) + +# define dump_wd generic_dump +# define dump_hh generic_dump +# define dump_qqqq generic_dump +# define undump_wd generic_undump +# define undump_hh generic_undump +# define undump_qqqq generic_undump + +/* `dump_int' is called with constant integers, so we put them into a + variable first. */ +# define dump_int(x) \ + do \ + { \ + int x_val = (x); \ + generic_dump (x_val); \ + } \ + while (0) + +/* web2c/regfix puts variables in the format file loading into + registers. Some compilers aren't willing to take addresses of such + variables. So we must kludge. */ +# if defined(REGFIX) || defined(WIN32) +# define undump_int(x) \ + do \ + { \ + int x_val; \ + generic_undump (x_val); \ + x = x_val; \ + } \ + while (0) +# else +# define undump_int generic_undump +# endif /* not (REGFIX || WIN32) */ + + + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.c b/Build/source/texk/web2c/luatexdir/tex/equivalents.c new file mode 100644 index 00000000000..3d260d5c5ee --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.c @@ -0,0 +1,1220 @@ +/* + +equivalents.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +halfword last_cs_name = null_cs; + +/*tex |eqtb[p]| has just been restored or retained. */ + +static void diagnostic_trace(halfword p, const char *s) +{ + begin_diagnostic(); + print_char('{'); + tprint(s); + print_char(' '); + show_eqtb(p); + print_char('}'); + end_diagnostic(false); +} + +void show_eqtb_meaning(halfword n); + +/*tex + +Now that we have studied the data structures for \TeX's semantic routines, we +ought to consider the data structures used by its syntactic routines. In other +words, our next concern will be the tables that \TeX\ looks at when it is +scanning what the user has written. + +The biggest and most important such table is called |eqtb|. It holds the current +``equivalents'' of things; i.e., it explains what things mean or what their +current values are, for all quantities that are subject to the nesting structure +provided by \TeX's grouping mechanism. There are six parts to |eqtb|: + +1) |eqtb[null_cs]| holds the current equivalent of the zero-length control +sequence. + +2) |eqtb[hash_base..(glue_base-1)]| holds the current equivalents of single- and +multiletter control sequences. + +3) |eqtb[glue_base..(local_base-1)]| holds the current equivalents of glue +parameters like the current baselineskip. + +4) |eqtb[local_base..(int_base-1)]| holds the current equivalents of local +halfword quantities like the current box registers, the current ``catcodes,'' the +current font, and a pointer to the current paragraph shape. + +5) |eqtb[int_base..(dimen_base-1)]| holds the current equivalents of fullword +integer parameters like the current hyphenation penalty. + +6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents of fullword +dimension parameters like the current hsize or amount of hanging indentation. + +Note that, for example, the current amount of baselineskip glue is determined by +the setting of a particular location in region~3 of |eqtb|, while the current +meaning of the control sequence `\.{\\baselineskip}' (which might have been +changed by \.{\\def} or \.{\\let}) appears in region~2. + +The last two regions of |eqtb| have fullword values instead of the three fields +|eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary, but \TeX\ needs +to store the |eq_level| information in another array called |xeq_level|. + +*/ + +memory_word *eqtb; +halfword eqtb_top; +quarterword xeq_level[(eqtb_size + 1)]; + +void initialize_equivalents(void) +{ + int k; + for (k = int_base; k <= eqtb_size; k++) + xeq_level[k] = level_one; +} + +/*tex + +The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups in +\TeX\ means that |eqtb| entries valid in outer groups should be saved and +restored later if they are overridden inside the braces. When a new |eqtb| value +is being assigned, the program therefore checks to see if the previous entry +belongs to an outer level. In such a case, the old value is placed on the +|save_stack| just before the new value enters |eqtb|. At the end of a grouping +level, i.e., when the right brace is sensed, the |save_stack| is used to restore +the outer values, and the inner ones are destroyed. + +Entries on the |save_stack| are of type |save_record|. The top item on this stack +is |save_stack[p]|, where |p=save_ptr-1|; it contains three fields called +|save_type|, |save_level|, and |save_value|, and it is interpreted in one of four +ways: + +1) If |save_type(p)=restore_old_value|, then |save_value(p)| is a location in +|eqtb| whose current value should be destroyed at the end of the current group +and replaced by |save_word(p-1)| (|save_type(p-1)==saved_eqtb|). Furthermore if +|save_value(p)>=int_base|, then |save_level(p)| should replace the corresponding +entry in |xeq_level| (if |save_value(p)<int_base|, then the level is part of +|save_word(p-1)|). + +2) If |save_type(p)=restore_zero|, then |save_value(p)| is a location in |eqtb| +whose current value should be destroyed at the end of the current group, when it +should be replaced by the current value of |eqtb[undefined_control_sequence]|. + +3) If |save_type(p)=insert_token|, then |save_value(p)| is a token that should be +inserted into \TeX's input when the current group ends. + +4) If |save_type(p)=level_boundary|, then |save_level(p)| is a code explaining +what kind of group we were previously in, and |save_value(p)| points to the level +boundary word at the bottom of the entries for that group. Furthermore, +|save_value(p-1)| contains the source line number at which the current level of +grouping was entered, this field has itself a type: |save_type(p-1)==saved_line|. + +Besides this `official' use, various subroutines push temporary variables on the +save stack when it is handy to do so. These all have an explicit |save_type|, and +they are: + +|saved_adjust| signifies an adjustment is beging scanned, +|saved_insert| an insertion is being scanned, +|saved_disc| the \.{\\discretionary} sublist we are working on right now, +|saved_boxtype| whether a \.{\\localbox} is \.{\\left} or \.{\\right}, +|saved_textdir| a text direction to be restored, +|saved_eqno| diffentiates between \.{\\eqno} and \.{\\leqno}, +|saved_choices| the \.{\\mathchoices} sublist we are working on right now, +|saved_math| and interrupted math list, +|saved_boxcontext| the box context value, +|saved_boxspec| the box \.{to} or \.{spread} specification, +|saved_boxdir| the box \.{dir} specification, +|saved_boxattr| the box \.{attr} specification, +|saved_boxpack| the box \.{pack} specification. + +The global variable |cur_group| keeps track of what sort of group we are +currently in. Another global variable, |cur_boundary|, points to the topmost +|level_boundary| word. And |cur_level| is the current depth of nesting. The +routines are designed to preserve the condition that no entry in the |save_stack| +or in |eqtb| ever has a level greater than |cur_level|. + +*/ + +save_record *save_stack; +int save_ptr; /* first unused entry on the save stack */ +int max_save_stack; /* maximum usage of save stack */ +quarterword cur_level = level_one; /* current nesting level for groups */ +group_code cur_group = bottom_level; /* current group type */ +int cur_boundary; /* where the current level begins */ + +/*tex + +At this time it might be a good idea for the reader to review the introduction to +|eqtb| that was given above just before the long lists of parameter names. Recall +that the ``outer level'' of the program is |level_one|, since undefined control +sequences are assumed to be ``defined'' at |level_zero|. + +The following macro is used to test if there is room for up to eight more entries +on |save_stack|. By making a conservative test like this, we can get by with +testing for overflow in only a few places. + +*/ + +#define check_full_save_stack() do { \ + if (save_ptr>max_save_stack) { \ + max_save_stack=save_ptr; \ + if (max_save_stack>save_size-8) \ + overflow("save size",(unsigned)save_size); \ + } \ +} while (0) + +/*tex + +Procedure |new_save_level| is called when a group begins. The argument is a group +identification code like `|hbox_group|'. After calling this routine, it is safe +to put six more entries on |save_stack|. + +In some cases integer-valued items are placed onto the |save_stack| just below a +|level_boundary| word, because this is a convenient place to keep information +that is supposed to ``pop up'' just when the group has finished. For example, +when `\.{\\hbox to 100pt}' is being treated, the 100pt dimension is stored on +|save_stack| just before |new_save_level| is called. + +*/ + +void new_save_level(group_code c) +{ /*tex We begin a new level of grouping. */ + check_full_save_stack(); + set_saved_record(0, saved_line, 0, line); + incr(save_ptr); + save_type(save_ptr) = level_boundary; + save_level(save_ptr) = cur_group; + save_value(save_ptr) = cur_boundary; + if (cur_level == max_quarterword) { + overflow("grouping levels", max_quarterword - min_quarterword); + } + /*tex We quit if |(cur_level+1)| is too big to be stored in |eqtb|. */ + cur_boundary = save_ptr; + cur_group = c; + if (tracing_groups_par > 0) + group_trace(false); + incr(cur_level); + incr(save_ptr); +} + +static const char *save_stack_type(int v) +{ + const char *s = ""; + switch (save_type(v)) { + case restore_old_value: s = "restore_old_value"; break; + case restore_zero: s = "restore_zero"; break; + case insert_token: s = "insert_token"; break; + case level_boundary: s = "level_boundary"; break; + case saved_line: s = "saved_line"; break; + case saved_adjust: s = "saved_adjust"; break; + case saved_insert: s = "saved_insert"; break; + case saved_disc: s = "saved_disc"; break; + case saved_boxtype: s = "saved_boxtype"; break; + case saved_textdir: s = "saved_textdir"; break; + case saved_eqno: s = "saved_eqno"; break; + case saved_choices: s = "saved_choices"; break; + case saved_math: s = "saved_math"; break; + case saved_boxcontext: s = "saved_boxcontext"; break; + case saved_boxspec: s = "saved_boxspec"; break; + case saved_boxdir: s = "saved_boxdir"; break; + case saved_boxattr: s = "saved_boxattr"; break; + case saved_boxpack: s = "saved_boxpack"; break; + case saved_attrlist: s = "saved_attrlist"; break; + case saved_eqtb: s = "saved_eqtb"; break; + default: break; + } + return s; +} + +void print_save_stack(void) +{ + int i; + begin_diagnostic(); + selector = term_and_log; + print_ln(); + for (i = (save_ptr - 1); i >= 0; i--) { + tprint("save_stack["); + if (i < 100) + print_char(' '); + if (i < 10) + print_char(' '); + print_int(i); + tprint("]: "); + tprint(save_stack_type(i)); + switch (save_type(i)) { + case restore_old_value: + tprint(", "); + show_eqtb_meaning(save_value(i)); + tprint("="); + if (save_value(i) >= int_base) { + print_int(save_word(i - 1).cint); + } else { + print_int(eq_type_field(save_word(i - 1))); + print_char(','); + print_int(equiv_field(save_word(i - 1))); + } + i--; + break; + case restore_zero: + tprint(", "); + show_eqtb_meaning(save_value(i)); + break; + case insert_token: + tprint(", "); + { + halfword p = get_avail(); + set_token_info(p, save_value(i)); + show_token_list(p, null, 1); + free_avail(p); + } + break; + case level_boundary: + tprint(", old group="); + print_int(save_level(i)); + tprint(", boundary = "); + print_int(save_value(i)); + tprint(", line = "); + print_int(save_value(i - 1)); + i--; + break; + case saved_adjust: + tprint(", "); + /*tex vadjust vs vadjust pre */ + print_int(save_level(i)); + break; + case saved_insert: + tprint(", "); + /*tex insert number */ + print_int(save_value(i)); + break; + case saved_boxtype: + /*tex \.{\\localleftbox} vs \.{\\localrightbox} */ + tprint(", "); + print_int(save_value(i)); + break; + case saved_eqno: + /*tex \.{\\eqno} vs \.{\\leqno} */ + tprint(", "); + print_int(save_value(i)); + break; + case saved_disc: + case saved_choices: + tprint(", "); + print_int(save_value(i)); + break; + case saved_math: + tprint(", listptr="); + print_int(save_value(i)); + break; + case saved_boxcontext: + tprint(", "); + print_int(save_value(i)); + break; + case saved_boxspec: + tprint(", spec="); + print_int(save_level(i)); + tprint(", dimen="); + print_int(save_value(i)); + break; + case saved_textdir: + case saved_boxdir: + tprint(", "); + print_dir_text(save_value(i)); + break; + case saved_boxattr: + case saved_boxpack: + case saved_attrlist: + tprint(", "); + print_int(save_value(i)); + break; + case saved_line: + case saved_eqtb: + break; + default: + break; + } + print_ln(); + } + end_diagnostic(true); +} + +/*tex + +The \.{\\showgroups} command displays all currently active grouping levels. + +The modifications of \TeX\ required for the display produced by the +|show_save_groups| procedure were first discussed by Donald~E. Knuth in {\sl +TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990. @^Knuth, Donald Ervin@> + +In order to understand a group type we also have to know its mode. Since +unrestricted horizontal modes are not associated with grouping, they are skipped +when traversing the semantic nest. + +*/ + +void show_save_groups(void) +{ + int p = nest_ptr; + int m; + save_pointer v = save_ptr; + quarterword l = cur_level; + group_code c = cur_group; + int a = 1; /* to keep track of alignments */ + int i; + quarterword j; + const char *s = NULL; + save_ptr = cur_boundary; + decr(cur_level); + tprint_nl(""); + print_ln(); + while (1) { + tprint_nl("### "); + print_group(true); + if (cur_group == bottom_level) + goto DONE; + do { + m = nest[p].mode_field; + if (p > 0) + decr(p); + else + m = vmode; + } while (m == hmode); + tprint(" ("); + switch (cur_group) { + case simple_group: + incr(p); + goto FOUND2; + break; + case hbox_group: + case adjusted_hbox_group: + s = "hbox"; + break; + case vbox_group: + s = "vbox"; + break; + case vtop_group: + s = "vtop"; + break; + case align_group: + if (a == 0) { + if (m == -vmode) + s = "halign"; + else + s = "valign"; + a = 1; + goto FOUND1; + } else { + if (a == 1) + tprint("align entry"); + else + tprint_esc("cr"); + if (p >= a) + p = p - a; + a = 0; + goto FOUND; + } + break; + case no_align_group: + incr(p); + a = -1; + tprint_esc("noalign"); + goto FOUND2; + break; + case output_group: + tprint_esc("output"); + goto FOUND; + break; + case math_group: + goto FOUND2; + break; + case disc_group: + tprint_esc("discretionary"); + for (i = 1; i < 3; i++) { + if (i <= saved_value(-2)) { + tprint("{}"); + } + } + goto FOUND2; + break; + case math_choice_group: + tprint_esc("mathchoice"); + for (i = 1; i < 4; i++) { + /*tex A different offset because |-2==saved_textdir|: */ + if (i <= saved_value(-3)) { + tprint("{}"); + } + } + goto FOUND2; + break; + case insert_group: + if (saved_type(-1) == saved_adjust) { + tprint_esc("vadjust"); + if (saved_level(-1) != 0) + tprint(" pre"); + } else { + tprint_esc("insert"); + print_int(saved_value(-1)); + } + goto FOUND2; + break; + case vcenter_group: + s = "vcenter"; + goto FOUND1; + break; + case semi_simple_group: + incr(p); + tprint_esc("begingroup"); + goto FOUND; + break; + case math_shift_group: + if (m == mmode) { + print_char('$'); + } else if (nest[p].mode_field == mmode) { + print_cmd_chr(eq_no_cmd, saved_value(-2)); + goto FOUND; + } + print_char('$'); + goto FOUND; + break; + case math_left_group: + if (subtype(nest[p + 1].eTeX_aux_field) == left_noad_side) + tprint_esc("left"); + else + tprint_esc("middle"); + goto FOUND; + break; + default: + confusion("showgroups"); + break; + } + /*tex Show the box context */ + i = saved_value(-5); + if (i != 0) { + if (i < box_flag) { + if (abs(nest[p].mode_field) == vmode) + j = hmove_cmd; + else + j = vmove_cmd; + if (i > 0) + print_cmd_chr(j, 0); + else + print_cmd_chr(j, 1); + print_scaled(abs(i)); + tprint("pt"); + } else if (i < ship_out_flag) { + if (i >= global_box_flag) { + tprint_esc("global"); + i = i - (global_box_flag - box_flag); + } + tprint_esc("setbox"); + print_int(i - box_flag); + print_char('='); + } else { + print_cmd_chr(leader_ship_cmd, i - (leader_flag - a_leaders)); + } + } + FOUND1: + tprint_esc(s); + /*tex Show the box packaging info. The offsets may vary. */ + { + int ii = -1; + while (saved_type(ii) != saved_boxspec) + ii--; + if (saved_value(ii) != 0) { + print_char(' '); + if (saved_level(ii) == exactly) + tprint("to"); + else + tprint("spread"); + print_scaled(saved_value(ii)); + tprint("pt"); + } + } + FOUND2: + print_char('{'); + FOUND: + print_char(')'); + decr(cur_level); + cur_group = save_level(save_ptr); + save_ptr = save_value(save_ptr); + } + DONE: + save_ptr = v; + cur_level = l; + cur_group = c; +} + +/*tex + +Just before an entry of |eqtb| is changed, the following procedure should be +called to update the other data structures properly. It is important to keep in +mind that reference counts in |mem| include references from within |save_stack|, +so these counts must be handled carefully. @^reference counts@> + +We don't need to destroy when an assignment has the same node: + +*/ + +void eq_destroy(memory_word w) +{ + halfword q; + switch (eq_type_field(w)) { + case call_cmd: + case long_call_cmd: + case outer_call_cmd: + case long_outer_call_cmd: + delete_token_ref(equiv_field(w)); + break; + case glue_ref_cmd: + flush_node(equiv_field(w)); + break; + case shape_ref_cmd: + q = equiv_field(w); + if (q != null) { + /*tex + We need to free a \.{\\parshape} block. Such a block is + |2n+1| words long, where |n=vinfo(q)|. It happens in the + flush function. + */ + flush_node(q); + } + break; + case box_ref_cmd: + flush_node_list(equiv_field(w)); + break; + default: + break; + } +} + +/*tex + +To save a value of |eqtb[p]| that was established at level |l|, we can use the +following subroutine. + +*/ +void eq_save(halfword p, quarterword l) +{ + check_full_save_stack(); + if (l == level_zero) { + save_type(save_ptr) = restore_zero; + } else { + save_word(save_ptr) = eqtb[p]; + save_type(save_ptr) = saved_eqtb; + incr(save_ptr); + save_type(save_ptr) = restore_old_value; + } + save_level(save_ptr) = l; + save_value(save_ptr) = p; + incr(save_ptr); +} + +/*tex + +The procedure |eq_define| defines an |eqtb| entry having specified |eq_type| and +|equiv| fields, and saves the former value if appropriate. This procedure is used +only for entries in the first four regions of |eqtb|, i.e., only for entries that +have |eq_type| and |equiv| fields. After calling this routine, it is safe to put +four more entries on |save_stack|, provided that there was room for four more +entries before the call, since |eq_save| makes the necessary test. + +*/ + +void eq_define(halfword p, quarterword t, halfword e) +{ + boolean trace = tracing_assigns_par > 0; + if ((eq_type(p) == t) && (equiv(p) == e)) { + if (trace) + diagnostic_trace(p, "reassigning"); + eq_destroy(eqtb[p]); + return; + } + if (trace) + diagnostic_trace(p, "changing"); + if (eq_level(p) == cur_level) + eq_destroy(eqtb[p]); + else if (cur_level > level_one) + eq_save(p, eq_level(p)); + set_eq_level(p, cur_level); + set_eq_type(p, t); + set_equiv(p, e); + if (trace) + diagnostic_trace(p, "into"); +} + +/*tex + +The counterpart of |eq_define| for the remaining (fullword) positions in |eqtb| +is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all |p|, a +`|restore_zero|' will never be used in this case. + +*/ + +void eq_word_define(halfword p, int w) +{ + boolean trace = tracing_assigns_par > 0; + if (eqtb[p].cint == w) { + if (trace) + diagnostic_trace(p, "reassigning"); + return; + } + if (trace) + diagnostic_trace(p, "changing"); + if (xeq_level[p] != cur_level) { + eq_save(p, xeq_level[p]); + xeq_level[p] = cur_level; + } + eqtb[p].cint = w; + if (trace) + diagnostic_trace(p, "into"); +} + +/*tex + +The |eq_define| and |eq_word_define| routines take care of local definitions. +@^global definitions@> Global definitions are done in almost the same way, but +there is no need to save old values, and the new value is associated with +|level_one|. + +*/ + +void geq_define(halfword p, quarterword t, halfword e) +{ + boolean trace = tracing_assigns_par > 0; + if (trace) + diagnostic_trace(p, "globally changing"); + eq_destroy(eqtb[p]); + set_eq_level(p, level_one); + set_eq_type(p, t); + set_equiv(p, e); + if (trace) + diagnostic_trace(p, "into"); +} + +void geq_word_define(halfword p, int w) +{ + boolean trace = tracing_assigns_par > 0; + if (trace) + diagnostic_trace(p, "globally changing"); + eqtb[p].cint = w; + xeq_level[p] = level_one; + if (trace) + diagnostic_trace(p, "into"); +} + +/*tex + +Subroutine |save_for_after| puts a token on the stack for save-keeping. + +*/ + +void save_for_after(halfword t) +{ + if (cur_level > level_one) { + check_full_save_stack(); + save_type(save_ptr) = insert_token; + save_level(save_ptr) = level_zero; + save_value(save_ptr) = t; + incr(save_ptr); + } +} + +/*tex + +The |unsave| routine goes the other way, taking items off of |save_stack|. This +routine takes care of restoration when a level ends; everything belonging to the +topmost group is cleared off of the save stack. + +*/ + +void unsave(void) +{ + halfword p; + quarterword l = level_one; + /*tex Variable |a| registers if we already have processed an \.{\\aftergroup}. */ + boolean a = false; + unsave_math_codes(cur_level); + unsave_cat_codes(cat_code_table_par, cur_level); + unsave_text_codes(cur_level); + unsave_math_data(cur_level); + if (cur_level > level_one) { + boolean trace = tracing_restores_par > 0; + decr(cur_level); + /*tex Clear off top level from |save_stack|. */ + while (true) { + decr(save_ptr); + if (save_type(save_ptr) == level_boundary) + break; + p = save_value(save_ptr); + if (save_type(save_ptr) == insert_token) { + reinsert_token(a, p); + /*tex always |true| as we are always in \ETEX\ now. */ + a = true; + } else { + if (save_type(save_ptr) == restore_old_value) { + l = save_level(save_ptr); + decr(save_ptr); + } else { + save_word(save_ptr) = eqtb[undefined_control_sequence]; + } + /*tex + Store |save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]| + holds a global value A global definition, which sets the + level to |level_one|, will not be undone by |unsave|. If at + least one global definition of |eqtb[p]| has been carried out + within the group that just ended, the last such definition + will therefore survive. + */ + if (p < int_base || p > eqtb_size) { + if (eq_level(p) == level_one) { + /*tex Destroy the saved value: */ + eq_destroy(save_word(save_ptr)); + if (trace) + diagnostic_trace(p, "retaining"); + } else { + /*tex Destroy the current value: */ + eq_destroy(eqtb[p]); + /*tex Restore the saved value: */ + eqtb[p] = save_word(save_ptr); + if (trace) + diagnostic_trace(p, "restoring"); + } + } else if (xeq_level[p] != level_one) { + eqtb[p] = save_word(save_ptr); + xeq_level[p] = l; + if (trace) + diagnostic_trace(p, "restoring"); + } else { + if (trace) + diagnostic_trace(p, "retaining"); + } + } + } + + if (tracing_groups_par > 0) { + group_trace(true); + } + if (grp_stack[in_open] == cur_boundary) { + /*tex Groups are possibly not properly nested with files. */ + group_warning(); + } + cur_group = save_level(save_ptr); + cur_boundary = save_value(save_ptr); + decr(save_ptr); + } else { + /*tex |unsave| is not used when |cur_group=bottom_level| */ + confusion("curlevel"); + } + attr_list_cache = cache_disabled; +} + +/*tex + +Most of the parameters kept in |eqtb| can be changed freely, but there's an +exception: The magnification should not be used with two different values during +any \TeX\ job, since a single magnification is applied to an entire run. The +global variable |mag_set| is set to the current magnification whenever it becomes +necessary to ``freeze'' it at a particular value. + +The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| for +magnification. If nonzero, this magnification should be used henceforth. We might +drop magnifaction at some point. + +*/ + +int mag_set = 0; + +void prepare_mag(void) +{ + if ((mag_set > 0) && (mag_par != mag_set)) { + print_err("Incompatible magnification ("); + print_int(mag_par); + tprint(");"); + tprint_nl(" the previous value will be retained"); + help2( + "I can handle only one magnification ratio per job. So I've", + "reverted to the magnification you used earlier on this run." + ); + int_error(mag_set); + geq_word_define(int_base + mag_code, mag_set); /* |mag:=mag_set| */ + } + if ((mag_par <= 0) || (mag_par > 32768)) { + print_err("Illegal magnification has been changed to 1000"); + help1( + "The magnification ratio must be between 1 and 32768." + ); + int_error(mag_par); + geq_word_define(int_base + mag_code, 1000); + } + if ((mag_set == 0) && (mag_par != mag_set)) { + if (mag_par != 1000) + one_true_inch = xn_over_d(one_hundred_inch, 10, mag_par); + else + one_true_inch = one_inch; + } + mag_set = mag_par; +} + +/*tex + +Let's pause a moment now and try to look at the Big Picture. The \TeX\ program +consists of three main parts: syntactic routines, semantic routines, and output +routines. The chief purpose of the syntactic routines is to deliver the user's +input to the semantic routines, one token at a time. The semantic routines act as +an interpreter responding to these tokens, which may be regarded as commands. And +the output routines are periodically called on to convert box-and-glue lists into +a compact set of instructions that will be sent to a typesetter. We have +discussed the basic data structures and utility routines of \TeX, so we are good +and ready to plunge into the real activity by considering the syntactic routines. + +Our current goal is to come to grips with the |get_next| procedure, which is the +keystone of \TeX's input mechanism. Each call of |get_next| sets the value of +three variables |cur_cmd|, |cur_chr|, and |cur_cs|, representing the next input +token. + +$$ + \vbox{\halign{#\hfil\cr + \hbox{|cur_cmd| denotes a command code from the long list of codes given above;}\cr + \hbox{|cur_chr| denotes a character code or other modifier of the command code;}\cr + \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr + \hbox{\qquad if the current token was a control sequence, otherwise it's zero.}\cr}} +$$ + +Underlying this external behavior of |get_next| is all the machinery necessary to +convert from character files to tokens. At a given time we may be only partially +finished with the reading of several files (for which \.{\\input} was specified), +and partially finished with the expansion of some user-defined macros and/or some +macro parameters, and partially finished with the generation of some text in a +template for \.{\\halign}, and so on. When reading a character file, special +characters must be classified as math delimiters, etc.; comments and extra blank +spaces must be removed, paragraphs must be recognized, and control sequences must +be found in the hash table. Furthermore there are occasions in which the scanning +routines have looked ahead for a word like `\.{plus}' but only part of that word +was found, hence a few characters must be put back into the input and scanned +again. + +To handle these situations, which might all be present simultaneously, \TeX\ uses +various stacks that hold information about the incomplete activities, and there +is a finite state control for each level of the input mechanism. These stacks +record the current state of an implicitly recursive process, but the |get_next| +procedure is not recursive. Therefore it will not be difficult to translate these +algorithms into low-level languages that do not support recursion. + +In general, |cur_cmd| is the current command as set by |get_next|, while +|cur_chr| is the operand of the current command. The control sequence found here +is registsred in |cur_cs| and is zero if none found. The |cur_tok| variable +contains the packed representative of |cur_cmd| and |cur_chr| and like the other +ones is global. + +*/ + +int cur_cmd; +halfword cur_chr; +halfword cur_cs; +halfword cur_tok; + +/*tex + +Here is a procedure that displays the current command. The variable |n| holds the +level of \.{\\if...\\fi} nesting and |l| the line where \.{\\if} started. + +*/ + +void show_cur_cmd_chr(void) +{ + int n, l; + halfword p; + begin_diagnostic(); + tprint_nl("{"); + if (mode_par != shown_mode) { + print_mode(mode_par); + tprint(": "); + shown_mode = mode_par; + } + print_cmd_chr((quarterword) cur_cmd, cur_chr); + if (tracing_ifs_par > 0) { + if (cur_cmd >= if_test_cmd) { + if (cur_cmd <= fi_or_else_cmd) { + tprint(": "); + if (cur_cmd == fi_or_else_cmd) { + print_cmd_chr(if_test_cmd, cur_if); + print_char(' '); + n = 0; + l = if_line; + } else { + n = 1; + l = line; + } + p = cond_ptr; + while (p != null) { + incr(n); + p = vlink(p); + } + tprint("(level "); + print_int(n); + print_char(')'); + print_if_line(l); + } + } + } + print_char('}'); + end_diagnostic(false); +} + +/*tex + +Here is a procedure that displays the contents of |eqtb[n]| symbolically. + +*/ + +void show_eqtb(halfword n) +{ + if (n < null_cs) { + /*tex + This can't happen in a pure \TEX\ run, but careless usage of tokens + at the \LUA\ end can make you end up here. + */ + tprint("? bad token, case 1: "); + print_int(n); + } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) { + /*tex + This routine show the current meaning of |eqtb| entry |n| in region 1 or 2. + Similar routines for the other regions will appear below. + */ + sprint_cs(n); + print_char('='); + print_cmd_chr(eq_type(n), equiv(n)); + if (eq_type(n) >= call_cmd) { + print_char(':'); + show_token_list(token_link(equiv(n)), null, 32); + } + } else if (n < local_base) { + /*tex + Here we show equivalent |n| in region 3. All glue parameters and registers + are initially `\.{0pt plus0pt minus0pt}'. + */ + if (n < skip_base) { + if (n < glue_base + thin_mu_skip_code) + print_cmd_chr(assign_glue_cmd, n); + else + print_cmd_chr(assign_mu_glue_cmd, n); + print_char('='); + if (n < glue_base + thin_mu_skip_code) + print_spec(equiv(n), "pt"); + else + print_spec(equiv(n), "mu"); + } else if (n < mu_skip_base) { + tprint_esc("skip"); + print_int(n - skip_base); + print_char('='); + print_spec(equiv(n), "pt"); + } else { + tprint_esc("muskip"); + print_int(n - mu_skip_base); + print_char('='); + print_spec(equiv(n), "mu"); + } + + } else if (n < int_base) { + /*tex + We're now at equivalent |n| in region 4. First we initialize most + things to null or undefined values. An undefined font is represented + by the internal code |font_base|. + + However, the character code tables are given initial values based on + the conventional interpretation of ASCII code. These initial values + should not be changed when \TeX\ is adapted for use with non-English + languages; all changes to the initialization conventions should be + made in format packages, not in \TeX\ itself, so that global + interchange of formats is possible. + */ + if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) { + if (n == par_shape_loc) + print_cmd_chr(set_tex_shape_cmd, n); + else + print_cmd_chr(set_etex_shape_cmd, n); + print_char('='); + if (equiv(n) == null) { + print_char('0'); + } else if (n > par_shape_loc) { + print_int(penalty(equiv(n))); + print_char(' '); + print_int(penalty(equiv(n) + 1)); + if (penalty(equiv(n)) > 1) + tprint_esc("ETC."); + } else { + print_int(vinfo(par_shape_par_ptr + 1)); + } + } else if (n < toks_base) { + print_cmd_chr(assign_toks_cmd, n); + print_char('='); + if (equiv(n) != null) + show_token_list(token_link(equiv(n)), null, 32); + } else if (n < box_base) { + tprint_esc("toks"); + print_int(n - toks_base); + print_char('='); + if (equiv(n) != null) + show_token_list(token_link(equiv(n)), null, 32); + } else if (n < cur_font_loc) { + tprint_esc("box"); + print_int(n - box_base); + print_char('='); + if (equiv(n) == null) { + tprint("void"); + } else { + depth_threshold = 0; + breadth_max = 1; + show_node_list(equiv(n)); + } + } else if (n == cur_font_loc) { + /*tex + Let's show the font identifier in |eqtb[n]|, that's + |font_id_text(equiv(n))| + */ + tprint("current font"); + print_char('='); + print_esc(hash[font_id_base + equiv(n)].rh); + } + } else if (n < dimen_base) { + /*tex Show equivalent |n| in region 5: */ + if (n < dir_base) { + print_cmd_chr(assign_int_cmd, n); + print_char('='); + print_int(eqtb[n].cint); + } else if (n < count_base) { + print_cmd_chr(assign_dir_cmd, n); + print_char(' '); + print_dir_par(eqtb[n].cint); + } else if (n < attribute_base) { + tprint_esc("count"); + print_int(n - count_base); + print_char('='); + print_int(eqtb[n].cint); + } else if (n < del_code_base) { + tprint_esc("attribute"); + print_int(n - attribute_base); + print_char('='); + print_int(eqtb[n].cint); + } + } else if (n <= eqtb_size) { + /*tex Show equivalent |n| in region 6: */ + if (n < scaled_base) { + print_cmd_chr(assign_dimen_cmd, n); + } else { + tprint_esc("dimen"); + print_int(n - scaled_base); + } + print_char('='); + print_scaled(eqtb[n].cint); + tprint("pt"); + } else { + /*tex This can't happen unless you messed up at the \LUA\ end. */ + tprint("? bad token, case 2: "); + print_int(n); + } +} + +void show_eqtb_meaning(halfword n) +{ + if (n < null_cs) { + /*tex This can't happen. */ + print_char('?'); + } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) { + /*tex + Here is a routine that displays the current meaning of an |eqtb| + entry in region 1 or~2. Similar routines for the other regions will + appear below. + */ + sprint_cs(n); + } else if (n < local_base) { + /* + Show equivalent |n| in region 3. All glue parameters and registers + are initially `\.{0pt plus0pt minus0pt}'. + */ + if (n < skip_base) { + if (n < glue_base + thin_mu_skip_code) + print_cmd_chr(assign_glue_cmd, n); + else + print_cmd_chr(assign_mu_glue_cmd, n); + } else if (n < mu_skip_base) { + tprint_esc("skip"); + print_int(n - skip_base); + } else { + tprint_esc("muskip"); + print_int(n - mu_skip_base); + } + } else if (n < int_base) { + /*tex + Show equivalent |n| in region 4. We initialize most things to null or + undefined values. An undefined font is represented by the internal + code |font_base|. However, the character code tables are given + initial values based on the conventional interpretation of ASCII + code. These initial values should not be changed when \TeX\ is + adapted for use with non-English languages; all changes to the + initialization conventions should be made in format packages, not in + \TeX\ itself, so that global interchange of formats is possible. + */ + if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) { + if (n == par_shape_loc) + print_cmd_chr(set_tex_shape_cmd, n); + else + print_cmd_chr(set_etex_shape_cmd, n); + } else if (n < toks_base) { + print_cmd_chr(assign_toks_cmd, n); + } else if (n < box_base) { + tprint_esc("toks"); + print_int(n - toks_base); + } else if (n < cur_font_loc) { + tprint_esc("box"); + print_int(n - box_base); + } else if (n == cur_font_loc) { + /*tex Show the font identifier in |eqtb[n]|. */ + tprint("current font"); + } + } else if (n < dimen_base) { + /*tex Show equivalent |n| in region 5. */ + if (n < dir_base) { + print_cmd_chr(assign_int_cmd, n); + } else if (n < count_base) { + print_cmd_chr(assign_dir_cmd, n); + } else if (n < attribute_base) { + tprint_esc("count"); + print_int(n - count_base); + } else if (n < del_code_base) { + tprint_esc("attribute"); + print_int(n - attribute_base); + } + } else if (n <= eqtb_size) { + /*tex Show equivalent |n| in region 6. */ + if (n < scaled_base) { + print_cmd_chr(assign_dimen_cmd, n); + } else { + tprint_esc("dimen"); + print_int(n - scaled_base); + } + } else { + /*tex This can't happen either. */ + print_char('?'); + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.h b/Build/source/texk/web2c/luatexdir/tex/equivalents.h new file mode 100644 index 00000000000..83117c64fa1 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.h @@ -0,0 +1,916 @@ +/* equivalents.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef EQUIVALENTS_H +# define EQUIVALENTS_H + +/* + +Like the preceding parameters, the following quantities can be changed at compile +time to extend or reduce \TeX's capacity. But if they are changed, it is +necessary to rerun the initialization program \.{INITEX} @.INITEX@> to generate +new tables for the production \TeX\ program. One can't simply make helter-skelter +changes to the following constants, since certain rather complex initialization +numbers are computed from them. They are defined here using \.{WEB} macros, +instead of being put into \PASCAL's |const| list, in order to emphasize this +distinction. + +*/ + +# define font_base 0 /* smallest internal font number; must not be less than |min_quarterword| */ +# define biggest_reg 65535 /* the largest allowed register number; must be |< max_quarterword| */ +# define number_regs 65536 /* |biggest_reg+1| */ +# define number_attrs 65536 /* total numbeer of attributes */ +# define biggest_char 1114111 /* 0x10FFFF, the largest allowed character number; must be |< max_halfword| */ +# define too_big_char (biggest_char+1) /* 1114112, |biggest_char+1| */ +# define special_char (biggest_char+2) /* 1114113, |biggest_char+2| */ +# define number_chars (biggest_char+3) /* 1114112, |biggest_char+1| */ +# define number_fonts (5535-font_base+1) +# define biggest_lang 32767 +# define too_big_lang 32768 +# define text_size 0 /* size code for the largest size in a family */ +# define script_size 1 /* size code for the medium size in a family */ +# define script_script_size 2 /* size code for the smallest size in a family */ + +/* + +Each entry in |eqtb| is a |memory_word|. Most of these words are of type +|two_halves|, and subdivided into three fields: + +\yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at which +this equivalent was defined. If the level is |level_zero|, the equivalent has +never been defined; |level_one| refers to the outer level (outside of all +groups), and this level is also used for global definitions that never go away. +Higher levels are for equivalents that will disappear at the end of their group. +@^global definitions@> + +\yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of entry +this is. There are many types, since each \TeX\ primitive like \.{\\hbox}, +\.{\\def}, etc., has its own special code. The list of command codes above +includes all possible settings of the |eq_type| field. + +\yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value. This +may be a font number, a pointer into |mem|, or a variety of other things. + +Many locations in |eqtb| have symbolic names. The purpose of the next paragraphs +is to define these names, and to set up the initial values of the equivalents. + +In the first region we have a single entry for the `null csname' of length zero. +In luatex, the active characters and and single-letter control sequence names are +part of the next region. + +Then comes region~2, which corresponds to the hash table that we will define +later. The maximum address in this region is used for a dummy control sequence +that is perpetually undefined. There also are several locations for control +sequences that are perpetually defined (since they are used in error recovery). + +@ Region 3 of |eqtb| contains the |number_regs| \.{\\skip} registers, as well as +the glue parameters defined here. It is important that the ``muskip'' parameters +have larger numbers than the others. + +@ Region 4 of |eqtb| contains the local quantities defined here. The bulk of this +region is taken up by five tables that are indexed by eight-bit characters; these +tables are important to both the syntactic and semantic portions of \TeX. There +are also a bunch of special things like font and token parameters, as well as the +tables of \.{\\toks} and \.{\\box} registers. + +@ Region 5 of |eqtb| contains the integer parameters and registers defined here, +as well as the |del_code| table. The latter table differs from the +|cat_code..math_code| tables that precede it, since delimiter codes are fullword +integers while the other kinds of codes occupy at most a halfword. This is what +makes region~5 different from region~4. We will store the |eq_level| information +in an auxiliary array of quarterwords that will be defined later. + +@ The integer parameters should really be initialized by a macro package; the +following initialization does the minimum to keep \TeX\ from complete failure. + +@^null delimiter@> + +@ The final region of |eqtb| contains the dimension parameters defined here, and +the |number_regs| \.{\\dimen} registers. + +*/ + +# define null_cs 1 /* equivalent of \.{\\csname\\endcsname} */ +# define hash_base (null_cs+1) /* beginning of region 2, for the hash table */ +# define frozen_control_sequence (hash_base+hash_size) /* for error recovery */ +# define frozen_protection (frozen_control_sequence) /* inaccessible but definable */ +# define frozen_cr (frozen_control_sequence+1) /* permanent `\.{\\cr}' */ +# define frozen_end_group (frozen_control_sequence+2) /* permanent `\.{\\endgroup}' */ +# define frozen_right (frozen_control_sequence+3) /* permanent `\.{\\right}' */ +# define frozen_fi (frozen_control_sequence+4) /* permanent `\.{\\fi}' */ +# define frozen_end_template (frozen_control_sequence+5) /* permanent `\.{\\endtemplate}' */ +# define frozen_endv (frozen_control_sequence+6) /* second permanent `\.{\\endtemplate}' */ +# define frozen_relax (frozen_control_sequence+7) /* permanent `\.{\\relax}' */ +# define end_write (frozen_control_sequence+8) /* permanent `\.{\\endwrite}' */ +# define frozen_dont_expand (frozen_control_sequence+9 ) /* permanent `\.{\\notexpanded:}' */ +# define frozen_primitive (frozen_control_sequence+11 ) /* permanent `\.{\\primitive}' */ +# define frozen_special (frozen_control_sequence+12 ) /* permanent `\.{\\special}' */ +# define frozen_null_font (frozen_control_sequence+13 ) /* permanent `\.{\\nullfont}' */ +# define font_id_base (frozen_null_font-font_base ) /* begins table of |number_fonts| permanent font identifiers */ +# define undefined_control_sequence (frozen_null_font+number_fonts) +# define glue_base (undefined_control_sequence+1) /* beginning of region 3 */ + +# define line_skip_code 0 /* interline glue if |baseline_skip| is infeasible */ +# define baseline_skip_code 1 /* desired glue between baselines */ +# define par_skip_code 2 /* extra glue just above a paragraph */ +# define above_display_skip_code 3 /* extra glue just above displayed math */ +# define below_display_skip_code 4 /* extra glue just below displayed math */ +# define above_display_short_skip_code 5 /* glue above displayed math following short lines */ +# define below_display_short_skip_code 6 /* glue below displayed math following short lines */ +# define left_skip_code 7 /* glue at left of justified lines */ +# define right_skip_code 8 /* glue at right of justified lines */ +# define top_skip_code 9 /* glue at top of main pages */ +# define split_top_skip_code 10 /* glue at top of split pages */ +# define tab_skip_code 11 /* glue between aligned entries */ +# define space_skip_code 12 /* glue between words (if not |zero_glue|) */ +# define xspace_skip_code 13 /* glue after sentences (if not |zero_glue|) */ +# define par_fill_skip_code 14 /* glue on last line of paragraph */ +# define math_skip_code 15 +# define thin_mu_skip_code 16 /* thin space in math formula */ +# define med_mu_skip_code 17 /* medium space in math formula */ +# define thick_mu_skip_code 18 /* thick space in math formula */ +# define glue_pars 19 /* total number of glue parameters */ + +# define skip_base (glue_base+glue_pars) /* table of |number_regs| ``skip'' registers */ +# define mu_skip_base (skip_base+number_regs) /* table of |number_regs| ``muskip'' registers */ +# define local_base (mu_skip_base+number_regs) /* beginning of region 4 */ + +# define par_shape_loc (local_base) /* specifies paragraph shape */ +# define output_routine_loc (local_base+1) /* points to token list for \.{\\output} */ +# define every_par_loc (local_base+2) /* points to token list for \.{\\everypar} */ +# define every_math_loc (local_base+3) /* points to token list for \.{\\everymath} */ +# define every_display_loc (local_base+4) /* points to token list for \.{\\everydisplay} */ +# define every_hbox_loc (local_base+5) /* points to token list for \.{\\everyhbox} */ +# define every_vbox_loc (local_base+6) /* points to token list for \.{\\everyvbox} */ +# define every_job_loc (local_base+7) /* points to token list for \.{\\everyjob} */ +# define every_cr_loc (local_base+8) /* points to token list for \.{\\everycr} */ +# define err_help_loc (local_base+9) /* points to token list for \.{\\errhelp} */ +# define every_eof_loc (local_base+10) /* points to token list for \.{\\everyeof} */ + +# define backend_toks_base (local_base+11) +# define backend_toks_last (local_base+20) + +# define toks_base (local_base+21) /* table of |number_regs| token list registers */ + +# define etex_pen_base (toks_base+number_regs) /* start of table of \eTeX's penalties */ +# define inter_line_penalties_loc (etex_pen_base) /* additional penalties between lines */ +# define club_penalties_loc (etex_pen_base+1) /* penalties for creating club lines */ +# define widow_penalties_loc (etex_pen_base+2) /* penalties for creating widow lines */ +# define display_widow_penalties_loc (etex_pen_base+3) /* ditto, just before a display */ +# define etex_pens (etex_pen_base+4) /* end of table of \eTeX's penalties */ +# define local_left_box_base (etex_pens) +# define local_right_box_base (local_left_box_base+1) +# define box_base (local_right_box_base+1) /* table of |number_regs| box registers */ +# define cur_font_loc (box_base+number_regs) /* internal font number outside math mode */ +# define internal_math_param_base (cur_font_loc+1 ) /* current math parameter data index */ +# define cat_code_base (internal_math_param_base+1) /* current category code data index */ +# define lc_code_base (cat_code_base+1) /* table of |number_chars| lowercase mappings */ +# define uc_code_base (lc_code_base+1) /* table of |number_chars| uppercase mappings */ +# define sf_code_base (uc_code_base+1) /* table of |number_chars| spacefactor mappings */ +# define math_code_base (sf_code_base+1) /* table of |number_chars| math mode mappings */ +# define int_base (math_code_base+1) /* beginning of region 5 */ + +# define pretolerance_code 0 /* badness tolerance before hyphenation */ +# define tolerance_code 1 /* badness tolerance after hyphenation */ +# define line_penalty_code 2 /* added to the badness of every line */ +# define hyphen_penalty_code 3 /* penalty for break after discretionary hyphen */ +# define ex_hyphen_penalty_code 4 /* penalty for break after explicit hyphen */ +# define club_penalty_code 5 /* penalty for creating a club line */ +# define widow_penalty_code 6 /* penalty for creating a widow line */ +# define display_widow_penalty_code 7 /* ditto, just before a display */ +# define broken_penalty_code 8 /* penalty for breaking a page at a broken line */ +# define bin_op_penalty_code 9 /* penalty for breaking after a binary operation */ +# define rel_penalty_code 10 /* penalty for breaking after a relation */ +# define pre_display_penalty_code 11 /* penalty for breaking just before a displayed formula */ +# define post_display_penalty_code 12 /* penalty for breaking just after a displayed formula */ +# define inter_line_penalty_code 13 /* additional penalty between lines */ +# define double_hyphen_demerits_code 14 /* demerits for double hyphen break */ +# define final_hyphen_demerits_code 15 /* demerits for final hyphen break */ +# define adj_demerits_code 16 /* demerits for adjacent incompatible lines */ +# define mag_code 17 /* magnification ratio */ +# define delimiter_factor_code 18 /* ratio for variable-size delimiters */ +# define looseness_code 19 /* change in number of lines for a paragraph */ +# define time_code 20 /* current time of day */ +# define day_code 21 /* current day of the month */ +# define month_code 22 /* current month of the year */ +# define year_code 23 /* current year of our Lord */ +# define show_box_breadth_code 24 /* nodes per level in |show_box| */ +# define show_box_depth_code 25 /* maximum level in |show_box| */ +# define hbadness_code 26 /* hboxes exceeding this badness will be shown by |hpack| */ +# define vbadness_code 27 /* vboxes exceeding this badness will be shown by |vpack| */ +# define pausing_code 28 /* pause after each line is read from a file */ +# define tracing_online_code 29 /* show diagnostic output on terminal */ +# define tracing_macros_code 30 /* show macros as they are being expanded */ +# define tracing_stats_code 31 /* show memory usage if \TeX\ knows it */ +# define tracing_paragraphs_code 32 /* show line-break calculations */ +# define tracing_pages_code 33 /* show page-break calculations */ +# define tracing_output_code 34 /* show boxes when they are shipped out */ +# define tracing_lost_chars_code 35 /* show characters that aren't in the font */ +# define tracing_commands_code 36 /* show command codes at |big_switch| */ +# define tracing_restores_code 37 /* show equivalents when they are restored */ +# define uc_hyph_code 38 /* hyphenate words beginning with a capital letter */ +# define output_penalty_code 39 /* penalty found at current page break */ +# define max_dead_cycles_code 40 /* bound on consecutive dead cycles of output */ +# define hang_after_code 41 /* hanging indentation changes after this many lines */ +# define floating_penalty_code 42 /* penalty for insertions heldover after a split */ +# define global_defs_code 43 /* override \.{\\global} specifications */ +# define cur_fam_code 44 /* current family */ +# define escape_char_code 45 /* escape character for token output */ +# define default_hyphen_char_code 46 /* value of \.{\\hyphenchar} when a font is loaded */ +# define default_skew_char_code 47 /* value of \.{\\skewchar} when a font is loaded */ +# define end_line_char_code 48 /* character placed at the right end of the buffer */ +# define new_line_char_code 49 /* character that prints as |print_ln| */ +# define language_code 50 /* current hyphenation table */ +# define left_hyphen_min_code 51 /* minimum left hyphenation fragment size */ +# define right_hyphen_min_code 52 /* minimum right hyphenation fragment size */ +# define holding_inserts_code 53 /* do not remove insertion nodes from \.{\\box255} */ +# define error_context_lines_code 54 /* maximum intermediate line pairs shown */ +# define local_inter_line_penalty_code 55 /* local \.{\\interlinepenalty} */ +# define local_broken_penalty_code 56 /* local \.{\\brokenpenalty} */ +# define no_local_whatsits_code 57 /* counts local whatsits */ +# define no_local_dirs_code 58 + +# define disable_lig_code 60 +# define disable_kern_code 61 +# define disable_space_code 62 +# define cat_code_table_code 63 +# define output_box_code 64 +# define cur_lang_code 65 /* current language id */ +# define ex_hyphen_char_code 66 +# define hyphenation_min_code 67 /* minimum word length */ +# define adjust_spacing_code 68 /* level of spacing adjusting */ +# define protrude_chars_code 69 /* protrude chars at left/right edge of paragraphs */ +# define output_mode_code 70 /* switch on PDF output if positive */ +# define draft_mode_code 71 +# define tracing_fonts_code 72 +# define tracing_assigns_code 73 /* show assignments */ +# define tracing_groups_code 74 /* show save/restore groups */ +# define tracing_ifs_code 75 /* show conditionals */ +# define tracing_scan_tokens_code 76 /* show pseudo file open and close */ +# define tracing_nesting_code 77 /* show incomplete groups and ifs within files */ +# define pre_display_direction_code 78 /* text direction preceding a display */ +# define last_line_fit_code 79 /* adjustment for last line of paragraph */ +# define saving_vdiscards_code 80 /* save items discarded from vlists */ +# define saving_hyph_codes_code 81 /* save hyphenation codes for languages */ +# define suppress_fontnotfound_error_code 82 /* suppress errors for missing fonts */ +# define suppress_long_error_code 83 /* suppress errors for missing fonts */ +# define suppress_ifcsname_error_code 84 /* suppress errors for failed \.{\\ifcsname} */ +# define suppress_outer_error_code 85 /* suppress errors for \.{\\outer} */ +# define suppress_mathpar_error_code 86 /* suppress errors for \.{\\par}} in math */ +# define math_eqno_gap_step_code 87 /* factor/1000 used for distance between eq and eqno */ +# define math_display_skip_mode_code 88 +# define math_scripts_mode_code 89 +# define math_nolimits_mode_code 90 +# define math_rules_mode_code 91 +# define math_rules_fam_code 92 +# define math_italics_mode_code 93 +# define synctex_code 94 /* is synctex file generation enabled ? */ +# define shape_mode_code 95 +# define first_valid_language_code 96 +# define hyphenation_bounds_code 97 +# define math_skip_mode_code 98 +# define math_pre_display_gap_factor_code 99 +# define hyphen_penalty_mode_code 100 +# define automatic_hyphen_penalty_code 101 +# define explicit_hyphen_penalty_code 102 +# define automatic_hyphen_mode_code 103 +# define compound_hyphen_mode_code 104 +# define break_after_dir_mode_code 105 +# define exception_penalty_code 106 + +# define pre_bin_op_penalty_code 107 +# define pre_rel_penalty_code 108 +# define math_penalties_mode_code 109 +# define math_delimiters_mode_code 110 +# define math_script_box_mode_code 111 +# define math_script_char_mode_code 112 +# define math_rule_thickness_mode_code 113 +# define math_flatten_mode_code 114 + +# define copy_lua_input_nodes_code 115 +# define suppress_primitive_error_code 116 +# define fixup_boxes_code 117 +# define glyph_dimensions_code 118 + +# define math_option_code 119 + +# define mathoption_int_base_code (math_option_code+1) /* one reserve */ +# define mathoption_int_last_code (mathoption_int_base_code+8) + +# define backend_int_base_code (mathoption_int_last_code+1) +# define backend_int_last_code (backend_int_base_code+32) /* we need some 25 but take some slack */ + +# define tex_int_pars (backend_int_last_code+1) /* total number of integer parameters */ + +# define mathoption_int_base (int_base+mathoption_int_base_code) +# define mathoption_int_last (int_base+mathoption_int_last_code) + +# define backend_int_base (int_base+backend_int_base_code) +# define backend_int_last (int_base+backend_int_last_code) + +# define page_direction_code (tex_int_pars) +# define body_direction_code (tex_int_pars+1) +# define par_direction_code (tex_int_pars+2) +# define text_direction_code (tex_int_pars+3) +# define math_direction_code (tex_int_pars+4) +# define line_direction_code (tex_int_pars+5) /* gets remapped so is no real register */ + +# define int_pars (tex_int_pars+5) /* total number of integer parameters */ + +# define dir_base (int_base+tex_int_pars) +# define count_base (int_base+int_pars) /* |number_regs| user \.{\\count} registers */ +# define attribute_base (count_base+number_regs) /* |number_attrs| user \.{\\attribute} registers */ +# define del_code_base (attribute_base+number_attrs) /* |number_chars| delimiter code mappings */ +# define dimen_base (del_code_base+1) /* beginning of region 6 */ + +# define par_indent_code 0 /* indentation of paragraphs */ +# define math_surround_code 1 /* space around math in text */ +# define line_skip_limit_code 2 /* threshold for |line_skip| instead of |baseline_skip| */ +# define hsize_code 3 /* line width in horizontal mode */ +# define vsize_code 4 /* page height in vertical mode */ +# define max_depth_code 5 /* maximum depth of boxes on main pages */ +# define split_max_depth_code 6 /* maximum depth of boxes on split pages */ +# define box_max_depth_code 7 /* maximum depth of explicit vboxes */ +# define hfuzz_code 8 /* tolerance for overfull hbox messages */ +# define vfuzz_code 9 /* tolerance for overfull vbox messages */ +# define delimiter_shortfall_code 10 /* maximum amount uncovered by variable delimiters */ +# define null_delimiter_space_code 11 /* blank space in null delimiters */ +# define script_space_code 12 /* extra space after subscript or superscript */ +# define pre_display_size_code 13 /* length of text preceding a display */ +# define display_width_code 14 /* length of line for displayed equation */ +# define display_indent_code 15 /* indentation of line for displayed equation */ +# define overfull_rule_code 16 /* width of rule that identifies overfull hboxes */ +# define hang_indent_code 17 /* amount of hanging indentation */ +# define h_offset_code 18 /* amount of horizontal offset when shipping pages out */ +# define v_offset_code 19 /* amount of vertical offset when shipping pages out */ +# define emergency_stretch_code 20 /* reduces badnesses on final pass of line-breaking */ +# define page_left_offset_code 21 +# define page_top_offset_code 22 +# define page_right_offset_code 23 +# define page_bottom_offset_code 24 +# define px_dimen_code 25 +# define page_width_code 26 /* page width of the output */ +# define page_height_code 27 /* page height of the output */ + +# define backend_dimen_base (dimen_base+28) +# define backend_dimen_last (dimen_base+37) + +# define dimen_pars (38) /* total number of dimension parameters */ + +# define scaled_base (dimen_base+dimen_pars) /* table of |number_regs| user-defined \.{\\dimen} registers */ +# define eqtb_size (scaled_base+biggest_reg) /* largest subscript of |eqtb| */ + +extern memory_word *eqtb; + +extern halfword eqtb_top; /* maximum of the |eqtb| */ + +extern quarterword xeq_level[(eqtb_size + 1)]; +extern void initialize_equivalents(void); + +# define eq_level_field(A) (A).hh.b1 +# define eq_type_field(A) (A).hh.b0 +# define equiv_field(A) (A).hh.rh + +# define eq_level(A) eq_level_field(eqtb[(A)]) /* level of definition */ +# define eq_type(A) eq_type_field(eqtb[(A)]) /* command code for equivalent */ +# define equiv(A) equiv_field(eqtb[(A)]) /* equivalent value */ + +# define set_eq_level(A,B) eq_level((A)) = (B) +# define set_eq_type(A,B) eq_type((A)) = (B) +# define set_equiv(A,B) equiv((A)) = (B) + +typedef struct save_record_ { + quarterword type_; + quarterword level_; + memory_word word_; +} save_record; + +extern save_record *save_stack; +extern int save_ptr; /* first unused entry on |save_stack| */ +extern int max_save_stack; /* maximum usage of save stack */ +extern quarterword cur_level; /* current nesting level for groups */ +extern group_code cur_group; /* current group type */ +extern int cur_boundary; /* where the current level begins */ + +# define save_type(A) save_stack[(A)].type_ /* classifies a |save_stack| entry */ +# define save_level(A) save_stack[(A)].level_ /* saved level for regions 5 and 6, or group code */ +# define save_value(A) save_stack[(A)].word_.cint /* |eqtb| location or token or |save_stack| location */ +# define save_word(A) save_stack[(A)].word_ /* |eqtb| entry */ + +/* + +We use the notation |saved(k)| to stand for an item that appears in location +|save_ptr+k| of the save stack. + +*/ + +# define saved_type(A) save_stack[save_ptr+(A)].type_ +# define saved_level(A) save_stack[save_ptr+(A)].level_ +# define saved_value(A) save_stack[save_ptr+(A)].word_.cint + +# define set_saved_record(A,B,C,D) do { \ + saved_type(A) = (quarterword)(B); \ + saved_level(A) = (quarterword)(C); \ + saved_value(A) = (D); \ + } while (0) + +# define restore_old_value 0 /* |save_type| when a value should be restored later */ +# define restore_zero 1 /* |save_type| when an undefined entry should be restored */ +# define insert_token 2 /* |save_type| when a token is being saved for later use */ +# define level_boundary 3 /* |save_type| corresponding to beginning of group */ +# define saved_line 4 +# define saved_adjust 5 +# define saved_insert 6 +# define saved_disc 7 +# define saved_boxtype 8 +# define saved_textdir 9 +# define saved_eqno 10 +# define saved_choices 11 +# define saved_math 12 +# define saved_boxcontext 13 +# define saved_boxspec 14 +# define saved_boxdir 15 +# define saved_boxattr 16 +# define saved_boxpack 17 +# define saved_attrlist 18 +# define saved_eqtb 19 + +extern void print_save_stack(void); + +# define int_par(A) eqtb[int_base+(A)].cint +# define dimen_par(A) eqtb[dimen_base+(A)].cint +# define loc_par(A) equiv(local_base+(A)) +# define glue_par(A) equiv(glue_base+(A)) + +typedef enum { + c_mathoption_old_code = 0, /* this one is stable */ + /* + c_mathoption_umathcode_meaning_code, + */ +} math_option_codes ; + +# define mathoption_int_par(A) eqtb[mathoption_int_base+(A)].cint + +/* if nonzero, this magnification should be used henceforth */ + +extern int mag_set; +extern void prepare_mag(void); + +/* + +@ Here are the group codes that are used to discriminate between different kinds +of groups. They allow \TeX\ to decide what special actions, if any, should be +performed when a group ends. \def\grp{\.{\char'173...\char'175}} + +Some groups are not supposed to be ended by right braces. For example, the `\.\$' +that begins a math formula causes a |math_shift_group| to be started, and this +should be terminated by a matching `\.\$'. Similarly, a group that starts with +\.{\\left} should end with \.{\\right}, and one that starts with \.{\\begingroup} +should end with \.{\\endgroup}. + +*/ + +typedef enum { + bottom_level = 0, /* group code for the outside world */ + simple_group, /* group code for local structure only */ + hbox_group, /* code for `\.{\\hbox}\grp' */ + adjusted_hbox_group, /* code for `\.{\\hbox}\grp' in vertical mode */ + vbox_group, /* code for `\.{\\vbox}\grp' */ + vtop_group, /* code for `\.{\\vtop}\grp' */ + align_group, /* code for `\.{\\halign}\grp', `\.{\\valign}\grp' */ + no_align_group, /* code for `\.{\\noalign}\grp' */ + output_group, /* code for output routine */ + math_group, /* code for, e.g., `\.{\char'136}\grp' */ + disc_group, /* code for `\.{\\discretionary}\grp\grp\grp' */ + insert_group, /* code for `\.{\\insert}\grp', `\.{\\vadjust}\grp' */ + vcenter_group, /* code for `\.{\\vcenter}\grp' */ + math_choice_group, /* code for `\.{\\mathchoice}\grp\grp\grp\grp' */ + semi_simple_group, /* code for `\.{\\begingroup...\\endgroup}' */ + math_shift_group, /* code for `\.{\$...\$}' */ + math_left_group, /* code for `\.{\\left...\\right}' */ + local_box_group, /* code for `\.{\\localleftbox...\\localrightbox}' */ + split_off_group, /* box code for the top part of a \.{\\vsplit} */ + split_keep_group, /* box code for the bottom part of a \.{\\vsplit} */ + preamble_group, /* box code for the preamble processing in an alignment */ + align_set_group, /* box code for the final item pass in an alignment */ + fin_row_group /* box code for a provisory line in an alignment */ +} tex_group_codes; + +typedef enum { + new_graf_par_code = 0, + local_box_par_code, + hmode_par_par_code, + penalty_par_code, + math_par_code, +} tex_par_codes ; + +typedef enum { + display_style = 0, /* |subtype| for \.{\\displaystyle} */ + cramped_display_style, /* |subtype| for \.{\\crampeddisplaystyle} */ + text_style, /* |subtype| for \.{\\textstyle} */ + cramped_text_style, /* |subtype| for \.{\\crampedtextstyle} */ + script_style, /* |subtype| for \.{\\scriptstyle} */ + cramped_script_style, /* |subtype| for \.{\\crampedscriptstyle} */ + script_script_style, /* |subtype| for \.{\\scriptscriptstyle} */ + cramped_script_script_style, /* |subtype| for \.{\\crampedscriptscriptstyle} */ +} math_style_subtypes; + +typedef enum { /* this could move to directions.h */ + dir_TLT = 0, + dir_TRT, + dir_LTL, + dir_RTT, +} dir_codes; + +# define max_group_code local_box_group /* which is wrong, but is what the web says */ + +extern int cur_cmd; /* current command set by |get_next| */ +extern halfword cur_chr; /* operand of current command */ +extern halfword cur_cs; /* control sequence found here, zero if none found */ +extern halfword cur_tok; /* packed representative of |cur_cmd| and |cur_chr| */ + +extern void show_cur_cmd_chr(void); + +extern void new_save_level(group_code c); /* begin a new level of grouping */ +extern void eq_destroy(memory_word w); /* gets ready to forget |w| */ +extern void eq_save(halfword p, quarterword l); /* saves |eqtb[p]| */ +extern void eq_define(halfword p, quarterword t, halfword e); /* new data for |eqtb| */ +extern void eq_word_define(halfword p, int w); +extern void geq_define(halfword p, quarterword t, halfword e); /* global |eq_define| */ +extern void geq_word_define(halfword p, int w); /* global |eq_word_define| */ +extern void save_for_after(halfword t); +extern void unsave(void); /* pops the top level off the save stack */ +extern void show_save_groups(void); + +# define level_zero 0 /* level for undefined quantities */ +# define level_one 1 /* outermost level for defined quantities */ + +extern void show_eqtb(halfword n); + +/* \csname \ifcsname */ + +extern halfword last_cs_name; + +/* + The *_par macros expand to the variables that are (in most cases) also accessible + at the users end. Most are registers but some are in the (stack) lists. + + More *_par will move here : there is no real need for thse macros but because there + were already a bunch and because they were defined all over the place we moved them + here. + +*/ + +#define space_skip_par glue_par(space_skip_code) +#define xspace_skip_par glue_par(xspace_skip_code) +#define math_skip_par glue_par(math_skip_code) +#define math_skip_mode int_par(math_skip_mode_code) + +#define pre_display_size_par dimen_par(pre_display_size_code) +#define display_width_par dimen_par(display_width_code) +#define display_indent_par dimen_par(display_indent_code) +#define math_surround_par dimen_par(math_surround_code) + +#define display_skip_mode_par int_par(math_display_skip_mode_code) +#define math_eqno_gap_step_par int_par(math_eqno_gap_step_code) + +#define body_direction_par int_par(body_direction_code) +#define page_direction_par int_par(page_direction_code) +#define par_direction_par int_par(par_direction_code) +#define text_direction_par int_par(text_direction_code) +#define math_direction_par int_par(math_direction_code) + +#define shape_mode_par int_par(shape_mode_code) +#define hyphenation_bounds_par int_par(hyphenation_bounds_code) +#define first_valid_language_par int_par(first_valid_language_code) + +#define hsize_par dimen_par(hsize_code) +#define vsize_par dimen_par(vsize_code) +#define hfuzz_par dimen_par(hfuzz_code) +#define vfuzz_par dimen_par(vfuzz_code) +#define hbadness_par int_par(hbadness_code) +#define vbadness_par int_par(vbadness_code) + +#define baseline_skip_par glue_par(baseline_skip_code) +#define par_indent_par dimen_par(par_indent_code) +#define hang_indent_par dimen_par(hang_indent_code) +#define hang_after_par int_par(hang_after_code) +#define left_skip_par glue_par(left_skip_code) +#define right_skip_par glue_par(right_skip_code) + +#define emergency_stretch_par dimen_par(emergency_stretch_code) +#define pretolerance_par int_par(pretolerance_code) +#define tolerance_par int_par(tolerance_code) +#define looseness_par int_par(looseness_code) +#define adjust_spacing_par int_par(adjust_spacing_code) +#define adj_demerits_par int_par(adj_demerits_code) +#define protrude_chars_par int_par(protrude_chars_code) +#define line_penalty_par int_par(line_penalty_code) +#define last_line_fit_par int_par(last_line_fit_code) +#define double_hyphen_demerits_par int_par(double_hyphen_demerits_code) +#define final_hyphen_demerits_par int_par(final_hyphen_demerits_code) +#define inter_line_penalty_par int_par(inter_line_penalty_code) +#define club_penalty_par int_par(club_penalty_code) +#define broken_penalty_par int_par(broken_penalty_code) +#define display_widow_penalty_par int_par(display_widow_penalty_code) +#define widow_penalty_par int_par(widow_penalty_code) +#define line_skip_limit_par dimen_par(line_skip_limit_code) + +#define delimiter_shortfall_par dimen_par(delimiter_shortfall_code) +#define null_delimiter_space_par dimen_par(null_delimiter_space_code) +#define script_space_par dimen_par(script_space_code) +#define max_depth_par dimen_par(max_depth_code) +#define box_max_depth_par dimen_par(box_max_depth_code) +#define split_max_depth_par dimen_par(split_max_depth_code) +#define overfull_rule_par dimen_par(overfull_rule_code) +#define box_max_depth_par dimen_par(box_max_depth_code) +#define top_skip_par glue_par(top_skip_code) +#define split_top_skip_par glue_par(split_top_skip_code) + +#define cur_fam_par int_par(cur_fam_code) +#define pre_display_direction_par int_par(pre_display_direction_code) +#define pre_display_penalty_par int_par(pre_display_penalty_code) +#define post_display_penalty_par int_par(post_display_penalty_code) + +#define cur_fam_par_in_range ((cur_fam_par>=0)&&(cur_fam_par<256)) + +#define local_inter_line_penalty_par int_par(local_inter_line_penalty_code) +#define local_broken_penalty_par int_par(local_broken_penalty_code) +#define local_left_box_par equiv(local_left_box_base) +#define local_right_box_par equiv(local_right_box_base) + +#define end_line_char_par int_par(end_line_char_code) +#define new_line_char_par int_par(new_line_char_code) +#define escape_char_par int_par(escape_char_code) + +#define end_line_char_inactive ((end_line_char_par < 0) || (end_line_char_par > 127)) + +#define delimiter_factor_par int_par(delimiter_factor_code) +#define bin_op_penalty_par int_par(bin_op_penalty_code) +#define rel_penalty_par int_par(rel_penalty_code) +#define pre_bin_op_penalty_par int_par(pre_bin_op_penalty_code) +#define pre_rel_penalty_par int_par(pre_rel_penalty_code) +#define math_penalties_mode_par int_par(math_penalties_mode_code) +#define math_delimiters_mode_par int_par(math_delimiters_mode_code) +#define math_script_box_mode_par int_par(math_script_box_mode_code) +#define math_script_char_mode_par int_par(math_script_char_mode_code) +#define math_rule_thickness_mode_par int_par(math_rule_thickness_mode_code) +#define math_flatten_mode_par int_par(math_flatten_mode_code) +#define null_delimiter_space_par dimen_par(null_delimiter_space_code) +#define disable_lig_par int_par(disable_lig_code) +#define disable_kern_par int_par(disable_kern_code) +#define disable_space_par int_par(disable_space_code) +#define math_scripts_mode_par int_par(math_scripts_mode_code) +#define math_nolimits_mode_par int_par(math_nolimits_mode_code) +#define math_italics_mode_par int_par(math_italics_mode_code) +#define math_rules_mode_par int_par(math_rules_mode_code) +#define math_rules_fam_par int_par(math_rules_fam_code) + +#define thin_mu_skip_par glue_par(thin_mu_skip_code) +#define med_mu_skip_par glue_par(med_mu_skip_code) +#define thick_mu_skip_par glue_par(thick_mu_skip_code) + +#define every_math_par equiv(every_math_loc) +#define every_display_par equiv(every_display_loc) +#define every_cr_par equiv(every_cr_loc) +#define every_hbox_par equiv(every_hbox_loc) +#define every_vbox_par equiv(every_vbox_loc) +#define every_eof_par equiv(every_eof_loc) +#define every_par_par equiv(every_par_loc) +#define err_help_par equiv(err_help_loc) + +#define no_local_whatsits_par int_par(no_local_whatsits_code) +#define no_local_dirs_par int_par(no_local_dirs_code) + +#define prev_depth_par cur_list.prev_depth_field +#define prev_graf_par cur_list.pg_field +#define tail_par cur_list.tail_field +#define head_par cur_list.head_field +#define mode_par cur_list.mode_field +#define dirs_par cur_list.dirs_field +#define space_factor_par cur_list.space_factor_field +#define incompleat_noad_par cur_list.incompleat_noad_field +#define mode_line_par cur_list.ml_field +#define aux_par cur_list.eTeX_aux_field +#define delim_par aux_par + +#define par_shape_par_ptr equiv(par_shape_loc) +#define inter_line_penalties_par_ptr equiv(inter_line_penalties_loc) +#define club_penalties_par_ptr equiv(club_penalties_loc) +#define widow_penalties_par_ptr equiv(widow_penalties_loc) +#define display_widow_penalties_par_ptr equiv(display_widow_penalties_loc) + +#define page_width_par dimen_par(page_width_code) +#define page_height_par dimen_par(page_height_code) +#define h_offset_par dimen_par(h_offset_code) +#define v_offset_par dimen_par(v_offset_code) +#define page_left_offset_par dimen_par(page_left_offset_code) +#define page_top_offset_par dimen_par(page_top_offset_code) +#define page_right_offset_par dimen_par(page_right_offset_code) +#define page_bottom_offset_par dimen_par(page_bottom_offset_code) +#define px_dimen_par dimen_par(px_dimen_code) + +#define max_dead_cycles_par int_par(max_dead_cycles_code) +#define output_box_par int_par(output_box_code) +#define holding_inserts_par int_par(holding_inserts_code) +#define output_routine_par equiv(output_routine_loc) +#define floating_penalty_par int_par(floating_penalty_code) + +#define mag_par int_par(mag_code) + +#define global_defs_par int_par(global_defs_code) +#define cat_code_table_par int_par(cat_code_table_code) +#define saving_vdiscards_par int_par(saving_vdiscards_code) + +#define tracing_output_par int_par(tracing_output_code) +#define tracing_stats_par int_par(tracing_stats_code) +#define tracing_online_par int_par(tracing_online_code) +#define tracing_paragraphs_par int_par(tracing_paragraphs_code) +#define tracing_nesting_par int_par(tracing_nesting_code) +#define tracing_lost_chars_par int_par(tracing_lost_chars_code) +#define tracing_scan_tokens_par int_par(tracing_scan_tokens_code) +#define tracing_ifs_par int_par(tracing_ifs_code) +#define tracing_commands_par int_par(tracing_commands_code) +#define tracing_macros_par int_par(tracing_macros_code) +#define tracing_assigns_par int_par(tracing_assigns_code) +#define tracing_fonts_par int_par(tracing_fonts_code) +#define tracing_pages_par int_par(tracing_pages_code) +#define tracing_restores_par int_par(tracing_restores_code) +#define tracing_groups_par int_par(tracing_groups_code) + +#define show_box_depth_par int_par(show_box_depth_code) +#define show_box_breadth_par int_par(show_box_breadth_code) + +#define pausing_par int_par(pausing_code) + +#define suppress_outer_error_par int_par(suppress_outer_error_code) +#define suppress_long_error_par int_par(suppress_long_error_code) +#define suppress_mathpar_error_par int_par(suppress_mathpar_error_code) +#define suppress_fontnotfound_error_par int_par(suppress_fontnotfound_error_code) +#define suppress_ifcsname_error_par int_par(suppress_ifcsname_error_code) +#define suppress_primitive_error_par int_par(suppress_primitive_error_code) +#define error_context_lines_par int_par(error_context_lines_code) +#define copy_lua_input_nodes_par int_par(copy_lua_input_nodes_code) + +#define math_old_par mathoption_int_par(c_mathoption_old_code) + +/* +#define math_umathcode_meaning_par mathoption_int_par(c_mathoption_umathcode_meaning_code) +*/ + +#define math_pre_display_gap_factor_par int_par(math_pre_display_gap_factor_code) + +#define time_par int_par(time_code) +#define day_par int_par(day_code) +#define month_par int_par(month_code) +#define year_par int_par(year_code) + +#define output_mode_par int_par(output_mode_code) +#define draft_mode_par int_par(draft_mode_code) +#define synctex_par int_par(synctex_code) + +#define language_par int_par(language_code) +#define uc_hyph_par int_par(uc_hyph_code) +#define left_hyphen_min_par int_par(left_hyphen_min_code) +#define right_hyphen_min_par int_par(right_hyphen_min_code) +#define ex_hyphen_char_par int_par(ex_hyphen_char_code) +#define hyphen_penalty_par int_par(hyphen_penalty_code) +#define ex_hyphen_penalty_par int_par(ex_hyphen_penalty_code) +#define default_hyphen_char_par int_par(default_hyphen_char_code) +#define default_skew_char_par int_par(default_skew_char_code) +#define saving_hyph_codes_par int_par(saving_hyph_codes_code) + +#define hyphen_penalty_mode_par int_par(hyphen_penalty_mode_code) +#define automatic_hyphen_penalty_par int_par(automatic_hyphen_penalty_code) +#define explicit_hyphen_penalty_par int_par(explicit_hyphen_penalty_code) +#define automatic_hyphen_mode_par int_par(automatic_hyphen_mode_code) +#define compound_hyphen_mode_par int_par(compound_hyphen_mode_code) +#define break_after_dir_mode_par int_par(break_after_dir_mode_code) +#define exception_penalty_par int_par(exception_penalty_code) + +#define cur_lang_par int_par(cur_lang_code) +#define cur_font_par equiv(cur_font_loc) + +#define fixup_boxes_par int_par(fixup_boxes_code) +#define glyph_dimensions_par int_par(glyph_dimensions_code) + +/* */ + +#define math_use_current_family_code 7 + +/* + #define box(A) equiv(box_base+(A)) + #define box(A) eqtb[box_base+(A)].hh.rh +*/ + +#define attribute(A) equiv(attribute_base+(A)) +#define box(A) equiv(box_base+(A)) +#define count(A) equiv(count_base+(A)) +#define dimen(A) equiv(scaled_base+(A)) +#define mu_skip(A) equiv(mu_skip_base+(A)) +#define skip(A) equiv(skip_base+(A)) +#define toks(A) equiv(toks_base+(A)) + +#define get_tex_attribute_register(j) attribute(j) +#define get_tex_box_register(j) box(j) +#define get_tex_count_register(j) count(j) +#define get_tex_dimen_register(j) dimen(j) +#define get_tex_mu_skip_register(j) mu_skip(j) +#define get_tex_skip_register(j) skip(j) + +#define font_id_text(A) cs_text(font_id_base+(A)) + +#define end_template_token (cs_token_flag+frozen_end_template) +#define end_write_token (cs_token_flag+end_write) + +/* for now here */ + +#define xspace_skip_subtype (xspace_skip_code + 1) +#define space_skip_subtype (space_skip_code + 1) + +/* + +hyphen_penalty_mode_par automatic_disc (-) explicit_disc (\-) +--------------------------------------------------------------------------------- +0 (default) ex_hyphen_penalty_par ex_hyphen_penalty_par +1 hyphen_penalty_par hyphen_penalty_par +2 ex_hyphen_penalty_par hyphen_penalty_par +3 hyphen_penalty_par ex_hyphen_penalty_par +4 automatic_hyphen_penalty_par explicit_disc_penalty_par +5 ex_hyphen_penalty_par explicit_disc_penalty_par +6 hyphen_penalty_par explicit_disc_penalty_par +7 automatic_hyphen_penalty_par ex_hyphen_penalty_par +8 automatic_hyphen_penalty_par hyphen_penalty_par +*/ + +#define set_automatic_disc_penalty(n) \ + switch (hyphen_penalty_mode_par) { \ + case 0: \ + case 2: \ + case 5: \ + /* we take ex_hyphen_penalty */ \ + disc_penalty(n) = ex_hyphen_penalty_par; \ + break; \ + case 1: \ + case 3: \ + case 6: \ + /* we take hyphen_penalty */ \ + disc_penalty(n) = hyphen_penalty_par; \ + break; \ + case 4: \ + case 7: \ + case 8: \ + /* we take automatic_hyphen_penalty */ \ + disc_penalty(n) = automatic_hyphen_penalty_par; \ + break; \ + default: \ + /* what we've done since the beginning */ \ + disc_penalty(n) = ex_hyphen_penalty_par; \ + break; \ + } + +#define set_explicit_disc_penalty(n) \ + switch (hyphen_penalty_mode_par) { \ + case 0: \ + case 3: \ + case 7: \ + /* we take ex_hyphen_penalty */ \ + disc_penalty(n) = ex_hyphen_penalty_par; \ + break; \ + case 1: \ + case 2: \ + case 8: \ + /* we take hyphen_penalty */ \ + disc_penalty(n) = hyphen_penalty_par; \ + break; \ + case 4: \ + case 5: \ + case 6: \ + /* we take automatic_hyphen_penalty */ \ + disc_penalty(n) = explicit_hyphen_penalty_par; \ + break; \ + default: \ + /* what we've done since the beginning */ \ + disc_penalty(n) = ex_hyphen_penalty_par; \ + break; \ + } + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.c b/Build/source/texk/web2c/luatexdir/tex/errors.c new file mode 100644 index 00000000000..ce80df99e7e --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/errors.c @@ -0,0 +1,1055 @@ +/* + +errors.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#define edit_var "TEXEDIT" + +/*tex + +When something anomalous is detected, \TeX\ typically does something like this: +$$\vbox{\halign{#\hfil\cr |print_err("Something anomalous has been +detected");|\cr |help3("This is the first line of my offer to help.")|\cr |("This +is the second line. I'm trying to")|\cr |("explain the best way for you to +proceed.");|\cr |error;|\cr}}$$ A two-line help message would be given using +|help2|, etc.; these informal helps should use simple vocabulary that complements +the words used in the official error message that was printed. (Outside the +U.S.A., the help messages should preferably be translated into the local +vernacular. Each line of help is at most 60 characters long, in the present +implementation, so that |max_print_line| will not be exceeded.) + +The |print_err| procedure supplies a `\.!' before the official message, and makes +sure that the terminal is awake if a stop is going to occur. The |error| +procedure supplies a `\..' after the official message, then it shows the location +of the error; and if |interaction=error_stop_mode|, it also enters into a dialog +with the user, during which time the help message may be printed. @^system +dependencies@> + +*/ + +/*tex The current level of interaction: */ + +int interaction; + +/*tex Set from the command line: */ + +int interactionoption; + +char *last_error = NULL; +char *last_lua_error = NULL; +char *last_warning_tag = NULL; +char *last_warning_str = NULL; +char *last_error_context = NULL; + +int err_old_setting = 0 ; +int in_error = 0 ; + +void set_last_error_context(void) +{ + str_number str; + int sel = selector; + int saved_new_line_char; + int saved_new_string_line; + selector = new_string; + saved_new_line_char = new_line_char_par; + saved_new_string_line = new_string_line; + new_line_char_par = 10; + new_string_line = 10; + show_context(); + xfree(last_error_context); + str = make_string(); + last_error_context = makecstring(str); + flush_str(str); + selector = sel; + new_line_char_par = saved_new_line_char; + new_string_line = saved_new_string_line; + return; +} + +void flush_err(void) +{ + str_number s_error; + char *s = NULL; + int callback_id ; + if (in_error) { + selector = err_old_setting; + str_room(1); + s_error = make_string(); + s = makecstring(s_error); + flush_str(s_error); + if (interaction == error_stop_mode) { + wake_up_terminal(); + } + xfree(last_error); + last_error = (string) xmalloc((unsigned) (strlen(s) + 1)); + strcpy(last_error,s); + callback_id = callback_defined(show_error_message_callback); + if (callback_id > 0) { + run_callback(callback_id, "->"); + } else { + tprint(s); + } + in_error = 0 ; + } +} + +void print_err(const char *s) +{ + int callback_id = callback_defined(show_error_message_callback); + if (interaction == error_stop_mode) { + wake_up_terminal(); + } + if (callback_id > 0) { + err_old_setting = selector; + selector = new_string; + in_error = 1 ; + } + if (filelineerrorstylep) { + print_file_line(); + } else { + tprint_nl("! "); + } + tprint(s); + if (callback_id <= 0) { + xfree(last_error); + last_error = (string) xmalloc((unsigned) (strlen(s) + 1)); + strcpy(last_error,s); + } +} + +/*tex + +\TeX\ is careful not to call |error| when the print |selector| setting might be +unusual. The only possible values of |selector| at the time of error messages are + +|no_print| (when |interaction=batch_mode| and |log_file| not yet open); + +|term_only| (when |interaction>batch_mode| and |log_file| not yet open); + +|log_only| (when |interaction=batch_mode| and |log_file| is open); + +|term_and_log| (when |interaction>batch_mode| and |log_file| is open). + +*/ + +void fixup_selector(boolean logopened) +{ + if (interaction == batch_mode) + selector = no_print; + else + selector = term_only; + if (logopened) + selector = selector + 2; +} + +/*tex + +A global variable |deletions_allowed| is set |false| if the |get_next| routine is +active when |error| is called; this ensures that |get_next| and related routines +like |get_token| will never be called recursively. A similar interlock is +provided by |set_box_allowed|. @^recursion@> + +The global variable |history| records the worst level of error that has been +detected. It has four possible values: |spotless|, |warning_issued|, +|error_message_issued|, and |fatal_error_stop|. + +Another global variable, |error_count|, is increased by one when an |error| +occurs without an interactive dialog, and it is reset to zero at the end of every +paragraph. If |error_count| reaches 100, \TeX\ decides that there is no point in +continuing further. + +*/ + +/*tex Is it safe for |error| to call |get_token|? */ + +boolean deletions_allowed; + +/*tex Is it safe to do a \.{\\setbox} assignment? */ + +boolean set_box_allowed; +/*tex Has the source input been clean so far? */ + +int history; + +/*tex The number of scrolled errors since the last paragraph ended. */ + +int error_count; + +/*tex Should \TeX\ pause for instructions? */ + +int interrupt; + +/*tex Should interrupts be observed? */ + +boolean OK_to_interrupt; + +/*tex + +The value of |history| is initially |fatal_error_stop|, but it will be changed to +|spotless| if \TeX\ survives the initialization process. + +*/ + +void initialize_errors(void) +{ + if (interactionoption == unspecified_mode) + interaction = error_stop_mode; + else + interaction = interactionoption; + deletions_allowed = true; + set_box_allowed = true; + OK_to_interrupt = true; +} + +/*tex + +It is possible for |error| to be called recursively if some error arises when +|get_token| is being used to delete a token, and/or if some fatal error occurs +while \TeX\ is trying to fix a non-fatal one. But such recursion @^recursion@> is +never more than two levels deep. + +Individual lines of help are recorded in the array |help_line|. + +*/ + +const char *help_line[7]; + +/*tex + Should the |err_help| list be shown? +*/ + +boolean use_err_help; + +/*tex + +The |jump_out| procedure just cuts across all active procedure levels and exits +the program. It is used when there is no recovery from a particular error. The +exit code can be overloaded. + +*/ + +int defaultexitcode = 0; + +__attribute__ ((noreturn)) +void do_final_end(void) +{ + update_terminal(); + ready_already = 0; + lua_close(Luas); + if ((history != spotless) && (history != warning_issued)) + uexit(1); + else + uexit(defaultexitcode); +} + +__attribute__ ((noreturn)) +void jump_out(void) +{ + close_files_and_terminate(); + do_final_end(); +} + +/*tex + +Here is the function that calls the editor, if one is defined. This is loosely +based on a similar function in kpathsea, but the calling convention is quite +different. + +*/ + +static const_string edit_value = EDITOR; + +#if defined(WIN32) + +static int Isspace (char c) +{ + return (c == ' ' || c == '\t'); +} + +#endif /* WIN32 */ + +__attribute__ ((noreturn)) +static void luatex_calledit (int baseptr, int linenumber) +{ + char *temp, *command; + char *fullcmd = NULL; /* avoid compiler warning */ + char c; + int sdone, ddone, i; + char *filename = makecstring(input_stack[base_ptr].name_field); + int fnlength = strlen(filename); +#ifdef WIN32 + char *fp, *ffp, *env, editorname[256], buffer[256]; + int cnt = 0; + int dontchange = 0; +#endif + sdone = ddone = 0; + /*tex + Close any open input files, since we're going to kill the job. + */ + close_files_and_terminate(); + /*tex + Replace the default with the value of the appropriate environment + variable or config file value, if it's set. + */ + temp = kpse_var_value (edit_var); + if (temp != NULL) + edit_value = temp; + /*tex + Construct the command string. The `11' is the maximum length an + integer might be. + */ + command = xmalloc (strlen (edit_value) + fnlength + 11); + /*tex + So we can construct it as we go. + */ + temp = command; +#ifdef WIN32 + fp = editorname; + if ((isalpha(*edit_value) && *(edit_value + 1) == ':' && IS_DIR_SEP (*(edit_value + 2))) + || (*edit_value == '"' && isalpha(*(edit_value + 1)) + && *(edit_value + 2) == ':' && IS_DIR_SEP (*(edit_value + 3)))) { + dontchange = 1; + } +#endif + while ((c = *edit_value++) != 0) { + if (c == '%') { + switch (c = *edit_value++) { + case 'd': + if (ddone) + FATAL1 ("call_edit: `%%d' appears twice in editor command: `%s'", edit_value); + sprintf (temp, "%ld", (long int)linenumber); + while (*temp != '\0') + temp++; + ddone = 1; + break; + case 's': + if (sdone) + FATAL1 ("call_edit: `%%s' appears twice in editor command: `%s'", edit_value); + for (i =0; i < fnlength; i++) + *temp++ = filename[i]; + sdone = 1; + break; + case '\0': + *temp++ = '%'; + /*tex + Back up to the null to force termination. + */ + edit_value--; + break; + default: + *temp++ = '%'; + *temp++ = c; + break; + } + } else { +#ifdef WIN32 + if (dontchange) { + *temp++ = c; + } else if(Isspace(c) && cnt == 0) { + cnt++; + temp = command; + *temp++ = c; + *fp = '\0'; + } else if(!Isspace(c) && cnt == 0) { + *fp++ = c; + } else { + *temp++ = c; + } +#else + *temp++ = c; +#endif + } + } + *temp = 0; +#ifdef WIN32 + if (dontchange == 0) { + if(editorname[0] == '.' || editorname[0] == '/' || editorname[0] == '\\') { + fprintf(stderr, "%s is not allowed to execute.\n", editorname); + do_final_end(); + } + env = (char *)getenv("PATH"); + if(SearchPath(env, editorname, ".exe", 256, buffer, &ffp)==0) { + if(SearchPath(env, editorname, ".bat", 256, buffer, &ffp)==0) { + fprintf(stderr, "I cannot find %s in the PATH.\n", editorname); + do_final_end(); + } + } + fullcmd = (char *)xmalloc(strlen(buffer)+strlen(command)+5); + strcpy(fullcmd, "\""); + strcat(fullcmd, buffer); + strcat(fullcmd, "\""); + strcat(fullcmd, command); + } +#else + fullcmd = command; +#endif + /*tex Execute the command. */ + if (system (fullcmd) != 0) { + fprintf (stderr, "! Trouble executing `%s'.\n", command); + } + /*tex Quit, since we found an error. */ + do_final_end (); +} + +/*tex + + This completes the job of error reporting. + +*/ + +void error(void) +{ + /*tex What the user types :*/ + ASCII_code c; + int callback_id; + /*tex Used to save global variables when deleting tokens: */ + int s1, s2, s3, s4; + int i; + flush_err(); + if (history < error_message_issued) + history = error_message_issued; + callback_id = callback_defined(show_error_hook_callback); + if (callback_id > 0) { + set_last_error_context(); + run_callback(callback_id, "->"); + } else { + print_char('.'); + show_context(); + } + if (haltonerrorp) { + history = fatal_error_stop; + jump_out(); + } + if (interaction == error_stop_mode) { + /*tex Get user's advice and |return|. */ + while (1) { + CONTINUE: + /*tex + Original reports: + + https://tex.stackexchange.com/questions/551313/ + https://tug.org/pipermail/tex-live/2020-June/045876.html + + This will probably be fixed by DEK in the 2021 tuneup in a different + way (so we'll have to remove or alter this change), but the interaction + sequence in the reports above causes a segmentation fault in web2c - + writing to the closed \write15 stream because we wrongly decrement + selector from 16 to 15 in term_input, due to the lack of this check in + recursive error() call. + */ + if (interaction !=error_stop_mode) + return; + clear_for_error_prompt(); + prompt_input("? "); + if (last == first) + return; + c = buffer[first]; + if (c >= 'a') + c = c + 'A' - 'a'; + /*tex + Interpret code |c| and |return| if done. It is desirable to + provide an `\.E' option here that gives the user an easy way + to return from \TeX\ to the system editor, with the offending + line ready to be edited. But such an extension requires some + system wizardry, so the present implementation simply types + out the name of the file that should be edited and the + relevant line number. + */ + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (deletions_allowed) { + /*tex + Delete |c-"0"| tokens and |goto continue|. We allow + deletion of up to 99 tokens at a time. + */ + s1 = cur_tok; + s2 = cur_cmd; + s3 = cur_chr; + s4 = align_state; + align_state = 1000000; + OK_to_interrupt = false; + if ((last > first + 1) && (buffer[first + 1] >= '0') + && (buffer[first + 1] <= '9')) + c = c * 10 + buffer[first + 1] - '0' * 11; + else + c = c - '0'; + while (c > 0) { + /*tex One-level recursive call of |error| is possible. */ + get_token(); + decr(c); + } + cur_tok = s1; + cur_cmd = s2; + cur_chr = s3; + align_state = s4; + OK_to_interrupt = true; + help2( + "I have just deleted some text, as you asked.", + "You can now delete more, or insert, or whatever." + ); + show_context(); + goto CONTINUE; + } + break; + case 'E': + if (base_ptr > 0) { + int callback_id = callback_defined(call_edit_callback); + if (callback_id>0) { + (void)run_callback(callback_id, "Sd->", makecstring(input_stack[base_ptr].name_field), line); + /*tex This should not be reached. */ + jump_out(); + } else { + tprint_nl("You want to edit file "); + print(input_stack[base_ptr].name_field); + tprint(" at line "); + print_int(line); + interaction = scroll_mode; + if (kpse_init) { + luatex_calledit(base_ptr, line); + } else { + /*tex This should not be reached. */ + tprint_nl("There is no valid callback defined."); + jump_out(); + } + } + } + break; + case 'H': + /*tex Print the help information and |goto continue| */ + if (use_err_help) { + give_err_help(); + } else { + if (help_line[0] == NULL) { + help2( + "Sorry, I don't know how to help in this situation.", + "Maybe you should try asking a human?" + ); + } + i = 0; + while (help_line[i] != NULL) + tprint_nl(help_line[i++]); + help4( + "Sorry, I already gave what help I could...", + "Maybe you should try asking a human?", + "An error might have occurred before I noticed any problems.", + "``If all else fails, read the instructions.''" + ); + goto CONTINUE; + } + break; + case 'I': + /*tex + + Introduce new material from the terminal and |return|. When + the following code is executed, |buffer[(first+1)..(last-1)]| + may contain the material inserted by the user; otherwise + another prompt will be given. In order to understand this + part of the program fully, you need to be familiar with + \TeX's input stacks. + + We enter a new syntactic level for terminal input: + + */ + begin_file_reading(); + /*tex + Now |state=mid_line|, so an initial blank space will count as + a blank. + */ + if (last > first + 1) { + iloc = first + 1; + buffer[first] = ' '; + } else { + prompt_input("insert>"); + iloc = first; + } + first = last; + /*tex No |end_line_char| ends this line. */ + ilimit = last - 1; + return; + break; + case 'Q': + case 'R': + case 'S': + /*tex + + Change the interaction level and |return|. Here the author of + \TeX\ apologizes for making use of the numerical relation + between |"Q"|, |"R"|, |"S"|, and the desired interaction + settings |batch_mode|, |nonstop_mode|, |scroll_mode|. + + */ + error_count = 0; + interaction = batch_mode + c - 'Q'; + tprint("OK, entering "); + switch (c) { + case 'Q': + tprint_esc("batchmode"); + decr(selector); + break; + case 'R': + tprint_esc("nonstopmode"); + break; + case 'S': + tprint_esc("scrollmode"); + break; + } + tprint("..."); + print_ln(); + update_terminal(); + return; + break; + case 'X': + interaction = scroll_mode; + jump_out(); + break; + default: + break; + } + if (!use_err_help) { + /* Print the menu of available options */ + tprint("Type <return> to proceed, S to scroll future error messages,"); + tprint_nl("R to run without stopping, Q to run quietly,"); + tprint_nl("I to insert something, "); + if (base_ptr > 0) + tprint("E to edit your file,"); + if (deletions_allowed) + tprint_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); + tprint_nl("H for help, X to quit."); + } + use_err_help = false; + } + + } + incr(error_count); + if (error_count == 100) { + tprint_nl("(That makes 100 errors; please try again.)"); + history = fatal_error_stop; + jump_out(); + } + /*tex Put help message on the transcript file. */ + if (interaction > batch_mode) { + /*tex Avoid terminal output: */ + decr(selector); + } + if (use_err_help) { + print_ln(); + give_err_help(); + } else { + int i1 = 0; + while (help_line[i1] != NULL) + tprint_nl(help_line[i1++]); + } + print_ln(); + if (interaction > batch_mode) { + /*tex Re-enable terminal output: */ + incr(selector); + } + print_ln(); +} + +/*tex + +A dozen or so error messages end with a parenthesized integer, so we save a teeny +bit of program space by declaring the following procedure: + +*/ + +void int_error(int n) +{ + tprint(" ("); + print_int(n); + print_char(')'); + error(); +} + +/*tex + +In anomalous cases, the print selector might be in an unknown state; the +following subroutine is called to fix things just enough to keep running a bit +longer. + +*/ + +void normalize_selector(void) +{ + if (log_opened_global) + selector = term_and_log; + else + selector = term_only; + if (job_name == 0) + open_log_file(); + if (interaction == batch_mode) + decr(selector); +} + +/*tex + +The following procedure prints \TeX's last words before dying. + +*/ + +void succumb(void) +{ + if (interaction == error_stop_mode) { + /*tex No more interaction: */ + interaction = scroll_mode; + } + if (log_opened_global) { + error(); + } + history = fatal_error_stop; + /*tex Irrecoverable error: */ + jump_out(); +} + +/*tex + +This prints |s|, and that's it. + +*/ + +void fatal_error(const char *s) +{ + normalize_selector(); + print_err("Emergency stop"); + help1(s); + succumb(); +} + +/*tex + +Here is the most dreaded error message. We stop due to finiteness. + +*/ + +void overflow(const char *s, unsigned int n) +{ + normalize_selector(); + print_err("TeX capacity exceeded, sorry ["); + tprint(s); + print_char('='); + print_int((int) n); + print_char(']'); + if (varmem == NULL) { + print_err("Sorry, I ran out of memory."); + print_ln(); + exit(EXIT_FAILURE); + } + help2( + "If you really absolutely need more capacity,", + "you can ask a wizard to enlarge me." + ); + succumb(); +} + +/*tex + +The program might sometime run completely amok, at which point there is no choice +but to stop. If no previous error has been detected, that's bad news; a message +is printed that is really intended for the \TeX\ maintenance person instead of +the user (unless the user has been particularly diabolical). The index entries +for `this can't happen' may help to pinpoint the problem. @^dry rot@> + +*/ + +void confusion(const char *s) +{ /* consistency check violated; |s| tells where */ + normalize_selector(); + if (history < error_message_issued) { + print_err("This can't happen ("); + tprint(s); + print_char(')'); + help1( + "I'm broken. Please show this to someone who can fix" + ); + } else { + print_err("I can't go on meeting you like this"); + help2( + "One of your faux pas seems to have wounded me deeply...", + "in fact, I'm barely conscious. Please fix it and try again." + ); + } + succumb(); +} + +/*tex + +Users occasionally want to interrupt \TeX\ while it's running. If the runtime +system allows this, one can implement a routine that sets the global variable +|interrupt| to some nonzero value when such an interrupt is signalled. Otherwise +there is probably at least a way to make |interrupt| nonzero using the debugger. +@^system dependencies@> @^debugging@> + +*/ + +void check_interrupt(void) +{ + if (interrupt != 0) + pause_for_instructions(); +} + +/*tex + +When an interrupt has been detected, the program goes into its highest +interaction level and lets the user have nearly the full flexibility of the +|error| routine. \TeX\ checks for interrupts only at times when it is safe to do +this. + +*/ + +void pause_for_instructions(void) +{ + if (OK_to_interrupt) { + interaction = error_stop_mode; + if ((selector == log_only) || (selector == no_print)) + incr(selector); + print_err("Interruption"); + help3( + "You rang?", + "Try to insert some instructions for me (e.g.,`I\\showlists'),", + "unless you just want to quit by typing `X'." + ); + deletions_allowed = false; + error(); + deletions_allowed = true; + interrupt = 0; + } +} + +void tex_error(const char *msg, const char **hlp) +{ + print_err(msg); + if (hlp != NULL) { + int i; + for (i = 0; (hlp[i] != NULL && i <= 5); i++) { + help_line[i] = hlp[i]; + } + help_line[i] = NULL; + } else { + help_line[0] = NULL; + } + error(); +} + +/*tex + +The |back_error| routine is used when we want to replace an offending token just +before issuing an error message. This routine, like |back_input|, requires that +|cur_tok| has been set. We disable interrupts during the call of |back_input| so +that the help message won't be lost. + +*/ + +void back_error(void) +{ + OK_to_interrupt = false; + back_input(); + OK_to_interrupt = true; + error(); +} + +/*tex + + Back up one inserted token and call |error|. +*/ + +void ins_error(void) +{ + OK_to_interrupt = false; + back_input(); + token_type = inserted; + OK_to_interrupt = true; + error(); +} + +/*tex + +When \TeX\ wants to typeset a character that doesn't exist, the character node is +not created; thus the output routine can assume that characters exist when it +sees them. The following procedure prints a warning message unless the user has +suppressed it. +If |tracing_lost_chars_par| (i.e. \.{\\tracinglostchar}) is greater than 2, +it's considered as an error. + +*/ + +void char_warning(internal_font_number f, int c) +{ + /*tex saved value of |tracing_online| */ + int old_setting; + /* index to current digit; we assume that $0\L n<16^{22}$ */ + int k; + if (tracing_lost_chars_par > 0) { + old_setting = tracing_online_par; + if (tracing_lost_chars_par > 1) + tracing_online_par = 1; + begin_diagnostic(); + tprint_nl("Missing character: There is no "); + print(c); + tprint(" (U+"); + k = 0; + if (c < 16) + print_char('0'); + if (c < 256) + print_char('0'); + if (c < 4096) + print_char('0'); + do { + dig[k] = c % 16; + c = c / 16; + incr(k); + } while (c != 0); + print_the_digs((eight_bits) k); + tprint(") in font "); + print_font_name(f); + print_char('!'); + end_diagnostic(false); + tracing_online_par = old_setting; + } + if (tracing_lost_chars_par > 2) { + error(); + } +} + +void wrapup_backend(void) { + ensure_output_state(static_pdf, ST_OMODE_FIX); + if (output_mode_used == OMODE_NONE) { + print_err(" ==> Fatal error occurred, no FMT file produced!"); + } else { + backend_out_control[backend_control_finish_file](static_pdf,history == fatal_error_stop); + } +} + +void normal_error(const char *t, const char *p) +{ + normalize_selector(); + if (interaction == error_stop_mode) { + wake_up_terminal(); + } + if (filelineerrorstylep) { + print_file_line(); + } else { + tprint_nl("! "); + } + tprint("error: "); + if (cur_file_name) { + tprint(" (file "); + tprint(cur_file_name); + tprint(")"); + } + if (t != NULL) { + tprint(" ("); + tprint(t); + tprint(")"); + } + tprint(": "); + if (p != NULL) + tprint(p); + history = fatal_error_stop; + wrapup_backend(); + exit(EXIT_FAILURE); +} + +void normal_warning(const char *t, const char *p) +{ + int report_id ; + if (strcmp(t,"lua") == 0) { + int saved_new_line_char; + saved_new_line_char = new_line_char_par; + new_line_char_par = 10; + report_id = callback_defined(show_lua_error_hook_callback); + if (report_id == 0) { + if (p != NULL) + tprint(p); + help2( + "The lua interpreter ran into a problem, so the", + "remainder of this lua chunk will be ignored." + ); + } else { + (void) run_callback(report_id, "->"); + } + error(); + new_line_char_par = saved_new_line_char; + } else { + report_id = callback_defined(show_warning_message_callback); + if (report_id > 0) { + /*tex Free the last ones, */ + xfree(last_warning_str); + xfree(last_warning_tag); + last_warning_str = (string) xmalloc(strlen(p) + 1); + last_warning_tag = (string) xmalloc(strlen(t) + 1); + strcpy(last_warning_str,p); + strcpy(last_warning_tag,t); + run_callback(report_id, "->"); + } else { + print_ln(); + tprint("warning "); + if (cur_file_name) { + tprint(" (file "); + tprint(cur_file_name); + tprint(")"); + } + if (t != NULL) { + tprint(" ("); + tprint(t); + tprint(")"); + } + tprint(": "); + if (p != NULL) + tprint(p); + print_ln(); + } + if (history == spotless) + history = warning_issued; + } +} + +static char print_buf[PRINTF_BUF_SIZE]; + +__attribute__ ((format(printf, 2,3))) +void formatted_error(const char *t, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); + normal_error(t,print_buf); + va_end(args); +} + +__attribute__ ((format(printf, 2,3))) +void formatted_warning(const char *t, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); + normal_warning(t,print_buf); + va_end(args); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.h b/Build/source/texk/web2c/luatexdir/tex/errors.h new file mode 100644 index 00000000000..39fdf6b46e2 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/errors.h @@ -0,0 +1,116 @@ +/* errors.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef ERRORS_H +# define ERRORS_H + +/* +The global variable |interaction| has four settings, representing increasing +amounts of user interaction: +*/ + +typedef enum { + batch_mode = 0, /* omits all stops and omits terminal output */ + nonstop_mode = 1, /* omits all stops */ + scroll_mode = 2, /* omits error stops */ + error_stop_mode = 3, /* stops at every opportunity to interact */ + unspecified_mode = 4, /* extra value for command-line switch */ +} interaction_settings; + +extern int interaction; /* current level of interaction */ +extern int interactionoption; /* set from command line */ + +extern int defaultexitcode; /* the exit code can be overloaded */ + +extern void initialize_errors(void); + +extern char *last_error; +extern char *last_lua_error; +extern char *last_warning_tag; +extern char *last_warning_str; +extern char *last_error_context; + +extern void set_last_error_context(void); + +extern void print_err(const char *s); + +extern void fixup_selector(boolean log_opened); + +extern boolean deletions_allowed; +extern boolean set_box_allowed; +extern int history; +extern int error_count; +extern int interrupt; +extern boolean OK_to_interrupt; + +typedef enum { + spotless = 0, /* |history| value when nothing has been amiss yet */ + warning_issued = 1, /* |history| value when |begin_diagnostic| has been called */ + error_message_issued = 2, /* |history| value when |error| has been called */ + fatal_error_stop = 3, /* |history| value when termination was premature */ +} error_states; + +extern const char *help_line[7]; /* helps for the next |error| */ +extern boolean use_err_help; /* should the |err_help| list be shown? */ + +/* these macros are just temporary, until everything is in C */ + +# define hlp1(A) help_line[0]=A +# define hlp2(A,B) help_line[1]=A; hlp1(B) +# define hlp3(A,B,C) help_line[2]=A; hlp2(B,C) +# define hlp4(A,B,C,D) help_line[3]=A; hlp3(B,C,D) +# define hlp5(A,B,C,D,E) help_line[4]=A; hlp4(B,C,D,E) +# define hlp6(A,B,C,D,E,F) help_line[5]=A; hlp5(B,C,D,E,F) + +# define help0() help_line[0]=NULL /* sometimes there might be no help */ +# define help1(A) do { help_line[1]=NULL; hlp1(A); } while (0) +# define help2(A,B) do { help_line[2]=NULL; hlp2(B,A); } while (0) +# define help3(A,B,C) do { help_line[3]=NULL; hlp3(C,B,A); } while (0) +# define help4(A,B,C,D) do { help_line[4]=NULL; hlp4(D,C,B,A); } while (0) +# define help5(A,B,C,D,E) do { help_line[5]=NULL; hlp5(E,D,C,B,A); } while (0) +# define help6(A,B,C,D,E,F) do { help_line[6]=NULL; hlp6(F,E,D,C,B,A); } while (0) + +extern void do_final_end(void); +extern void jump_out(void); +extern void error(void); +extern void int_error(int n); +extern void normalize_selector(void); +extern void succumb(void); +extern void fatal_error(const char *s); +extern void overflow(const char *s, unsigned int n); +extern void confusion(const char *s); +extern void check_interrupt(void); +extern void pause_for_instructions(void); + +extern void tex_error(const char *msg, const char **hlp); +extern void normal_error(const char *t, const char *p); +extern void normal_warning(const char *t, const char *p); +extern void formatted_error(const char *t, const char *fmt, ...); +extern void formatted_warning(const char *t, const char *fmt, ...); + +extern void back_error(void); +extern void ins_error(void); +extern void flush_err(void); + +extern void char_warning(internal_font_number f, int c); + +extern void wrapup_backend(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.c b/Build/source/texk/web2c/luatexdir/tex/expand.c new file mode 100644 index 00000000000..e85c8f3bdb6 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/expand.c @@ -0,0 +1,967 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + + Only a dozen or so command codes |>max_command| can possibly be returned by + |get_next|; in increasing order, they are |undefined_cs|, |expand_after|, + |no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|, + |top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and + |end_template|. + + Sometimes, recursive calls to the following |expand| routine may cause + exhaustion of the run-time calling stack, resulting in forced execution stops + by the operating system. To diminish the chance of this happening, a counter + is used to keep track of the recursion depth, in conjunction with a constant + called |expand_depth|. + + Note that this does not catch all possible infinite recursion loops, just the + ones that exhaust the application calling stack. The actual maximum value of + |expand_depth| is outside of our control, but the initial setting of |100| + should be enough to prevent problems. + +*/ + +static int expand_depth_count = 0; + +/*tex + + The |expand| subroutine is used when |cur_cmd>max_command|. It removes a + ``call'' or a conditional or one of the other special operations just listed. + It follows that |expand| might invoke itself recursively. In all cases, + |expand| destroys the current token, but it sets things up so that the next + |get_next| will deliver the appropriate next token. The value of |cur_tok| + need not be known when |expand| is called. + + Since several of the basic scanning routines communicate via global + variables, their values are saved as local variables of |expand| so that + recursive calls don't invalidate them. + +*/ + +int is_in_csname = 0; + +void expand(void) +{ + /*tex token that is being ``expanded after'' */ + halfword t; + /*tex for list manipulation */ + halfword p; + /*tex for a local token list pointer */ + halfword cur_ptr; + /*tex to save the global quantity |cur_val| */ + int cv_backup; + /*tex to save |cur_val_level|, etc. */ + int cvl_backup, radix_backup, co_backup; + /*tex to save |link(backup_head)| */ + halfword backup_backup; + /*tex temporary storage of |scanner_status| */ + int save_scanner_status; + incr(expand_depth_count); + if (expand_depth_count >= expand_depth) + overflow("expansion depth", (unsigned) expand_depth); + cv_backup = cur_val; + cvl_backup = cur_val_level; + radix_backup = radix; + co_backup = cur_order; + backup_backup = token_link(backup_head); + RESWITCH: + if (cur_cmd < call_cmd) { + /*tex Expand a nonmacro. */ + if (tracing_commands_par > 1) + show_cur_cmd_chr(); + switch (cur_cmd) { + case top_bot_mark_cmd: + /*tex Insert the appropriate mark text into the scanner. */ + t = cur_chr % marks_code; + if (cur_chr >= marks_code) + scan_mark_num(); + else + cur_val = 0; + switch (t) { + case first_mark_code: + cur_ptr = first_mark(cur_val); + break; + case bot_mark_code: + cur_ptr = bot_mark(cur_val); + break; + case split_first_mark_code: + cur_ptr = split_first_mark(cur_val); + break; + case split_bot_mark_code: + cur_ptr = split_bot_mark(cur_val); + break; + default: + cur_ptr = top_mark(cur_val); + break; + } + if (cur_ptr != null) + begin_token_list(cur_ptr, mark_text); + break; + case expand_after_cmd: + if (cur_chr == 0) { + /*tex + + Expand the token after the next token. It takes only a + little shuffling to do what \TeX\ calls + \.{\\expandafter}. + + */ + get_token(); + t = cur_tok; + get_token(); + if (cur_cmd > max_command_cmd) + expand(); + else + back_input(); + cur_tok = t; + back_input(); + } else { + /*tex + + Negate a boolean conditional and |goto reswitch|. The + result of a boolean condition is reversed when the + conditional is preceded by \.{\\unless}. + + */ + get_token(); + if ((cur_cmd == if_test_cmd) && (cur_chr != if_case_code)) { + cur_chr = cur_chr + unless_code; + goto RESWITCH; + } + print_err("You can't use `\\unless' before `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help1("Continue, and I'll forget that it ever happened."); + back_error(); + } + break; + case no_expand_cmd: + if (cur_chr == 0) { + /*tex + + Suppress expansion of the next token. The implementation + of \.{\\noexpand} is a bit trickier, because it is + necessary to insert a special `|dont_expand|' marker into + \TeX's reading mechanism. This special marker is + processed by |get_next|, but it does not slow down the + inner loop. + + Since \.{\\outer} macros might arise here, we must also + clear the |scanner_status| temporarily. + + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + t = cur_tok; + back_input(); + /*tex Now |start| and |loc| point to the backed-up token |t|. */ + if (t >= cs_token_flag) { + p = get_avail(); + set_token_info(p, cs_token_flag + frozen_dont_expand); + set_token_link(p, iloc); + istart = p; + iloc = p; + } + + } else { + /*tex + + The \.{\\primitive} handling. If the primitive meaning of + the next token is an expandable command, it suffices to + replace the current token with the primitive one and + restart |expand|. + + Otherwise, the token we just read has to be pushed back, + as well as a token matching the internal form of + \.{\\primitive}, that is sneaked in as an alternate form + of |ignore_spaces|. + + An implementation problem surfaces: There really is no + |cur_cs| attached to the inserted primitive command, so + it is safer to set |cur_cs| to zero. |cur_tok| has a + similar problem. And for the non-expanded branch, simply + pushing back a token that matches the correct internal + command does not work, because that approach would not + survive roundtripping to a temporary file or even a token + list. + + It would be smart to create |frozen_| versions of all the + primitives. Then, this problem would not happen, at the + expense of a few hundred extra control sequences. + + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + t = get_prim_eq_type(cur_cs); + if (t > max_command_cmd) { + cur_cmd = t; + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + cur_cs = 0; + goto RESWITCH; + } else { + back_input(); + /*tex Now |loc| and |start| point to a one-item list. */ + p = get_avail(); + set_token_info(p, cs_token_flag + frozen_primitive); + set_token_link(p, iloc); + iloc = p; + istart = p; + } + } else if (suppress_primitive_error_par == 0) { + print_err("Missing primitive name"); + help2( + "The control sequence marked <to be read again> does not", + "represent any known primitive." + ); + back_error(); + } + } + break; + case cs_name_cmd: + /*tex Manufacture a control sequence name. */ + if (cur_chr == 0) { + manufacture_csname(0); + } else if (cur_chr == 1) { + inject_last_tested_cs(); + } else { + manufacture_csname(1); + } + break; + case convert_cmd: + conv_toks(); + break; + case the_cmd: + ins_the_toks(); + break; + case if_test_cmd: + /*tex An experiment. */ + if (cur_chr == if_condition_code) { + break; + } + /*tex End of experiment. */ + conditional(); + break; + case fi_or_else_cmd: + /*tex + + Terminate the current conditional and skip to \.{\\fi} The + processing of conditionals is complete except for the + following code, which is actually part of |expand|. It comes + into play when \.{\\or}, \.{\\else}, or \.{\\fi} is scanned. + + */ + if (tracing_ifs_par > 0) + if (tracing_commands_par <= 1) + show_cur_cmd_chr(); + if (cur_chr > if_limit) { + if (if_limit == if_code) { + /*tex Condition is not yet evaluated. */ + insert_relax(); + } else { + print_err("Extra "); + print_cmd_chr(fi_or_else_cmd, cur_chr); + help1("I'm ignoring this; it doesn't match any \\if."); + error(); + } + } else { + while (cur_chr != fi_code) { + /*tex Skip to \.{\\fi}. */ + pass_text(); + } + pop_condition_stack(); + } + break; + case input_cmd: + /*tex Initiate or terminate input from a file */ + if (cur_chr == 1) + force_eof = true; + else if (cur_chr == 2) + pseudo_start(); + else if (cur_chr == 3) { + pseudo_start(); + iname = 19; + } else if (name_in_progress) + insert_relax(); + else + start_input(); + break; + case lua_expandable_call_cmd: + if (cur_chr <= 0) { + normal_error("luacall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luafunctioncall(cur_chr); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } + break; + case lua_local_call_cmd: + if (cur_chr <= 0) { + normal_error("luacall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + lua_rawgeti(Luas, LUA_REGISTRYINDEX, cur_chr); + lua_call(Luas,0,0); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } + break; + case variable_cmd: + do_variable(); + break; + case feedback_cmd: + do_feedback(); + break; + default: + /*tex Complain about an undefined macro */ + print_err("Undefined control sequence"); + help5( + "The control sequence at the end of the top line", + "of your error message was never \\def'ed. If you have", + "misspelled it (e.g., `\\hobx'), type `I' and the correct", + "spelling (e.g., `I\\hbox'). Otherwise just continue,", + "and I'll forget about whatever was undefined." + ); + error(); + break; + } + } else if (cur_cmd < end_template_cmd) { + macro_call(); + } else { + /*tex + + Insert a token containing |frozen_endv|. An |end_template| command is + effectively changed to an |endv| command by the following code. (The + reason for this is discussed below; the |frozen_end_template| at the + end of the template has passed the |check_outer_validity| test, so + its mission of error detection has been accomplished.) + + */ + cur_tok = cs_token_flag + frozen_endv; + back_input(); + } + cur_val = cv_backup; + cur_val_level = cvl_backup; + radix = radix_backup; + cur_order = co_backup; + set_token_link(backup_head, backup_backup); + decr(expand_depth_count); +} + +void complain_missing_csname(void) +{ + print_err("Missing \\endcsname inserted"); + help2( + "The control sequence marked <to be read again> should", + "not appear between \\csname and \\endcsname." + ); + back_error(); +} + +void manufacture_csname(boolean use) +{ + halfword p, q, r; + lstring *ss; + r = get_avail(); + p = r; + is_in_csname += 1; + do { + get_x_token(); + if (cur_cs == 0) + store_new_token(cur_tok); + } while (cur_cs == 0); + if (cur_cmd != end_cs_name_cmd) { + /*tex Complain about missing \.{\\endcsname}. */ + complain_missing_csname(); + } + /*tex Look up the characters of list |r| in the hash table, and set |cur_cs|. */ + ss = tokenlist_to_lstring(r, true); + is_in_csname -= 1; + if (use) { + if (ss->l > 0) { + cur_cs = string_lookup((char *) ss->s, ss->l); + } else { + cur_cs = null_cs; + } + last_cs_name = cur_cs ; + free_lstring(ss); + flush_list(r); + if (cur_cs == null_cs) { + /*tex skip */ + } else if (eq_type(cur_cs) == undefined_cs_cmd) { + /*tex skip */ + } else { + cur_tok = cur_cs + cs_token_flag; + back_input(); + } + } else { + if (ss->l > 0) { + no_new_control_sequence = false; + cur_cs = string_lookup((char *) ss->s, ss->l); + no_new_control_sequence = true; + } else { + /*tex the list is empty */ + cur_cs = null_cs; + } + last_cs_name = cur_cs ; + free_lstring(ss); + flush_list(r); + if (eq_type(cur_cs) == undefined_cs_cmd) { + /*tex The |save_stack| might change! */ + eq_define(cur_cs, relax_cmd, too_big_char); + }; + /*tex The control sequence will now match \.{\\relax} */ + cur_tok = cur_cs + cs_token_flag; + back_input(); + } +} + +void inject_last_tested_cs(void) +{ + if (last_cs_name != null_cs) { + cur_cs = last_cs_name; + cur_tok = last_cs_name + cs_token_flag; + back_input(); + } +} + +/*tex + + Sometimes the expansion looks too far ahead, so we want to insert a harmless + \.{\\relax} into the user's input. + +*/ + +void insert_relax(void) +{ + cur_tok = cs_token_flag + cur_cs; + back_input(); + cur_tok = cs_token_flag + frozen_relax; + back_input(); + token_type = inserted; +} + + +/*tex + + Here is a recursive procedure that is \TeX's usual way to get the next token + of input. It has been slightly optimized to take account of common cases. + +*/ + +void get_x_token(void) +{ + /*tex This code sets |cur_cmd|, |cur_chr|, |cur_tok|, and expands macros. */ + RESTART: + get_next(); + if (cur_cmd <= max_command_cmd) + goto DONE; + if (cur_cmd >= call_cmd) { + if (cur_cmd < end_template_cmd) { + macro_call(); + } else { + cur_cs = frozen_endv; + cur_cmd = endv_cmd; + /*tex Now |cur_chr=null_list|. */ + goto DONE; + } + } else { + expand(); + } + goto RESTART; + DONE: + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + +/*tex + + The |get_x_token| procedure is equivalent to two consecutive procedure calls: + |get_next; x_token|. It's |get_x_token| without the initial |get_next|. + +*/ + +void x_token(void) +{ + while (cur_cmd > max_command_cmd) { + expand(); + get_next(); + } + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + +/*tex + + A control sequence that has been \.{\\def}'ed by the user is expanded by + \TeX's |macro_call| procedure. + + Before we get into the details of |macro_call|, however, let's consider the + treatment of primitives like \.{\\topmark}, since they are essentially macros + without parameters. The token lists for such marks are kept in five global + arrays of pointers; we refer to the individual entries of these arrays by + symbolic macros |top_mark|, etc. The value of |top_mark(x)|, etc. is either + |null| or a pointer to the reference count of a token list. + + The variable |biggest_used_mark| is an aid to try and keep the code somehwat + efficient without too much extra work: it registers the highest mark class + ever instantiated by the user, so the loops in |fire_up| and |vsplit| do not + have to traverse the full range |0..biggest_mark|. + +*/ + +halfword top_marks_array[(biggest_mark + 1)]; +halfword first_marks_array[(biggest_mark + 1)]; +halfword bot_marks_array[(biggest_mark + 1)]; +halfword split_first_marks_array[(biggest_mark + 1)]; +halfword split_bot_marks_array[(biggest_mark + 1)]; +halfword biggest_used_mark; + +void initialize_marks(void) +{ + int i; + biggest_used_mark = 0; + for (i = 0; i <= biggest_mark; i++) { + top_mark(i) = null; + first_mark(i) = null; + bot_mark(i) = null; + split_first_mark(i) = null; + split_bot_mark(i) = null; + } +} + +/*tex + + Now let's consider |macro_call| itself, which is invoked when \TeX\ is + scanning a control sequence whose |cur_cmd| is either |call|, |long_call|, + |outer_call|, or |long_outer_call|. The control sequence definition appears + in the token list whose reference count is in location |cur_chr| of |mem|. + + The global variable |long_state| will be set to |call| or to |long_call|, + depending on whether or not the control sequence disallows \.{\\par} in its + parameters. The |get_next| routine will set |long_state| to |outer_call| and + emit \.{\\par}, if a file ends or if an \.{\\outer} control sequence occurs + in the midst of an argument. + +*/ + +/*tex Governs the acceptance of \.{\\par}: */ + +int long_state; + +/*tex + + The parameters, if any, must be scanned before the macro is expanded. + Parameters are token lists without reference counts. They are placed on an + auxiliary stack called |pstack| while they are being scanned, since the + |param_stack| may be losing entries during the matching process. (Note that + |param_stack| can't be gaining entries, since |macro_call| is the only + routine that puts anything onto |param_stack|, and it is not recursive.) + +*/ + +/*tex The arguments supplied to a macro: */ + +halfword pstack[9]; + +/*tex + + After parameter scanning is complete, the parameters are moved to the + |param_stack|. Then the macro body is fed to the scanner; in other words, + |macro_call| places the defined text of the control sequence at the top of\/ + \TeX's input stack, so that |get_next| will proceed to read it next. + + The global variable |cur_cs| contains the |eqtb| address of the control + sequence being expanded, when |macro_call| begins. If this control sequence + has not been declared \.{\\long}, i.e., if its command code in the |eq_type| + field is not |long_call| or |long_outer_call|, its parameters are not allowed + to contain the control sequence \.{\\par}. If an illegal \.{\\par} appears, + the macro call is aborted, and the \.{\\par} will be rescanned. + +*/ + +/*tex This invokes a user-defined control sequence. */ + +void macro_call(void) +{ + /*tex current node in the macro's token list */ + halfword r; + /*tex current node in parameter token list being built */ + halfword p = null; + /*tex new node being put into the token list */ + halfword q; + /*tex backup pointer for parameter matching */ + halfword s; + /*tex cycle pointer for backup recovery */ + halfword t; + /*tex auxiliary pointers for backup recovery */ + halfword u, v; + /*tex one step before the last |right_brace| token */ + halfword rbrace_ptr = null; + /*tex the number of parameters scanned */ + int n = 0; + /*tex unmatched left braces in current parameter */ + halfword unbalance; + /*tex the number of tokens or groups (usually) */ + halfword m = 0; + /*tex start of the token list */ + halfword ref_count; + /*tex |scanner_status| upon entry */ + int save_scanner_status = scanner_status; + /*tex |warning_index| upon entry */ + halfword save_warning_index = warning_index; + /*tex character used in parameter */ + int match_chr = 0; + warning_index = cur_cs; + ref_count = cur_chr; + r = token_link(ref_count); + if (tracing_macros_par > 0) { + /*tex Show the text of the macro being expanded. */ + begin_diagnostic(); + print_input_level(); + // print_ln(); + print_cs(warning_index); + token_show(ref_count); + end_diagnostic(false); + } + if (token_info(r) == protected_token) + r = token_link(r); + if (token_info(r) != end_match_token) { + /*tex + + Scan the parameters and make |link(r)| point to the macro body; but + |return| if an illegal \.{\\par} is detected. + + At this point, the reader will find it advisable to review the + explanation of token list format that was presented earlier, since + many aspects of that format are of importance chiefly in the + |macro_call| routine. + + The token list might begin with a string of compulsory tokens before + the first |match| or |end_match|. In that case the macro name is + supposed to be followed by those tokens; the following program will + set |s=null| to represent this restriction. Otherwise |s| will be set + to the first token of a string that will delimit the next parameter. + + */ + scanner_status = matching; + unbalance = 0; + long_state = eq_type(cur_cs); + if (long_state >= outer_call_cmd) + long_state = long_state - 2; + do { + set_token_link(temp_token_head, null); + if ((token_info(r) >= end_match_token) + || (token_info(r) < match_token)) { + s = null; + } else { + match_chr = token_info(r) - match_token; + s = token_link(r); + r = s; + p = temp_token_head; + m = 0; + } + /*tex + + Scan a parameter until its delimiter string has been found; or, + if |s=null|, simply scan the delimiter string. + + If |info(r)| is a |match| or |end_match| command, it cannot be + equal to any token found by |get_token|. Therefore an undelimited + parameter---i.e., a |match| that is immediately followed by + |match| or |end_match|---will always fail the test + `|cur_tok=info(r)|' in the following algorithm. + + */ + CONTINUE: + /*tex Set |cur_tok| to the next token of input: */ + get_token(); + if (cur_tok == token_info(r)) { + /*tex + + Advance |r|; |goto found| if the parameter delimiter has been + fully matched, otherwise |goto continue|. + + A slightly subtle point arises here: When the parameter + delimiter ends with `\.{\#\{}', the token list will have a + left brace both before and after the |end_match|\kern-.4pt. + Only one of these should affect the |align_state|, but both + will be scanned, so we must make a correction. + + */ + r = token_link(r); + if ((token_info(r) >= match_token) + && (token_info(r) <= end_match_token)) { + if (cur_tok < left_brace_limit) + decr(align_state); + goto FOUND; + } else { + goto CONTINUE; + } + + } + /*tex + + Contribute the recently matched tokens to the current parameter, + and |goto continue| if a partial match is still in effect; but + abort if |s=null|. + + When the following code becomes active, we have matched tokens + from |s| to the predecessor of |r|, and we have found that + |cur_tok<>info(r)|. An interesting situation now presents itself: + If the parameter is to be delimited by a string such as `\.{ab}', + and if we have scanned `\.{aa}', we want to contribute one `\.a' + to the current parameter and resume looking for a `\.b'. The + program must account for such partial matches and for others that + can be quite complex. But most of the time we have |s=r| and + nothing needs to be done. + + Incidentally, it is possible for \.{\\par} tokens to sneak in to + certain parameters of non-\.{\\long} macros. For example, + consider a case like `\.{\\def\\a\#1\\par!\{...\}}' where the + first \.{\\par} is not followed by an exclamation point. In such + situations it does not seem appropriate to prohibit the + \.{\\par}, so \TeX\ keeps quiet about this bending of the rules. + + */ + if (s != r) { + if (s == null) { + /*tex Report an improper use of the macro and abort. */ + print_err("Use of "); + sprint_cs(warning_index); + tprint(" doesn't match its definition"); + help4( + "If you say, e.g., `\\def\\a1{...}', then you must always", + "put `1' after `\\a', since control sequence names are", + "made up of letters only. The macro here has not been", + "followed by the required stuff, so I'm ignoring it." + ); + error(); + goto EXIT; + + } else { + t = s; + do { + store_new_token(token_info(t)); + incr(m); + u = token_link(t); + v = s; + while (1) { + if (u == r) { + if (cur_tok != token_info(v)) { + goto DONE; + } else { + r = token_link(v); + goto CONTINUE; + } + } + if (token_info(u) != token_info(v)) + goto DONE; + u = token_link(u); + v = token_link(v); + } + DONE: + t = token_link(t); + } while (t != r); + r = s; + /*tex At this point, no tokens are recently matched. */ + } + } + if (cur_tok == par_token) + if (long_state != long_call_cmd) + if (!suppress_long_error_par) { + goto RUNAWAY; + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + /*tex Contribute an entire group to the current parameter. */ + unbalance = 1; + while (1) { + fast_store_new_token(cur_tok); + get_token(); + if (cur_tok == par_token) { + if (long_state != long_call_cmd) { + if (!suppress_long_error_par) { + goto RUNAWAY; + + } + } + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + break; + } + } + } + rbrace_ptr = p; + store_new_token(cur_tok); + } else { + /*tex Report an extra right brace and |goto continue|. */ + back_input(); + print_err("Argument of "); + sprint_cs(warning_index); + tprint(" has an extra }"); + help6( + "I've run across a `}' that doesn't seem to match anything.", + "For example, `\\def\\a#1{...}' and `\\a}' would produce", + "this error. If you simply proceed now, the `\\par' that", + "I've just inserted will cause me to report a runaway", + "argument that might be the root of the problem. But if", + "your `}' was spurious, just type `2' and it will go away." + ); + incr(align_state); + long_state = call_cmd; + cur_tok = par_token; + ins_error(); + goto CONTINUE; + /*tex A white lie; the \.{\\par} won't always trigger a runaway. */ + } + } else { + /*tex + + Store the current token, but |goto continue| if it is a blank + space that would become an undelimited parameter. + + */ + if (cur_tok == space_token && token_info(r) <= end_match_token && token_info(r) >= match_token) + goto CONTINUE; + store_new_token(cur_tok); + } + incr(m); + if (token_info(r) > end_match_token) + goto CONTINUE; + if (token_info(r) < match_token) + goto CONTINUE; + FOUND: + if (s != null) { + /* + + Tidy up the parameter just scanned, and tuck it away. If the + parameter consists of a single group enclosed in braces, we + must strip off the enclosing braces. That's why |rbrace_ptr| + was introduced. + + */ + if ((m == 1) && (token_info(p) < right_brace_limit) + && (p != temp_token_head)) { + set_token_link(rbrace_ptr, null); + free_avail(p); + p = token_link(temp_token_head); + pstack[n] = token_link(p); + free_avail(p); + } else { + pstack[n] = token_link(temp_token_head); + } + incr(n); + if (tracing_macros_par > 0) { + begin_diagnostic(); + print_input_level(); + // print_nl(match_chr); + print(match_chr); + print_int(n); + tprint("<-"); + show_token_list(pstack[n - 1], null, 1000); + end_diagnostic(false); + } + } + /*tex + + Now |info(r)| is a token whose command code is either |match| or + |end_match|. + + */ + } while (token_info(r) != end_match_token); + } + /*tex + + Feed the macro body and its parameters to the scanner Before we put a new + token list on the input stack, it is wise to clean off all token lists + that have recently been depleted. Then a user macro that ends with a call + to itself will not require unbounded stack space. + + */ + while ((istate == token_list) && (iloc == null) && (token_type != v_template)) { + /*tex Conserve stack space. */ + end_token_list(); + } + begin_token_list(ref_count, macro); + iname = warning_index; + iloc = token_link(r); + if (n > 0) { + if (param_ptr + n > max_param_stack) { + max_param_stack = param_ptr + n; + if (max_param_stack > param_size) + overflow("parameter stack size", (unsigned) param_size); + } + for (m = 0; m <= n - 1; m++) + param_stack[param_ptr + m] = pstack[m]; + param_ptr = param_ptr + n; + } + goto EXIT; + RUNAWAY: + /*tex + + Report a runaway argument and abort. If |long_state=outer_call|, a + runaway argument has already been reported. + + */ + if (long_state == call_cmd) { + runaway(); + print_err("Paragraph ended before "); + sprint_cs(warning_index); + tprint(" was complete"); + help3( + "I suspect you've forgotten a `}', causing me to apply this", + "control sequence to too much text. How can we recover?", + "My plan is to forget the whole thing and hope for the best." + ); + back_error(); + } + pstack[n] = token_link(temp_token_head); + align_state = align_state - unbalance; + for (m = 0; m <= n; m++) + flush_list(pstack[m]); + EXIT: + scanner_status = save_scanner_status; + warning_index = save_warning_index; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.h b/Build/source/texk/web2c/luatexdir/tex/expand.h new file mode 100644 index 00000000000..18c0514e317 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/expand.h @@ -0,0 +1,102 @@ +/* expand.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef EXPAND_H +# define EXPAND_H + +extern boolean is_in_csname; +extern void expand(void); +extern void complain_missing_csname(void); +extern void manufacture_csname(boolean use); +extern void inject_last_tested_cs(void); +extern void insert_relax(void); +extern void get_x_token(void); +extern void x_token(void); + +# define top_mark_code 0 /* the mark in effect at the previous page break */ +# define first_mark_code 1 /* the first mark between |top_mark| and |bot_mark| */ +# define bot_mark_code 2 /* the mark in effect at the current page break */ +# define split_first_mark_code 3 + /* the first mark found by \.{\\vsplit} */ +# define split_bot_mark_code 4 /* the last mark found by \.{\\vsplit} */ +# define marks_code 5 + +# define top_mark(A) top_marks_array[(A)] +# define first_mark(A) first_marks_array[(A)] +# define bot_mark(A) bot_marks_array[(A)] +# define split_first_mark(A) split_first_marks_array[(A)] +# define split_bot_mark(A) split_bot_marks_array[(A)] + +# define set_top_mark(A,B) top_mark(A)=(B) +# define set_first_mark(A,B) first_mark(A)=(B) +# define set_bot_mark(A,B) bot_mark(A)=(B) +# define set_split_first_mark(A,B) split_first_mark(A)=(B) +# define set_split_bot_mark(A,B) split_bot_mark(A)=(B) + +# define delete_top_mark(A) do { \ + if (top_mark(A)!=null) \ + delete_token_ref(top_mark(A)); \ + top_mark(A)=null; \ + } while (0) + +# define delete_bot_mark(A) do { \ + if (bot_mark(A)!=null) \ + delete_token_ref(bot_mark(A)); \ + bot_mark(A)=null; \ + } while (0) + +# define delete_first_mark(A) do { \ + if (first_mark(A)!=null) \ + delete_token_ref(first_mark(A)); \ + first_mark(A)=null; \ + } while (0) + +# define delete_split_first_mark(A) do { \ + if (split_first_mark(A)!=null) \ + delete_token_ref(split_first_mark(A)); \ + split_first_mark(A)=null; \ + } while (0) + +# define delete_split_bot_mark(A) do { \ + if (split_bot_mark(A)!=null) \ + delete_token_ref(split_bot_mark(A)); \ + split_bot_mark(A)=null; \ +} while (0) + +# define clear_marks_code 1 + +# define biggest_mark 65535 + +extern halfword top_marks_array[(biggest_mark + 1)]; +extern halfword first_marks_array[(biggest_mark + 1)]; +extern halfword bot_marks_array[(biggest_mark + 1)]; +extern halfword split_first_marks_array[(biggest_mark + 1)]; +extern halfword split_bot_marks_array[(biggest_mark + 1)]; +extern halfword biggest_used_mark; + + +extern void initialize_marks(void); + +extern int long_state; +extern halfword pstack[9]; +extern void macro_call(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.c b/Build/source/texk/web2c/luatexdir/tex/extensions.c new file mode 100644 index 00000000000..19d4e444173 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/extensions.c @@ -0,0 +1,1344 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +#define mode mode_par +#define tail tail_par +#define head head_par +#define dir_save dirs_par + +/*tex + + The program above includes a bunch of ``hooks'' that allow further + capabilities to be added without upsetting \TeX's basic structure. Most of + these hooks are concerned with ``whatsit'' nodes, which are intended to be + used for special purposes; whenever a new extension to \TeX\ involves a new + kind of whatsit node, a corresponding change needs to be made to the routines + below that deal with such nodes, but it will usually be unnecessary to make + many changes to the other parts of this program. + + In order to demonstrate how extensions can be made, we shall treat + `\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}', and + `\.{\\special}' as if they were extensions. These commands are actually + primitives of \TeX, and they should appear in all implementations of the + system; but let's try to imagine that they aren't. Then the program below + illustrates how a person could add them. + + Sometimes, of course, an extension will require changes to \TeX\ itself; no + system of hooks could be complete enough for all conceivable extensions. The + features associated with `\.{\\write}' are almost all confined to the + following paragraphs, but there are small parts of the |print_ln| and + |print_char| procedures that were introduced specifically to \.{\\write} + characters. Furthermore one of the token lists recognized by the scanner is a + |write_text|; and there are a few other miscellaneous places where we have + already provided for some aspect of \.{\\write}. The goal of a \TeX\ extender + should be to minimize alterations to the standard parts of the program, and + to avoid them completely if possible. He or she should also be quite sure + that there's no easy way to accomplish the desired goals with the standard + features that \TeX\ already has. ``Think thrice before extending,'' because + that may save a lot of work, and it will also keep incompatible extensions of + \TeX\ from proliferating. + + First let's consider the format of whatsit nodes that are used to represent + the data associated with \.{\\write} and its relatives. Recall that a whatsit + has |type=whatsit_node|, and the |subtype| is supposed to distinguish + different kinds of whatsits. Each node occupies two or more words; the exact + number is immaterial, as long as it is readily determined from the |subtype| + or other data. + + We shall introduce five |subtype| values here, corresponding to the control + sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, and \.{\\special}. The + second word of I/O whatsits has a |write_stream| field that identifies the + write-stream number (0 to 15, or 16 for out-of-range and positive, or 17 for + out-of-range and negative). In the case of \.{\\write} and \.{\\special}, + there is also a field that points to the reference count of a token list that + should be sent. In the case of \.{\\openout}, we need three words and three + auxiliary subfields to hold the string numbers for name, area, and extension. + + Extensions might introduce new command codes; but it's best to use + |extension| with a modifier, whenever possible, so that |main_control| stays + the same. + + The sixteen possible \.{\\write} streams are represented by the |write_file| + array. The |j|th file is open if and only if |write_open[j]=true|. The last + two streams are special; |write_open[16]| represents a stream number greater + than 15, while |write_open[17]| represents a negative stream number, and both + of these variables are always |false|. + +*/ + +alpha_file write_file[last_file_selector+1]; +halfword write_file_mode[last_file_selector+1]; +halfword write_file_translation[last_file_selector+1]; +boolean write_open[last_file_selector+1]; + +scaled neg_wd; +scaled pos_wd; +scaled neg_ht; + +/*tex + + The variable |write_loc| just introduced is used to provide an appropriate + error message in case of ``runaway'' write texts. + +*/ + +/*tex The |eqtb| address of \.{\\write}: */ + +halfword write_loc; + +/*tex + + When an |extension| command occurs in |main_control|, in any mode, the + |do_extension| routine is called. + +*/ + +int last_saved_image_index ; +int last_saved_image_pages ; +int last_saved_box_index ; +scaledpos last_position = { 0, 0 }; + +static void do_extension_dvi(int immediate) +{ + if (scan_keyword("literal")) { + new_whatsit(special_node); + write_stream(tail) = null; + scan_toks(false, true); + write_tokens(tail) = def_ref; + } else { + tex_error("unexpected use of \\dviextension",null); + } +} + +static void do_extension_pdf(int immediate) +{ + int i; + if (scan_keyword("literal")) { + new_whatsit(pdf_literal_node); + if (scan_keyword("direct")) + set_pdf_literal_mode(tail, direct_always); + else if (scan_keyword("page")) + set_pdf_literal_mode(tail, direct_page); + else if (scan_keyword("text")) + set_pdf_literal_mode(tail, direct_text); + else if (scan_keyword("raw")) + set_pdf_literal_mode(tail, direct_raw); + else if (scan_keyword("origin")) + set_pdf_literal_mode(tail, set_origin); + else + set_pdf_literal_mode(tail, set_origin); + scan_toks(false, true); + set_pdf_literal_type(tail, normal); + set_pdf_literal_data(tail, def_ref); + } else if (scan_keyword("dest")) { + scan_pdfdest(static_pdf); + } else if (scan_keyword("annot")) { + scan_annot(static_pdf); + } else if (scan_keyword("save")) { + new_whatsit(pdf_save_node); + } else if (scan_keyword("restore")) { + new_whatsit(pdf_restore_node); + } else if (scan_keyword("linkstate")) { + new_whatsit(pdf_link_state_node); + scan_int(); + pdf_link_state(tail) = cur_val; + } else if (scan_keyword("setmatrix")) { + new_whatsit(pdf_setmatrix_node); + scan_toks(false, true); + set_pdf_setmatrix_data(tail, def_ref); + } else if (scan_keyword("obj")) { + scan_obj(static_pdf); + if (immediate) { + if (obj_data_ptr(static_pdf, pdf_last_obj) == 0) { + /*tex This object has not been initialized yet. */ + normal_error("pdf backend","\\pdfextension obj 'reserveobjnum' cannot be used with \\immediate"); + } + pdf_write_obj(static_pdf, pdf_last_obj); + } + } else if (scan_keyword("refobj")) { + scan_refobj(static_pdf); + } else if (scan_keyword("colorstack")) { + scan_int(); + if (cur_val >= colorstackused()) { + print_err("Unknown color stack number "); + print_int(cur_val); + help3( + "Allocate and initialize a color stack with \\pdfextension colorstackinit.", + "I'll use default color stack 0 here.", + "Proceed, with fingers crossed." + ); + error(); + cur_val = 0; + } + if (cur_val < 0) { + print_err("Invalid negative color stack number"); + help2( + "I'll use default color stack 0 here.", + "Proceed, with fingers crossed." + ); + error(); + cur_val = 0; + } + if (scan_keyword("set")) + i = colorstack_set; + else if (scan_keyword("push")) + i = colorstack_push; + else if (scan_keyword("pop")) + i = colorstack_pop; + else if (scan_keyword("current")) + i = colorstack_current; + else + i = -1; + if (i >= 0) { + new_whatsit(pdf_colorstack_node); + set_pdf_colorstack_stack(tail, cur_val); + set_pdf_colorstack_cmd(tail, i); + set_pdf_colorstack_data(tail, null); + if (i <= colorstack_data) { + scan_toks(false, true); + set_pdf_colorstack_data(tail, def_ref); + } + } else { + print_err("Color stack action is missing"); + help3( + "The expected actions for \\pdfextension colorstack:", + " set, push, pop, current", + "I'll ignore the color stack command." + ); + error(); + } + } else if (scan_keyword("startlink")) { + scan_startlink(static_pdf); + } else if (scan_keyword("endlink")) { + if (abs(mode) == vmode) + normal_error("pdf backend", "\\pdfextension endlink cannot be used in vertical mode"); + new_whatsit(pdf_end_link_node); + } else if (scan_keyword("startthread")) { + new_annot_whatsit(pdf_start_thread_node); + scan_thread_id(); + } else if (scan_keyword("endthread")) { + new_whatsit(pdf_end_thread_node); + } else if (scan_keyword("thread")) { + new_annot_whatsit(pdf_thread_node); + scan_thread_id(); + } else if (scan_keyword("outline")) { + scan_pdfoutline(static_pdf); + } else if (scan_keyword("glyphtounicode")) { + glyph_to_unicode(); + } else if (scan_keyword("catalog")) { + scan_pdfcatalog(static_pdf); + } else if (scan_keyword("fontattr")) { + /*tex + + The font attributes are simply initialized to zero now, this is + easier to deal with from C than an empty \TeX{} string, and surely + nobody will want to set font attr to a string containing a single + zero, as that would be nonsensical in the PDF output. + + */ + scan_font_ident(); + i = cur_val; + if (i == null_font) + normal_error("pdf backend", "invalid font identifier"); + scan_toks(false, true); + set_pdf_font_attr(i, tokens_to_string(def_ref)); + if (str_length(pdf_font_attr(i)) == 0) { + /*tex From |tokens_to_string|. */ + flush_str((str_ptr - 1)); + set_pdf_font_attr(i, 0); + } + } else if (scan_keyword("mapfile")) { + scan_toks(false, true); + pdfmapfile(def_ref); + delete_token_ref(def_ref); + } else if (scan_keyword("mapline")) { + scan_toks(false, true); + pdfmapline(def_ref); + delete_token_ref(def_ref); + } else if (scan_keyword("includechars")) { + pdf_include_chars(static_pdf); + } else if (scan_keyword("info")) { + scan_toks(false, true); + pdf_info_toks = concat_tokens(pdf_info_toks, def_ref); + } else if (scan_keyword("names")) { + scan_toks(false, true); + pdf_names_toks = concat_tokens(pdf_names_toks, def_ref); + } else if (scan_keyword("trailer")) { + scan_toks(false, true); + pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref); + } else { + tex_error("unexpected use of \\pdfextension",null); + } +} + +static void do_resource_dvi(int immediate, int code) +{ + /*tex Nothing is done here. */ +} + +static void do_resource_pdf(int immediate, int code) +{ + switch (code) { + case use_box_resource_code: + scan_pdfrefxform(static_pdf); + break; + case use_image_resource_code: + scan_pdfrefximage(static_pdf); + break; + case save_box_resource_code: + scan_pdfxform(static_pdf); + if (immediate) { + pdf_cur_form = last_saved_box_index; + ship_out(static_pdf, obj_xform_box(static_pdf, last_saved_box_index), SHIPPING_FORM); + } + break; + case save_image_resource_code: + scan_pdfximage(static_pdf); + if (immediate) { + pdf_write_image(static_pdf, last_saved_image_index); + } + break; + } +} + +/*tex + + Ad immediate: + + To write a token list, we must run it through \TeX's scanner, expanding + macros and \.{\\the} and \.{\\number}, etc. This might cause runaways, if a + delimited macro parameter isn't matched, and runaways would be extremely + confusing since we are calling on \TeX's scanner in the middle of a + \.{\\shipout} command. Therefore we will put a dummy control sequence as a + ``stopper,'' right after the token list. This control sequence is + artificially defined to be \.{\\outer}. + + The presence of `\.{\\immediate}' causes the |do_extension| procedure to + descend to one level of recursion. Nothing happens unless \.{\\immediate} is + followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'. + +*/ + +/*tex + + The extensions are backend related. The next subroutine uses |cur_chr| to + decide what sort of whatsit is involved, and also inserts a |write_stream| + number. + +*/ + +static void new_write_whatsit(int w, int check) +{ + new_whatsit(cur_chr); + if (check) { + /*tex So we check with open and close. */ + scan_limited_int(last_file_selector,NULL); + } else { + /*tex But we're tolerant with the rest. */ + scan_int(); + if (cur_val < 0) + cur_val = term_only; + else if (cur_val > last_file_selector) { + cur_val = term_and_log; + } + } + write_stream(tail) = cur_val; +} + +void do_extension(int immediate) +{ + /*tex An all-purpose pointer. */ + halfword p; + if (cur_cmd == extension_cmd) { + /*tex These have their own range starting at 0. */ + switch (cur_chr) { + case open_code: + p = tail; + new_write_whatsit(open_node_size,1); + scan_optional_equals(); + /* scan_file_name(); */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + back_input(); + if (cur_cmd != left_brace_cmd) { + scan_file_name(); + } else { + scan_file_name_toks(); + } + /* */ + open_name(tail) = cur_name; + open_area(tail) = cur_area; + open_ext(tail) = cur_ext; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case write_code: + /*tex + + When `\.{\\write 12\{...\}}' appears, we scan the token list + `\.{\{...\}}' without expanding its macros; the macros will + be expanded later when this token list is rescanned. + + */ + p = tail; + new_write_whatsit(write_node_size,0); + cur_cs = write_stream(tail); + scan_toks(false, false); + write_tokens(tail) = def_ref; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case close_code: + p = tail; + new_write_whatsit(close_node_size,1); + write_tokens(tail) = null; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case special_code: + /*tex + + When `\.{\\special\{...\}}' appears, we expand the macros in + the token list as in \.{\\xdef} and \.{\\mark}. + + */ + new_whatsit(special_node); + write_stream(tail) = null; + p = scan_toks(false, true); + write_tokens(tail) = def_ref; + break; + case immediate_code: + get_x_token(); + do_extension(1); + break; + case end_local_code: + if (tracing_nesting_par > 2) { + local_control_message("leaving token scanner"); + } + end_local_control(); + break; + case use_box_resource_code: + case use_image_resource_code: + case save_box_resource_code: + case save_image_resource_code: + switch (get_o_mode()) { + case OMODE_DVI: + do_resource_dvi(immediate,cur_chr); + break; + case OMODE_PDF: + do_resource_pdf(immediate,cur_chr); + break; + default: + break; + } + break; + /*tex Backend extensions have their own range starting at 32. */ + case dvi_extension_code: + if (get_o_mode() == OMODE_DVI) + do_extension_dvi(immediate); + break; + case pdf_extension_code: + if (get_o_mode() == OMODE_PDF) + do_extension_pdf(immediate); + break; + /*tex Done. */ + default: + if (immediate) { + back_input(); + } else { + confusion("invalid extension"); + } + break; + } + } else { + /*tex No extension command, quite certainly following |\immediate|. */ + back_input(); + } +} + +/*tex + + Here is a subroutine that creates a whatsit node having a given |subtype| and + a given number of words. It initializes only the first word of the whatsit, + and appends it to the current list. + +*/ + +void new_whatsit(int s) +{ + halfword p = new_node(whatsit_node, s); + couple_nodes(tail, p); + tail = p; +} + +/*tex + + The final line of this routine is slightly subtle; at least, the author + didn't think about it until getting burnt! There is a used-up token list on + the stack, namely the one that contained |end_write_token|. (We insert this + artificial `\.{\\endwrite}' to prevent runaways, as explained above.) If it + were not removed, and if there were numerous writes on a single page, the + stack would overflow. + +*/ + +void expand_macros_in_tokenlist(halfword p) +{ + int old_mode; + pointer q = get_avail(); + pointer r = get_avail(); + token_info(q) = right_brace_token + '}'; + token_link(q) = r; + token_info(r) = end_write_token; + begin_token_list(q, inserted); + begin_token_list(write_tokens(p), write_text); + q = get_avail(); + token_info(q) = left_brace_token + '{'; + begin_token_list(q, inserted); + /*tex + + Now we're ready to scan `\.\{$\langle\,$token list$\,\rangle$\.{\} + \\endwrite}'. + + */ + old_mode = mode; + mode = 0; + /*tex + + Disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, + \.{\\prevgraf}. + + */ + cur_cs = write_loc; + /*tex Expand macros, etc. */ + q = scan_toks(false, true); + get_token(); + if (cur_tok != end_write_token) { + /*tex Recover from an unbalanced write command */ + const char *hlp[] = { + "On this page there's a \\write with fewer real {'s than }'s.", + "I can't handle that very well; good luck.", + NULL + }; + tex_error("Unbalanced write command", hlp); + do { + get_token(); + } while (cur_tok != end_write_token); + } + mode = old_mode; + /*tex Conserve stack space. */ + end_token_list(); +} + +void write_out(halfword p) +{ + /*tex holds print |selector| */ + int old_setting; + /*tex write stream number */ + int j; + /*tex line to be written, as a Lua string */ + lstring *ss = NULL; + lstring *s = NULL; + int callback_id; + int lua_retval; + expand_macros_in_tokenlist(p); + old_setting = selector; + j = write_stream(p); + if (file_can_be_written(j)) { + selector = j; + } else if ((j == term_only) && (selector == term_and_log)) { + /*tex write to the terminal if file isn't open */ + selector = log_only; + tprint_nl(""); + } else { + tprint_nl(""); + } + s = tokenlist_to_lstring(def_ref, false); + if (selector < no_print) { + /*tex selector is a file */ + callback_id = callback_defined(process_output_buffer_callback); + if (callback_id > 0) { + /*tex fix up the output buffer using callbacks */ + lua_retval = run_callback(callback_id, "L->L", s, &ss); + if ((lua_retval == true) && (ss != NULL)) { + free_lstring(s); + s = ss; + } + } + } + lprint(s); + free_lstring(s); + print_ln(); + flush_list(def_ref); + selector = old_setting; +} + +void finalize_write_files(void) { + int k; + for (k = 0; k <= last_file_selector; k++) { + if (write_open[k]) { + lua_a_close_out(write_file[k]); + } + } +} + +void initialize_write_files(void) { + int k; + for (k = 0; k <= last_file_selector; k++) { + write_open[k] = false; + } +} + +void close_write_file(int id) { + if (write_open[id]) { + lua_a_close_out(write_file[id]); + write_open[id] = false; + } +} + +boolean open_write_file(int id, char *fn) { + if (lua_a_open_out(&(write_file[id]), fn, (id + 1))) { + write_open[id] = true; + return true; + } else { + return false; + } +} + +/*tex + + To implement primitives as \.{\\pdfextension info}, \.{\\pdfextension + catalog} or \.{\\pdfextension names} we need to concatenate tokens lists. + +*/ + +halfword concat_tokens(halfword q, halfword r) +{ + /*tex concat |q| and |r| and returns the result tokens list */ + halfword p; + if (q == null) + return r; + p = q; + while (token_link(p) != null) + p = token_link(p); + set_token_link(p, token_link(r)); + free_avail(r); + return q; +} + +/*tex + + The \eTeX\ features available in extended mode are grouped into two + categories: (1)~Some of them are permanently enabled and have no semantic + effect as long as none of the additional primitives are executed. (2)~The + remaining \eTeX\ features are optional and can be individually enabled and + disabled. For each optional feature there is an \eTeX\ state variable named + \.{\\...state}; the feature is enabled, resp.\ disabled by assigning a + positive, resp.\ non-positive value to that integer. + + In order to handle \.{\\everyeof} we need an array |eof_seen| of boolean + variables. + +*/ + +boolean *eof_seen; + +/*tex + + The |print_group| procedure prints the current level of grouping and the name + corresponding to |cur_group|. + +*/ + +void print_group(boolean e) +{ + switch (cur_group) { + case bottom_level: + tprint("bottom level"); + return; + break; + case simple_group: + case semi_simple_group: + if (cur_group == semi_simple_group) + tprint("semi "); + tprint("simple"); + break;; + case hbox_group: + case adjusted_hbox_group: + if (cur_group == adjusted_hbox_group) + tprint("adjusted "); + tprint("hbox"); + break; + case vbox_group: + tprint("vbox"); + break; + case vtop_group: + tprint("vtop"); + break; + case align_group: + case no_align_group: + if (cur_group == no_align_group) + tprint("no "); + tprint("align"); + break; + case output_group: + tprint("output"); + break; + case disc_group: + tprint("disc"); + break; + case insert_group: + tprint("insert"); + break; + case vcenter_group: + tprint("vcenter"); + break; + case math_group: + case math_choice_group: + case math_shift_group: + case math_left_group: + tprint("math"); + if (cur_group == math_choice_group) + tprint(" choice"); + else if (cur_group == math_shift_group) + tprint(" shift"); + else if (cur_group == math_left_group) + tprint(" left"); + break; + } + tprint(" group (level "); + print_int(cur_level); + print_char(')'); + if (saved_value(-1) != 0) { + /*tex |saved_line| */ + if (e) + tprint(" entered at line "); + else + tprint(" at line "); + print_int(saved_value(-1)); + } +} + +/*tex + + The |group_trace| procedure is called when a new level of grouping begins + (|e=false|) or ends (|e=true|) with |saved_value(-1)| containing the line + number. + +*/ + +void group_trace(boolean e) +{ + begin_diagnostic(); + print_char('{'); + if (e) + tprint("leaving "); + else + tprint("entering "); + print_group(e); + print_char('}'); + end_diagnostic(false); +} + +/*tex + + A group entered (or a conditional started) in one file may end in a different + file. Such slight anomalies, although perfectly legitimate, may cause errors + that are difficult to locate. In order to be able to give a warning message + when such anomalies occur, \eTeX\ uses the |grp_stack| and |if_stack| arrays + to record the initial |cur_boundary| and |cond_ptr| values for each input + file. + +*/ + +/*tex initial |cur_boundary| */ + +save_pointer *grp_stack; + +/*tex initial |cond_ptr| */ + +halfword *if_stack; + +/*tex + + When a group ends that was apparently entered in a different input file, the + |group_warning| procedure is invoked in order to update the |grp_stack|. If + moreover \.{\\tracingnesting} is positive we want to give a warning message. + The situation is, however, somewhat complicated by two facts: (1)~There may + be |grp_stack| elements without a corresponding \.{\\input} file or + \.{\\scantokens} pseudo file (e.g., error insertions from the terminal); and + (2)~the relevant information is recorded in the |name_field| of the + |input_stack| only loosely synchronized with the |in_open| variable indexing + |grp_stack|. + +*/ + +void group_warning(void) +{ + /*tex do we need a warning? */ + boolean w = false; + /*tex index into |grp_stack| */ + int i = in_open; + base_ptr = input_ptr; + /*tex store current state */ + input_stack[base_ptr] = cur_input; + while ((grp_stack[i] == cur_boundary) && (i > 0)) { + /*tex + + Set variable |w| to indicate if this case should be reported. This + code scans the input stack in order to determine the type of the + current input file. + + */ + if (tracing_nesting_par > 0) { + while ((input_stack[base_ptr].state_field == token_list) || (input_stack[base_ptr].index_field > i)) + decr(base_ptr); + if (input_stack[base_ptr].name_field > 17) + w = true; + } + grp_stack[i] = save_value(save_ptr); + decr(i); + } + if (w) { + tprint_nl("Warning: end of "); + print_group(true); + tprint(" of a different file"); + print_ln(); + if (tracing_nesting_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; + } +} + +/*tex + + When a conditional ends that was apparently started in a different input + file, the |if_warning| procedure is invoked in order to update the + |if_stack|. If moreover \.{\\tracingnesting} is positive we want to give a + warning message (with the same complications as above). + +*/ + +void if_warning(void) +{ + /*tex Do we need a warning? */ + boolean w = false; + int i = in_open; + base_ptr = input_ptr; + /*tex Store current state. */ + input_stack[base_ptr] = cur_input; + while (if_stack[i] == cond_ptr) { + /*tex Set variable |w| to. */ + if (tracing_nesting_par > 0) { + while ((input_stack[base_ptr].state_field == token_list) || (input_stack[base_ptr].index_field > i)) + decr(base_ptr); + if (input_stack[base_ptr].name_field > 17) + w = true; + } + if_stack[i] = vlink(cond_ptr); + decr(i); + } + if (w) { + tprint_nl("Warning: end of "); + print_cmd_chr(if_test_cmd, cur_if); + print_if_line(if_line); + tprint(" of a different file"); + print_ln(); + if (tracing_nesting_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; + } +} + +/*tex + + Conversely, the |file_warning| procedure is invoked when a file ends and some + groups entered or conditionals started while reading from that file are still + incomplete. + +*/ + +void file_warning(void) +{ + /*tex saved value of |save_ptr| or |cond_ptr| */ + halfword p = save_ptr; + /*tex saved value of |cur_level| or |if_limit| */ + int l = cur_level; + /*tex saved value of |cur_group| or |cur_if| */ + int c = cur_group; + /*tex saved value of |if_line| */ + int i; + save_ptr = cur_boundary; + while (grp_stack[in_open] != save_ptr) { + decr(cur_level); + tprint_nl("Warning: end of file when "); + print_group(true); + tprint(" is incomplete"); + cur_group = save_level(save_ptr); + save_ptr = save_value(save_ptr); + } + save_ptr = p; + cur_level = (quarterword) l; + /*tex Restore old values. */ + cur_group = (group_code) c; + p = cond_ptr; + l = if_limit; + c = cur_if; + i = if_line; + while (if_stack[in_open] != cond_ptr) { + tprint_nl("Warning: end of file when "); + print_cmd_chr(if_test_cmd, cur_if); + if (if_limit == fi_code) + tprint_esc("else"); + print_if_line(if_line); + tprint(" is incomplete"); + if_line = if_line_field(cond_ptr); + cur_if = if_limit_subtype(cond_ptr); + if_limit = if_limit_type(cond_ptr); + cond_ptr = vlink(cond_ptr); + } + /*tex restore old values */ + cond_ptr = p; + if_limit = l; + cur_if = c; + if_line = i; + print_ln(); + if (tracing_nesting_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; +} + +/*tex The |par_fill_skip| glue node of the new paragraph. */ + +halfword last_line_fill; + +/*tex + + The lua interface needs some extra functions. The functions themselves are + quite boring, but they are handy because otherwise this internal stuff has to + be accessed from C directly, where lots of the defines are not available. + +*/ + +#define get_tex_dimen_register(j) dimen(j) +#define get_tex_skip_register(j) skip(j) +#define get_tex_mu_skip_register(j) mu_skip(j) +#define get_tex_count_register(j) count(j) +#define get_tex_attribute_register(j) attribute(j) +#define get_tex_box_register(j) box(j) + +/*tex These can now be macros (todo). */ + +int get_tex_extension_count_register(int i) +{ + return (int) int_par(backend_int_base-int_base+i); +} + +void set_tex_extension_count_register(int i, int d) +{ + int_par(backend_int_base-int_base+i) = d; +} + +int get_tex_extension_dimen_register(int i) +{ + return (int) dimen_par(backend_dimen_base-dimen_base+i); +} + +void set_tex_extension_dimen_register(int i, int d) +{ + dimen_par(backend_dimen_base-dimen_base+i) = d; +} + +int get_tex_extension_toks_register(int i) +{ + return equiv(backend_toks_base+i); +} + +int set_tex_dimen_register(int j, scaled v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + word_define(j + scaled_base, v); + return 0; +} + +int set_tex_skip_register(int j, halfword v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + if (type(v) != glue_spec_node) + return 1; + word_define(j + skip_base, v); + return 0; +} + +int set_tex_mu_skip_register(int j, halfword v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + if (type(v) != glue_spec_node) + return 1; + word_define(j + mu_skip_base, v); + return 0; +} + +int set_tex_count_register(int j, scaled v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + word_define(j + count_base, v); + return 0; +} + +int set_tex_box_register(int j, scaled v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + define(j + box_base, box_ref_cmd, v); + return 0; +} + +int set_tex_attribute_register(int j, scaled v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + if (j > max_used_attr) + max_used_attr = j; + attr_list_cache = cache_disabled; + word_define(j + attribute_base, v); + return 0; +} + +int get_tex_toks_register(int j) +{ + str_number s = get_nullstr(); + if (toks(j) != null) { + s = tokens_to_string(toks(j)); + } + return s; +} + +int set_tex_toks_register(int j, lstring s) +{ + int a; + halfword ref = get_avail(); + (void) str_toks(s); + set_token_ref_count(ref, 0); + set_token_link(ref, token_link(temp_token_head)); + if (global_defs_par > 0) + a = 4; + else + a = 0; + define(j + toks_base, call_cmd, ref); + return 0; +} + +int scan_tex_toks_register(int j, int c, lstring s) +{ + int a; + halfword ref = get_avail(); + (void) str_scan_toks(c,s); + set_token_ref_count(ref, 0); + set_token_link(ref, token_link(temp_token_head)); + if (global_defs_par > 0) + a = 4; + else + a = 0; + define(j + toks_base, call_cmd, ref); + return 0; +} + +scaled get_tex_box_width(int j) +{ + halfword q = box(j); + if (q != null) + return width(q); + return 0; +} + +int set_tex_box_width(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + width(q) = v; + return 0; +} + +scaled get_tex_box_height(int j) +{ + halfword q = box(j); + if (q != null) + return height(q); + return 0; +} + +int set_tex_box_height(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + height(q) = v; + return 0; +} + +scaled get_tex_box_depth(int j) +{ + halfword q = box(j); + if (q != null) + return depth(q); + return 0; +} + +int set_tex_box_depth(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + depth(q) = v; + return 0; +} + +/*tex + + This section is devoted to the {\sl Synchronize \TeX nology} - or simply {\sl + Sync\TeX} - used to synchronize between input and output. This section + explains how synchronization basics are implemented. Before we enter into + more technical details, let us recall in a few words what is synchronization. + + \TeX\ typesetting system clearly separates the input and the output material, + and synchronization will provide a new link between both that can help text + editors and viewers to work together. More precisely, forwards + synchronization is the ability, given a location in the input source file, to + find what is the corresponding place in the output. Backwards synchronization + just performs the opposite: given a location in the output, retrieve the + corresponding material in the input source file. + + For better code management and maintainance, we adopt a naming convention. + Throughout this program, code related to the {\sl Synchronize \TeX nology} is + tagged with the ``{\sl synctex}'' key word. Any code extract where {\sl + Sync\TeX} plays its part, either explicitly or implicitly, (should) contain + the string ``{\sl synctex}''. This naming convention also holds for external + files. Moreover, all the code related to {\sl Sync\TeX} is gathered in this + section, except the definitions. + + Enabling synchronization should be performed from the command line, + |synctexoption| is used for that purpose. This global integer variable is + declared here but it is not used here. This is just a placeholder where the + command line controller will put the {\sl Sync\TeX} related options, and the + {\sl Sync\TeX} controller will read them. + +*/ + +int synctexoption; + +/*tex + + A convenient primitive is provided: \.{\\synctex=1} in the input source file + enables synchronization whereas \.{\\synctex=0} disables it. Its memory + address is |synctex_code|. It is initialized by the {\sl Sync\TeX} controller + to the command-line option if given. The controller may filter some reserved + bits. + + In order to give the {\sl Sync\TeX} controller read and write access to the + contents of the \.{\\synctex} primitive, we declare |synctexoffset|, such + that |mem[synctexoffset]| and \.{\\synctex} correspond to the same memory + storage. |synctexoffset| is initialized to the correct value when quite + everything is initialized. + +*/ + +/*tex Holds the true value of |synctex_code|: */ + +int synctexoffset; + +/*tex + + Synchronization is achieved with the help of an auxiliary file named `\.{{\sl + jobname}.synctex}' ({\sl jobname} is the contents of the \.{\\jobname} + macro), where a {\sl Sync\TeX} controller implemented in the external + |synctex.c| file will store geometrical information. This {\sl Sync\TeX} + controller will take care of every technical details concerning the {\sl + Sync\TeX} file, we will only focus on the messages the controller will + receive from the \TeX\ program. + + The most accurate synchronization information should allow to map any + character of the input source file to the corresponding location in the + output, if relevant. Ideally, the synchronization information of the input + material consists of the file name, the line and column numbers of every + character. The synchronization information in the output is simply the page + number and either point coordinates, or box dimensions and position. The + problem is that the mapping between these informations is only known at ship + out time, which means that we must keep track of the input synchronization + information until the pages ship out. + + As \TeX\ only knows about file names and line numbers, but forgets the column + numbers, we only consider a restricted input synchronization information + called {\sl Sync\TeX\ information}. It consists of a unique file name + identifier, the {\sl Sync\TeX\ file tag}, and the line number. + + Keeping track of such information, should be different whether characters or + nodes are involved. Actually, only certain nodes are involved in {\sl + Sync\TeX}, we call them {\sl synchronized nodes}. Synchronized nodes store + the {\sl Sync\TeX} information in their last two words: the first one + contains a {\sl Sync\TeX\ file tag} uniquely identifying the input file, and + the second one contains the current line number, as returned by the + \.{\\inputlineno} primitive. The |synctex_field_size| macro contains the + necessary size to store the {\sl Sync\TeX} information in a node. + + When declaring the size of a new node, it is recommanded to use the following + convention: if the node is synchronized, use a definition similar to + |my_synchronized_node_size|={\sl xxx}+|synctex_field_size|. Moreover, one + should expect that the {\sl Sync\TeX} information is always stored in the + last two words of a synchronized node. + + By default, every node with a sufficiently big size is initialized at + creation time in the |get_node| routine with the current {\sl Sync\TeX} + information, whether or not the node is synchronized. One purpose is to set + this information very early in order to minimize code dependencies, including + forthcoming extensions. Another purpose is to avoid the assumption that every + node type has a dedicated getter, where initialization should take place. + Actually, it appears that some nodes are created using directly the + |get_node| routine and not the dedicated constructor. And finally, + initializing the node at only one place is less error prone. + + Instead of storing the input file name, it is better to store just an + identifier. Each time \TeX\ opens a new file, it notifies the {\sl Sync\TeX} + controller with a |synctex_start_input| message. This controller will create + a new {\sl Sync\TeX} file tag and will update the current input state record + accordingly. If the input comes from the terminal or a pseudo file, the + |synctex_tag| is set to 0. It results in automatically disabling + synchronization for material input from the terminal or pseudo files. + + Synchronized nodes are boxes, math, kern and glue nodes. Other nodes should + be synchronized too, in particular math noads. \TeX\ assumes that math, kern + and glue nodes have the same size, this is why both are synchronized. {\sl In + fine}, only horizontal lists are really used in {\sl Sync\TeX}, but all box + nodes are considered the same with respect to synchronization, because a box + node type is allowed to change at execution time. + + {\em Nota Bene:} The {\sl Sync\TeX} code is very close to the memory model. + It is not connected to any other part of the code, except for memory + management. It is possible to neutralize the {\sl Sync\TeX} code rather + simply. The first step is to define a null |synctex_field_size|. The second + step is to comment out the code in ``Initialize bigger nodes...'' and every + ``Copy ... {\sl Sync\TeX} information''. The last step will be to comment out + the |synctex_tag_field| related code in the definition of |synctex_tag| and + the various ``Prepare ... {\sl Sync\TeX} information''. Then all the + remaining code should be just harmless. The resulting program would behave + exactly the same as if absolutely no {\sl Sync\TeX} related code was there, + including memory management. Of course, all this assumes that {\sl Sync\TeX} + is turned off from the command line. + + Here are extra variables for Web2c. (This numbering of the system-dependent + section allows easy integration of Web2c and e-\TeX, etc.) + +*/ + +/*tex where the filename to switch to starts */ + +pool_pointer edit_name_start; + +/*tex what line to start editing at */ + +int edit_name_length, edit_line; + +/*tex whether |more_name| returns false for space */ + +boolean stop_at_space; + +/*tex + + The |edit_name_start| will be set to point into |str_pool| somewhere after + its beginning if \TeX\ is supposed to switch to an editor on exit. + +*/ + +int shellenabledp; +int restrictedshell; +char *output_comment; + +/*tex + + Are we printing extra info as we read the format file? + +*/ + +boolean debug_format_file; + +void wrapup_leader(halfword p) +{ + /*tex Do some work that has been queued up for \.{\\write}. */ + if (!doing_leaders) { + int j = write_stream(p); + if (subtype(p) == write_node) { + write_out(p); + } else if (subtype(p) == close_node) { + close_write_file(j); + } else if (valid_write_file(j)) { + char *fn; + close_write_file(j); + cur_name = open_name(p); + cur_area = open_area(p); + cur_ext = open_ext(p); + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(".tex"); + fn = pack_file_name(cur_name, cur_area, cur_ext); + while (! open_write_file(j,fn)) { + fn = prompt_file_name("output file name", ".tex"); + } + } + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.h b/Build/source/texk/web2c/luatexdir/tex/extensions.h new file mode 100644 index 00000000000..5e22ddf26c3 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/extensions.h @@ -0,0 +1,147 @@ +/* extensions.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef EXTENSIONS_H +# define EXTENSIONS_H + +extern alpha_file write_file[last_file_selector+1]; +extern halfword write_file_mode[last_file_selector+1]; +extern halfword write_file_translation[last_file_selector+1]; +extern boolean write_open[last_file_selector+1]; + +# define valid_write_file(n) ((n>=0) && (n<=last_file_selector)) +# define file_can_be_written(n) (valid_write_file(n) && write_open[n]) + +extern void expand_macros_in_tokenlist(halfword p); +extern void write_out(halfword p); +extern void finalize_write_files(void); +extern void initialize_write_files(void); +extern void close_write_file(int id); +extern boolean open_write_file(int id, char *fn); + +extern scaled neg_wd; +extern scaled pos_wd; +extern scaled neg_ht; + +extern halfword write_loc; + +extern void do_extension(int immediate); + +/* +User defined whatsits can be inserted into node lists to pass data +along from one lua call to another without interference from the +typesetting engine itself. Each has an id, a type, and a value. The +type of the value depends on the |user_node_type| field. +*/ + +extern void new_whatsit(int s); + +extern halfword concat_tokens(halfword q, halfword r); + +/* +The \.{\\pagediscards} and \.{\\splitdiscards} commands share the +command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are +distinguished by their |chr_code| values |last_box_code| and +|vsplit_code|. These |chr_code| values are larger than |box_code| and +|copy_code|. +*/ + +extern boolean *eof_seen; /* has eof been seen? */ +extern void print_group(boolean e); +extern void group_trace(boolean e); +extern save_pointer *grp_stack; /* initial |cur_boundary| */ +extern halfword *if_stack; /* initial |cond_ptr| */ +extern void group_warning(void); +extern void if_warning(void); +extern void file_warning(void); + +extern halfword last_line_fill; /* the |par_fill_skip| glue node of the new paragraph */ + +# define get_tex_dimen_register(j) dimen(j) +# define get_tex_skip_register(j) skip(j) +# define get_tex_mu_skip_register(j) mu_skip(j) +# define get_tex_count_register(j) count(j) +# define get_tex_attribute_register(j) attribute(j) +# define get_tex_box_register(j) box(j) + +extern int get_tex_extension_count_register(int i); +extern void set_tex_extension_count_register(int i, int d); +extern int get_tex_extension_dimen_register(int i); +extern void set_tex_extension_dimen_register(int i, int d); +extern int get_tex_extension_toks_register (int i); + +extern int set_tex_dimen_register(int j, scaled v); +extern int set_tex_skip_register(int j, halfword v); +extern int set_tex_mu_skip_register(int j, halfword v); +extern int set_tex_count_register(int j, scaled v); +extern int set_tex_box_register(int j, scaled v); +extern int set_tex_attribute_register(int j, scaled v); +extern int get_tex_toks_register(int l); +extern int set_tex_toks_register(int j, lstring s); +extern int scan_tex_toks_register(int j, int c, lstring s); +extern scaled get_tex_box_width(int j); +extern int set_tex_box_width(int j, scaled v); +extern scaled get_tex_box_height(int j); +extern int set_tex_box_height(int j, scaled v); +extern scaled get_tex_box_depth(int j); +extern int set_tex_box_depth(int j, scaled v); + +/* Synctex variables */ + +extern int synctexoption; +extern int synctexoffset; + +/* Here are extra variables for Web2c. */ + +extern pool_pointer edit_name_start; +extern int edit_name_length, edit_line; +extern boolean stop_at_space; +extern int shellenabledp; +extern int restrictedshell; +extern char *output_comment; +extern boolean debug_format_file; + +extern int last_saved_box_index ; +extern int last_saved_image_index ; +extern int last_saved_image_pages ; +extern scaledpos last_position ; + +typedef enum { + /* traditional extensions */ + open_code = 0, + write_code, + close_code, + reserved_extension_code, // 3: we moved special below immediate // + reserved_immediate_code, // 4: same number as main codes, expected value // + /* backend specific implementations */ + special_code, + save_box_resource_code, + use_box_resource_code, + save_image_resource_code, + use_image_resource_code, + end_local_code, + /* backend */ + dvi_extension_code, + pdf_extension_code, +} extension_codes ; + +extern void wrapup_leader(halfword p); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.c b/Build/source/texk/web2c/luatexdir/tex/filename.c new file mode 100644 index 00000000000..1e06e877ae8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/filename.c @@ -0,0 +1,383 @@ +/* + +filename.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + + +/*tex + + In order to isolate the system-dependent aspects of file names, the @^system + dependencies@> system-independent parts of \TeX\ are expressed in terms of + three system-dependent procedures called |begin_name|, |more_name|, and + |end_name|. In essence, if the user-specified characters of the file name are + $c_1\ldots c_n$, the system-independent driver program does the operations + $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); + \,|end_name|.$$ + + These three procedures communicate with each other via global variables. + Afterwards the file name will appear in the string pool as three strings + called |cur_name|\penalty10000\hskip-.05em, |cur_area|, and |cur_ext|; the + latter two are null (i.e., |""|), unless they were explicitly specified by + the user. + + Actually the situation is slightly more complicated, because \TeX\ needs to + know when the file name ends. The |more_name| routine is a function (with + side effects) that returns |true| on the calls |more_name|$(c_1)$, \dots, + |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ returns |false|; + or, it returns |true| and the token following $c_n$ is something like + `\.{\\hbox}' (i.e., not a character). In other words, |more_name| is supposed + to return |true| unless it is sure that the file name has been completely + scanned; and |end_name| is supposed to be able to finish the assembly of + |cur_name|, |cur_area|, and |cur_ext| regardless of whether + $|more_name|(c_n)$ returned |true| or |false|. + + + Here now is the first of the system-dependent routines for file name + scanning. @^system dependencies@> + +*/ + +static void begin_name(void) +{ + area_delimiter = 0; + ext_delimiter = 0; + quoted_filename = false; +} + +/*tex + + And here's the second. The string pool might change as the file name is being + scanned, since a new \.{\\csname} might be entered; therefore we keep + |area_delimiter| and |ext_delimiter| relative to the beginning of the current + string, instead of assigning an absolute address like |pool_ptr| to them. + @^system dependencies@> + +*/ + +static boolean more_name(ASCII_code c) +{ + if (c == ' ' && stop_at_space && (!quoted_filename)) { + return false; + } else if (c == '"') { + quoted_filename = !quoted_filename; + return true; + } else { + str_room(1); + append_char(c); + if (IS_DIR_SEP(c)) { + area_delimiter = (pool_pointer) cur_length; + ext_delimiter = 0; + } else if (c == '.') + ext_delimiter = (pool_pointer) cur_length; + return true; + } +} + +/*tex + + The third. @^system dependencies@> + +*/ + +static void end_name(void) +{ + unsigned char *s; + if (str_ptr + 3 > (max_strings + STRING_OFFSET)) + overflow( + "number of strings", + (unsigned) (max_strings - init_str_ptr + STRING_OFFSET) + ); + /*tex At this point, the full string lives in |cur_string|. */ + if (area_delimiter == 0) { + cur_area = get_nullstr(); + } else { + s = (unsigned char *) xstrdup((char *) (cur_string + area_delimiter)); + cur_string[area_delimiter] = '\0'; + cur_length = (unsigned) strlen((char *) cur_string); + cur_area = make_string(); + xfree(cur_string); + cur_length = (unsigned) strlen((char *) s); + cur_string = s; + } + if (ext_delimiter == 0) { + cur_name = make_string(); + cur_ext = get_nullstr(); + } else { + int l = (ext_delimiter - area_delimiter - 1); + s = (unsigned char *) xstrdup((char *) (cur_string + l)); + cur_string[l] = '\0'; + cur_length = (unsigned) strlen((char *) cur_string); + cur_name = make_string(); + xfree(cur_string); + cur_length = (unsigned) strlen((char *) s); + cur_string = s; + cur_ext = make_string(); + } +} + +/*tex + + Now let's consider the ``driver'' routines by which \TeX\ deals with file + names in a system-independent manner. First comes a procedure that looks for a + file name in the input by calling |get_x_token| for the information. + +*/ + +void scan_file_name(void) +{ + str_number u = 0; + name_in_progress = true; + begin_name(); + /*tex Get the next non-blank non-call token: */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + while (true) { + if ((cur_cmd > other_char_cmd) || (cur_chr > biggest_char)) { /* not a character */ + back_input(); + break; + } + /*tex + If |cur_chr| is a space and we're not scanning a token list, check + whether we're at the end of the buffer. Otherwise we end up adding + spurious spaces to file names in some cases. + */ + if ((cur_chr == ' ') && (istate != token_list) && (iloc > ilimit) + && !quoted_filename) + break; + if (cur_chr > 127) { + unsigned char *bytes; + unsigned char *thebytes; + thebytes = uni2str((unsigned) cur_chr); + bytes = thebytes; + while (*bytes) { + if (!more_name(*bytes)) + break; + bytes++; + } + xfree(thebytes); + } else { + if (!more_name(cur_chr)) + break; + } + u = save_cur_string(); + get_x_token(); + restore_cur_string(u); + } + end_name(); + name_in_progress = false; +} + +/* + This function constructs a the three file name strings from a token list. +*/ + +void scan_file_name_toks(void) +{ + char *a, *n, *e, *s = NULL; + int i, l = 0; + int save_scanner_status = scanner_status; + halfword save_def_ref = def_ref; + (void) scan_toks(false, true); + s = tokenlist_to_cstring(def_ref, true, &l); + a = n = s; + e = NULL; + for (i = 0; i < l; i++) { + if (IS_DIR_SEP(s[i])) { + n = s + i + 1; + e = NULL; + } else if (s[i] == '.') { + e = s + i; + } + } + if (n != s) { + /*tex explicit area */ + cur_area = maketexlstring(a, (size_t) (n - a)); + } else { + cur_area = get_nullstr(); + } + if (e != NULL) { + /*tex explicit extension */ + cur_name = maketexlstring(n, (size_t) (e - n)); + cur_ext = maketexstring(e); + } else { + cur_name = maketexstring(n); + cur_ext = get_nullstr(); + } + flush_list(def_ref); + scanner_status = save_scanner_status; + def_ref = save_def_ref; + xfree(s); +} + +/*tex + + Here is a routine that manufactures the output file names, assuming that + |job_name<>0|. It ignores and changes the current settings of |cur_area| and + |cur_ext|; |s = ".log"|, |".dvi"|, or |format_extension| +*/ + +char *pack_job_name(const char *s) +{ + cur_area = get_nullstr(); + cur_ext = maketexstring(s); + cur_name = job_name; + return pack_file_name(cur_name, cur_area, cur_ext); +} + +/*tex + + If some trouble arises when \TeX\ tries to open a file, the following routine + calls upon the user to supply another file name. Parameter~|s| is used in the + error message to identify the type of file; parameter~|e| is the default + extension if none is given. Upon exit from the routine, variables |cur_name|, + |cur_area|, and |cur_ext| are ready for another attempt at file opening. + +*/ + +char *prompt_file_name(const char *s, const char *e) +{ + int k; /* index into |buffer| */ + str_number saved_cur_name; /* to catch empty terminal input */ + int callback_id ; + char prompt[256]; + char *ar, *na, *ex; + saved_cur_name = cur_name; + if (interaction == scroll_mode) { + wake_up_terminal(); + } + ar = makecstring(cur_area); + na = makecstring(cur_name); + ex = makecstring(cur_ext); + if (strcmp(s, "input file name") == 0) { + snprintf(prompt, 255, "I can't find file `%s%s%s'.", ar, na, ex); + } else { + snprintf(prompt, 255, "I can't write on file `%s%s%s'.", ar, na, ex); + } + free(ar); + free(na); + free(ex); + print_err(prompt); + callback_id = callback_defined(show_error_hook_callback); + if (callback_id > 0) { + flush_err(); + run_callback(callback_id, "->"); + } else { + if ((strcmp(e, ".tex") == 0) || (strcmp(e, "") == 0)) + show_context(); + if (strcmp(s, "input file name") == 0) + tprint_nl(promptfilenamehelpmsg ")"); + } + tprint_nl("Please type another "); + tprint(s); + if (interaction < scroll_mode) + fatal_error("*** (job aborted, file error in nonstop mode)"); + clear_terminal(); + prompt_input(": "); + begin_name(); + k = first; + while ((buffer[k] == ' ') && (k < last)) + k++; + while (true) { + if (k == last) + break; + if (!more_name(buffer[k])) + break; + k++; + } + end_name(); + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(e); + if (str_length(cur_name) == 0) + cur_name = saved_cur_name; + return pack_file_name(cur_name, cur_area, cur_ext); +} + +void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e) +{ + boolean must_quote; /* whether to quote the filename */ + unsigned char *j; /* index into string */ + must_quote = false; + if (a != NULL) { + j = a; + while ((!must_quote) && (*j)) { + must_quote = (*j == ' '); + j++; + } + } + if (n != NULL) { + j = n; + while ((!must_quote) && (*j)) { + must_quote = (*j == ' '); + j++; + } + } + if (e != NULL) { + j = e; + while ((!must_quote) && (*j)) { + must_quote = (*j == ' '); + j++; + } + } + /*tex + Alternative is to assume that any filename that has to be quoted has at + least one quoted component...if we pick this, a number of insertions of + |print_file_name| should go away. + */ + if (must_quote) + print_char('"'); + if (a != NULL) { + for (j = a; *j; j++) + if (*j != '"') + print_char(*j); + } + if (n != NULL) { + for (j = n; *j; j++) + if (*j != '"') + print_char(*j); + } + if (e != NULL) { + for (j = e; *j; j++) + if (*j != '"') + print_char(*j); + } + if (must_quote) + print_char('"'); +} + +void print_file_name(str_number n, str_number a, str_number e) +{ + char *nam, *are, *ext; + nam = makecstring(n); + are = makecstring(a); + ext = makecstring(e); + tprint_file_name( + (unsigned char *) nam, + (unsigned char *) are, + (unsigned char *) ext + ); + free(nam); + free(are); + free(ext); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.h b/Build/source/texk/web2c/luatexdir/tex/filename.h new file mode 100644 index 00000000000..cb3d060fd83 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/filename.h @@ -0,0 +1,33 @@ +/* filename.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef FILENAME_H +# define FILENAME_H + +extern void scan_file_name(void); +extern void scan_file_name_toks(void); +extern char *pack_job_name(const char *s); +extern char *prompt_file_name(const char *s, const char *e); +extern void tprint_file_name(unsigned char *n, unsigned char *a, + unsigned char *e); +extern void print_file_name(str_number, str_number, str_number); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/inputstack.c b/Build/source/texk/web2c/luatexdir/tex/inputstack.c new file mode 100644 index 00000000000..6b0e1a055eb --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.c @@ -0,0 +1,960 @@ +/* + +inputstack.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +in_state_record *input_stack = NULL; + +/*tex First unused location of |input_stack|: */ + +int input_ptr = 0; + +/*tex Largest value of |input_ptr| when pushing: */ + +int max_in_stack = 0; + +/*tex The `top' input state: */ + +in_state_record cur_input; + +/*tex The number of lines in the buffer, less one: */ + +int in_open = 0; + +/*tex The number of open text files: */ + +int open_parens = 0; + +alpha_file *input_file = NULL; + +/*tex The current line number in the current source file: */ + +int line = 0; + +int *line_stack = NULL; + +str_number *source_filename_stack = NULL; + +char **full_source_filename_stack = NULL; + +/*tex Can a subfile end now? */ + +int scanner_status = 0; + +/*tex Identifier relevant to non-|normal| scanner status: */ + +pointer warning_index = null; + +/*tex Reference count of token list being defined: */ + +pointer def_ref = null; + +/*tex + +Here is a procedure that uses |scanner_status| to print a warning message when a +subfile has ended, and at certain other crucial times: + +*/ + +void runaway(void) +{ + /*tex The head of the runaway list: */ + pointer p = null; + if (scanner_status > skipping) { + switch (scanner_status) { + case defining: + tprint_nl("Runaway definition"); + p = def_ref; + break; + case matching: + tprint_nl("Runaway argument"); + p = temp_token_head; + break; + case aligning: + tprint_nl("Runaway preamble"); + p = hold_token_head; + break; + case absorbing: + tprint_nl("Runaway text"); + p = def_ref; + break; + default: + /*tex There are no other cases. */ + break; + } + print_char('?'); + print_ln(); + show_token_list(token_link(p), null, error_line - 10); + } +} + +/*tex + +The |param_stack| is an auxiliary array used to hold pointers to the token lists +for parameters at the current level and subsidiary levels of input. This stack is +maintained with convention (2), and it grows at a different rate from the others. + +*/ + +/*tex Token list pointers for parameters: */ + +pointer *param_stack = NULL; + +/*tex First unused entry in |param_stack|: */ + +int param_ptr = 0; + +/*tex Largest value of |param_ptr|, will be |<=param_size+9|: */ + +int max_param_stack = 0; + +/*tex + +The input routines must also interact with the processing of \.{\\halign} and +\.{\\valign}, since the appearance of tab marks and \.{\\cr} in certain places is +supposed to trigger the beginning of special $v_j$ template text in the scanner. +This magic is accomplished by an |align_state| variable that is increased by~1 +when a `\.{\char'173}' is scanned and decreased by~1 when a `\.{\char'175}' is +scanned. The |align_state| is nonzero during the $u_j$ template, after which it +is set to zero; the $v_j$ template begins when a tab mark or \.{\\cr} occurs at a +time that |align_state=0|. + +*/ + +/*tex The group level with respect to current alignment: */ + +int align_state = 0; + +/*tex + +Thus, the ``current input state'' can be very complicated indeed; there can be +many levels and each level can arise in a variety of ways. The |show_context| +procedure, which is used by \TeX's error-reporting routine to print out the +current input state on all levels down to the most recent line of characters from +an input file, illustrates most of these conventions. The global variable +|base_ptr| contains the lowest level that was displayed by this procedure. + +*/ + +/*tex The shallowest level shown by |show_context|: */ + +int base_ptr = 0; + +/*tex + +The status at each level is indicated by printing two lines, where the first line +indicates what was read so far and the second line shows what remains to be read. +The context is cropped, if necessary, so that the first line contains at most +|half_error_line| characters, and the second contains at most |error_line|. +Non-current input levels whose |token_type| is `|backed_up|' are shown only if +they have not been fully read. + +*/ + +static void print_token_list_type(int t) +{ + switch (t) { + case parameter: + tprint_nl("<argument> "); + break; + case u_template: + case v_template: + tprint_nl("<template> "); + break; + case backed_up: + if (iloc == null) + tprint_nl("<recently read> "); + else + tprint_nl("<to be read again> "); + break; + case inserted: + tprint_nl("<inserted text> "); + break; + case macro: + print_ln(); + print_cs(iname); + break; + case output_text: + tprint_nl("<output> "); + break; + case every_par_text: + tprint_nl("<everypar> "); + break; + case every_math_text: + tprint_nl("<everymath> "); + break; + case every_display_text: + tprint_nl("<everydisplay> "); + break; + case every_hbox_text: + tprint_nl("<everyhbox> "); + break; + case every_vbox_text: + tprint_nl("<everyvbox> "); + break; + case every_job_text: + tprint_nl("<everyjob> "); + break; + case every_cr_text: + tprint_nl("<everycr> "); + break; + case mark_text: + tprint_nl("<mark> "); + break; + case every_eof_text: + tprint_nl("<everyeof> "); + break; + case write_text: + tprint_nl("<write> "); + break; + case local_text: + tprint_nl("<local> "); + break; + default: + tprint_nl("?"); + /*tex This should never happen. */ + break; + } +} + +/*tex + +Here it is necessary to explain a little trick. We don't want to store a long +string that corresponds to a token list, because that string might take up lots +of memory; and we are printing during a time when an error message is being +given, so we dare not do anything that might overflow one of \TeX's tables. So +`pseudoprinting' is the answer: We enter a mode of printing that stores +characters into a buffer of length |error_line|, where character $k+1$ is placed +into \hbox{|trick_buf[k mod error_line]|} if |k<trick_count|, otherwise character +|k| is dropped. Initially we set |tally:=0| and |trick_count:=1000000|; then when +we reach the point where transition from line 1 to line 2 should occur, we set +|first_count:=tally| and |trick_count:=@tmax@>(error_line, +tally+1+error_line-half_error_line)|. At the end of the pseudoprinting, the +values of |first_count|, |tally|, and |trick_count| give us all the information +we need to print the two lines, and all of the necessary text is in |trick_buf|. + +Namely, let |l| be the length of the descriptive information that appears on the +first line. The length of the context information gathered for that line is +|k=first_count|, and the length of the context information gathered for line~2 is +$m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|, where |h=half_error_line|, we +print |trick_buf[0..k-1]| after the descriptive information on line~1, and set +|n:=l+k|; here |n| is the length of line~1. If $l+k>h$, some cropping is +necessary, so we set |n:=h| and print `\.{...}' followed by +$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$ where subscripts of |trick_buf| are +circular modulo |error_line|. The second line consists of |n|~spaces followed by +|trick_buf[k..(k+m-1)]|, unless |n+m>error_line|; in the latter case, further +cropping is done. This is easier to program than to explain. + +The following code sets up the print routines so that they will gather the +desired information. + +*/ + +void set_trick_count(void) +{ + first_count = tally; + trick_count = tally + 1 + error_line - half_error_line; + if (trick_count < error_line) + trick_count = error_line; +} + +#define begin_pseudoprint() do { \ + l=tally; \ + tally=0; \ + selector=pseudo; \ + trick_count=1000000; \ + } while (0) + +#define PSEUDO_PRINT_THE_LINE() do { \ + begin_pseudoprint(); \ + if (buffer[ilimit]==end_line_char_par) { \ + j=ilimit; \ + } else { \ + /*tex Determine the effective end of the line. */ \ + j=ilimit+1; \ + } \ + if (j>0) { \ + for (i=istart;i<=j-1;i++) { \ + if (i==iloc) \ + set_trick_count(); \ + print_char(buffer[i]); \ + } \ + } \ +} while (0) + +/*tex + +We don't care too much if we stay a bit too much below the max error_line even if +we have more room on the line. If length is really an issue then any length is. +After all one can set the length larger. + +*/ + +#define print_valid_utf8(q) do { \ + c = (int)trick_buf[q % error_line]; \ + if (c < 128) { \ + print_char(c); \ + } else if (c < 194) { \ + /* invalid */ \ + } else if (c < 224) { \ + print_char(c); \ + print_char(trick_buf[(q+1) % error_line]); \ + } else if (c < 240) { \ + print_char(c); \ + print_char(trick_buf[(q+1) % error_line]); \ + print_char(trick_buf[(q+2) % error_line]); \ + } else if (c < 245) { \ + print_char(c); \ + print_char(trick_buf[(q+1) % error_line]); \ + print_char(trick_buf[(q+2) % error_line]); \ + print_char(trick_buf[(q+3) % error_line]); \ + } else { \ + /*tex Invalid character! */ \ + } \ +} while (0) + +/*tex + +This one prints where the scanner is. + +*/ + +void show_context(void) +{ + /*tex Saved |selector| setting: */ + int old_setting; + /*tex Number of contexts shown so far, less one: */ + int nn = -1; + /*tex Have we reached the final context to be shown? */ + boolean bottom_line = false; + /*tex Index into |buffer|: */ + int i; + /*tex End of current line in |buffer|: */ + int j; + /*tex Length of descriptive information on line 1: */ + int l; + /*tex Context information gathered for line 2: */ + int m; + /*tex Length of line 1: */ + int n; + /*tex Starting or ending place in |trick_buf|: */ + int p; + /*tex Temporary index: */ + int q; + /*tex Used in sanitizer: */ + int c; + base_ptr = input_ptr; + input_stack[base_ptr] = cur_input; + /*tex Store the current state. */ + while (true) { + /*tex Enter into the context. */ + cur_input = input_stack[base_ptr]; + if (istate != token_list) { + if ((iname > 21) || (base_ptr == 0)) + bottom_line = true; + } + if ((base_ptr == input_ptr) || bottom_line || (nn < error_context_lines_par)) { + /*tex Display the current context. */ + if ((base_ptr == input_ptr) || (istate != token_list) || (token_type != backed_up) || (iloc != null)) { + /*tex + We omit backed-up token lists that have already been read. + Get ready to count characters. + */ + tally = 0; + old_setting = selector; + if (istate != token_list) { + /*tex + Print the location of the current line. This routine + should be changed, if necessary, to give the best + possible indication of where the current line resides in + the input file. For example, on some systems it is best + to print both a page and line number. + */ + if (iname <= 17) { + if (terminal_input) { + if (base_ptr == 0) + tprint_nl("<*>"); + else + tprint_nl("<insert> "); + } else { + tprint_nl("<read "); + if (iname == 17) + print_char('*'); + else + print_int(iname - 1); + print_char('>'); + }; + } else { + tprint_nl("l."); + if (iindex == in_open) { + print_int(line); + } else { + /*tex Input from a pseudo file. */ + print_int(line_stack[iindex + 1]); + } + } + print_char(' '); + PSEUDO_PRINT_THE_LINE(); + } else { + print_token_list_type(token_type); + + begin_pseudoprint(); + if (token_type < macro) { + show_token_list(istart, iloc, 100000); + } else { + /*tex Avoid reference count. */ + show_token_list(token_link(istart), iloc, 100000); + } + } + /*tex Stop pseudoprinting. */ + selector = old_setting; + /*tex Print two lines using the tricky pseudoprinted information. */ + if (trick_count == 1000000) { + set_trick_count(); + } + /*tex The |set_trick_count| must be performed. */ + if (tally < trick_count) { + m = tally - first_count; + } else { + /* The context on line 2: */ + m = trick_count - first_count; + } + if (l + first_count <= half_error_line) { + p = 0; + n = l + first_count; + } else { + tprint("..."); + p = l + first_count - half_error_line + 3; + n = half_error_line; + } + for (q = p; q <= first_count - 1; q++) { + print_valid_utf8(q); + } + print_ln(); + /*tex Print |n| spaces to begin line 2. */ + for (q = 1; q <= n; q++) { + print_char(' '); + } + if (m + n <= error_line) + p = first_count + m; + else + p = first_count + (error_line - n - 3); + for (q = first_count; q <= p - 1; q++) + print_valid_utf8(q); + if (m + n > error_line) + tprint("..."); + incr(nn); + } + } else if (nn == error_context_lines_par) { + tprint_nl("..."); + incr(nn); + /*tex Omitted if |error_context_lines_par<0|. */ + } + if (bottom_line) + break; + decr(base_ptr); + } + /*tex Restore the original state. */ + cur_input = input_stack[input_ptr]; +} + +/*tex + +The following subroutines change the input status in commonly needed ways. + +First comes |push_input|, which stores the current state and creates a +new level (having, initially, the same properties as the old). + +Enter a new input level, save the old: + +*/ + +# define pop_input() \ + cur_input=input_stack[--input_ptr] + +# define push_input() \ + if (input_ptr > max_in_stack) { \ + max_in_stack = input_ptr; \ + if (input_ptr == stack_size) \ + overflow("input stack size", (unsigned) stack_size); \ + } \ + input_stack[input_ptr] = cur_input; \ + nofilter = false; \ + incr(input_ptr); + +/*tex + +Here is a procedure that starts a new level of token-list input, given a token +list |p| and its type |t|. If |t=macro|, the calling routine should set |name| +and |loc|. + +*/ + +void begin_token_list(halfword p, quarterword t) +{ + push_input(); + istate = token_list; + istart = p; + token_type = (unsigned char) t; + if (t >= macro) { + /*tex The token list starts with a reference count. */ + add_token_ref(p); + if (t == macro) { + param_start = param_ptr; + } else { + iloc = token_link(p); + if (tracing_macros_par > 1) { + begin_diagnostic(); + // tprint_nl(""); + print_input_level(); + if (t == mark_text) + tprint_esc("mark"); + else if (t == write_text) + tprint_esc("write"); + else + print_cmd_chr(assign_toks_cmd, t - output_text + output_routine_loc); + tprint("->"); + token_show(p); + end_diagnostic(false); + } + } + } else { + iloc = p; + } +} + +/*tex + +When a token list has been fully scanned, the following computations should be +done as we leave that level of input. The |token_type| tends to be equal to +either |backed_up| or |inserted| about 2/3 of the time. @^inner loop@> + +*/ + +void end_token_list(void) +{ + /*tex Leave a token-list input level: */ + if (token_type >= backed_up) { + /*tex The token list to be deleted: */ + if (token_type <= inserted) { + flush_list(istart); + } else { + /*tex Update the reference count: */ + delete_token_ref(istart); + if (token_type == macro) { + /*tex Parameters must be flushed: */ + while (param_ptr > param_start) { + decr(param_ptr); + flush_list(param_stack[param_ptr]); + } + } + } + } else if (token_type == u_template) { + if (align_state > 500000) + align_state = 0; + else + fatal_error("(interwoven alignment preambles are not allowed)"); + } + pop_input(); + check_interrupt(); +} + +/*tex + +Sometimes \TeX\ has read too far and wants to ``unscan'' what it has seen. The +|back_input| procedure takes care of this by putting the token just scanned back +into the input stream, ready to be read again. This procedure can be used only if +|cur_tok| represents the token to be replaced. Some applications of \TeX\ use +this procedure a lot, so it has been slightly optimized for speed. @^inner loop@> + +*/ + +/* undoes one token of input */ + +void back_input(void) +{ + /*tex A token list of length one: */ + halfword p; + while ((istate == token_list) && (iloc == null) && (token_type != v_template)) { + /*tex Conserve stack space. */ + end_token_list(); + } + p = get_avail(); + set_token_info(p, cur_tok); + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) + decr(align_state); + else + incr(align_state); + } + push_input(); + /*tex This is |back_list(p)|, without procedure overhead: */ + istate = token_list; + istart = p; + token_type = backed_up; + iloc = p; +} + +/*tex + +Insert token |p| into \TeX's input + +*/ + +void reinsert_token(boolean a, halfword pp) +{ + halfword t; + t = cur_tok; + cur_tok = pp; + if (a) { + halfword p; + p = get_avail(); + set_token_info(p, cur_tok); + set_token_link(p, iloc); + iloc = p; + istart = p; + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) + decr(align_state); + else + incr(align_state); + } + } else { + back_input(); + } + cur_tok = t; +} + +/*tex + +The |begin_file_reading| procedure starts a new level of input for lines of +characters to be read from a file, or as an insertion from the terminal. It does +not take care of opening the file, nor does it set |loc| or |limit| or |line|. +@^system dependencies@> + +*/ + +void begin_file_reading(void) +{ + if (in_open == max_in_open) + overflow("text input levels", (unsigned) max_in_open); + if (first == buf_size) + check_buffer_overflow(first); + incr(in_open); + push_input(); + iindex = (unsigned short)(in_open); + source_filename_stack[iindex] = 0; + full_source_filename_stack[iindex] = NULL; + eof_seen[iindex] = false; + grp_stack[iindex] = cur_boundary; + if_stack[iindex] = cond_ptr; + line_stack[iindex] = line; + istart = first; + istate = mid_line; + iname = 0; + /*tex Variable |terminal_input| is now |true|. */ + line_catcode_table = DEFAULT_CAT_TABLE; + line_partial = false; + /*tex Prepare terminal input \SYNCTEX\ information. */ + synctex_tag = 0; +} + +/*tex + +Conversely, the variables must be downdated when such a level of input is +finished: + +*/ + +void end_file_reading(void) +{ + first = istart; + line = line_stack[iindex]; + if ((iname >= 18) && (iname <= 20)) { + pseudo_close(); + } else if (iname == 21) { + luacstring_close(iindex); + } else if (iname > 17) { + /*tex Forget it. */ + lua_a_close_in(cur_file, 0); + source_filename_stack[iindex] = 0; + if (full_source_filename_stack[iindex] != NULL) { + free(full_source_filename_stack[iindex]); + full_source_filename_stack[iindex] = NULL; + } + } + pop_input(); + decr(in_open); +} + +/*tex + +In order to keep the stack from overflowing during a long sequence of +inserted `\.{\\show}' commands, the following routine removes completed +error-inserted lines from memory. + +*/ +void clear_for_error_prompt(void) +{ + while ((istate != token_list) && terminal_input && (input_ptr > 0) && (iloc > ilimit)) { + end_file_reading(); + } + print_ln(); + clear_terminal(); +} + +/*tex + +To get \TeX's whole input mechanism going, we perform the following actions. + +*/ + +void initialize_inputstack(void) +{ + input_ptr = 0; + max_in_stack = 0; + source_filename_stack[0] = 0; + + full_source_filename_stack[0] = NULL; + in_open = 0; + open_parens = 0; + max_buf_stack = 0; + + grp_stack[0] = 0; + if_stack[0] = null; + param_ptr = 0; + max_param_stack = 0; + first = buf_size; + do { + buffer[first] = 0; + decr(first); + } while (first != 0); + scanner_status = normal; + warning_index = null; + first = 1; + istate = new_line; + istart = 1; + iindex = 0; + line = 0; + iname = 0; + nofilter = false; + force_eof = false; + luacstrings = 0; + line_catcode_table = DEFAULT_CAT_TABLE; + line_partial = false; + align_state = 1000000; + if (!init_terminal()) { + /*tex |goto final_end|; */ + exit(EXIT_FAILURE); + } + /* |init_terminal| has set |loc| and |last| */ + ilimit = last; + first = last + 1; +} + +/*tex + +The global variable |pseudo_files| is used to maintain a stack of pseudo files. +The |pseudo_lines| field of each pseudo file points to a linked list of variable +size nodes representing lines not yet processed: the |subtype| field contains the +size of this node, all the following words contain ASCII codes. + +If this is really critical code (which it isn't) then we can consider a c stack +and store a pointer to a line in the line node instead which saves splitting here +and reconstructing later. + +*/ + +halfword pseudo_files; + +static halfword string_to_pseudo(str_number str, int nl) +{ + halfword i, r, q = null; + unsigned l, len; + four_quarters w; + int sz; + halfword h = new_node(pseudo_file_node, 0); + unsigned char *s = str_string(str); + len = (unsigned) str_length(str); + l = 0; + while (l < len) { + /*tex start of current line */ + unsigned m = l; + while ((l < len) && (s[l] != nl)) + l++; + sz = (int) (l - m + 7) / 4; + if (sz == 1) + sz = 2; + r = new_node(pseudo_line_node, sz); + i = r; + while (--sz > 1) { + w.b0 = s[m++]; + w.b1 = s[m++]; + w.b2 = s[m++]; + w.b3 = s[m++]; + varmem[++i].qqqq = w; + } + w.b0 = (quarterword) (l > m ? s[m++] : ' '); + w.b1 = (quarterword) (l > m ? s[m++] : ' '); + w.b2 = (quarterword) (l > m ? s[m++] : ' '); + w.b3 = (quarterword) (l > m ? s[m] : ' '); + varmem[++i].qqqq = w; + if (q == null) { + pseudo_lines(h) = r; + } else { + /*tex There is no |prev| node here so no need to couple_nodes! */ + vlink(q) = r ; + } + q = r ; + if (s[l] == nl) + l++; + } + return h; +} + +/*tex + +The |pseudo_start| procedure initiates reading from a pseudo file. + +*/ + +void pseudo_from_string(void) +{ + /*tex The string to be converted into a pseudo file: */ + str_number s; + /*tex A helper for list construction: */ + halfword p; + s = make_string(); + /*tex Convert string |s| into a new pseudo file */ + p = string_to_pseudo(s, new_line_char_par); + vlink(p) = pseudo_files; + pseudo_files = p; + flush_str(s); + /*tex + Initiate input from new pseudo file. It sets up |cur_file| and a new level + of input + */ + begin_file_reading(); + line = 0; + ilimit = istart; + /*tex force line read */ + iloc = ilimit + 1; + if (tracing_scan_tokens_par > 0) { + if (term_offset > max_print_line - 3) + print_ln(); + else if ((term_offset > 0) || (file_offset > 0)) + print_char(' '); + iname = 20; + tprint("( "); + incr(open_parens); + update_terminal(); + } else { + iname = 18; + } + /*tex Prepare pseudo file \SYNCTEX\ information. */ + synctex_tag = 0; +} + +void pseudo_start(void) +{ + int old_setting; + scan_general_text(); + old_setting = selector; + selector = new_string; + token_show(temp_token_head); + selector = old_setting; + flush_list(token_link(temp_token_head)); + str_room(1); + pseudo_from_string(); +} + +void lua_string_start(void) +{ + /*tex Set up |cur_file| and a new level of input: */ + begin_file_reading(); + line = 0; + ilimit = istart; + /*tex Force line read: */ + iloc = ilimit + 1; + iname = 21; + luacstring_start(iindex); +} + +/*tex + +Here we read a line from the current pseudo file into |buffer|. +It inputs the next line or returns |false|. + +*/ + +boolean pseudo_input(void) +{ + /*tex The current line from pseudo file: */ + halfword p; + /*tex The size of node |p|: */ + int sz; + /*tex Four ASCII codes: */ + four_quarters w; + /*tex The loop index: */ + halfword r; + /*tex cf.\ Matthew 19\thinspace:\thinspace30 */ + last = first; + p = pseudo_lines(pseudo_files); + if (p == null) { + return false; + } else { + pseudo_lines(pseudo_files) = vlink(p); + sz = subtype(p); + if (4 * sz - 3 >= buf_size - last) + check_buffer_overflow(last + 4 * sz); + last = first; + for (r = p + 1; r <= p + sz - 1; r++) { + w = varmem[r].qqqq; + buffer[last] = (packed_ASCII_code) w.b0; + buffer[last + 1] = (packed_ASCII_code) w.b1; + buffer[last + 2] = (packed_ASCII_code) w.b2; + buffer[last + 3] = (packed_ASCII_code) w.b3; + last += 4; + } + if (last >= max_buf_stack) + max_buf_stack = last + 1; + while ((last > first) && (buffer[last - 1] == ' ')) + decr(last); + flush_node(p); + } + return true; +} + +/*tex + +When we are done with a pseudo file we `close' it. + +*/ + +void pseudo_close(void) +{ + halfword p; + p = vlink(pseudo_files); + flush_node(pseudo_files); + pseudo_files = p; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/inputstack.h b/Build/source/texk/web2c/luatexdir/tex/inputstack.h new file mode 100644 index 00000000000..0c7669ff3c8 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.h @@ -0,0 +1,344 @@ +/* inputstack.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef INPUTSTACK_H +# define INPUTSTACK_H 1 + +/* + The state of \TeX's input mechanism appears in the input stack, whose + entries are records with six fields, called |state|, |index|, |start|, |loc|, + |limit|, and |name|. +*/ + +typedef struct in_state_record { + halfword start_field; + halfword loc_field; + halfword limit_field; + halfword name_field; + int synctex_tag_field; /* stack the tag of the current file */ + unsigned short state_field:16; + unsigned short index_field:16; + signed short cattable_field:16; /* category table used by the current line (see textoken.c) */ + boolean partial_field:8; /* is the current line partial? (see textoken.c) */ + boolean nofilter_field:8; /* used by token filtering */ +} in_state_record; + +extern in_state_record *input_stack; +extern int input_ptr; +extern int max_in_stack; +extern in_state_record cur_input; /* the ``top'' input state */ + +# define iloc cur_input.loc_field /* location of first unread character in |buffer| */ +# define istate cur_input.state_field /* current scanner state */ +# define iindex cur_input.index_field /* reference for buffer information */ +# define istart cur_input.start_field /* starting position in |buffer| */ +# define ilimit cur_input.limit_field /* end of current line in |buffer| */ +# define iname cur_input.name_field /* name of the current file */ +# define nofilter cur_input.nofilter_field /* is token filtering explicitly disallowed? */ +# define synctex_tag cur_input.synctex_tag_field /* tag of the current file */ +# define line_catcode_table cur_input.cattable_field +# define line_partial cur_input.partial_field + +/* +Let's look more closely now at the control variables +(|state|,~|index|,~|start|,~|loc|,~|limit|,~|name|), +assuming that \TeX\ is reading a line of characters that have been input +from some file or from the user's terminal. There is an array called +|buffer| that acts as a stack of all lines of characters that are +currently being read from files, including all lines on subsidiary +levels of the input stack that are not yet completed. \TeX\ will return to +the other lines when it is finished with the present input file. + +(Incidentally, on a machine with byte-oriented addressing, it might be +appropriate to combine |buffer| with the |str_pool| array, +letting the buffer entries grow downward from the top of the string pool +and checking that these two tables don't bump into each other.) + +The line we are currently working on begins in position |start| of the +buffer; the next character we are about to read is |buffer[loc]|; and +|limit| is the location of the last character present. If |loc>limit|, +the line has been completely read. Usually |buffer[limit]| is the +|end_line_char|, denoting the end of a line, but this is not +true if the current line is an insertion that was entered on the user's +terminal in response to an error message. + +The |name| variable is a string number that designates the name of +the current file, if we are reading a text file. It is zero if we +are reading from the terminal; it is |n+1| if we are reading from +input stream |n|, where |0<=n<=16|. (Input stream 16 stands for +an invalid stream number; in such cases the input is actually from +the terminal, under control of the procedure |read_toks|.) +Finally |18<=name<=20| indicates that we are reading a pseudo file +created by the \.{\\scantokens} or \.{\\scantextokens} command. + +The |state| variable has one of three values, when we are scanning such +files: +$$\baselineskip 15pt\vbox{\halign{#\hfil\cr +1) |state=mid_line| is the normal state.\cr +2) |state=skip_blanks| is like |mid_line|, but blanks are ignored.\cr +3) |state=new_line| is the state at the beginning of a line.\cr}}$$ +These state values are assigned numeric codes so that if we add the state +code to the next character's command code, we get distinct values. For +example, `|mid_line+spacer|' stands for the case that a blank +space character occurs in the middle of a line when it is not being +ignored; after this case is processed, the next value of |state| will +be |skip_blanks|. +*/ + +# define max_char_code 15 /* largest catcode for individual characters */ + +typedef enum { + mid_line = 1, /* |state| code when scanning a line of characters */ + skip_blanks = 2 + max_char_code, /* |state| code when ignoring blanks */ + new_line = 3 + max_char_code + max_char_code, /* |state| code at start of line */ +} state_codes; + +/* +Additional information about the current line is available via the +|index| variable, which counts how many lines of characters are present +in the buffer below the current level. We have |index=0| when reading +from the terminal and prompting the user for each line; then if the user types, +e.g., `\.{\\input paper}', we will have |index=1| while reading +the file \.{paper.tex}. However, it does not follow that |index| is the +same as the input stack pointer, since many of the levels on the input +stack may come from token lists. For example, the instruction `\.{\\input +paper}' might occur in a token list. + +The global variable |in_open| is equal to the |index| +value of the highest non-token-list level. Thus, the number of partially read +lines in the buffer is |in_open+1|, and we have |in_open=index| +when we are not reading a token list. + +If we are not currently reading from the terminal, or from an input +stream, we are reading from the file variable |input_file[index]|. We use +the notation |terminal_input| as a convenient abbreviation for |name=0|, +and |cur_file| as an abbreviation for |input_file[index]|. + +The global variable |line| contains the line number in the topmost +open file, for use in error messages. If we are not reading from +the terminal, |line_stack[index]| holds the line number for the +enclosing level, so that |line| can be restored when the current +file has been read. Line numbers should never be negative, since the +negative of the current line number is used to identify the user's output +routine in the |mode_line| field of the semantic nest entries. + +If more information about the input state is needed, it can be +included in small arrays like those shown here. For example, +the current page or segment number in the input file might be +put into a variable |@!page|, maintained for enclosing levels in +`\ignorespaces|@!page_stack:array[1..max_in_open] of integer|\unskip' +by analogy with |line_stack|. +@^system dependencies@> +*/ + +# define terminal_input (iname==0) /* are we reading from the terminal? */ +# define cur_file input_file[iindex] /* the current |alpha_file| variable */ + +extern int in_open; +extern int open_parens; +extern alpha_file *input_file; +extern int line; +extern int *line_stack; +extern str_number *source_filename_stack; +extern char **full_source_filename_stack; + +/* +Users of \TeX\ sometimes forget to balance left and right braces properly, +and one of the ways \TeX\ tries to spot such errors is by considering an +input file as broken into subfiles by control sequences that +are declared to be \.{\\outer}. + +A variable called |scanner_status| tells \TeX\ whether or not to complain +when a subfile ends. This variable has six possible values: + +\yskip\hang|normal|, means that a subfile can safely end here without incident. + +\yskip\hang|skipping|, means that a subfile can safely end here, but not a file, +because we're reading past some conditional text that was not selected. + +\yskip\hang|defining|, means that a subfile shouldn't end now because a +macro is being defined. + +\yskip\hang|matching|, means that a subfile shouldn't end now because a +macro is being used and we are searching for the end of its arguments. + +\yskip\hang|aligning|, means that a subfile shouldn't end now because we are +not finished with the preamble of an \.{\\halign} or \.{\\valign}. + +\yskip\hang|absorbing|, means that a subfile shouldn't end now because we are +reading a balanced token list for \.{\\message}, \.{\\write}, etc. + +\yskip\noindent +If the |scanner_status| is not |normal|, the variable |warning_index| points +to the |eqtb| location for the relevant control sequence name to print +in an error message. +*/ + +typedef enum { + skipping = 1, /* |scanner_status| when passing conditional text */ + defining = 2, /* |scanner_status| when reading a macro definition */ + matching = 3, /* |scanner_status| when reading macro arguments */ + aligning = 4, /* |scanner_status| when reading an alignment preamble */ + absorbing = 5, /* |scanner_status| when reading a balanced text */ +} scanner_states; + +extern int scanner_status; +extern pointer warning_index; +extern pointer def_ref; + +extern void runaway(void); + +/* +However, the discussion about input state really applies only to the +case that we are inputting from a file. There is another important case, +namely when we are currently getting input from a token list. In this case +|state=token_list|, and the conventions about the other state variables +are different: + +\yskip\hang|loc| is a pointer to the current node in the token list, i.e., +the node that will be read next. If |loc=null|, the token list has been +fully read. + +\yskip\hang|start| points to the first node of the token list; this node +may or may not contain a reference count, depending on the type of token +list involved. + +\yskip\hang|token_type|, which takes the place of |index| in the +discussion above, is a code number that explains what kind of token list +is being scanned. + +\yskip\hang|name| points to the |eqtb| address of the control sequence +being expanded, if the current token list is a macro. + +\yskip\hang|param_start|, which takes the place of |limit|, tells where +the parameters of the current macro begin in the |param_stack|, if the +current token list is a macro. + +\yskip\noindent The |token_type| can take several values, depending on +where the current token list came from: + +\yskip\hang|parameter|, if a parameter is being scanned; + +\hang|u_template|, if the \<u_j> part of an alignment +template is being scanned; + +\hang|v_template|, if the \<v_j> part of an alignment +template is being scanned; + +\hang|backed_up|, if the token list being scanned has been inserted as +`to be read again'. + +\hang|inserted|, if the token list being scanned has been inserted as +the text expansion of a \.{\\count} or similar variable; + +\hang|macro|, if a user-defined control sequence is being scanned; + +\hang|output_text|, if an \.{\\output} routine is being scanned; + +\hang|every_par_text|, if the text of \.{\\everypar} is being scanned; + +\hang|every_math_text|, if the text of \.{\\everymath} is being scanned; + +\hang|every_display_text|, if the text of \.{\\everydisplay} is being scanned; + +\hang|every_hbox_text|, if the text of \.{\\everyhbox} is being scanned; + +\hang|every_vbox_text|, if the text of \.{\\everyvbox} is being scanned; + +\hang|every_job_text|, if the text of \.{\\everyjob} is being scanned; + +\hang|every_cr_text|, if the text of \.{\\everycr} is being scanned; + +\hang|mark_text|, if the text of a \.{\\mark} is being scanned; + +\hang|write_text|, if the text of a \.{\\write} is being scanned. + +\yskip\noindent +The codes for |output_text|, |every_par_text|, etc., are equal to a constant +plus the corresponding codes for token list parameters |output_routine_loc|, +|every_par_loc|, etc. + +The token list begins with a reference count if and +only if |token_type>=macro|. +@^reference counts@> + +Since \eTeX's additional token list parameters precede |toks_base|, the +corresponding token types must precede |write_text|. +*/ + +# define token_list 0 /* |state| code when scanning a token list */ +# define token_type iindex /* type of current token list */ +# define param_start ilimit /* base of macro parameters in |param_stack| */ + + +typedef enum { + parameter = 0, /* |token_type| code for parameter */ + u_template = 1, /* |token_type| code for \<u_j> template */ + v_template = 2, /* |token_type| code for \<v_j> template */ + backed_up = 3, /* |token_type| code for text to be reread */ + inserted = 4, /* |token_type| code for inserted texts */ + macro = 5, /* |token_type| code for defined control sequences */ + output_text = 6, /* |token_type| code for output routines */ + every_par_text = 7, /* |token_type| code for \.{\\everypar} */ + every_math_text = 8, /* |token_type| code for \.{\\everymath} */ + every_display_text = 9, /* |token_type| code for \.{\\everydisplay} */ + every_hbox_text = 10, /* |token_type| code for \.{\\everyhbox} */ + every_vbox_text = 11, /* |token_type| code for \.{\\everyvbox} */ + every_job_text = 12, /* |token_type| code for \.{\\everyjob} */ + every_cr_text = 13, /* |token_type| code for \.{\\everycr} */ + mark_text = 14, /* |token_type| code for \.{\\topmark}, etc. */ + every_eof_text = 15, /* |token_type| code for \.{\\everyeof} */ + write_text = 16, /* |token_type| code for \.{\\write} */ + local_text = 17, /* |token_type| code for special purposed */ +} token_types; + +extern pointer *param_stack; +extern int param_ptr; +extern int max_param_stack; + +extern int align_state; + +extern int base_ptr; + +extern void show_context(void); +extern void set_trick_count(void); + +# define back_list(A) begin_token_list(A,backed_up) /* backs up a simple token list */ +# define ins_list(A) begin_token_list(A,inserted) /* inserts a simple token list */ + +extern void begin_token_list(halfword p, quarterword t); +extern void end_token_list(void); +extern void back_input(void); +extern void reinsert_token(boolean a, halfword pp); +extern void begin_file_reading(void); +extern void end_file_reading(void); +extern void clear_for_error_prompt(void); + +extern void initialize_inputstack(void); + +extern halfword pseudo_files; +extern void pseudo_from_string(void); +extern void pseudo_start(void); +extern void lua_string_start(void); +extern boolean pseudo_input(void); +extern void pseudo_close(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.c b/Build/source/texk/web2c/luatexdir/tex/linebreak.c new file mode 100644 index 00000000000..646a5a771f7 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.c @@ -0,0 +1,2537 @@ +/* + +Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + + We come now to what is probably the most interesting algorithm of \TeX: the + mechanism for choosing the ``best possible'' breakpoints that yield the + individual lines of a paragraph. \TeX's line-breaking algorithm takes a given + horizontal list and converts it to a sequence of boxes that are appended to + the current vertical list. In the course of doing this, it creates a special + data structure containing three kinds of records that are not used elsewhere + in \TeX. Such nodes are created while a paragraph is being processed, and + they are destroyed afterwards; thus, the other parts of \TeX\ do not need to + know anything about how line-breaking is done. + + The method used here is based on an approach devised by Michael F. Plass and + the author in 1977, subsequently generalized and improved by the same two + people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice + \AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the + line-breaking problem can be regarded as a special case of the problem of + computing the shortest path in an acyclic network. The cited paper includes + numerous examples and describes the history of line breaking as it has been + practiced by printers through the ages. The present implementation adds two + new ideas to the algorithm of 1980: Memory space requirements are + considerably reduced by using smaller records for inactive nodes than for + active ones, and arithmetic overflow is avoided by using ``delta distances'' + instead of keeping track of the total distance from the beginning of the + paragraph to the current point. + + The |line_break| procedure should be invoked only in horizontal mode; it + leaves that mode and places its output into the current vlist of the + enclosing vertical mode (or internal vertical mode). There is one explicit + parameter: |d| is true for partial paragraphs preceding display math mode; in + this case the amount of additional penalty inserted before the final line is + |display_widow_penalty| instead of |widow_penalty|. + + There are also a number of implicit parameters: The hlist to be broken starts + at |vlink(head)|, and it is nonempty. The value of |prev_graf| in the + enclosing semantic level tells where the paragraph should begin in the + sequence of line numbers, in case hanging indentation or \.{\\parshape} are + in use; |prev_graf| is zero unless this paragraph is being continued after a + displayed formula. Other implicit parameters, such as the |par_shape_ptr| and + various penalties to use for hyphenation, etc., appear in |eqtb|. + + After |line_break| has acted, it will have updated the current vlist and the + value of |prev_graf|. Furthermore, the global variable |just_box| will point + to the final box created by |line_break|, so that the width of this line can + be ascertained when it is necessary to decide whether to use + |above_display_skip| or |above_display_short_skip| before a displayed + formula. + +*/ + +/*tex The |hlist_node| for the last line of the new paragraph: */ + +halfword just_box; + +/*tex + + In it's complete form, |line_break| is a rather lengthy procedure---sort of a + small world unto itself---we must build it up little by little. Below you see + only the general outline. + + The main task performed here is to move the list from |head| to |temp_head| + and go into the enclosing semantic level. We also append the + \.{\\parfillskip} glue to the end of the paragraph, removing a space (or + other glue node) if it was there, since spaces usually precede blank lines + and instances of `\.{\$\$}'. The |par_fill_skip| is preceded by an infinite + penalty, so it will never be considered as a potential breakpoint. + + That code assumes that a |glue_node| and a |penalty_node| occupy the same + number of |mem|~words. + + Most other processing is delegated to external functions. + +*/ + +void line_break(boolean d, int line_break_context) +{ + /*tex Main direction of paragraph: */ + int paragraph_dir = 0; + halfword final_par_glue; + halfword start_of_par; + int callback_id; + /*tex this is for over/underfull box messages */ + pack_begin_line = cur_list.ml_field; + alink(temp_head) = null; + vlink(temp_head) = vlink(cur_list.head_field); + new_hyphenation(temp_head, cur_list.tail_field); + cur_list.tail_field = new_ligkern(temp_head, cur_list.tail_field); + if (is_char_node(cur_list.tail_field)) { + tail_append(new_penalty(inf_penalty,line_penalty)); + } else if (type(cur_list.tail_field) != glue_node) { + tail_append(new_penalty(inf_penalty,line_penalty)); + } else { + halfword t = alink(cur_list.tail_field); + flush_node(cur_list.tail_field); + cur_list.tail_field = t; + tail_append(new_penalty(inf_penalty,line_penalty)); + } + final_par_glue = new_param_glue(par_fill_skip_code); + couple_nodes(cur_list.tail_field, final_par_glue); + cur_list.tail_field = vlink(cur_list.tail_field); + lua_node_filter(pre_linebreak_filter_callback, line_break_context, temp_head, addressof(cur_list.tail_field)); + last_line_fill = cur_list.tail_field; + pop_nest(); + start_of_par = cur_list.tail_field; + callback_id = callback_defined(linebreak_filter_callback); + if (callback_id > 0) { + callback_id = lua_linebreak_callback(d, temp_head, addressof(cur_list.tail_field)); + if (callback_id > 0) { + /*tex find the correct value for the |just_box| */ + halfword box_search = cur_list.tail_field; + just_box = null; + if (box_search != null) { + do { + if (type(box_search) == hlist_node) { + just_box = box_search; + } + box_search = vlink(box_search); + } while (box_search != null); + } + if (just_box == null) { + help3( + "A linebreaking routine should return a non-empty list of nodes", + "and at least one of those has to be a \\hbox.", + "Sorry, I cannot recover from this." + ); + print_err("Invalid linebreak_filter"); + succumb(); + } + } else { + if (tracing_paragraphs_par > 0) { + begin_diagnostic(); + print_int(line); + end_diagnostic(true); + } + } + } + if (callback_id == 0) { + if ((!is_char_node(vlink(temp_head))) && ((type(vlink(temp_head)) == local_par_node))) { + paragraph_dir = local_par_dir(vlink(temp_head)); + } else { + confusion("weird par dir"); + } + ext_do_line_break( + paragraph_dir, + pretolerance_par, + tracing_paragraphs_par, + tolerance_par, + emergency_stretch_par, + looseness_par, + adjust_spacing_par, + par_shape_par_ptr, + adj_demerits_par, + protrude_chars_par, + line_penalty_par, + last_line_fit_par, + double_hyphen_demerits_par, + final_hyphen_demerits_par, + hang_indent_par, + hsize_par, + hang_after_par, + left_skip_par, + right_skip_par, + inter_line_penalties_par_ptr, + inter_line_penalty_par, + club_penalty_par, + club_penalties_par_ptr, + (d ? display_widow_penalties_par_ptr : widow_penalties_par_ptr), + (d ? display_widow_penalty_par : widow_penalty_par), + broken_penalty_par, + final_par_glue + ); + } + lua_node_filter(post_linebreak_filter_callback, line_break_context, start_of_par, addressof(cur_list.tail_field)); + pack_begin_line = 0; +} + +/*tex + + Glue nodes in a horizontal list that is being paragraphed are not supposed to + include ``infinite'' shrinkability; that is why the algorithm maintains four + registers for stretching but only one for shrinking. If the user tries to + introduce infinite shrinkability, the shrinkability will be reset to finite + and an error message will be issued. A boolean variable |no_shrink_error_yet| + prevents this error message from appearing more than once per paragraph. + +*/ + +#define check_shrinkage(a) \ + if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \ + a=finite_shrink((a)) + +/*tex Have we complained about infinite shrinkage? */ + +static boolean no_shrink_error_yet; + +/*tex Recovers from infinite shrinkage. */ + +static halfword finite_shrink(halfword p) +{ + const char *hlp[] = { + "The paragraph just ended includes some glue that has", + "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.", + "Such glue doesn't belong there---it allows a paragraph", + "of any length to fit on one line. But it's safe to proceed,", + "since the offensive shrinkability has been made finite.", + NULL + }; + if (no_shrink_error_yet) { + no_shrink_error_yet = false; + tex_error("Infinite glue shrinkage found in a paragraph", hlp); + } + shrink_order(p) = normal; + return p; +} + +/*tex + + A pointer variable |cur_p| runs through the given horizontal list as we look + for breakpoints. This variable is global, since it is used both by + |line_break| and by its subprocedure |try_break|. + + Another global variable called |threshold| is used to determine the + feasibility of individual lines: breakpoints are feasible if there is a way + to reach them without creating lines whose badness exceeds |threshold|. (The + badness is compared to |threshold| before penalties are added, so that + penalty values do not affect the feasibility of breakpoints, except that no + break is allowed when the penalty is 10000 or more.) If |threshold| is 10000 + or more, all legal breaks are considered feasible, since the |badness| + function specified above never returns a value greater than~10000. + + Up to three passes might be made through the paragraph in an attempt to find + at least one set of feasible breakpoints. On the first pass, we have + |threshold=pretolerance| and |second_pass=final_pass=false|. If this pass + fails to find a feasible solution, |threshold| is set to |tolerance|, + |second_pass| is set |true|, and an attempt is made to hyphenate as many + words as possible. If that fails too, we add |emergency_stretch| to the + background stretchability and set |final_pass=true|. + +*/ + +/*tex is this our second attempt to break this paragraph? */ + +static boolean second_pass; + +/*tex is this our final attempt to break this paragraph? */ + +static boolean final_pass; + +/*tex maximum badness on feasible lines */ + +static int threshold; + +/*tex + + The maximum fill level for |hlist_stack|. Maybe good if larger than |2 * + max_quarterword|, so that box nesting level would overflow first. + +*/ + +#define max_hlist_stack 512 + +/*tex stack for |find_protchar_left()| and |find_protchar_right()| */ + +static halfword hlist_stack[max_hlist_stack]; + +/*tex fill level for |hlist_stack| */ + +static short hlist_stack_level = 0; + +static void push_node(halfword p) +{ + if (hlist_stack_level >= max_hlist_stack) + normal_error("push_node","stack overflow"); + hlist_stack[hlist_stack_level++] = p; +} + +static halfword pop_node(void) +{ + if (hlist_stack_level <= 0) { + /*tex This can point to some bug. */ + normal_error("pop_node","stack underflow (internal error)"); + } + return hlist_stack[--hlist_stack_level]; +} + +/*tex maximal stretch ratio of expanded fonts */ + +static int max_stretch_ratio = 0; + +/*tex maximal shrink ratio of expanded fonts */ + +static int max_shrink_ratio = 0; + +/*tex the current step of expanded fonts */ + +static int cur_font_step = 0; + +static boolean check_expand_pars(internal_font_number f) +{ + int m; + if ((font_step(f) == 0) || ((font_max_stretch(f) == 0) && (font_max_shrink(f) == 0))) + return false; + if (cur_font_step < 0) + cur_font_step = font_step(f); + else if (cur_font_step != font_step(f)) + normal_error("font expansion","using fonts with different step of expansion in one paragraph is not allowed"); + m = font_max_stretch(f); + if (m != 0) { + if (max_stretch_ratio < 0) + max_stretch_ratio = m; + else if (max_stretch_ratio != m) + normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed"); + } + m = font_max_shrink(f); + if (m != 0) { + if (max_shrink_ratio < 0) + max_shrink_ratio = -m; + else if (max_shrink_ratio != -m) + normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed"); + } + return true; +} + +/*tex Search left to right from list head |l|, returns 1st non-skipable item */ + +halfword find_protchar_left(halfword l, boolean d) +{ + halfword t; + boolean run; + boolean done = false ; + while ((vlink(l) != null) && (type(l) == hlist_node) && zero_dimensions(l) && (list_ptr(l) == null)) { + /*tex For paragraph start with \.{\\parindent} = 0pt or any empty hbox. */ + l = vlink(l); + done = true ; + } + if ((!done) && (type(l) == local_par_node)) { + l = vlink(l); + done = true ; + } + if ((!done) && d) { + while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) { + /*tex standard discardables at line break, \TeX book, p 95 */ + l = vlink(l); + } + } + if (type(l) != glyph_node) { + hlist_stack_level = 0; + run = true; + do { + t = l; + while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) { + push_node(l); + l = list_ptr(l); + } + while (run && cp_skipable(l)) { + while ((vlink(l) == null) && (hlist_stack_level > 0)) { + /*tex Don't visit this node again. */ + l = pop_node(); + run = false; + } + if ((vlink(l) != null) && (type(l) == boundary_node) && (subtype(l) == protrusion_boundary) && + ((boundary_value(l) == 1) || (boundary_value(l) == 3))) { + /*tex Skip next node. */ + l = vlink(l); + } + if (vlink(l) != null) { + l = vlink(l); + } else if (hlist_stack_level == 0) { + run = false; + } + } + } while (t != l); + } + return l; +} + +/*tex + + Search right to left from list tail |r| to head |l|, returns 1st non-skipable + item. + +*/ + +halfword find_protchar_right(halfword l, halfword r) +{ + halfword t; + boolean run = true; + if (r == null) + return null; + hlist_stack_level = 0; + do { + t = r; + while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) { + push_node(l); + push_node(r); + l = list_ptr(r); + r = l; + while (vlink(r) != null) { + halfword s = r; + r = vlink(r); + alink(r) = s; + } + } + while (run && cp_skipable(r)) { + while ((r == l) && (hlist_stack_level > 0)) { + /*tex Don't visit this node again. */ + r = pop_node(); + l = pop_node(); + } + if ((r != l) && (r != null)) { + if ((alink(r) != null) && (type(r) == boundary_node) && (subtype(r) == protrusion_boundary) && + ((boundary_value(r) == 2) || (boundary_value(r) == 3))) { + /*tex Skip next node. */ + r = alink(r); + } + if (alink(r) != null) { + r = alink(r); + } else { + /*tex This is the input: \.{\\leavevmode\\penalty-10000\\penalty-10000} (bug \#268). */ + run = false; + } + } else if ((r == l) && (hlist_stack_level == 0)) + run = false; + } + } while (t != r); + return r; +} + +#define left_pw(a) char_pw((a), left_side) +#define right_pw(a) char_pw((a), right_side) + +/*tex + + When looking for optimal line breaks, \TeX\ creates a ``break node'' for each + break that is {\sl feasible}, in the sense that there is a way to end a line + at the given place without requiring any line to stretch more than a given + tolerance. A break node is characterized by three things: the position of the + break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|, or + |disc_node|); the ordinal number of the line that will follow this + breakpoint; and the fitness classification of the line that has just ended, + i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|. + +*/ + +typedef enum { + /*tex fitness classification for lines stretching more than their stretchability */ + very_loose_fit = 0, + /*tex fitness classification for lines stretching 0.5 to 1.0 of their stretchability */ + loose_fit, + /*tex fitness classification for all other lines */ + decent_fit, + /*tex fitness classification for lines shrinking 0.5 to 1.0 of their shrinkability */ + tight_fit +} fitness_value; + +/*tex + + The algorithm essentially determines the best possible way to achieve each + feasible combination of position, line, and fitness. Thus, it answers + questions like, ``What is the best way to break the opening part of the + paragraph so that the fourth line is a tight line ending at such-and-such a + place?'' However, the fact that all lines are to be the same length after a + certain point makes it possible to regard all sufficiently large line numbers + as equivalent, when the looseness parameter is zero, and this makes it + possible for the algorithm to save space and time. + + An ``active node'' and a ``passive node'' are created in |mem| for each + feasible breakpoint that needs to be considered. Active nodes are three words + long and passive nodes are two words long. We need active nodes only for + breakpoints near the place in the paragraph that is currently being examined, + so they are recycled within a comparatively short time after they are + created. + + An active node for a given breakpoint contains six fields: + + \startitemize[n] + + \startitem + |vlink| points to the next node in the list of active nodes; the last + active node has |vlink=active|. + \stopitem + + \startitem + |break_node| points to the passive node associated with this + breakpoint. + \stopitem + + \startitem + |line_number| is the number of the line that follows this breakpoint. + \stopitem + + \startitem + |fitness| is the fitness classification of the line ending at this + breakpoint. + \stopitem + + \startitem + |type| is either |hyphenated_node| or |unhyphenated_node|, depending + on whether this breakpoint is a |disc_node|. + \stopitem + + \startitem + |total_demerits| is the minimum possible sum of demerits over all + lines leading from the beginning of the paragraph to this breakpoint. + \stopitem + + \stopitemize + + The value of |vlink(active)| points to the first active node on a vlinked + list of all currently active nodes. This list is in order by |line_number|, + except that nodes with |line_number>easy_line| may be in any order relative + to each other. + +*/ + +void initialize_active(void) +{ + type(active) = hyphenated_node; + line_number(active) = max_halfword; + /*tex The |subtype| is never examined. */ + subtype(active) = 0; +} + +/*tex + + The passive node for a given breakpoint contains eight fields: + + \startitemize + + \startitem + |vlink| points to the passive node created just before this one, if + any, otherwise it is |null|. + \stopitem + + \startitem + |cur_break| points to the position of this breakpoint in the + horizontal list for the paragraph being broken. + \stopitem + + \startitem + |prev_break| points to the passive node that should precede this one + in an optimal path to this breakpoint. + \stopitem + + \startitem + |serial| is equal to |n| if this passive node is the |n|th one + created during the current pass. (This field is used only when + printing out detailed statistics about the line-breaking + calculations.) + \stopitem + + \startitem + |passive_pen_inter| holds the current \.{\\localinterlinepenalty} + \stopitem + + \startitem + |passive_pen_broken| holds the current \.{\\localbrokenpenalty} + \stopitem + + \stopitemize + + There is a global variable called |passive| that points to the most recently + created passive node. Another global variable, |printed_node|, is used to + help print out the paragraph when detailed information about the + line-breaking computation is being displayed. + +*/ + +/*tex most recent node on passive list */ + +static halfword passive; + +/*tex most recent node that has been printed */ + +static halfword printed_node; + +/*tex the number of passive nodes allocated on this pass */ + +static halfword pass_number; + +/*tex + + The active list also contains ``delta'' nodes that help the algorithm compute + the badness of individual lines. Such nodes appear only between two active + nodes, and they have |type=delta_node|. If |p| and |r| are active nodes and + if |q| is a delta node between them, so that |vlink(p)=q| and |vlink(q)=r|, + then |q| tells the space difference between lines in the horizontal list that + start after breakpoint |p| and lines that start after breakpoint |r|. In + other words, if we know the length of the line that starts after |p| and ends + at our current position, then the corresponding length of the line that + starts after |r| is obtained by adding the amounts in node~|q|. A delta node + contains seven scaled numbers, since it must record the net change in glue + stretchability with respect to all orders of infinity. The natural width + difference appears in |mem[q+1].sc|; the stretch differences in units of pt, + sfi, fil, fill, and filll appear in |mem[q+2..q+6].sc|; and the shrink + difference appears in |mem[q+7].sc|. The |subtype| field of a delta node is + not used. + + Actually, we have two more fields that are used by |pdftex|. + + As the algorithm runs, it maintains a set of seven delta-like registers for + the length of the line following the first active breakpoint to the current + position in the given hlist. When it makes a pass through the active list, it + also maintains a similar set of seven registers for the length following the + active breakpoint of current interest. A third set holds the length of an + empty line (namely, the sum of \.{\\leftskip} and \.{\\rightskip}); and a + fourth set is used to create new delta nodes. + + When we pass a delta node we want to do operations like: + + \starttyping + for k := 1 to 7 do + cur_active_width[k] := cur_active_width[k] + mem[q+k].sc|}; + \stoptyping + + and we want to do this without the overhead of |for| loops. The |do_all_six| + macro makes such six-tuples convenient. + +*/ + +/*tex distance from first active node to~|cur_p| */ + +static scaled active_width[10] = { 0 }; + +/*tex length of an ``empty'' line */ + +static scaled background[10] = { 0 }; + +/*tex length being computed after current break */ + +static scaled break_width[10] = { 0 }; + +/*tex Make |auto_breaking| accessible out of |line_break|: */ + +static boolean auto_breaking; + +/*tex + + Let's state the principles of the delta nodes more precisely and concisely, + so that the following programs will be less obscure. For each legal + breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and + $\beta(p)$ such that the length of material in a line from breakpoint~|p| to + breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$. + Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from + the beginning of the paragraph to a point ``after'' a break at |p| and to a + point ``before'' a break at |q|; and $\gamma$ is the width of an empty line, + namely the length contributed by \.{\\leftskip} and \.{\\rightskip}. + + Suppose, for example, that the paragraph consists entirely of alternating + boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and let the + skips have widths $y_1\ldots y_n$, so that the paragraph can be represented + by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint at $y_i$; then + $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)= x_1+y_1+\cdots+x_i$. + To check this, note that the length of material from $p_2$ to $p_5$, say, is + $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5) -\alpha(p_2)$. + + The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and + shrinkability as well as a natural width. If we were to compute $\alpha(p)$ + and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and + the multiprecise numbers would have to be kept in the active nodes. \TeX\ + avoids this problem by working entirely with relative differences or + ``deltas.'' Suppose, for example, that the active list contains + $a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints + and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$ + and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is + currently positioned at some other breakpoint |p|, the |active_width| array + contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through + the list of active nodes and considering a tentative line that runs from + $a_2$ to~|p|, say, the |cur_active_width| array will contain the value + $\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$, we + want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this is just + $\delta_2$, which appears in the active list between $a_2$ and $a_3$. The + |background| array contains $\gamma$. The |break_width| array will be used to + calculate values of new delta nodes when the active list is being updated. + + The heart of the line-breaking procedure is `|try_break|', a subroutine that + tests if the current breakpoint |cur_p| is feasible, by running through the + active list to see what lines of text can be made from active nodes + to~|cur_p|. If feasible breaks are possible, new break nodes are created. If + |cur_p| is too far from an active node, that node is deactivated. + + The parameter |pi| to |try_break| is the penalty associated with a break at + |cur_p|; we have |pi=eject_penalty| if the break is forced, and + |pi=inf_penalty| if the break is illegal. + + The other parameter, |break_type|, is set to |hyphenated_node| or + |unhyphenated_node|, depending on whether or not the current break is at a + |disc_node|. The end of a paragraph is also regarded as `|hyphenated_node|'; + this case is distinguishable by the condition |cur_p=null|. + +*/ + +/*tex running \.{\\localinterlinepenalty} */ + +static int internal_pen_inter; + +/*tex running \.{\\localbrokenpenalty} */ + +static int internal_pen_broken; + +/*tex running \.{\\localleftbox} */ + +static halfword internal_left_box; + +/*tex running \.{\\localleftbox} width */ + +static int internal_left_box_width; + +/*tex running \.{\\localleftbox} */ + +static halfword init_internal_left_box; + +/*tex running \.{\\localleftbox} width */ + +static int init_internal_left_box_width; + +/*tex running \.{\\localrightbox} */ + +static halfword internal_right_box; + +/*tex running \.{\\localrightbox} width */ + +static int internal_right_box_width; + +/*tex the length of discretionary material preceding a break */ + +static scaled disc_width[10] = { 0 }; + +/*tex + + As we consider various ways to end a line at |cur_p|, in a given line number + class, we keep track of the best total demerits known, in an array with one + entry for each of the fitness classifications. For example, + |minimal_demerits[tight_fit]| contains the fewest total demerits of feasible + line breaks ending at |cur_p| with a |tight_fit| line; + |best_place[tight_fit]| points to the passive node for the break + before~|cur_p| that achieves such an optimum; and |best_pl_line[tight_fit]| + is the |line_number| field in the active node corresponding to + |best_place[tight_fit]|. When no feasible break sequence is known, the + |minimal_demerits| entries will be equal to |awful_bad|, which is $2^{30}-1$. + Another variable, |minimum_demerits|, keeps track of the smallest value in + the |minimal_demerits| array. + +*/ + +/*tex best total demerits known for current line class and position, given the fitness */ + +static int minimal_demerits[4]; + +/*tex best total demerits known for current line class and position */ + +static int minimum_demerits; + +/*tex how to achieve |minimal_demerits| */ + +static halfword best_place[4]; + +/*tex corresponding line number */ + +static halfword best_pl_line[4]; + +/*tex + + The length of lines depends on whether the user has specified \.{\\parshape} + or \.{\\hangindent}. If |par_shape_ptr| is not null, it points to a + $(2n+1)$-word record in |mem|, where the |vinfo| in the first word contains + the value of |n|, and the other $2n$ words contain the left margins and line + lengths for the first |n| lines of the paragraph; the specifications for line + |n| apply to all subsequent lines. If |par_shape_ptr=null|, the shape of the + paragraph depends on the value of |n=hang_after|; if |n>=0|, hanging + indentation takes place on lines |n+1|, |n+2|, \dots, otherwise it takes + place on lines 1, \dots, $\vert n\vert$. When hanging indentation is active, + the left margin is |hang_indent|, if |hang_indent>=0|, else it is 0; the line + length is $|hsize|-\vert|hang_indent|\vert$. The normal setting is + |par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|. Note that if + |hang_indent=0|, the value of |hang_after| is irrelevant. + +*/ + +/*tex line numbers |>easy_line| are equivalent in break nodes */ + +static halfword easy_line; + +/*tex line numbers |>last_special_line| all have the same width */ + +static halfword last_special_line; + +/*tex the width of all lines |<=last_special_line|, if no \.{\\parshape} has been specified */ + +static scaled first_width; + +/*tex the width of all lines |>last_special_line| */ + +static scaled second_width; + +/*tex left margin to go with |first_width| */ + +static scaled first_indent; + +/*tex left margin to go with |second_width| */ + +static scaled second_indent; + +/*tex use this passive node and its predecessors */ + +static halfword best_bet; + +/*tex the demerits associated with |best_bet| */ + +static int fewest_demerits; + +/*tex line number following the last line of the new paragraph */ + +static halfword best_line; + +/*tex the difference between |line_number(best_bet)| and the optimum |best_line| */ + +static int actual_looseness; + +/*tex the difference between the current line number and the optimum |best_line| */ + +static int line_diff; + +/*tex + + \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, |rule_node|, + |ins_node|, |mark_node|, |adjust_node|, |disc_node|, |whatsit_node|, and + |math_node| are at the low end of the type codes, by permitting a break at + glue in a list if and only if the |type| of the previous node is less than + |math_node|. Furthermore, a node is discarded after a break if its type is + |math_node| or~more. + +*/ + +#define do_all_six(a) a(1);a(2);a(3);a(4);a(5);a(6);a(7) +#define do_seven_eight(a) if (adjust_spacing > 1) { a(8);a(9); } +#define do_all_eight(a) do_all_six(a); do_seven_eight(a) +#define do_one_seven_eight(a) a(1); do_seven_eight(a) + +#define store_background(a) {active_width[a]=background[a];} + +#define kern_break() { \ + if ((!is_char_node(vlink(cur_p))) && auto_breaking) \ + if (type(vlink(cur_p))==glue_node) \ + ext_try_break(\ + 0, \ + unhyphenated_node, \ + line_break_dir, \ + adjust_spacing, \ + par_shape_ptr, \ + adj_demerits, \ + tracing_paragraphs, \ + protrude_chars, \ + line_penalty, \ + last_line_fit, \ + double_hyphen_demerits, \ + final_hyphen_demerits, \ + first_p, \ + cur_p \ + ); \ + if (type(cur_p)!=math_node) \ + active_width[1] += width(cur_p); \ + else \ + active_width[1] += surround(cur_p); \ +} + +#define clean_up_the_memory() { \ + q=vlink(active); \ + while (q!=active) { \ + cur_p = vlink(q); \ + if (type(q)==delta_node) \ + flush_node(q); \ + else \ + flush_node(q); \ + q = cur_p; \ + } \ + q = passive; \ + while (q!=null) { \ + cur_p = vlink(q); \ + flush_node(q); \ + q = cur_p; \ + } \ +} + +/*tex special algorithm for last line of paragraph? */ + +static boolean do_last_line_fit; + +/*tex infinite stretch components of |par_fill_skip| */ + +static scaled fill_width[4]; + +/*tex |shortfall| corresponding to |minimal_demerits| */ + +static scaled best_pl_short[4]; + +/*tex corresponding glue stretch or shrink */ + +static scaled best_pl_glue[4]; + +#define reset_disc_width(a) disc_width[(a)] = 0 + +#define add_disc_width_to_break_width(a) break_width[(a)] += disc_width[(a)] +#define sub_disc_width_from_active_width(a) active_width[(a)] -= disc_width[(a)] + +#define add_char_shrink(a,b) a += char_shrink((b)) +#define add_char_stretch(a,b) a += char_stretch((b)) +#define sub_char_shrink(a,b) a -= char_shrink((b)) +#define sub_char_stretch(a,b) a -= char_stretch((b)) + +#define add_kern_shrink(a,b) a += kern_shrink((b)) +#define add_kern_stretch(a,b) a += kern_stretch((b)) +#define sub_kern_shrink(a,b) a -= kern_shrink((b)) +#define sub_kern_stretch(a,b) a -= kern_stretch((b)) + +/*tex + + This function is used to add the width of a list of nodes (from a + discretionary) to one of the width arrays. + + Replacement texts and discretionary texts are supposed to contain only + character nodes, kern nodes, and box or rule nodes. + +*/ + +#define bad_node_in_disc_error(p) { \ + if (type(p) == whatsit_node) { \ + formatted_error("linebreak","invalid node with type %s and subtype %i found in discretionary",node_data[type(p)].name,subtype(p)); \ + } else { \ + formatted_error("linebreak","invalid node with type %s found in discretionary",node_data[type(p)].name); \ + } \ +} + +static void add_to_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths) +{ + while (s != null) { + if (is_char_node(s)) { + widths[1] += pack_width(line_break_dir, dir_TRT, s, true); + if ((adjust_spacing > 1) && check_expand_pars(font(s))) { + set_prev_char_p(s); + add_char_stretch(widths[8], s); + add_char_shrink(widths[9], s); + }; + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + widths[1] += pack_width(line_break_dir, box_dir(s), s, false); + break; + case kern_node: + if ((adjust_spacing == 2) && (subtype(s) == normal)) { + add_kern_stretch(widths[8], s); + add_kern_shrink(widths[9], s); + } + /*tex fall through */ + case rule_node: + widths[1] += width(s); + break; + case disc_node: + break; + default: + bad_node_in_disc_error(s); + break; + } + } + s = vlink(s); + } +} + +/*tex + + This function is used to substract the width of a list of nodes (from a + discretionary) from one of the width arrays. It is used only once, but + deserves it own function because of orthogonality with the |add_to_widths| + function. + +*/ + +static void sub_from_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths) +{ + while (s != null) { + /*tex Subtract the width of node |s| from |break_width|; */ + if (is_char_node(s)) { + widths[1] -= pack_width(line_break_dir, dir_TRT, s, true); + if ((adjust_spacing > 1) && check_expand_pars(font(s))) { + set_prev_char_p(s); + sub_char_stretch(widths[8], s); + sub_char_shrink(widths[9], s); + } + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + widths[1] -= pack_width(line_break_dir, box_dir(s), s, false); + break; + case kern_node: + if ((adjust_spacing == 2) && (subtype(s) == normal)) { + sub_kern_stretch(widths[8], s); + sub_kern_shrink(widths[9], s); + } + /*tex fall through */ + case rule_node: + widths[1] -= width(s); + break; + case disc_node: + break; + default: + bad_node_in_disc_error(s); + break; + } + } + s = vlink(s); + } +} + +/*tex + + When we insert a new active node for a break at |cur_p|, suppose this new + node is to be placed just before active node |a|; then we essentially want to + insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where + $\delta=\alpha(a)-\alpha(|cur_p|)$ and + $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$ in the notation explained above. + The |cur_active_width| array now holds $\gamma+\beta(|cur_p|)-\alpha(a)$; so + $\delta$ can be obtained by subtracting |cur_active_width| from the quantity + $\gamma+\beta(|cur_p|)- \alpha(|cur_p|)$. The latter quantity can be regarded + as the length of a line ``from |cur_p| to |cur_p|''; we call it the + |break_width| at |cur_p|. + + The |break_width| is usually negative, since it consists of the background + (which is normally zero) minus the width of nodes following~|cur_p| that are + eliminated after a break. If, for example, node |cur_p| is a glue node, the + width of this glue is subtracted from the background; and we also look ahead + to eliminate all subsequent glue and penalty and kern and math nodes, + subtracting their widths as well. + + Kern nodes do not disappear at a line break unless they are |explicit|. + +*/ + +static void compute_break_width(int break_type, int line_break_dir, int adjust_spacing, halfword p) +{ + /*tex + + Glue and other 'whitespace' to be skipped after a break; used if + unhyphenated, or |post_break==empty|. + + */ + halfword s = p; + if (break_type > unhyphenated_node && p != null) { + /*tex + + Compute the discretionary |break_width| values. + + When |p| is a discretionary break, the length of a line ``from |p| to + |p|'' has to be defined properly so that the other calculations work + out. Suppose that the pre-break text at |p| has length $l_0$, the + post-break text has length $l_1$, and the replacement text has length + |l|. Suppose also that |q| is the node following the replacement + text. Then length of a line from |p| to |q| will be computed as + $\gamma+\beta(q)-\alpha(|p|)$, where $\beta(q)=\beta(|p|)-l_0+l$. The + actual length will be the background plus $l_1$, so the length from + |p| to |p| should be $\gamma+l_0+l_1-l$. If the post-break text of + the discretionary is empty, a break may also discard~|q|; in that + unusual case we subtract the length of~|q| and any other nodes that + will be discarded after the discretionary break. + + The value of $l_0$ need not be computed, since |line_break| will put + it into the global variable |disc_width| before calling |try_break|. + + In case of nested discretionaries, we always follow the no-break + path, as we are talking about the breaking on {\it this} position. + + */ + sub_from_widths(vlink_no_break(p), line_break_dir, adjust_spacing, break_width); + add_to_widths(vlink_post_break(p), line_break_dir, adjust_spacing, break_width); + do_one_seven_eight(add_disc_width_to_break_width); + if (vlink_post_break(p) == null) { + /*tex no |post_break|: 'skip' any 'whitespace' following */ + s = vlink(p); + } else { + s = null; + } + } + while (s != null) { + switch (type(s)) { + case math_node: + /*tex begin mathskip code */ + if (glue_is_zero(s)) { + break_width[1] -= surround(s); + break; + } else { + /*tex fall through */ + } + /*tex end mathskip code */ + case glue_node: + /*tex Subtract glue from |break_width|; */ + break_width[1] -= width(s); + break_width[2 + stretch_order(s)] -= stretch(s); + break_width[7] -= shrink(s); + break; + case penalty_node: + break; + case kern_node: + if (subtype(s) != explicit_kern && subtype(s) != italic_kern) + return; + else + break_width[1] -= width(s); + break; + default: + return; + }; + s = vlink(s); + } +} + +static void print_break_node(halfword q, fitness_value fit_class, quarterword break_type, halfword cur_p) +{ + /*tex Print a symbolic description of the new break node. */ + tprint_nl("@@"); + print_int(serial(passive)); + tprint(": line "); + print_int(line_number(q) - 1); + print_char('.'); + print_int(fit_class); + if (break_type == hyphenated_node) + print_char('-'); + tprint(" t="); + print_int(total_demerits(q)); + if (do_last_line_fit) { + /*tex Print additional data in the new active node. */ + tprint(" s="); + print_scaled(active_short(q)); + if (cur_p == null) + tprint(" a="); + else + tprint(" g="); + print_scaled(active_glue(q)); + } + tprint(" -> @"); + if (prev_break(passive) == null) + print_char('0'); + else + print_int(serial(prev_break(passive))); +} + +static void print_feasible_break(halfword cur_p, pointer r, halfword b, int pi, int d, boolean artificial_demerits) +{ + /*tex + + Print a symbolic description of this feasible break. + + */ + if (printed_node != cur_p) { + /*tex + + Print the list between |printed_node| and |cur_p|, then set + |printed_node:=cur_p|. + + */ + tprint_nl(""); + if (cur_p == null) { + short_display(vlink(printed_node)); + } else { + halfword save_link = vlink(cur_p); + vlink(cur_p) = null; + tprint_nl(""); + short_display(vlink(printed_node)); + vlink(cur_p) = save_link; + } + printed_node = cur_p; + } + tprint_nl("@"); + if (cur_p == null) { + tprint_esc("par"); + } else if (type(cur_p) != glue_node) { + if (type(cur_p) == penalty_node) + tprint_esc("penalty"); + else if (type(cur_p) == disc_node) + tprint_esc("discretionary"); + else if (type(cur_p) == kern_node) + tprint_esc("kern"); + else + tprint_esc("math"); + } + tprint(" via @"); + if (break_node(r) == null) + print_char('0'); + else + print_int(serial(break_node(r))); + tprint(" b="); + if (b > inf_bad) + print_char('*'); + else + print_int(b); + tprint(" p="); + print_int(pi); + tprint(" d="); + if (artificial_demerits) + print_char('*'); + else + print_int(d); +} + +#define add_disc_width_to_active_width(a) active_width[a] += disc_width[a] +#define update_width(a) cur_active_width[a] += varmem[(r+(a))].cint + +#define set_break_width_to_background(a) break_width[a]=background[(a)] + +#define convert_to_break_width(a) \ + varmem[(prev_r+(a))].cint = varmem[(prev_r+(a))].cint-cur_active_width[(a)]+break_width[(a)] + +#define store_break_width(a) active_width[(a)]=break_width[(a)] + +#define new_delta_to_break_width(a) \ + varmem[(q+(a))].cint=break_width[(a)]-cur_active_width[(a)] + +#define new_delta_from_break_width(a) \ + varmem[(q+(a))].cint=cur_active_width[(a)]-break_width[(a)] + +#define copy_to_cur_active(a) cur_active_width[(a)]=active_width[(a)] + +#define combine_two_deltas(a) varmem[(prev_r+(a))].cint += varmem[(r+(a))].cint +#define downdate_width(a) cur_active_width[(a)] -= varmem[(prev_r+(a))].cint +#define update_active(a) active_width[(a)]+=varmem[(r+(a))].cint + +#define total_font_stretch cur_active_width[8] +#define total_font_shrink cur_active_width[9] + +#define cal_margin_kern_var(a) { \ + character(cp) = character((a)); \ + font(cp) = font((a)); \ + do_subst_font(cp, 1000); \ + if (font(cp) != font((a))) \ + margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \ + font(cp) = font((a)); \ + do_subst_font(cp, -1000); \ + if (font(cp) != font((a))) \ + margin_kern_shrink += (left_pw(cp) - left_pw((a))); \ +} + +static void ext_try_break( + int pi, + quarterword break_type, + int line_break_dir, + int adjust_spacing, + int par_shape_ptr, + int adj_demerits, + int tracing_paragraphs, + int protrude_chars, + int line_penalty, + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, halfword first_p, halfword cur_p +) +{ + /*tex runs through the active list */ + pointer r; + scaled margin_kern_stretch; + scaled margin_kern_shrink; + halfword lp, rp, cp; + /*tex stays a step behind |r| */ + halfword prev_r = active; + /*tex a step behind |prev_r|, if |type(prev_r)=delta_node| */ + halfword prev_prev_r = null; + /*tex maximum line number in current equivalence class of lines */ + halfword old_l = 0; + /*tex have we found a feasible break at |cur_p|? */ + boolean no_break_yet = true; + /*tex points to a new node being created */ + halfword q; + /*tex line number of current active node */ + halfword l; + /*tex should node |r| remain in the active list? */ + boolean node_r_stays_active; + /*tex the current line will be justified to this width */ + scaled line_width = 0; + /*tex possible fitness class of test line */ + fitness_value fit_class; + /*tex badness of test line */ + halfword b; + /*tex demerits of test line */ + int d; + /*tex has |d| been forced to zero? */ + boolean artificial_demerits; + /*tex used in badness calculations */ + scaled shortfall; + /*tex glue stretch or shrink of test line, adjustment for last line */ + scaled g = 0; + /*tex distance from current active node */ + scaled cur_active_width[10] = { 0 }; + /*tex Make sure that |pi| is in the proper range; */ + if (pi >= inf_penalty) { + /*tex this breakpoint is inhibited by infinite penalty */ + return; + } else if (pi <= -inf_penalty) { + /*tex this breakpoint will be forced */ + pi = eject_penalty; + } + + do_all_eight(copy_to_cur_active); + + while (1) { + r = vlink(prev_r); + /*tex + + If node |r| is of type |delta_node|, update |cur_active_width|, set + |prev_r| and |prev_prev_r|, then |goto continue|. The following code + uses the fact that |type(active)<>delta_node|. + + */ + if (type(r) == delta_node) { + /*tex implicit */ + do_all_eight(update_width); + prev_prev_r = prev_r; + prev_r = r; + continue; + } + /*tex + + If a line number class has ended, create new active nodes for the + best feasible breaks in that class; then |return| if |r=active|, + otherwise compute the new |line_width|. + + The first part of the following code is part of \TeX's inner loop, so + we don't want to waste any time. The current active node, namely node + |r|, contains the line number that will be considered next. At the + end of the list we have arranged the data structure so that + |r=active| and |line_number(active)>old_l|. + + */ + l = line_number(r); + if (l > old_l) { + /*tex now we are no longer in the inner loop */ + if ((minimum_demerits < awful_bad) + && ((old_l != easy_line) || (r == active))) { + /*tex + + Create new active nodes for the best feasible breaks just + found. It is not necessary to create new active nodes having + |minimal_demerits| greater than + |minimum_demerits+abs(adj_demerits)|, since such active nodes + will never be chosen in the final paragraph breaks. This + observation allows us to omit a substantial number of + feasible breakpoints from further consideration. + + */ + if (no_break_yet) { + no_break_yet = false; + do_all_eight(set_break_width_to_background); + compute_break_width(break_type, line_break_dir, adjust_spacing, cur_p); + } + /*tex + + Insert a delta node to prepare for breaks at |cur_p|. We use + the fact that |type(active)<>delta_node|. + + */ + if (type(prev_r) == delta_node) { + /*tex modify an existing delta node */ + do_all_eight(convert_to_break_width); + } else if (prev_r == active) { + /*tex no delta node needed at the beginning */ + do_all_eight(store_break_width); + } else { + q = new_node(delta_node, 0); + vlink(q) = r; + do_all_eight(new_delta_to_break_width); + vlink(prev_r) = q; + prev_prev_r = prev_r; + prev_r = q; + } + if (abs(adj_demerits) >= awful_bad - minimum_demerits) + minimum_demerits = awful_bad - 1; + else + minimum_demerits += abs(adj_demerits); + for (fit_class = very_loose_fit; fit_class <= tight_fit; + fit_class++) { + if (minimal_demerits[fit_class] <= minimum_demerits) { + /*tex + + Insert a new active node from |best_place[fit_class]| + to |cur_p|. When we create an active node, we also + create the corresponding passive node. + + */ + q = new_node(passive_node, 0); + vlink(q) = passive; + passive = q; + cur_break(q) = cur_p; + incr(pass_number); + serial(q) = pass_number; + prev_break(q) = best_place[fit_class]; + /*tex + + Here we keep track of the subparagraph penalties in + the break nodes. + + */ + passive_pen_inter(q) = internal_pen_inter; + passive_pen_broken(q) = internal_pen_broken; + passive_last_left_box(q) = internal_left_box; + passive_last_left_box_width(q) = + internal_left_box_width; + if (prev_break(q) != null) { + passive_left_box(q) = passive_last_left_box(prev_break(q)); + passive_left_box_width(q) = passive_last_left_box_width(prev_break(q)); + } else { + passive_left_box(q) = init_internal_left_box; + passive_left_box_width(q) = init_internal_left_box_width; + } + passive_right_box(q) = internal_right_box; + passive_right_box_width(q) = internal_right_box_width; + q = new_node(break_type, fit_class); + break_node(q) = passive; + line_number(q) = best_pl_line[fit_class] + 1; + total_demerits(q) = minimal_demerits[fit_class]; + if (do_last_line_fit) { + /*tex + + Store additional data in the new active node. + Here we save these data in the active node + representing a potential line break. + + */ + active_short(q) = best_pl_short[fit_class]; + active_glue(q) = best_pl_glue[fit_class]; + } + vlink(q) = r; + vlink(prev_r) = q; + prev_r = q; + if (tracing_paragraphs > 0) + print_break_node(q, fit_class, break_type, cur_p); + } + minimal_demerits[fit_class] = awful_bad; + } + minimum_demerits = awful_bad; + /*tex + + Insert a delta node to prepare for the next active node. When + the following code is performed, we will have just inserted + at least one active node before |r|, so + |type(prev_r)<>delta_node|. + + */ + if (r != active) { + q = new_node(delta_node, 0); + vlink(q) = r; + do_all_eight(new_delta_from_break_width); + vlink(prev_r) = q; + prev_prev_r = prev_r; + prev_r = q; + } + } + if (r == active) + return; + /*tex + + Compute the new line width. When we come to the following code, + we have just encountered the first active node~|r| whose + |line_number| field contains |l|. Thus we want to compute the + length of the $l\mskip1mu$th line of the current paragraph. + Furthermore, we want to set |old_l| to the last number in the + class of line numbers equivalent to~|l|. + + */ + if (l > easy_line) { + old_l = max_halfword - 1; + line_width = second_width; + } else { + old_l = l; + if (l > last_special_line) { + line_width = second_width; + } else if (par_shape_ptr == null) { + line_width = first_width; + } else { + line_width = varmem[(par_shape_ptr + 2 * l + 1)].cint; + } + } + } + /*tex + + If a line number class has ended, create new active nodes for the + best feasible breaks in that class; then |return| if |r=active|, + otherwise compute the new |line_width|. + + Consider the demerits for a line from |r| to |cur_p|; deactivate node + |r| if it should no longer be active; then |goto continue| if a line + from |r| to |cur_p| is infeasible, otherwise record a new feasible + break. + + */ + artificial_demerits = false; + shortfall = line_width - cur_active_width[1]; + if (break_node(r) == null) + shortfall -= init_internal_left_box_width; + else + shortfall -= passive_last_left_box_width(break_node(r)); + shortfall -= internal_right_box_width; + if (protrude_chars > 1) { + halfword l1, o; + l1 = (break_node(r) == null) ? first_p : cur_break(break_node(r)); + if (cur_p == null) { + o = null; + } else { + o = alink(cur_p); + assert(vlink(o) == cur_p); + } + /*tex + + The disc could be a SELECT subtype, to we might need to get the + last character as |pre_break| from either the |pre_break| list + (if the previous INIT disc was taken), or the |no_break| (sic) + list (if the previous INIT disc was not taken). + + The last characters (hyphenation character) if these two list + should always be the same anyway, so we just look at |pre_break|. + + Let's look at the right margin first. + + */ + if ((cur_p != null) && (type(cur_p) == disc_node) && (vlink_pre_break(cur_p) != null)) { + /*tex a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ + o = tlink_pre_break(cur_p); + } else { + o = find_protchar_right(l1, o); + } + /*tex now the left margin */ + if ((l1 != null) && (type(l1) == disc_node) && (vlink_post_break(l1) != null)) { + /*tex The first char could be a disc! Protrude the first char. */ + l1 = vlink_post_break(l1); + } else { + l1 = find_protchar_left(l1, true); + } + shortfall += (left_pw(l1) + right_pw(o)); + } + if (shortfall != 0) { + margin_kern_stretch = 0; + margin_kern_shrink = 0; + if (protrude_chars > 1) { + /*tex Calculate variations of marginal kerns. */ + lp = last_leftmost_char; + rp = last_rightmost_char; + cp = raw_glyph_node(); + if (lp != null) { + cal_margin_kern_var(lp); + } + if (rp != null) { + cal_margin_kern_var(rp); + } + flush_node(cp); + } + if ((shortfall > 0) && ((total_font_stretch + margin_kern_stretch) > 0)) { + if ((total_font_stretch + margin_kern_stretch) > shortfall) + shortfall = ((total_font_stretch + margin_kern_stretch) / (max_stretch_ratio / cur_font_step)) / 2; + else + shortfall -= (total_font_stretch + margin_kern_stretch); + } else if ((shortfall < 0) && ((total_font_shrink + margin_kern_shrink) > 0)) { + if ((total_font_shrink + margin_kern_shrink) > -shortfall) + shortfall = -((total_font_shrink + margin_kern_shrink) / (max_shrink_ratio / cur_font_step)) / 2; + else + shortfall += (total_font_shrink + margin_kern_shrink); + } + } + if (shortfall > 0) { + /*tex + + Set the value of |b| to the badness for stretching the line, and + compute the corresponding |fit_class|. + + When a line must stretch, the available stretchability can be + found in the subarray |cur_active_width[2..6]|, in units of + points, sfi, fil, fill and filll. + + The present section is part of \TeX's inner loop, and it is most + often performed when the badness is infinite; therefore it is + worth while to make a quick test for large width excess and small + stretchability, before calling the |badness| subroutine. + + */ + if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) || + (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) { + if (do_last_line_fit) { + if (cur_p == null) { + /*tex + + The last line of a paragraph. Perform computations + for last line and |goto found|. + + Here we compute the adjustment |g| and badness |b| + for a line from |r| to the end of the paragraph. When + any of the criteria for adjustment is violated we + fall through to the normal algorithm. + + The last line must be too short, and have infinite + stretch entirely due to |par_fill_skip|. + + */ + if ((active_short(r) == 0) || (active_glue(r) <= 0)) + /*tex + + Previous line was neither stretched nor shrunk, + or was infinitely bad. + + */ + goto NOT_FOUND; + if ((cur_active_width[3] != fill_width[0]) || (cur_active_width[4] != fill_width[1]) || + (cur_active_width[5] != fill_width[2]) || (cur_active_width[6] != fill_width[3])) + /*tex + + Infinite stretch of this line not entirely due to |par_fill_skip|. + + */ + goto NOT_FOUND; + if (active_short(r) > 0) + g = cur_active_width[2]; + else + g = cur_active_width[7]; + if (g <= 0) + /*tex No finite stretch resp.\ no shrink. */ + goto NOT_FOUND; + arith_error = false; + g = fract(g, active_short(r), active_glue(r), + max_dimen); + if (last_line_fit < 1000) + g = fract(g, last_line_fit, 1000, max_dimen); + if (arith_error) { + if (active_short(r) > 0) + g = max_dimen; + else + g = -max_dimen; + } + if (g > 0) { + /*tex + + Set the value of |b| to the badness of the last + line for stretching, compute the corresponding + |fit_class, and |goto found|. These badness + computations are rather similar to those of the + standard algorithm, with the adjustment amount + |g| replacing the |shortfall|. + + */ + if (g > shortfall) + g = shortfall; + if (g > 7230584) { + if (cur_active_width[2] < 1663497) { + b = inf_bad; + fit_class = very_loose_fit; + goto FOUND; + } + } + b = badness(g, cur_active_width[2]); + if (b > 99) { + fit_class = very_loose_fit; + } else if (b > 12) { + fit_class = loose_fit; + } else { + fit_class = decent_fit; + } + goto FOUND; + } else if (g < 0) { + /*tex + + Set the value of |b| to the badness of the last + line for shrinking, compute the corresponding + |fit_class, and |goto found||. + + */ + if (-g > cur_active_width[7]) + g = -cur_active_width[7]; + b = badness(-g, cur_active_width[7]); + if (b > 12) + fit_class = tight_fit; + else + fit_class = decent_fit; + goto FOUND; + } + } + NOT_FOUND: + shortfall = 0; + } + b = 0; + /*tex Infinite stretch. */ + fit_class = decent_fit; + } else if (shortfall > 7230584 && cur_active_width[2] < 1663497) { + b = inf_bad; + fit_class = very_loose_fit; + } else { + b = badness(shortfall, cur_active_width[2]); + if (b > 99) { + fit_class = very_loose_fit; + } else if (b > 12) { + fit_class = loose_fit; + } else { + fit_class = decent_fit; + } + } + } else { + /*tex + + Set the value of |b| to the badness for shrinking the line, and + compute the corresponding |fit_class|. Shrinkability is never + infinite in a paragraph; we can shrink the line from |r| to + |cur_p| by at most |cur_active_width[7]|. + + */ + if (-shortfall > cur_active_width[7]) + b = inf_bad + 1; + else + b = badness(-shortfall, cur_active_width[7]); + if (b > 12) + fit_class = tight_fit; + else + fit_class = decent_fit; + } + if (do_last_line_fit) { + /*tex Adjust the additional data for last line; */ + if (cur_p == null) + shortfall = 0; + if (shortfall > 0) { + g = cur_active_width[2]; + } else if (shortfall < 0) { + g = cur_active_width[7]; + } else { + g = 0; + } + } + FOUND: + if ((b > inf_bad) || (pi == eject_penalty)) { + /*tex + + Prepare to deactivate node~|r|, and |goto deactivate| unless + there is a reason to consider lines of text from |r| to |cur_p|. + During the final pass, we dare not lose all active nodes, lest we + lose touch with the line breaks already found. The code shown + here makes sure that such a catastrophe does not happen, by + permitting overfull boxes as a last resort. This particular part + of \TeX\ was a source of several subtle bugs before the correct + program logic was finally discovered; readers who seek to + ``improve'' \TeX\ should therefore think thrice before daring to + make any changes here. + + */ + if (final_pass && (minimum_demerits == awful_bad) && + (vlink(r) == active) && (prev_r == active)) { + /*tex Set demerits zero, this break is forced. */ + artificial_demerits = true; + } else if (b > threshold) { + goto DEACTIVATE; + } + node_r_stays_active = false; + } else { + prev_r = r; + if (b > threshold) + continue; + node_r_stays_active = true; + } + /*tex + + Record a new feasible break. When we get to this part of the code, + the line from |r| to |cur_p| is feasible, its badness is~|b|, and its + fitness classification is |fit_class|. We don't want to make an + active node for this break yet, but we will compute the total + demerits and record them in the |minimal_demerits| array, if such a + break is the current champion among all ways to get to |cur_p| in a + given line-number class and fitness class. + + */ + if (artificial_demerits) { + d = 0; + } else { + /*tex Compute the demerits, |d|, from |r| to |cur_p|. */ + d = line_penalty + b; + if (abs(d) >= 10000) + d = 100000000; + else + d = d * d; + if (pi != 0) { + if (pi > 0) { + d += (pi * pi); + } else if (pi > eject_penalty) { + d -= (pi * pi); + } + } + if ((break_type == hyphenated_node) && (type(r) == hyphenated_node)) { + if (cur_p != null) + d += double_hyphen_demerits; + else + d += final_hyphen_demerits; + } + /*tex + + Direct calculation of the absolute value in ((|fit_class| - |fitness(r)|) > 1) + can lead to unexpected results even if the type of the members of |fit_class|, which is |int| + (see C99 §6.7.2.2), and the integer promotion rules for |fitness(r)|, whose also + give an |int| type, should set the expression as substraction between two |int|. + In this case GCC set the type of |fit_class| to |unsigned int| (perhaps because the members are all positives?) + and hence the expression is converted to a sum of |unsigned int|, leading to a different result. + The choice of type is implementation-defined, as stated in C99 §6.7.2.2.4: + + "Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. + The choice of type is implementation-defined, but shall be capable of representing the values + of all the members of the enumeration." + + It's better to use the equivalent expanded expression. + */ + if ( (fit_class>(fitness(r)+1)) || (fitness(r)>(fit_class+1)) ) + d = d + adj_demerits; + + } + if (tracing_paragraphs > 0) { + print_feasible_break(cur_p, r, b, pi, d, artificial_demerits); + } + /*tex This is the minimum total demerits from the beginning to |cur_p| via |r|. */ + d += total_demerits(r); + if (d <= minimal_demerits[fit_class]) { + minimal_demerits[fit_class] = d; + best_place[fit_class] = break_node(r); + best_pl_line[fit_class] = l; + if (do_last_line_fit) { + /*tex + + Store additional data for this feasible break. For each + feasible break we record the shortfall and glue stretch or + shrink (or adjustment). + + */ + best_pl_short[fit_class] = shortfall; + best_pl_glue[fit_class] = g; + } + if (d < minimum_demerits) + minimum_demerits = d; + } + /*tex Record a new feasible break. */ + if (node_r_stays_active) { + /*tex |prev_r| has been set to |r|. */ + continue; + } + DEACTIVATE: + /*tex + + Deactivate node |r|. When an active node disappears, we must delete + an adjacent delta node if the active node was at the beginning or the + end of the active list, or if it was surrounded by delta nodes. We + also must preserve the property that |cur_active_width| represents + the length of material from |vlink(prev_r)| to~|cur_p|. + + */ + vlink(prev_r) = vlink(r); + flush_node(r); + if (prev_r == active) { + /*tex + + Update the active widths, since the first active node has been + deleted. The following code uses the fact that + |type(active)<>delta_node|. If the active list has just become + empty, we do not need to update the |active_width| array, since + it will be initialized when an active node is next inserted. + + */ + r = vlink(active); + if (type(r) == delta_node) { + do_all_eight(update_active); + do_all_eight(copy_to_cur_active); + vlink(active) = vlink(r); + flush_node(r); + } + } else if (type(prev_r) == delta_node) { + r = vlink(prev_r); + if (r == active) { + do_all_eight(downdate_width); + vlink(prev_prev_r) = active; + flush_node(prev_r); + prev_r = prev_prev_r; + } else if (type(r) == delta_node) { + do_all_eight(update_width); + do_all_eight(combine_two_deltas); + vlink(prev_r) = vlink(r); + flush_node(r); + } + } + } +} + +void ext_do_line_break( + int paragraph_dir, + int pretolerance, + int tracing_paragraphs, + int tolerance, + scaled emergency_stretch, + int looseness, + int adjust_spacing, + halfword par_shape_ptr, + int adj_demerits, + int protrude_chars, + int line_penalty, + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, + int hang_indent, + int hsize, + int hang_after, + halfword left_skip, + halfword right_skip, + halfword inter_line_penalties_ptr, + int inter_line_penalty, + int club_penalty, + halfword club_penalties_ptr, + halfword widow_penalties_ptr, + int widow_penalty, + int broken_penalty, + halfword final_par_glue +) +{ + /*tex Miscellaneous nodes of temporary interest. */ + halfword cur_p, q, r, s; + int line_break_dir = paragraph_dir; + /*tex Get ready to start */ + minimum_demerits = awful_bad; + minimal_demerits[tight_fit] = awful_bad; + minimal_demerits[decent_fit] = awful_bad; + minimal_demerits[loose_fit] = awful_bad; + minimal_demerits[very_loose_fit] = awful_bad; + fewest_demerits = 0; + actual_looseness = 0; + /*tex + + We compute the values of |easy_line| and the other local variables + relating to line length when the |line_break| procedure is initializing + itself. + + */ + if (par_shape_ptr == null) { + if (hang_indent == 0) { + last_special_line = 0; + second_width = hsize; + second_indent = 0; + } else { + halfword used_hang_indent = swap_hang_indent(hang_indent); + /*tex + + Set line length parameters in preparation for hanging + indentation. We compute the values of |easy_line| and the other + local variables relating to line length when the |line_break| + procedure is initializing itself. + + */ + last_special_line = abs(hang_after); + if (hang_after < 0) { + first_width = hsize - abs(used_hang_indent); + if (used_hang_indent >= 0) + first_indent = used_hang_indent; + else + first_indent = 0; + second_width = hsize; + second_indent = 0; + } else { + first_width = hsize; + first_indent = 0; + second_width = hsize - abs(used_hang_indent); + if (used_hang_indent >= 0) + second_indent = used_hang_indent; + else + second_indent = 0; + } + } + } else { + last_special_line = vinfo(par_shape_ptr + 1) - 1; + second_indent = varmem[(par_shape_ptr + 2 * (last_special_line + 1))].cint; + second_width = varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint; + second_indent = swap_parshape_indent(second_indent,second_width); + } + if (looseness == 0) + easy_line = last_special_line; + else + easy_line = max_halfword; + no_shrink_error_yet = true; + check_shrinkage(left_skip); + check_shrinkage(right_skip); + q = left_skip; + r = right_skip; + background[1] = width(q) + width(r); + background[2] = 0; + background[3] = 0; + background[4] = 0; + background[5] = 0; + background[6] = 0; + background[2 + stretch_order(q)] = stretch(q); + background[2 + stretch_order(r)] += stretch(r); + background[7] = shrink(q) + shrink(r); + if (adjust_spacing > 1) { + background[8] = 0; + background[9] = 0; + max_stretch_ratio = -1; + max_shrink_ratio = -1; + cur_font_step = -1; + set_prev_char_p(null); + } + /*tex + + Check for special treatment of last line of paragraph. The new algorithm + for the last line requires that the stretchability |par_fill_skip| is + infinite and the stretchability of |left_skip| plus |right_skip| is + finite. + + */ + do_last_line_fit = false; + if (last_line_fit > 0) { + q = last_line_fill; + if ((stretch(q) > 0) && (stretch_order(q) > normal)) { + if ((background[3] == 0) && (background[4] == 0) && (background[5] == 0) && (background[6] == 0)) { + do_last_line_fit = true; + fill_width[0] = 0; + fill_width[1] = 0; + fill_width[2] = 0; + fill_width[3] = 0; + fill_width[stretch_order(q) - 1] = stretch(q); + } + } + } + /*tex Initialize |dir_ptr| for |line_break|. */ + if (dir_ptr != null) { + flush_node_list(dir_ptr); + dir_ptr = null; + } + /*tex Find optimal breakpoints. */ + threshold = pretolerance; + if (threshold >= 0) { + if (tracing_paragraphs > 0) { + begin_diagnostic(); + tprint_nl("@firstpass"); + } + second_pass = false; + final_pass = false; + } else { + threshold = tolerance; + second_pass = true; + final_pass = (emergency_stretch <= 0); + if (tracing_paragraphs > 0) + begin_diagnostic(); + } + while (1) { + halfword first_p; + halfword nest_stack[10]; + int nest_index = 0; + if (threshold > inf_bad) + threshold = inf_bad; + /*tex Create an active breakpoint representing the beginning of the paragraph. */ + q = new_node(unhyphenated_node, decent_fit); + vlink(q) = active; + break_node(q) = null; + line_number(q) = cur_list.pg_field + 1; + total_demerits(q) = 0; + active_short(q) = 0; + active_glue(q) = 0; + vlink(active) = q; + do_all_eight(store_background); + passive = null; + printed_node = temp_head; + pass_number = 0; + font_in_short_display = null_font; + /*tex Create an active breakpoint representing the beginning of the paragraph. */ + auto_breaking = true; + cur_p = vlink(temp_head); + /*tex Initialize with first |local_paragraph| node. */ + if ((cur_p != null) && (type(cur_p) == local_par_node)) { + /*tex This used to be an assert, but may as well force it. */ + alink(cur_p) = temp_head; + internal_pen_inter = local_pen_inter(cur_p); + internal_pen_broken = local_pen_broken(cur_p); + init_internal_left_box = local_box_left(cur_p); + init_internal_left_box_width = local_box_left_width(cur_p); + internal_left_box = init_internal_left_box; + internal_left_box_width = init_internal_left_box_width; + internal_right_box = local_box_right(cur_p); + internal_right_box_width = local_box_right_width(cur_p); + } else { + internal_pen_inter = 0; + internal_pen_broken = 0; + init_internal_left_box = null; + init_internal_left_box_width = 0; + internal_left_box = init_internal_left_box; + internal_left_box_width = init_internal_left_box_width; + internal_right_box = null; + internal_right_box_width = 0; + } + /*tex Initialize with first |local_paragraph| node. */ + set_prev_char_p(null); + first_p = cur_p; + /*tex + + To access the first node of paragraph as the first active node has + |break_node=null|. + + */ + while ((cur_p != null) && (vlink(active) != active)) { + /*tex + + |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, + also look at |disc_node|s. + + */ + while (is_char_node(cur_p)) { + /*tex + + Advance |cur_p| to the node following the present string of + characters. The code that passes over the characters of words + in a paragraph is part of \TeX's inner loop, so it has been + streamlined for speed. We use the fact that + `\.{\\parfillskip}' glue appears at the end of each + paragraph; it is therefore unnecessary to check if + |vlink(cur_p)=null| when |cur_p| is a character node. + + */ + active_width[1] += pack_width(line_break_dir, dir_TRT, cur_p, true); + if ((adjust_spacing > 1) && check_expand_pars(font(cur_p))) { + set_prev_char_p(cur_p); + add_char_stretch(active_width[8], cur_p); + add_char_shrink(active_width[9], cur_p); + } + cur_p = vlink(cur_p); + while (cur_p == null && nest_index > 0) { + cur_p = nest_stack[--nest_index]; + } + } + if (cur_p == null) { + normal_error("linebreak","invalid list tail, probably missing glue"); + } + /*tex + + Determine legal breaks: As we move through the hlist, we need to + keep the |active_width| array up to date, so that the badness of + individual lines is readily calculated by |try_break|. It is + convenient to use the short name |active_width[1]| for the + component of active width that represents real width as opposed + to glue. + + */ + switch (type(cur_p)) { + case hlist_node: + case vlist_node: + active_width[1] += pack_width(line_break_dir, box_dir(cur_p), cur_p, false); + break; + case rule_node: + active_width[1] += width(cur_p); + break; + case dir_node: + /*tex Adjust the dir stack for the |line_break| routine. */ + if (subtype(cur_p) == normal_dir) { + line_break_dir = dir_dir(cur_p); + /* Adds to |dir_ptr|. */ + push_dir_node(dir_ptr,cur_p); + } else { + pop_dir_node(dir_ptr); + if (dir_ptr != null) { + line_break_dir = dir_dir(dir_ptr); + } + } + break; + case local_par_node: + /*tex Advance past a |local_paragraph| node. */ + internal_pen_inter = local_pen_inter(cur_p); + internal_pen_broken = local_pen_broken(cur_p); + internal_left_box = local_box_left(cur_p); + internal_left_box_width = local_box_left_width(cur_p); + internal_right_box = local_box_right(cur_p); + internal_right_box_width = local_box_right_width(cur_p); + break; + case math_node: + auto_breaking = (subtype(cur_p) == after); + /*tex begin mathskip code */ + if (glue_is_zero(cur_p) || ignore_math_skip(cur_p)) { + kern_break(); + break; + } else { + /*tex fall through */ + } + /*tex end mathskip code */ + case glue_node: + /*tex + + If node |cur_p| is a legal breakpoint, call |try_break|; + then update the active widths by including the glue in + |glue_ptr(cur_p)|. + + When node |cur_p| is a glue node, we look at the previous + to see whether or not a breakpoint is legal at |cur_p|, + as explained above. + + We only break after certain nodes (see texnodes.h), a + font related kern and a dir node when + |\breakafterdirmode=1|. + + */ + if (auto_breaking) { + halfword prev_p = alink(cur_p); + if (prev_p != temp_head && (is_char_node(prev_p) + || precedes_break(prev_p) || precedes_kern(prev_p) || precedes_dir(prev_p))) { + ext_try_break( + 0, + unhyphenated_node, + line_break_dir, + adjust_spacing, + par_shape_ptr, + adj_demerits, + tracing_paragraphs, + protrude_chars, + line_penalty, + last_line_fit, + double_hyphen_demerits, + final_hyphen_demerits, + first_p, + cur_p + ); + } + } + check_shrinkage(cur_p); + active_width[1] += width(cur_p); + active_width[2 + stretch_order(cur_p)] += stretch(cur_p); + active_width[7] += shrink(cur_p); + break; + case kern_node: + if (subtype(cur_p) == explicit_kern || subtype(cur_p) == italic_kern) { + kern_break(); + } else { + active_width[1] += width(cur_p); + if ((adjust_spacing == 2) && (subtype(cur_p) == normal)) { + add_kern_stretch(active_width[8], cur_p); + add_kern_shrink(active_width[9], cur_p); + } + } + break; + case disc_node: + /*tex + + |select_disc|s are handled by the leading |init_disc|. + + */ + if (subtype(cur_p) == select_disc) + break; + /*tex + + Try to break after a discretionary fragment, then |goto + done5|. The following code knows that discretionary texts + contain only character nodes, kern nodes, box nodes, and + rule nodes. This branch differs a bit from older engines + because in \LUATEX\ we already have hyphenated the list. + This means that we need to skip automatic disc nodes. Of + better, we need to treat discretionaries and explicit + hyphens always, even in the first pass. + + */ + if (second_pass || subtype(cur_p) <= automatic_disc) { + int actual_penalty = (int) disc_penalty(cur_p); + s = vlink_pre_break(cur_p); + do_one_seven_eight(reset_disc_width); + if (s == null) { + /*tex trivial pre-break */ + ext_try_break(actual_penalty, hyphenated_node, + line_break_dir, adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, protrude_chars, + line_penalty, last_line_fit, + double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + } else { + add_to_widths(s, line_break_dir, adjust_spacing, disc_width); + do_one_seven_eight(add_disc_width_to_active_width); + ext_try_break(actual_penalty, hyphenated_node, + line_break_dir, adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, protrude_chars, + line_penalty, last_line_fit, + double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + if (subtype(cur_p) == init_disc) { + /*tex + + We should at two break points after the one + we added above: + + \startitemize[n] + \startitem + which does a possible break in INIT's + |post_break| + \stopitem + \startitem + which means the |no_break| actually + was broken just a character later + \stopitem + \stopitemize + + Do the select-0 case |f-f-i|: + + */ + s = vlink_pre_break(vlink(cur_p)); + add_to_widths(s, line_break_dir, adjust_spacing, disc_width); + ext_try_break(actual_penalty, hyphenated_node, + line_break_dir, adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, + protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, + vlink(cur_p)); + /*tex This does not work. */ +#if 0 + /*tex Go back to the starting situation. */ + do_one_seven_eight(sub_disc_width_from_active_width); + do_one_seven_eight(reset_disc_width); + /*tex Add select |no_break| to |active_width|. */ + s = vlink_no_break(vlink(cur_p)); + add_to_widths(s, line_break_dir, adjust_spacing, disc_width); + ext_try_break(actual_penalty, hyphenated_node, + line_break_dir, adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, + protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, + vlink(cur_p)); +#endif + } + do_one_seven_eight(sub_disc_width_from_active_width); + } + } + s = vlink_no_break(cur_p); + add_to_widths(s, line_break_dir, adjust_spacing, active_width); + break; + case penalty_node: + ext_try_break(penalty(cur_p), unhyphenated_node, line_break_dir, + adjust_spacing, par_shape_ptr, adj_demerits, + tracing_paragraphs, protrude_chars, + line_penalty, last_line_fit, + double_hyphen_demerits, final_hyphen_demerits, + first_p, cur_p); + break; + case boundary_node: + case whatsit_node: + /*tex Advance past a whatsit node in the |line_break| loop. */ + case mark_node: + case ins_node: + case adjust_node: + break; + case glue_spec_node: + normal_warning("parbuilder","found a glue_spec in a paragraph"); + break; + default: + formatted_error("parbuilder","weird node %d in paragraph",type(cur_p)); + } + cur_p = vlink(cur_p); + while (cur_p == null && nest_index > 0) { + cur_p = nest_stack[--nest_index]; + } + } + if (cur_p == null) { + /*tex + + Try the final line break at the end of the paragraph, and |goto + done| if the desired breakpoints have been found. + + The forced line break at the paragraph's end will reduce the list + of breakpoints so that all active nodes represent breaks at + |cur_p=null|. On the first pass, we insist on finding an active + node that has the correct ``looseness.'' On the final pass, there + will be at least one active node, and we will match the desired + looseness as well as we can. + + The global variable |best_bet| will be set to the active node for + the best way to break the paragraph, and a few other variables + are used to help determine what is best. + + */ + ext_try_break(eject_penalty, hyphenated_node, line_break_dir, + adjust_spacing, par_shape_ptr, adj_demerits, + tracing_paragraphs, protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + if (vlink(active) != active) { + /*tex Find an active node with fewest demerits; */ + r = vlink(active); + fewest_demerits = awful_bad; + do { + if (type(r) != delta_node) { + if (total_demerits(r) < fewest_demerits) { + fewest_demerits = total_demerits(r); + best_bet = r; + } + } + r = vlink(r); + } while (r != active); + best_line = line_number(best_bet); + /*tex + Find an active node with fewest demerits; + */ + if (looseness == 0) + goto DONE; + /*tex + + Find the best active node for the desired looseness; + + The adjustment for a desired looseness is a slightly more + complicated version of the loop just considered. Note that if + a paragraph is broken into segments by displayed equations, + each segment will be subject to the looseness calculation, + independently of the other segments. + + */ + r = vlink(active); + actual_looseness = 0; + do { + if (type(r) != delta_node) { + line_diff = line_number(r) - best_line; + if (((line_diff < actual_looseness) + && (looseness <= line_diff)) + || ((line_diff > actual_looseness) + && (looseness >= line_diff))) { + best_bet = r; + actual_looseness = line_diff; + fewest_demerits = total_demerits(r); + } else if ((line_diff == actual_looseness) && + (total_demerits(r) < fewest_demerits)) { + best_bet = r; + fewest_demerits = total_demerits(r); + } + } + r = vlink(r); + } while (r != active); + best_line = line_number(best_bet); + /*tex + Find the best active node for the desired looseness. + */ + if ((actual_looseness == looseness) || final_pass) + goto DONE; + } + } + /*tex Clean up the memory by removing the break nodes. */ + clean_up_the_memory(); + /*tex Clean up the memory by removing the break nodes. */ + if (!second_pass) { + if (tracing_paragraphs > 0) + tprint_nl("@secondpass"); + threshold = tolerance; + second_pass = true; + final_pass = (emergency_stretch <= 0); + } else { + /*tex If at first you do not succeed, then: */ + if (tracing_paragraphs > 0) + tprint_nl("@emergencypass"); + background[2] += emergency_stretch; + final_pass = true; + } + } + + DONE: + if (tracing_paragraphs > 0) { + end_diagnostic(true); + normalize_selector(); + } + if (do_last_line_fit) { + /*tex + Adjust the final line of the paragraph; here we either reset + |do_last_line_fit| or adjust the |par_fill_skip| glue. + */ + if (active_short(best_bet) == 0) { + do_last_line_fit = false; + } else { + width(last_line_fill) += (active_short(best_bet) - active_glue(best_bet)); + stretch(last_line_fill) = 0; + } + } + /*tex + Break the paragraph at the chosen. Once the best sequence of + breakpoints has been found (hurray), we call on the procedure + |post_line_break| to finish the remainder of the work. By introducing + this subprocedure, we are able to keep |line_break| from getting + extremely long. + + the first thing |ext_post_line_break| does is reset |dir_ptr|. + + */ + flush_node_list(dir_ptr); + dir_ptr = null; + ext_post_line_break(paragraph_dir, + right_skip, + left_skip, + protrude_chars, + par_shape_ptr, + adjust_spacing, + inter_line_penalties_par_ptr, + inter_line_penalty, + club_penalty, + club_penalties_ptr, + widow_penalties_ptr, + widow_penalty, + broken_penalty, + final_par_glue, + best_bet, + last_special_line, + second_width, + second_indent, first_width, first_indent, best_line); + /*tex + + Clean up the memory by removing the break nodes. + + */ + clean_up_the_memory(); +} + +void get_linebreak_info (int *f, int *a) +{ + *f = fewest_demerits; + *a = actual_looseness; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.h b/Build/source/texk/web2c/luatexdir/tex/linebreak.h new file mode 100644 index 00000000000..d1e1768f704 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.h @@ -0,0 +1,98 @@ +/* linebreak.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef LINEBREAK_H +# define LINEBREAK_H + +extern halfword just_box; /* the |hlist_node| for the last line of the new paragraph */ + +extern void line_break(boolean d, int line_break_context); + +# define inf_bad 10000 /* infinitely bad value */ +# define awful_bad 07777777777 /* more than a billion demerits */ + +extern void initialize_active(void); + +extern void ext_do_line_break(int paragraph_dir, + int pretolerance, + int tracing_paragraphs, + int tolerance, + scaled emergency_stretch, + int looseness, + /* + int hyphen_penalty, + int ex_hyphen_penalty, + */ + int adjust_spacing, + halfword par_shape_ptr, + int adj_demerits, + int protrude_chars, + int line_penalty, + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, + int hang_indent, + int hsize, + int hang_after, + halfword left_skip, + halfword right_skip, + halfword inter_line_penalties_ptr, + int inter_line_penalty, + int club_penalty, + halfword club_penalties_ptr, + halfword widow_penalties_ptr, + int widow_penalty, + int broken_penalty, halfword final_par_glue); + +extern void get_linebreak_info(int *, int *); +extern halfword find_protchar_left(halfword l, boolean d); +extern halfword find_protchar_right(halfword l, halfword r); + +/* skipable nodes at the margins during character protrusion */ + +# define zero_dimensions(a) ( \ + (width((a)) == 0) && \ + (height((a)) == 0) && \ + (depth((a)) == 0) \ +) + +# define empty_disc(a) ( \ + (vlink_pre_break(a) == null) && \ + (vlink_post_break(a) == null) && \ + (vlink_no_break(a) == null) \ +) + +# define cp_skipable(a) ( (! is_char_node((a))) && ( \ + ((type((a)) == glue_node) && (glue_is_zero((a)))) \ + || (type((a)) == penalty_node) \ + || ((type((a)) == disc_node) && empty_disc(a)) \ + || ((type((a)) == kern_node) && ((width((a)) == 0) || (subtype((a)) == normal))) \ + || ((type((a)) == rule_node) && zero_dimensions(a)) \ + || ((type((a)) == math_node) && (surround((a)) == 0 || (glue_is_zero((a))))) \ + || (type((a)) == dir_node) \ + || ((type((a)) == hlist_node) && (list_ptr((a)) == null) && zero_dimensions(a)) \ + || (type((a)) == local_par_node) \ + || (type((a)) == ins_node) \ + || (type((a)) == mark_node) \ + || (type((a)) == adjust_node) \ + || (type((a)) == boundary_node) \ + || (type((a)) == whatsit_node) \ +) ) + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/mainbody.c b/Build/source/texk/web2c/luatexdir/tex/mainbody.c new file mode 100644 index 00000000000..2f45f9989d2 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mainbody.c @@ -0,0 +1,780 @@ +/* + +mainbody.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +This is where the action starts. We're speaking of \LUATEX, a continuation of +\PDFTEX\ (which included \ETEX) and \ALEPH. As \TEX, \LUATEX\ is a document +compiler intended to simplify high quality typesetting for many of the world's +languages. It is an extension of D. E. Knuth's \TEX, which was designed +essentially for the typesetting of languages using the Latin alphabet. Although +it is a direct decendant of \TEX, and therefore mostly compatible, there are +some subtle differences that relate to \UNICODE\ support and \OPENTYPE\ math. + +The \ALEPH\ subsystem loosens many of the restrictions imposed by~\TeX: register +numbers are no longer limited to 8~bits. Fonts may have more than 256~characters, +more than 256~fonts may be used, etc. We use a similar model. We also borrowed +the directional model but have upgraded it a bit as well as integrated it more +tightly. + +This program is directly derived from Donald E. Knuth's \TEX; the change history +which follows and the reward offered for finders of bugs refer specifically to +\TEX; they should not be taken as referring to \LUATEX, \PDFTEX, nor \ETEX, +although the change history is relevant in that it demonstrates the evolutionary +path followed. This program is not \TEX; that name is reserved strictly for the +program which is the creation and sole responsibility of Professor Knuth. + +\starttyping +% Version 0 was released in September 1982 after it passed a variety of tests. +% Version 1 was released in November 1983 after thorough testing. +% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984). +% Version 1.2 allowed `0' in response to an error, et alia (October 1984). +% Version 1.3 made memory allocation more flexible and local (November 1984). +% Version 1.4 fixed accents right after line breaks, et alia (April 1985). +% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985). +% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986). +% Version 2.1 corrected anomalies in discretionary breaks (January 1987). +% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987). +% Version 2.3 avoided incomplete page in premature termination (August 1987). +% Version 2.4 fixed \noaligned rules in indented displays (August 1987). +% Version 2.5 saved cur_order when expanding tokens (September 1987). +% Version 2.6 added 10sp slop when shipping leaders (November 1987). +% Version 2.7 improved rounding of negative-width characters (November 1987). +% Version 2.8 fixed weird bug if no \patterns are used (December 1987). +% Version 2.9 made \csname\endcsname's "relax" local (December 1987). +% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988). +% Version 2.92 fixed \patterns, also file names with complex macros (May 1988). +% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988). +% Version 2.94 kept open_log_file from calling fatal_error (November 1988). +% Version 2.95 solved that problem a better way (December 1988). +% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989). +% Version 2.97 corrected blunder in creating 2.95 (February 1989). +% Version 2.98 omitted save_for_after at outer level (March 1989). +% Version 2.99 caught $$\begingroup\halign..$$ (June 1989). +% Version 2.991 caught .5\ifdim.6... (June 1989). +% Version 2.992 introduced major changes for 8-bit extensions (September 1989). +% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989). +% Version 3.0 fixed unusual displays; was more \output robust (March 1990). +% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990). +% Version 3.14 fixed unprintable font names and corrected typos (March 1991). +% Version 3.141 more of same; reconstituted ligatures better (March 1992). +% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993). +% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995). +% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002). +% Version 3.1415926 was a general cleanup with minor fixes (February 2008). +\stoptyping + +Although considerable effort has been expended to make the LuaTeX program correct +and reliable, no warranty is implied; the authors disclaim any obligation or +liability for damages, including but not limited to special, indirect, or +consequential damages arising out of or in connection with the use or performance +of this software. This work has been a ``labor of love'' and the authors hope +that users enjoy it. + +{\em You will find a lot of comments that originate in original \TEX. We kept them +as a side effect of the conversion from \WEB\ to \CWEB. Because there is not much +webbing going on here eventually the files became regular \CCODE\ files with still +potentially typeset comments. As we add our own comments, and also comments are +there from \PDFTEX, \ALEPH\ and \ETEX, we get a curious mix. The best comments are +of course from Don Knuth. All bad comments are ours. All errors are ours too! + +Not all comments make sense, because some things are implemented differently, for +instance some memory management. But the principles of tokens and nodes stayed. +It anyway means that you sometimes need to keep in mind that the explanation is +more geared to traditional \TEX. But that's not a bad thing. Sorry Don for any +confusion we introduced. The readers should have a copy of the \TEX\ books at hand +anyway.} + +A large piece of software like \TeX\ has inherent complexity that cannot be +reduced below a certain level of difficulty, although each individual part is +fairly simple by itself. The \.{WEB} language is intended to make the algorithms +as readable as possible, by reflecting the way the individual program pieces fit +together and by providing the cross-references that connect different parts. +Detailed comments about what is going on, and about why things were done in +certain ways, have been liberally sprinkled throughout the program. These +comments explain features of the implementation, but they rarely attempt to +explain the \TeX\ language itself, since the reader is supposed to be familiar +with {\sl The \TeX book}. + +The present implementation has a long ancestry, beginning in the summer of~1977, +when Michael~F. Plass and Frank~M. Liang designed and coded a prototype @^Plass, +Michael Frederick@> @^Liang, Franklin Mark@> @^Knuth, Donald Ervin@> based on +some specifications that the author had made in May of that year. This original +proto\TeX\ included macro definitions and elementary manipulations on boxes and +glue, but it did not have line-breaking, page-breaking, mathematical formulas, +alignment routines, error recovery, or the present semantic nest; furthermore, it +used character lists instead of token lists, so that a control sequence like +\.{\\halign} was represented by a list of seven characters. A complete version of +\TeX\ was designed and coded by the author in late 1977 and early 1978; that +program, like its prototype, was written in the {\mc SAIL} language, for which an +excellent debugging system was available. Preliminary plans to convert the {\mc +SAIL} code into a form somewhat like the present ``web'' were developed by Luis +Trabb~Pardo and @^Trabb Pardo, Luis Isidoro@> the author at the beginning of +1979, and a complete implementation was created by Ignacio~A. Zabala in 1979 and +1980. The \TeX82 program, which @^Zabala Salelles, Ignacio Andr\'es@> was written +by the author during the latter part of 1981 and the early part of 1982, also +incorporates ideas from the 1979 implementation of @^Guibas, Leonidas Ioannis@> +@^Sedgewick, Robert@> @^Wyatt, Douglas Kirk@> \TeX\ in {\mc MESA} that was +written by Leonidas Guibas, Robert Sedgewick, and Douglas Wyatt at the Xerox Palo +Alto Research Center. Several hundred refinements were introduced into \TeX82 +based on the experiences gained with the original implementations, so that +essentially every part of the system has been substantially improved. After the +appearance of ``Version 0'' in September 1982, this program benefited greatly +from the comments of many other people, notably David~R. Fuchs and Howard~W. +Trickey. A final revision in September 1989 extended the input character set to +eight-bit codes and introduced the ability to hyphenate words from different +languages, based on some ideas of Michael~J. Ferguson. @^Fuchs, David Raymond@> +@^Trickey, Howard Wellington@> @^Ferguson, Michael John@> + +No doubt there still is plenty of room for improvement, but the author is firmly +committed to keeping \TeX82 ``frozen'' from now on; stability and reliability are +to be its main virtues. +On the other hand, the \.{WEB} description can be extended without changing the +core of \TeX82 itself, and the program has been designed so that such extensions +are not extremely difficult to make. The |banner| string defined here should be +changed whenever \TeX\ undergoes any modifications, so that it will be clear +which version of \TeX\ might be the guilty party when a problem arises. +@^extensions to \TeX@> @^system dependencies@> + +This program contains code for various features extending \TeX, therefore this +program is called `\eTeX' and not `\TeX'; the official name `\TeX' by itself is +reserved for software systems that are fully compatible with each other. A +special test suite called the ``\.{TRIP} test'' is available for helping to +determine whether a particular implementation deserves to be known as `\TeX' +[cf.~Stanford Computer Science report CS1027, November 1984]. + +A similar test suite called the ``\.{e-TRIP} test'' is available for helping to +determine whether a particular implementation deserves to be known as `\eTeX'. + +This is the first of many sections of \TeX\ where global variables are defined. + +*/ + +/*tex Are we using lua for initializations? */ + +boolean luainit; + +/*tex Print file open and close info? */ + +boolean tracefilenames; + +/*tex + +This program has two important variations: (1) There is a long and slow version +called \.{INITEX}, which does the extra calculations needed to @.INITEX@> +initialize \TeX's internal tables; and (2)~there is a shorter and faster +production version, which cuts the initialization to a bare minimum. + +*/ + +/*tex are we \.{INITEX}? */ + +boolean ini_version; + +/*tex was the dump name option used? */ + +boolean dump_option; + +/*tex was a \.{\%\AM format} line seen? */ + +boolean dump_line; + +/*tex temporary for setup */ + +int bound_default; + +/*tex temporary for setup */ + +char *bound_name; + +/*tex width of context lines on terminal error messages */ + +int error_line; + +/*tex + width of first lines of contexts in terminal error messages; should be + between 30 and |error_line-15| +*/ + +int half_error_line; + +/*tex width of longest text lines output; should be at least 60 */ + +int max_print_line; + +/*tex maximum number of strings; must not exceed |max_halfword| */ + +int max_strings; + +/*tex strings available after format loaded */ + +int strings_free; + +/*tex loop variable for initialization */ + +int font_k; + +/*tex + putting a character in front of a macro trace (feature suggested by PO on + tex implementor list +*/ + +int level_max; +int level_chr; + +/*tex + maximum number of characters simultaneously present in current lines of open + files and in control sequences between \.{\\csname} and \.{\\endcsname}; must + not exceed |max_halfword| +*/ + +int buf_size; + +/*tex maximum number of simultaneous input sources */ + +int stack_size; + +/*tex + maximum number of input files and error insertions that can be going on + simultaneously +*/ + +int max_in_open; + +/*tex maximum number of simultaneous macro parameters */ + +int param_size; + +/*tex maximum number of semantic levels simultaneously active */ + +int nest_size; + +/*tex + space for saving values outside of current group; must be at most + |max_halfword| +*/ + +int save_size; + +/*tex limits recursive calls of the |expand| procedure */ + +int expand_depth; + +/*tex parse the first line for options */ + +int parsefirstlinep; + +/*tex format messages as file:line:error */ + +int filelineerrorstylep; + +/*tex stop at first error */ + +int haltonerrorp; + +/*tex current filename is quoted */ + +boolean quoted_filename; + +int get_luatexversion(void) +{ + return luatex_version; +} + +/*tex the number of pages that have been shipped out */ + +int total_pages = 0; + +/*tex recent outputs that didn't ship anything out */ + +int dead_cycles = 0; + +str_number get_luatexrevision(void) +{ + return luatex_revision; +} + +/*tex + +This is it: the part of \TeX\ that executes all those procedures we have written. + +We have noted that there are two versions of \TeX82. One, called \.{INITEX}, +@.INITEX@> has to be run first; it initializes everything from scratch, without +reading a format file, and it has the capability of dumping a format file. The +other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs +@.VIRTEX@> to input a format file in order to get started. + +For \LUATEX\ it is important to know that we still dump a format. But, in order +to gain speed and a smaller footprint, we gzip the format (level 3). We also +store some information that makes an abort possible in case of an incompatible +engine version, which is important as \LUATEX\ develops. It is possible to store +\LUA\ code in the format but not the current upvalues so you still need to +initialize. Also, traditional fonts are stored, as are extended fonts but any +additional information needed for instance to deal with \OPENTYPE\ fonts is to be +handled by \LUA\ code and therefore not present in the format. + +*/ + +#define const_chk(A) do { \ + if (A < inf_##A) \ + A = inf_##A; \ + if (A > sup_##A) \ + A = sup_##A; \ +} while (0) + +#define setup_bound_var(A,B,C) do { \ + if (luainit>0) { \ + get_lua_number("texconfig",B,&C); \ + if (C==0) \ + C=A; \ + } else { \ + integer x; \ + setupboundvariable(&x, B, A); \ + C = (int)x; \ + } \ +} while (0) + +int ready_already = 0; + +int main_initialize(void) +{ + /* + In case somebody has inadvertently made bad settings of the + ``constants,'' \LUATEX\ checks them using a variable called |bad|. + */ + int bad = 0; + /*tex + Bounds that may be set from the configuration file. We want the user to + be able to specify the names with underscores, but \.{TANGLE} removes + underscores, so we're stuck giving the names twice, once as a string, + once as the identifier. How ugly. (We can change that now.) + */ + setup_bound_var(15000, "max_strings", max_strings); + setup_bound_var(100, "strings_free", strings_free); + setup_bound_var(3000, "buf_size", buf_size); + setup_bound_var(50, "nest_size", nest_size); + setup_bound_var(15, "max_in_open", max_in_open); + setup_bound_var(60, "param_size", param_size); + setup_bound_var(4000, "save_size", save_size); + setup_bound_var(300, "stack_size", stack_size); + setup_bound_var(16384, "dvi_buf_size", dvi_buf_size); + setup_bound_var(79, "error_line", error_line); + setup_bound_var(50, "half_error_line", half_error_line); + setup_bound_var(79, "max_print_line", max_print_line); + setup_bound_var(0, "hash_extra", hash_extra); + setup_bound_var(72, "pk_dpi", pk_dpi); + setup_bound_var(10000, "expand_depth", expand_depth); + setup_bound_var(0, "level_max", level_max); + setup_bound_var('.', "level_chr", level_chr); + /*tex + Check other constants against their sup and inf. + */ + const_chk(buf_size); + const_chk(nest_size); + const_chk(max_in_open); + const_chk(param_size); + const_chk(save_size); + const_chk(stack_size); + const_chk(dvi_buf_size); + const_chk(max_strings); + const_chk(strings_free); + const_chk(hash_extra); + const_chk(pk_dpi); + if (error_line > ssup_error_line) { + error_line = ssup_error_line; + } + /*tex + Array memory allocation + */ + buffer = xmallocarray(packed_ASCII_code, (unsigned) buf_size); + nest = xmallocarray(list_state_record, (unsigned) nest_size); + save_stack = xmallocarray(save_record, (unsigned) save_size); + input_stack = xmallocarray(in_state_record, (unsigned) stack_size); + input_file = xmallocarray(alpha_file, (unsigned) max_in_open); + input_file_callback_id = xmallocarray(int, (unsigned) max_in_open); + line_stack = xmallocarray(int, (unsigned) max_in_open); + eof_seen = xmallocarray(boolean, (unsigned) max_in_open); + grp_stack = xmallocarray(save_pointer, (unsigned) max_in_open); + if_stack = xmallocarray(pointer, (unsigned) max_in_open); + source_filename_stack = xmallocarray(str_number, (unsigned) max_in_open); + full_source_filename_stack = xmallocarray(char *, (unsigned) max_in_open); + param_stack = xmallocarray(halfword, (unsigned) param_size); + dvi_buf = xmallocarray(eight_bits, (unsigned) dvi_buf_size); + /*tex + Only in ini mode: + */ + if (ini_version) { + fixmem = xmallocarray(smemory_word, fix_mem_init + 1); + memset(voidcast(fixmem), 0, (fix_mem_init + 1) * sizeof(smemory_word)); + fix_mem_min = 0; + fix_mem_max = fix_mem_init; + eqtb_top = eqtb_size + hash_extra; + if (hash_extra == 0) + hash_top = undefined_control_sequence; + else + hash_top = eqtb_top; + hash = xmallocarray(two_halves, (unsigned) (hash_top + 1)); + memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); + eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); + memset(eqtb, 0, sizeof(memory_word) * (unsigned) (eqtb_top + 1)); + init_string_pool_array((unsigned) max_strings); + reset_cur_string(); + } + /*tex + Check the ``constant'' values... + */ + if ((half_error_line < 30) || (half_error_line > error_line - 15)) + bad = 1; + if (max_print_line < 60) + bad = 2; + if (dvi_buf_size % 8 != 0) + bad = 3; + if (hash_prime > hash_size) + bad = 5; + if (max_in_open >= (sup_max_in_open+1)) /* 128 */ + bad = 6; + /*tex + Here are the inequalities that the quarterword and halfword values + must satisfy (or rather, the inequalities that they mustn't satisfy): + */ + if ((min_quarterword > 0) || (max_quarterword < 0x7FFF)) + bad = 11; + if ((min_halfword > 0) || (max_halfword < 0x3FFFFFFF)) + bad = 12; + if ((min_quarterword < min_halfword) || (max_quarterword > max_halfword)) + bad = 13; + if (font_base < min_quarterword) + bad = 15; + if ((save_size > max_halfword) || (max_strings > max_halfword)) + bad = 17; + if (buf_size > max_halfword) + bad = 18; + if (max_quarterword - min_quarterword < 0xFFFF) + bad = 19; + if (cs_token_flag + eqtb_size + hash_extra > max_halfword) + bad = 21; + if (bad > 0) { + wterm_cr(); + fprintf(term_out, + "Ouch---my internal constants have been clobbered! ---case %d", + (int) bad + ); + } else { + /*tex Set global variables to their starting values. */ + initialize(); + if (ini_version) { + /*tex Initialize all the primitives. */ + no_new_control_sequence = false; + first = 0; + initialize_commands(); + initialize_etex_commands(); + init_str_ptr = str_ptr; + no_new_control_sequence = true; + fix_date_and_time(); + } + ready_already = 314159; + } + return bad; +} + + +void main_body(void) +{ + static char pdftex_map[] = "pdftex.map"; + int bad = main_initialize(); + /*tex in case we quit during initialization */ + history = fatal_error_stop; + /*tex open the terminal for output */ + t_open_out(); + if (!luainit) + tracefilenames = true; + if (bad > 0) { + goto FINAL_END; + } + print_banner(luatex_version_string); + /*tex + Get the first line of input and prepare to start When we begin the + following code, \TeX's tables may still contain garbage; the strings + might not even be present. Thus we must proceed cautiously to get + bootstrapped in. + + But when we finish this part of the program, \TeX\ is ready to call on + the |main_control| routine to do its work. + */ + /*tex + This copies the command line, + */ + initialize_inputstack(); + if (buffer[iloc] == '*') + incr(iloc); + if ((format_ident == 0) || (buffer[iloc] == '&') || dump_line) { + char *fname = NULL; + if (format_ident != 0 && !ini_version) { + /*tex Erase preloaded format. */ + initialize(); + } + if ((fname = open_fmt_file()) == NULL) + goto FINAL_END; + if (!load_fmt_file(fname)) { + zwclose(fmt_file); + goto FINAL_END; + } + zwclose(fmt_file); + while ((iloc < ilimit) && (buffer[iloc] == ' ')) + incr(iloc); + } + if (output_mode_option != 0) + output_mode_par = output_mode_value; + if (draft_mode_option != 0) { + draft_mode_par = draft_mode_value; + } + /*tex can this be moved? */ + pdf_init_map_file((char *) pdftex_map); + /* */ + if (end_line_char_inactive) + decr(ilimit); + else + buffer[ilimit] = (packed_ASCII_code) end_line_char_par; + fix_date_and_time(); + random_seed = (microseconds * 1000) + (epochseconds % 1000000); + init_randoms(random_seed); + initialize_math(); + fixup_selector(log_opened_global); + check_texconfig_init(); + if ((iloc < ilimit) && (get_cat_code(cat_code_table_par, buffer[iloc]) != escape_cmd)) { + /*tex \.{\\input} assumed */ + start_input(); + } + /*tex Initialize |text_dir_ptr| */ + text_dir_ptr = new_dir(0); + /*tex Ready to go! */ + history = spotless; + /*tex Initialize synctex primitive */ + synctexinitcommand(); + /*tex Come to life. */ + main_control(); + flush_node(text_dir_ptr); + /*tex Prepare for death. */ + final_cleanup(); + close_files_and_terminate(); + FINAL_END: + do_final_end(); +} + +/*tex + +Here we do whatever is needed to complete \TeX's job gracefully on the local +operating system. The code here might come into play after a fatal error; it must +therefore consist entirely of ``safe'' operations that cannot produce error +messages. For example, it would be a mistake to call |str_room| or |make_string| +at this time, because a call on |overflow| might lead to an infinite loop. +@^system dependencies@> + +Actually there's one way to get error messages, via |prepare_mag|; but that can't +cause infinite recursion. @^recursion@> + +This program doesn't bother to close the input files that may still be open. + +*/ + +void close_files_and_terminate(void) +{ + int callback_id; + callback_id = callback_defined(stop_run_callback); + finalize_write_files(); + if (tracing_stats_par > 0) { + if (callback_id == 0) { + /*tex + Output statistics about this job. The present section goes + directly to the log file instead of using |print| commands, + because there's no need for these strings to take up + |string_pool| memory when a non-{\bf stat} version of \TeX\ is + being used. + */ + if (log_opened_global) { + fprintf(log_file, + "\n\nHere is how much of LuaTeX's memory you used:\n" + ); + fprintf(log_file, " %d string%s out of %d\n", + (int) (str_ptr - init_str_ptr), + (str_ptr == (init_str_ptr + 1) ? "" : "s"), + (int) (max_strings - init_str_ptr + STRING_OFFSET) + ); + fprintf(log_file, " %d,%d words of node,token memory allocated", + (int) var_mem_max, (int) fix_mem_max + ); + print_node_mem_stats(); + fprintf(log_file, + " %d multiletter control sequences out of %ld+%d\n", + (int) cs_count, (long) hash_size, (int) hash_extra + ); + fprintf(log_file, " %d font%s using %d bytes\n", + (int) max_font_id(), (max_font_id() == 1 ? "" : "s"), + (int) font_bytes + ); + fprintf(log_file, + " %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds\n", + (int) max_in_stack, (int) max_nest_stack, + (int) max_param_stack, (int) max_buf_stack, + (int) max_save_stack + 6, (int) stack_size, + (int) nest_size, (int) param_size, (int) buf_size, + (int) save_size + ); + } + } + } + wake_up_terminal(); + /*tex + Rubish, these \PDF arguments, passed, needs to be fixed, e.g. with a + dummy in \DVI. + */ + wrapup_backend(); + /*tex + Close {\sl Sync\TeX} file and write status. + */ + synctexterminate(log_opened_global); + /*tex + The following is needed because synctex removes files and we want to keep + them which means renaming a temp file .. we can't bypass the terminate + because it might do mem cleanup. + */ + if (synctex_get_mode() > 0) { + callback_id = callback_defined(finish_synctex_callback); + if (callback_id > 0) { + run_callback(callback_id, "->"); + } + } + /* free_text_codes(); */ + /* free_math_codes(); */ + if (log_opened_global) { + wlog_cr(); + selector = selector - 2; + if ((selector == term_only) && (callback_id == 0)) { + tprint_nl("Transcript written on "); + tprint_file_name(NULL, texmf_log_name, NULL); + print_char('.'); + print_ln(); + } + lua_a_close_out(log_file); + log_opened_global = false; + } + callback_id = callback_defined(wrapup_run_callback); + if (callback_id > 0) { + run_callback(callback_id, "->"); + } + free_text_codes(); + free_math_codes(); +} + +/*tex + +We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has been +scanned and |its_all_over|. + +*/ + +void final_cleanup(void) +{ + /*tex This one gets the value 0 for \.{\\end}, 1 for \.{\\dump}. */ + int c; + /*tex Here's one for looping marks: */ + halfword i; + /*tex This was a global temp_ptr: */ + halfword t; + c = cur_chr; + if (job_name == 0) + open_log_file(); + while (input_ptr > 0) + if (istate == token_list) + end_token_list(); + else + end_file_reading(); + while (open_parens > 0) { + report_stop_file(filetype_tex); + decr(open_parens); + } + if (cur_level > level_one) { + tprint_nl("(\\end occurred inside a group at level "); + print_int(cur_level - level_one); + print_char(')'); + show_save_groups(); + } + while (cond_ptr != null) { + tprint_nl("(\\end occurred when "); + print_cmd_chr(if_test_cmd, cur_if); + if (if_line != 0) { + tprint(" on line "); + print_int(if_line); + } + tprint(" was incomplete)"); + if_line = if_line_field(cond_ptr); + cur_if = subtype(cond_ptr); + t = cond_ptr; + cond_ptr = vlink(cond_ptr); + flush_node(t); + } + if (callback_defined(stop_run_callback) == 0) + if (history != spotless) + if ((history == warning_issued) || (interaction < error_stop_mode)) + if (selector == term_and_log) { + selector = term_only; + tprint_nl("(see the transcript file for additional information)"); + selector = term_and_log; + } + if (c == 1) { + if (ini_version) { + for (i = 0; i <= biggest_used_mark; i++) { + delete_top_mark(i); + delete_first_mark(i); + delete_bot_mark(i); + delete_split_first_mark(i); + delete_split_bot_mark(i); + } + for (c = last_box_code; c <= vsplit_code; c++) + flush_node_list(disc_ptr[c]); + if (last_glue != max_halfword) { + flush_node(last_glue); + } + /*tex Flush the pseudo files. */ + while (pseudo_files != null) { + pseudo_close(); + } + store_fmt_file(); + return; + } + tprint_nl("(\\dump is performed only by INITEX)"); + return; + } +} + +/*tex + +Once \TeX\ is working, you should be able to diagnose most errors with the +\.{\\show} commands and other diagnostic features. + +Because we have made some internal changes the optional debug interface +has been removed. + +*/ diff --git a/Build/source/texk/web2c/luatexdir/tex/mainbody.h b/Build/source/texk/web2c/luatexdir/tex/mainbody.h new file mode 100644 index 00000000000..0d274aeffbe --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mainbody.h @@ -0,0 +1,246 @@ +/* mainbody.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef MAINBODY_H +# define MAINBODY_H + +extern int luatex_version; +extern int luatex_revision; +extern const char *luatex_version_string; +extern const char *engine_name; + +/* +The following parameters can be changed at compile time to extend or +reduce \TeX's capacity. They may have different values in \.{INITEX} and +in production versions of \TeX. +@.INITEX@> +@^system dependencies@> +*/ + +# define ssup_max_strings 2097151 + +# define inf_max_strings 100000 +# define sup_max_strings ssup_max_strings +# define inf_strings_free 100 +# define sup_strings_free sup_max_strings + +# define inf_buf_size 500 +# define sup_buf_size 100000000 + +# define inf_nest_size 40 +# define sup_nest_size 4000 + +# define inf_max_in_open 6 +# define sup_max_in_open 600 /* was 127. now okay for some 512 nested lua calls */ + +# define inf_param_size 60 +# define sup_param_size 32767 + +# define inf_save_size 600 +# define sup_save_size 500000 + +# define inf_stack_size 200 +# define sup_stack_size 50000 + +# define inf_dvi_buf_size 800 +# define sup_dvi_buf_size 65536 + +# define sup_hash_extra sup_max_strings +# define inf_hash_extra 0 + +# define inf_expand_depth 100 +# define sup_expand_depth 10000000 + +# define inf_level_max 0 +# define sup_level_max 500 + +# define inf_level_chr 1 +# define sup_level_chr 126 + +# include <stdio.h> + +/* Types in the outer block */ +typedef int ASCII_code; /* characters */ +typedef unsigned char eight_bits; /* unsigned one-byte quantity */ +typedef FILE *alpha_file; /* files that contain textual data */ +typedef FILE *byte_file; /* files that contain binary data */ + +typedef int str_number; +typedef int pool_pointer; +typedef unsigned char packed_ASCII_code; + +typedef int scaled; /* this type is used for scaled integers */ +typedef char small_number; /* this type is self-explanatory */ + +typedef float glue_ratio; /* one-word representation of a glue expansion factor */ + +typedef unsigned short quarterword; +typedef int halfword; + +typedef unsigned char glue_ord; /* infinity to the 0, 1, 2, 3, or 4 power */ + +typedef unsigned short group_code; /* |save_level| for a level boundary */ + +typedef int font_index; /* index into |font_info| */ + +typedef int save_pointer; + +/* +Characters of text that have been converted to \TeX's internal form +are said to be of type |ASCII_code|, which is a subrange of the integers. + +We are assuming that our runtime system is able to read and write UTF-8. + +Some of the ASCII codes without visible characters have been given symbolic +names in this program because they are used with a special meaning. +*/ + + +# define null_code '\0' /* ASCII code that might disappear */ +# define carriage_return '\r' /* ASCII code used at end of line */ + +/* Global variables */ +extern boolean luainit; /* are we using lua for initializations */ +extern boolean tracefilenames; /* print file open-close info? */ + + +extern boolean ini_version; /* are we \.{INITEX}? */ +extern boolean dump_option; +extern boolean dump_line; +extern int bound_default; +extern char *bound_name; +extern int error_line; +extern int half_error_line; +extern int max_print_line; +extern int max_strings; +extern int level_max; +extern int level_chr; +extern int strings_free; +extern int font_k; +extern int buf_size; +extern int stack_size; +extern int max_in_open; +extern int param_size; +extern int nest_size; +extern int save_size; +extern int expand_depth; +extern int parsefirstlinep; +extern int filelineerrorstylep; +extern int haltonerrorp; +extern boolean quoted_filename; + +extern int total_pages; +extern int dead_cycles; + +/* +In order to make efficient use of storage space, \TeX\ bases its major data +structures on a |memory_word|, which contains either a (signed) integer, +possibly scaled, or a (signed) |glue_ratio|, or a small number of +fields that are one half or one quarter of the size used for storing +integers. + +If |x| is a variable of type |memory_word|, it contains up to four +fields that can be referred to as follows: +$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr +|x|&.|int|&(an |integer|)\cr +|x|&.|sc|\qquad&(a |scaled| integer)\cr +|x|&.|gr|&(a |glue_ratio|)\cr +|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr +|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword + field)\cr +|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt + &\qquad\qquad\qquad(four quarterword fields)\cr}}$$ +This is somewhat cumbersome to write, and not very readable either, but +macros will be used to make the notation shorter and more transparent. +The \PASCAL\ code below gives a formal definition of |memory_word| and +its subsidiary types, using packed variant records. \TeX\ makes no +assumptions about the relative positions of the fields within a word. + +We are assuming 32-bit integers, a halfword must contain at least +32 bits, and a quarterword must contain at least 16 bits. +@^system dependencies@> + +N.B.: Valuable memory space will be dreadfully wasted unless \TeX\ is compiled +by a \PASCAL\ that packs all of the |memory_word| variants into +the space of a single integer. This means, for example, that |glue_ratio| +words should be |short_real| instead of |real| on some computers. Some +\PASCAL\ compilers will pack an integer whose subrange is `|0..255|' into +an eight-bit field, but others insist on allocating space for an additional +sign bit; on such systems you can get 256 values into a quarterword only +if the subrange is `|-128..127|'. + +The present implementation tries to accommodate as many variations as possible, +so it makes few assumptions. If integers having the subrange +`|min_quarterword..max_quarterword|' can be packed into a quarterword, +and if integers having the subrange `|min_halfword..max_halfword|' +can be packed into a halfword, everything should work satisfactorily. + +It is usually most efficient to have |min_quarterword=min_halfword=0|, +so one should try to achieve this unless it causes a severe problem. +The values defined here are recommended for most 32-bit computers. + +We cannot use the full range of 32 bits in a halfword, because we have +to allow negative values for potential backend tricks like web2c's +dynamic allocation, and parshapes pointers have to be able to store at +least twice the value |max_halfword| (see below). Therefore, +|max_halfword| is $2^{30}-1$ +*/ + +# include "tex/memoryword.h" + +# define min_quarterword 0 /*smallest allowable value in a |quarterword| */ +# define max_quarterword 65535 /* largest allowable value in a |quarterword| */ +# define min_halfword -0x3FFFFFFF /* smallest allowable value in a |halfword| */ +# define max_halfword 0x3FFFFFFF + /* largest allowable value in a |halfword| */ + + +/* +The following procedure, which is called just before \TeX\ initializes its +input and output, establishes the initial values of the date and time. +It calls a macro-defined |dateandtime| routine. |dateandtime| in turn is +also a C macro, which calls |get_date_and_time|, passing it the addresses of +the day, month, etc., so they can be set by the routine. +|get_date_and_time| also sets up interrupt catching if that +is conditionally compiled in the C code. +@^system dependencies@> +*/ + +# define fix_date_and_time() dateandtime(time_par,day_par,month_par,year_par) + +extern int get_luatexversion(void); +extern str_number get_luatexrevision(void); + +extern int ready_already; + +extern void main_body(void); +extern void close_files_and_terminate(void); + +extern void final_cleanup(void); +extern void debug_help(void); /* routine to display various things */ + +extern int main_initialize(void); + +/* lazy me */ +# define get_cur_font() equiv(cur_font_loc) +# define zset_cur_font set_cur_font + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.c b/Build/source/texk/web2c/luatexdir/tex/maincontrol.c new file mode 100644 index 00000000000..22f601ba275 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.c @@ -0,0 +1,4245 @@ +/* + +maincontrol.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#include "lua/luatex-api.h" + +#define mode mode_par +#define tail tail_par +#define head head_par +#define dir_save dirs_par + +/*tex + +We come now to the |main_control| routine, which contains the master switch that +causes all the various pieces of \TeX\ to do their things, in the right order. + +In a sense, this is the grand climax of the program: It applies all the tools +that we have worked so hard to construct. In another sense, this is the messiest +part of the program: It necessarily refers to other pieces of code all over the +place, so that a person can't fully understand what is going on without paging +back and forth to be reminded of conventions that are defined elsewhere. We are +now at the hub of the web, the central nervous system that touches most of the +other parts and ties them together. @^brain@> + +The structure of |main_control| itself is quite simple. There's a label called +|big_switch|, at which point the next token of input is fetched using +|get_x_token|. Then the program branches at high speed into one of about 100 +possible directions, based on the value of the current mode and the newly fetched +command code; the sum |abs(mode)+cur_cmd| indicates what to do next. For example, +the case `|vmode+letter|' arises when a letter occurs in vertical mode (or +internal vertical mode); this case leads to instructions that initialize a new +paragraph and enter horizontal mode. + +The big |case| statement that contains this multiway switch has been labeled +|reswitch|, so that the program can |goto reswitch| when the next token has +already been fetched. Most of the cases are quite short; they call an ``action +procedure'' that does the work for that case, and then they either |goto +reswitch| or they ``fall through'' to the end of the |case| statement, which +returns control back to |big_switch|. Thus, |main_control| is not an extremely +large procedure, in spite of the multiplicity of things it must do; it is small +enough to be handled by PASCAL compilers that put severe restrictions on +procedure size. @!@^action procedure@> + +One case is singled out for special treatment, because it accounts for most of +\TeX's activities in typical applications. The process of reading simple text and +converting it into |char_node| records, while looking for ligatures and kerns, is +part of \TeX's ``inner loop''; the whole program runs efficiently when its inner +loop is fast, so this part has been written with particular care. + +We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we set +it equal to |sf_code(cur_chr)|, except that it should never change from a value +less than 1000 to a value exceeding 1000. The most common case is +|sf_code(cur_chr)=1000|, so we want that case to be fast. + +*/ + +void adjust_space_factor(void) +{ + halfword s = get_sf_code(cur_chr); + if (s == 1000) { + space_factor_par = 1000; + } else if (s < 1000) { + if (s > 0) + space_factor_par = s; + } else if (space_factor_par < 1000) { + space_factor_par = 1000; + } else { + space_factor_par = s; + } +} + +/*tex + +To handle the execution state of |main_control|'s eternal loop, an extra global +variable is used, along with a macro to define its values. + +*/ + +#define goto_next 0 +#define goto_skip_token 1 +#define goto_return 2 + +static int main_control_state; +static int local_level = 0; + +/*tex + +@* Main control helpers. + +Here are all the functions that are called from |main_control| that +are not already defined elsewhere. For the moment, this list simply +in the order that the appear in |init_main_control|, below. + +*/ + +static void run_char_num (void) { + scan_char_num(); + cur_chr = cur_val; + adjust_space_factor(); + tail_append(new_char(cur_font_par, cur_chr)); +} + +static void run_char (void) { + adjust_space_factor(); + tail_append(new_char(cur_font_par, cur_chr)); +} + +static void run_node (void) { + halfword n = cur_chr; + if (copy_lua_input_nodes_par) { + n = copy_node_list(n); + } + tail_append(n); + if (nodetype_has_attributes(type(n)) && node_attr(n) == null) { + build_attribute_list(n); + } + while (vlink(n) != null) { + n = vlink(n); + tail_append(n); + if (nodetype_has_attributes(type(n)) && node_attr(n) == null) { + build_attribute_list(n); + } + } +} + +static void run_lua_call(void) { + if (cur_chr <= 0) { + normal_error("luacall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luafunctioncall(cur_chr); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } +} + +/*tex + +The occurrence of blank spaces is almost part of \TeX's inner loop, since we +usually encounter about one space for every five non-blank characters. Therefore +|main_control| gives second-highest priority to ordinary spaces. + +When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will see to it +later that the corresponding glue specification is precisely |zero_glue|, not +merely a pointer to some specification that happens to be full of zeroes. +Therefore it is simple to test whether a glue parameter is zero or~not. + +*/ + +static void run_app_space (void) { + halfword p; /* was a global temp_ptr */ + int method = disable_space_par ; + if (method == 1) { + /*tex Don't inject anything, not even zero skip. */ + } else if (method == 2) { + p = new_glue(zero_glue); + couple_nodes(tail,p); + tail = p; + } else if ((abs(mode) + cur_cmd == hmode + spacer_cmd) && (!(space_factor_par == 1000))) { + app_space(); + } else { + /*tex Append a normal inter-word space to the current list. */ + if (glue_is_zero(space_skip_par)) { + /*tex Find the glue specification for text spaces in the current font. */ + p = new_glue(zero_glue); + width(p) = space(cur_font_par); + stretch(p) = space_stretch(cur_font_par); + shrink(p) = space_shrink(cur_font_par); + + } else { + p = new_param_glue(space_skip_code); + } + /*tex So from now we have a subtype with spaces: */ + subtype(p) = space_skip_code + 1 ; + couple_nodes(tail,p); + tail = p; + } +} + +/*tex + +Append a |boundary_node| + +*/ + +static void run_boundary (void) { + halfword n ; + n = new_node(boundary_node,cur_chr); + if ((cur_chr == 1) || (cur_chr == 2) ) { + /*tex We expect a user boundary or protrusion boundary. */ + scan_int(); + boundary_value(n) = cur_val; + } + couple_nodes(tail, n); + tail = n; +} + +static void run_char_ghost (void) { + int t; + t = cur_chr; + get_x_token(); + if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd) + || (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) { + halfword p = new_glyph(get_cur_font(), cur_chr); + if (t == 0) { + set_is_leftghost(p); + } else { + set_is_rightghost(p); + } + tail_append(p); + } +} + +static void run_relax (void) { + return; +} + +/*tex + +|ignore_spaces| is a special case: after it has acted, |get_x_token| has already +fetched the next token from the input, so that operation in |main_control| should +be skipped. + +*/ + +static void run_ignore_spaces (void) { + if (cur_chr == 0) { + /*tex Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + main_control_state = goto_skip_token; + } else { + int t = scanner_status; + scanner_status = normal; + get_next(); + scanner_status = t; + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = (cur_cmd * STRING_OFFSET) + cur_chr; + main_control_state = goto_skip_token; + } + } +} + +/*tex + +|stop| is the second special case. We want |main_control| to return to its caller +if there is nothing left to do. + +*/ + +static void run_stop (void) { + if (its_all_over()) { + /*tex this is the only way out */ + main_control_state= goto_return; + } +} + +static void run_non_math_math (void) { + back_input(); + new_graf(true); +} + +/*tex + + We build up an argument to |set_math_char|: + +*/ + +static void run_math_char_num (void) { + mathcodeval mval; + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(umath_mathcode); + else + mval = scan_mathchar(umathnum_mathcode); + math_char_in_text(mval); +} + +/*tex + + We build up an argument to |set_math_char|: +*/ + +static void run_math_given (void) { + mathcodeval mval; + mval = mathchar_from_integer(cur_chr, tex_mathcode); + math_char_in_text(mval); +} + +/*tex + + We build up an argument to |set_math_char| the \LUATEX\ way: +*/ + +static void run_xmath_given (void) { + mathcodeval mval; + mval = mathchar_from_integer(cur_chr, umath_mathcode); + math_char_in_text(mval); +} + +/*tex + +The most important parts of |main_control| are concerned with \TeX's chief +mission of box-making. We need to control the activities that put entries on +vlists and hlists, as well as the activities that convert those lists into boxes. +All of the necessary machinery has already been developed; it remains for us to +``push the buttons'' at the right times. + +As an introduction to these routines, let's consider one of the simplest cases: +What happens when `\.{\\hrule}' occurs in vertical mode, or `\.{\\vrule}' in +horizontal mode or math mode? The code in |main_control| is short, since the +|scan_rule_spec| routine already does most of what is required; thus, there is no +need for a special action procedure. + +Note that baselineskip calculations are disabled after a rule in vertical mode, +by setting |prev_depth:=ignore_depth|. + +*/ + +static void run_rule (void) { + tail_append(scan_rule_spec()); + if (abs(mode) == vmode) + prev_depth_par = ignore_depth; + else if (abs(mode) == hmode) + space_factor_par = 1000; +} + +/*tex + +Many of the actions related to box-making are triggered by the appearance of +braces in the input. For example, when the user says `\.{\\hbox} \.{to} +\.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode, the information +about the box size (100pt, |exactly|) is put onto |save_stack| with a level +boundary word just above it, and |cur_group:=adjusted_hbox_group|; \TeX\ enters +restricted horizontal mode to process the hlist. The right brace eventually +causes |save_stack| to be restored to its former state, at which time the +information about the box size (100pt, |exactly|) is available once again; a box +is packaged and we leave restricted horizontal mode, appending the new box to the +current list of the enclosing mode (in this case to the current list of vertical +mode), followed by any vertical adjustments that were removed from the box by +|hpack|. + +The next few sections of the program are therefore concerned with the treatment +of left and right curly braces. + +If a left brace occurs in the middle of a page or paragraph, it simply introduces +a new level of grouping, and the matching right brace will not have such a +drastic effect. Such grouping affects neither the mode nor the current list. + +*/ + +static void run_left_brace (void) { + new_save_level(simple_group); + eq_word_define(int_base + no_local_whatsits_code, 0); + eq_word_define(int_base + no_local_dirs_code, 0); +} + +static void run_begin_group (void) { + new_save_level(semi_simple_group); + eq_word_define(int_base + no_local_whatsits_code, 0); + eq_word_define(int_base + no_local_dirs_code, 0); +} + +static void run_end_group (void) { + if (cur_group == semi_simple_group) { + fixup_directions(); + } else { + off_save(); + } +} + +/*tex + +Constructions that require a box are started by calling |scan_box| with a +specified context code. The |scan_box| routine verifies that a |make_box| command +comes next and then it calls |begin_box|. + +*/ + +static void run_move (void) { + int t = cur_chr; + scan_normal_dimen(); + if (t == 0) + scan_box(cur_val); + else + scan_box(-cur_val); +} + +static void run_leader_ship (void) { + scan_box(leader_flag - a_leaders + cur_chr); +} + +static void run_make_box (void) { + begin_box(0); +} + +static void run_box_dir (void) { + scan_register_num(); + cur_box = box(cur_val); + scan_optional_equals(); + scan_direction(); + if (cur_box != null) + box_dir(cur_box) = cur_val; +} + +static void run_box_direction (void) { + scan_register_num(); + cur_box = box(cur_val); + scan_optional_equals(); + scan_int(); + check_dir_value(cur_val); + if (cur_box != null) + box_dir(cur_box) = cur_val; +} + +/*tex + +There is a really small patch to add a new primitive called \.{\\quitvmode}. In +vertical modes, it is identical to \.{\\indent}, but in horizontal and math modes +it is really a no-op (as opposed to \.{\\indent}, which executes the +|indent_in_hmode| procedure). + +A paragraph begins when horizontal-mode material occurs in vertical mode, or when +the paragraph is explicitly started by `\.{\\quitvmode}', `\.{\\indent}' or +`\.{\\noindent}'. + +*/ + +static void run_start_par_vmode (void) { + new_graf((cur_chr > 0)); +} + +static void run_start_par (void) { + if (cur_chr != 2) + indent_in_hmode(); +} + +static void run_new_graf (void) { + back_input(); + new_graf(true); +} + +/*tex + +A paragraph ends when a |par_end| command is sensed, or when we are in horizontal +mode when reaching the right brace of vertical-mode routines like \.{\\vbox}, +\.{\\insert}, or \.{\\output}. + +*/ + +static void run_par_end_vmode (void) { + normal_paragraph(); + if (mode > 0) { + checked_page_filter(vmode_par); + build_page(); + } +} + +static void run_par_end_hmode (void) { + if (align_state < 0) { + /*tex This tries to recover from an alignment that didn't end properly. */ + off_save(); + } + /* This takes us to the enclosing mode, if |mode>0|. */ + end_graf(bottom_level); + if (mode == vmode) { + checked_page_filter(hmode_par); + build_page(); + } +} + +static void append_italic_correction_mmode (void) { + tail_append(new_kern(0)); +} + +static void run_local_box (void) { + append_local_box(cur_chr); +} + +static void run_halign_mmode (void) { + if (privileged()) { + if (cur_group == math_shift_group) + init_align(); + else + off_save(); + } +} + +static void run_eq_no (void) { + if (privileged()) { + if (cur_group == math_shift_group) + start_eq_no(); + else + off_save(); + } +} + +static void run_letter_mmode (void) { + set_math_char(get_math_code(cur_chr)); +} + +static void run_char_num_mmode (void) { + scan_char_num(); + cur_chr = cur_val; + set_math_char(get_math_code(cur_chr)); +} + +static void run_math_char_num_mmode (void) { + mathcodeval mval; + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(umath_mathcode); + else + mval = scan_mathchar(umathnum_mathcode); + set_math_char(mval); +} + +static void run_math_given_mmode (void) { + mathcodeval mval; + mval = mathchar_from_integer(cur_chr, tex_mathcode); + set_math_char(mval); +} + +static void run_xmath_given_mmode (void) { + mathcodeval mval; + mval = mathchar_from_integer(cur_chr, umath_mathcode); + set_math_char(mval); +} + +static void run_delim_num (void) { + mathcodeval mval; + if (cur_chr == 0) + mval = scan_delimiter_as_mathchar(tex_mathcode); + else + mval = scan_delimiter_as_mathchar(umath_mathcode); + set_math_char(mval); +} + +static void run_vcenter (void) { + scan_spec(vcenter_group); + normal_paragraph(); + push_nest(); + mode = -vmode; + prev_depth_par = ignore_depth; + if (every_vbox_par != null) + begin_token_list(every_vbox_par, every_vbox_text); +} + +static void run_math_style (void) { + tail_append(new_style((small_number) cur_chr)); +} + +static void run_non_script (void) { + tail_append(new_glue(zero_glue)); + subtype(tail) = cond_math_glue; +} + +static void run_math_choice (void) { + if (cur_chr == 0) + append_choices(); + else + setup_math_style(); +} + +static void run_math_shift (void) { + if (cur_group == math_shift_group) + after_math(); + else + off_save(); +} + +static void run_after_assignment (void) { + get_token(); + after_token = cur_tok; +} + +static void run_after_group (void) { + get_token(); + save_for_after(cur_tok); +} + +static void run_extension (void) { + do_extension(0); +} + +static void run_normal (void) { +{ + switch (cur_chr) { + case save_pos_code: + new_whatsit(save_pos_node); + break; + case save_cat_code_table_code: + scan_int(); + if ((cur_val < 0) || (cur_val > 0x7FFF)) { + print_err("Invalid \\catcode table"); + help1( + "All \\catcode table ids must be between 0 and 0x7FFF" + ); + error(); + } else { + if (cur_val == cat_code_table_par) { + print_err("Invalid \\catcode table"); + help1( + "You cannot overwrite the current \\catcode table" + ); + error(); + } else { + copy_cat_codes(cat_code_table_par, cur_val); + } + } + break; + case init_cat_code_table_code: + scan_int(); + if ((cur_val < 0) || (cur_val > 0x7FFF)) { + print_err("Invalid \\catcode table"); + help1( + "All \\catcode table ids must be between 0 and 0x7FFF" + ); + error(); + } else { + if (cur_val == cat_code_table_par) { + print_err("Invalid \\catcode table"); + help1( + "You cannot overwrite the current \\catcode table" + ); + error(); + } else { + initex_cat_codes(cur_val); + } + } + break; + case set_random_seed_code: + /* Negative random seed values are silently converted to positive ones */ + scan_int(); + if (cur_val < 0) + negate(cur_val); + random_seed = cur_val; + init_randoms(random_seed); + break; + case late_lua_code: + new_whatsit(late_lua_node); /* type == normal */ + late_lua_name(tail) = scan_lua_state(); + (void) scan_toks(false, false); + late_lua_data(tail) = def_ref; + break; + case late_lua_call_code: + new_whatsit(late_lua_node); + late_lua_type(tail) = lua_refid_call; + scan_int(); + late_lua_data(tail) = cur_val; + break; + case expand_font_code: + read_expand_font(); + break; + default: + confusion("int1"); + break; + } + } +} + +/*tex + +This is experimental and not used for production, only for testing and writing +macros (some options stay). + +*/ + +#define mathoption_set_int(A) \ + scan_int(); \ + word_define(mathoption_int_base+A, cur_val); + +static void run_option(void) { + int a = 0 ; + switch (cur_chr) { + case math_option_code: + if (scan_keyword("old")) { + mathoption_set_int(c_mathoption_old_code); + /* + } else if (scan_keyword("umathcodemeaning")) { + mathoption_set_int(c_mathoption_umathcode_meaning_code); + */ + } else { + normal_warning("mathoption","unknown key"); + } + break; + default: + /* harmless */ + break; + } +} + +static void lua_function_call(void) { + scan_int(); + if (cur_val <= 0) { + normal_error("luafunctioncall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luafunctioncall(cur_val); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } +} + +static void lua_bytecode_call(void) { + scan_int(); + if (cur_val < 0 || cur_val > 65535) { + normal_error("luabytecodecall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luabytecodecall(cur_val); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } +} + +/*tex + +For mode-independent commands, the following macro is useful. + +Also, there is a list of cases where the user has probably gotten into or out of +math mode by mistake. \TeX\ will insert a dollar sign and rescan the current +token, and it makes sense ot have a macro for that as well. + +*/ + +#define any_mode(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; jump_table[mmode+(A)]=B +#define non_math(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; + +/*tex + +The |main_control| uses a jump table, and |init_main_control| sets that table up. + +*/ + +typedef void (*main_control_function) (void); + +main_control_function *jump_table; + +static void init_main_control (void) { + jump_table = xmalloc((mmode+max_command_cmd+1) * sizeof(main_control_function)) ; + + jump_table[hmode + char_num_cmd] = run_char_num; + jump_table[hmode + letter_cmd] = run_char; + jump_table[hmode + other_char_cmd] = run_char; + jump_table[hmode + char_given_cmd] = run_char; + jump_table[hmode + spacer_cmd] = run_app_space; + jump_table[hmode + ex_space_cmd] = run_app_space; + jump_table[mmode + ex_space_cmd] = run_app_space; + jump_table[hmode + boundary_cmd] = run_boundary; + jump_table[hmode + char_ghost_cmd] = run_char_ghost; + jump_table[mmode + char_ghost_cmd] = run_char_ghost; + any_mode(relax_cmd, run_relax); + jump_table[vmode + spacer_cmd] = run_relax; + jump_table[mmode + spacer_cmd] = run_relax; + jump_table[mmode + boundary_cmd] = run_relax; + any_mode(ignore_spaces_cmd,run_ignore_spaces); + jump_table[vmode + stop_cmd] = run_stop; + jump_table[vmode + math_char_num_cmd] = run_non_math_math; + jump_table[vmode + math_given_cmd] = run_non_math_math; + jump_table[vmode + xmath_given_cmd] = run_non_math_math; + jump_table[hmode + math_char_num_cmd] = run_math_char_num; + jump_table[hmode + math_given_cmd] = run_math_given; + jump_table[hmode + xmath_given_cmd] = run_xmath_given; + + jump_table[vmode + vmove_cmd] = report_illegal_case; + jump_table[hmode + hmove_cmd] = report_illegal_case; + jump_table[mmode + hmove_cmd] = report_illegal_case; + any_mode(last_item_cmd, report_illegal_case); + jump_table[vmode + vadjust_cmd] = report_illegal_case; + jump_table[vmode + ital_corr_cmd] = report_illegal_case; + non_math(eq_no_cmd,report_illegal_case); + any_mode(mac_param_cmd,report_illegal_case); + + non_math(sup_mark_cmd, insert_dollar_sign); + non_math(sub_mark_cmd, insert_dollar_sign); + non_math(super_sub_script_cmd, insert_dollar_sign); + non_math(no_super_sub_script_cmd, insert_dollar_sign); + non_math(math_comp_cmd, insert_dollar_sign); + non_math(delim_num_cmd, insert_dollar_sign); + non_math(left_right_cmd, insert_dollar_sign); + non_math(above_cmd, insert_dollar_sign); + non_math(radical_cmd, insert_dollar_sign); + non_math(math_style_cmd, insert_dollar_sign); + non_math(math_choice_cmd, insert_dollar_sign); + non_math(vcenter_cmd, insert_dollar_sign); + non_math(non_script_cmd, insert_dollar_sign); + non_math(mkern_cmd, insert_dollar_sign); + non_math(limit_switch_cmd, insert_dollar_sign); + non_math(mskip_cmd, insert_dollar_sign); + non_math(math_accent_cmd, insert_dollar_sign); + jump_table[mmode + endv_cmd] = insert_dollar_sign; + jump_table[mmode + par_end_cmd] = insert_dollar_sign_par_end; + jump_table[mmode + stop_cmd] = insert_dollar_sign; + jump_table[mmode + vskip_cmd] = insert_dollar_sign; + jump_table[mmode + un_vbox_cmd] = insert_dollar_sign; + jump_table[mmode + valign_cmd] = insert_dollar_sign; + jump_table[mmode + hrule_cmd] = insert_dollar_sign; + jump_table[mmode + no_hrule_cmd] = insert_dollar_sign; + jump_table[vmode + hrule_cmd] = run_rule; + jump_table[vmode + no_hrule_cmd] = run_rule; + jump_table[hmode + vrule_cmd] = run_rule; + jump_table[hmode + no_vrule_cmd] = run_rule; + jump_table[mmode + vrule_cmd] = run_rule; + jump_table[mmode + no_vrule_cmd] = run_rule; + jump_table[vmode + vskip_cmd] = append_glue; + jump_table[hmode + hskip_cmd] = append_glue; + jump_table[mmode + hskip_cmd] = append_glue; + jump_table[mmode + mskip_cmd] = append_glue; + any_mode(kern_cmd, append_kern); + jump_table[mmode + mkern_cmd] = append_kern; + non_math(left_brace_cmd, run_left_brace); + any_mode(begin_group_cmd,run_begin_group); + any_mode(end_group_cmd, run_end_group); + any_mode(right_brace_cmd, handle_right_brace); + jump_table[vmode + hmove_cmd] = run_move; + jump_table[hmode + vmove_cmd] = run_move; + jump_table[mmode + vmove_cmd] = run_move; + any_mode(leader_ship_cmd, run_leader_ship); + any_mode(make_box_cmd, run_make_box); + any_mode(assign_box_dir_cmd, run_box_dir); + any_mode(assign_box_direction_cmd, run_box_direction); + jump_table[vmode + start_par_cmd] = run_start_par_vmode; + jump_table[hmode + start_par_cmd] = run_start_par; + jump_table[mmode + start_par_cmd] = run_start_par; + jump_table[vmode + letter_cmd] = run_new_graf; + jump_table[vmode + other_char_cmd] = run_new_graf; + jump_table[vmode + char_num_cmd] = run_new_graf; + jump_table[vmode + char_given_cmd] = run_new_graf; + jump_table[vmode + char_ghost_cmd] = run_new_graf; + jump_table[vmode + math_shift_cmd] = run_new_graf; + jump_table[vmode + math_shift_cs_cmd] = run_new_graf; + jump_table[vmode + un_hbox_cmd] = run_new_graf; + jump_table[vmode + vrule_cmd] = run_new_graf; + jump_table[vmode + no_vrule_cmd] = run_new_graf; + jump_table[vmode + accent_cmd] = run_new_graf; + jump_table[vmode + discretionary_cmd] = run_new_graf; + jump_table[vmode + hskip_cmd] = run_new_graf; + jump_table[vmode + valign_cmd] = run_new_graf; + jump_table[vmode + ex_space_cmd] = run_new_graf; + jump_table[vmode + boundary_cmd] = run_new_graf; + jump_table[vmode + par_end_cmd] = run_par_end_vmode; + jump_table[hmode + par_end_cmd] = run_par_end_hmode; + jump_table[hmode + stop_cmd] = head_for_vmode; + jump_table[hmode + vskip_cmd] = head_for_vmode; + jump_table[hmode + hrule_cmd] = head_for_vmode; + jump_table[hmode + no_hrule_cmd] = head_for_vmode; + jump_table[hmode + un_vbox_cmd] = head_for_vmode; + jump_table[hmode + halign_cmd] = head_for_vmode; + any_mode(insert_cmd,begin_insert_or_adjust); + jump_table[hmode + vadjust_cmd] = begin_insert_or_adjust; + jump_table[mmode + vadjust_cmd] = begin_insert_or_adjust; + any_mode(mark_cmd, handle_mark); + any_mode(break_penalty_cmd, append_penalty); + any_mode(remove_item_cmd, delete_last); + jump_table[vmode + un_vbox_cmd] = unpackage; + jump_table[hmode + un_hbox_cmd] = unpackage; + jump_table[mmode + un_hbox_cmd] = unpackage; + jump_table[hmode + ital_corr_cmd] = append_italic_correction; + jump_table[mmode + ital_corr_cmd] = append_italic_correction_mmode; + jump_table[hmode + discretionary_cmd] = append_discretionary; + jump_table[mmode + discretionary_cmd] = append_discretionary; + any_mode(assign_local_box_cmd, run_local_box); + jump_table[hmode + accent_cmd] = make_accent; + any_mode(car_ret_cmd,align_error); + any_mode(tab_mark_cmd,align_error); + any_mode(no_align_cmd,no_align_error); + any_mode(omit_cmd, omit_error); + jump_table[vmode + halign_cmd] = init_align; + jump_table[hmode + valign_cmd] = init_align; + jump_table[mmode + halign_cmd] = run_halign_mmode; + jump_table[vmode + endv_cmd] = do_endv; + jump_table[hmode + endv_cmd] = do_endv; + any_mode(end_cs_name_cmd, cs_error); + jump_table[hmode + math_shift_cmd] = init_math; + jump_table[hmode + math_shift_cs_cmd] = init_math; + jump_table[mmode + eq_no_cmd] = run_eq_no; + jump_table[mmode + left_brace_cmd] = math_left_brace; + jump_table[mmode + letter_cmd] = run_letter_mmode; + jump_table[mmode + other_char_cmd] = run_letter_mmode; + jump_table[mmode + char_given_cmd] = run_letter_mmode; + jump_table[mmode + char_num_cmd] = run_char_num_mmode; + jump_table[mmode + math_char_num_cmd] = run_math_char_num_mmode; + jump_table[mmode + math_given_cmd] = run_math_given_mmode; + jump_table[mmode + xmath_given_cmd] = run_xmath_given_mmode; + jump_table[mmode + delim_num_cmd] = run_delim_num; + jump_table[mmode + math_comp_cmd] = math_math_comp; + jump_table[mmode + limit_switch_cmd] = math_limit_switch; + jump_table[mmode + radical_cmd] = math_radical; + jump_table[mmode + accent_cmd] = math_ac; + jump_table[mmode + math_accent_cmd] = math_ac; + jump_table[mmode + vcenter_cmd] = run_vcenter; + jump_table[mmode + math_style_cmd] = run_math_style; + jump_table[mmode + non_script_cmd] = run_non_script; + jump_table[mmode + math_choice_cmd] = run_math_choice; + jump_table[mmode + above_cmd] = math_fraction; + jump_table[mmode + sub_mark_cmd] = sub_sup; + jump_table[mmode + sup_mark_cmd] = sub_sup; + jump_table[mmode + super_sub_script_cmd] = sub_sup; + jump_table[mmode + no_super_sub_script_cmd] = no_sub_sup; + jump_table[mmode + left_right_cmd] = math_left_right; + jump_table[mmode + math_shift_cmd] = run_math_shift; + jump_table[mmode + math_shift_cs_cmd] = run_math_shift; + any_mode(toks_register_cmd, prefixed_command); + any_mode(assign_toks_cmd, prefixed_command); + any_mode(assign_int_cmd, prefixed_command); + any_mode(assign_attr_cmd, prefixed_command); + any_mode(assign_dir_cmd, prefixed_command); + any_mode(assign_direction_cmd, prefixed_command); + any_mode(assign_dimen_cmd, prefixed_command); + any_mode(assign_glue_cmd, prefixed_command); + any_mode(assign_mu_glue_cmd, prefixed_command); + any_mode(assign_font_dimen_cmd, prefixed_command); + any_mode(assign_font_int_cmd, prefixed_command); + any_mode(set_aux_cmd, prefixed_command); + any_mode(set_prev_graf_cmd, prefixed_command); + any_mode(set_page_dimen_cmd, prefixed_command); + any_mode(set_page_int_cmd, prefixed_command); + any_mode(set_box_dimen_cmd, prefixed_command); + any_mode(set_tex_shape_cmd, prefixed_command); + any_mode(set_etex_shape_cmd, prefixed_command); + any_mode(def_char_code_cmd, prefixed_command); + any_mode(def_del_code_cmd, prefixed_command); + any_mode(extdef_math_code_cmd, prefixed_command); + any_mode(extdef_del_code_cmd, prefixed_command); + any_mode(def_family_cmd, prefixed_command); + any_mode(set_math_param_cmd, prefixed_command); + any_mode(set_font_cmd, prefixed_command); + any_mode(def_font_cmd, prefixed_command); + any_mode(letterspace_font_cmd, prefixed_command); + any_mode(copy_font_cmd, prefixed_command); + any_mode(set_font_id_cmd, prefixed_command); + any_mode(register_cmd, prefixed_command); + any_mode(advance_cmd, prefixed_command); + any_mode(multiply_cmd, prefixed_command); + any_mode(divide_cmd, prefixed_command); + any_mode(prefix_cmd, prefixed_command); + any_mode(let_cmd, prefixed_command); + any_mode(shorthand_def_cmd, prefixed_command); + any_mode(read_to_cs_cmd, prefixed_command); + any_mode(def_cmd, prefixed_command); + any_mode(set_box_cmd, prefixed_command); + any_mode(hyph_data_cmd, prefixed_command); + any_mode(set_interaction_cmd, prefixed_command); + any_mode(after_assignment_cmd,run_after_assignment); + any_mode(after_group_cmd,run_after_group); + any_mode(in_stream_cmd,open_or_close_in); + any_mode(message_cmd,issue_message); + any_mode(case_shift_cmd, shift_case); + any_mode(xray_cmd, show_whatever); + any_mode(normal_cmd, run_normal); + any_mode(extension_cmd, run_extension); + any_mode(option_cmd, run_option); + + any_mode(lua_function_call_cmd, lua_function_call); + any_mode(lua_bytecode_call_cmd, lua_bytecode_call); + any_mode(def_lua_call_cmd, prefixed_command); + any_mode(lua_call_cmd, run_lua_call); + /* any_mode(lua_expandable_call_cmd, run_lua_call); */ /* no! outside jump table anyway, handled in expand() */ + any_mode(node_cmd, run_node); + + any_mode(combine_toks_cmd, combine_the_toks); +} + +/*tex + + And here is |main_control| itself. It is quite short nowadays. + +*/ + +void main_control(void) +{ + main_control_state = goto_next; + init_main_control() ; + if (equiv(every_job_loc) != null) { + begin_token_list(equiv(every_job_loc), every_job_text); + } + while (1) { + if (main_control_state == goto_skip_token) { + main_control_state = goto_next; + } else { + get_x_token(); + } + /*tex + Give diagnostic information, if requested When a new token has just + been fetched at |big_switch|, we have an ideal place to monitor + \TeX's activity. + */ + if (interrupt != 0 && OK_to_interrupt) { + back_input(); + check_interrupt(); + continue; + } + if (tracing_commands_par > 0) { + show_cur_cmd_chr(); + } + /*tex run the command */ + (jump_table[(abs(mode) + cur_cmd)])(); + if (main_control_state == goto_return) { + return; + } + } + /*tex not reached */ + return; +} + +/*tex + +We assume a trailing relax: |{...}\relax|, so we don't need a |back_input()| here. + +*/ + +extern void local_control_message(const char *s) +{ + tprint("local control level "); + print_int(local_level); + tprint(": "); + tprint(s); + tprint_nl(""); +} + +/*tex Note for me (HH): in luametatex I have the option to use the save stack which for + some experiments seems to work better. But I'll probably not go forward with + some intended extension anyway (too messy), so we're currently in sync. + Instead there are now two optional parameters in |runtoks|, plus a |quittoks| + companion. I also added some more tracing as well as a warning when we jump + out of control too often. +*/ + +void local_control(void) +{ + /*tex Wrr to saving the state local_control is like some of the conv_toks which + is not entirely correct but good enough for now. Future extensions might + demand another solution. + */ + int save_scanner_status = scanner_status; + halfword save_def_ref = def_ref; + halfword save_warning_index = warning_index; + int ll = local_level; + main_control_state = goto_next; + local_level += 1; + if (tracing_nesting_par > 2) { + local_control_message("entering local control"); + } + while (1) { + if (main_control_state == goto_skip_token) { + main_control_state = goto_next; + } else { + get_x_token(); + } + if (interrupt != 0 && OK_to_interrupt) { + back_input(); + check_interrupt(); + continue; + } + if (tracing_commands_par > 0) { + show_cur_cmd_chr(); + } + (jump_table[(abs(mode) + cur_cmd)])(); + if (local_level <= ll) { + main_control_state = goto_next; + if (tracing_nesting_par > 2) { + local_control_message("leaving local control due to level change"); + } + break; + } else if (main_control_state == goto_return) { + if (tracing_nesting_par > 2) { + local_control_message("leaving local control due to triggering"); + } + break; + } + } + /*tex From the perspective of ending and changing the level this is the + wrong spot, as it should be done in |end_local_control| in which case + we should use the save stack. Maybe some day. + */ + scanner_status = save_scanner_status; + def_ref = save_def_ref; + warning_index = save_warning_index; +} + +void end_local_control(void ) +{ + if (local_level > 0) { + local_level -= 1; + } else { + local_control_message("redundant end local control"); + } +} + +/*tex + We need to go back to the main loop. This is rather nasty and dirty + and counterintuive code and there might be a cleaner way. Basically + we trigger the main control state from here. + + \starttyping + 0 0 \directlua{token.scan_list()}\hbox{!} + -1 0 \setbox0\hbox{x}\directlua{token.scan_list()}\box0 + 1 1 \toks0={\directlua{token.scan_list()}\hbox{x}}\directlua{tex.runtoks(0)} + 0 0 1 1 \directlua{tex.box[0]=token.scan_list()}\hbox{x\directlua{node.write(token.scan_list())}\hbox{x}} + 0 0 0 1 \setbox0\hbox{x}\directlua{tex.box[0]=token.scan_list()}\hbox{x\directlua{node.write(token.scan_list())}\box0} + \stoptyping + + It's rather fragile code so we added some tracing options. + +*/ + +halfword local_scan_box(void) +{ + int old_mode = mode; + int ll = local_level; + mode = -hmode; + scan_box(lua_scan_flag); + if (local_level == ll) { + /*tex |\directlua{print(token.scan_list())}\hbox{!}| (n n) */ + if (tracing_nesting_par > 2) { + local_control_message("entering at end of box scanning"); + } + local_control(); + } else { + /*tex |\directlua{print(token.scan_list())}\box0| (n-1 n) */ + /* + if (tracing_nesting_par > 2) { + local_control_message("setting level after box scanning"); + } + */ + local_level = ll; + } + mode = old_mode; + return cur_box; +} + +/*tex + + We have an issue with modes when we quit here because we're coming + from and still staying at the \LUA\ end. So, unless we're already + nested, we trigger an end_local_level token (an extension code). + +*/ + +static void wrapup_local_scan_box(void) +{ + /* + if (tracing_nesting_par > 2) { + local_control_message("leaving box scanner"); + } + */ + local_level -= 1; +} + +int current_local_level(void) +{ + return local_level; +} + +void app_space(void) +{ /* handle spaces when |space_factor<>1000| */ + halfword q; /* glue node */ + if ((space_factor_par >= 2000) && (! glue_is_zero(xspace_skip_par))) { + q = new_param_glue(xspace_skip_code); + /* so from now we have a subtype with spaces: */ + subtype(q) = xspace_skip_code + 1; + } else { + if (!glue_is_zero(space_skip_par)) { + q = new_glue(space_skip_par); + } else { + q = new_glue(zero_glue); + width(q) = space(cur_font_par); + stretch(q) = space_stretch(cur_font_par); + shrink(q) = space_shrink(cur_font_par); + } + /* Modify the glue specification in |q| according to the space factor */ + if (space_factor_par >= 2000) + width(q) = width(q) + extra_space(cur_font_par); + stretch(q) = xn_over_d(stretch(q), space_factor_par, 1000); + shrink(q) = xn_over_d(shrink(q), 1000, space_factor_par); + + /* so from now we have a subtype with spaces: */ + subtype(q) = space_skip_code + 1; + } + couple_nodes(tail, q); + tail = q; +} + +void insert_dollar_sign(void) +{ + back_input(); + cur_tok = math_shift_token + '$'; + print_err("Missing $ inserted"); + help2( + "I've inserted a begin-math/end-math symbol since I think", + "you left one out. Proceed, with fingers crossed." + ); + ins_error(); +} + +/*tex + + We can silently ignore \.{\\par}s in a math formula. + +*/ + +void insert_dollar_sign_par_end(void) +{ + if (!suppress_mathpar_error_par) { + insert_dollar_sign() ; + } +} + +/*tex + +The `|you_cant|' procedure prints a line saying that the current command is +illegal in the current mode; it identifies these things symbolically. + +*/ + +void you_cant(void) +{ + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_in_mode(mode); +} + +/*tex + +When erroneous situations arise, \TeX\ usually issues an error message specific +to the particular error. For example, `\.{\\noalign}' should not appear in any +mode, since it is recognized by the |align_peek| routine in all of its legitimate +appearances; a special error message is given when `\.{\\noalign}' occurs +elsewhere. But sometimes the most appropriate error message is simply that the +user is not allowed to do what he or she has attempted. For example, +`\.{\\moveleft}' is allowed only in vertical mode, and `\.{\\lower}' only in +non-vertical modes. Such cases are enumerated here and in the other sections +referred to under `See also \dots.' + +*/ + +void report_illegal_case(void) +{ + you_cant(); + help4( + "Sorry, but I'm not programmed to handle this case;", + "I'll just pretend that you didn''t ask for it.", + "If you're in the wrong mode, you might be able to", + "return to the right one by typing `I}' or `I$' or `I\\par'." + ); + error(); +} + +/*tex + +Some operations are allowed only in privileged modes, i.e., in cases that +|mode>0|. The |privileged| function is used to detect violations of this rule; it +issues an error message and returns |false| if the current |mode| is negative. + +*/ + +boolean privileged(void) +{ + if (mode > 0) { + return true; + } else { + report_illegal_case(); + return false; + } +} + +/*tex + +We don't want to leave |main_control| immediately when a |stop| command is +sensed, because it may be necessary to invoke an \.{\\output} routine several +times before things really grind to a halt. (The output routine might even say +`\.{\\gdef\\end\{...\}}', to prolong the life of the job.) Therefore +|its_all_over| is |true| only when the current page and contribution list are +empty, and when the last output was not a ``dead cycle.'' + +*/ + + +boolean its_all_over(void) +{ /* do this when \.{\\end} or \.{\\dump} occurs */ + if (privileged()) { + if ((page_head == page_tail) && (head == tail) && (dead_cycles == 0)) { + return true; + } + back_input(); /* we will try to end again after ejecting residual material */ + tail_append(new_null_box()); + width(tail) = hsize_par; + tail_append(new_glue(fill_glue)); + tail_append(new_penalty(-010000000000,final_penalty)); + normal_page_filter(end); + build_page(); /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */ + } + return false; +} + +/*tex + +The |hskip| and |vskip| command codes are used for control sequences like +\.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}. The +difference is in the value of |cur_chr|. + +All the work relating to glue creation has been relegated to the following +subroutine. It does not call |build_page|, because it is used in at least one +place where that would be a mistake. + +*/ + +void append_glue(void) +{ + int s = cur_chr; + switch (s) { + case fil_code: + cur_val = new_glue(fil_glue); + break; + case fill_code: + cur_val = new_glue(fill_glue); + break; + case ss_code: + cur_val = new_glue(ss_glue); + break; + case fil_neg_code: + cur_val = new_glue(fil_neg_glue); + break; + case skip_code: + scan_glue(glue_val_level); + break; + case mskip_code: + scan_glue(mu_val_level); + break; + } + /* now |cur_val| points to the glue specification */ + tail_append(new_glue(cur_val)); + flush_node(cur_val); + if (s > skip_code) { + subtype(tail) = mu_glue; + } +} + +void append_kern(void) +{ + int s = cur_chr; /* |subtype| of the kern node */ + scan_dimen((s == mu_glue), false, false); + tail_append(new_kern(cur_val)); + subtype(tail) = (quarterword) s; +} + +/*tex + +We have to deal with errors in which braces and such things are not properly +nested. Sometimes the user makes an error of commission by inserting an extra +symbol, but sometimes the user makes an error of omission. \TeX\ can't always +tell one from the other, so it makes a guess and tries to avoid getting into a +loop. + +The |off_save| routine is called when the current group code is wrong. It tries +to insert something into the user's input that will help clean off the top level. + +*/ + +void off_save(void) +{ + halfword p, q; + if (cur_group == bottom_level) { + /*tex Drop current token and complain that it was unmatched */ + print_err("Extra "); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + help1( + "Things are pretty mixed up, but I think the worst is over." + ); + error(); + + } else { + back_input(); + p = get_avail(); + set_token_link(temp_token_head, p); + print_err("Missing "); + /*tex + Prepare to insert a token that matches |cur_group|, and print what it + is. At this point, |link(temp_token_head)=p|, a pointer to an empty + one-word node. + */ + switch (cur_group) { + case semi_simple_group: + set_token_info(p, cs_token_flag + frozen_end_group); + tprint_esc("endgroup"); + break; + case math_shift_group: + set_token_info(p, math_shift_token + '$'); + print_char('$'); + break; + case math_left_group: + set_token_info(p, cs_token_flag + frozen_right); + q = get_avail(); + set_token_link(p, q); + p = token_link(p); + set_token_info(p, other_token + '.'); + tprint_esc("right."); + break; + default: + set_token_info(p, right_brace_token + '}'); + print_char('}'); + break; + } + tprint(" inserted"); + ins_list(token_link(temp_token_head)); + help5( + "I've inserted something that you may have forgotten.", + "(See the <inserted text> above.)", + "With luck, this will get me unwedged. But if you", + "really didn't forget anything, try typing `2' now; then", + "my insertion and my current dilemma will both disappear." + ); + error(); + } +} +/*tex + +The routine for a |right_brace| character branches into many subcases, since a +variety of things may happen, depending on |cur_group|. Some types of groups are +not supposed to be ended by a right brace; error messages are given in hopes of +pinpointing the problem. Most branches of this routine will be filled in later, +when we are ready to understand them; meanwhile, we must prepare ourselves to +deal with such errors. + +*/ + +void handle_right_brace(void) +{ + halfword p, q; /* for short-term use */ + scaled d; /* holds |split_max_depth| in |insert_group| */ + int f; /* holds |floating_penalty| in |insert_group| */ + p = null; + switch (cur_group) { + case simple_group: + fixup_directions(); + break; + case bottom_level: + print_err("Too many }'s"); + help2( + "You've closed more groups than you opened.", + "Such booboos are generally harmless, so keep going." + ); + error(); + break; + case semi_simple_group: + case math_shift_group: + case math_left_group: + extra_right_brace(); + break; + /*tex + When the right brace occurs at the end of an \.{\\hbox} or + \.{\\vbox} or \.{\\vtop} construction, the |package| routine + comes into action. We might also have to finish a paragraph that + hasn't ended. + */ + case hbox_group: + if (fixup_boxes_par) { + /*tex + This is unofficial! Fixing up (also elsewhere) might become default + some day but for a while I will test this in ConTeXt. + */ + fixup_directions_only(); + } + package(0); + break; + case adjusted_hbox_group: + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + package(0); + break; + case vbox_group: + end_graf(vbox_group); + package(0); + break; + case vtop_group: + end_graf(vtop_group); + package(vtop_code); + break; + case insert_group: + end_graf(insert_group); + q = new_glue(split_top_skip_par); + d = split_max_depth_par; + f = floating_penalty_par; + unsave(); + save_ptr--; + /*tex + Now |saved_value(0)| is the insertion number, or the |vadjust| subtype. + */ + p = vpack(vlink(head), 0, additional, -1); + pop_nest(); + if (saved_type(0) == saved_insert) { + tail_append(new_node(ins_node, saved_value(0))); + height(tail) = height(p) + depth(p); + ins_ptr(tail) = list_ptr(p); + split_top_ptr(tail) = q; + depth(tail) = d; + float_cost(tail) = f; + } else if (saved_type(0) == saved_adjust) { + tail_append(new_node(adjust_node, saved_value(0))); + adjust_ptr(tail) = list_ptr(p); + flush_node(q); + } else { + confusion("insert_group"); + } + list_ptr(p) = null; + flush_node(p); + if (nest_ptr == 0) { + checked_page_filter(insert); + build_page(); + } + break; + case output_group: + /*tex + this is needed in case the \.{\\output} executes a \.{\\textdir} command. + */ + if (dir_level(text_dir_ptr) == cur_level) { + /*tex Remove from |text_dir_ptr| */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; + } + resume_after_output(); + break; + case disc_group: + build_discretionary(); + break; + case local_box_group: + build_local_box(); + break; + case align_group: + back_input(); + cur_tok = cs_token_flag + frozen_cr; + print_err("Missing \\cr inserted"); + help1( + "I'm guessing that you meant to end an alignment here." + ); + ins_error(); + break; + case no_align_group: + end_graf(no_align_group); + unsave(); + align_peek(); + break; + case vcenter_group: + end_graf(vcenter_group); + finish_vcenter(); + break; + case math_choice_group: + build_choices(); + break; + case math_group: + close_math_group(p); + break; + default: + confusion("rightbrace"); + break; + } +} + +void extra_right_brace(void) +{ + print_err("Extra }, or forgotten "); + switch (cur_group) { + case semi_simple_group: + tprint_esc("endgroup"); + break; + case math_shift_group: + print_char('$'); + break; + case math_left_group: + tprint_esc("right"); + break; + } + help5( + "I've deleted a group-closing symbol because it seems to be", + "spurious, as in `$x}$'. But perhaps the } is legitimate and", + "you forgot something else, as in `\\hbox{$x}'. In such cases", + "the way to recover is to insert both the forgotten and the", + "deleted material, e.g., by typing `I$}'." + ); + error(); + incr(align_state); +} + +/*tex + +Here is where we clear the parameters that are supposed to revert to their +default values after every paragraph and when internal vertical mode is entered. + +*/ + +void normal_paragraph(void) +{ + if (looseness_par != 0) + eq_word_define(int_base + looseness_code, 0); + if (hang_indent_par != 0) + eq_word_define(dimen_base + hang_indent_code, 0); + if (hang_after_par != 1) + eq_word_define(int_base + hang_after_code, 1); + if (par_shape_par_ptr != null) + eq_define(par_shape_loc, shape_ref_cmd, null); + if (inter_line_penalties_par_ptr != null) + eq_define(inter_line_penalties_loc, shape_ref_cmd, null); + if (shape_mode_par > 0) + eq_word_define(dimen_base + shape_mode_code, 0); +} + +/*tex + +The global variable |cur_box| will point to a newly-made box. If the box is void, +we will have |cur_box=null|. Otherwise we will have |type(cur_box)=hlist_node| or +|vlist_node| or |rule_node|; the |rule_node| case can occur only with leaders. + +*/ + +halfword cur_box; /* box to be placed into its context */ + +/*tex + +The |box_end| procedure does the right thing with |cur_box|, if |box_context| +represents the context as explained above. + +*/ + +void box_end(int box_context) +{ + if (box_context < box_flag) { + /*tex + + Append box |cur_box| to the current list, shifted by |box_context|. + The global variable |adjust_tail| will be non-null if and only if the + current box might include adjustments that should be appended to the + current vertical list. + + */ + + if (cur_box != null) { + shift_amount(cur_box) = box_context; + if (abs(mode) == vmode) { + if (pre_adjust_tail != null) { + if (pre_adjust_head != pre_adjust_tail) + append_list(pre_adjust_head, pre_adjust_tail); + pre_adjust_tail = null; + } + append_to_vlist(cur_box,lua_key_index(box)); + if (adjust_tail != null) { + if (adjust_head != adjust_tail) + append_list(adjust_head, adjust_tail); + adjust_tail = null; + } + if (mode > 0) { + checked_page_filter(box); + build_page(); + } + } else { + if (abs(mode) == hmode) + space_factor_par = 1000; + else + cur_box = new_sub_box(cur_box); + couple_nodes(tail, cur_box); + tail = cur_box; + } + } + } else if (box_context < ship_out_flag) { + /*tex + Store |cur_box| in a box register + */ + if (box_context < global_box_flag) + eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box); + else + geq_define(box_base + box_context - global_box_flag, box_ref_cmd, cur_box); + } else if (box_context == lua_scan_flag) { + /*tex + We are done with scanning so let's return to the caller. + */ + wrapup_local_scan_box(); + } else if (cur_box != null) { + /*tex + The leaders contexts come after shipout and luascan contexts. + */ + /* if (box_context > lua_scan_flag) { */ + if (box_context >= leader_flag) { + /*tex + Append a new leader node that uses |cur_box| and get the next + non-blank non-relax... + */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (((cur_cmd == hskip_cmd) && (abs(mode) != vmode)) || + ((cur_cmd == vskip_cmd) && (abs(mode) == vmode))) { + append_glue(); + subtype(tail) = (quarterword) (box_context - (leader_flag - a_leaders)); + leader_ptr(tail) = cur_box; + } else { + print_err("Leaders not followed by proper glue"); + help3( + "You should say `\\leaders <box or rule><hskip or vskip>'.", + "I found the <box or rule>, but there's no suitable", + "<hskip or vskip>, so I'm ignoring these leaders." + ); + back_error(); + flush_node_list(cur_box); + } + } else { + if (box_context != ship_out_flag) { + normal_error("scanner","shipout expected"); + } + ship_out(static_pdf, cur_box, SHIPPING_PAGE); + } + } +} + +/*tex + +the next input should specify a box or perhaps a rule + +*/ + +void scan_box(int box_context) +{ + /*tex Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd == make_box_cmd) { + begin_box(box_context); + } else if ((box_context >= leader_flag) && + ((cur_cmd == hrule_cmd) || (cur_cmd == vrule_cmd) || + (cur_cmd == no_hrule_cmd) || (cur_cmd == no_vrule_cmd))) { + cur_box = scan_rule_spec(); + box_end(box_context); + } else { + print_err("A <box> was supposed to be here"); + help3( + "I was expecting to see \\hbox or \\vbox or \\copy or \\box or", + "something like that. So you might find something missing in", + "your output. But keep trying; you can fix this later." + ); + back_error(); + if (box_context == lua_scan_flag) { + cur_box = null; + box_end(box_context); + } + } +} + +void new_graf(boolean indented) +{ + halfword p, q, dir_graf_tmp; + halfword dir_rover; + int callback_id; + if ((mode == vmode) || (head != tail)) { + tail_append(new_param_glue(par_skip_code)); + } + callback_id = callback_defined(new_graf_callback); + if (callback_id > 0) { + run_callback(callback_id, "db->b", cur_list.mode_field,indented,&indented); + } + prev_graf_par = 0; + push_nest(); + mode = hmode; + space_factor_par = 1000; + /*tex Add local paragraph node */ + tail_append(make_local_par_node(new_graf_par_code)); + if (indented) { + p = new_null_box(); + box_dir(p) = par_direction_par; + width(p) = par_indent_par; + subtype(p) = indent_list; + q = tail; + tail_append(p); + } else { + q = tail; + } + dir_rover = text_dir_ptr; + while (dir_rover != null) { + if ((vlink(dir_rover) != null) || (dir_dir(dir_rover) != par_direction_par)) { + dir_graf_tmp = new_dir(dir_dir(dir_rover)); + try_couple_nodes(dir_graf_tmp,vlink(q)); + couple_nodes(q,dir_graf_tmp); + } + dir_rover = vlink(dir_rover); + } + q = head; + while (vlink(q) != null) + q = vlink(q); + tail = q; + if (every_par_par != null) + begin_token_list(every_par_par, every_par_text); + if (nest_ptr == 1) { + checked_page_filter(new_graf); + /*tex put |par_skip| glue on current page */ + build_page(); + } +} + +void indent_in_hmode(void) +{ + halfword p; + if (cur_chr > 0) { + /*tex \.{\\indent} */ + p = new_null_box(); + width(p) = par_indent_par; + if (abs(mode) == hmode) + space_factor_par = 1000; + else + p = new_sub_box(p); + tail_append(p); + } +} + +void head_for_vmode(void) +{ + if (mode < 0) { + if ((cur_cmd != hrule_cmd) && (cur_cmd != no_hrule_cmd)) { + off_save(); + } else { + print_err("You can't use `\\hrule' here except with leaders"); + help2( + "To put a horizontal rule in an hbox or an alignment,", + "you should use \\leaders or \\hrulefill (see The TeXbook)." + ); + error(); + } + } else { + back_input(); + cur_tok = par_token; + back_input(); + token_type = inserted; + } +} + +/*tex + +|dir_save| would have been set by |line_break| by means of |post_line_break|, but +this is not done right now, as it introduces pretty heavy memory leaks. This +means the current code might be wrong in some way that relates to in-paragraph +displays. + +*/ + +void end_graf(int line_break_context) +{ + if (mode == hmode) { + if ((head == tail) || (vlink(head) == tail)) { + if (vlink(head) == tail) + flush_node(vlink(head)); + /*tex |null| paragraphs are ignored, all contain a |local_paragraph| node */ + pop_nest(); + } else { + line_break(false, line_break_context); + } + if (dir_save != null) { + flush_node_list(dir_save); + dir_save = null; + } + normal_paragraph(); + error_count = 0; + } +} + +void begin_insert_or_adjust(void) +{ + if (cur_cmd != vadjust_cmd) { + scan_register_num(); + if (cur_val == output_box_par) { + print_err("You can't \\insert"); + print_int(output_box_par); + help1( + "I'm changing to \\insert0; box \\outputbox is special." + ); + error(); + cur_val = 0; + } + set_saved_record(0, saved_insert, 0, cur_val); + } else if (scan_keyword("pre")) { + set_saved_record(0, saved_adjust, 0, 1); + } else { + set_saved_record(0, saved_adjust, 0, 0); + } + save_ptr++; + new_save_level(insert_group); + scan_left_brace(); + normal_paragraph(); + push_nest(); + mode = -vmode; + prev_depth_par = ignore_depth; +} + +/*tex + +I (TH)'ve renamed the |make_mark| procedure to this, because if the current chr +code is 1, then the actual command was \.{\\clearmarks}, which does not generate +a mark node but instead destroys the current mark tokenlists. + +*/ + +void handle_mark(void) +{ + halfword p; /* new node */ + halfword c; /* the mark class */ + if (cur_chr == clear_marks_code) { + scan_mark_num(); + c = cur_val; + delete_top_mark(c); + delete_bot_mark(c); + delete_first_mark(c); + delete_split_first_mark(c); + delete_split_bot_mark(c); + } else { + if (cur_chr == 0) { + c = 0; + } else { + scan_mark_num(); + c = cur_val; + if (c > biggest_used_mark) + biggest_used_mark = c; + } + p = scan_toks(false, true); + p = new_node(mark_node, 0); /* the |subtype| is not used */ + mark_class(p) = c; + mark_ptr(p) = def_ref; + couple_nodes(tail, p); + tail = p; + } +} + +void append_penalty(void) +{ + scan_int(); + tail_append(new_penalty(cur_val,user_penalty)); + if (mode == vmode) { + checked_page_filter(penalty); + build_page(); + } +} + +/*tex + +When |delete_last| is called, |cur_chr| is the |type| of node that will be +deleted, if present. + +The |remove_item| command removes a penalty, kern, or glue node if it appears at +the tail of the current list, using a brute-force linear scan. Like +\.{\\lastbox}, this command is not allowed in vertical mode (except internal +vertical mode), since the current list in vertical mode is sent to the page +builder. But if we happen to be able to implement it in vertical mode, we do. + +*/ + +void delete_last(void) +{ + halfword p, q; /* run through the current list */ + if ((mode == vmode) && (tail == head)) { + /*tex + Apologize for inability to do the operation now, unless \.{\\unskip} + follows non-glue + */ + if ((cur_chr != glue_node) || (last_glue != max_halfword)) { + you_cant(); + if (cur_chr == kern_node) { + help2( + "Sorry...I usually can't take things from the current page.", + "Try `I\\kern-\\lastkern' instead." + ); + } else if (cur_chr != glue_node) { + help2 + ("Sorry...I usually can't take things from the current page.", + "Perhaps you can make the output routine do it."); + } else { + help2 + ("Sorry...I usually can't take things from the current page.", + "Try `I\\vskip-\\lastskip' instead."); + } + error(); + } + } else { + /*tex Todo: clean this up! */ + if (!is_char_node(tail)) { + if (type(tail) == cur_chr) { + q = head; + do { + p = q; + if (!is_char_node(q)) { + if (type(q) == disc_node) { + if (p == tail) + return; + } + } + q = vlink(p); + } while (q != tail); + vlink(p) = null; + flush_node_list(tail); + tail = p; + } + } + } +} + +void unpackage(void) +{ + halfword p; /* the box */ + halfword r; /* to remove marginal kern nodes */ + int c; /* should we copy? */ + halfword s; /* for varmem assignment */ + if (cur_chr > copy_code) { + /*tex Handle saved items and |goto done| */ + try_couple_nodes(tail, disc_ptr[cur_chr]); + disc_ptr[cur_chr] = null; + goto DONE; + } + c = cur_chr; + scan_register_num(); + p = box(cur_val); + if (p == null) + return; + if ((abs(mode) == mmode) + || ((abs(mode) == vmode) && (type(p) != vlist_node)) + || ((abs(mode) == hmode) && (type(p) != hlist_node))) { + print_err("Incompatible list can't be unboxed"); + help3( + "Sorry, Pandora. (You sneaky devil.)", + "I refuse to unbox an \\hbox in vertical mode or vice versa.", + "And I can't open any boxes in math mode." + ); + error(); + return; + } + if (c == copy_code) { + s = copy_node_list(list_ptr(p)); + try_couple_nodes(tail,s); + } else { + try_couple_nodes(tail,list_ptr(p)); + box(cur_val) = null; + list_ptr(p) = null; + flush_node(p); + } + DONE: + while (vlink(tail) != null) { + r = vlink(tail); + if (!is_char_node(r) && (type(r) == margin_kern_node)) { + try_couple_nodes(tail,vlink(r)); + flush_node(r); + } + tail = vlink(tail); + } +} + +/*tex + +Italic corrections are converted to kern nodes when the |ital_corr| command +follows a character. In math mode the same effect is achieved by appending a kern +of zero here, since italic corrections are supplied later. + +*/ + +void append_italic_correction(void) +{ + halfword p; /* |char_node| at the tail of the current list */ + internal_font_number f; /* the font in the |char_node| */ + if (tail != head) { + if (is_char_node(tail)) + p = tail; + else + return; + f = font(p); + tail_append(new_kern(char_italic(f, character(p)))); + subtype(tail) = italic_kern; + } +} + +void append_local_box(int kind) +{ + incr(save_ptr); + set_saved_record(-1, saved_boxtype, 0, kind); + new_save_level(local_box_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor_par = 1000; +} + +/*tex + +Discretionary nodes are easy in the common case `\.{\\-}', but in the general +case we must process three braces full of items. + +The space factor does not change when we append a discretionary node, but it +starts out as 1000 in the subsidiary lists. + +*/ + +void append_discretionary(void) +{ + int c; + tail_append(new_disc()); + subtype(tail) = (quarterword) cur_chr; + if (cur_chr == explicit_disc) { + /* |\-| */ + c = get_pre_hyphen_char(cur_lang_par); + if (c > 0) { + vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(pre_break(tail))) = pre_break(tail); + tlink(pre_break(tail)) = vlink(pre_break(tail)); + } + c = get_post_hyphen_char(cur_lang_par); + if (c > 0) { + vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(post_break(tail))) = post_break(tail); + tlink(post_break(tail)) = vlink(post_break(tail)); + } + set_explicit_disc_penalty(tail); + } else if (cur_chr == automatic_disc) { + /*tex As done in hyphenator: */ + c = get_pre_exhyphen_char(cur_lang_par); + if (c <= 0) { + c = ex_hyphen_char_par; + } + if (c > 0) { + vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(pre_break(tail))) = pre_break(tail); + tlink(pre_break(tail)) = vlink(pre_break(tail)); + } + c = get_post_exhyphen_char(cur_lang_par); + if (c > 0) { + vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(post_break(tail))) = post_break(tail); + tlink(post_break(tail)) = vlink(post_break(tail)); + } + c = ex_hyphen_char_par; + if (c > 0) { + vlink(no_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(no_break(tail))) = no_break(tail); + tlink(no_break(tail)) = vlink(no_break(tail)); + } + set_automatic_disc_penalty(tail); + } else { + /*tex |\discretionary| */ + if (scan_keyword("penalty")) { + scan_int(); + disc_penalty(tail) = cur_val; + } + incr(save_ptr); + set_saved_record(-1, saved_disc, 0, 0); + new_save_level(disc_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor_par = 1000; + /*tex Already preset: |disc_penalty(tail) = hyphen_penalty_par;| */ + } +} + +/*tex + +The test for |p != null| ensures that empty \.{\\localleftbox} and +\.{\\localrightbox} commands are not applied. + +*/ + +void build_local_box(void) +{ + halfword p; + int kind; + unsave(); + assert(saved_type(-1) == saved_boxtype); + kind = saved_value(-1); + decr(save_ptr); + p = vlink(head); + pop_nest(); + if (p != null) { + /*tex + Somehow |filtered_hpack| goes beyond the first node so we loose it. + */ + /*tex + There is no need for |new_hyphenation(p, null);| here as we're in + an |\hbox|. + */ + (void) new_ligkern(p, null); + p = lua_hpack_filter(p, 0, additional, local_box_group, -1, null); + /*tex + We really need something packed so we play safe! This feature is inherited + but could have been delegated to a callback anyway. + */ + p = hpack(p, 0, additional, -1); + } + if (kind == 0) + eq_define(local_left_box_base, box_ref_cmd, p); + else + eq_define(local_right_box_base, box_ref_cmd, p); + if (abs(mode) == hmode) { + /*tex Add local paragraph node */ + tail_append(make_local_par_node(local_box_par_code)); + } + eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); +} + +/*tex + +The three discretionary lists are constructed somewhat as if they were hboxes. +A~subroutine called |build_discretionary| handles the transitions. (This is sort +of fun.) + +*/ + +void build_discretionary(void) +{ + halfword p, q; /* for link manipulation */ + int n; /* length of discretionary list */ + unsave(); + /*tex + Prune the current list, if necessary, until it contains only |char_node|, + |kern_node|, |hlist_node|, |vlist_node| and |rule_node| items; set |n| to + the length of the list, and set |q| to the lists tail. During this loop, + |p=vlink(q)| and there are |n| items preceding |p|. + */ + q = head; + p = vlink(q); + n = 0; + while (p != null) { + if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) { + print_err("Improper discretionary list"); + help1( + "Discretionary lists must contain only boxes and kerns." + ); + error(); + begin_diagnostic(); + tprint_nl("The following discretionary sublist has been deleted:"); + show_box(p); + end_diagnostic(true); + flush_node_list(p); + vlink(q) = null; + break; + } + alink(p) = q; + q = p; + p = vlink(q); + incr(n); + } + + p = vlink(head); + pop_nest(); + assert(saved_type(-1) == saved_disc); + switch (saved_value(-1)) { + case 0: + if (n > 0) { + vlink(pre_break(tail)) = p; + alink(p) = pre_break(tail); + tlink(pre_break(tail)) = q; + } + break; + case 1: + if (n > 0) { + vlink(post_break(tail)) = p; + alink(p) = post_break(tail); + tlink(post_break(tail)) = q; + } + break; + case 2: + /*tex + Attach list |p| to the current list, and record its length; then + finish up and |return| + */ + if ((n > 0) && (abs(mode) == mmode)) { + print_err("Illegal math \\discretionary"); + help2( + "Sorry: The third part of a discretionary break must be", + "empty, in math formulas. I had to delete your third part." + ); + flush_node_list(p); + error(); + } else { + if (n > 0) { + vlink(no_break(tail)) = p; + alink(p) = no_break(tail); + tlink(no_break(tail)) = q; + } + } + decr(save_ptr); + /*tex There are no other cases. */ + return; + break; + } + set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1)); + new_save_level(disc_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor_par = 1000; +} + +/*tex + +The positioning of accents is straightforward but tedious. Given an accent of +width |a|, designed for characters of height |x| and slant |s|; and given a +character of width |w|, height |h|, and slant |t|: We will shift the accent down +by |x-h|, and we will insert kern nodes that have the effect of centering the +accent over the character and shifting the accent to the right by +$\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is absent from the +font, we will simply use the other, without shifting. + +*/ + +void make_accent(void) +{ + double s, t; /* amount of slant */ + halfword p, q, r; /* character, box, and kern nodes */ + internal_font_number f; /* relevant font */ + scaled a, h, x, w, delta; /* heights and widths, as explained above */ + scan_char_num(); + f = equiv(cur_font_loc); + p = new_glyph(f, cur_val); + if (p != null) { + x = x_height(f); + /*tex real division */ + s = float_cast(slant(f)) / float_constant(65536); + a = glyph_width(p); + do_assignments(); + /*tex + Create a character node |q| for the next character, but set |q:=null| + if problems arise + */ + q = null; + f = equiv(cur_font_loc); + if ((cur_cmd == letter_cmd) || + (cur_cmd == other_char_cmd) || (cur_cmd == char_given_cmd)) { + q = new_glyph(f, cur_chr); + } else if (cur_cmd == char_num_cmd) { + scan_char_num(); + q = new_glyph(f, cur_val); + } else { + back_input(); + } + + if (q != null) { + /*tex + Append the accent with appropriate kerns, then set |p:=q|. The + kern nodes appended here must be distinguished from other kerns, + lest they be wiped away by the hyphenation algorithm or by a + previous line break. The two kerns are computed with + (machine-dependent) |real| arithmetic, but their sum is + machine-independent; the net effect is machine-independent, + because the user cannot remove these nodes nor access them via + \.{\\lastkern}. + */ + t = float_cast(slant(f)) / float_constant(65536); /* real division */ + w = glyph_width(q); + h = glyph_height(q); + if (h != x) { + /*tex the accent must be shifted up or down */ + p = hpack(p, 0, additional, -1); + shift_amount(p) = x - h; + } + /*tex real multiplication */ + delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); + r = new_kern(delta); + subtype(r) = accent_kern; + couple_nodes(tail, r); + couple_nodes(r, p); + tail = new_kern(-a - delta); + subtype(tail) = accent_kern; + couple_nodes(p, tail); + p = q; + + } + couple_nodes(tail, p); + tail = p; + space_factor_par = 1000; + } +} + +/*tex + +When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner into +|main_control|, it might be that the user has foolishly inserted one of them into +something that has nothing to do with alignment. But it is far more likely that a +left brace or right brace has been omitted, since |get_next| takes actions +appropriate to alignment only when `\.{\\cr}' or `\.{\\span}' or tab marks occur +with |align_state=0|. The following program attempts to make an appropriate +recovery. + +*/ + +void align_error(void) +{ + if (abs(align_state) > 2) { + /*tex + Express consternation over the fact that no alignment is in progress. + */ + print_err("Misplaced "); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + if (cur_tok == tab_token + '&') { + help6( + "I can't figure out why you would want to use a tab mark", + "here. If you just want an ampersand, the remedy is", + "simple: Just type `I\\&' now. But if some right brace", + "up above has ended a previous alignment prematurely,", + "you're probably due for more error messages, and you", + "might try typing `S' now just to see what is salvageable." + ); + } else { + help5( + "I can't figure out why you would want to use a tab mark", + "or \\cr or \\span just now. If something like a right brace", + "up above has ended a previous alignment prematurely,", + "you're probably due for more error messages, and you", + "might try typing `S' now just to see what is salvageable." + ); + } + error(); + + } else { + back_input(); + if (align_state < 0) { + print_err("Missing { inserted"); + incr(align_state); + cur_tok = left_brace_token + '{'; + } else { + print_err("Missing } inserted"); + decr(align_state); + cur_tok = right_brace_token + '}'; + } + help3( + "I've put in what seems to be necessary to fix", + "the current column of the current alignment.", + "Try to go on, since this might almost work." + ); + ins_error(); + } +} + +/*tex + +The help messages here contain a little white lie, since \.{\\noalign} and +\.{\\omit} are allowed also after `\.{\\noalign\{...\}}'. + +*/ + +void no_align_error(void) +{ + print_err("Misplaced \\noalign"); + help2( + "I expect to see \\noalign only after the \\cr of", + "an alignment. Proceed, and I'll ignore this case." + ); + error(); +} + +void omit_error(void) +{ + print_err("Misplaced \\omit"); + help2( + "I expect to see \\omit only after tab marks or the \\cr of", + "an alignment. Proceed, and I'll ignore this case." + ); + error(); +} + +/*tex + +We've now covered most of the abuses of \.{\\halign} and \.{\\valign}. Let's take +a look at what happens when they are used correctly. + +An |align_group| code is supposed to remain on the |save_stack| during an entire +alignment, until |fin_align| removes it. + +A devious user might force an |endv| command to occur just about anywhere; we +must defeat such hacks. + +*/ + +void do_endv(void) +{ + base_ptr = input_ptr; + input_stack[base_ptr] = cur_input; + while ((input_stack[base_ptr].index_field != v_template) && + (input_stack[base_ptr].loc_field == null) && + (input_stack[base_ptr].state_field == token_list)) + decr(base_ptr); + if ((input_stack[base_ptr].index_field != v_template) || + (input_stack[base_ptr].loc_field != null) || + (input_stack[base_ptr].state_field != token_list)) + fatal_error("(interwoven alignment preambles are not allowed)"); + /*tex interwoven alignment preambles... */ + if (cur_group == align_group) { + end_graf(align_group); + if (fin_col()) + fin_row(); + } else { + off_save(); + } +} + +/*tex + +Finally, \.{\\endcsname} is not supposed to get through to |main_control|. + +*/ + +void cs_error(void) +{ + print_err("Extra \\endcsname"); + help1( + "I'm ignoring this, since I wasn't doing a \\csname." + ); + error(); +} + +/*tex + +Assignments to values in |eqtb| can be global or local. Furthermore, a control +sequence can be defined to be `\.{\\long}', `\.{\\protected}', or `\.{\\outer}', +and it might or might not be expanded. The prefixes `\.{\\global}', `\.{\\long}', +`\.{\\protected}', and `\.{\\outer}' can occur in any order. Therefore we assign +binary numeric codes, making it possible to accumulate the union of all specified +prefixes by adding the corresponding codes. (PASCAL's |set| operations could also +have been used.) + +Every prefix, and every command code that might or might not be prefixed, calls +the action procedure |prefixed_command|. This routine accumulates a sequence of +prefixes until coming to a non-prefix, then it carries out the command. + +*/ + +/*tex + +If the user says, e.g., `\.{\\global\\global}', the redundancy is silently +accepted. The different types of code values have different legal ranges; the +following program is careful to check each case properly. + +*/ + +#define check_def_code(A) do { \ + if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \ + print_err("Invalid code ("); \ + print_int(cur_val); \ + if (p<(A)) \ + tprint("), should be in the range 0.."); \ + else \ + tprint("), should be at most "); \ + print_int(n); \ + help1( \ + "I'm going to use 0 instead of that illegal code value." \ + ); \ + error(); \ + cur_val=0; \ + } \ +} while (0) + +/* +halfword swap_hang_indent(halfword indentation, halfword shape_mode) { + if (shape_mode == 1 || shape_mode == 3 || shape_mode == -1 || shape_mode == -3) { + return negate(indentation); + } else { + return indentation; + } +} + +halfword swap_parshape_indent(halfword indentation, halfword width, halfword shape_mode) { + if (shape_mode == 2 || shape_mode == 3 || shape_mode == -2 || shape_mode == -3) { + return hsize_par - width - indentation; + } else { + return indentation; + } +} + +*/ + +void prefixed_command(void) +{ + int a; /* accumulated prefix codes so far */ + internal_font_number f; /* identifies a font */ + halfword j; /* index into a \.{\\parshape} specification */ + halfword p, q; /* for temporary short-term use */ + int n; /* ditto */ + boolean e, check_glue; /* should a definition be expanded? or was \.{\\let} not done? */ + mathcodeval mval; /* for handling of \.{\\mathchardef}s */ + a = 0; + while (cur_cmd == prefix_cmd) { + if (!odd(a / cur_chr)) + a = a + cur_chr; + /*tex + Get the next non-blank non-relax... + */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd <= max_non_prefixed_command) { + /*tex + Discard erroneous prefixes and |return| + */ + print_err("You can't use a prefix with `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help2( + "I'll pretend you didn't say \\long or \\outer or \\global or", + "\\protected." + ); + back_error(); + return; + } + if (tracing_commands_par > 2) + show_cur_cmd_chr(); + } + /*tex + Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant + */ + if (a >= 8) { + j = protected_token; + a = a - 8; + } else { + j = 0; + } + if ((cur_cmd != def_cmd) && (cur_cmd != def_lua_call_cmd) && ((a % 4 != 0) || (j != 0))) { + print_err("You can't use `\\long' or `\\outer' or `\\protected' with `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help1( + "I'll pretend you didn't say \\long or \\outer or \\protected here." + ); + error(); + } + /*tex + Adjust for the setting of \.{\\globaldefs} + */ + if (global_defs_par != 0) { + if (global_defs_par < 0) { + if (is_global(a)) + a = a - 4; + } else { + if (!is_global(a)) + a = a + 4; + } + } + switch (cur_cmd) { + case set_font_cmd: + /*tex + Here's an example of the way many of the following routines operate. + (Unfortunately, they aren't all as simple as this.) + */ + define(cur_font_loc, data_cmd, cur_chr); + break; + case def_cmd: + /*tex + When a |def| command has been scanned, |cur_chr| is odd if the + definition is supposed to be global, and |cur_chr>=2| if the + definition is supposed to be expanded. + */ + if (odd(cur_chr) && !is_global(a) && (global_defs_par >= 0)) + a = a + 4; + e = (cur_chr >= 2); + get_r_token(); + p = cur_cs; + q = scan_toks(true, e); + if (j != 0) { + q = get_avail(); + set_token_info(q, j); + set_token_link(q, token_link(def_ref)); + set_token_link(def_ref, q); + } + define(p, call_cmd + (a % 4), def_ref); + break; + case let_cmd: + n = cur_chr; + switch (n) { + case 0: + /*tex |glet| */ + if (!is_global(a) && (global_defs_par >= 0)) { + a = a + 4; + } + case 1: + /*tex |let| */ + get_r_token(); + p = cur_cs; + do { + get_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '=') { + get_token(); + if (cur_cmd == spacer_cmd) + get_token(); + } + break; + case 2: + /*tex |futurelet| */ + get_r_token(); + p = cur_cs; + get_token(); + q = cur_tok; + get_token(); + back_input(); + cur_tok = q; + /*tex + We look ahead and then back up. Note that |back_input| doesn't + affect |cur_cmd|, |cur_chr| + */ + back_input(); + break; + case 3: + /*tex |letcharcode| */ + scan_int(); + if (cur_val > 0) { + /*tex HH: I need to do a more extensive test later. */ + p = active_to_cs(cur_val, true); + do { + get_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '=') { + get_token(); + if (cur_cmd == spacer_cmd) + get_token(); + } + } else { + p = null; + tex_error("invalid number for \\letcharcode",NULL); + } + break; + default: + /*tex We please the compiler. */ + p = null; + confusion("let"); + break; + } + if (cur_cmd >= call_cmd) + add_token_ref(cur_chr); + define(p, cur_cmd, cur_chr); + break; + case shorthand_def_cmd: + /*tex + We temporarily define |p| to be |relax|, so that an occurrence of + |p| while scanning the definition will simply stop the scanning + instead of producing an ``undefined control sequence'' error or + expanding the previous meaning. This allows, for instance, + `\.{\\chardef\\foo=123\\foo}'. + */ + n = cur_chr; + get_r_token(); + p = cur_cs; + define(p, relax_cmd, too_big_char); + scan_optional_equals(); + switch (n) { + case char_def_code: + scan_char_num(); + define(p, char_given_cmd, cur_val); + break; + case math_char_def_code: + mval = scan_mathchar(tex_mathcode); + /* if (math_umathcode_meaning_par == 1) { */ + /* cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; */ + /* define(p, xmath_given_cmd, cur_val); */ + /* } else { */ + cur_val = (mval.class_value * 16 + mval.family_value) * 256 + mval.character_value; + define(p, math_given_cmd, cur_val); + /* } */ + break; + case xmath_char_def_code: + mval = scan_mathchar(umath_mathcode); + cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; + define(p, xmath_given_cmd, cur_val); + break; + case umath_char_def_code: + mval = scan_mathchar(umathnum_mathcode); + cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; + define(p, xmath_given_cmd, cur_val); + break; + default: + scan_register_num(); + switch (n) { + case count_def_code: + define(p, assign_int_cmd, count_base + cur_val); + break; + case attribute_def_code: + define(p, assign_attr_cmd, attribute_base + cur_val); + break; + case dimen_def_code: + define(p, assign_dimen_cmd, scaled_base + cur_val); + break; + case skip_def_code: + define(p, assign_glue_cmd, skip_base + cur_val); + break; + case mu_skip_def_code: + define(p, assign_mu_glue_cmd, mu_skip_base + cur_val); + break; + case toks_def_code: + define(p, assign_toks_cmd, toks_base + cur_val); + break; + default: + confusion("shorthand_def"); + break; + } + break; + } + break; + case read_to_cs_cmd: + j = cur_chr; + scan_int(); + n = cur_val; + if (!scan_keyword("to")) { + print_err("Missing `to' inserted"); + help2( + "You should have said `\\read<number> to \\cs'.", + "I'm going to look for the \\cs now." + ); + error(); + } + get_r_token(); + p = cur_cs; + read_toks(n, p, j); + define(p, call_cmd, cur_val); + break; + case toks_register_cmd: + case assign_toks_cmd: + /*tex + The token-list parameters, \.{\\output} and \.{\\everypar}, etc., + receive their values in the following way. (For safety's sake, we + place an enclosing pair of braces around an \.{\\output} list.) + */ + q = cur_cs; + if (cur_cmd == toks_register_cmd) { + scan_register_num(); + p = toks_base + cur_val; + } else { + /*tex |p=every_par_loc| or |output_routine_loc| or \dots */ + p = cur_chr; + } + scan_optional_equals(); + /*tex Get the next non-blank non-relax non-call token */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd != left_brace_cmd) { + /*tex + If the right-hand side is a token parameter or token + register, finish the assignment and |goto done| + */ + if (cur_cmd == toks_register_cmd) { + scan_register_num(); + cur_cmd = assign_toks_cmd; + cur_chr = toks_base + cur_val; + } + if (cur_cmd == assign_toks_cmd) { + q = equiv(cur_chr); + if (q == null) { + define(p, undefined_cs_cmd, null); + } else { + add_token_ref(q); + define(p, call_cmd, q); + } + goto DONE; + } + } + back_input(); + cur_cs = q; + q = scan_toks(false, false); + if (token_link(def_ref) == null) { /* empty list: revert to the default */ + define(p, undefined_cs_cmd, null); + free_avail(def_ref); + } else { + if (p == output_routine_loc) { /* enclose in curlies */ + p = get_avail(); + set_token_link(q, p); + p = output_routine_loc; + q = token_link(q); + set_token_info(q, right_brace_token + '}'); + q = get_avail(); + set_token_info(q, left_brace_token + '{'); + set_token_link(q, token_link(def_ref)); + set_token_link(def_ref, q); + } + define(p, call_cmd, def_ref); + } + break; + case assign_int_cmd: + /*tex Similar routines are used to assign values to the numeric parameters. */ + p = cur_chr; + scan_optional_equals(); + scan_int(); + assign_internal_value(a, p, cur_val); + break; + case assign_attr_cmd: + p = cur_chr; + scan_optional_equals(); + scan_int(); + if ((p - attribute_base) > max_used_attr) + max_used_attr = (p - attribute_base); + attr_list_cache = cache_disabled; + word_define(p, cur_val); + break; + case assign_direction_cmd: + case assign_dir_cmd: + /*tex Assign direction codes. */ + if (cur_cmd == assign_direction_cmd) { + p = cur_chr; + scan_optional_equals(); + scan_int(); + check_dir_value(cur_val); + cur_chr = p; + } else { + scan_direction(); + } + switch (cur_chr) { + case int_base + page_direction_code: + eq_word_define(int_base + page_direction_code, cur_val); + break; + case int_base + body_direction_code: + eq_word_define(int_base + body_direction_code, cur_val); + break; + case int_base + par_direction_code: + eq_word_define(int_base + par_direction_code, cur_val); + break; + case int_base + math_direction_code: + eq_word_define(int_base + math_direction_code, cur_val); + break; + case int_base + text_direction_code: + case int_base + line_direction_code: + /* + pre version 0.97 this was a commented section because various tests hint that this + is unnecessary and sometimes even produces weird results, like: + + (\hbox{\textdir TRT ABC\textdir TLT DEF})) + + becomes + + (DEFCBA) + + in the output when we use + + tail_append(new_dir(text_direction_par) + + but when we append the reverse of the current it goes better + + */ + check_glue = (cur_chr == (int_base + line_direction_code)); + if (check_glue) { + cur_chr = int_base + text_direction_code ; + } + if (abs(mode) == hmode) { + if (no_local_dirs_par > 0) { + /*tex |tail| is non zero but we test anyway. */ + if (check_glue && (tail != null && type(tail) == glue_node)) { + halfword prev = alink(tail); + halfword dirn = new_dir(text_direction_par); + subtype(dirn) = cancel_dir; + couple_nodes(prev,dirn); + couple_nodes(dirn,tail); + } else { + tail_append(new_dir(text_direction_par)); + subtype(tail) = cancel_dir; + } + } else { + /*tex What is the use of nolocaldirs? Maybe we should get rid of it. */ + } + update_text_dir_ptr(cur_val); + tail_append(new_dir(cur_val)); + dir_level(tail) = cur_level; + } else { + update_text_dir_ptr(cur_val); + } + eq_word_define(int_base + text_direction_code, cur_val); + eq_word_define(int_base + no_local_dirs_code, no_local_dirs_par + 1); + break; + } + break; + case assign_dimen_cmd: + p = cur_chr; + scan_optional_equals(); + scan_normal_dimen(); + assign_internal_value(a, p, cur_val); + break; + case assign_glue_cmd: + case assign_mu_glue_cmd: + p = cur_chr; + n = cur_cmd; + scan_optional_equals(); + if (n == assign_mu_glue_cmd) + scan_glue(mu_val_level); + else + scan_glue(glue_val_level); + define(p, glue_ref_cmd, cur_val); + break; + case def_char_code_cmd: + case def_del_code_cmd: + /*tex Let |n| be the largest legal code value, based on |cur_chr| */ + if (cur_chr == cat_code_base) + n = max_char_code; + else if (cur_chr == sf_code_base) + n = 077777; + else + n = biggest_char; + p = cur_chr; + if (cur_chr == math_code_base) { + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + scan_extdef_math_code(cur_val1, tex_mathcode); + } else if (cur_chr == lc_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(lc_code_base); + define_lc_code(p, cur_val); + } else if (cur_chr == uc_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(uc_code_base); + define_uc_code(p, cur_val); + } else if (cur_chr == sf_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(sf_code_base); + define_sf_code(p, cur_val); + } else if (cur_chr == cat_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(cat_code_base); + define_cat_code(p, cur_val); + } else if (cur_chr == del_code_base) { + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + scan_extdef_del_code(cur_val1, tex_mathcode); + } + break; + case extdef_math_code_cmd: + case extdef_del_code_cmd: + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + if (cur_chr == math_code_base) + scan_extdef_math_code(cur_val1, umath_mathcode); + else if (cur_chr == math_code_base + 1) + scan_extdef_math_code(cur_val1, umathnum_mathcode); + else if (cur_chr == del_code_base) + scan_extdef_del_code(cur_val1, umath_mathcode); + else if (cur_chr == del_code_base + 1) + scan_extdef_del_code(cur_val1, umathnum_mathcode); + break; + case def_family_cmd: + p = cur_chr; + scan_math_family_int(); + cur_val1 = cur_val; + scan_optional_equals(); + scan_font_ident(); + define_fam_fnt(cur_val1, p, cur_val); + break; + case set_math_param_cmd: + p = cur_chr; + get_token(); + if (cur_cmd != math_style_cmd) { + print_err("Missing math style, treated as \\displaystyle"); + help1( + "A style should have been here; I inserted `\\displaystyle'." + ); + cur_val1 = display_style; + back_error(); + } else { + cur_val1 = cur_chr; + } + scan_optional_equals(); + if (p < math_param_first_mu_glue) { + if (p == math_param_radical_degree_raise) + scan_int(); + else + scan_dimen(false, false, false); + } else { + scan_glue(mu_val_level); + if (cur_val == thin_mu_skip_par) + cur_val = thin_mu_skip_code; + else if (cur_val == med_mu_skip_par) + cur_val = med_mu_skip_code; + else if (cur_val == thick_mu_skip_par) + cur_val = thick_mu_skip_code; + } + define_math_param(p, cur_val1, cur_val); + break; + case register_cmd: + case advance_cmd: + case multiply_cmd: + case divide_cmd: + do_register_command(a); + break; + case set_box_cmd: + /*tex + The processing of boxes is somewhat different, because we may + need to scan and create an entire box before we actually change + the value of the old one. + */ + scan_register_num(); + if (is_global(a)) + n = global_box_flag + cur_val; + else + n = box_flag + cur_val; + scan_optional_equals(); + if (set_box_allowed) { + scan_box(n); + } else { + print_err("Improper \\setbox"); + help3( + "Sorry, \\setbox is not allowed after \\halign in a display,", + "between \\accent and an accented character, or in immediate", + "assignments." + ); + error(); + } + break; + case set_aux_cmd: + /*tex + The |space_factor| or |prev_depth| settings are changed when a + |set_aux| command is sensed. Similarly, |prev_graf| is changed in + the presence of |set_prev_graf|, and |dead_cycles| or + |insert_penalties| in the presence of |set_page_int|. These + definitions are always global. + */ + alter_aux(); + break; + case set_prev_graf_cmd: + alter_prev_graf(); + break; + case set_page_dimen_cmd: + alter_page_so_far(); + break; + case set_page_int_cmd: + alter_integer(); + break; + case set_box_dimen_cmd: + /*tex + When some dimension of a box register is changed, the change + isn't exactly global; but \TeX\ does not look at the \.{\\global} + switch. + */ + alter_box_dimen(); + break; + case set_tex_shape_cmd: + q = cur_chr; + scan_optional_equals(); + scan_int(); + n = cur_val; + if (n <= 0) { + p = null; + } else { + p = new_node(shape_node, 2 * (n + 1) + 1); + vinfo(p + 1) = n; + for (j = 1; j <= n; j++) { + scan_normal_dimen(); + varmem[p + 2 * j].cint = cur_val; /* indentation */ + scan_normal_dimen(); + varmem[p + 2 * j + 1].cint = cur_val; /* width */ + } + } + define(q, shape_ref_cmd, p); + break; + case set_etex_shape_cmd: + q = cur_chr; + scan_optional_equals(); + scan_int(); + n = cur_val; + if (n <= 0) { + p = null; + } else { + n = (cur_val / 2) + 1; + p = new_node(shape_node, 2 * n + 1 + 1); + vinfo(p + 1) = n; + n = cur_val; + varmem[p + 2].cint = n; /* number of penalties */ + for (j = p + 3; j <= p + n + 2; j++) { + scan_int(); + varmem[j].cint = cur_val; /* penalty values */ + } + if (!odd(n)) + varmem[p + n + 3].cint = 0; /* unused */ + } + define(q, shape_ref_cmd, p); + break; + case hyph_data_cmd: + /*tex + All of \TeX's parameters are kept in |eqtb| except the font + information, the interaction mode, and the hyphenation tables; + these are strictly global. + */ + switch (cur_chr) { + case 0: + new_hyph_exceptions(); + break; + case 1: + new_patterns(); + break; + case 2: + new_pre_hyphen_char(); + break; + case 3: + new_post_hyphen_char(); + break; + case 4: + new_pre_exhyphen_char(); + break; + case 5: + new_post_exhyphen_char(); + break; + case 6: + new_hyphenation_min(); + break; + case 7: + new_hj_code(); + break; + } + break; + case assign_font_dimen_cmd: + set_font_dimen(); + break; + case assign_font_int_cmd: + n = cur_chr; + scan_font_ident(); + f = cur_val; + if (n == no_lig_code) { + set_no_ligatures(f); + } else if (n < lp_code_base) { + scan_optional_equals(); + scan_int(); + if (n == 0) + set_hyphen_char(f, cur_val); + else + set_skew_char(f, cur_val); + } else { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + switch (n) { + case lp_code_base: + set_lp_code(f, p, cur_val); + break; + case rp_code_base: + set_rp_code(f, p, cur_val); + break; + case ef_code_base: + set_ef_code(f, p, cur_val); + break; + case tag_code: + set_tag_code(f, p, cur_val); + break; + } + } + break; + case def_font_cmd: + /*tex Here is where the information for a new font gets loaded. */ + tex_def_font((small_number) a); + break; + case def_lua_call_cmd: + get_r_token(); + p = cur_cs; + scan_optional_equals(); + scan_int(); + if (j != 0) { + define(p, lua_call_cmd, cur_val); + } else { + define(p, lua_expandable_call_cmd, cur_val); + } + break; + case letterspace_font_cmd: + new_letterspaced_font((small_number) a); + break; + case copy_font_cmd: + make_font_copy((small_number) a); + break; + case set_font_id_cmd: + scan_int(); + if (is_valid_font(cur_val)) + zset_cur_font(cur_val); + break ; + case set_interaction_cmd: + new_interaction(); + break; + default: + confusion("prefix"); + break; + } + /*tex End of assignments cases. */ + DONE: + /*tex Insert a token saved by \.{\\afterassignment}, if any. */ + if (after_token != 0) { + cur_tok = after_token; + back_input(); + after_token = 0; + } +} + +void fixup_directions(void) +{ + int temp_no_whatsits = no_local_whatsits_par; + int temp_no_dirs = no_local_dirs_par; + int temporary_dir = text_direction_par; + if (dir_level(text_dir_ptr) == cur_level) { + /* Remove from |text_dir_ptr|. */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; + } + unsave(); + if (abs(mode) == hmode) { + if (temp_no_dirs != 0) { + /* Add local dir node. */ + tail_append(new_dir(text_direction_par)); + dir_dir(tail) = temporary_dir; + subtype(tail) = cancel_dir; + } + if (temp_no_whatsits != 0) { + /*tex Add local paragraph node. */ + tail_append(make_local_par_node(hmode_par_par_code)); + } + } +} + +/*tex + + This is experimental and needs more checking! + +*/ + +void fixup_directions_only(void) +{ + int temp_no_dirs = no_local_dirs_par; + int temporary_dir = text_direction_par; + if (dir_level(text_dir_ptr) == cur_level) { + /* Remove from |text_dir_ptr|. */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; + } + if (temp_no_dirs != 0) { + /* Add local dir node. */ + tail_append(new_dir(text_direction_par)); + dir_dir(tail) = temporary_dir; + subtype(tail) = cancel_dir; + } +} + +/*tex + +When a control sequence is to be defined, by \.{\\def} or \.{\\let} or something +similar, the |get_r_token| routine will substitute a special control sequence for +a token that is not redefinable. + +*/ + +void get_r_token(void) +{ + RESTART: + do { + get_token(); + } while (cur_tok == space_token); + if ((cur_cs == 0) || (cur_cs > eqtb_top) || + ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) { + print_err("Missing control sequence inserted"); + help5( + "Please don't say `\\def cs{...}', say `\\def\\cs{...}'.", + "I've inserted an inaccessible control sequence so that your", + "definition will be completed without mixing me up too badly.", + "You can recover graciously from this error, if you're", + "careful; see exercise 27.2 in The TeXbook." + ); + if (cur_cs == 0) + back_input(); + cur_tok = cs_token_flag + frozen_protection; + ins_error(); + goto RESTART; + } +} + +void assign_internal_value(int a, halfword p, int val) +{ + halfword n; + if ((p >= int_base) && (p < attribute_base)) { + switch ((p - int_base)) { + case cat_code_table_code: + if (valid_catcode_table(val)) { + if (val != cat_code_table_par) + word_define(p, val); + } else { + print_err("Invalid \\catcode table"); + help2( + "You can only switch to a \\catcode table that is initialized", + "using \\savecatcodetable or \\initcatcodetable, or to table 0" + ); + error(); + } + break; + case output_box_code: + if ((val > 65535) | (val < 0)) { + print_err("Invalid \\outputbox"); + help1( + "The value for \\outputbox has to be between 0 and 65535." + ); + error(); + } else { + word_define(p, val); + } + break; + case new_line_char_code: + if (val > 127) { + print_err("Invalid \\newlinechar"); + help2( + "The value for \\newlinechar has to be no higher than 127.", + "Your invalid assignment will be ignored." + ); + error(); + } else { + word_define(p, val); + } + break; + case end_line_char_code: + if (val > 127) { + print_err("Invalid \\endlinechar"); + help2( + "The value for \\endlinechar has to be no higher than 127.", + "Your invalid assignment will be ignored." + ); + error(); + } else { + word_define(p, val); + } + break; + case language_code: + if (val < 0) { + word_define(int_base + cur_lang_code, -1); + word_define(p, -1); + } else if (val > 16383) { + print_err("Invalid \\language"); + help2( + "The absolute value for \\language has to be no higher than 16383.", + "Your invalid assignment will be ignored." + ); + error(); + } else { + word_define(int_base + cur_lang_code, val); + word_define(p, val); + } + break; + default: + word_define(p, val); + break; + } + /*tex + If we are defining subparagraph penalty levels while we are in hmode, + then we put out a whatsit immediately, otherwise we leave it alone. + This mechanism might not be sufficiently powerful, and some other + algorithm, searching down the stack, might be necessary. Good first + step. + */ + if ((abs(mode) == hmode) && + ((p == (int_base + local_inter_line_penalty_code)) || + (p == (int_base + local_broken_penalty_code)))) { + /*tex Add local paragraph node */ + tail_append(make_local_par_node(penalty_par_code)); + eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); + } + } else if ((p >= dimen_base) && (p <= eqtb_size)) { + if (p == (dimen_base + page_left_offset_code)) { + n = val - one_true_inch; + word_define(dimen_base + h_offset_code, n); + } else if (p == (dimen_base + h_offset_code)) { + n = val + one_true_inch; + word_define(dimen_base + page_left_offset_code, n); + } else if (p == (dimen_base + page_top_offset_code)) { + n = val - one_true_inch; + word_define(dimen_base + v_offset_code, n); + } else if (p == (dimen_base + v_offset_code)) { + n = val + one_true_inch; + word_define(dimen_base + page_top_offset_code, n); + } + word_define(p, val); + } else if ((p >= local_base) && (p < toks_base)) { + /*tex internal locals */ + define(p, call_cmd, val); + } else { + confusion("assign internal value"); + } +} + +/*tex + +We use the fact that |register| $<$ |advance| $<$ |multiply| $<$ |divide|/ We +compute the register location |l| and its type |p| but |return| if invalid. Here +we use the fact that the consecutive codes |int_val..mu_val| and +|assign_int..assign_mu_glue| correspond to each other nicely. + +*/ + +void do_register_command(int a) +{ + int p; + halfword q = cur_cmd; + halfword l = 0; + if (q != register_cmd) { + get_x_token(); + if ((cur_cmd >= assign_int_cmd) && (cur_cmd <= assign_mu_glue_cmd)) { + l = cur_chr; + p = cur_cmd - assign_int_cmd; + goto FOUND; + } + if (cur_cmd != register_cmd) { + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + tprint("' after "); + print_cmd_chr((quarterword) q, 0); + help1( + "I'm forgetting what you said and not changing anything." + ); + error(); + return; + } + } + p = cur_chr; + scan_register_num(); + if (p == int_val_level) + l = cur_val + count_base; + else if (p == attr_val_level) + l = cur_val + attribute_base; + else if (p == dimen_val_level) + l = cur_val + scaled_base; + else if (p == glue_val_level) + l = cur_val + skip_base; + else if (p == mu_val_level) + l = cur_val + mu_skip_base; + FOUND: + if (q == register_cmd) { + scan_optional_equals(); + } else if (scan_keyword("by")) { + /*tex optional `\.{by}' */ + } + arith_error = false; + if (q < multiply_cmd) { + /*tex Compute result of |register| or |advance|, put it in |cur_val|. */ + if (p < glue_val_level) { + if ((p == int_val_level) || (p == attr_val_level)) + scan_int(); + else + scan_normal_dimen(); + if (q == advance_cmd) + cur_val = cur_val + eqtb[l].cint; + } else { + scan_glue(p); + if (q == advance_cmd) { + /* Compute the sum of two glue specs */ + halfword r = equiv(l); + q = new_spec(cur_val); + flush_node(cur_val); + width(q) = width(q) + width(r); + if (stretch(q) == 0) { + stretch_order(q) = normal; + } + if (stretch_order(q) == stretch_order(r)) { + stretch(q) = stretch(q) + stretch(r); + } else if ((stretch_order(q) < stretch_order(r)) && (stretch(r) != 0)) { + stretch(q) = stretch(r); + stretch_order(q) = stretch_order(r); + } + if (shrink(q) == 0) { + shrink_order(q) = normal; + } + if (shrink_order(q) == shrink_order(r)) { + shrink(q) = shrink(q) + shrink(r); + } else if ((shrink_order(q) < shrink_order(r)) && (shrink(r) != 0)) { + shrink(q) = shrink(r); + shrink_order(q) = shrink_order(r); + } + cur_val = q; + } + } + } else { + /*tex Compute result of |multiply| or |divide|, put it in |cur_val| */ + scan_int(); + if (p < glue_val_level) { + if (q == multiply_cmd) { + if ((p == int_val_level) || (p == attr_val_level)) { + cur_val = mult_integers(eqtb[l].cint, cur_val); + } else { + cur_val = nx_plus_y(eqtb[l].cint, cur_val, 0); + } + } else { + cur_val = x_over_n(eqtb[l].cint, cur_val); + } + } else { + halfword s = equiv(l); + halfword r = new_spec(s); + if (q == multiply_cmd) { + width(r) = nx_plus_y(width(s), cur_val, 0); + stretch(r) = nx_plus_y(stretch(s), cur_val, 0); + shrink(r) = nx_plus_y(shrink(s), cur_val, 0); + } else { + width(r) = x_over_n(width(s), cur_val); + stretch(r) = x_over_n(stretch(s), cur_val); + shrink(r) = x_over_n(shrink(s), cur_val); + } + cur_val = r; + } + } + if (arith_error) { + print_err("Arithmetic overflow"); + help2( + "I can't carry out that multiplication or division,", + "since the result is out of range." + ); + if (p >= glue_val_level) + flush_node(cur_val); + error(); + return; + } + if (p < glue_val_level) { + if (p == attr_val_level) { + if ((l - attribute_base) > max_used_attr) + max_used_attr = (l - attribute_base); + attr_list_cache = cache_disabled; + } + if ((p == int_val_level) || (p == dimen_val_level)) + assign_internal_value(a, l, cur_val); + else + word_define(l, cur_val); + } else { + define(l, glue_ref_cmd, cur_val); + } +} + +void alter_aux(void) +{ + halfword c; /* |hmode| or |vmode| */ + if (cur_chr != abs(mode)) { + report_illegal_case(); + } else { + c = cur_chr; + scan_optional_equals(); + if (c == vmode) { + scan_normal_dimen(); + prev_depth_par = cur_val; + } else { + scan_int(); + if ((cur_val <= 0) || (cur_val > 32767)) { + print_err("Bad space factor"); + help1( + "I allow only values in the range 1..32767 here." + ); + int_error(cur_val); + } else { + space_factor_par = cur_val; + } + } + } +} + +void alter_prev_graf(void) +{ + int p = nest_ptr; /* index into |nest| */ + while (abs(nest[p].mode_field) != vmode) + decr(p); + scan_optional_equals(); + scan_int(); + if (cur_val < 0) { + print_err("Bad \\prevgraf"); + help1( + "I allow only nonnegative values here." + ); + int_error(cur_val); + } else { + nest[p].pg_field = cur_val; + } +} + +void alter_page_so_far(void) +{ + int c = cur_chr; /* index into |page_so_far| */ + scan_optional_equals(); + scan_normal_dimen(); + page_so_far[c] = cur_val; +} + +/*tex + The value of |c| is 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. +*/ + +void alter_integer(void) +{ + int c = cur_chr; + scan_optional_equals(); + scan_int(); + if (c == 0) { + dead_cycles = cur_val; + } else if (c == 2) { + if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) { + print_err("Bad interaction mode"); + help2( + "Modes are 0=batch, 1=nonstop, 2=scroll, and", + "3=errorstop. Proceed, and I'll ignore this case." + ); + int_error(cur_val); + } else { + cur_chr = cur_val; + new_interaction(); + } + } else { + insert_penalties = cur_val; + } +} + +void alter_box_dimen(void) +{ + int c; /* |width_offset| or |height_offset| or |depth_offset| */ + int b; /* box number */ + c = cur_chr; + scan_register_num(); + b = cur_val; + scan_optional_equals(); + scan_normal_dimen(); + if (box(b) != null) + varmem[box(b) + c].cint = cur_val; +} + +void new_interaction(void) +{ + print_ln(); + interaction = cur_chr; + if (interaction == batch_mode) + kpse_make_tex_discard_errors = 1; + else + kpse_make_tex_discard_errors = 0; + fixup_selector(log_opened_global); +} + +/*tex + +The \.{\\afterassignment} command puts a token into the global variable +|after_token|. This global variable is examined just after every assignment has +been performed. It's value is zero, or a saved token. + +*/ + +halfword after_token; + +/*tex + + Here is a procedure that might be called `Get the next non-blank non-relax + non-call non-assignment token'. + +*/ + +void do_assignments(void) +{ + while (true) { + /*tex Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd <= max_non_prefixed_command) + return; + set_box_allowed = false; + prefixed_command(); + set_box_allowed = true; + } +} + +/*tex |cur_chr| is 1 for \.{\\openin}, 0 for \.{\\closein}: */ + +void open_or_close_in(void) +{ + int c = cur_chr; + int n; + char *fn; + scan_four_bit_int(); + n = cur_val; + if (read_open[n] != closed) { + lua_a_close_in(read_file[n], (n + 1)); + read_open[n] = closed; + } + if (c != 0) { + scan_optional_equals(); + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + back_input(); + if (cur_cmd != left_brace_cmd) { + /*tex Set |cur_name| to desired file name. */ + scan_file_name(); + } else { + scan_file_name_toks(); + } + fn = pack_file_name(cur_name, cur_area, cur_ext); + if (lua_a_open_in(&(read_file[n]), fn, (n + 1))) { + read_open[n] = just_open; + } + } +} + +/*tex + Has the long \.{\\errmessage} help been used? +*/ + +boolean long_help_seen; + +void issue_message(void) +{ + int old_setting; /* holds |selector| setting */ + int c; /* identifies \.{\\message} and \.{\\errmessage} */ + str_number s; /* the message */ + c = cur_chr; + (void) scan_toks(false, true); + old_setting = selector; + selector = new_string; + token_show(def_ref); + selector = old_setting; + flush_list(def_ref); + str_room(1); + s = make_string(); + if (c == 0) { + /*tex Print string |s| on the terminal */ + if (term_offset + (int) str_length(s) > max_print_line - 2) + print_ln(); + else if ((term_offset > 0) || (file_offset > 0)) + print_char(' '); + print(s); + update_terminal(); + + } else { + /*tex + Print string |s| as an error message. If \.{\\errmessage} occurs + often in |scroll_mode|, without user-defined \.{\\errhelp}, we don't + want to give a long help message each time. So we give a verbose + explanation only once. + */ + print_err(""); + print(s); + if (err_help_par != null) { + use_err_help = true; + } else if (long_help_seen) { + help1( + "(That was another \\errmessage.)" + ); + } else { + if (interaction < error_stop_mode) + long_help_seen = true; + help4( + "This error message was generated by an \\errmessage", + "command, so I can't give any explicit help.", + "Pretend that you're Hercule Poirot: Examine all clues,", + "and deduce the truth by order and method." + ); + } + error(); + use_err_help = false; + + } + flush_str(s); +} + +/*tex + +The |error| routine calls on |give_err_help| if help is requested from the +|err_help| parameter. + +*/ + +void give_err_help(void) +{ + token_show(err_help_par); +} + +/*tex + +The \.{\\uppercase} and \.{\\lowercase} commands are implemented by building a +token list and then changing the cases of the letters in it. + +*/ + +void shift_case(void) +{ + halfword b; /* |lc_code_base| or |uc_code_base| */ + halfword p; /* runs through the token list */ + halfword t; /* token */ + halfword c; /* character code */ + halfword i; /* inbetween */ + b = cur_chr; + p = scan_toks(false, false); + p = token_link(def_ref); + while (p != null) { + /*tex + Change the case of the token in |p|, if a change is appropriate. When + the case of a |chr_code| changes, we don't change the |cmd|. We also + change active characters. + */ + t = token_info(p); + if (t < cs_token_flag) { + c = t % STRING_OFFSET; + if (b == uc_code_base) + i = get_uc_code(c); + else + i = get_lc_code(c); + if (i != 0) + set_token_info(p, t - c + i); + } else if (is_active_cs(cs_text(t - cs_token_flag))) { + c = active_cs_value(cs_text(t - cs_token_flag)); + if (b == uc_code_base) + i = get_uc_code(c); + else + i = get_lc_code(c); + if (i != 0) + set_token_info(p, active_to_cs(i, true) + cs_token_flag); + } + p = token_link(p); + } + back_list(token_link(def_ref)); + free_avail(def_ref); +} + +/*tex + +We come finally to the last pieces missing from |main_control|, namely the +`\.{\\show}' commands that are useful when debugging. + +*/ + +void show_whatever(void) +{ + halfword p; /* tail of a token list to show */ + int t; /* type of conditional being shown */ + int m; /* upper bound on |fi_or_else| codes */ + int l; /* line where that conditional began */ + int n; /* level of \.{\\if...\\fi} nesting */ + switch (cur_chr) { + case show_lists: + begin_diagnostic(); + show_activities(); + break; + case show_box_code: + /*tex Show the current contents of a box. */ + scan_register_num(); + begin_diagnostic(); + tprint_nl("> \\box"); + print_int(cur_val); + print_char('='); + if (box(cur_val) == null) + tprint("void"); + else + show_box(box(cur_val)); + break; + case show_code: + /*tex Show the current meaning of a token, then |goto common_ending|. */ + get_token(); + if (interaction == error_stop_mode) + wake_up_terminal(); + tprint_nl("> "); + if (cur_cs != 0) { + sprint_cs(cur_cs); + print_char('='); + } + print_meaning(); + goto COMMON_ENDING; + break; + /*tex Cases for |show_whatever| */ + case show_groups: + begin_diagnostic(); + show_save_groups(); + break; + case show_ifs: + begin_diagnostic(); + tprint_nl(""); + print_ln(); + if (cond_ptr == null) { + tprint_nl("### "); + tprint("no active conditionals"); + } else { + p = cond_ptr; + n = 0; + do { + incr(n); + p = vlink(p); + } while (p != null); + p = cond_ptr; + t = cur_if; + l = if_line; + m = if_limit; + do { + tprint_nl("### level "); + print_int(n); + tprint(": "); + print_cmd_chr(if_test_cmd, t); + if (m == fi_code) + tprint_esc("else"); + print_if_line(l); + decr(n); + t = if_limit_subtype(p); + l = if_line_field(p); + m = if_limit_type(p); + p = vlink(p); + } while (p != null); + } + break; + default: + /*tex + Show the current value of some parameter or register, then |goto + common_ending|. + */ + p = the_toks(); + if (interaction == error_stop_mode) + wake_up_terminal(); + tprint_nl("> "); + token_show(temp_token_head); + flush_list(token_link(temp_token_head)); + goto COMMON_ENDING; + break; + } + /*tex Complete a potentially long \.{\\show} command: */ + end_diagnostic(true); + print_err("OK"); + if (selector == term_and_log) { + if (tracing_online_par <= 0) { + selector = term_only; + tprint(" (see the transcript file)"); + selector = term_and_log; + } + } + COMMON_ENDING: + if (interaction < error_stop_mode) { + help0(); + decr(error_count); + } else if (tracing_online_par > 0) { + help3( + "This isn't an error message; I'm just \\showing something.", + "Type `I\\show...' to show more (e.g., \\show\\cs,", + "\\showthe\\count10, \\showbox255, \\showlists)." + ); + } else { + help5( + "This isn't an error message; I'm just \\showing something.", + "Type `I\\show...' to show more (e.g., \\show\\cs,", + "\\showthe\\count10, \\showbox255, \\showlists).", + "And type `I\\tracingonline=1\\show...' to show boxes and", + "lists on your terminal as well as in the transcript file." + ); + } + error(); +} + +/*tex + This procedure gets things started properly: +*/ + +void initialize(void) +{ + int k; /* index into |mem|, |eqtb|, etc. */ + /*tex + Initialize whatever \TeX\ might access and set initial values of key + variables + */ + initialize_errors(); + initialize_arithmetic(); + max_used_attr = -1; + attr_list_cache = cache_disabled; + initialize_nesting(); + /*tex Start a new current page: */ + page_contents = empty; + page_tail = page_head; +#if 0 + vlink(page_head) = null; +#endif + last_glue = max_halfword; + last_penalty = 0; + last_kern = 0; + last_node_type = -1; + page_depth = 0; + page_max_depth = 0; + + initialize_equivalents(); + no_new_control_sequence = true; /* new identifiers are usually forbidden */ + init_primitives(); + + mag_set = 0; + initialize_marks(); + initialize_read(); + + static_pdf = init_pdf_struct(static_pdf); /* should be init_backend() */ + + format_ident = 0; + format_name = get_nullstr(); + initialize_directions(); + initialize_write_files(); + seconds_and_micros(epochseconds, microseconds); + initialize_start_time(static_pdf); + + edit_name_start = 0; + stop_at_space = true; + + if (ini_version) { + /*tex Initialize table entries (done by \.{INITEX} only). */ + init_node_mem(500); + initialize_tokens(); + /*tex Initialize the special list heads and constant nodes. */ + initialize_alignments(); + initialize_buildpage(); + + initialize_active(); + + set_eq_type(undefined_control_sequence, undefined_cs_cmd); + set_equiv(undefined_control_sequence, null); + set_eq_level(undefined_control_sequence, level_zero); + for (k = null_cs; k <= (eqtb_top - 1); k++) + eqtb[k] = eqtb[undefined_control_sequence]; + set_equiv(glue_base, zero_glue); + set_eq_level(glue_base, level_one); + set_eq_type(glue_base, glue_ref_cmd); + for (k = glue_base + 1; k <= local_base - 1; k++) { + eqtb[k] = eqtb[glue_base]; + } + par_shape_par_ptr = null; + set_eq_type(par_shape_loc, shape_ref_cmd); + set_eq_level(par_shape_loc, level_one); + for (k = etex_pen_base; k <= (etex_pens - 1); k++) + eqtb[k] = eqtb[par_shape_loc]; + for (k = output_routine_loc; k <= toks_base + biggest_reg; k++) + eqtb[k] = eqtb[undefined_control_sequence]; + box(0) = null; + set_eq_type(box_base, box_ref_cmd); + set_eq_level(box_base, level_one); + for (k = box_base + 1; k <= (box_base + biggest_reg); k++) + eqtb[k] = eqtb[box_base]; + cur_font_par = null_font; + set_eq_type(cur_font_loc, data_cmd); + set_eq_level(cur_font_loc, level_one); + set_equiv(cat_code_base, 0); + set_eq_type(cat_code_base, data_cmd); + set_eq_level(cat_code_base, level_one); + eqtb[internal_math_param_base] = eqtb[cat_code_base]; + eqtb[lc_code_base] = eqtb[cat_code_base]; + eqtb[uc_code_base] = eqtb[cat_code_base]; + eqtb[sf_code_base] = eqtb[cat_code_base]; + eqtb[math_code_base] = eqtb[cat_code_base]; + cat_code_table_par = 0; + initialize_math_codes(); + initialize_text_codes(); + initex_cat_codes(0); + for (k = '0'; k <= '9'; k++) + set_math_code(k, math_use_current_family_code, 0, k, level_one); + for (k = 'A'; k <= 'Z'; k++) { + set_math_code(k, math_use_current_family_code, 1, k, level_one); + set_math_code((k + 32), math_use_current_family_code, 1, (k + 32), level_one); + set_lc_code(k, k + 32, level_one); + set_lc_code(k + 32, k + 32, level_one); + set_uc_code(k, k, level_one); + set_uc_code(k + 32, k, level_one); + set_sf_code(k, 999, level_one); + } + for (k = int_base; k <= attribute_base - 1; k++) + eqtb[k].cint = 0; + for (k = attribute_base; k <= del_code_base - 1; k++) + eqtb[k].cint = UNUSED_ATTRIBUTE; + mag_par = 1000; + tolerance_par = 10000; + hang_after_par = 1; + max_dead_cycles_par = 25; + math_pre_display_gap_factor_par = 2000; + pre_bin_op_penalty_par = inf_penalty; + math_script_box_mode_par = 1; + math_script_char_mode_par = 1; + pre_rel_penalty_par = inf_penalty; + compound_hyphen_mode_par = 1; + escape_char_par = '\\'; + end_line_char_par = carriage_return; + set_del_code('.', 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */ + ex_hyphen_char_par = '-'; + output_box_par = 255; + for (k = dimen_base; k <= eqtb_size; k++) + eqtb[k].cint = 0; + page_left_offset_par = one_inch; + page_top_offset_par = one_inch; + page_right_offset_par = one_inch; + page_bottom_offset_par = one_inch; + ini_init_primitives(); + hash_used = frozen_control_sequence; + hash_high = 0; + cs_count = 0; + set_eq_type(frozen_dont_expand, dont_expand_cmd); + cs_text(frozen_dont_expand) = maketexstring("notexpanded:"); + set_eq_type(frozen_primitive, ignore_spaces_cmd); + set_equiv(frozen_primitive, 1); + set_eq_level(frozen_primitive, level_one); + cs_text(frozen_primitive) = maketexstring("primitive"); + create_null_font(); + font_bytes = 0; + px_dimen_par = one_bp; + math_eqno_gap_step_par = 1000 ; + math_flatten_mode_par = 1; /* ord */ + cs_text(frozen_protection) = maketexstring("inaccessible"); + format_ident = maketexstring(" (INITEX)"); + cs_text(end_write) = maketexstring("endwrite"); + set_eq_level(end_write, level_one); + set_eq_type(end_write, outer_call_cmd); + set_equiv(end_write, null); + /*tex Bah, this is a bad place do do this. */ + set_pdf_major_version(1); + set_pdf_minor_version(0); + } + synctexoffset = int_base + synctex_code; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.h b/Build/source/texk/web2c/luatexdir/tex/maincontrol.h new file mode 100644 index 00000000000..04608c11e9b --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.h @@ -0,0 +1,217 @@ +/* maincontrol.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef MAINCONTROL_H +# define MAINCONTROL_H + +# define fil_code 0 /* identifies \.{\\hfil} and \.{\\vfil} */ +# define fill_code 1 /* identifies \.{\\hfill} and \.{\\vfill} */ +# define ss_code 2 /* identifies \.{\\hss} and \.{\\vss} */ +# define fil_neg_code 3 /* identifies \.{\\hfilneg} and \.{\\vfilneg} */ +# define skip_code 4 /* identifies \.{\\hskip} and \.{\\vskip} */ +# define mskip_code 5 /* identifies \.{\\mskip} */ + +/* The |prefixed_command| does not have to adjust |a| so that |a mod 4=0|, +since the following routines test for the \.{\\global} prefix as follows. +*/ + +# define is_global(a) (a>=4) + +# define define(A,B,C) do { \ + if (is_global(a)) \ + geq_define((A),(quarterword)(B),(C)); \ + else \ + eq_define((A),(quarterword)(B),(C)); \ + } while (0) + +# define word_define(A,B) do { \ + if (is_global(a)) \ + geq_word_define((A),(B)); \ + else \ + eq_word_define((A),(B)); \ + } while (0) + +# define define_lc_code(A,B) do { \ + if (is_global(a)) \ + set_lc_code((A),(B),level_one); \ + else \ + set_lc_code((A),(B),cur_level); \ + } while (0) + +# define define_uc_code(A,B) do { \ + if (is_global(a)) \ + set_uc_code((A),(B),level_one); \ + else \ + set_uc_code((A),(B),cur_level); \ +} while (0) + +# define define_sf_code(A,B) do { \ + if (is_global(a)) \ + set_sf_code((A),(B),level_one); \ + else \ + set_sf_code((A),(B),cur_level); \ + } while (0) + +# define define_cat_code(A,B) do { \ + if (is_global(a)) \ + set_cat_code(cat_code_table_par,(A),(B),level_one); \ + else \ + set_cat_code(cat_code_table_par,(A),(B),cur_level); \ + } while (0) + +# define define_fam_fnt(A,B,C) do { \ + if (is_global(a)) \ + def_fam_fnt((A),(B),(C),level_one); \ + else \ + def_fam_fnt((A),(B),(C),cur_level); \ + } while (0) + +# define define_math_param(A,B,C) do { \ + if (is_global(a)) \ + def_math_param((A),(B),(C),level_one); \ + else \ + def_math_param((A),(B),(C),cur_level); \ + } while (0) + +extern halfword cur_box; /* box to be placed into its context */ + +/* +A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|; +a \.{\\mathchardef} creates a control sequence whose |cmd| is |math_given|; +and the corresponding |chr| is the character code or math code. A \.{\\countdef} +or \.{\\dimendef} or \.{\\skipdef} or \.{\\muskipdef} creates a control +sequence whose |cmd| is |assign_int| or \dots\ or |assign_mu_glue|, and the +corresponding |chr| is the |eqtb| location of the internal register in question. +*/ + +# define char_def_code 0 /* |shorthand_def| for \.{\\chardef} */ +# define math_char_def_code 1 /* |shorthand_def| for \.{\\mathchardef} */ +# define xmath_char_def_code 2 /* |shorthand_def| for \.{\\Umathchardef} */ +# define count_def_code 3 /* |shorthand_def| for \.{\\countdef} */ +# define attribute_def_code 4 /* |shorthand_def| for \.{\\attributedef} */ +# define dimen_def_code 5 /* |shorthand_def| for \.{\\dimendef} */ +# define skip_def_code 6 /* |shorthand_def| for \.{\\skipdef} */ +# define mu_skip_def_code 7 /* |shorthand_def| for \.{\\muskipdef} */ +# define toks_def_code 8 /* |shorthand_def| for \.{\\toksdef} */ +# define umath_char_def_code 9 /* |shorthand_def| for \.{\\Umathcharnumdef} */ + +extern void adjust_space_factor(void); +extern void main_control(void); +extern void app_space(void); +extern void insert_dollar_sign(void); +extern void insert_dollar_sign_par_end(void); +extern void you_cant(void); +extern void report_illegal_case(void); +extern boolean privileged(void); +extern boolean its_all_over(void); +extern void append_glue(void); +extern void append_kern(void); +extern void off_save(void); +extern void handle_right_brace(void); +extern void extra_right_brace(void); +extern void normal_paragraph(void); +extern void box_end(int box_context); +extern void scan_box(int box_context); +extern void new_graf(boolean indented); +extern void indent_in_hmode(void); +extern void head_for_vmode(void); +extern void end_graf(int); +extern void begin_insert_or_adjust(void); +extern void handle_mark(void); +extern void append_penalty(void); +extern void delete_last(void); +extern void unpackage(void); +extern void append_italic_correction(void); +extern void append_local_box(int kind); +extern void append_discretionary(void); +extern void build_local_box(void); +extern void build_discretionary(void); +extern void make_accent(void); +extern void align_error(void); +extern void no_align_error(void); +extern void omit_error(void); +extern void do_endv(void); +extern void cs_error(void); +extern void prefixed_command(void); +extern void fixup_directions(void); +extern void fixup_directions_only(void); + + +/* Assignments from Lua need helpers. */ + +# define is_int_assign(cmd) (cmd==assign_int_cmd) +# define is_attr_assign(cmd) (cmd==assign_attr_cmd) +# define is_dim_assign(cmd) (cmd==assign_dimen_cmd) +# define is_glue_assign(cmd) (cmd==assign_glue_cmd) +# define is_mu_glue_assign(cmd) (cmd==assign_mu_glue_cmd) +# define is_toks_assign(cmd) (cmd==assign_toks_cmd) + +# define show_code 0 /* \.{\\show} */ +# define show_box_code 1 /* \.{\\showbox} */ +# define show_the_code 2 /* \.{\\showthe} */ +# define show_lists 3 /* \.{\\showlists} */ +# define show_groups 4 /* \.{\\showgroups} */ +# define show_tokens 5 /* \.{\\showtokens} , must be odd! */ +# define show_ifs 6 /* \.{\\showifs} */ + +# define print_if_line(A) if ((A)!=0) { \ + tprint(" entered on line "); print_int((A)); \ + } + +/* + extern halfword swap_hang_indent(halfword indentation, halfword shape_mode); + extern halfword swap_parshape_indent(halfword indentation, halfword width, halfword shape_mode); +*/ + +#define swap_hang_indent(indentation) \ + ( ((shape_mode_par == 1 || shape_mode_par == 3 || shape_mode_par == -1 || shape_mode_par == -3)) ? negate(indentation) : indentation ) + +#define swap_parshape_indent(indentation,width) \ + ( ((shape_mode_par == 2 || shape_mode_par == 3 || shape_mode_par == -2 || shape_mode_par == -3)) ? (hsize_par - width - indentation) : indentation ) + +extern void get_r_token(void); +extern void assign_internal_value(int a, halfword p, int cur_val); +extern void do_register_command(int a); +extern void alter_aux(void); +extern void alter_prev_graf(void); +extern void alter_page_so_far(void); +extern void alter_integer(void); +extern void alter_box_dimen(void); +extern void new_interaction(void); +extern halfword after_token; +extern void do_assignments(void); +extern void open_or_close_in(void); +extern boolean long_help_seen; +extern void issue_message(void); +extern void give_err_help(void); +extern void shift_case(void); +extern void show_whatever(void); + +extern void initialize(void); /* this procedure gets things started properly */ + +/*extern int local_level;*/ + +extern void local_control(void); +extern halfword local_scan_box(void); +extern int current_local_level(void); +extern void end_local_control(void); +extern void local_control_message(const char *s); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/mathcodes.c b/Build/source/texk/web2c/luatexdir/tex/mathcodes.c new file mode 100644 index 00000000000..42e8b0b15b9 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mathcodes.c @@ -0,0 +1,360 @@ +/* + +mathnodes.w + +Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +We support tre traditional math codes as well as larger ones suitable for +\UNICODE\ input and fonts. + +*/ + +static sa_tree mathcode_head = NULL; + +/*tex the |0xFFFFFFFF| is a flag value. */ + +#define MATHCODESTACK 8 +#define MATHCODEDEFAULT 0xFFFFFFFF +#define MATHCODEACTIVE 0xFFFFFFFE + +/*tex + +Delcodes are also went larger. + +*/ + +static sa_tree delcode_head = NULL; + +#define DELCODESTACK 4 +#define DELCODEDEFAULT 0xFFFFFFFF + +/*tex + +We now get lots of helpers for definitions and printing. The storage model +that we use is different because we can hav emany more so we need to be +sparse. Therefore we use trees. + +*/ + + +#define print_hex_digit(A) do { \ + if ((A)>=10) print_char('A'+(A)-10); \ + else print_char('0'+(A)); \ + } while (0) + +#define two_hex(A) do { \ + print_hex_digit((A)/16); \ + print_hex_digit((A)%16); \ + } while (0) + +#define four_hex(A) do { \ + two_hex((A)/256); \ + two_hex((A)%256); \ + } while (0) + +#define six_hex(A) do { \ + two_hex((A)/65536); \ + two_hex(((A)%65536)/256); \ + two_hex((A)%256); \ + } while (0) + +mathcodeval mathchar_from_integer(int value, int extcode) +{ + mathcodeval mval; + if (extcode == tex_mathcode) { + mval.class_value = (value / 0x1000); + mval.family_value = ((value % 0x1000) / 0x100); + mval.character_value = (value % 0x100); + } else { + int mfam = (value / 0x200000) & 0x7FF; + mval.class_value = mfam % 0x08; + mval.family_value = mfam / 0x08; + mval.character_value = value & 0x1FFFFF; + } + return mval; +} + +void show_mathcode_value_old(int value) +{ + print_char('"'); + four_hex(value); +} + +void show_mathcode_value(mathcodeval c) +{ + print_char('"'); + print_hex_digit(c.class_value); + print_char('"'); + two_hex(c.family_value); + print_char('"'); + six_hex(c.character_value); +} + +static void show_mathcode(int n) +{ + mathcodeval c = get_math_code(n); + tprint_esc("Umathcode"); + print_int(n); + print_char('='); + show_mathcode_value(c); +} + +static void unsavemathcode(quarterword gl) +{ + sa_stack_item st; + if (mathcode_head->stack == NULL) + return; + while (mathcode_head->stack_ptr > 0 && abs(mathcode_head->stack[mathcode_head->stack_ptr].level) >= gl) { + st = mathcode_head->stack[mathcode_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(mathcode_head, st.code, st.value); + if (tracing_restores_par > 1) { + begin_diagnostic(); + print_char('{'); + tprint("restoring"); + print_char(' '); + show_mathcode(st.code); + print_char('}'); + end_diagnostic(false); + } + } + (mathcode_head->stack_ptr)--; + } +} + +void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword level) +{ + sa_tree_item v; + if (mathclass == 8 && mathfamily == 0 && mathcharacter == 0) { + v.uint_value = MATHCODEACTIVE; + } else { + v.math_code_value.class_value = mathclass; + v.math_code_value.family_value = mathfamily; + v.math_code_value.character_value = mathcharacter; + } + set_sa_item(mathcode_head, n, v, level); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + print_char('{'); + tprint("assigning"); + print_char(' '); + show_mathcode(n); + print_char('}'); + end_diagnostic(false); + } +} + +mathcodeval get_math_code(int n) +{ + mathcodeval d; + sa_tree_item v = get_sa_item(mathcode_head, n); + if (v.uint_value == MATHCODEDEFAULT) { + d.class_value = 0; + d.family_value = 0; + d.character_value = n; + } else if (v.uint_value == MATHCODEACTIVE) { + d.class_value = 8; + d.family_value = 0; + d.character_value = 0; + } else { + d.class_value = v.math_code_value.class_value; + if (d.class_value == 8) { + d.family_value = 0; + d.character_value = n; + } else { + d.family_value = v.math_code_value.family_value; + d.character_value = v.math_code_value.character_value; + } + } + return d; +} + +int get_math_code_num(int n) +{ + mathcodeval d = get_math_code(n); + return (d.class_value + (d.family_value * 8)) * (65536 * 32) + d.character_value; +} + +static void initializemathcode(void) +{ + sa_tree_item sa_value = { 0 }; + sa_value.uint_value = MATHCODEDEFAULT; + mathcode_head = new_sa_tree(MATHCODESTACK, 1, sa_value); +} + +static void dumpmathcode(void) +{ + dump_sa_tree(mathcode_head,"mathcodes"); +} + +static void undumpmathcode(void) +{ + mathcode_head = undump_sa_tree("mathcodes"); +} + +static void show_delcode(int n) +{ + delcodeval c; + c = get_del_code(n); + tprint_esc("Udelcode"); + print_int(n); + print_char('='); + if (c.small_family_value < 0) { + print_char('-'); + print_char('1'); + } else { + print_char('"'); + two_hex(c.small_family_value); + six_hex(c.small_character_value); + } +} + +static void unsavedelcode(quarterword gl) +{ + sa_stack_item st; + if (delcode_head->stack == NULL) + return; + while (delcode_head->stack_ptr > 0 && abs(delcode_head->stack[delcode_head->stack_ptr].level) >= gl) { + st = delcode_head->stack[delcode_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(delcode_head, st.code, st.value); + if (tracing_restores_par > 1) { + begin_diagnostic(); + print_char('{'); + tprint("restoring"); + print_char(' '); + show_delcode(st.code); + print_char('}'); + end_diagnostic(false); + } + } + (delcode_head->stack_ptr)--; + } +} + +void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl) +{ + sa_tree_item v; + v.del_code_value.class_value = 0; + v.del_code_value.small_family_value = smathfamily; + v.del_code_value.small_character_value = smathcharacter; + v.del_code_value.dummy_value = 0; + v.del_code_value.large_family_value = lmathfamily; + v.del_code_value.large_character_value = lmathcharacter; + /*tex Always global! */ + set_sa_item(delcode_head, n, v, gl); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + print_char('{'); + tprint("assigning"); + print_char(' '); + show_delcode(n); + print_char('}'); + end_diagnostic(false); + } +} + +delcodeval get_del_code(int n) +{ + delcodeval d; + sa_tree_item v = get_sa_item(delcode_head, n); + if (v.uint_value == DELCODEDEFAULT) { + d.class_value = 0; + d.small_family_value = -1; + d.small_character_value = 0; + d.large_family_value = 0; + d.large_character_value = 0; + } else { + d.class_value = v.del_code_value.class_value; + d.small_family_value = v.del_code_value.small_family_value; + d.small_character_value = v.del_code_value.small_character_value; + d.large_family_value = v.del_code_value.large_family_value; + d.large_character_value = v.del_code_value.large_character_value; + } + return d; +} + +/*tex + +This really only works for old-style delcodes! + +*/ + +int get_del_code_num(int n) +{ + delcodeval d = get_del_code(n); + if (d.small_family_value < 0) { + return -1; + } else { + return ((d.small_family_value * 256 + d.small_character_value) * 4096 + + (d.large_family_value * 256) + d.large_character_value); + } +} + +static void initializedelcode(void) +{ + sa_tree_item sa_value = { 0 }; + sa_value.uint_value = DELCODEDEFAULT; + delcode_head = new_sa_tree(DELCODESTACK, 2, sa_value); +} + +static void dumpdelcode(void) +{ + dump_sa_tree(delcode_head,"delcodes"); +} + +static void undumpdelcode(void) +{ + delcode_head = undump_sa_tree("delcodes"); +} + +void unsave_math_codes(quarterword grouplevel) +{ + unsavemathcode(grouplevel); + unsavedelcode(grouplevel); +} + +void initialize_math_codes(void) +{ + initializemathcode(); + initializedelcode(); +} + +void free_math_codes(void) +{ + destroy_sa_tree(mathcode_head); + destroy_sa_tree(delcode_head); +} + +void dump_math_codes(void) +{ + dumpmathcode(); + dumpdelcode(); +} + +void undump_math_codes(void) +{ + undumpmathcode(); + undumpdelcode(); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/mathcodes.h b/Build/source/texk/web2c/luatexdir/tex/mathcodes.h new file mode 100644 index 00000000000..fe731ff2be5 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mathcodes.h @@ -0,0 +1,67 @@ +/* mathcodes.h + + Copyright 2009-2012 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef MATHCODES_H +# define MATHCODES_H + +/* this is a flag for |scan_delimiter| */ + +# define no_mathcode 0 +# define tex_mathcode 8 +# define umath_mathcode 21 +# define umathnum_mathcode 22 + +typedef struct mathcodeval { + int class_value; + int family_value; + int character_value; +} mathcodeval; + +void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword gl); + +mathcodeval get_math_code(int n); +int get_math_code_num(int n); +int get_del_code_num(int n); +mathcodeval scan_mathchar(int extcode); +mathcodeval scan_delimiter_as_mathchar(int extcode); + +mathcodeval mathchar_from_integer(int value, int extcode); +void show_mathcode_value(mathcodeval d); +void show_mathcode_value_old(int value); + +typedef struct delcodeval { + int class_value; + int small_family_value; + int small_character_value; + int large_family_value; + int large_character_value; +} delcodeval; + +void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl); + +delcodeval get_del_code(int n); + +void unsave_math_codes(quarterword grouplevel); +void initialize_math_codes(void); +void dump_math_codes(void); +void undump_math_codes(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/memoryword.c b/Build/source/texk/web2c/luatexdir/tex/memoryword.c new file mode 100644 index 00000000000..7bf758dd0d4 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/memoryword.c @@ -0,0 +1,29 @@ +/* +memoryword.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +The debug code is no longer present! + +*/ diff --git a/Build/source/texk/web2c/luatexdir/tex/memoryword.h b/Build/source/texk/web2c/luatexdir/tex/memoryword.h new file mode 100644 index 00000000000..5db2bb8cb36 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/memoryword.h @@ -0,0 +1,128 @@ +/* memoryword.h + + Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +/* This header file is extra special because it is read in from + within the pascal source */ + +#ifndef MEMORYWORD_H +# define MEMORYWORD_H + +/* texmfmem.h: the memory_word type, which is too hard to translate + automatically from Pascal. We have to make sure the byte-swapping + that the (un)dumping routines do suffices to put things in the right + place in memory. + + A memory_word can be broken up into a `twohalves' or a + `fourquarters', and a `twohalves' can be further broken up. Here is + a picture. ..._M = most significant byte, ..._L = least significant + byte. + + + BigEndian: + twohalves.v: RH_MM RH_ML RH_LM RH_LL LH_MM LH_ML LH_LM LH_LL + twohalves.u: ---------JUNK---------- ----B0----- ----B1----- + fourquarters: ----B0----- ----B1----- ----B2----- ----B3----- + twoints: ---------CINT0--------- ---------CINT1--------- + + LittleEndian: + twohalves.v: LH_LL LH_LM LH_ML LH_MM RH_LL RH_LM RH_ML RH_MM + twohalves.u: ----B1----- ----B0----- + fourquarters: ----B3----- ----B2----- ----B1----- ----B0----- + twoints: ---------CINT1--------- ---------CINT0--------- + +*/ + + +typedef union { + struct { +# ifdef WORDS_BIGENDIAN + halfword RH, LH; +# else + halfword LH, RH; +# endif + } v; + + struct { /* Make B0,B1 overlap the most significant bytes of LH. */ +# ifdef WORDS_BIGENDIAN + halfword junk; + quarterword B0, B1; +# else /* not WORDS_BIGENDIAN */ + /* If 32-bit memory words, have to do something. */ + quarterword B1, B0; +# endif /* LittleEndian */ + } u; +} two_halves; + +typedef struct { + struct { +# ifdef WORDS_BIGENDIAN + quarterword B0, B1, B2, B3; +# else + quarterword B3, B2, B1, B0; +# endif + } u; +} four_quarters; + +typedef struct { +# ifdef WORDS_BIGENDIAN + int CINT0, CINT1; +# else + int CINT1, CINT0; +# endif +} two_ints; + +typedef struct { + glue_ratio GLUE; +} glues; + +typedef union { + two_halves hh; + four_quarters qqqq; + two_ints ii; + glues gg; +} memory_word; + +# define b0 u.B0 +# define b1 u.B1 +# define b2 u.B2 +# define b3 u.B3 + +# define rh v.RH +# define lhfield v.LH + +# define cint ii.CINT0 +# define cint1 ii.CINT1 + +# define gr gg.GLUE + +/* the next five defines are needed for the prototypes in web2c's coerce.h */ + +# define memoryword memory_word +# define strnumber str_number +# define packedASCIIcode packed_ASCII_code +# define poolpointer pool_pointer + +typedef FILE *word_file; + +# ifdef DEBUG +extern void print_word(memory_word w); +# endif + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.c b/Build/source/texk/web2c/luatexdir/tex/mlist.c new file mode 100644 index 00000000000..2ac631ea339 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mlist.c @@ -0,0 +1,4830 @@ +/* + +Copyright 2006-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +/*tex + + In traditional \TeX\ the italic correction is added to the width of the + glyph. This is part of the engine design and related font design. In opentype + math this is different. There the italic correction had more explicit usage. + The 1.7 spec says: + + \startitemize + + \startitem + {\em italic correction:} When a run of slanted characters is followed by + a straight character (such as an operator or a delimiter), the italics + correction of the last glyph is added to its advance width. + + When positioning limits on an N-ary operator (e.g., integral sign), the + horizontal position of the upper limit is moved to the right by ½ of the + italics correction, while the position of the lower limit is moved to the + left by the same distance. + + When positioning superscripts and subscripts, their default horizontal + positions are also different by the amount of the italics correction of + the preceding glyph. + \stopitem + + \startitem + {\em math kerning:} Set the default horizontal position for the + superscript as shifted relative to the position of the subscript by the + italics correction of the base glyph. + \stopitem + + \stopitemize + + Before this was specified we had to gamble a bit and assume that cambria was + the font benchmark and trust our eyes (and msword) for the logic. I must + admit that I have been fighting these italics in fonts (and the heuristics + that Lua\TeX\ provided) right from the start (e.g. using Lua based + postprocessing) but by now we know more and have more fonts to test with. + More fonts are handy because not all fonts are alike when it comes to + italics. Axis are another area of concern, as it looks like opentype math + fonts often already apply that shift. + +*/ + +#define is_new_mathfont(A) ((font_math_params(A) >0) && (math_old_par == 0)) +#define is_old_mathfont(A,B) ((font_math_params(A)==0) && (font_params(A)>=(B))) +#define do_new_math(A) ((font_math_params(A) >0) && (font_oldmath(A) == 0) && (math_old_par == 0)) +#define protect_glyph(A) subtype(A)=256 + +#include "ptexlib.h" +#include "lua/luatex-api.h" + +#define reset_attributes(p,newatt) do { \ + delete_attribute_ref(node_attr(p)); \ + node_attr(p) = newatt; \ + if (newatt!=null) { \ + add_node_attr_ref(node_attr(p)); \ + } \ + } while (0) + +#define DEFINE_MATH_PARAMETERS(A,B,C,D) do { \ + if (B==text_size) { \ + def_math_param(A, text_style, (C),D); \ + def_math_param(A, cramped_text_style, (C),D); \ + } else if (B==script_size) { \ + def_math_param(A, script_style, (C),D); \ + def_math_param(A, cramped_script_style, (C),D); \ + } else if (B==script_script_size) { \ + def_math_param(A, script_script_style, (C),D); \ + def_math_param(A, cramped_script_script_style, (C),D); \ + } \ + } while (0) + +#define DEFINE_DMATH_PARAMETERS(A,B,C,D) do { \ + if (B==text_size) { \ + def_math_param(A, display_style,(C),D); \ + def_math_param(A, cramped_display_style,(C),D); \ + } \ + } while (0) + +#define font_MATH_par(a,b) \ + (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter) + +/*tex + + Here are the math parameters that are font-dependant. + + Before an mlist is converted to an hlist, \TeX\ makes sure that the fonts in + family~2 have enough parameters to be math-symbol fonts, and that the fonts + in family~3 have enough parameters to be math-extension fonts. The + math-symbol parameters are referred to by using the following macros, which + take a size code as their parameter; for example, |num1(cur_size)| gives the + value of the |num1| parameter for the current size. + +*/ + +#define total_mathsy_params 22 +#define total_mathex_params 13 + +#define mathsy(A,B) font_param(fam_fnt(2,A),B) + +/*tex height of `\.x' */ + +#define math_x_height(A) mathsy(A,5) + +/*tex \.{18mu} */ + +#define math_quad(A) mathsy(A,6) + +/*tex numerator shift-up in display styles */ + +#define num1(A) mathsy(A,8) + +/*tex numerator shift-up in non-display, non-\.{\\atop} */ + +#define num2(A) mathsy(A,9) + +/*tex numerator shift-up in non-display \.{\\atop} */ + +#define num3(A) mathsy(A,10) + +/*tex denominator shift-down in display styles */ + +#define denom1(A) mathsy(A,11) + +/*tex denominator shift-down in non-display styles */ + +#define denom2(A) mathsy(A,12) + +/*tex superscript shift-up in uncramped display style */ + +#define sup1(A) mathsy(A,13) + +/*tex superscript shift-up in uncramped non-display */ + +#define sup2(A) mathsy(A,14) + +/*tex superscript shift-up in cramped styles */ + +#define sup3(A) mathsy(A,15) + +/*tex subscript shift-down if superscript is absent */ + +#define sub1(A) mathsy(A,16) + +/*tex subscript shift-down if superscript is present */ + +#define sub2(A) mathsy(A,17) + +/*tex superscript baseline below top of large box */ + +#define sup_drop(A) mathsy(A,18) + +/*tex subscript baseline below bottom of large box */ + +#define sub_drop(A) mathsy(A,19) + +/*tex size of \.{\\atopwithdelims} delimiters in display styles */ + +#define delim1(A) mathsy(A,20) + +/*tex size of \.{\\atopwithdelims} delimiters in non-displays */ + +#define delim2(A) mathsy(A,21) + +/*tex height of fraction lines above the baseline */ + +#define axis_height(A) mathsy(A,22) + +/*tex + + The math-extension parameters have similar macros, but the size code is + omitted (since it is always |cur_size| when we refer to such parameters). + +*/ + +#define mathex(A,B) font_param(fam_fnt(3,A),B) + +/*tex thickness of \.{\\over} bars */ + +#define default_rule_thickness(A) mathex(A,8) + +/*tex minimum clearance above a displayed op */ + +#define big_op_spacing1(A) mathex(A,9) + +/*tex minimum clearance below a displayed op */ + +#define big_op_spacing2(A) mathex(A,10) + +/*tex minimum baselineskip above displayed op */ + +#define big_op_spacing3(A) mathex(A,11) + +/*tex minimum baselineskip below displayed op */ + +#define big_op_spacing4(A) mathex(A,12) + +/*tex padding above and below displayed limits */ + +#define big_op_spacing5(A) mathex(A,13) + +/*tex + + \LUATEX makes a bunch of extensions cf. the |MATH| table in \OPENTYPE, but + some of the |MathConstants| values have no matching usage in \LUATEX\ right + now. + + \startitemize + + \startitem + |ScriptPercentScaleDown| |ScriptScriptPercentScaleDown|: These should + be handled by the macro package, on the engine side there are three + separate fonts. + \stopitem + + \startitem + |DelimitedSubFormulaMinHeight|: This is perhaps related to word's + natural math input? We have no idea what to do about it. + \stopitem + + \startitem + |MathLeading|: \LUATEX does not currently handle multi-line displays, + and the parameter does not seem to make much sense elsewhere. + \stopitem + + \startitem + |FlattenedAccentBaseHeight|: This is based on the |flac| |GSUB| + feature. It would not be hard to support that, but proper math accent + placements cf.\ |MATH| needs support for |MathTopAccentAttachment| + table to be implemented first. + \stopitem + + \stopitemize + +*/ + +static void math_param_error(const char *param, int style) +{ + char s[256]; + const char *hlp[] = { + "Sorry, but I can't typeset math unless various parameters have", + "been set. This is normally done by loading special math fonts", + "into the math family slots. Your font set is lacking at least", + "the parameter mentioned earlier.", + NULL + }; + snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", param, math_style_names[style]); + tex_error(s, hlp); + return; +} + +static scaled accent_base_height(int f) +{ + scaled a; + if (do_new_math(f)) { + a = font_MATH_par(f, AccentBaseHeight); + if (a == undefined_math_parameter) + a = x_height(f); + } else { + a = x_height(f); + } + return a; +} + +/*tex + + The non-staticness of this function is for the benefit of |texmath.w|. + Watch out, this one uses the style! The style and size numbers don't + match because we have cramped styles. + +*/ + +scaled get_math_quad_style(int var) +{ + scaled a = get_math_param(math_param_quad, var); + if (a == undefined_math_parameter) { + math_param_error("quad", var); + return 0; + } else { + return a; + } +} + +/*tex + + For this reason the next one is different because it is called with a size + specifier instead of a style specifier. + +*/ + +static scaled math_axis_size(int b) +{ + scaled a; + int var; + if (b == script_size) + var = script_style; + else if (b == script_script_size) + var = script_script_style; + else + var = text_style; + a = get_math_param(math_param_axis, var); + if (a == undefined_math_parameter) { + math_param_error("axis", var); + return 0; + } else { + return a; + } +} + +scaled get_math_quad_size(int b) +{ + int var; + if (b == script_size) + var = script_style; + else if (b == script_script_size) + var = script_script_style; + else + var = text_style; + return get_math_param(math_param_quad, var); +} + +static scaled minimum_operator_size(int var) +{ + scaled a = get_math_param(math_param_operator_size, var); + return a; +} + +/*tex + + Old-style fonts do not define the |radical_rule|. This allows |make_radical| + to select the backward compatibility code, and it means that we can't raise + an error here. + +*/ + +static scaled radical_rule_par(int var) +{ + scaled a = get_math_param(math_param_radical_rule, var); + return a; +} + +/*tex + + Now follow all the trivial math parameters. + +*/ + +#define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b) +#define get_math_param_or_zero(a,b) do_get_math_param_or_zero(a, math_param_##b, #b) + +static scaled do_get_math_param_or_error(int var, int param, const char *name) +{ + scaled a = get_math_param(param, var); + if (a == undefined_math_parameter) { + math_param_error(name, var); + a = 0; + } + return a; +} + +static scaled do_get_math_param_or_zero(int var, int param, const char *name) +{ + scaled a = get_math_param(param, var); + if (a == undefined_math_parameter) { + a = 0; + } + return a; +} + +/*tex + + A variant on a suggestion on the list based on analysis by UV. + +*/ + +static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { + scaled delta, delta1, delta2; + if (axis) { + delta2 = max_d + math_axis_size(cur_size); + } else { + delta2 = max_d; + } + delta1 = max_h + max_d - delta2; + if (delta2 > delta1) { + /*tex |delta1| is max distance from axis */ + delta1 = delta2; + } + delta = (delta1 / 500) * delimiter_factor_par; + delta2 = delta1 + delta1 - delimiter_shortfall_par; + if (delta < delta2) { + return delta2; + } else { + return delta; + } +} + +#define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before) +#define radical_degree_after(a) get_math_param_or_error(a, radical_degree_after) +#define radical_degree_raise(a) get_math_param_or_error(a, radical_degree_raise) + +#define connector_overlap_min(a) get_math_param_or_error(a, connector_overlap_min) + +#define overbar_rule(a) get_math_param_or_error(a, overbar_rule) +#define overbar_kern(a) get_math_param_or_error(a, overbar_kern) +#define overbar_vgap(a) get_math_param_or_error(a, overbar_vgap) + +#define underbar_rule(a) get_math_param_or_error(a, underbar_rule) +#define underbar_kern(a) get_math_param_or_error(a, underbar_kern) +#define underbar_vgap(a) get_math_param_or_error(a, underbar_vgap) + +#define under_delimiter_vgap(a) get_math_param_or_error(a, under_delimiter_vgap) +#define under_delimiter_bgap(a) get_math_param_or_error(a, under_delimiter_bgap) + +#define over_delimiter_vgap(a) get_math_param_or_error(a, over_delimiter_vgap) +#define over_delimiter_bgap(a) get_math_param_or_error(a, over_delimiter_bgap) + +#define radical_vgap(a) get_math_param_or_error(a, radical_vgap) +#define radical_kern(a) get_math_param_or_error(a, radical_kern) + +#define stack_vgap(a) get_math_param_or_error(a, stack_vgap) +#define stack_num_up(a) get_math_param_or_error(a, stack_num_up) +#define stack_denom_down(a) get_math_param_or_error(a, stack_denom_down) + +#define fraction_rule(a) get_math_param_or_error(a, fraction_rule) +#define fraction_num_vgap(a) get_math_param_or_error(a, fraction_num_vgap) +#define fraction_denom_vgap(a) get_math_param_or_error(a, fraction_denom_vgap) +#define fraction_num_up(a) get_math_param_or_error(a, fraction_num_up) +#define fraction_denom_down(a) get_math_param_or_error(a, fraction_denom_down) +#define fraction_del_size_new(a) get_math_param_or_error(a, fraction_del_size) +/* fraction_del_size_old(a) get_math_param (a, math_param_fraction_del_size) */ +#define fraction_del_size_old(a) get_math_param_or_error(a, fraction_del_size) + +#define skewed_fraction_hgap(a) get_math_param_or_error(a, skewed_fraction_hgap) +#define skewed_fraction_vgap(a) get_math_param_or_error(a, skewed_fraction_vgap) + +#define limit_above_vgap(a) get_math_param_or_error(a, limit_above_vgap) +#define limit_above_bgap(a) get_math_param_or_error(a, limit_above_bgap) +#define limit_above_kern(a) get_math_param_or_error(a, limit_above_kern) + +#define limit_below_vgap(a) get_math_param_or_error(a, limit_below_vgap) +#define limit_below_bgap(a) get_math_param_or_error(a, limit_below_bgap) +#define limit_below_kern(a) get_math_param_or_error(a, limit_below_kern) + +#define nolimit_sub_factor(a) get_math_param_or_zero(a, nolimit_sub_factor) +#define nolimit_sup_factor(a) get_math_param_or_zero(a, nolimit_sup_factor) + +#define sub_shift_drop(a) get_math_param_or_error(a, sub_shift_drop) +#define sup_shift_drop(a) get_math_param_or_error(a, sup_shift_drop) +#define sub_shift_down(a) get_math_param_or_error(a, sub_shift_down) +#define sub_sup_shift_down(a) get_math_param_or_error(a, sub_sup_shift_down) +#define sup_shift_up(a) get_math_param_or_error(a, sup_shift_up) +#define sub_top_max(a) get_math_param_or_error(a, sub_top_max) +#define sup_bottom_min(a) get_math_param_or_error(a, sup_bottom_min) +#define sup_sub_bottom_max(a) get_math_param_or_error(a, sup_sub_bottom_max) +#define subsup_vgap(a) get_math_param_or_error(a, subsup_vgap) + +#define space_after_script(a) get_math_param_or_error(a, space_after_script) + +void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) +{ + if (is_new_mathfont(f)) { + + /*tex Fix all known parameters. */ + + DEFINE_MATH_PARAMETERS(math_param_quad, size_id, + font_size(f), lvl); + DEFINE_DMATH_PARAMETERS(math_param_quad, size_id, + font_size(f), lvl); + DEFINE_MATH_PARAMETERS(math_param_axis, size_id, + font_MATH_par(f, AxisHeight), lvl); + DEFINE_DMATH_PARAMETERS(math_param_axis, size_id, + font_MATH_par(f, AxisHeight), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id, + font_MATH_par(f, OverbarExtraAscender), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id, + font_MATH_par(f, OverbarExtraAscender), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id, + font_MATH_par(f, OverbarRuleThickness), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id, + font_MATH_par(f, OverbarRuleThickness), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id, + font_MATH_par(f, OverbarVerticalGap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id, + font_MATH_par(f, OverbarVerticalGap), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id, + font_MATH_par(f, UnderbarExtraDescender), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id, + font_MATH_par(f, UnderbarExtraDescender), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id, + font_MATH_par(f, UnderbarRuleThickness), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id, + font_MATH_par(f, UnderbarRuleThickness), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id, + font_MATH_par(f, UnderbarVerticalGap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id, + font_MATH_par(f, UnderbarVerticalGap), lvl); + + DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id, + font_MATH_par(f, StretchStackGapAboveMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id, + font_MATH_par(f, StretchStackGapAboveMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id, + font_MATH_par(f, StretchStackBottomShiftDown), lvl); + DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id, + font_MATH_par(f, StretchStackBottomShiftDown), lvl); + + DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id, + font_MATH_par(f, StretchStackGapBelowMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id, + font_MATH_par(f, StretchStackGapBelowMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, + font_MATH_par(f, StretchStackTopShiftUp), lvl); + DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, + font_MATH_par(f, StretchStackTopShiftUp), lvl); + + DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id, + font_MATH_par(f, StackTopShiftUp), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id, + font_MATH_par(f, StackTopDisplayStyleShiftUp), lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id, + font_MATH_par(f, StackBottomShiftDown), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id, + font_MATH_par(f, StackBottomDisplayStyleShiftDown), lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id, + font_MATH_par(f, StackGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id, + font_MATH_par(f, StackDisplayStyleGapMin), lvl); + + DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id, + font_MATH_par(f, RadicalExtraAscender), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id, + font_MATH_par(f, RadicalExtraAscender), lvl); + + DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id, + font_MATH_par(f, DisplayOperatorMinHeight), lvl); + + DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id, + font_MATH_par(f, RadicalRuleThickness), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id, + font_MATH_par(f, RadicalRuleThickness), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id, + font_MATH_par(f, RadicalVerticalGap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id, + font_MATH_par(f, RadicalDisplayStyleVerticalGap), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id, + font_MATH_par(f, RadicalKernBeforeDegree), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id, + font_MATH_par(f, RadicalKernBeforeDegree), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id, + font_MATH_par(f, RadicalKernAfterDegree), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id, + font_MATH_par(f, RadicalKernAfterDegree), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id, + font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id, + font_MATH_par(f, RadicalDegreeBottomRaisePercent), lvl); + + if (size_id == text_size) { + def_math_param(math_param_sup_shift_up, display_style, + font_MATH_par(f, SuperscriptShiftUp), lvl); + def_math_param(math_param_sup_shift_up, cramped_display_style, + font_MATH_par(f, SuperscriptShiftUpCramped), lvl); + def_math_param(math_param_sup_shift_up, text_style, + font_MATH_par(f, SuperscriptShiftUp), lvl); + def_math_param(math_param_sup_shift_up, cramped_text_style, + font_MATH_par(f, SuperscriptShiftUpCramped), lvl); + } else if (size_id == script_size) { + def_math_param(math_param_sup_shift_up, script_style, + font_MATH_par(f, SuperscriptShiftUp), lvl); + def_math_param(math_param_sup_shift_up, cramped_script_style, + font_MATH_par(f, SuperscriptShiftUpCramped), lvl); + } else if (size_id == script_script_size) { + def_math_param(math_param_sup_shift_up, script_script_style, + font_MATH_par(f, SuperscriptShiftUp), lvl); + def_math_param(math_param_sup_shift_up, cramped_script_script_style, + font_MATH_par(f, SuperscriptShiftUpCramped), lvl); + } + + DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id, + font_MATH_par(f, SubscriptBaselineDropMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id, + font_MATH_par(f, SubscriptBaselineDropMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id, + font_MATH_par(f, SuperscriptBaselineDropMax), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id, + font_MATH_par(f, SuperscriptBaselineDropMax), lvl); + DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDown), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDown), lvl); + + if (font_MATH_par(f, SubscriptShiftDownWithSuperscript) != undefined_math_parameter) { + DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDownWithSuperscript), lvl); + } else { + DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDown), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDown), lvl); + } + + DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id, + font_MATH_par(f, SubscriptTopMax), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id, + font_MATH_par(f, SubscriptTopMax), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id, + font_MATH_par(f, SuperscriptBottomMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id, + font_MATH_par(f, SuperscriptBottomMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, + font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, + font_MATH_par(f, SuperscriptBottomMaxWithSubscript), lvl); + DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id, + font_MATH_par(f, SubSuperscriptGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id, + font_MATH_par(f, SubSuperscriptGapMin), lvl); + + DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id, + font_MATH_par(f, UpperLimitGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id, + font_MATH_par(f, UpperLimitGapMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id, + font_MATH_par(f, UpperLimitBaselineRiseMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id, + font_MATH_par(f, UpperLimitBaselineRiseMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id, + 0, lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id, + 0, lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id, + font_MATH_par(f, LowerLimitGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id, + font_MATH_par(f, LowerLimitGapMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id, + font_MATH_par(f, LowerLimitBaselineDropMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id, + font_MATH_par(f, LowerLimitBaselineDropMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id, + 0, lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id, + 0, lvl); + DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id, + font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */ + DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id, + font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */ + DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id, + font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */ + DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id, + font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */ + + DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id, + font_MATH_par(f, FractionRuleThickness), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id, + font_MATH_par(f, FractionRuleThickness), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id, + font_MATH_par(f, FractionNumeratorGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id, + font_MATH_par(f, FractionNumeratorDisplayStyleGapMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id, + font_MATH_par(f, FractionNumeratorShiftUp), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id, + font_MATH_par(f, FractionNumeratorDisplayStyleShiftUp), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id, + font_MATH_par(f, FractionDenominatorGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id, + font_MATH_par(f,FractionDenominatorDisplayStyleGapMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id, + font_MATH_par(f, FractionDenominatorShiftDown), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id, + font_MATH_par(f, FractionDenominatorDisplayStyleShiftDown), lvl); + + DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id, + font_MATH_par(f, FractionDelimiterSize), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id, + font_MATH_par(f, FractionDelimiterDisplayStyleSize), lvl); + + DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id, + font_MATH_par(f, SkewedFractionHorizontalGap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id, + font_MATH_par(f, SkewedFractionHorizontalGap), lvl); + DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id, + font_MATH_par(f, SkewedFractionVerticalGap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id, + font_MATH_par(f, SkewedFractionVerticalGap), lvl); + + DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id, + font_MATH_par(f, SpaceAfterScript), lvl); + DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id, + font_MATH_par(f, SpaceAfterScript), lvl); + + DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id, + font_MATH_par(f, MinConnectorOverlap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id, + font_MATH_par(f, MinConnectorOverlap), lvl); + + } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) { + + /*tex Fix old-style |sy| parameters. */ + + DEFINE_MATH_PARAMETERS(math_param_quad, size_id, + math_quad(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_quad, size_id, + math_quad(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_axis, size_id, + axis_height(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_axis, size_id, + axis_height(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id, + num3(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id, + num1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id, + denom2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id, + denom1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id, + num2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id, + num1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id, + denom2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id, + denom1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id, + delim2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id, + delim1(size_id), lvl); + + DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id, + 0, lvl); + DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_hgap, size_id, + 0, lvl); + DEFINE_MATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id, + 0, lvl); + DEFINE_DMATH_PARAMETERS(math_param_skewed_fraction_vgap, size_id, + 0, lvl); + + if (size_id == text_size) { + def_math_param(math_param_sup_shift_up, display_style, + sup1(size_id), lvl); + def_math_param(math_param_sup_shift_up, cramped_display_style, + sup3(size_id), lvl); + def_math_param(math_param_sup_shift_up, text_style, + sup2(size_id), lvl); + def_math_param(math_param_sup_shift_up, cramped_text_style, + sup3(size_id), lvl); + } else if (size_id == script_size) { + def_math_param(math_param_sub_shift_drop, display_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, cramped_display_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, text_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, cramped_text_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, display_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, cramped_display_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, text_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, cramped_text_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_up, script_style, + sup2(size_id), lvl); + def_math_param(math_param_sup_shift_up, cramped_script_style, + sup3(size_id), lvl); + } else if (size_id == script_script_size) { + def_math_param(math_param_sub_shift_drop, script_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, cramped_script_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, script_script_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, + cramped_script_script_style, sub_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, script_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, cramped_script_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, script_script_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, + cramped_script_script_style, sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_up, script_script_style, + sup2(size_id), lvl); + def_math_param(math_param_sup_shift_up, cramped_script_script_style, + sup3(size_id), lvl); + } + + DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id, + sub1(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id, + sub1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + sub2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + sub2(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id, + (abs(math_x_height(size_id) * 4) / 5), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id, + (abs(math_x_height(size_id) * 4) / 5), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id, + (abs(math_x_height(size_id)) / 4), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id, + (abs(math_x_height(size_id)) / 4), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, + (abs(math_x_height(size_id) * 4) / 5), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, + (abs(math_x_height(size_id) * 4) / 5), lvl); + + /*tex + + The display-size |radical_vgap| is done twice because it needs values + from both the sy and the ex font. + + */ + + DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id, + (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id, + 60, lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id, + 60, lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id, + xn_over_d(get_math_quad_size(size_id), 5, 18), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id, + xn_over_d(get_math_quad_size(size_id), 5, 18), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id, + (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id, + (-xn_over_d (get_math_quad_size(size_id), 10, 18)), lvl); + + } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) { + + /*tex Fix old-style |ex| parameters. */ + + DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id, + (default_rule_thickness(size_id) + (abs(default_rule_thickness(size_id)) / 4)), lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id, + 7 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id, + big_op_spacing1(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id, + big_op_spacing1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id, + big_op_spacing3(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id, + big_op_spacing3(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id, + big_op_spacing5(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id, + big_op_spacing5(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id, + big_op_spacing2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id, + big_op_spacing2(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id, + big_op_spacing4(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id, + big_op_spacing4(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id, + big_op_spacing5(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id, + big_op_spacing5(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_nolimit_sub_factor, size_id, + font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */ + DEFINE_DMATH_PARAMETERS(math_param_nolimit_sub_factor, size_id, + font_MATH_par(f, NoLimitSubFactor), lvl); /* bonus */ + DEFINE_MATH_PARAMETERS(math_param_nolimit_sup_factor, size_id, + font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */ + DEFINE_DMATH_PARAMETERS(math_param_nolimit_sup_factor, size_id, + font_MATH_par(f, NoLimitSupFactor), lvl); /* bonus */ + DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id, + 4 * default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id, + 4 * default_rule_thickness(size_id), lvl); + + /*tex + + All of the |space_after_script|s are done in + |finalize_math_parameters| because the \.{\\scriptspace} may have + been altered by the user. + + */ + + DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id, + 0, lvl); + DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id, + 0, lvl); + + DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id, + big_op_spacing2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id, + big_op_spacing2(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id, + big_op_spacing4(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id, + big_op_spacing4(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id, + big_op_spacing1(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id, + big_op_spacing1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, + big_op_spacing3(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, + big_op_spacing3(size_id), lvl); + + /*tex + + The display-size |radical_vgap| is done twice because it needs values + from both the sy and the ex font. + + */ + + DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id, + (default_rule_thickness(size_id) + (abs(math_x_height(size_id)) / 4)), lvl); + + } +} + +/*tex + + This needs to be called just at the start of |mlist_to_hlist|, for backward + compatibility with \.{\\scriptspace}. + +*/ + +static void finalize_math_parameters(void) +{ + int saved_trace = tracing_assigns_par; + tracing_assigns_par = 0; + if (get_math_param(math_param_space_after_script, display_style) == undefined_math_parameter) { + def_math_param(math_param_space_after_script, display_style, + script_space_par, level_one); + def_math_param(math_param_space_after_script, text_style, + script_space_par, level_one); + def_math_param(math_param_space_after_script, script_style, + script_space_par, level_one); + def_math_param(math_param_space_after_script, script_script_style, + script_space_par, level_one); + def_math_param(math_param_space_after_script, cramped_display_style, + script_space_par, level_one); + def_math_param(math_param_space_after_script, cramped_text_style, + script_space_par, level_one); + def_math_param(math_param_space_after_script, cramped_script_style, + script_space_par, level_one); + def_math_param(math_param_space_after_script, cramped_script_script_style, + script_space_par, level_one); + } + tracing_assigns_par = saved_trace; +} + +/*tex + + In order to convert mlists to hlists, i.e., noads to nodes, we need several + subroutines that are conveniently dealt with now. + + Let us first introduce the macros that make it easy to get at the parameters + and other font information. A size code, which is a multiple of 256, is added + to a family number to get an index into the table of internal font numbers + for each combination of family and size. (Be alert: Size codes get larger as + the type gets smaller.) + +*/ + +static const char *math_size_string(int s) +{ + if (s == text_size) + return "textfont"; + else if (s == script_size) + return "scriptfont"; + else + return "scriptscriptfont"; +} + +/*tex + + When the style changes, the following piece of program computes associated + information: + +*/ + +#define setup_cur_size(a) do { \ + if (a==script_style || a==cramped_script_style) \ + cur_size = script_size; \ + else if (a==script_script_style || a==cramped_script_script_style) \ + cur_size = script_script_size; \ + else \ + cur_size = text_size; \ +} while (0) + + +/*tex + + A simple routine that creates a flat copy of a nucleus. + +*/ + +static pointer math_clone(pointer q) +{ + pointer x; + if (q == null) + return null; + x = new_node(type(q), 0); + reset_attributes(x, node_attr(q)); + if (type(q) == math_char_node) { + math_fam(x) = math_fam(q); + math_character(x) = math_character(q); + } else { + math_list(x) = math_list(q); + } + return x; +} + +/*tex + + Here is a function that returns a pointer to a rule node having a given + thickness |t|. The rule will extend horizontally to the boundary of the vlist + that eventually contains it. + +*/ + +static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfword cur_size, halfword cur_fam) +{ + pointer p; + if (math_rules_mode_par) { + p = new_rule(some_rule); + rule_math_size(p) = cur_size; + rule_math_font(p) = fam_fnt(cur_fam, cur_size); + } else { + p = new_rule(normal_rule); + } + rule_dir(p) = math_direction_par; + height(p) = t; + depth(p) = 0; + reset_attributes(p, att); + return p; +} + +/*tex + + The |overbar| function returns a pointer to a vlist box that consists of a + given box |b|, above which has been placed a kern of height |k| under a + fraction rule of thickness |t| under additional space of height |ht|. + +*/ + +static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, halfword index, halfword cur_size, halfword cur_fam) +{ + pointer p, q; + p = new_kern(k); + reset_attributes(p, att); + couple_nodes(p,b); + q = do_fraction_rule(t, att, index, cur_size, cur_fam); + couple_nodes(q,p); + p = new_kern(ht); + reset_attributes(p, att); + couple_nodes(p,q); + q = vpackage(p, 0, additional, max_dimen, math_direction_par); + reset_attributes(q, att); + return q; +} + +/*tex + + Here is a subroutine that creates a new box, whose list contains a single + character, and whose width includes the italic correction for that character. + The height or depth of the box will be negative, if the height or depth of + the character is negative; thus, this routine may deliver a slightly + different result than |hpack| would produce. + +*/ + +static pointer char_box(internal_font_number f, int c, pointer bb) +{ + /*tex The new box and its character node. */ + pointer b, p; + b = new_null_box(); + if (do_new_math(f)) + width(b) = char_width(f, c); + else + width(b) = char_width(f, c) + char_italic(f, c); + height(b) = char_height(f, c); + depth(b) = char_depth(f, c); + subtype(b) = math_char_list ; + reset_attributes(b, bb); + p = new_glyph(f, c); + protect_glyph(p); + reset_attributes(p, bb); + list_ptr(b) = p; + return b; +} + +/*tex + + Another handy subroutine computes the height plus depth of a given character: + +*/ + +static scaled height_plus_depth(internal_font_number f, int c) +{ + return (char_height(f, c) + char_depth(f, c)); +} + +/*tex + + When we build an extensible character, it's handy to have the following + subroutine, which puts a given character on top of the characters already in + box |b|: + +*/ + +static scaled stack_into_box(pointer b, internal_font_number f, int c) +{ + /*tex New node placed into |b|: */ + pointer p, q; + /*tex Italic gets added to width. */ + p = char_box(f, c, node_attr(b)); + if (type(b) == vlist_node) { + try_couple_nodes(p,list_ptr(b)); + list_ptr(b) = p; + height(b) = height(p); + if (width(b) < width(p)) + width(b) = width(p); + return height_plus_depth(f, c); + } else { + q = list_ptr(b); + if (q == null) { + list_ptr(b) = p; + } else { + while (vlink(q) != null) + q = vlink(q); + couple_nodes(q,p); + } + if (height(b) < height(p)) + height(b) = height(p); + if (depth(b) < depth(p)) + depth(b) = depth(p); + return char_width(f, c); + } +} + +static void stack_glue_into_box(pointer b, scaled min, scaled max) { + halfword p = new_glue(zero_glue); + width(p) = min; + stretch(p) = max - min; + if (node_attr(b) != null) { + reset_attributes(p, node_attr(b)); + } + if (type(b) == vlist_node) { + try_couple_nodes(p,list_ptr(b)); + list_ptr(b) = p; + } else { + halfword q = list_ptr(b); + if (q == null) { + list_ptr(b) = p; + } else { + while (vlink(q) != null) { + q = vlink(q); + } + couple_nodes(q,p); + } + } +} + +/*tex + + \TeX's most important routine for dealing with formulas is called + |mlist_to_hlist|. After a formula has been scanned and represented as an + mlist, this routine converts it to an hlist that can be placed into a box or + incorporated into the text of a paragraph. The explicit parameter |cur_mlist| + points to the first node or noad in the given mlist (and it might be |null|); + the parameter |penalties| is |true| if penalty nodes for potential line + breaks are to be inserted into the resulting hlist, the parameter |cur_style| + is a style code. After |mlist_to_hlist| has acted, |vlink(temp_head)| points + to the translated hlist. + + Since mlists can be inside mlists, the procedure is recursive. And since this + is not part of \TeX's inner loop, the program has been written in a manner + that stresses compactness over efficiency. + +*/ + +/*tex Size code corresponding to |cur_style|: */ + +int cur_size = 0; + +static pointer get_delim_box(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att) +{ + int callback_id = callback_defined(make_extensible_callback); + if (callback_id > 0) { + /*tex + This call is not optimized as it hardly makes sense to use it ... special + and a it of feature creep too. + */ + halfword b = null; + run_callback(callback_id, "ddddbN->N",fnt,chr,v,min_overlap,horizontal,att,&b); + if (b == null) { + /*tex + We see this as a signal to do it the \TEX\ way. + */ + } else if (type(b) == hlist_node || type(b) == vlist_node) { + return b; + } else { + formatted_error("fonts","invalid extensible character %i created for font %i, [h|v]list expected",chr,fnt); + } + } + return make_extensible(fnt, chr, v, min_overlap, horizontal, att); +} + +pointer make_extensible(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att) +{ + /*tex new box */ + pointer b; + /*tex natural (maximum) size of the stack */ + scaled b_max; + /*tex amount of possible shrink in the stack */ + scaled s_max; + extinfo *cur; + extinfo *ext; + scaled prev_overlap; + /*tex a temporary counter number of extensible pieces */ + int i; + /*tex number of times to repeat each repeatable item in |ext| */ + int with_extenders; + int num_extenders, num_normal; + scaled a, c, d; + b = new_null_box(); + with_extenders = -1; + num_extenders = 0; + num_normal = 0; + if (min_overlap < 0) { + min_overlap = 0; + } + if (horizontal) { + type(b) = (quarterword) hlist_node; + ext = get_charinfo_hor_variants(char_info(fnt,chr)); + } else { + type(b) = (quarterword) vlist_node; + ext = get_charinfo_vert_variants(char_info(fnt,chr)); + } + if (att != null) { + reset_attributes(b,att); + } + cur = ext; + while (cur != NULL) { + if (!char_exists(fnt, cur->glyph)) { + const char *hlp[] = { + "Each glyph part in an extensible item should exist in the font.", + "I will give up trying to find a suitable size for now. Fix your font!", + NULL + }; + tex_error("Variant part doesn't exist.", hlp); + width(b) = null_delimiter_space_par; + return b; + } + if (cur->extender > 0) + num_extenders++; + else + num_normal++; + /*tex No negative overlaps or advances are allowed. */ + if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) { + const char *hlp[] = { + "All measurements in extensible items should be positive.", + "To get around this problem, I have changed the font metrics.", + "Fix your font!", + NULL + }; + tex_error("Extensible recipe has negative fields.", hlp); + if (cur->start_overlap < 0) + cur->start_overlap = 0; + if (cur->end_overlap < 0) + cur->end_overlap = 0; + if (cur->advance < 0) + cur->advance = 0; + } + cur = cur->next; + } + if (num_normal == 0) { + const char *hlp[] = { + "Each extensible recipe should have at least one non-repeatable part.", + "To get around this problem, I have changed the first part to be", + "non-repeatable. Fix your font!", + NULL + }; + tex_error("Extensible recipe has no fixed parts.", hlp); + ext->extender = 0; + num_normal = 1; + num_extenders--; + } + /*tex + + |ext| holds a linked list of numerous items that may or may not be + repeatable. For the total height, we have to figure out how many items + are needed to create a stack of at least |v|. + + The next |while| loop does that. It has two goals: it finds out the + natural height |b_max| of the all the parts needed to reach at least |v|, + and it sets |with_extenders| to the number of times each of the + repeatable items in |ext| has to be repeated to reach that height. + + */ + cur = ext; + b_max = 0; + while (b_max < v && num_extenders > 0) { + b_max = 0; + prev_overlap = 0; + with_extenders++; + for (cur = ext; cur != NULL; cur = cur->next) { + if (cur->extender == 0) { + c = cur->start_overlap; + if (min_overlap < c) + c = min_overlap; + if (prev_overlap < c) + c = prev_overlap; + a = cur->advance; + if (a == 0) { + /*tex for tfm fonts */ + if (horizontal) { + a = char_width(fnt, cur->glyph); + } else { + a = height_plus_depth(fnt, cur->glyph); + } + if (a < 0) { + formatted_error("fonts","bad extensible character %i in font %i",chr,fnt); + } + } + b_max += a - c; + prev_overlap = cur->end_overlap; + } else { + i = with_extenders; + while (i > 0) { + c = cur->start_overlap; + if (min_overlap < c) + c = min_overlap; + if (prev_overlap < c) + c = prev_overlap; + a = cur->advance; + if (a == 0) { + /*tex for tfm fonts */ + if (horizontal) { + a = char_width(fnt, cur->glyph); + } else { + a = height_plus_depth(fnt, cur->glyph); + } + if (a < 0) { + formatted_error("fonts","bad extensible character %i in font %i",chr,fnt); + } + } + b_max += a - c; + prev_overlap = cur->end_overlap; + i--; + } + } + } + } + /*tex + + Assemble box using |with_extenders| copies of each extender, with + appropriate glue wherever an overlap occurs. + + */ + prev_overlap = 0; + b_max = 0; + s_max = 0; + for (cur = ext; cur != NULL; cur = cur->next) { + if (cur->extender == 0) { + c = cur->start_overlap; + if (prev_overlap < c) + c = prev_overlap; + d = c; + if (min_overlap < c) + c = min_overlap; + if (d > 0) { + stack_glue_into_box(b, -d, -c); + s_max += (-c) - (-d); + b_max -= d; + } + b_max += stack_into_box(b, fnt, cur->glyph); + prev_overlap = cur->end_overlap; + i--; + } else { + i = with_extenders; + while (i > 0) { + c = cur->start_overlap; + if (prev_overlap < c) + c = prev_overlap; + d = c; + if (min_overlap < c) + c = min_overlap; + if (d > 0) { + stack_glue_into_box(b, -d, -c); + s_max += (-c) - (-d); + b_max -= d; + } + b_max += stack_into_box(b, fnt, cur->glyph); + prev_overlap = cur->end_overlap; + i--; + } + } + } + /*tex Set glue so as to stretch the connections if needed. */ + d = 0; + if (v > b_max && s_max > 0) { + d = v-b_max; + /*tex Don't stretch more than |s_max|. */ + if (d > s_max) + d = s_max; + glue_order(b) = normal; + glue_sign(b) = stretching; + glue_set(b) = unfloat(d/(float) s_max); + b_max += d; + } + if (horizontal) { + width(b) = b_max; + subtype(b) = math_h_extensible_list; + } else { + height(b) = b_max; + subtype(b) = math_v_extensible_list; + } + return b; +} + +/*tex + + The |var_delimiter| function, which finds or constructs a sufficiently large + delimiter, is the most interesting of the auxiliary functions that currently + concern us. Given a pointer |d| to a delimiter field in some noad, together + with a size code |s| and a vertical distance |v|, this function returns a + pointer to a box that contains the smallest variant of |d| whose height plus + depth is |v| or more. (And if no variant is large enough, it returns the + largest available variant.) In particular, this routine will construct + arbitrarily large delimiters from extensible components, if |d| leads to such + characters. + + The value returned is a box whose |shift_amount| has been set so that the box + is vertically centered with respect to the axis in the given size. If a + built-up symbol is returned, the height of the box before shifting will be + the height of its topmost component. + +*/ + +static void endless_loop_error(internal_font_number g, int y) +{ + char s[256]; + const char *hlp[] = { + "You managed to create a seemingly endless charlist chain in the current", + "font. I have counted until 10000 already and still have not escaped, so" + "I will jump out of the loop all by myself now. Fix your font!", + NULL + }; + snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)", (int) y, font_name(g)); + tex_error(s, hlp); +} + +static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, int cur_style, boolean shift, boolean *stack, scaled *delta, int *same) +{ + /*tex the box that will be constructed */ + pointer b; + /*tex best-so-far and tentative font codes */ + internal_font_number f, g; + /*tex best-so-far and tentative character codes */ + int c, i, x, y; + /*tex height-plus-depth of a tentative character */ + scaled u; + /*tex largest height-plus-depth so far */ + scaled w = 0; + /*tex runs through font family members */ + int z; + /*tex are we trying the ``large'' variant? */ + boolean large_attempt = false; + /*tex to save the current attribute list */ + pointer att = null; + int emas = 0 ; + boolean do_parts = false; + boolean parts_done = false; + extinfo *ext; + + if (d && ! small_fam(d) && ! large_fam(d) && ! small_char(d) && ! large_char(d)) { + halfword b = new_null_box(); + subtype(b) = math_v_delimiter_list; + if (! flat) + width(b) = null_delimiter_space_par; + node_attr(b) = node_attr(d); + node_attr(d) = null; + flush_node(d); + return b; + } + + f = null_font; + c = 0; + if (d == null) { + goto FOUND; + } + z = small_fam(d); + x = small_char(d); + i = 0; + if (same != NULL) { + emas = *same ; + same = 0; + } + while (true) { + /*tex + + The search process is complicated slightly by the facts that some of + the characters might not be present in some of the fonts, and they + might not be probed in increasing order of height. + + */ + if ((z != 0) || (x != 0)) { + g = fam_fnt(z, s); + if (g != null_font) { + y = x; + CONTINUE: + i++; + if (char_exists(g, y)) { + if (flat) + u = char_width(g, y); + else + u = height_plus_depth(g, y); + if (u > w) { + f = g; + c = y; + w = u; + if (u >= v) + goto FOUND; + } + if (char_tag(g, y) == ext_tag) { + f = g; + c = y; + do_parts = true; + goto FOUND; + } + if (i > 10000) { + endless_loop_error(g, y); + goto FOUND; + } + if (char_tag(g, y) == list_tag) { + y = char_remainder(g, y); + goto CONTINUE; + } + } + } + } + if (large_attempt) { + /*tex There were none large enough. */ + goto FOUND; + } + large_attempt = true; + z = large_fam(d); + x = large_char(d); + } + FOUND: + if (d != null) { + att = node_attr(d); + node_attr(d) = null; + flush_node(d); + } + if (f != null_font) { + /*tex + + When the following code is executed, |do_parts| will be true if a + built-up symbol is supposed to be returned. + + */ + ext = NULL; + if ((do_parts) && ((!flat && (ext = get_charinfo_vert_variants(char_info(f,c))) != NULL) + || ( flat && (ext = get_charinfo_hor_variants (char_info(f,c))) != NULL))) { + parts_done = true; + if (flat) { + b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 1, att); + } else { + b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 0, att); + } + if (delta != NULL) { + if (do_new_math(f)) { + *delta = char_vert_italic(f,x); + } else { + *delta = char_italic(f,x); + } + } + if (stack != NULL) + *stack = true ; + } else { + parts_done = false; + if (same != NULL && x == c) { + *same = emas; + } + /*tex + Here italic is added to width in traditional fonts which makes the delimiter get + the real width. An \OPENTYPE\ font already has the right width. + */ + b = char_box(f, c, att); + /*tex + There is one case where |delta| (ic) gets subtracted but only for a traditional + font. In that case the traditional width (which is fake width + italic) becomes + less and the delta is added. See (**). (On the mailing list font |ntxexx| was + mentioned as test case by MK.) + */ + if (delta != NULL) { + *delta = char_italic(f, c); + } + if (stack != NULL) + *stack = false ; + } + } else { + b = new_null_box(); + reset_attributes(b, att); + if (flat) { + width(b) = 0; + subtype(b) = math_h_delimiter_list; + } else { + /*tex Use this width if no delimiter was found. */ + width(b) = null_delimiter_space_par; + subtype(b) = math_v_delimiter_list; + } + if (delta != NULL) { + *delta = 0; + } + if (stack != NULL) + *stack = false ; + } + if (!flat) { + /*tex when emas ~= 0 then we have a non scaled character */ + if (emas != 0 && delimitermodesamenos) { + /*tex same character and no shift when same forced */ + goto DONE; + } + if (! parts_done && delimitermodecharnos) { + /*tex same character and no shift when same forced */ + goto DONE; + } + if (delimitermodenoshift) { + /*tex no shift forced */ + goto DONE; + } + /*tex vertical variant */ + shift_amount(b) = half(height(b) - depth(b)); + if (shift) { + shift_amount(b) -= math_axis_size(s); + } + } + DONE: + delete_attribute_ref(att); + return b; +} + +/*tex + + The next subroutine is much simpler; it is used for numerators and + denominators of fractions as well as for displayed operators and their limits + above and below. It takes a given box~|b| and changes it so that the new box + is centered in a box of width~|w|. The centering is done by putting \.{\\hss} + glue at the left and right of the list inside |b|, then packaging the new + box; thus, the actual box might not really be centered, if it already + contains infinite glue. + + The given box might contain a single character whose italic correction has + been added to the width of the box; in this case a compensating kern is + inserted. + +*/ + +static pointer rebox(pointer b, scaled w) +{ + /*tex temporary registers for list manipulation */ + pointer p, q, r, att; + /*tex font in a one-character box */ + internal_font_number f; + /*tex width of a character without italic correction */ + scaled v; + if ((width(b) != w) && (list_ptr(b) != null)) { + if (type(b) == vlist_node) { + p = hpack(b, 0, additional, -1); + reset_attributes(p, node_attr(b)); + b = p; + } + p = list_ptr(b); + att = node_attr(b); + add_node_attr_ref(att); + if ((is_char_node(p)) && (vlink(p) == null)) { + f = font(p); + v = char_width(f, character(p)); + if (v != width(b)) { + q = new_kern(width(b) - v); + reset_attributes(q, att); + couple_nodes(p,q); + } + } + list_ptr(b) = null; + flush_node(b); + b = new_glue(ss_glue); + reset_attributes(b, att); + couple_nodes(b,p); + while (vlink(p) != null) + p = vlink(p); + q = new_glue(ss_glue); + reset_attributes(q, att); + couple_nodes(p,q); + r = hpack(b, w, exactly, -1); + reset_attributes(r, att); + delete_attribute_ref(att); + return r; + } else { + width(b) = w; + return b; + } +} + +/*tex + + Here is a subroutine that creates a new glue specification from another one + that is expressed in `\.{mu}', given the value of the math unit. + +*/ + +#define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen) + +static pointer math_glue(pointer g, scaled m) +{ + /*tex integer part of |m| */ + int n = x_over_n(m, unity); + /*tex fraction part of |m| */ + scaled f = tex_remainder; + /*tex the new glue specification */ + pointer p; + if (f < 0) { + decr(n); + f = f + unity; + } + p = new_node(glue_node, 0); + /* convert \.{mu} to \.{pt} */ + width(p) = mu_mult(width(g)); + stretch_order(p) = stretch_order(g); + if (stretch_order(p) == normal) + stretch(p) = mu_mult(stretch(g)); + else + stretch(p) = stretch(g); + shrink_order(p) = shrink_order(g); + if (shrink_order(p) == normal) + shrink(p) = mu_mult(shrink(g)); + else + shrink(p) = shrink(g); + return p; +} + +static void math_glue_to_glue(pointer p, scaled m) +{ + /*tex integer part of |m| */ + int n = x_over_n(m, unity); + /*tex fraction part of |m| */ + scaled f = tex_remainder; + if (f < 0) { + decr(n); + f = f + unity; + } + /* convert \.{mu} to \.{pt} */ + width(p) = mu_mult(width(p)); + if (stretch_order(p) == normal) + stretch(p) = mu_mult(stretch(p)); + if (shrink_order(p) == normal) + shrink(p) = mu_mult(shrink(p)); + subtype(p) = normal; +} + +/*tex + + The |math_kern| subroutine removes |mu_glue| from a kern node, given the + value of the math unit. + +*/ +static void math_kern(pointer p, scaled m) +{ + /*tex integer part of |m| */ + int n; + /*tex fraction part of |m| */ + scaled f; + if (subtype(p) == mu_glue) { + n = x_over_n(m, unity); + f = tex_remainder; + if (f < 0) { + decr(n); + f = f + unity; + } + width(p) = mu_mult(width(p)); + /* this is weird, it's not a italic but explicit_kern */ + subtype(p) = italic_kern; + } +} + +void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle) +{ + int callback_id; + int a, sfix, i; + if (p == null) { + vlink(temp_head) = null; + return; + } + finalize_math_parameters(); + callback_id = callback_defined(mlist_to_hlist_callback); + if (callback_id > 0) { + sfix = lua_gettop(Luas); + if (!get_callback(Luas, callback_id)) { + lua_settop(Luas, sfix); + return; + } + alink(p) = null ; + nodelist_to_lua(Luas, p); + lua_push_math_style_name(Luas, mstyle); + lua_pushboolean(Luas, penalties); + if ((i=lua_pcall(Luas, 3, 1, 0)) != 0) { + formatted_warning("mlist to hlist","error: %s",lua_tostring(Luas, -1)); + lua_settop(Luas, sfix); + luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return; + } + a = nodelist_from_lua(Luas,-1); + /* alink(vlink(a)) = null; */ + vlink(temp_head) = a; + lua_settop(Luas, sfix); + } else if (callback_id == 0) { + mlist_to_hlist(p, penalties, mstyle); + } else { + vlink(temp_head) = null; + } +} + +/*tex + + The recursion in |mlist_to_hlist| is due primarily to a subroutine called + |clean_box| that puts a given noad field into a box using a given math style; + |mlist_to_hlist| can call |clean_box|, which can call |mlist_to_hlist|. + + The box returned by |clean_box| is ``clean'' in the sense that its + |shift_amount| is zero. + +*/ + +static pointer clean_box(pointer p, int s, int cur_style, halfword st) +{ + /*tex beginning of a list to be boxed */ + pointer q; + /*tex box to be returned */ + pointer x; + /*tex temporary pointer */ + pointer r; + /*tex beginning of mlist to be translated */ + pointer mlist = null; + switch (type(p)) { + case math_char_node: + mlist = new_noad(); + r = math_clone(p); + nucleus(mlist) = r; + break; + case sub_box_node: + q = math_list(p); + goto FOUND; + break; + case sub_mlist_node: + mlist = math_list(p); + break; + default: + q = new_null_box(); + subtype(q) = math_list_list; + goto FOUND; + } + mlist_to_hlist(mlist, false, s); + /*tex recursive call */ + q = vlink(temp_head); + setup_cur_size(cur_style); + FOUND: + if (is_char_node(q) || (q == null)) + x = hpack(q, 0, additional, -1); + else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0)) + /*tex It's already clean. */ + x = q; + else + x = hpack(q, 0, additional, -1); + if (x != q && q != null) + reset_attributes(x, node_attr(q)); + subtype(x) = st; + /*tex Here we save memory space in a common case. */ + q = list_ptr(x); + if (is_char_node(q)) { + r = vlink(q); + if (r != null) { + if (vlink(r) == null) { + if (!is_char_node(r)) { + if (type(r) == kern_node) { + /*tex Unneeded italic correction. */ + flush_node(r); + vlink(q) = null; + } + } + } + } + } + return x; +} + +/*tex + + It is convenient to have a procedure that converts a |math_char| field to an + ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c| to the font + code and character code of a given noad field. It also takes care of issuing + error messages for nonexistent characters; in such cases, + |char_exists(cur_f,cur_c)| will be |false| after |fetch| has acted, and the + field will also have been reset to |null|. + + The outputs of |fetch| are placed in global variables. + +*/ + +/*tex the |font| field of a |math_char| */ + +internal_font_number cur_f; + +/*tex the |character| field of a |math_char| */ + +int cur_c; + +/*tex Here we unpack the |math_char| field |a|. */ + +static void fetch(pointer a) +{ + cur_c = math_character(a); + cur_f = fam_fnt(math_fam(a), cur_size); + if (cur_f == null_font) { + char *msg; + const char *hlp[] = { + "Somewhere in the math formula just ended, you used the", + "stated character from an undefined font family. For example,", + "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,", + "and I'll try to forget that I needed that character.", + NULL + }; + msg = xmalloc(256); + snprintf(msg, 255, "\\%s%d is undefined (character %d)", + math_size_string(cur_size), (int) math_fam(a), (int) cur_c); + tex_error(msg, hlp); + free(msg); + } else if (!(char_exists(cur_f, cur_c))) { + char_warning(cur_f, cur_c); + } +} + +/*tex + + We need to do a lot of different things, so |mlist_to_hlist| makes two passes + over the given mlist. + + The first pass does most of the processing: It removes ``mu'' spacing from + glue, it recursively evaluates all subsidiary mlists so that only the + top-level mlist remains to be handled, it puts fractions and square roots and + such things into boxes, it attaches subscripts and superscripts, and it + computes the overall height and depth of the top-level mlist so that the size + of delimiters for a |fence_noad| will be known. The hlist resulting from each + noad is recorded in that noad's |new_hlist| field, an integer field that + replaces the |nucleus| or |thickness|. + + The second pass eliminates all noads and inserts the correct glue and + penalties between nodes. + +*/ + +static void assign_new_hlist(pointer q, pointer r) +{ + switch (type(q)) { + case fraction_noad: + math_list(numerator(q)) = null; + flush_node(numerator(q)); + numerator(q) = null; + math_list(denominator(q)) = null; + flush_node(denominator(q)); + denominator(q) = null; + break; + case radical_noad: + case simple_noad: + case accent_noad: + if (nucleus(q) != null) { + math_list(nucleus(q)) = null; + flush_node(nucleus(q)); + nucleus(q) = null; + } + break; + } + new_hlist(q) = r; +} + +#define choose_mlist(A) do { p=A(q); A(q)=null; } while (0) + +/*tex + + Most of the actual construction work of |mlist_to_hlist| is done by + procedures with names like |make_fraction|, |make_radical|, etc. To + illustrate the general setup of such procedures, let's begin with a couple of + simple ones. + +*/ + +static void make_over(pointer q, int cur_style, int cur_size, int cur_fam) +{ + /*tex + + No rule adaption yet, maybe never as overbars should be proper + extensibles. + + */ + pointer p; + scaled f, t; + scaled used_thickness = overbar_rule(cur_style); + scaled used_fam = cur_fam; + if (math_rule_thickness_mode_par > 0) { + f = noad_fam(q); + if (f >= 0) { + t = fam_fnt(f,cur_size); + if (do_new_math(t)) { + t = font_MATH_par(t, OverbarRuleThickness); + if (t != undefined_math_parameter) { + used_thickness = t; + used_fam = f; + } + } + } + } + p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style, math_nucleus_list), + overbar_vgap(cur_style), used_thickness, overbar_kern(cur_style), + node_attr(nucleus(q)), math_over_rule, cur_size, used_fam); + subtype(p) = math_over_list; + math_list(nucleus(q)) = p; + type(nucleus(q)) = sub_box_node; +} + +static void make_under(pointer q, int cur_style, int cur_size, int cur_fam) +{ + /*tex + + No rule adaption yet, maybe never as underbars should be proper + extensibles. + + */ + /*tex temporary registers for box construction */ + pointer p, x, y, r; + /*tex overall height plus depth */ + scaled delta; + scaled f, t; + scaled used_thickness = underbar_rule(cur_style); + scaled used_fam = cur_fam; + x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list); + p = new_kern(underbar_vgap(cur_style)); + reset_attributes(p, node_attr(q)); + couple_nodes(x,p); + if (math_rule_thickness_mode_par > 0) { + f = noad_fam(q); + if (f >= 0) { + t = fam_fnt(f,cur_size); + if (do_new_math(t)) { + t = font_MATH_par(t, UnderbarRuleThickness); + if (t != undefined_math_parameter) { + used_thickness = t; + used_fam = f; + } + } + } + } + r = do_fraction_rule(used_thickness, node_attr(q), math_under_rule, cur_size, used_fam); + couple_nodes(p,r); + y = vpackage(x, 0, additional, max_dimen, math_direction_par); + reset_attributes(y, node_attr(q)); + subtype(y) = math_under_list; + delta = height(y) + depth(y) + underbar_kern(cur_style); + height(y) = height(x); + depth(y) = delta - height(y); + math_list(nucleus(q)) = y; + type(nucleus(q)) = sub_box_node; +} + +static void make_vcenter(pointer q) +{ + /*tex the box that should be centered vertically */ + pointer v; + /*tex its height plus depth */ + scaled delta; + v = math_list(nucleus(q)); + if (type(v) != vlist_node) + confusion("vcenter"); + delta = height(v) + depth(v); + height(v) = math_axis_size(cur_size) + half(delta); + depth(v) = delta - height(v); +} + +/*tex + + According to the rules in the \.{DVI} file specifications, we ensure + alignment between a square root sign and the rule above its nucleus by + assuming that the baseline of the square-root symbol is the same as the + bottom of the rule. The height of the square-root symbol will be the + thickness of the rule, and the depth of the square-root symbol should exceed + or equal the height-plus-depth of the nucleus plus a certain minimum + clearance~|psi|. The symbol will be placed so that the actual clearance is + |psi| plus half the excess. + +*/ + +static void make_hextension(pointer q, int cur_style) +{ + pointer e, p; + halfword w; + boolean stack = false; + e = do_delimiter(q, left_delimiter(q), cur_size, radicalwidth(q), true, cur_style, true, &stack, NULL, NULL); + w = width(e); + if (!stack&& (radicalwidth(q) != 0) && (radicalwidth(q) != width(e))) { + if (radicalmiddle(q)) { + p = new_kern(half(radicalwidth(q)-w)); + reset_attributes(p, node_attr(q)); + couple_nodes(p,e); + e = p; + w = radicalwidth(q); + } else if (radicalexact(q)) { + w = radicalwidth(q); + } + } + e = hpack(e, 0, additional, -1); + width(e) = w ; + reset_attributes(e, node_attr(q)); + math_list(nucleus(q)) = e; + left_delimiter(q) = null; +} + +static void make_radical(pointer q, int cur_style) +{ + /*tex temporary registers for box construction */ + pointer x, y, p, l1, l2; + /*tex dimensions involved in the calculation */ + scaled delta, clr, theta, h, f; + scaled t, used_fam ; + x = clean_box(nucleus(q), cramped_style(cur_style), cur_style, math_nucleus_list); + clr = radical_vgap(cur_style); + theta = radical_rule_par(cur_style); + used_fam = small_fam(left_delimiter(q)); + /*tex + + We can take the rule width from the fam/style of the delimiter or use the + most recent math parameters value. + + */ + if (math_rule_thickness_mode_par > 0) { + f = small_fam(left_delimiter(q)); + if (f >= 0) { + t = fam_fnt(f,cur_size); + if (do_new_math(t)) { + t = font_MATH_par(t, RadicalRuleThickness); + if (t != undefined_math_parameter) { + theta = t; + used_fam = f; + } + } + } + } + if (theta == undefined_math_parameter) { + /*tex a real radical */ + theta = fraction_rule(cur_style); + y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL); + /*tex + + If |y| is a composite then set |theta| to the height of its top + character, else set it to the height of |y|. + + */ + l1 = list_ptr(y); + if ((l1 != null) && (type(l1) == hlist_node)) { + /*tex possible composite */ + l2 = list_ptr(l1); + if ((l2 != null) && (type(l2) == glyph_node)) { + /*tex top character */ + theta = char_height(font(l2), character(l2)); + } else { + theta = height(y); + } + } else { + theta = height(y); + } + } else { + /*tex + + Not really a radical but we use its node, historical sharing (like in + mathml). + + */ + y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL); + } + /*tex + + Weird hack, in overbar we use small_fam(left_delimiter(q)) so actually + small_fam(0). + + */ + left_delimiter(q) = null; + delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr); + if (delta > 0) { + /*tex increase the actual clearance */ + clr = clr + half(delta); + } + shift_amount(y) = (height(y) - theta) - (height(x) + clr); + h = depth(y) + height(y); + p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y), math_radical_rule, cur_size, used_fam); + subtype(p) = math_radical_list; + couple_nodes(y,p); + if (degree(q) != null) { + scaled wr, br, ar; + pointer r = clean_box(degree(q), script_script_style, cur_style, math_degree_list); + reset_attributes(r, node_attr(degree(q))); + wr = width(r); + if (wr == 0) { + flush_node(r); + } else { + br = radical_degree_before(cur_style); + ar = radical_degree_after(cur_style); + if (-ar > (wr + br)) + ar = -(wr + br); + x = new_kern(ar); + reset_attributes(x, node_attr(degree(q))); + couple_nodes(x,y); + shift_amount(r) = + -((xn_over_d(h, radical_degree_raise(cur_style), 100)) - + depth(y) - shift_amount(y)); + couple_nodes(r,x); + x = new_kern(br); + reset_attributes(x, node_attr(degree(q))); + couple_nodes(x,r); + y = x; + } + /*tex for \.{\\Uroot ..{<list>}{}} : */ + math_list(degree(q)) = null; + flush_node(degree(q)); + } + p = hpack(y, 0, additional, -1); + reset_attributes(p, node_attr(q)); + math_list(nucleus(q)) = p; + type(nucleus(q)) = sub_box_node; +} + +/*tex Construct a vlist box: */ + +static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down, halfword st) +{ + pointer p; + pointer v = new_null_box(); + type(v) = vlist_node; + subtype(v) = st; + height(v) = shift_up + height(x); + depth(v) = depth(y) + shift_down; + reset_attributes(v, node_attr(q)); + p = new_kern((shift_up - depth(x)) - (height(y) - shift_down)); + reset_attributes(p, node_attr(q)); + couple_nodes(p,y); + couple_nodes(x,p); + list_ptr(v) = x; + return v; +} + +/*tex When |exact| use radicalwidth (|y| is delimiter). */ + +#define fixup_widths(q,x,y) do { \ + if (width(y) >= width(x)) { \ + if (radicalwidth(q) != 0) { \ + shift_amount(x) += half(width(y)-width(x)) ; \ + } \ + width(x) = width(y); \ + } else { \ + if (radicalwidth(q) != 0) { \ + shift_amount(y) += half(width(x)-width(y)) ; \ + } \ + width(y) = width(x); \ + } \ +} while (0) + + +#define check_radical(q,stack,r,t) do { \ + if (!stack && (width(r) >= width(t)) && (radicalwidth(q) != 0) && (radicalwidth(q) != width(r))) { \ + if (radicalleft(q)) { \ + halfword p = new_kern(radicalwidth(q)-width(r)); \ + reset_attributes(p, node_attr(q)); \ + couple_nodes(p,r); \ + r = hpack(p, 0, additional, -1); \ + width(r) = radicalwidth(q); \ + reset_attributes(r, node_attr(q)); \ + } else if (radicalmiddle(q)) { \ + halfword p = new_kern(half(radicalwidth(q)-width(r))); \ + reset_attributes(p, node_attr(q)); \ + couple_nodes(p,r); \ + r = hpack(p, 0, additional, -1); \ + width(r) = radicalwidth(q); \ + reset_attributes(r, node_attr(q)); \ + } else if (radicalright(q)) { \ + /*tex also kind of exact compared to vertical */ \ + r = hpack(r, 0, additional, -1); \ + width(r) = radicalwidth(q); \ + reset_attributes(r, node_attr(q)); \ + } \ + } \ +} while (0) + +#define check_widths(q,p) do { \ + if (radicalwidth(q) != 0) { \ + wd = radicalwidth(q); \ + } else { \ + wd = width(p); \ + } \ +} while (0) + +/*tex + + This has the |nucleus| box |x| as a limit above an extensible delimiter |y|. + +*/ + +static void make_over_delimiter(pointer q, int cur_style) +{ + pointer x, y, v; + scaled shift_up, shift_down, clr, delta, wd; + boolean stack; + x = clean_box(nucleus(q), sub_style(cur_style), cur_style, math_nucleus_list); + check_widths(q,x); + y = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL, NULL); + left_delimiter(q) = null; + check_radical(q,stack,y,x); + fixup_widths(q, x, y); + shift_up = over_delimiter_bgap(cur_style); + shift_down = 0; + clr = over_delimiter_vgap(cur_style); + delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); + if (delta > 0) { + shift_up = shift_up + delta; + } + v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down, math_over_delimiter_list); + /*tex This also equals |width(y)|: */ + width(v) = width(x); + math_list(nucleus(q)) = v; + type(nucleus(q)) = sub_box_node; +} + +/*tex + + This has the extensible delimiter |x| as a limit below |nucleus| box |y|. + +*/ + +static void make_under_delimiter(pointer q, int cur_style) +{ + pointer x, y, v; + scaled shift_up, shift_down, clr, delta, wd; + boolean stack; + y = clean_box(nucleus(q), sup_style(cur_style), cur_style, math_nucleus_list); + check_widths(q,y); + x = do_delimiter(q, left_delimiter(q), cur_size, wd, true, cur_style, true, &stack, NULL, NULL); + left_delimiter(q) = null; + check_radical(q,stack,x,y); + fixup_widths(q, x, y); + shift_up = 0; + shift_down = under_delimiter_bgap(cur_style); + clr = under_delimiter_vgap(cur_style); + delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); + if (delta > 0) { + shift_down = shift_down + delta; + } + v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down, math_under_delimiter_list); + /*tex This also equals |width(y)|: */ + width(v) = width(y); + math_list(nucleus(q)) = v; + type(nucleus(q)) = sub_box_node; +} + +/*tex + + This has the extensible delimiter |x| as a limit above |nucleus| box |y|. + +*/ + +static void make_delimiter_over(pointer q, int cur_style) +{ + pointer x, y, v; + scaled shift_up, shift_down, clr, actual, wd; + boolean stack; + y = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list); + check_widths(q,y); + x = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL, NULL); + left_delimiter(q) = null; + check_radical(q,stack,x,y); + fixup_widths(q, x, y); + shift_up = over_delimiter_bgap(cur_style)-height(x)-depth(x); + shift_down = 0; + clr = over_delimiter_vgap(cur_style); + actual = shift_up - height(y); + if (actual < clr) { + shift_up = shift_up + (clr-actual); + } + v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down, math_over_delimiter_list); + /*tex This also equals |width(y)|: */ + width(v) = width(x); + math_list(nucleus(q)) = v; + type(nucleus(q)) = sub_box_node; +} + +/*tex + + This has the extensible delimiter |y| as a limit below a |nucleus| box |x|. + +*/ + +static void make_delimiter_under(pointer q, int cur_style) +{ + pointer x, y, v; + scaled shift_up, shift_down, clr, actual, wd; + boolean stack; + x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list); + check_widths(q,x); + y = do_delimiter(q, left_delimiter(q), cur_size + (cur_size == script_script_size ? 0 : 1), wd, true, cur_style, true, &stack, NULL, NULL); + left_delimiter(q) = null; + check_radical(q,stack,y,x); + fixup_widths(q, x, y); + shift_up = 0; + shift_down = under_delimiter_bgap(cur_style) - height(y)-depth(y); + clr = under_delimiter_vgap(cur_style); + actual = shift_down - depth(x); + if (actual<clr) { + shift_down += (clr-actual); + } + v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down, math_under_delimiter_list); + /*tex This also equals |width(y)|: */ + width(v) = width(y); + math_list(nucleus(q)) = v; + type(nucleus(q)) = sub_box_node; +} + +/*tex + + Slants are not considered when placing accents in math mode. The accenter is + centered over the accentee, and the accent width is treated as zero with + respect to the size of the final box. + +*/ + +#define TOP_CODE 1 +#define BOT_CODE 2 +#define OVERLAY_CODE 4 +#define STRETCH_ACCENT_CODE 8 + +static boolean compute_accent_skew(pointer q, int flags, scaled *s) +{ + /*tex temporary register for box construction */ + pointer p; + /*tex will be true if a top-accent is placed in |s| */ + boolean s_is_absolute = false; + if (type(nucleus(q)) == math_char_node) { + fetch(nucleus(q)); + if (do_new_math(cur_f)) { + /*tex + There is no bot_accent so let's assume similarity + + \starttyping + if (flags & (TOP_CODE | OVERLAY_CODE)) { + *s = char_top_accent(cur_f, cur_c); + if (*s != INT_MIN) { + s_is_absolute = true; + } + } else { + *s = char_bot_accent(cur_f, cur_c); + if (*s != INT_MIN) { + s_is_absolute = true; + } + } + \stoptyping + */ + *s = char_top_accent(cur_f, cur_c); + if (*s != INT_MIN) { + s_is_absolute = true; + } + } else { + if (flags & TOP_CODE) { + *s = get_kern(cur_f, cur_c, skew_char(cur_f)); + } else { + *s = 0; + } + } + } else if (type(nucleus(q)) == sub_mlist_node) { + /*tex + If |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we: + + \startitemize + \startitem + use the positioning of the nucleus of that noad, recursing until + \stopitem + \startitem + the inner most |accent_noad|. This way multiple stacked accents + are + \stopitem + \startitem + aligned to the inner most one. + \stopitem + \stoptitemize + + The vlink test was added in version 1.06, so that we only consider a + lone noad: + + $ + \Umathaccent bottom 0 0 "023DF { \Umathaccent fixed 0 0 "00302 { m } r } \quad + \Umathaccent bottom 0 0 "023DF { l \Umathaccent fixed 0 0 "00302 { m } r } \quad + \Umathaccent bottom 0 0 "023DF { l \Umathaccent fixed 0 0 "00302 { m } } \quad + \Umathaccent bottom 0 0 "023DF { \Umathaccent fixed 0 0 "00302 { m } } \quad + \Umathaccent bottom 0 0 "023DF { l r } + $ + + */ + p = math_list(nucleus(q)); + if (type(p) == accent_noad && vlink(p) == null) { + s_is_absolute = compute_accent_skew(p, flags, s); + } + } else { + } + + return s_is_absolute; +} + +static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style) +{ + /*tex temporary registers for box construction */ + pointer p, r, x, y; + /*tex amount to skew the accent to the right */ + scaled s; + /*tex height of character being accented */ + scaled h; + /*tex space to remove between accent and accentee */ + scaled delta; + /*tex width of the accentee, not including sub/superscripts */ + scaled w; + /*tex will be true if a top-accent is placed in |s| */ + boolean s_is_absolute; + scaled fraction ; + scaled ic = 0; + scaled target ; + extinfo *ext; + pointer attr_p; + attr_p = (flags & TOP_CODE ? top_accent_chr(q) : flags & BOT_CODE ? bot_accent_chr(q) : overlay_accent_chr(q)); + fraction = accentfraction(q); + c = cur_c; + f = cur_f; + s = 1; + if (fraction == 0) { + fraction = 1000; + } + /*tex Compute the amount of skew, or set |s| to an alignment point */ + s_is_absolute = compute_accent_skew(q, flags, &s); + x = clean_box(nucleus(q), cramped_style(cur_style), cur_style, math_nucleus_list); + w = width(x); + h = height(x); + if (do_new_math(cur_f) && !s_is_absolute) { + s = half(w); + s_is_absolute = true; + } + /*tex Switch to a larger accent if available and appropriate */ + y = null; + ext = NULL; + if (flags & OVERLAY_CODE) { + if (fraction > 0) { + target = xn_over_d(h,fraction,1000); + } else { + target = h; + } + } else { + if (fraction > 0) { + target = xn_over_d(w,fraction,1000); + } else { + target = w; + } + } + if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) { + while (1) { + if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) { + /*tex a bit weird for an overlay but anyway, here we don't need a factor as we don't step */ + y = get_delim_box(f, c, w, connector_overlap_min(cur_style), 1, node_attr(attr_p)); + break; + } else if (char_tag(f, c) != list_tag) { + break; + } else { + int yy = char_remainder(f, c); + if (!char_exists(f, yy)) { + break; + } else if (flags & OVERLAY_CODE) { + if (char_height(f, yy) > target) { + break; + } + } else { + if (char_width(f, yy) > target) + break; + } + c = yy; + } + } + } + if (y == null) { + /*tex italic gets added to width */ + y = char_box(f, c, node_attr(attr_p)); + } + if (flags & TOP_CODE) { + if (h < accent_base_height(f)) { + delta = h; + } else { + delta = accent_base_height(f); + } + } else if (flags & OVERLAY_CODE) { + /*tex center the accent vertically around the accentee */ + delta = half(height(y) + depth(y) + height(x) + depth(x)); + } else { + delta = 0; /* hm */ + } + if ((supscr(q) != null) || (subscr(q) != null)) { + if (type(nucleus(q)) == math_char_node) { + /*tex swap the subscript and superscript into box |x| */ + flush_node_list(x); + x = new_noad(); + r = math_clone(nucleus(q)); + nucleus(x) = r; + supscr(x) = supscr(q); + supscr(q) = null; + subscr(x) = subscr(q); + subscr(q) = null; + type(nucleus(q)) = sub_mlist_node; + math_list(nucleus(q)) = x; + x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list); + delta = delta + height(x) - h; + h = height(x); + } + } else if ((vlink(q) != null) && (type(nucleus(q)) == math_char_node)) { + /*tex only pure math char nodes */ + internal_font_number f = fam_fnt(math_fam(nucleus(q)),cur_size); + if (do_new_math(f)) { + ic = char_italic(f,math_character(nucleus(q))); + } + } + /*tex the top accents of both characters are aligned */ + if (s_is_absolute) { + scaled sa; + if (ext != NULL) { + /*tex if the accent is extensible just take the center */ + sa = half(width(y)); + } else { + /*tex + There is no bot_accent so let's assume similarity + + \starttyping + if (flags & BOT_CODE) { + sa = char_bot_accent(f, c); + } else { + sa = char_top_accent(f, c); + } + \stoptyping + */ + sa = char_top_accent(f, c); + } + if (sa == INT_MIN) { + /*tex just take the center */ + sa = half(width(y)); + } + if (math_direction_par == dir_TRT) { + shift_amount(y) = s + sa - width(y); + } else { + shift_amount(y) = s - sa; + } + } else { + if (width(y)== 0) { + shift_amount(y) = s + w; + } else if (math_direction_par == dir_TRT) { + shift_amount(y) = s + width(y); /* ok? */ + } else { + shift_amount(y) = s + half(w - width(y)); + } + } + width(y) = 0; + if (flags & (TOP_CODE | OVERLAY_CODE)) { + p = new_kern(-delta); + reset_attributes(p, node_attr(q)); + couple_nodes(p,x); + couple_nodes(y,p); + } else { + couple_nodes(x,y); + y = x; + } + r = vpackage(y, 0, additional, max_dimen, math_direction_par); + reset_attributes(r, node_attr(q)); + subtype(r) = math_accent_list; + width(r) = width(x); + y = r; + if (flags & (TOP_CODE | OVERLAY_CODE)) { + if (height(y) < h) { + /*tex make the height of box |y| equal to |h| */ + p = new_kern(h - height(y)); + reset_attributes(p, node_attr(q)); + try_couple_nodes(p,list_ptr(y)); + list_ptr(y) = p; + height(y) = h; + } + } else { + shift_amount(y) = -(h - height(y)); + } + if (ic != 0) { + /*tex old font codepath has ic built in, new font code doesn't */ + width(r) += ic ; + } + math_list(nucleus(q)) = y; + type(nucleus(q)) = sub_box_node; +} + +static void make_math_accent(pointer q, int cur_style) +{ + int topstretch = !(subtype(q) % 2); + int botstretch = !(subtype(q) / 2); + + if (top_accent_chr(q) != null) { + fetch(top_accent_chr(q)); + if (char_exists(cur_f, cur_c)) { + do_make_math_accent(q, cur_f, cur_c, TOP_CODE | (topstretch ? STRETCH_ACCENT_CODE : 0), cur_style); + } + flush_node(top_accent_chr(q)); + top_accent_chr(q) = null; + } + if (bot_accent_chr(q) != null) { + fetch(bot_accent_chr(q)); + if (char_exists(cur_f, cur_c)) { + do_make_math_accent(q, cur_f, cur_c, BOT_CODE | (botstretch ? STRETCH_ACCENT_CODE : 0), cur_style); + } + flush_node(bot_accent_chr(q)); + bot_accent_chr(q) = null; + } + if (overlay_accent_chr(q) != null) { + fetch(overlay_accent_chr(q)); + if (char_exists(cur_f, cur_c)) { + do_make_math_accent(q, cur_f, cur_c, OVERLAY_CODE | STRETCH_ACCENT_CODE, cur_style); + } + flush_node(overlay_accent_chr(q)); + overlay_accent_chr(q) = null; + } +} + +/*tex + + The |make_fraction| procedure is a bit different because it sets + |new_hlist(q)| directly rather than making a sub-box. + +*/ + +static void make_fraction(pointer q, int cur_style) +{ + pointer p, p1, p2, v, x, y, z, l, r, m; + scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2, f, t;\ + /*tex + + We can take the rule width from an explicitly set fam, even if a fraction + itself has no character, otherwise we just use the math parameter. + + */ + scaled used_fam = math_rules_fam_par; + if (math_rule_thickness_mode_par > 0 && thickness(q) != 0) { + f = fraction_fam(q); + if (f >= 0) { + t = fam_fnt(f,cur_size); + if (do_new_math(t)) { + t = font_MATH_par(t, FractionRuleThickness); + if (t != undefined_math_parameter) { + thickness(q) = t; + used_fam = f; + } + } + } + } + if (thickness(q) == default_code) + thickness(q) = fraction_rule(cur_style); + /*tex + + Create equal-width boxes |x| and |z| for the numerator and denominator, + and compute the default amounts |shift_up| and |shift_down| by which they + are displaced from the baseline. + + */ + x = clean_box(numerator(q), num_style(cur_style), cur_style, math_numerator_list); + z = clean_box(denominator(q), denom_style(cur_style), cur_style, math_denominator_list); + if (middle_delimiter(q) != null) { + delta = 0; + m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL); + middle_delimiter(q) = null; + } else { + m = null ; + if (width(x) < width(z)) { + x = rebox(x, width(z)); + } else { + z = rebox(z, width(x)); + } + } + if (m != null) { + shift_up = 0; + shift_down = 0; + } else if (thickness(q) == 0) { + shift_up = stack_num_up(cur_style); + shift_down = stack_denom_down(cur_style); + /*tex + + The numerator and denominator must be separated by a certain minimum + clearance, called |clr| in the following program. The difference + between |clr| and the actual clearance is |2delta|. + + */ + clr1 = stack_vgap(cur_style); + delta = half(clr1 - ((shift_up - depth(x)) - (height(z) - shift_down))); + if (delta > 0) { + shift_up = shift_up + delta; + shift_down = shift_down + delta; + } + } else { + shift_up = fraction_num_up(cur_style); + shift_down = fraction_denom_down(cur_style); + /*tex + + In the case of a fraction line, the minimum clearance depends on the + actual thickness of the line. + + */ + clr1 = fraction_num_vgap(cur_style); + clr2 = fraction_denom_vgap(cur_style); + delta = half(thickness(q)); + if (fractionexact(q)) { + delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis_size(cur_size) + delta)); + delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta)); + } else { + clr1 = ext_xn_over_d(clr1, thickness(q), fraction_rule(cur_style)); + clr2 = ext_xn_over_d(clr2, thickness(q), fraction_rule(cur_style)); + delta1 = clr1 - ((shift_up - depth(x) ) - (math_axis_size(cur_size) + delta)); + delta2 = clr2 - ((shift_down - height(z)) + (math_axis_size(cur_size) - delta)); + } + if (delta1 > 0) { + shift_up = shift_up + delta1; + } + if (delta2 > 0) { + shift_down = shift_down + delta2; + } + } + if (m != null) { + /*tex + + Construct a hlist box for the fraction, according to |hgap| and + |vgap|. + + */ + shift_up = skewed_fraction_vgap(cur_style); + + if (!fractionnoaxis(q)) { + shift_up += half(math_axis_size(cur_size)); + } + shift_down = shift_up; + v = new_null_box(); + reset_attributes(v, node_attr(q)); + type(v) = hlist_node; + subtype(v) = math_numerator_list; + list_ptr(v) = x; + width(v) = width(x); + height(v) = height(x) + shift_up; + depth(v) = depth(x); + shift_amount(v) = - shift_up; + x = v; + v = new_null_box(); + reset_attributes(v, node_attr(q)); + type(v) = hlist_node; + subtype(v) = math_denominator_list; + list_ptr(v) = z; + width(v) = width(z); + height(v) = height(z); + depth(v) = depth(z) + shift_down; + shift_amount(v) = shift_down; + z = v; + v = new_null_box(); + subtype(v) = math_fraction_list; + reset_attributes(v, node_attr(q)); + type(v) = hlist_node; + if (height(x) > height(z)) { + height(v) = height(x); + } else { + height(v) = height(z); + } + if (depth(x) > depth(z)) { + depth(v) = depth(x); + } else { + depth(v) = depth(z); + } + if (height(m) > height(v)) { + height(v) = height(m); + } + if (depth(m) > depth(v)) { + depth(v) = depth(m); + } + if (fractionexact(q)) { + delta1 = -half(skewed_fraction_hgap(cur_style)); + delta2 = delta1; + width(v) = width(x) + width(z) + width(m) - skewed_fraction_hgap(cur_style); + } else { + delta1 = half(skewed_fraction_hgap(cur_style)-width(m)); + delta2 = half(skewed_fraction_hgap(cur_style)+width(m)); + width(v) = width(x) + width(z) + skewed_fraction_hgap(cur_style); + width(m) = 0; + } + p1 = new_kern(delta1); + reset_attributes(p1, node_attr(q)); + p2 = new_kern(delta2); + reset_attributes(p2, node_attr(q)); + couple_nodes(x,p1); + couple_nodes(p1,m); + couple_nodes(m,p2); + couple_nodes(p2,z); + list_ptr(v) = x; + } else { + /*tex + + Construct a vlist box for the fraction, according to |shift_up| and + |shift_down|. + + */ + v = new_null_box(); + type(v) = vlist_node; + subtype(v) = math_fraction_list; + height(v) = shift_up + height(x); + depth(v) = depth(z) + shift_down; + /*tex This also equals |width(z)|. */ + width(v) = width(x); + reset_attributes(v, node_attr(q)); + if (thickness(q) && ! fractionnorule(q)) { + y = do_fraction_rule(thickness(q), node_attr(q), math_fraction_rule, cur_size, used_fam); + p = new_kern((math_axis_size(cur_size) - delta) - (height(z) - shift_down)); + reset_attributes(p, node_attr(q)); + couple_nodes(y,p); + couple_nodes(p,z); + p = new_kern((shift_up - depth(x)) - (math_axis_size(cur_size) + delta)); + couple_nodes(p,y); + } else { + p = new_kern((shift_up - depth(x)) - (height(z) - shift_down)); + couple_nodes(p,z); + } + reset_attributes(p, node_attr(q)); + couple_nodes(x,p); + list_ptr(v) = x; + } + /*tex + + Put the fraction into a box with its delimiters, and make |new_hlist(q)| + point to it. + + */ + if (do_new_math(cur_f)) { + delta = fraction_del_size_new(cur_style); + if (delta == undefined_math_parameter) { + delta = get_delimiter_height(depth(v), height(v), true); + } + } else { + delta = fraction_del_size_old(cur_style); + } + l = do_delimiter(q, left_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL); + left_delimiter(q) = null; + r = do_delimiter(q, right_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL); + right_delimiter(q) = null; + couple_nodes(l,v); + couple_nodes(v,r); + y = hpack(l, 0, additional, -1); + reset_attributes(y, node_attr(q)); + /*tex There can also be a nested one: */ + subtype(y) = math_fraction_list; + assign_new_hlist(q, y); +} + +/*tex + + If the nucleus of an |op_noad| is a single character, it is to be centered + vertically with respect to the axis, after first being enlarged (via a + character list in the font) if we are in display style. The normal convention + for placing displayed limits is to put them above and below the operator in + display style. + + The italic correction is removed from the character if there is a subscript + and the limits are not being displayed. The |make_op| routine returns the + value that should be used as an offset between subscript and superscript. + + After |make_op| has acted, |subtype(q)| will be |limits| if and only if the + limits have been set above and below the operator. In that case, + |new_hlist(q)| will already contain the desired final box. + +*/ + +static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift); +static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same); + +static scaled make_op(pointer q, int cur_style) +{ + /*tex offset between subscript and superscript */ + scaled delta = 0; + scaled dummy = 0; + /*tex temporary registers for box construction */ + pointer p, v, x, y, z, n; + /*tex register for character examination */ + int c; + /*tex dimensions for box calculation */ + scaled shift_up, shift_down; + boolean axis_shift = false; + scaled ok_size; + if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) { + subtype(q) = op_noad_type_limits; + } + if (type(nucleus(q)) == math_char_node) { + fetch(nucleus(q)); + if (cur_style < text_style) { + /*tex try to make it larger */ + ok_size = minimum_operator_size(cur_style); + if (ok_size != undefined_math_parameter) { + /*tex creating a temporary delimiter is the cleanest way */ + y = new_node(delim_node, 0); + reset_attributes(y, node_attr(nucleus(q))); + small_fam(y) = math_fam(nucleus(q)); + small_char(y) = math_character(nucleus(q)); + x = do_delimiter(q, y, text_size, ok_size, false, cur_style, true, NULL, &delta, NULL); + if (delta != 0) { + if (do_new_math(cur_f)) { + /*tex + As we never added italic correction we don't need to compensate. The ic + is stored in a special field of the node and applied in some occasions. + */ + } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { + /*tex + Here we (selectively) remove the italic correction that always gets added + in a traditional font. See (**). In \OPENTYPE\ mode we insert italic kerns, + but in traditional mode it's width manipulation. This actually makes sense + because those fonts have a fake width and the italic correction sets that + right. + */ + width(x) -= delta; + } + } + } else { + ok_size = height_plus_depth(cur_f, cur_c) + 1; + while ((char_tag(cur_f, cur_c) == list_tag) && height_plus_depth(cur_f, cur_c) < ok_size) { + c = char_remainder(cur_f, cur_c); + if (!char_exists(cur_f, c)) + break; + cur_c = c; + math_character(nucleus(q)) = c; + } + delta = char_italic(cur_f, cur_c); + x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list); + if (delta != 0) { + if (do_new_math(cur_f)) { + /*tex we never added italic correction */ + } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { + /*tex remove italic correction */ + width(x) -= delta; + } + } + axis_shift = true; + } + } else { + /*tex normal size */ + delta = char_italic(cur_f, cur_c); + x = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list); + if (delta != 0) { + if (do_new_math(cur_f)) { + /*tex we never added italic correction */ + } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { + /*tex remove italic correction */ + width(x) -= delta; + } + } + axis_shift = true; + } + if (axis_shift) { + /*tex center vertically */ + shift_amount(x) = half(height(x) - depth(x)) - math_axis_size(cur_size); + } + type(nucleus(q)) = sub_box_node; + math_list(nucleus(q)) = x; + } + /*tex we now handle op_nod_type_no_limits here too */ + if (subtype(q) == op_noad_type_no_limits) { + if (do_new_math(cur_f)) { + /*tex + Not: + + \starttyping + if (delta != 0) { + delta = half(delta) ; + } + \stoptyping + */ + p = check_nucleus_complexity(q, &dummy, cur_style, NULL); + if ((subscr(q) == null) && (supscr(q) == null)) { + assign_new_hlist(q, p); + } else { + /*tex + Not: + + \starttyping + make_scripts(q, p, 0, cur_style, delta, -delta); + \stoptyping + */ + int mode = math_nolimits_mode_par; /* wins */ + /*tex + + For easy configuration ... fonts are somewhat inconsistent + and the values for italic correction run from 30 to 60\% of. + the width. + + */ + switch (mode) { + case 0 : + /*tex full bottom correction */ + make_scripts(q, p, 0, cur_style, 0, -delta); + break; + case 1 : + /*tex |MathConstants| driven */ + make_scripts(q, p, 0, cur_style, + round_xn_over_d(delta, nolimit_sup_factor(cur_style), 1000), + -round_xn_over_d(delta, nolimit_sub_factor(cur_style), 1000)); + break ; + case 2 : + /*tex no correction */ + make_scripts(q, p, 0, cur_style, 0, 0); + break ; + case 3 : + /*tex half bottom correction */ + make_scripts(q, p, 0, cur_style, 0, -half(delta)); + break; + case 4 : + /*tex half bottom and top correction */ + make_scripts(q, p, 0, cur_style, half(delta), -half(delta)); + break; + default : + if (mode > 15) { + /*tex for quickly testing values */ + make_scripts(q, p, 0, cur_style, 0, -round_xn_over_d(delta, mode, 1000)); + } else { + make_scripts(q, p, 0, cur_style, 0, 0); + } + break; + } + } + delta = 0; + } else { + /*tex similar code then the caller (before CHECK_DIMENSIONS) */ + p = check_nucleus_complexity(q, &delta, cur_style, NULL); + if ((subscr(q) == null) && (supscr(q) == null)) { + assign_new_hlist(q, p); + } else { + make_scripts(q, p, delta, cur_style, 0, 0); + } + } + } else if (subtype(q) == op_noad_type_limits) { + /*tex + + The following program builds a vlist box |v| for displayed limits. + The width of the box is not affected by the fact that the limits may + be skewed. + + */ + x = clean_box(supscr(q), sup_style(cur_style), cur_style, math_sup_list); + y = clean_box(nucleus(q), cur_style, cur_style, math_nucleus_list); + z = clean_box(subscr(q), sub_style(cur_style), cur_style, math_sub_list); + v = new_null_box(); + reset_attributes(v, node_attr(q)); + type(v) = vlist_node; + subtype(v) = math_limits_list; + if (do_new_math(cur_f)) { + n = nucleus(q); + if (n != null) { + if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) { + n = math_list(n); + if (n != null) { + if (type(n) == hlist_node) { + /*tex just a not scaled char */ + n = list_ptr(n); + while (n != null) { + if (type(n) == glyph_node) { + delta = char_italic(font(n),character(n)); + } + n = vlink(n); + } + } else { + while (n != null) { + if (type(n) == fence_noad) { + if (delimiteritalic(n) > delta) { + /*tex we can have dummies, the period ones */ + delta = delimiteritalic(n); + } + } + n = vlink(n); + } + } + } + } else { + n = nucleus(q); + if (type(n) == math_char_node) { + delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n)); + } + } + } + } + width(v) = width(y); + if (width(x) > width(v)) + width(v) = width(x); + if (width(z) > width(v)) + width(v) = width(z); + x = rebox(x, width(v)); + y = rebox(y, width(v)); + z = rebox(z, width(v)); + shift_amount(x) = half(delta); + shift_amount(z) = -shift_amount(x); + /*tex v is the still empty target */ + height(v) = height(y); + depth(v) = depth(y); + /*tex + + Attach the limits to |y| and adjust |height(v)|, |depth(v)| to + account for their presence. + + We use |shift_up| and |shift_down| in the following program for the + amount of glue between the displayed operator |y| and its limits |x| + and |z|. + + The vlist inside box |v| will consist of |x| followed by |y| followed + by |z|, with kern nodes for the spaces between and around them; + |b| is baseline and |v| is the minumum gap. + + */ + if (supscr(q) == null) { + list_ptr(x) = null; + flush_node(x); + list_ptr(v) = y; + } else { + shift_up = limit_above_bgap(cur_style) - depth(x); + if (shift_up < limit_above_vgap(cur_style)) + shift_up = limit_above_vgap(cur_style); + p = new_kern(shift_up); + reset_attributes(p, node_attr(q)); + couple_nodes(p,y); + couple_nodes(x,p); + p = new_kern(limit_above_kern(cur_style)); + reset_attributes(p, node_attr(q)); + couple_nodes(p,x); + list_ptr(v) = p; + height(v) = height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + shift_up; + } + if (subscr(q) == null) { + list_ptr(z) = null; + flush_node(z); + } else { + shift_down = limit_below_bgap(cur_style) - height(z); + if (shift_down < limit_below_vgap(cur_style)) + shift_down = limit_below_vgap(cur_style); + p = new_kern(shift_down); + reset_attributes(p, node_attr(q)); + couple_nodes(y,p); + couple_nodes(p,z); + p = new_kern(limit_below_kern(cur_style)); + reset_attributes(p, node_attr(q)); + couple_nodes(z,p); + depth(v) = depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + shift_down; + } + if (subscr(q) != null) { + math_list(subscr(q)) = null; + flush_node(subscr(q)); + subscr(q) = null; + } + if (supscr(q) != null) { + math_list(supscr(q)) = null; + flush_node(supscr(q)); + supscr(q) = null; + } + assign_new_hlist(q, v); + if (do_new_math(cur_f)) { + delta = 0; + } + } + return delta; +} + +/*tex + + A ligature found in a math formula does not create a ligature, because there + is no question of hyphenation afterwards; the ligature will simply be stored + in an ordinary |glyph_node|, after residing in an |ord_noad|. + + The |type| is converted to |math_text_char| here if we would not want to + apply an italic correction to the current character unless it belongs to a + math font (i.e., a font with |space=0|). + + No boundary characters enter into these ligatures. + +*/ + +#define simple_char_noad(p) (\ + (p != null) && \ + (type(p) == simple_noad) && \ + (subtype(p) <= punct_noad_type) && \ + (type(nucleus(p)) == math_char_node) \ +) + +#define same_nucleus_fam(p,q) \ + (math_fam(nucleus(p)) == math_fam(nucleus(q))) + +static void make_ord(pointer q) +{ + /*tex the left-side character for lig/kern testing */ + int a; + /*tex temporary registers for list manipulation */ + pointer p, r, s; + /*tex a kern */ + scaled k; + /*tex a ligature */ + liginfo lig; + RESTART: + if (subscr(q) == null && supscr(q) == null && type(nucleus(q)) == math_char_node) { + p = vlink(q); + if (simple_char_noad(p) && same_nucleus_fam(p,q)) { + type(nucleus(q)) = math_text_char_node; + fetch(nucleus(q)); + a = cur_c; + /*tex add italic correction */ + if (do_new_math(cur_f) && (char_italic(cur_f,math_character(nucleus(q))) != 0)) { + p = new_kern(char_italic(cur_f,math_character(nucleus(q)))); + subtype(p) = italic_kern; + reset_attributes(p, node_attr(q)); + couple_nodes(p,vlink(q)); + couple_nodes(q,p); + return; + } + /*tex construct ligatures, quite unlikely in new math fonts */ + if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) { + cur_c = math_character(nucleus(p)); + /*tex + + If character |a| has a kern with |cur_c|, attach the kern + after~|q|; or if it has a ligature with |cur_c|, combine + noads |q| and~|p| appropriately; then |return| if the cursor + has moved past a noad, or |goto restart|. + + Note that a ligature between an |ord_noad| and another kind + of noad is replaced by an |ord_noad|, when the two noads + collapse into one. + + We could make a parenthesis (say) change shape when it + follows certain letters. Presumably a font designer will + define such ligatures only when this convention makes sense. + + */ + if (disable_lig_par == 0 && has_lig(cur_f, a)) { + lig = get_ligature(cur_f, a, cur_c); + if (is_valid_ligature(lig)) { + /*tex allow a way out of infinite ligature loop */ + check_interrupt(); + switch (lig_type(lig)) { + case 1: + /*tex \.{=:\char`\|} */ + case 5: + /*tex \.{=:\char`\|>} */ + math_character(nucleus(q)) = lig_replacement(lig); + break; + case 2: + /*tex \.{\char`\|=:} */ + case 6: + /*tex \.{\char`\|=:>} */ + math_character(nucleus(p)) = lig_replacement(lig); + break; + case 3: + /*tex \.{\char`\|=:\char`\|} */ + case 7: + /*tex \.{\char`\|=:\char`\|>} */ + case 11: + /*tex \.{\char`\|=:\char`\|>>} */ + r = new_noad(); + reset_attributes(r, node_attr(q)); + s = new_node(math_char_node, 0); + reset_attributes(s, node_attr(q)); + nucleus(r) = s; + math_character(nucleus(r)) = lig_replacement(lig); + math_fam(nucleus(r)) = math_fam(nucleus(q)); + couple_nodes(q,r); + couple_nodes(r,p); + if (lig_type(lig) < 11) { + type(nucleus(r)) = math_char_node; + } else { + /*tex prevent combination */ + type(nucleus(r)) = math_text_char_node; + } + break; + default: + try_couple_nodes(q,vlink(p)); + math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */ + subscr(q) = subscr(p); + supscr(q) = supscr(p); + subscr(p) = null ; + supscr(p) = null ; + flush_node(p); + break; + } + if (lig_type(lig) > 3) + return; + type(nucleus(q)) = math_char_node; + goto RESTART; + } + } + if (disable_kern_par == 0 && has_kern(cur_f, a)) { + /*tex todo: should this use mathkerns? */ + k = get_kern(cur_f, a, cur_c); + if (k != 0) { + p = new_kern(k); + reset_attributes(p, node_attr(q)); + couple_nodes(p,vlink(q)); + couple_nodes(q,p); + return; + } + } + } + } + } +} + +/*tex + + If the fonts for the left and right bits of a mathkern are not both new-style + fonts, then return a sentinel value meaning: please use old-style italic + correction placement + +*/ + +#define MATH_KERN_NOT_FOUND 0x7FFFFFFF + +/*tex + + This function tries to find the kern needed for proper cut-ins. The left side + doesn't move, but the right side does, so the first order of business is to + create a staggered fence line on the left side of the right character. + + The microsoft spec says that there are four quadrants, but the actual images + say. + +*/ + +static scaled math_kern_at(internal_font_number f, int c, int side, int v) +{ + int h, k, numkerns; + scaled *kerns_heights; + scaled kern = 0; + /*tex Known to exist: */ + charinfo *co = char_info(f, c); + numkerns = get_charinfo_math_kerns(co, side); + if (numkerns == 0) + return kern; + if (side == top_left_kern) { + kerns_heights = co->top_left_math_kern_array; + } else if (side == bottom_left_kern) { + kerns_heights = co->bottom_left_math_kern_array; + } else if (side == top_right_kern) { + kerns_heights = co->top_right_math_kern_array; + } else if (side == bottom_right_kern) { + kerns_heights = co->bottom_right_math_kern_array; + } else { + /*tex Not reached: */ + confusion("math_kern_at"); + kerns_heights = NULL; + } + if (v < kerns_heights[0]) + return kerns_heights[1]; + for (k = 0; k < numkerns; k++) { + h = kerns_heights[(k * 2)]; + kern = kerns_heights[(k * 2) + 1]; + if (h > v) { + return kern; + } + } + return kern; +} + +static scaled find_math_kern(internal_font_number l_f, int l_c, internal_font_number r_f, int r_c, int cmd, scaled shift) +{ + scaled corr_height_top = 0, corr_height_bot = 0; + scaled krn_l = 0, krn_r = 0, krn = 0; + if ((!do_new_math(l_f)) || (!do_new_math(r_f)) || (!char_exists(l_f,l_c)) || (!char_exists(r_f,r_c))) + return MATH_KERN_NOT_FOUND; + if (cmd == sup_mark_cmd) { + corr_height_top = char_height(l_f, l_c); + /*tex bottom of superscript */ + corr_height_bot = -char_depth(r_f, r_c) + shift; + krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top); + krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top); + krn = (krn_l + krn_r); + krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot); + krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot); + if ((krn_l + krn_r) < krn) + krn = (krn_l + krn_r); + return (krn); + } else if (cmd == sub_mark_cmd) { + /*tex top of subscript */ + corr_height_top = char_height(r_f, r_c) - shift; + corr_height_bot = -char_depth(l_f, l_c); + krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top); + krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top); + krn = (krn_l + krn_r); + krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot); + krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot); + if ((krn_l + krn_r) < krn) + krn = (krn_l + krn_r); + return (krn); + } else { + confusion("find_math_kern"); + } + /*tex Not reached: */ + return 0; +} + +/*tex Just a small helper: */ + +static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2, halfword subtyp) +{ + pointer y; + pointer z = new_kern(delta2); + if (subtyp != 0) { + subtype(z) = subtyp; + } + reset_attributes(z, node_attr(q)); + if (new_hlist(q) == null) { + /*tex this is somewhat weird */ + new_hlist(q) = z; + } else { + y = new_hlist(q); + while (vlink(y) != null) + y = vlink(y); + couple_nodes(y,z); + } + return new_hlist(q); +} + +/*tex + + The purpose of |make_scripts(q,it)| is to attach the subscript and/or + superscript of noad |q| to the list that starts at |new_hlist(q)|, given that + subscript and superscript aren't both empty. The superscript will be + horizontally shifted over |delta1|, the subscript over |delta2|. + + We set |shift_down| and |shift_up| to the minimum amounts to shift the + baseline of subscripts and superscripts based on the given nucleus. + + Note: We need to look at a character but also at the first one in a sub list + and there we ignore leading kerns and glue. Elsewhere is code that removes + kerns assuming that is italic correction. The heuristics are unreliable for + the new fonts so eventualy there will be an option to ignore such + corrections. + +*/ + +#define analyze_script(init,su_n,su_f,su_c) do { \ + su_n = init; \ + if (su_n != null) { \ + if (math_script_char_mode_par > 0 && type(su_n) == math_char_node) { \ + fetch(su_n); \ + if (char_exists(cur_f, cur_c)) { \ + su_f = cur_f; \ + su_c = cur_c; \ + } else { \ + su_n = null; \ + } \ + } else if (math_script_box_mode_par > 0 && type(su_n) == sub_mlist_node) { \ + su_n = math_list(su_n); \ + while (su_n != null) { \ + if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \ + su_n = vlink(su_n); \ + } else if (type(su_n) == simple_noad) { \ + su_n = nucleus(su_n); \ + if (type(su_n) == math_char_node) { \ + fetch(su_n); \ + if (char_exists(cur_f, cur_c)) { \ + su_f = cur_f; \ + su_c = cur_c; \ + } else { \ + su_n = null; \ + } \ + } else { \ + su_n = null; \ + } \ + break; \ + } else { \ + su_n = null; \ + break; \ + } \ + } \ + } else if (type(su_n) == sub_box_node) { \ + su_n = math_list(su_n); \ + if (su_n != null) { \ + if (type(su_n) == hlist_node) { \ + su_n = list_ptr(su_n); \ + } \ + if (su_n != null) { \ + if (math_script_box_mode_par == 2) { \ + while (su_n != null) { \ + if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \ + su_n = vlink(su_n); \ + } else if (type(su_n) == glyph_node) { \ + if (char_exists(font(su_n), character(su_n))) { \ + su_f = font(su_n); \ + su_c = character(su_n); \ + } else { \ + su_n = null; \ + } \ + break ; \ + } else { \ + su_n = null; \ + break; \ + } \ + } \ + } else if (math_script_box_mode_par == 3) { \ + int boundary = -1; \ + while (su_n != null) { \ + if ((type(su_n) == boundary_node) && (subtype(su_n) == user_boundary)) { \ + boundary = boundary_value(su_n); \ + su_n = vlink(su_n); \ + } else if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \ + su_n = vlink(su_n); \ + } else if ((boundary > -1) && (type(su_n) == glyph_node)) { \ + if (char_exists(font(su_n), character(su_n))) { \ + su_f = font(su_n); \ + su_c = character(su_n); \ + } else { \ + su_n = null; \ + } \ + break ; \ + } else { \ + su_n = null; \ + break; \ + } \ + } \ + } \ + } \ + } else { \ + su_n = null; \ + } \ + } else { \ + su_n = null; \ + } \ + } \ + } while (0) \ + +#define x_su_style(n,cur_style,su_style) \ + (noadoptionnosubscript(n) ? cur_style : su_style(cur_style)) + +static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift) +{ + pointer x, y, z; + scaled shift_up, shift_down, clr; + scaled delta1, delta2; + halfword sub_n, sup_n, subtyp; + internal_font_number sub_f, sup_f; + int sub_c, sup_c; + sub_n = null; + sup_n = null; + sub_f = 0; + sup_f = 0; + sub_c = 0; + sup_c = 0; + delta1 = it; + delta2 = 0; + subtyp = 0; + switch (type(nucleus(q))) { + case math_char_node: + case math_text_char_node: + if ((subscr(q) == null) && (delta1 != 0)) { + /*tex todo: selective italic correction */ + x = new_kern(delta1); + subtype(x) = italic_kern; + reset_attributes(x, node_attr(nucleus(q))); + couple_nodes(p,x); + delta1 = 0; + } + } + assign_new_hlist(q, p); + if (is_char_node(p)) { + shift_up = 0; + shift_down = 0; + } else { + z = hpack(p, 0, additional, -1); + shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */ + shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */ + list_ptr(z) = null; + flush_node(z); + } + if (is_char_node(p)) { + /*tex We look at the subscript character (_i) or first character in a list (_{ij}). */ + analyze_script(subscr(q),sub_n,sub_f,sub_c); + /*tex We look at the superscript character (^i) or first character in a list (^{ij}). */ + analyze_script(supscr(q),sup_n,sup_f,sup_c); + } + if (supscr(q) == null) { + /*tex + + Construct a subscript box |x| when there is no superscript. When + there is a subscript without a superscript, the top of the subscript + should not exceed the baseline plus four-fifths of the x-height. + + */ + x = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style, math_sub_list); + width(x) = width(x) + space_after_script(cur_style); + switch (math_scripts_mode_par) { + case 1: + shift_down = sub_shift_down(cur_style) ; + break; + case 2: + shift_down = sub_sup_shift_down(cur_style) ; + break; + case 3: + shift_down = sub_sup_shift_down(cur_style) ; + break; + case 4: + shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ; + break; + case 5: + shift_down = sub_shift_down(cur_style) ; + break; + default: + if (shift_down < sub_shift_down(cur_style)) + shift_down = sub_shift_down(cur_style); + clr = height(x) - sub_top_max(cur_style); + if (shift_down < clr) + shift_down = clr; + break; + } + shift_amount(x) = shift_down; + /*tex Now find and correct for horizontal shift. */ + subtyp = 0; + if (sub_n != null) { + delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down); + if (delta2 == MATH_KERN_NOT_FOUND) { + delta2 = subshift ; + } else { + delta2 = delta2 + subshift ; + subtyp = font_kern; + } + } else { + delta2 = subshift ; + } + if (delta2 != 0) { + p = attach_hkern_to_new_hlist(q, delta2, subtyp); + } + } else { + /*tex + + Construct a superscript box |x|. The bottom of a superscript should + never descend below the baseline plus one-fourth of the x-height. + + */ + x = clean_box(supscr(q), (noadoptionnosupscript(q) ? cur_style : sup_style(cur_style)), cur_style, math_sup_list); + width(x) = width(x) + space_after_script(cur_style); + switch (math_scripts_mode_par) { + case 1: + shift_up = sup_shift_up(cur_style); + break; + case 2: + shift_up = sup_shift_up(cur_style) ; + break; + case 3: + shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style) - sub_shift_down(cur_style) ; + break; + case 4: + shift_up = sup_shift_up(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ; + break; + case 5: + shift_up = sup_shift_up(cur_style) + sub_sup_shift_down(cur_style)-sub_shift_down(cur_style) ; + break; + default: + clr = sup_shift_up(cur_style); + if (shift_up < clr) + shift_up = clr; + clr = depth(x) + sup_bottom_min(cur_style); + if (shift_up < clr) + shift_up = clr; + break; + } + if (subscr(q) == null) { + shift_amount(x) = -shift_up; + /*tex Now find and correct for horizontal shift. */ + subtyp = 0; + if (sup_n != null) { + clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up); + if (clr == MATH_KERN_NOT_FOUND) { + clr = supshift ; + } else { + clr = clr + supshift ; + subtyp = font_kern; + } + } else { + clr = supshift; + } + if (clr != 0) { + p = attach_hkern_to_new_hlist(q, clr, subtyp); + } + } else { + /*tex + + Construct a sub/superscript combination box |x|, with the + superscript offset by |delta|. When both subscript and + superscript are present, the subscript must be separated from the + superscript by at least four times |default_rule_thickness| If + this condition would be violated, the subscript moves down, after + which both subscript and superscript move up so that the bottom + of the superscript is at least as high as the baseline plus + four-fifths of the x-height. + + */ + y = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style, math_sub_list); + width(y) = width(y) + space_after_script(cur_style); + switch (math_scripts_mode_par) { + case 1: + shift_down = sub_shift_down(cur_style) ; + break; + case 2: + shift_down = sub_sup_shift_down(cur_style) ; + break; + case 3: + shift_down = sub_sup_shift_down(cur_style) ; + break; + case 4: + shift_down = sub_shift_down(cur_style) + half(sub_sup_shift_down(cur_style)-sub_shift_down(cur_style)) ; + break; + case 5: + shift_down = sub_shift_down(cur_style) ; + break; + default: + if (shift_down < sub_sup_shift_down(cur_style)) + shift_down = sub_sup_shift_down(cur_style); + clr = subsup_vgap(cur_style) - ((shift_up - depth(x)) - (height(y) - shift_down)); + if (clr > 0) { + shift_down = shift_down + clr; + clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x)); + if (clr > 0) { + shift_up = shift_up + clr; + shift_down = shift_down - clr; + } + } + break; + } + /*tex Now find and correct for horizontal shift. */ + subtyp = 0; + if (sub_n != null) { + delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down); + if (delta2 == MATH_KERN_NOT_FOUND) { + delta2 = subshift ; + } else { + delta2 = delta2 + subshift ; + subtyp = font_kern; + } + } else { + delta2 = subshift ; + } + if (delta2 != 0) { + p = attach_hkern_to_new_hlist(q, delta2, subtyp); + } + /*tex + + Now the horizontal shift for the superscript; the superscript is + also to be shifted by |delta1| (the italic correction). + + */ + clr = MATH_KERN_NOT_FOUND; + if (sup_n != null) { + clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up); + } + /*tex + + The delta can already have been applied and now be 0. + + */ + if (delta2 == MATH_KERN_NOT_FOUND) + delta2 = - supshift ; + else + delta2 = delta2 - supshift ; + if (clr != MATH_KERN_NOT_FOUND) { + shift_amount(x) = clr + delta1 - delta2; + } else { + shift_amount(x) = delta1 - delta2; + } + /*tex todo: only if kern != 0 */ + p = new_kern((shift_up - depth(x)) - (height(y) - shift_down)); + reset_attributes(p, node_attr(q)); + couple_nodes(x,p); + couple_nodes(p,y); + /*tex We end up with funny dimensions. */ + x = vpackage(x, 0, additional, max_dimen, math_direction_par); + reset_attributes(x, node_attr(q)); + subtype(x) = math_scripts_list; + shift_amount(x) = shift_down; + } + } + if (new_hlist(q) == null) { + new_hlist(q) = x; + } else { + p = new_hlist(q); + while (vlink(p) != null) + p = vlink(p); + couple_nodes(p,x); + } + if (subscr(q) != null) { + math_list(subscr(q)) = null; + flush_node(subscr(q)); + subscr(q) = null; + } + if (supscr(q) != null) { + math_list(supscr(q)) = null; + flush_node(supscr(q)); + supscr(q) = null; + } +} + +/*tex + + The |make_left_right| function constructs a left or right delimiter of the + required size and returns the value |open_noad| or |close_noad|. The + |left_noad_side| and |right_noad_side| will both be based on the original + |style|, so they will have consistent sizes. + +*/ + +static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h) +{ + scaled delta; + pointer tmp, lst; + scaled ic = 0; + boolean stack = false; + boolean axis = false; + int same = subtype(q); + setup_cur_size(style); + if ((delimiterheight(q)!=0) || (delimiterdepth(q)!=0)) { + + delta = delimiterheight(q) + delimiterdepth(q); + tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, false, &stack, &ic, &same); + delimiteritalic(q) = ic; + /*tex + + Beware, a stacked delimiter has a shift but no corrected height/depth + (yet). + + */ + if (stack) { + shift_amount(tmp) = delimiterdepth(q); + } + if (delimiterexact(q)) { + delimiterheight(q) = height(tmp) - shift_amount(tmp); + delimiterdepth(q) = depth(tmp) + shift_amount(tmp); + } + if (delimiteraxis(q)) { + delimiterheight(q) += math_axis_size(cur_size); + delimiterdepth(q) -= math_axis_size(cur_size); + shift_amount(tmp) -= math_axis_size(cur_size); + } + lst = new_node(hlist_node,0); + reset_attributes(lst, node_attr(q)); + box_dir(lst) = dir_TLT ; + height(lst) = delimiterheight(q); + depth(lst) = delimiterdepth(q); + width(lst) = width(tmp); + list_ptr(lst) = tmp; + tmp = lst ; + } else { + axis = ! delimiternoaxis(q); + delta = get_delimiter_height(max_d,max_h,axis); + tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, axis, &stack, &ic, &same); + delimiteritalic(q) = ic; + } + delimiter(q) = null; + assign_new_hlist(q, tmp); + delimitersamesize(q) = same; + if (delimiterclass(q) >= ord_noad_type) { + if (delimiterclass(q) <= inner_noad_type) { + return delimiterclass(q); + } else { + return ord_noad_type; + } + } else if (subtype(q) == left_noad_side) { + return open_noad_type; + } else { + return close_noad_type; + } +} + +#define TEXT_STYLES(A,B) do { \ + def_math_param(A,display_style,(B),level_one); \ + def_math_param(A,cramped_display_style,(B),level_one); \ + def_math_param(A,text_style,(B),level_one); \ + def_math_param(A,cramped_text_style,(B),level_one); \ + } while (0) + +#define SCRIPT_STYLES(A,B) do { \ + def_math_param(A,script_style,(B),level_one); \ + def_math_param(A,cramped_script_style,(B),level_one); \ + def_math_param(A,script_script_style,(B),level_one); \ + def_math_param(A,cramped_script_script_style,(B),level_one); \ + } while (0) + +#define ALL_STYLES(A,B) do { \ + TEXT_STYLES(A,(B)); \ + SCRIPT_STYLES(A,(B)); \ + } while (0) + +#define SPLIT_STYLES(A,B,C) do { \ + TEXT_STYLES(A,(B)); \ + SCRIPT_STYLES(A,(C)); \ + } while (0) + +void initialize_math_spacing(void) +{ + ALL_STYLES (math_param_ord_ord_spacing, 0); + ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code); + SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0); + SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip_code, 0); + ALL_STYLES (math_param_ord_open_spacing, 0); + ALL_STYLES (math_param_ord_close_spacing, 0); + ALL_STYLES (math_param_ord_punct_spacing, 0); + SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip_code, 0); + + ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip_code); + ALL_STYLES (math_param_op_op_spacing, thin_mu_skip_code); + ALL_STYLES (math_param_op_bin_spacing, 0); + SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip_code, 0); + ALL_STYLES (math_param_op_open_spacing, 0); + ALL_STYLES (math_param_op_close_spacing, 0); + ALL_STYLES (math_param_op_punct_spacing, 0); + SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip_code, 0); + + SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip_code, 0); + SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip_code, 0); + ALL_STYLES (math_param_bin_bin_spacing, 0); + ALL_STYLES (math_param_bin_rel_spacing, 0); + SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip_code, 0); + ALL_STYLES (math_param_bin_close_spacing, 0); + ALL_STYLES (math_param_bin_punct_spacing, 0); + SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip_code, 0); + + SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip_code, 0); + SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip_code, 0); + ALL_STYLES (math_param_rel_bin_spacing, 0); + ALL_STYLES (math_param_rel_rel_spacing, 0); + SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip_code, 0); + ALL_STYLES (math_param_rel_close_spacing, 0); + ALL_STYLES (math_param_rel_punct_spacing, 0); + SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip_code, 0); + + ALL_STYLES (math_param_open_ord_spacing, 0); + ALL_STYLES (math_param_open_op_spacing, 0); + ALL_STYLES (math_param_open_bin_spacing, 0); + ALL_STYLES (math_param_open_rel_spacing, 0); + ALL_STYLES (math_param_open_open_spacing, 0); + ALL_STYLES (math_param_open_close_spacing, 0); + ALL_STYLES (math_param_open_punct_spacing, 0); + ALL_STYLES (math_param_open_inner_spacing, 0); + + ALL_STYLES (math_param_close_ord_spacing, 0); + ALL_STYLES (math_param_close_op_spacing, thin_mu_skip_code); + SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip_code, 0); + SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip_code, 0); + ALL_STYLES (math_param_close_open_spacing, 0); + ALL_STYLES (math_param_close_close_spacing, 0); + ALL_STYLES (math_param_close_punct_spacing, 0); + SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip_code, 0); + + SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip_code, 0); + ALL_STYLES (math_param_punct_bin_spacing, 0); + SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip_code, 0); + + SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip_code, 0); + ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip_code); + SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip_code, 0); + SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip_code, 0); + SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip_code, 0); + ALL_STYLES (math_param_inner_close_spacing, 0); + SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0); + SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0); +} + +#define both_types(A,B) ((A)*16+(B)) + +static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu) +{ + int x = -1; + pointer z = null; + if (l_type == op_noad_type_limits || l_type == op_noad_type_no_limits) + l_type = op_noad_type_normal; + if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits) + r_type = op_noad_type_normal; + switch (both_types(l_type, r_type)) { + case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break; + case both_types(ord_noad_type, op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break; + case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break; + case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break; + case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break; + case both_types(ord_noad_type, close_noad_type ): x = get_math_param(math_param_ord_close_spacing,mstyle); break; + case both_types(ord_noad_type, punct_noad_type ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break; + case both_types(ord_noad_type, inner_noad_type ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break; + case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break; + case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break; + /* */ + case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break; + case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break; + case both_types(op_noad_type_normal, close_noad_type ): x = get_math_param(math_param_op_close_spacing,mstyle); break; + case both_types(op_noad_type_normal, punct_noad_type ): x = get_math_param(math_param_op_punct_spacing,mstyle); break; + case both_types(op_noad_type_normal, inner_noad_type ): x = get_math_param(math_param_op_inner_spacing,mstyle); break; + case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break; + case both_types(bin_noad_type, op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break; + case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break; + /* */ + case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(bin_noad_type, close_noad_type ): x = get_math_param(math_param_bin_close_spacing,mstyle); break; + case both_types(bin_noad_type, punct_noad_type ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break; + /* */ + case both_types(bin_noad_type, inner_noad_type ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break; + case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break; + case both_types(rel_noad_type, op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break; + /* */ + case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break; + case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break; + case both_types(rel_noad_type, close_noad_type ): x = get_math_param(math_param_rel_close_spacing,mstyle); break; + case both_types(rel_noad_type, punct_noad_type ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break; + case both_types(rel_noad_type, inner_noad_type ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break; + case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break; + case both_types(open_noad_type, op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break; + /* */ + case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break; + case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break; + case both_types(open_noad_type, close_noad_type ): x = get_math_param(math_param_open_close_spacing,mstyle); break; + case both_types(open_noad_type, punct_noad_type ): x = get_math_param(math_param_open_punct_spacing,mstyle); break; + case both_types(open_noad_type, inner_noad_type ): x = get_math_param(math_param_open_inner_spacing,mstyle); break; + case both_types(close_noad_type, ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break; + case both_types(close_noad_type, op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break; + case both_types(close_noad_type, bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break; + case both_types(close_noad_type, rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break; + case both_types(close_noad_type, open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break; + case both_types(close_noad_type, close_noad_type ): x = get_math_param(math_param_close_close_spacing,mstyle); break; + case both_types(close_noad_type, punct_noad_type ): x = get_math_param(math_param_close_punct_spacing,mstyle); break; + case both_types(close_noad_type, inner_noad_type ): x = get_math_param(math_param_close_inner_spacing,mstyle); break; + case both_types(punct_noad_type, ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break; + case both_types(punct_noad_type, op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(punct_noad_type, bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break; + /* */ + case both_types(punct_noad_type, rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break; + case both_types(punct_noad_type, open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break; + case both_types(punct_noad_type, close_noad_type ): x = get_math_param(math_param_punct_close_spacing,mstyle); break; + case both_types(punct_noad_type, punct_noad_type ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break; + case both_types(punct_noad_type, inner_noad_type ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break; + case both_types(inner_noad_type, ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break; + case both_types(inner_noad_type, op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break; + case both_types(inner_noad_type, bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break; + case both_types(inner_noad_type, rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break; + case both_types(inner_noad_type, open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break; + case both_types(inner_noad_type, close_noad_type ): x = get_math_param(math_param_inner_close_spacing,mstyle); break; + case both_types(inner_noad_type, punct_noad_type ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break; + case both_types(inner_noad_type, inner_noad_type ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break; + } + if (x < 0) { + confusion("mathspacing"); + } + if (x != 0) { + if (x <= thick_mu_skip_code) { + /*tex trap thin/med/thick settings cf.\ old \TeX */ + z = math_glue(glue_par(x), mmu); + /*tex store a symbolic subtype */ + subtype(z) = (quarterword) (x + 1); + } else { + z = math_glue(x, mmu); + } + } + return z; +} + +static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same) +{ + pointer p = null; + pointer t = null; + if (same != NULL) { + *same = 0; + } + switch (type(nucleus(q))) { + case math_char_node: + case math_text_char_node: + fetch(nucleus(q)); + if (char_exists(cur_f, cur_c)) { + /*tex we could look at neighbours */ + if (do_new_math(cur_f)) { + /*tex cf spec only the last one */ + *delta = 0 ; + } else { + *delta = char_italic(cur_f, cur_c); + } + p = new_glyph(cur_f, cur_c); + protect_glyph(p); + reset_attributes(p, node_attr(nucleus(q))); + if (do_new_math(cur_f)) { + if (get_char_cat_code(cur_c) == 11) { + /*tex no italic correction in mid-word of text font */ + *delta = 0; + } + } else { + /*tex no italic correction in mid-word of text font */ + if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) { + *delta = 0; + } + } + /*tex so we only add italic correction when we have no scripts */ + if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) { + pointer x = new_kern(*delta); + subtype(x) = italic_kern; + reset_attributes(x, node_attr(nucleus(q))); + couple_nodes(p,x); + *delta = 0; + } else if (do_new_math(cur_f)) { + /*tex Needs checking but looks ok. It must be more selective. */ + *delta = char_italic(cur_f, cur_c); + } + } + break; + case sub_box_node: + p = math_list(nucleus(q)); + break; + case sub_mlist_node: + t = math_list(nucleus(q)); + /*tex Recursive call: */ + mlist_to_hlist(t, false, cur_style); + if (same != NULL && type(t) == fence_noad && delimitersamesize(t)) { + *same = delimitersamesize(t) ; + } + setup_cur_size(cur_style); + p = hpack(vlink(temp_head), 0, additional, -1); + reset_attributes(p, node_attr(nucleus(q))); + break; + default: + confusion("mlist2"); + } + return p; +} + +/*tex + + Here is the overall plan of |mlist_to_hlist|, and the list of its local + variables. + +*/ + +void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) +{ + /*tex runs through the mlist */ + pointer q = mlist; + /*tex the most recent noad preceding |q| */ + pointer r = null; + /*tex tuck global parameter away as local variable */ + int style = cur_style; + /*tex the |type| of noad |r|, or |op_noad| if |r=null| */ + int r_type = simple_noad; + /*tex the |subtype| of noad |r| if |r_type| is |fence_noad| */ + int r_subtype = op_noad_type_normal; + /*tex the effective |type| of noad |q| during the second pass */ + int t; + /*tex the effective |subtype| of noad |q| during the second pass */ + int t_subtype; + pointer p = null; + pointer pp = null; + pointer z = null; + halfword nxt ; + int same = 0; + /*tex a penalty to be inserted */ + int pen; + /*tex a penalty to be inserted */ + int prepen; + /*tex maximum height of the list translated so far */ + scaled max_hl = 0; + /*tex maximum depth of the list translated so far */ + scaled max_d = 0; + /*tex italic correction offset for subscript and superscript */ + scaled delta; + /*tex the math unit width corresponding to |cur_size| */ + scaled cur_mu; + r_subtype = op_noad_type_normal; + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad_size(cur_size), 18); + if (math_penalties_mode_par) { + /*tex + We could do this via the callback but it's nice to have it as + primitive too. + */ + penalties = 1; + } + while (q != null) { + /*tex + + We use the fact that no character nodes appear in an mlist, hence the + field |type(q)| is always present.One of the things we must do on the + first pass is change a |bin_noad| to an |ord_noad| if the |bin_noad| + is not in the context of a binary operator. The values of |r| and + |r_type| make this fairly easy. + + */ + RESWITCH: + delta = 0; + nxt = vlink(q); + switch (type(q)) { + case simple_noad: + switch (subtype(q)) { + case bin_noad_type: + switch (r_type) { + case simple_noad: + switch (r_subtype) { + case bin_noad_type: + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + case rel_noad_type: + case open_noad_type: + case punct_noad_type: + subtype(q) = ord_noad_type; + goto RESWITCH; + break; + } + break; + case fence_noad: + if (r_subtype == left_noad_side) { + /*tex So these can best be the same size. */ + subtype(q) = ord_noad_type; + goto RESWITCH; + } + break; + } + break; + case over_noad_type: + make_over(q, cur_style, cur_size, math_rules_fam_par); + break; + case under_noad_type: + make_under(q, cur_style, cur_size, math_rules_fam_par); + break; + case vcenter_noad_type: + make_vcenter(q); + break; + case rel_noad_type: + case close_noad_type: + case punct_noad_type: + if (r_type == simple_noad && r_subtype == bin_noad_type) { + /*tex Assumes the same size; can't this go. */ + type(r) = simple_noad; + subtype(r) = ord_noad_type; + } + break; + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + delta = make_op(q, cur_style); + if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits)) + goto CHECK_DIMENSIONS; + break; + case ord_noad_type: + make_ord(q); + break; + case open_noad_type: + case inner_noad_type: + break; + } + break; + case fence_noad: + if (subtype(q) != left_noad_side) { + if (r_type == simple_noad && r_subtype == bin_noad_type) { + /*tex Assumes the same size. */ + type(r) = simple_noad; + subtype(r) = ord_noad_type; + } + } + goto DONE_WITH_NOAD; + break; + case fraction_noad: + make_fraction(q, cur_style); + goto CHECK_DIMENSIONS; + break; + case radical_noad: + if (subtype(q) == 7) + make_hextension(q, cur_style); + else if (subtype(q) == 6) + make_delimiter_over(q, cur_style); + else if (subtype(q) == 5) + make_delimiter_under(q, cur_style); + else if (subtype(q) == 4) + make_over_delimiter(q, cur_style); + else if (subtype(q) == 3) + make_under_delimiter(q, cur_style); + else + make_radical(q, cur_style); + break; + case accent_noad: + make_math_accent(q, cur_style); + break; + case style_node: + cur_style = subtype(q); + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad_style(cur_style), 18); + goto DONE_WITH_NODE; + break; + case choice_node: + switch (cur_style / 2) { + case 0: + /*tex |display_style=0| */ + choose_mlist(display_mlist); + break; + case 1: + /*tex |text_style=2| */ + choose_mlist(text_mlist); + break; + case 2: + /*tex |script_style=4| */ + choose_mlist(script_mlist); + break; + case 3: + /*tex |script_script_style=6| */ + choose_mlist(script_script_mlist); + break; + } + flush_node_list(display_mlist(q)); + flush_node_list(text_mlist(q)); + flush_node_list(script_mlist(q)); + flush_node_list(script_script_mlist(q)); + type(q) = style_node; + subtype(q) = (quarterword) cur_style; + if (p != null) { + z = vlink(q); + couple_nodes(q,p); + while (vlink(p) != null) + p = vlink(p); + try_couple_nodes(p,z); + } + goto DONE_WITH_NODE; + break; + case ins_node: + case mark_node: + case adjust_node: + case boundary_node: + case whatsit_node: + case penalty_node: + case disc_node: + goto DONE_WITH_NODE; + break; + case rule_node: + if (height(q) > max_hl) + max_hl = height(q); + if (depth(q) > max_d) + max_d = depth(q); + goto DONE_WITH_NODE; + break; + case glue_node: + /*tex + + Conditional math glue (`\.{\\nonscript}') results in a + |glue_node| pointing to |zero_glue|, with + |subtype(q)=cond_math_glue|; in such a case the node + following will be eliminated if it is a glue or kern node and + if the current size is different from |text_size|. + + Unconditional math glue (`\.{\\muskip}') is converted to + normal glue by multiplying the dimensions by |cur_mu|. + + */ + if (subtype(q) == mu_glue) { + math_glue_to_glue(q, cur_mu); + } else if ((cur_size != text_size) && (subtype(q) == cond_math_glue)) { + p = vlink(q); + if (p != null) + if ((type(p) == glue_node) || (type(p) == kern_node)) { + if (vlink(p) != null) { + couple_nodes(q,vlink(p)); + vlink(p) = null; + } else { + vlink(q) = null; + } + flush_node_list(p); + } + } + goto DONE_WITH_NODE; + break; + case kern_node: + math_kern(q, cur_mu); + goto DONE_WITH_NODE; + break; + default: + confusion("mlist1"); + } + /*tex + + When we get to the following part of the program, we have ``fallen + through'' from cases that did not lead to |check_dimensions| or + |done_with_noad| or |done_with_node|. Thus, |q|~points to a noad + whose nucleus may need to be converted to an hlist, and whose + subscripts and superscripts need to be appended if they are present. + + If |nucleus(q)| is not a |math_char|, the variable |delta| is the + amount by which a superscript should be moved right with respect to a + subscript when both are present. + + */ + same = 0 ; + p = check_nucleus_complexity(q, &delta, cur_style, &same); + if (same) { + noadextra4(q) = same ; + } + if ((subscr(q) == null) && (supscr(q) == null)) { + /*tex + + Adding italic correction here is kind of fuzzy because some + characters already have that built in. However, we also add it in + the scripts so if it's optional here it also should be there. + + */ + if (nxt && (math_italics_mode_par > 0) && (delta != 0)) { + if (type(nxt) == simple_noad) { + switch (subtype(nxt)) { + case ord_noad_type: + case bin_noad_type: + case rel_noad_type: + case open_noad_type: + case close_noad_type: + case punct_noad_type: + delta = 0; + break; + case inner_noad_type: + if (! delimitermodeitalics) { + delta = 0; + } + break; + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + case under_noad_type: + case over_noad_type: + case vcenter_noad_type: + break; + default: + break; + } + } + if (delta != 0) { + pointer d = new_kern(delta); + subtype(d) = italic_kern; + reset_attributes(d, node_attr(q)); + couple_nodes(p,d); + } + } + assign_new_hlist(q, p); + } else { + /*tex top, bottom */ + make_scripts(q, p, delta, cur_style, 0, 0); + } + CHECK_DIMENSIONS: + z = hpack(new_hlist(q), 0, additional, -1); + if (height(z) > max_hl) + max_hl = height(z); + if (depth(z) > max_d) + max_d = depth(z); + list_ptr(z) = null; + /*tex only drop the \.{\\hbox} */ + flush_node(z); + DONE_WITH_NOAD: + r = q; + r_type = type(r); + r_subtype = subtype(r); + if (r_type == fence_noad) { + r_subtype = left_noad_side; + cur_style = style; + setup_cur_size(cur_style); + /*tex style */ + cur_mu = x_over_n(get_math_quad_size(cur_size), 18); + } + DONE_WITH_NODE: + q = vlink(q); + } + if (r_type == simple_noad && r_subtype == bin_noad_type) { + type(r) = simple_noad; + subtype(r) = ord_noad_type; + } + /*tex + + Make a second pass over the mlist, removing all noads and inserting the + proper spacing and penalties. + + We have now tied up all the loose ends of the first pass of + |mlist_to_hlist|. The second pass simply goes through and hooks + everything together with the proper glue and penalties. It also handles + the |fence_noad|s that might be present, since |max_hl| and |max_d| are + now known. Variable |p| points to a node at the current end of the final + hlist. + + */ + p = temp_head; + vlink(p) = null; + q = mlist; + r_type = 0; + r_subtype = 0; + cur_style = style; + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad_size(cur_size), 18); + NEXT_NODE: + while (q != null) { + /*tex + + If node |q| is a style node, change the style and |goto delete_q|; + otherwise if it is not a noad, put it into the hlist, advance |q|, + and |goto done|; otherwise set |s| to the size of noad |q|, set |t| + to the associated type (|ord_noad.. inner_noad|), and set |pen| to + the associated penalty. + + Just before doing the big |case| switch in the second pass, the + program sets up default values so that most of the branches are + short. + + */ + t = simple_noad; + t_subtype = ord_noad_type; + pen = inf_penalty; + prepen = inf_penalty; + switch (type(q)) { + case simple_noad: + t_subtype = subtype(q); + switch (t_subtype) { + case bin_noad_type: + pen = bin_op_penalty_par; + prepen = pre_bin_op_penalty_par; + break; + case rel_noad_type: + pen = rel_penalty_par; + prepen = pre_rel_penalty_par; + break; + case vcenter_noad_type: + case over_noad_type: + case under_noad_type: + t_subtype = ord_noad_type; + break; + } + case radical_noad: + break; + case accent_noad: + break; + case fraction_noad: + t = simple_noad; + t_subtype = inner_noad_type; + break; + case fence_noad: + t_subtype = make_left_right(q, style, max_d, max_hl); + break; + case style_node: + /*tex Change the current style and |goto delete_q| */ + cur_style = subtype(q); + setup_cur_size(cur_style); + cur_mu = x_over_n(get_math_quad_style(cur_style), 18); + goto DELETE_Q; + break; + case whatsit_node: + case penalty_node: + case rule_node: + case disc_node: + case adjust_node: + case ins_node: + case mark_node: + case glue_node: + case kern_node: + couple_nodes(p,q); + p = q; + q = vlink(q); + vlink(p) = null; + goto NEXT_NODE; + break; + default: + confusion("mlist3"); + } + /*tex Append inter-element spacing based on |r_type| and |t| */ + if (r_type > 0) { + /*tex not the first noad */ + pp = p; + if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1) { + z = math_spacing_glue(r_subtype, ord_noad_type, cur_style, cur_mu); + } else { + z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu); + } + if (z != null) { + reset_attributes(z, node_attr(p)); + couple_nodes(p,z); + p = z; + } + if (penalties && prepen < inf_penalty && type(pp) != penalty_node) { + /*tex no checking of prev node type */ + z = new_penalty(prepen,noad_penalty); + reset_attributes(z, node_attr(p)); + couple_nodes(p,z); + p = z; + } + } + /*tex + + Append any |new_hlist| entries for |q|, and any appropriate + penalties. We insert a penalty node after the hlist entries of noad + |q| if |pen| is not an ``infinite'' penalty, and if the node + immediately following |q| is not a penalty node or a |rel_noad| or + absent entirely. + + */ + if (new_hlist(q) != null) { + couple_nodes(p,new_hlist(q)); + do { + p = vlink(p); + } while (vlink(p) != null); + } + if (penalties && vlink(q) != null && pen < inf_penalty) { + r_type = type(vlink(q)); + r_subtype = subtype(vlink(q)); + if (r_type != penalty_node && (r_type != simple_noad || r_subtype != rel_noad_type)) { + z = new_penalty(pen,noad_penalty); + reset_attributes(z, node_attr(q)); + couple_nodes(p,z); + p = z; + } + } + if (type(q) == fence_noad && subtype(q) == right_noad_side) { + t = simple_noad; + t_subtype = open_noad_type; + } + r_type = t; + r_subtype = t_subtype; + DELETE_Q: + r = q; + q = vlink(q); + /*tex + + The m-to-hlist conversion takes place in-place, so the various + dependant fields may not be freed (as would happen if |flush_node| + was called). A low-level |free_node| is easier than attempting to + nullify such dependant fields for all possible node and noad types. + + */ + if (nodetype_has_attributes(type(r))) { + delete_attribute_ref(node_attr(r)); + } + reset_node_properties(r); + free_node(r, get_node_size(type(r), subtype(r))); + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.h b/Build/source/texk/web2c/luatexdir/tex/mlist.h new file mode 100644 index 00000000000..dc08e0cdd6f --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mlist.h @@ -0,0 +1,41 @@ +/* mlist.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef MLIST_H +# define MLIST_H 1 + +extern int cur_size; + +extern halfword del_width; +extern halfword del_height; +extern halfword del_depth; +extern halfword del_shift; + +extern void run_mlist_to_hlist(halfword, boolean, int); +extern void mlist_to_hlist(halfword, boolean, int); +extern void fixup_math_parameters(int fam_id, int size_id, int f, int lvl); + +extern scaled get_math_quad_style(int a); +extern scaled get_math_quad_size(int a); + +extern pointer make_extensible(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.c b/Build/source/texk/web2c/luatexdir/tex/nesting.c new file mode 100644 index 00000000000..bbad50b3cdb --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/nesting.c @@ -0,0 +1,429 @@ +/* + +nesting.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex These are for |show_activities|: */ + +#define page_goal page_so_far[0] + +/*tex + +\TeX\ is typically in the midst of building many lists at once. For example, when +a math formula is being processed, \TeX\ is in math mode and working on an mlist; +this formula has temporarily interrupted \TeX\ from being in horizontal mode and +building the hlist of a paragraph; and this paragraph has temporarily interrupted +\TeX\ from being in vertical mode and building the vlist for the next page of a +document. Similarly, when a \.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is +temporarily interrupted from working in restricted horizontal mode, and it enters +internal vertical mode. The ``semantic nest'' is a stack that keeps track of what +lists and modes are currently suspended. + +At each level of processing we are in one of six modes: + +|vmode| stands for vertical mode (the page builder); + +|hmode| stands for horizontal mode (the paragraph builder); + +|mmode| stands for displayed formula mode; + +|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox}); + +|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox}); + +|-mmode| stands for math formula mode (not displayed). + +The mode is temporarily set to zero while processing \.{\\write} texts in the +|ship_out| routine. + +Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that \TeX's ``big +semantic switch'' can select the appropriate thing to do by computing the value +|abs(mode)+cur_cmd|, where |mode| is the current mode and |cur_cmd| is the +current command code. + +*/ + +static const char *string_mode(int m) +{ + if (m > 0) { + switch (m / (max_command_cmd + 1)) { + case 0: + return "vertical mode"; + break; + case 1: + return "horizontal mode"; + break; + case 2: + return "display math mode"; + break; + default: + break; + } + } else if (m == 0) { + return "no mode"; + } else { + switch ((-m) / (max_command_cmd + 1)) { + case 0: + return "internal vertical mode"; + break; + case 1: + return "restricted horizontal mode"; + break; + case 2: + return "math mode"; + break; + default: + break; + } + } + return "unknown mode"; +} + +void print_mode(int m) +{ + tprint(string_mode(m)); +} + +void print_in_mode(int m) +{ + tprint("' in "); + tprint(string_mode(m)); +} + +int get_mode_id(void) +{ + int m = cur_list.mode_field; + if (m > 0) { + switch (m / (max_command_cmd + 1)) { + case 0: + return 'v'; + break; + case 1: + return 'h'; + break; + case 2: + return 'm'; + break; + default: + return '\0'; + break; + } + } else if (m == 0) { + return 'n';; + } else { + switch ((-m) / (max_command_cmd + 1)) { + case 0: + return 'V'; + break; + case 1: + return 'H'; + break; + case 2: + return 'M'; + break; + default: + return '\0'; + break; + } + } +} + +/*tex + +The state of affairs at any semantic level can be represented by five values: + +|mode| is the number representing the semantic mode, as just explained. + +|head| is a |pointer| to a list head for the list being built; |link(head)| +therefore points to the first element of the list, or to |null| if the list is +empty. + +|tail| is a |pointer| to the final node of the list being built; thus, +|tail=head| if and only if the list is empty. + +|prev_graf| is the number of lines of the current paragraph that have already +been put into the present vertical list. + +|aux| is an auxiliary |memory_word| that gives further information that is needed +to characterize the situation. + +In vertical mode, |aux| is also known as |prev_depth|; it is the scaled value +representing the depth of the previous box, for use in baseline calculations, or +it is |<=-1000|pt if the next box on the vertical list is to be exempt from +baseline calculations. In horizontal mode, |aux| is also known as |space_factor|; +it holds the current space factor used in spacing calculations. In math mode, +|aux| is also known as |incompleat_noad|; if not |null|, it points to a record +that represents the numerator of a generalized fraction for which the denominator +is currently being formed in the current list. + +There is also a sixth quantity, |mode_line|, which correlates the semantic nest +with the user's input; |mode_line| contains the source line number at which the +current level of nesting was entered. The negative of this line number is the +|mode_line| at the level of the user's output routine. + +A seventh quantity, |eTeX_aux|, is used by the extended features eTeX. In math +mode it is known as |delim_ptr| and points to the most recent |fence_noad| of a +|math_left_group|. + +In horizontal mode, the |prev_graf| field is used for initial language data. + +The semantic nest is an array called |nest| that holds the |mode|, |head|, +|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels below +the currently active one. Information about the currently active level is kept in +the global quantities |mode|, |head|, |tail|, |prev_graf|, |aux|, and +|mode_line|, which live in a struct that is ready to be pushed onto |nest| if +necessary. + +The math field is used by various bits and pieces in |texmath.w| + +This implementation of \TeX\ uses two different conventions for representing +sequential stacks. @^stack conventions@>@^conventions for representing stacks@> + +1) If there is frequent access to the top entry, and if the stack is essentially +never empty, then the top entry is kept in a global variable (even better would +be a machine register), and the other entries appear in the array +$\\{stack}[0\to(\\{ptr}-1)]$. The semantic stack is handled this way. + +2) If there is infrequent top access, the entire stack contents are in the array +$\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| is treated this way, +as we have seen. + +In |nest_ptr| we have the first unused location of |nest|, and |max_nest_stack| +has the maximum of |nest_ptr| when pushing. In |shown_mode| we store the most +recent mode shown by \.{\\tracingcommands} and with |save_tail| we can examine +whether we have an auto kern before a glue. + +*/ + +list_state_record *nest; +int nest_ptr, max_nest_stack, shown_mode; +halfword save_tail; + +/*tex + +We will see later that the vertical list at the bottom semantic level is split +into two parts; the ``current page'' runs from |page_head| to |page_tail|, and +the ``contribution list'' runs from |contrib_head| to |tail| of semantic level +zero. The idea is that contributions are first formed in vertical mode, then +``contributed'' to the current page (during which time the page-breaking +decisions are made). For now, we don't need to know any more details about the +page-building process. + +*/ + +void initialize_nesting(void) +{ + nest_ptr = 0; + max_nest_stack = 0; + shown_mode = 0; + cur_list.mode_field = vmode; + cur_list.head_field = contrib_head; + cur_list.tail_field = contrib_head; + cur_list.eTeX_aux_field = null; + cur_list.prev_depth_field = ignore_depth; + cur_list.space_factor_field = 1000; + cur_list.incompleat_noad_field = null; + cur_list.ml_field = 0; + cur_list.pg_field = 0; + cur_list.dirs_field = null; + init_math_fields(); +} + +/*tex + +Here is a common way to make the current list grow: + +*/ + +void tail_append(halfword p) +{ + couple_nodes(cur_list.tail_field, p); + cur_list.tail_field = vlink(cur_list.tail_field); +} + +halfword pop_tail(void) +{ + halfword n, r; + if (cur_list.tail_field != cur_list.head_field) { + r = cur_list.tail_field; + if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) { + n = alink(cur_list.tail_field); + } else { + n = cur_list.head_field; + while (vlink(n) != cur_list.tail_field) + n = vlink(n); + } + cur_list.tail_field = n; + alink(r) = null; + vlink(n) = null; + return r; + } else { + return null; + } +} + +/*tex + +When \TeX's work on one level is interrupted, the state is saved by calling +|push_nest|. This routine changes |head| and |tail| so that a new (empty) list is +begun; it does not change |mode| or |aux|. + +*/ + +void push_nest(void) +{ + if (nest_ptr > max_nest_stack) { + max_nest_stack = nest_ptr; + if (nest_ptr == nest_size) + overflow("semantic nest size", (unsigned) nest_size); + } + incr(nest_ptr); + cur_list.mode_field = nest[nest_ptr - 1].mode_field; + cur_list.head_field = new_node(temp_node, 0); + cur_list.tail_field = cur_list.head_field; + cur_list.eTeX_aux_field = null; + cur_list.ml_field = line; + cur_list.pg_field = 0; + cur_list.dirs_field = null; + cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field; + cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field; + cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field; + init_math_fields(); +} + +/*tex + +Conversely, when \TeX\ is finished on the current level, the former state is +restored by calling |pop_nest|. This routine will never be called at the lowest +semantic level, nor will it be called unless |head| is a node that should be +returned to free memory. + +*/ + +void pop_nest(void) +{ + flush_node(cur_list.head_field); + decr(nest_ptr); +} + +/*tex + +Here is a procedure that displays what \TeX\ is working on, at all levels. + +*/ + +void show_activities(void) +{ + /*tex Index into |nest|: */ + int p; + /*tex The mode: */ + int m; + /*tex For showing the current page: */ + halfword q, r; + /*tex Ditto: */ + int t; + tprint_nl(""); + print_ln(); + for (p = nest_ptr; p >= 0; p--) { + m = nest[p].mode_field; + tprint_nl("### "); + print_mode(m); + tprint(" entered at line "); + print_int(abs(nest[p].ml_field)); + if (nest[p].ml_field < 0) + tprint(" (\\output routine)"); + if (p == 0) { + /*tex Show the status of the current page */ + if (page_head != page_tail) { + tprint_nl("### current page:"); + if (output_active) + tprint(" (held over for next output)"); + show_box(vlink(page_head)); + if (page_contents > empty) { + tprint_nl("total height "); + print_totals(); + tprint_nl(" goal height "); + print_scaled(page_goal); + r = vlink(page_ins_head); + while (r != page_ins_head) { + print_ln(); + tprint_esc("insert"); + t = subtype(r); + print_int(t); + tprint(" adds "); + if (count(t) == 1000) + t = height(r); + else + t = x_over_n(height(r), 1000) * count(t); + print_scaled(t); + if (type(r) == split_up_node) { + q = page_head; + t = 0; + do { + q = vlink(q); + if ((type(q) == ins_node) + && (subtype(q) == subtype(r))) + incr(t); + } while (q != broken_ins(r)); + tprint(", #"); + print_int(t); + tprint(" might split"); + } + r = vlink(r); + } + } + } + if (vlink(contrib_head) != null) + tprint_nl("### recent contributions:"); + } + show_box(vlink(nest[p].head_field)); + /*tex Show the auxiliary field, |a|. */ + switch (abs(m) / (max_command_cmd + 1)) { + case 0: + tprint_nl("prevdepth "); + if (nest[p].prev_depth_field <= ignore_depth) + tprint("ignored"); + else + print_scaled(nest[p].prev_depth_field); + if (nest[p].pg_field != 0) { + tprint(", prevgraf "); + print_int(nest[p].pg_field); + if (nest[p].pg_field != 1) + tprint(" lines"); + else + tprint(" line"); + } + break; + case 1: + tprint_nl("spacefactor "); + print_int(nest[p].space_factor_field); + break; + case 2: + if (nest[p].incompleat_noad_field != null) { + tprint("this will be denominator of:"); + show_box(nest[p].incompleat_noad_field); + } + break; + } + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.h b/Build/source/texk/web2c/luatexdir/tex/nesting.h new file mode 100644 index 00000000000..238522a77d1 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/nesting.h @@ -0,0 +1,70 @@ +/* nesting.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef NESTING_H +# define NESTING_H + +# define vmode 1 /* vertical mode */ +# define hmode (vmode+max_command_cmd+1) + /* horizontal mode */ +# define mmode (hmode+max_command_cmd+1) + /* math mode */ + +extern void print_mode(int m); +extern void print_in_mode(int m); +extern int get_mode_id(void); + +# define ignore_depth -65536000 /* magic dimension value to mean `ignore me' */ + +typedef struct list_state_record_ { + int mode_field; + halfword head_field; + halfword tail_field; + halfword eTeX_aux_field; + int pg_field; + int ml_field; + halfword prev_depth_field; + halfword space_factor_field; + halfword incompleat_noad_field; + halfword dirs_field; + int math_field; + int math_style_field; +} list_state_record; + +extern list_state_record *nest; +extern int nest_ptr; +extern int max_nest_stack; +# define cur_list nest[nest_ptr] + /* the ``top'' semantic state */ +extern int shown_mode; +extern halfword save_tail; + +extern void push_nest(void); +extern void pop_nest(void); +extern void initialize_nesting(void); + +extern void tail_append(halfword p); +extern halfword pop_tail(void); + + +extern void show_activities(void); + + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/packaging.c b/Build/source/texk/web2c/luatexdir/tex/packaging.c new file mode 100644 index 00000000000..3d23c8b7320 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/packaging.c @@ -0,0 +1,2145 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + + We're essentially done with the parts of \TeX\ that are concerned with the + input (|get_next|) and the output (|ship_out|). So it's time to get heavily + into the remaining part, which does the real work of typesetting. + + After lists are constructed, \TeX\ wraps them up and puts them into boxes. + Two major subroutines are given the responsibility for this task: |hpack| + applies to horizontal lists (hlists) and |vpack| applies to vertical lists + (vlists). The main duty of |hpack| and |vpack| is to compute the dimensions + of the resulting boxes, and to adjust the glue if one of those dimensions is + pre-specified. The computed sizes normally enclose all of the material inside + the new box; but some items may stick out if negative glue is used, if the + box is overfull, or if a \.{\\vbox} includes other boxes that have been + shifted left. + + The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node| for a + box containing the hlist that starts at |p|. Parameter |w| specifies a width; + and parameter |m| is either `|exactly|' or `|additional|'. Thus, + |hpack(p,w,exactly)| produces a box whose width is exactly |w|, while + |hpack(p,w,additional)| yields a box whose width is the natural width plus + |w|. It is convenient to define a macro called `|natural|' to cover the most + common case, so that we can say |hpack(p,natural)| to get a box that has the + natural width of list |p|. + + Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a box + containing the vlist that starts at |p|. In this case |w| represents a height + instead of a width; the parameter |m| is interpreted as in |hpack|. + + The parameters to |hpack| and |vpack| correspond to \TeX's primitives like + `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note that + `\.{\\hbox}' with no dimension following it is equivalent to `\.{\\hbox} + \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such constructions in + the user's input, including the mandatory left brace that follows them, and + it puts the specification onto |save_stack| so that the desired box can later + be obtained by executing the following code: $$\vbox{\halign{#\hfil\cr + |save_ptr:=save_ptr-1;|\cr |hpack(p,saved_value(0),saved_level(0)).|\cr}}$$ + +*/ + +/*tex Scan a box specification and left brace: */ + +void scan_spec(group_code c) +{ + int spec_code; + boolean done = false ; + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd == left_brace_cmd) { + spec_code = additional; + cur_val = 0; + done = true; + } else { + /*tex todo: attr */ + back_input(); + if (scan_keyword("to")) { + spec_code = exactly; + scan_normal_dimen(); + } else if (scan_keyword("spread")) { + spec_code = additional; + scan_normal_dimen(); + } else { + spec_code = additional; + cur_val = 0; + } + } + set_saved_record(0, saved_boxspec, spec_code, cur_val); + save_ptr++; + new_save_level(c); + if (!done) { + scan_left_brace(); + } +} + +/*tex + + When scanning, special care is necessary to ensure that the special + |save_stack| codes are placed just below the new group code, because scanning + can change |save_stack| when \.{\\csname} appears. + + This coincides with the text on |dir| and |attr| keywords, as these are + exaclty the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the input + stream (the others are \.{\\vcenter}, \.{\\valign}, and \.{\\halign}). + +*/ + +/*tex Scan a box specification and left brace: */ + +#define first_char_is(a,A) (cur_cs == 0 && (cur_chr == a || cur_chr == A)) + +void scan_full_spec(group_code c, int spec_direction, int just_pack) +{ + int s, i, v, spec_code; + boolean done = false ; + halfword attr_list; + boolean attr_done = false ; + boolean dir_done = false ; + if (attr_list_cache == cache_disabled) + update_attribute_cache(); + attr_list = attr_list_cache; + /*tex The box context: */ + s = saved_value(0); + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd == left_brace_cmd) { + goto QUICK; + } else { + back_input(); + goto KEYWORDS; + } + CONTINUE: + while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) { + get_x_token(); + if (cur_cmd == left_brace_cmd) { + goto QUICK; + } else if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd) { + back_input(); + break; + } + } + KEYWORDS: + /*tex Multiple |attr| keys possible (before or after dir). */ + if (scan_keyword("attr")) { + scan_register_num(); + i = cur_val; + scan_optional_equals(); + scan_int(); + v = cur_val; + if (! attr_done) { + attr_list = copy_attribute_list(attr_list_cache); + attr_done = true; + } + attr_list = do_set_attribute(attr_list, i, v); + goto CONTINUE; + } + /*tex We only permit one |(b)dir| directive. */ + if (! dir_done) { + if (scan_keyword("bdir")) { + scan_int(); + check_dir_value(cur_val); + spec_direction = cur_val; + dir_done = true; + goto CONTINUE; + } + if (scan_keyword("dir")) { + scan_direction(); + spec_direction = cur_val; + dir_done = true; + goto CONTINUE; + } + } + /*tex Only one |to| or |spread| key possible and it comes last. */ + if (scan_keyword("to")) { + spec_code = exactly; + } else if (scan_keyword("spread")) { + spec_code = additional; + } else { + spec_code = additional; + cur_val = 0; + goto FOUND; + } + scan_normal_dimen(); + goto FOUND; + QUICK: + spec_code = additional; + cur_val = 0; + done = true; + FOUND: + add_node_attr_ref(attr_list); + set_saved_record(0, saved_boxcontext, 0, s); + set_saved_record(1, saved_boxspec, spec_code, cur_val); + /*tex Adjust |text_dir_ptr| for |scan_spec|. */ + if (spec_direction != -1) { + set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr); + text_dir_ptr = new_dir(spec_direction); + } else { + set_saved_record(2, saved_boxdir, spec_direction, null); + } + set_saved_record(3, saved_boxattr, 0, attr_list); + set_saved_record(4, saved_boxpack, 0, just_pack); + save_ptr += 5; + new_save_level(c); + if (! done) { + scan_left_brace(); + } + /*tex No gain in |if (body_direction_par != spec_direction)| etc. */ + eq_word_define(int_base + body_direction_code, spec_direction); + eq_word_define(int_base + par_direction_code, spec_direction); + eq_word_define(int_base + text_direction_code, spec_direction); +} + +/*tex + + To figure out the glue setting, |hpack| and |vpack| determine how much + stretchability and shrinkability are present, considering all four orders of + infinity. The highest order of infinity that has a nonzero coefficient is + then used as if no other orders were present. + + For example, suppose that the given list contains six glue nodes with the + respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill. Then + the total is essentially 2fil; and if a total additional space of 6pt is to + be achieved by stretching, the actual amounts of stretch will be 0pt, 0pt, + 15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be considered. (The + `fill' glue is therefore not really stretching infinitely with respect to + `fil'; nobody would actually want that to happen.) + + The arrays |total_stretch| and |total_shrink| are used to determine how much + glue of each kind is present. A global variable |last_badness| is used to + implement \.{\\badness}. + +*/ + +/*tex Glue found by |hpack| or |vpack|. */ + +scaled total_stretch[5]; +scaled total_shrink[5]; + +/*tex Badness of the most recently packaged box. */ + +int last_badness; + +/*tex + + If the global variable |adjust_tail| is non-null, the |hpack| routine also + removes all occurrences of |ins_node|, |mark_node|, and |adjust_node| items + and appends the resulting material onto the list that ends at location + |adjust_tail|. + +*/ + +/*tex Tail of adjustment list. */ + +halfword adjust_tail; + +/*tex + + Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to + |pre_adjust_tail| instead of |adjust_tail|. + +*/ + +halfword pre_adjust_tail; + +halfword last_leftmost_char; +halfword last_rightmost_char; + +/*tex Pointers to the prev and next char of an implicit kern. */ + +halfword next_char_p; +halfword prev_char_p; + +/*tex This procedure is called repeatedly from inside the line break algorithm. */ + +void set_prev_char_p(halfword p) +{ + prev_char_p = p; +} + +/*tex + + The kern stretch / shrink code was (or had become) rather weird ... the width + field is set, and then used in a second calculation, repeatedly, so why is + that ... maybe some some weird left-over ... anyway, the values are so small + that in practice they are not significant at all when the backend sees them + because a few hundred sp positive or negative are just noise there (so + adjustlevel 3 has hardly any consequence for the result but is more + efficient). + +*/ + +scaled char_stretch(halfword p) +{ + internal_font_number f = font(p); + int m = font_max_stretch(f); + if (m > 0) { + int c = character(p); + int ef = get_ef_code(f, c); + if (ef > 0) { + scaled dw = calc_char_width(f, c, m) - char_width(f, c); + if (dw > 0) { + return round_xn_over_d(dw, ef, 1000); + } + } + } + return 0; +} + +scaled char_shrink(halfword p) +{ + internal_font_number f = font(p); + int m = font_max_shrink(f); + if (m > 0) { + int c = character(p); + int ef = get_ef_code(f, c); + if (ef > 0) { + scaled dw = char_width(f, c) - calc_char_width(f, c, -m); + if (dw > 0) { + return round_xn_over_d(dw, ef, 1000); + } + } + } + return 0; +} + +scaled kern_stretch(halfword p) +{ + int m; + scaled d, e, x; + scaled w = width(p) ; + halfword l; + halfword r; + if (w == 0) { + /*tex Why bother about zero kerns. */ + return 0; + } + l = prev_char_p ; + if ((l == null) || (vlink(l) != p)) { + /*tex We only care about kerns following a char. */ + return 0; + } + r = vlink(p); + if (r == null) { + /*tex We only care about kerns between a char and something else. */ + } + if (!(is_char_node(l) && is_char_node(r))) { + /*tex We want two chars (but but don't care about the fonts). */ + return 0; + } + /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */ + m = (font_max_stretch(font(l)) + font_max_stretch(font(r)))/2; + if (m == 0) { + /*tex nothing to kern */ + return 0; + } + d = round_xn_over_d(w, 1000 + m, 1000); + /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */ + e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ; + if (e == 1000) { + x = d - w; + } else { + x = round_xn_over_d(d - w, e, 1000); + } + return x; +} + +scaled kern_shrink(halfword p) +{ + int m; + scaled d, e, x; + scaled w = width(p) ; + halfword l; + halfword r; + if (w == 0) { + /*tex Why bother about zero kerns. */ + return 0; + } + l = prev_char_p ; + if ((l == null) || (vlink(l) != p)) { + /*tex We only care about kerns following a char. */ + return 0; + } + r = vlink(p); + if (r == null) { + /*tex We only care about kerns between a char and something else. */ + } + if (!(is_char_node(l) && is_char_node(r))) { + /*tex We want two chars (but but don't care about the fonts). */ + return 0; + } + /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */ + m = (font_max_shrink(font(l)) + font_max_shrink(font(r)))/2; + if (m == 0) { + /*tex Nothing to kern. */ + return 0; + } + d = round_xn_over_d(w, 1000 - m, 1000); + e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ; + if (e == 1000) { + x = w - d ; + } else { + x = round_xn_over_d(w - d, e, 1000); + } + return x; +} + +void do_subst_font(halfword p, int ex_ratio) +{ + if (type(p) == disc_node) { + halfword r = vlink(pre_break(p)); + while (r != null) { + if (is_char_node(r)) + do_subst_font(r, ex_ratio); + r = vlink(r); + } + r = vlink(post_break(p)); + while (r != null) { + if (is_char_node(r)) + do_subst_font(r, ex_ratio); + r = vlink(r); + } + r = vlink(no_break(p)); + while (r != null) { + if (is_char_node(r)) + do_subst_font(r, ex_ratio); + r = vlink(r); + } + return; + } + if (! is_char_node(p)) { + normal_error("font expansion", "invalid node type"); + return; + } else { + internal_font_number f = font(p); + int ef = get_ef_code(f, character(p)); + if (ef == 0) + return; + if ((font_max_stretch(f) > 0) && (ex_ratio > 0)) { + int ex_stretch = ext_xn_over_d(ex_ratio * ef, font_max_stretch(f), 1000000); + ex_glyph(p) = fix_expand_value(f, ex_stretch)*1000; + } else if ((font_max_shrink(f) > 0) && (ex_ratio < 0)) { + int ex_shrink = ext_xn_over_d(ex_ratio * ef, font_max_shrink(f), 1000000); + ex_glyph(p) = fix_expand_value(f, ex_shrink)*1000; + } + } +} + +scaled char_pw(halfword p, int side) +{ + internal_font_number f; + int c, w; + if (side == left_side) + last_leftmost_char = null; + else + last_rightmost_char = null; + if (p == null) + return 0; + if (!is_char_node(p)) + return 0; + f = font(p); + if (side == left_side) { + c = get_lp_code(f, character(p)); + last_leftmost_char = p; + } else { + c = get_rp_code(f, character(p)); + last_rightmost_char = p; + } + if (c == 0) + return 0; + w = quad(f); + return round_xn_over_d(w, c, 1000); +} + +halfword new_margin_kern(scaled w, halfword p, int side) +{ + halfword k, q; + k = new_node(margin_kern_node, side); + width(k) = w; + if (p == null) + normal_error("margin kerning", "invalid pointer to marginal char node"); + q = new_char(font(p), character(p)); + margin_char(k) = q; + return k; +} + +/*tex + + Here we prepare for |hpack|, which is place where we do font substituting + when font expansion is being used. + +*/ + +/*tex The current expansion ratio, needed for recursive call. */ + +int font_expand_ratio = 0; + +int ignore_math_skip(halfword p) +{ + if (math_skip_mode == 6) { + if (subtype(p) == after) { + if (math_skip_boundary(vlink(p))) { + return 0; + } + } else { + if (math_skip_boundary(alink(p))) { + return 0; + } + } + } else if (math_skip_mode == 7) { + if (subtype(p) == after) { + if (! math_skip_boundary(vlink(p))) { + return 0; + } + } else { + if (! math_skip_boundary(alink(p))) { + return 0; + } + } + } else { + return 0; + } + reset_glue_to_zero(p); + return 1; +} + +halfword hpack(halfword p, scaled w, int m, int pack_direction) +{ + /*tex the box node that will be returned */ + halfword r; + /*tex trails behind |p| */ + halfword q; + /*tex height */ + scaled h = 0; + /*tex depth */ + scaled d = 0; + /*tex natural width */ + scaled x = 0; + scaled_whd whd; + /*tex shift amount */ + scaled s; + /*tex order of infinity */ + int o; + /*tex for managing the direction stack */ + halfword dir_ptr1 = null; + /*tex the current direction */ + int hpack_dir; + int disc_level = 0; + halfword pack_interrupt[8]; + scaled font_stretch = 0; + scaled font_shrink = 0; + int adjust_spacing = adjust_spacing_par; + last_badness = 0; + /*tex the box node that will be returned */ + r = new_node(hlist_node, min_quarterword); + if (pack_direction == -1) { + hpack_dir = text_direction_par; + } else { + hpack_dir = pack_direction; + } + box_dir(r) = hpack_dir; + /*tex + + A potential optimimization, save a little but neglectable in practice + (not so many empty boxes are used): + + \starttyping + if (p == null) { + width(r) = w; + return r; + } + \stoptyping + + */ + /*tex push null */ + push_dir(dir_ptr1,hpack_dir); + /*tex hm, adding something to a node address? */ + q = r + list_offset; + vlink(q) = p; + if (m == cal_expand_ratio) { + /*tex Why not always: */ + prev_char_p = null; + } + if (adjust_spacing > 2) { + adjust_spacing = 0; + } + total_stretch[normal] = 0; + total_shrink[normal] = 0; + total_stretch[sfi] = 0; + total_shrink[sfi] = 0; + total_stretch[fil] = 0; + total_shrink[fil] = 0; + total_stretch[fill] = 0; + total_shrink[fill] = 0; + total_stretch[filll] = 0; + total_shrink[filll] = 0; + RESWITCH: + while ((p != null) || (disc_level > 0)) { + if (p == null) { + decr(disc_level); + p = pack_interrupt[disc_level]; + goto RESWITCH; + } + /*tex + + Examine node |p| in the hlist, taking account of its effect on the + dimensions of the new box, or moving it to the adjustment list; then + advance |p| to the next node. + + */ + while (is_char_node(p)) { + /*tex + + Incorporate character dimensions into the dimensions of the hbox + that will contain~it, then move to the next node. + + The following code is part of \TeX's inner loop; i.e., adding + another character of text to the user's input will cause each of + these instructions to be exercised one more time. + + */ + if (m >= cal_expand_ratio) { + prev_char_p = p; + if (m == cal_expand_ratio) { + font_stretch += char_stretch(p); + font_shrink += char_shrink(p); + } else if (m == subst_ex_font) { + do_subst_font(p, font_expand_ratio); + } + } + whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true); + x += whd.wd; + if (whd.ht > h) + h = whd.ht; + if (whd.dp > d) + d = whd.dp; + p = vlink(p); + } + if (p != null) { + switch (type(p)) { + case hlist_node: + case vlist_node: + /*tex + + Incorporate box dimensions into the dimensions of the + hbox that will contain~it + + The code here implicitly uses the fact that running + dimensions are indicated by |null_flag|, which will be + ignored in the calculations because it is a highly + negative number. + + */ + s = shift_amount(p); + whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false); + x += whd.wd; + if (whd.ht - s > h) + h = whd.ht - s; + if (whd.dp + s > d) + d = whd.dp + s; + break; + case rule_node: + case unset_node: + x += width(p); + if (height(p) > h) + h = height(p); + if (depth(p) > d) + d = depth(p); + break; + case math_node: + /*tex Begin mathskip code. */ + if (glue_is_zero(p) || ignore_math_skip(p)) { + x += surround(p); + break; + } else { + /*tex Fall through. */ + } + /*tex End mathskip code. */ + case glue_node: + /*tex Incorporate glue into the horizontal totals. */ + x += width(p); + o = stretch_order(p); + total_stretch[o] = total_stretch[o] + stretch(p); + o = shrink_order(p); + total_shrink[o] = total_shrink[o] + shrink(p); + if (subtype(p) >= a_leaders) { + halfword g = leader_ptr(p); + if (height(g) > h) + h = height(g); + if (depth(g) > d) + d = depth(g); + } + break; + case kern_node: + x += width(p); + if (subtype(p) == font_kern && adjust_spacing) { + /*tex So only when 1 or 2. */ + if (m == cal_expand_ratio) { + font_stretch = font_stretch + kern_stretch(p); + font_shrink = font_shrink + kern_shrink(p); + } else if (m == subst_ex_font) { + /*tex This is the finalizer. */ + int k = 0; + if (font_expand_ratio > 0) { + k = kern_stretch(p); + } else if (font_expand_ratio < 0) { + k = kern_shrink(p); + } + ex_kern(p) = k; + x += k; + } + } + break; + case disc_node: + if (m == subst_ex_font) + do_subst_font(p, font_expand_ratio); + if ((subtype(p) != select_disc) && (vlink(no_break(p)) != null)) { + pack_interrupt[disc_level] = vlink(p); + incr(disc_level); + p = no_break(p); + } + break; + case dir_node: + /*tex Adjust the dir stack for the |hpack| routine. */ + if (subtype(p) == normal_dir) { + hpack_dir = dir_dir(p); + push_dir_node(dir_ptr1,p); + } else { + pop_dir_node(dir_ptr1); + if (dir_ptr1 != null) + hpack_dir = dir_dir(dir_ptr1); + } + break; + case margin_kern_node: + if (m == cal_expand_ratio) { + int f = font(margin_char(p)); + do_subst_font(margin_char(p), 1000); + if (f != font(margin_char(p))) + font_stretch = font_stretch - width(p) - char_pw(margin_char(p), subtype(p)); + font(margin_char(p)) = f; + do_subst_font(margin_char(p), -1000); + if (f != font(margin_char(p))) + font_shrink = font_shrink - width(p) - char_pw(margin_char(p), subtype(p)); + font(margin_char(p)) = f; + } else if (m == subst_ex_font) { + do_subst_font(margin_char(p), font_expand_ratio); + width(p) = -char_pw(margin_char(p), subtype(p)); + } + x += width(p); + break; + case ins_node: + case mark_node: + case adjust_node: + /*tex + + Transfer node |p| to the adjustment list.\Although node + |q| is not necessarily the immediate predecessor of node + |p|, it always points to some node in the list preceding + |p|. Thus, we can delete nodes by moving |q| when + necessary. The algorithm takes linear time, and the extra + computation does not intrude on the inner loop unless it + is necessary to make a deletion. + + */ + if (adjust_tail != null || pre_adjust_tail != null) { + while (vlink(q) != p) + q = vlink(q); + if (type(p) == adjust_node) { + if (adjust_pre(p) != 0) + update_adjust_list(pre_adjust_tail); + else + update_adjust_list(adjust_tail); + p = vlink(p); + adjust_ptr(vlink(q)) = null; + flush_node(vlink(q)); + } else { + vlink(adjust_tail) = p; + adjust_tail = p; + p = vlink(p); + } + vlink(q) = p; + p = q; + } + break; + default: + break; + } + p = vlink(p); + } + } + + if (adjust_tail != null) + vlink(adjust_tail) = null; + if (pre_adjust_tail != null) + vlink(pre_adjust_tail) = null; + height(r) = h; + depth(r) = d; + /*tex + + Determine the value of |width(r)| and the appropriate glue setting; then + |return| or |goto common_ending|. + + When we get to the present part of the program, |x| is the natural width + of the box being packaged. + + */ + if (m == additional) + w = x + w; + width(r) = w; + x = w - x; + /*tex Now |x| is the excess to be made up. */ + if (x == 0) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + goto EXIT; + } else if (x > 0) { + /*tex + + Determine horizontal glue stretch setting, then |return| or + |goto common_ending|. + + If |hpack| is called with |m=cal_expand_ratio| we calculate + |font_expand_ratio| and return without checking for overfull or + underfull box. + + */ + if (total_stretch[filll] != 0) + o = filll; + else if (total_stretch[fill] != 0) + o = fill; + else if (total_stretch[fil] != 0) + o = fil; + else if (total_stretch[sfi] != 0) + o = sfi; + else + o = normal; + if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) { + font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0); + goto EXIT; + } + glue_order(r) = (quarterword) o; + glue_sign(r) = stretching; + if (total_stretch[o] != 0) { + glue_set(r) = unfloat((double) x / total_stretch[o]); + } else { + /*tex There's nothing to stretch. */ + glue_sign(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } + if (o == normal) { + if (list_ptr(r) != null) { + /*tex + + Report an underfull hbox and |goto common_ending|, if this + box is sufficiently bad. + + */ + last_badness = badness(x, total_stretch[normal]); + if (last_badness > hbadness_par) { + int callback_id = callback_defined(hpack_quality_callback); + if (callback_id > 0) { + halfword rule = null; + if (last_badness > 100) { + run_callback(callback_id, "SdNdd->N","underfull",last_badness,r,abs(pack_begin_line),line,&rule); + } else { + run_callback(callback_id, "SdNdd->N","loose",last_badness,r,abs(pack_begin_line),line,&rule); + } + if (rule != null) { + while (vlink(q) != null) { + q = vlink(q); + } + couple_nodes(q,rule); + } + } else { + print_ln(); + if (last_badness > 100) { + tprint_nl("Underfull \\hbox (badness "); + } else { + tprint_nl("Loose \\hbox (badness "); + } + print_int(last_badness); + goto COMMON_ENDING; + } + } + } + } + goto EXIT; + } else { + /*tex + + Determine horizontal glue shrink setting, then |return| or |goto + common_ending|, + + */ + if (total_shrink[filll] != 0) + o = filll; + else if (total_shrink[fill] != 0) + o = fill; + else if (total_shrink[fil] != 0) + o = fil; + else if (total_shrink[sfi] != 0) + o = sfi; + else + o = normal; + if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) { + font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0); + goto EXIT; + } + glue_order(r) = (quarterword) o; + glue_sign(r) = shrinking; + if (total_shrink[o] != 0) { + glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]); + } else { + /*tex There's nothing to shrink. */ + glue_sign(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } + if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) { + int overshoot = -x - total_shrink[normal] ; + last_badness = 1000000; + /*tex Use the maximum shrinkage */ + set_glue_ratio_one(glue_set(r)); + /*tex + + Report an overfull hbox and |goto common_ending|, if this box is + sufficiently bad. + + */ + if ((overshoot > hfuzz_par) || (hbadness_par < 100)) { + int callback_id = callback_defined(hpack_quality_callback); + halfword rule = null; + if (callback_id > 0) { + run_callback(callback_id, "SdNdd->N","overfull",overshoot,r,abs(pack_begin_line),line,&rule); + } else if (overfull_rule_par > 0) { + rule = new_rule(normal_rule); + rule_dir(rule) = box_dir(r); + width(rule) = overfull_rule_par; + } + if (rule != null) { + while (vlink(q) != null) { + q = vlink(q); + } + couple_nodes(q,rule); + } + if (callback_id == 0) { + print_ln(); + tprint_nl("Overfull \\hbox ("); + print_scaled(overshoot); + tprint("pt too wide"); + goto COMMON_ENDING; + } + } + } else if (o == normal) { + if (list_ptr(r) != null) { + /*tex + + Report a tight hbox and |goto common_ending|, if this box is + sufficiently bad. + + */ + last_badness = badness(-x, total_shrink[normal]); + if (last_badness > hbadness_par) { + int callback_id = callback_defined(hpack_quality_callback); + if (callback_id > 0) { + halfword rule = null; + run_callback(callback_id, "SdNdd->N","tight",last_badness,r,abs(pack_begin_line),line,&rule); + if (rule != null) { + while (vlink(q) != null) { + q = vlink(q); + } + couple_nodes(q,rule); + } + } else { + print_ln(); + tprint_nl("Tight \\hbox (badness "); + print_int(last_badness); + goto COMMON_ENDING; + } + } + } + } + goto EXIT; + } + + COMMON_ENDING: + /*tex Finish issuing a diagnostic message for an overfull or underfull hbox. */ + if (output_active) { + tprint(") has occurred while \\output is active"); + } else { + if (pack_begin_line != 0) { + if (pack_begin_line > 0) { + tprint(") in paragraph at lines "); + } else { + tprint(") in alignment at lines "); + } + print_int(abs(pack_begin_line)); + tprint("--"); + } else { + tprint(") detected at line "); + } + print_int(line); + } + + print_ln(); + font_in_short_display = null_font; + short_display(list_ptr(r)); + print_ln(); + begin_diagnostic(); + show_box(r); + end_diagnostic(true); + EXIT: + if ((m == cal_expand_ratio) && (font_expand_ratio != 0)) { + font_expand_ratio = fix_int(font_expand_ratio, -1000, 1000); + q = list_ptr(r); + list_ptr(r) = null; + flush_node(r); + /*tex This nested call uses the more or less global font_expand_ratio. */ + r = hpack(q, w, subst_ex_font, hpack_dir); + } + while (dir_ptr1 != null) + pop_dir_node(dir_ptr1); + /*tex Here we reset the |font_expand_ratio|. */ + font_expand_ratio = 0; + return r; +} + +halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int pac, int just_pack, halfword attr) +{ + halfword q; + if (just_pack) { + q = vlink(p); + } else if (type(p) == temp_node && vlink(p) == null) { + q = vlink(p); + } else { + new_hyphenation(p, qt); + /*tex We don't care about the tail in this case. */ + (void) new_ligkern(p, qt); + q = vlink(p); + /*tex Maybe here: |alink(p) = null|. */ + /*tex ignores empty anyway. Maybe also pass tail? */ + q = lua_hpack_filter(q, w, m, grp, pac, attr); + } + return hpack(q, w, m, pac); +} + +/*tex + + Here is a function to calculate the natural whd of a (horizontal) node list. + +*/ + +scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, int g_sign, int g_order, int pack_direction) +{ + /*tex shift amount */ + scaled s; + /*tex points to a glue specification */ + halfword g; + int hpack_dir; + /*tex For recursion */ + scaled_whd xx; + scaled_whd whd, siz = { 0, 0, 0 }; + scaled gp = 0; + scaled gm = 0; + if (pack_direction == -1) { + hpack_dir = text_direction_par; + } else { + hpack_dir = pack_direction; + } + while (p != pp && p != null) { + while (is_char_node(p) && p != pp) { + whd = pack_width_height_depth(hpack_dir, dir_TRT, p, true); + siz.wd += whd.wd; + if (whd.ht > siz.ht) + siz.ht = whd.ht; + if (whd.dp > siz.dp) + siz.dp = whd.dp; + p = vlink(p); + } + if (p != pp && p != null) { + switch (type(p)) { + case hlist_node: + case vlist_node: + s = shift_amount(p); + whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false); + siz.wd += whd.wd; + if (whd.ht - s > siz.ht) + siz.ht = whd.ht - s; + if (whd.dp + s > siz.dp) + siz.dp = whd.dp + s; + break; + case rule_node: + case unset_node: + siz.wd += width(p); + if (height(p) > siz.ht) + siz.ht = height(p); + if (depth(p) > siz.dp) + siz.dp = depth(p); + break; + case math_node: + /*tex Begin mathskip code. */ + if (glue_is_zero(p) || ignore_math_skip(p)) { + siz.wd += surround(p); + break; + } else { + /*tex Fall through. */ + } + /*tex End mathskip code. */ + case glue_node: + siz.wd += width(p); + if (g_sign != normal) { + if (g_sign == stretching) { + if (stretch_order(p) == g_order) { + /*tex + |siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p)))| + */ + gp += stretch(p); + } + } else if (shrink_order(p) == g_order) { + /*tex + |siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));| + */ + gm += shrink(p); + } + } + if (subtype(p) >= a_leaders) { + g = leader_ptr(p); + if (height(g) > siz.ht) + siz.ht = height(g); + if (depth(g) > siz.dp) + siz.dp = depth(g); + } + break; + case margin_kern_node: + siz.wd += width(p); + break; + case kern_node: + siz.wd += width(p) + ex_kern(p); + break; + case disc_node: + xx = natural_sizes(no_break(p), null, g_mult, g_sign, g_order, hpack_dir); + siz.wd += xx.wd; + if (xx.ht > siz.ht) + siz.ht = xx.ht; + if (xx.dp > siz.dp) + siz.dp = xx.dp; + break; + default: + break; + } + p = vlink(p); + } + + } + if (g_sign != normal) { + if (g_sign == stretching) { + siz.wd += float_round(float_cast(g_mult) * float_cast(gp)); + } else { + siz.wd -= float_round(float_cast(g_mult) * float_cast(gm)); + } + } + return siz; +} + +/*tex + + In order to provide a decent indication of where an overfull or underfull box + originated, we use a global variable |pack_begin_line| that is set nonzero + only when |hpack| is being called by the paragraph builder or the alignment + finishing routine. + + The source file line where the current paragraph or alignment began; a + negative value denotes alignment: + +*/ + +int pack_begin_line; + +/*tex + + The |vpack| subroutine is actually a special case of a slightly more general + routine called |vpackage|, which has four parameters. The fourth parameter, + which is |max_dimen| in the case of |vpack|, specifies the maximum depth of + the page box that is constructed. The depth is first computed by the normal + rules; if it exceeds this limit, the reference point is simply moved down + until the limiting depth is attained. + +*/ + +halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) +{ + /*tex the box node that will be returned */ + halfword r; + /*tex width */ + scaled w = 0; + /*tex depth */ + scaled d = 0; + /*tex natural height */ + scaled x = 0; + scaled_whd whd; + /*tex shift amount */ + scaled s; + /*tex order of infinity */ + int o; + last_badness = 0; + r = new_node(vlist_node, 0); + if (pack_direction == -1) { + box_dir(r) = body_direction_par; + } else { + box_dir(r) = pack_direction; + } + subtype(r) = min_quarterword; + shift_amount(r) = 0; + list_ptr(r) = p; + total_stretch[normal] = 0; + total_shrink[normal] = 0; + total_stretch[sfi] = 0; + total_shrink[sfi] = 0; + total_stretch[fil] = 0; + total_shrink[fil] = 0; + total_stretch[fill] = 0; + total_shrink[fill] = 0; + total_stretch[filll] = 0; + total_shrink[filll] = 0; + while (p != null) { + /*tex + + Examine node |p| in the vlist, taking account of its effect on the + dimensions of the new box; then advance |p| to the next node. + + */ + if (is_char_node(p)) { + confusion("vpack"); + } else { + switch (type(p)) { + case hlist_node: + case vlist_node: + /*tex + + Incorporate box dimensions into the dimensions of the vbox + that will contain it. + + */ + s = shift_amount(p); + whd = pack_width_height_depth(box_dir(r), box_dir(p), p, false); + if (whd.wd + s > w) + w = whd.wd + s; + x += d + whd.ht; + d = whd.dp; + break; + case rule_node: + case unset_node: + x += d + height(p); + d = depth(p); + if (width(p) > w) + w = width(p); + break; + case glue_node: + /*tex Incorporate glue into the vertical totals. */ + x += d; + d = 0; + x += width(p); + o = stretch_order(p); + total_stretch[o] = total_stretch[o] + stretch(p); + o = shrink_order(p); + total_shrink[o] = total_shrink[o] + shrink(p); + if (subtype(p) >= a_leaders) { + halfword g = leader_ptr(p); + if (width(g) > w) + w = width(g); + } + break; + case kern_node: + x += d + width(p); + d = 0; + break; + default: + break; + } + } + p = vlink(p); + } + width(r) = w; + if (d > l) { + x += d - l; + depth(r) = l; + } else { + depth(r) = d; + } + /*tex + + Determine the value of |height(r)| and the appropriate glue setting; then + |return| or |goto common_ending|. + + When we get to the present part of the program, |x| is the natural height + of the box being packaged. + */ + if (m == additional) + h = x + h; + height(r) = h; + x = h - x; + /*tex Now |x| is the excess to be made up. */ + if (x == 0) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + return r; + } else if (x > 0) { + /*tex + + Determine vertical glue stretch setting, then |return| or |goto + common_ending|. + + */ + if (total_stretch[filll] != 0) + o = filll; + else if (total_stretch[fill] != 0) + o = fill; + else if (total_stretch[fil] != 0) + o = fil; + else if (total_stretch[sfi] != 0) + o = sfi; + else + o = normal; + glue_order(r) = (quarterword) o; + glue_sign(r) = stretching; + if (total_stretch[o] != 0) { + glue_set(r) = unfloat((double) x / total_stretch[o]); + } else { + /*tex There's nothing to stretch. */ + glue_sign(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } + if (o == normal) { + if (list_ptr(r) != null) { + /*tex + + Report an underfull vbox and |goto common_ending|, if this + box is sufficiently bad. + + */ + last_badness = badness(x, total_stretch[normal]); + if (last_badness > vbadness_par) { + int callback_id = callback_defined(vpack_quality_callback); + if (callback_id > 0) { + if (last_badness > 100) { + run_callback(callback_id, "SdNdd->","underfull",last_badness,r,abs(pack_begin_line),line); + } else { + run_callback(callback_id, "SdNdd->","loose",last_badness,r,abs(pack_begin_line),line); + } + goto EXIT; + } else { + print_ln(); + if (last_badness > 100) { + tprint_nl("Underfull \\vbox (badness "); + } else { + tprint_nl("Loose \\vbox (badness "); + } + print_int(last_badness); + goto COMMON_ENDING; + } + } + } + } + return r; + } else { + /*tex + + Determine vertical glue shrink setting, then |return| or |goto + common_ending|. + + */ + if (total_shrink[filll] != 0) + o = filll; + else if (total_shrink[fill] != 0) + o = fill; + else if (total_shrink[fil] != 0) + o = fil; + else if (total_shrink[sfi] != 0) + o = sfi; + else + o = normal; + glue_order(r) = (quarterword) o; + glue_sign(r) = shrinking; + if (total_shrink[o] != 0) { + glue_set(r) = unfloat((double) (-x) / total_shrink[o]); + } else { + /*tex There's nothing to shrink. */ + glue_sign(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } + if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) { + int overshoot = -x - total_shrink[normal]; + last_badness = 1000000; + /*tex Use the maximum shrinkage */ + set_glue_ratio_one(glue_set(r)); + /*tex + + Report an overfull vbox and |goto common_ending|, if this box is + sufficiently bad. + + */ + if ((overshoot > vfuzz_par) || (vbadness_par < 100)) { + int callback_id = callback_defined(vpack_quality_callback); + if (callback_id > 0) { + run_callback(callback_id, "SdNdd->","overfull",overshoot,r,abs(pack_begin_line),line); + goto EXIT; + } else { + print_ln(); + tprint_nl("Overfull \\vbox ("); + print_scaled(-x - total_shrink[normal]); + tprint("pt too high"); + goto COMMON_ENDING; + } + } + } else if (o == normal) { + if (list_ptr(r) != null) { + /*tex + + Report a tight vbox and |goto common_ending|, if this box is + sufficiently bad. + + */ + last_badness = badness(-x, total_shrink[normal]); + if (last_badness > vbadness_par) { + int callback_id = callback_defined(vpack_quality_callback); + if (callback_id > 0) { + run_callback(callback_id, "SdNdd->","tight",last_badness,r,abs(pack_begin_line),line); + goto EXIT; + } else { + print_ln(); + tprint_nl("Tight \\vbox (badness "); + print_int(last_badness); + goto COMMON_ENDING; + } + } + } + } + return r; + } + + COMMON_ENDING: + /*tex Finish issuing a diagnostic message or an overfull or underfull vbox. */ + if (output_active) { + tprint(") has occurred while \\output is active"); + } else { + if (pack_begin_line != 0) { + /*tex It's actually negative. */ + tprint(") in alignment at lines "); + print_int(abs(pack_begin_line)); + tprint("--"); + } else { + tprint(") detected at line "); + } + print_int(line); + print_ln(); + } + begin_diagnostic(); + show_box(r); + end_diagnostic(true); + EXIT: + return r; +} + +halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, int pack_direction, int just_pack, halfword attr) +{ + halfword q = p; + if (!just_pack) + q = lua_vpack_filter(q, h, m, l, grp, pack_direction, attr); + return vpackage(q, h, m, l, pack_direction); +} + +void finish_vcenter(void) +{ + halfword p; + unsave(); + save_ptr--; + p = vpack(vlink(cur_list.head_field), saved_value(0), saved_level(0), -1); + pop_nest(); + p = math_vcenter_group(p); + tail_append(p); +} + +void package(int c) +{ + halfword saved0, saved2, saved3, saved4; + int grp = cur_group; + scaled d = box_max_depth_par; + unsave(); + save_ptr -= 5; + saved0 = saved_value(0); + saved2 = saved_value(2); + saved3 = saved_value(3); + saved4 = saved_value(4); + if (cur_list.mode_field == -hmode) { + cur_box = filtered_hpack(cur_list.head_field, cur_list.tail_field, + saved_value(1), saved_level(1), grp, saved_level(2), saved4, saved3); + subtype(cur_box) = hbox_list; + } else { + cur_box = filtered_vpackage(vlink(cur_list.head_field), + saved_value(1), saved_level(1), d, grp, saved_level(2), saved4, saved3); + if (c == vtop_code) { + /*tex + + Read just the height and depth of |cur_box|, for \.{\\vtop}. The + height of a `\.{\\vtop}' box is inherited from the first item on + its list, if that item is an |hlist_node|, |vlist_node|, or + |rule_node|; otherwise the \.{\\vtop} height is zero. + + */ + scaled h = 0; + halfword p = list_ptr(cur_box); + if ((p != null) && (type(p) <= rule_node)) { + /* hlist, vlist, rule */ + h = height(p); + } + depth(cur_box) = depth(cur_box) - h + height(cur_box); + height(cur_box) = h; + } + } + if (saved2 != null) { + /*tex Adjust back |text_dir_ptr| for |scan_spec| */ + flush_node_list(text_dir_ptr); + text_dir_ptr = saved2; + + } + replace_attribute_list(cur_box, saved3); + pop_nest(); + box_end(saved0); +} + +/*tex + + When a box is being appended to the current vertical list, the baselineskip + calculation is handled by the |append_to_vlist| routine. + +*/ + +void append_to_vlist(halfword b, int location) +{ + /*tex The deficiency of space between baselines: */ + scaled d; + /*tex A new glue node. */ + halfword p; + boolean mirrored = (type(b) == hlist_node) && is_mirrored(box_dir(b)) ; + halfword result = null; + halfword next_depth = ignore_depth; + boolean prev_set = false ; + if (lua_appendtovlist_callback(b,location,prev_depth_par,mirrored,&result,&next_depth,&prev_set)) { + while (result != null) { + couple_nodes(cur_list.tail_field, result); + cur_list.tail_field = result; + result = vlink(result); + } + if (prev_set) { + prev_depth_par = next_depth; + } + } else { + if (prev_depth_par > ignore_depth) { + if (mirrored) { + d = width(baseline_skip_par) - prev_depth_par - depth(b); + } else { + d = width(baseline_skip_par) - prev_depth_par - height(b); + } + if (d < line_skip_limit_par) { + p = new_param_glue(line_skip_code); + } else { + p = new_skip_param(baseline_skip_code); + width(p) = d; + } + couple_nodes(cur_list.tail_field, p); + cur_list.tail_field = p; + } + couple_nodes(cur_list.tail_field, b); + cur_list.tail_field = b; + if (mirrored) { + prev_depth_par = height(b); + } else { + prev_depth_par = depth(b); + } + } +} + +/*tex + + When |saving_vdiscards| is positive then the glue, kern, and penalty nodes + removed by the page builder or by \.{\\vsplit} from the top of a vertical + list are saved in special lists instead of being discarded. + +*/ + +/*tex last item removed by page builder */ + +#define tail_page_disc disc_ptr[copy_code] + +/*tex first item removed by page builder */ + +#define page_disc disc_ptr[last_box_code] + +/*tex first item removed by \.{\\vsplit} */ + +#define split_disc disc_ptr[vsplit_code] + +/*tex List pointers. */ + +halfword disc_ptr[(vsplit_code + 1)]; + +/*tex + + The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation, is + considerably simpler than |line_break| because it doesn't have to worry about + hyphenation, and because its mission is to discover a single break instead of + an optimum sequence of breakpoints. But before we get into the details of + |vsplit|, we need to consider a few more basic things. + + A subroutine called |prune_page_top| takes a pointer to a vlist and returns a + pointer to a modified vlist in which all glue, kern, and penalty nodes have + been deleted before the first box or rule node. However, the first box or + rule is actually preceded by a newly created glue node designed so that the + topmost baseline will be at distance |split_top_skip| from the top, whenever + this is possible without backspacing. + + When the second argument |s| is |false| the deleted nodes are destroyed, + otherwise they are collected in a list starting at |split_disc|. + +*/ + +halfword prune_page_top(halfword p, boolean s) +{ + halfword q; + /*tex Lags one step behind |p|. */ + halfword prev_p = temp_head; + halfword r = null; + vlink(temp_head) = p; + while (p != null) { + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + /*tex Insert glue for |split_top_skip| and set |p:=null|. */ + q = new_skip_param(split_top_skip_code); + vlink(prev_p) = q; + vlink(q) = p; + if (width(q) > height(p)) + width(q) = width(q) - height(p); + else + width(q) = 0; + p = null; + break; + case boundary_node: + case whatsit_node: + case mark_node: + case ins_node: + prev_p = p; + p = vlink(prev_p); + break; + case glue_node: + case kern_node: + case penalty_node: + q = p; + p = vlink(q); + vlink(q) = null; + vlink(prev_p) = p; + if (s) { + if (split_disc == null) + split_disc = q; + else + vlink(r) = q; + r = q; + } else { + flush_node_list(q); + } + break; + default: + confusion("pruning"); + break; + } + } + return vlink(temp_head); +} + +/*tex + + The next subroutine finds the best place to break a given vertical list so as + to obtain a box of height~|h|, with maximum depth~|d|. A pointer to the + beginning of the vertical list is given, and a pointer to the optimum + breakpoint is returned. The list is effectively followed by a forced break, + i.e., a penalty node with the |eject_penalty|; if the best break occurs at + this artificial node, the value |null| is returned. + +*/ + +/*tex The distance from first active node to |cur_p|: */ + +scaled active_height[10]; + +/*tex + + An array of six |scaled| distances is used to keep track of the height from + the beginning of the list to the current place, just as in |line_break|. In + fact, we use one of the same arrays, only changing its name to reflect its + new significance. + +*/ + +#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7) +#define set_height_zero(A) active_height[A]=0 /* initialize the height to zero */ + +/*tex + + A global variable |best_height_plus_depth| will be set to the natural size of + the box that corresponds to the optimum breakpoint found by |vert_break|. + (This value is used by the insertion-splitting algorithm of the page + builder.) + +*/ + +/*tex The height of the best box, without stretching or shrinking: */ + +scaled best_height_plus_depth; + +halfword vert_break(halfword p, scaled h, scaled d) +{ + /*tex + If |p| is a glue node, |type(prev_p)| determines whether |p| is a legal + breakpoint, an initial glue node is not a legal breakpoint. + */ + halfword prev_p = p; + /*tex penalty value */ + int pi = 0; + /*tex badness at a trial breakpoint */ + int b; + /*tex |type| of the node following a kern */ + int t; + /*tex the smallest badness plus penalties found so far */ + int least_cost; + /*tex the most recent break that leads to |least_cost| */ + halfword best_place = null; + /*tex depth of previous box in the list */ + scaled prev_dp = 0; + least_cost = awful_bad; + do_all_six(set_height_zero); + while (1) { + /*tex + + If node |p| is a legal breakpoint, check if this break is the best + known, and |goto done| if |p| is null or if the page-so-far is + already too full to accept more stuff. + + A subtle point to be noted here is that the maximum depth~|d| might + be negative, so |cur_height| and |prev_dp| might need to be corrected + even after a glue or kern node. + */ + if (p == null) { + pi = eject_penalty; + } else { + /*tex + + Use node |p| to update the current height and depth measurements; + if this node is not a legal breakpoint, |goto not_found| or + |update_heights|, otherwise set |pi| to the associated penalty at + the break. + + */ + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + cur_height = cur_height + prev_dp + height(p); + prev_dp = depth(p); + goto NOT_FOUND; + break; + case boundary_node: + case whatsit_node: + goto NOT_FOUND; + break; + case glue_node: + if (precedes_break(prev_p)) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case kern_node: + if (vlink(p) == null) + t = penalty_node; + else + t = type(vlink(p)); + if (t == glue_node) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case penalty_node: + pi = penalty(p); + break; + case mark_node: + case ins_node: + goto NOT_FOUND; + break; + default: + confusion("vertbreak"); + break; + } + } + /*tex + + Check if node |p| is a new champion breakpoint; then |goto done| if + |p| is a forced break or if the page-so-far is already too full. + + */ + if (pi < inf_penalty) { + /*tex Compute the badness, |b|, using |awful_bad| if the box is too full. */ + if (cur_height < h) { + if ((active_height[3] != 0) || (active_height[4] != 0) || + (active_height[5] != 0) || (active_height[6] != 0)) + b = 0; + else + b = badness(h - cur_height, active_height[2]); + } else if (cur_height - h > active_height[7]) { + b = awful_bad; + } else { + b = badness(cur_height - h, active_height[7]); + } + if (b < awful_bad) { + if (pi <= eject_penalty) + b = pi; + else if (b < inf_bad) + b = b + pi; + else + b = deplorable; + } + if (b <= least_cost) { + best_place = p; + least_cost = b; + best_height_plus_depth = cur_height + prev_dp; + } + if ((b == awful_bad) || (pi <= eject_penalty)) + goto DONE; + } + if ((type(p) < glue_node) || (type(p) > kern_node)) + goto NOT_FOUND; + UPDATE_HEIGHTS: + /*tex + + Update the current height and depth measurements with respect to a + glue or kern node~|p|. Vertical lists that are subject to the + |vert_break| procedure should not contain infinite shrinkability, + since that would permit any amount of information to ``fit'' on one + page. + + */ + if (type(p) != kern_node) { + active_height[2 + stretch_order(p)] += stretch(p); + active_height[7] += shrink(p); + if ((shrink_order(p) != normal) && (shrink(p) != 0)) { + print_err("Infinite glue shrinkage found in box being split"); + help4( + "The box you are \\vsplitting contains some infinitely", + "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.", + "Such glue doesn't belong there; but you can safely proceed,", + "since the offensive shrinkability has been made finite." + ); + error(); + shrink_order(p) = normal; + } + } + cur_height = cur_height + prev_dp + width(p); + prev_dp = 0; + NOT_FOUND: + if (prev_dp > d) { + cur_height = cur_height + prev_dp - d; + prev_dp = d; + } + prev_p = p; + p = vlink(prev_p); + } + DONE: + return best_place; +} + +/*tex + + Now we are ready to consider |vsplit| itself. Most of its work is + accomplished by the two subroutines that we have just considered. + + Given the number of a vlist box |n|, and given a desired page height |h|, the + |vsplit| function finds the best initial segment of the vlist and returns a + box for a page of height~|h|. The remainder of the vlist, if any, replaces + the original box, after removing glue and penalties and adjusting for + |split_top_skip|. Mark nodes in the split-off box are used to set the values + of |split_first_mark| and |split_bot_mark|; we use the fact that + |split_first_mark(x)=null| if and only if |split_bot_mark(x)=null|. + + The original box becomes ``void'' if and only if it has been entirely + extracted. The extracted box is ``void'' if and only if the original box was + void (or if it was, erroneously, an hlist box). + +*/ + +/*tex Extract a page of height |h| from box |n|: */ + +halfword vsplit(halfword n, scaled h, int m) +{ + /*tex the box to be split */ + halfword v; + /*tex the direction of the box to be split */ + int vdir; + /*tex runs through the vlist */ + halfword p; + /*tex points to where the break occurs */ + halfword q; + /*tex for traversing marks lists */ + halfword i; + v = box(n); + vdir = box_dir(v); + flush_node_list(split_disc); + split_disc = null; + for (i = 0; i <= biggest_used_mark; i++) { + delete_split_first_mark(i); + delete_split_bot_mark(i); + } + /*tex Dispense with trivial cases of void or bad boxes. */ + if (v == null) { + return null; + } + if (type(v) != vlist_node) { + print_err("\\vsplit needs a \\vbox"); + help2( + "The box you are trying to split is an \\hbox.", + "i can't split such a box, so I''ll leave it alone." + ); + error(); + return null; + } + q = vert_break(list_ptr(v), h, split_max_depth_par); + /*tex + + Look at all the marks in nodes before the break, and set the final link + to |null| at the break. It's possible that the box begins with a penalty + node that is the ``best'' break, so we must be careful to handle this + special case correctly. + + */ + p = list_ptr(v); + if (p == q) { + list_ptr(v) = null; + } else { + while (1) { + if (type(p) == mark_node) { + if (split_first_mark(mark_class(p)) == null) { + set_split_first_mark(mark_class(p), mark_ptr(p)); + set_split_bot_mark(mark_class(p), split_first_mark(mark_class(p))); + set_token_ref_count(split_first_mark(mark_class(p)), + token_ref_count(split_first_mark(mark_class(p))) + 2); + } else { + delete_token_ref(split_bot_mark(mark_class(p))); + set_split_bot_mark(mark_class(p), mark_ptr(p)); + add_token_ref(split_bot_mark(mark_class(p))); + } + } + if (vlink(p) == q) { + vlink(p) = null; + break; + } + p = vlink(p); + } + } + q = prune_page_top(q, saving_vdiscards_par > 0); + p = list_ptr(v); + list_ptr(v) = null; + flush_node(v); + if (q == null) { + /*tex The |eq_level| of the box stays the same. */ + box(n) = null; + } else { + box(n) = filtered_vpackage(q, 0, additional, max_depth_par, split_keep_group, vdir, 0, 0); + } + if (m == exactly) { + return filtered_vpackage(p, h, exactly, split_max_depth_par, split_off_group, vdir, 0, 0); + } else { + return filtered_vpackage(p, 0, additional, max_depth_par, split_off_group, vdir, 0, 0); + } +} + +/*tex + + Now that we can see what eventually happens to boxes, we can consider the + first steps in their creation. The |begin_box| routine is called when + |box_context| is a context specification, |cur_chr| specifies the type of box + desired, and |cur_cmd=make_box|. + +*/ + +void begin_box(int box_context) +{ + /*tex run through the current list */ + halfword q; + /*tex 0 or |vmode| or |hmode| */ + halfword k; + /*tex a box number */ + int n; + int spec_direction = -1; + int just_pack = 0; + int split_mode = exactly ; + switch (cur_chr) { + case box_code: + scan_register_num(); + cur_box = box(cur_val); + /*tex The box becomes void, at the same level. */ + box(cur_val) = null; + break; + case copy_code: + scan_register_num(); + cur_box = copy_node_list(box(cur_val)); + break; + case last_box_code: + /*tex + + If the current list ends with a box node, delete it from the list + and make |cur_box| point to it; otherwise set |cur_box:=null|. + + */ + cur_box = null; + if (abs(cur_list.mode_field) == mmode) { + you_cant(); + help1("Sorry; this \\lastbox will be void."); + error(); + } else if ((cur_list.mode_field == vmode) && (cur_list.head_field == cur_list.tail_field)) { + you_cant(); + help2( + "Sorry...I usually can't take things from the current page.", + "This \\lastbox will therefore be void." + ); + error(); + } else { + if (cur_list.head_field != cur_list.tail_field) { + if ((type(cur_list.tail_field) == hlist_node) || (type(cur_list.tail_field) == vlist_node)) { + /*tex Remove the last box */ + q = alink(cur_list.tail_field); + if (q == null || vlink(q) != cur_list.tail_field) { + q = cur_list.head_field; + while (vlink(q) != cur_list.tail_field) + q = vlink(q); + } + uncouple_node(cur_list.tail_field); + cur_box = cur_list.tail_field; + shift_amount(cur_box) = 0; + cur_list.tail_field = q; + vlink(cur_list.tail_field) = null; + } + } + } + break; + case vsplit_code: + /*tex + + Split off part of a vertical box, make |cur_box| point to it. + Here we deal with things like `\.{\\vsplit 13 to 100pt}'. + + */ + scan_register_num(); + n = cur_val; + if (scan_keyword("upto")) { + split_mode = additional ; + } else if (!scan_keyword("to")) { + print_err("Missing `to' inserted"); + help2( + "I'm working on `\\vsplit<box number> to <dimen>';", + "will look for the <dimen> next." + ); + error(); + } + scan_normal_dimen(); + cur_box = vsplit(n, cur_val, split_mode); + break; + default: + /*tex + + Initiate the construction of an hbox or vbox, then |return|. Here + is where we enter restricted horizontal mode or internal vertical + mode, in order to make a box. + + */ + switch (cur_chr) { + case tpack_code: + cur_chr = vtop_code; + just_pack = 1; + break; + case vpack_code: + cur_chr = vtop_code + vmode; + just_pack = 1; + break; + case hpack_code: + cur_chr = vtop_code + hmode; + just_pack = 1; + break; + } + k = cur_chr - vtop_code; + set_saved_record(0, saved_boxcontext, 0, box_context); + switch (abs(cur_list.mode_field)) { + case vmode: + spec_direction = body_direction_par; + break; + case hmode: + spec_direction = text_direction_par; + break; + case mmode: + spec_direction = math_direction_par; + break; + } + if (k == hmode) { + if ((box_context < box_flag) && (abs(cur_list.mode_field) == vmode)) + scan_full_spec(adjusted_hbox_group, spec_direction,just_pack); + else + scan_full_spec(hbox_group, spec_direction,just_pack); + } else { + if (k == vmode) { + scan_full_spec(vbox_group, spec_direction,just_pack); + } else { + scan_full_spec(vtop_group, spec_direction,just_pack); + k = vmode; + } + normal_paragraph(); + } + push_nest(); +/* new */ +/* eq_word_define(int_base + no_local_whatsits_code, 0); */ +eq_word_define(int_base + no_local_dirs_code, 0); + cur_list.mode_field = -k; + if (k == vmode) { + prev_depth_par = ignore_depth; + if (every_vbox_par != null) + begin_token_list(every_vbox_par, every_vbox_text); + } else { + space_factor_par = 1000; + if (every_hbox_par != null) + begin_token_list(every_hbox_par, every_hbox_text); + } + return; + break; + } + /*tex In simple cases, we use the box immediately. */ + box_end(box_context); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/packaging.h b/Build/source/texk/web2c/luatexdir/tex/packaging.h new file mode 100644 index 00000000000..9353afeaf20 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/packaging.h @@ -0,0 +1,156 @@ +/* packaging.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef PACKAGING_H +# define PACKAGING_H + +/* We define some constants used when calling |hpack| to deal with font expansion. */ + +typedef enum { + exactly = 0, /*a box dimension is pre-specified */ + additional, /*a box dimension is increased from the natural one */ + cal_expand_ratio, /* calculate amount for font expansion after breaking paragraph into lines */ + subst_ex_font /* substitute fonts */ +} hpack_subtypes; + +# define substituted 3 /* |subtype| of kern nodes that should be substituted */ + +extern void scan_spec(group_code c); +extern void scan_full_spec(group_code c, int spec_direction, int justpack); + +extern scaled total_stretch[5]; +extern scaled total_shrink[5]; /* glue found by |hpack| or |vpack| */ +extern int last_badness; /* badness of the most recently packaged box */ +extern halfword adjust_tail; /* tail of adjustment list */ +extern halfword pre_adjust_tail; +extern int font_expand_ratio; /* current expansion ratio */ +extern halfword last_leftmost_char; +extern halfword last_rightmost_char; +extern halfword next_char_p; /* pointer to the next char of an implicit kern */ +extern halfword prev_char_p; /* pointer to the previous char of an implicit kern */ + +extern void set_prev_char_p(halfword p); +extern scaled char_stretch(halfword p); +extern scaled char_shrink(halfword p); +extern scaled kern_stretch(halfword p); +extern scaled kern_shrink(halfword p); +extern void do_subst_font(halfword p, int ex_ratio); +extern scaled char_pw(halfword p, int side); +extern halfword new_margin_kern(scaled w, halfword p, int side); + +# define update_adjust_list(A) do { \ + if (A == null) \ + normal_error("pre vadjust", "adjust_tail or pre_adjust_tail is null"); \ + vlink(A) = adjust_ptr(p); \ + while (vlink(A) != null) \ + A = vlink(A); \ + } while (0) + +extern halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int d, int just_pack, halfword attr); +extern scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, int g_sign, int g_order, int d); +extern halfword hpack(halfword p, scaled w, int m, int d); + +extern int pack_begin_line; + +extern halfword vpackage(halfword p, scaled h, int m, scaled l, int d); +extern halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, int d, int just_pack, halfword attr); +extern void finish_vcenter(void); +extern void package(int c); +extern void append_to_vlist(halfword b, int location); + +# define vpack(A,B,C,D) vpackage(A,B,C,max_dimen,D) /* special case of unconstrained depth */ + +extern halfword prune_page_top(halfword p, boolean s); +extern scaled active_height[10]; /* distance from first active node to~|cur_p| */ + +# define cur_height active_height[1] /* the natural height */ +# define awful_bad 07777777777 /* more than a billion demerits */ +# define deplorable 100000 /* more than |inf_bad|, but less than |awful_bad| */ + +extern scaled best_height_plus_depth; /* height of the best box, without stretching or shrinking */ + +extern halfword vert_break(halfword p, scaled h, scaled d); +extern halfword vsplit(halfword n, scaled h, int m); /* extracts a page of height |h| from box |n| */ + +# define box_code 0 /* |chr_code| for `\.{\\box}' */ +# define copy_code 1 /* |chr_code| for `\.{\\copy}' */ +# define last_box_code 2 /* |chr_code| for `\.{\\lastbox}' */ +# define vsplit_code 3 /* |chr_code| for `\.{\\vsplit}' */ +# define tpack_code 4 +# define vpack_code 5 +# define hpack_code 6 +# define vtop_code 7 /* |chr_code| for `\.{\\vtop}' */ + +# define tail_page_disc disc_ptr[copy_code] /* last item removed by page builder */ +# define page_disc disc_ptr[last_box_code] /* first item removed by page builder */ +# define split_disc disc_ptr[vsplit_code] /* first item removed by \.{\\vsplit} */ + +extern halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */ + +/* + +Now let's turn to the question of how \.{\\hbox} is treated. We actually need to +consider also a slightly larger context, since constructions like + +`\.{\\setbox3=}\penalty0\.{\\hbox...}' and +`\.{\\leaders}\penalty0\.{\\hbox...}' and +`\.{\\lower3.8pt\\hbox...}' + +are supposed to invoke quite different actions after the box has been packaged. +Conversely, constructions like `\.{\\setbox3=}' can be followed by a variety of +different kinds of boxes, and we would like to encode such things in an efficient +way. + +In other words, there are two problems: To represent the context of a box, and to +represent its type. The first problem is solved by putting a ``context code'' on +the |save_stack|, just below the two entries that give the dimensions produced by +|scan_spec|. The context code is either a (signed) shift amount, or it is a large +integer |>=box_flag|, where |box_flag=@t$2^{30}$@>|. Codes |box_flag| through +|box_flag+biggest_reg| represent `\.{\\setbox0}' through +`\.{\\setbox}|biggest_reg|'; codes |box_flag+biggest_reg+1| through +|box_flag+2*biggest_reg| represent `\.{\\global\\setbox0}' through +`\.{\\global\\setbox}|biggest_reg|'; code |box_flag+2*number_regs| represents +`\.{\\shipout}'; and codes |box_flag+2*number_regs+1| through +|box_flag+2*number_regs+3| represent `\.{\\leaders}', `\.{\\cleaders}', and +`\.{\\xleaders}'. + +The second problem is solved by giving the command code |make_box| to all control +sequences that produce a box, and by using the following |chr_code| values to +distinguish between them: |box_code|, |copy_code|, |last_box_code|, +|vsplit_code|, |vtop_code|, |vtop_code+vmode|, and |vtop_code+hmode|, where the +latter two are used denote \.{\\vbox} and \.{\\hbox}, respectively. + +*/ + +# define box_flag 010000000000 /* context code for `\.{\\setbox0}' */ +# define global_box_flag (box_flag+number_regs) /* context code for `\.{\\global\\setbox0}' */ +# define max_global_box_flag (global_box_flag+number_regs) +# define ship_out_flag (max_global_box_flag+1) /* context code for `\.{\\shipout}' */ +# define lua_scan_flag (max_global_box_flag+2) /* context code for |scan_list| */ +# define leader_flag (max_global_box_flag+3) /* context code for `\.{\\leaders}' */ + +extern void begin_box(int box_context); + +#define math_skip_boundary(n) \ +(n && type(n) == glue_node && (subtype(n) == space_skip_subtype || subtype(n) == xspace_skip_subtype)) + +extern int ignore_math_skip(halfword p) ; + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c new file mode 100644 index 00000000000..c6f82d56052 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c @@ -0,0 +1,603 @@ +/* + +postlinebreak.w + +Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +So far we have gotten a little way into the |line_break| routine, having covered +its important |try_break| subroutine. Now let's consider the rest of the process. + +The main loop of |line_break| traverses the given hlist, starting at +|vlink(temp_head)|, and calls |try_break| at each legal breakpoint. A variable +called |auto_breaking| is set to true except within math formulas, since glue +nodes are not legal breakpoints when they appear in formulas. + +The current node of interest in the hlist is pointed to by |cur_p|. Another +variable, |prev_p|, is usually one step behind |cur_p|, but the real meaning of +|prev_p| is this: If |type(cur_p)=glue_node| then |cur_p| is a legal breakpoint +if and only if |auto_breaking| is true and |prev_p| does not point to a glue +node, penalty node, explicit kern node, or math node. + +The total number of lines that will be set by |post_line_break| is +|best_line-prev_graf-1|. The last breakpoint is specified by +|break_node(best_bet)|, and this passive node points to the other breakpoints via +the |prev_break| links. The finishing-up phase starts by linking the relevant +passive nodes in forward order, changing |prev_break| to |next_break|. (The +|next_break| fields actually reside in the same memory space as the |prev_break| +fields did, but we give them a new name because of their new significance.) Then +the lines are justified, one by one. + +The |post_line_break| must also keep an dir stack, so that it can output end +direction instructions at the ends of lines and begin direction instructions at +the beginnings of lines. + +*/ + +/*tex The new name for |prev_break| after links are reversed: */ + +#define next_break prev_break + +/*tex The |int|s are actually halfwords. */ + +void ext_post_line_break(int paragraph_dir, + int right_skip, + int left_skip, + int protrude_chars, + halfword par_shape_ptr, + int adjust_spacing, + halfword inter_line_penalties_ptr, + int inter_line_penalty, + int club_penalty, + halfword club_penalties_ptr, + halfword widow_penalties_ptr, + int widow_penalty, + int broken_penalty, + halfword final_par_glue, + halfword best_bet, + halfword last_special_line, + scaled second_width, + scaled second_indent, + scaled first_width, + scaled first_indent, halfword best_line) +{ + + boolean have_directional = true; + /*tex temporary registers for list manipulation */ + halfword q, r; + halfword k; + scaled w; + /*tex was a break at glue? */ + boolean glue_break; + /*tex was the current break at a discretionary node? */ + boolean disc_break; + /*tex and did it have a nonempty post-break part? */ + boolean post_disc_break; + /*tex width of line number |cur_line| */ + scaled cur_width; + /*tex left margin of line number |cur_line| */ + scaled cur_indent; + /*tex use when calculating penalties between lines */ + int pen; + /*tex |cur_p|, but localized */ + halfword cur_p; + /*tex the current line number being justified */ + halfword cur_line; + /*tex the current direction: */ + dir_ptr = cur_list.dirs_field; + /*tex + Reverse the links of the relevant passive nodes, setting |cur_p| to the + first breakpoint. The job of reversing links in a list is conveniently + regarded as the job of taking items off one stack and putting them on + another. In this case we take them off a stack pointed to by |q| and + having |prev_break| fields; we put them on a stack pointed to by |cur_p| + and having |next_break| fields. Node |r| is the passive node being moved + from stack to stack. + */ + q = break_node(best_bet); + /*tex |cur_p| will become the first breakpoint; */ + cur_p = null; + do { + r = q; + q = prev_break(q); + next_break(r) = cur_p; + cur_p = r; + } while (q != null); + /*tex prevgraf + 1 */ + cur_line = cur_list.pg_field + 1; + do { + /*tex + Justify the line ending at breakpoint |cur_p|, and append it to the + current vertical list, together with associated penalties and other + insertions. + + The current line to be justified appears in a horizontal list + starting at |vlink(temp_head)| and ending at |cur_break(cur_p)|. If + |cur_break(cur_p)| is a glue node, we reset the glue to equal the + |right_skip| glue; otherwise we append the |right_skip| glue at the + right. If |cur_break(cur_p)| is a discretionary node, we modify the + list so that the discretionary break is compulsory, and we set + |disc_break| to |true|. We also append the |left_skip| glue at the + left of the line, unless it is zero. + */ + if (dir_ptr != null) { + /*tex Insert dir nodes at the beginning of the current line. */ + for (q = dir_ptr; q != null; q = vlink(q)) { + halfword tmp = new_dir(dir_dir(q)); + halfword nxt = vlink(temp_head); + delete_attribute_ref(node_attr(tmp)); + node_attr(tmp) = node_attr(temp_head); + add_node_attr_ref(node_attr(tmp)); + couple_nodes(temp_head, tmp); + /*tex \.{\\break}\.{\\par} */ + try_couple_nodes(tmp, nxt); + } + flush_node_list(dir_ptr); + dir_ptr = null; + } + /*tex + Modify the end of the line to reflect the nature of the break and to + include \.{\\rightskip}; also set the proper value of |disc_break|. + At the end of the following code, |q| will point to the final node on + the list about to be justified. In the meanwhile |r| will point to + the node we will use to insert end-of-line stuff after. |q==null| + means we use the final position of |r|. + */ + + if (temp_head != null) { + /*tex begin mathskip code */ + q = temp_head; + while(q != null) { + if (type(q) == math_node) { + surround(q) = 0 ; + reset_glue_to_zero(q); + break; + } else if ((type(q) == hlist_node) && (subtype(q) == indent_list)) { + /* go on */ + } else if (is_char_node(q)) { + break; + } else if (non_discardable(q)) { + break; + } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) { + break; + } + q = vlink(q); + } + /*tex end mathskip code */ + } + + r = cur_break(cur_p); + q = null; + disc_break = false; + post_disc_break = false; + glue_break = false; + + + if (r == null) { + for (r = temp_head; vlink(r) != null; r = vlink(r)); + if (r == final_par_glue) { + /*tex This should almost always be true... */ + q = r; + /*tex |q| refers to the last node of the line (and paragraph) */ + r = alink(r); + } + /*tex |r| refers to the node after which the dir nodes should be closed */ + } else if (type(r) == math_node) { + surround(r) = 0; + /*tex begin mathskip code */ + reset_glue_to_zero(r); + /*tex end mathskip code */ + } else if (type(r) == glue_node) { + copy_glue_values(r,right_skip); + subtype(r) = right_skip_code + 1; + glue_break = true; + /*tex |q| refers to the last node of the line */ + q = r; + r = alink(r); + assert(vlink(r) == q); + /*tex |r| refers to the node after which the dir nodes should be closed */ + } else if (type(r) == disc_node) { + halfword a = alink(r); + halfword v = vlink(r); + assert(a != null); + assert(v != null); + switch (subtype(r)) { + case select_disc: + if (vlink_pre_break(r) != null) { + flush_node_list(vlink_pre_break(r)); + vlink_pre_break(r) = null; + tlink_pre_break(r) = null; + } + if (vlink_no_break(r) != null) { + couple_nodes(a, vlink_no_break(r)); + couple_nodes(tlink_no_break(r), r); + vlink_no_break(r) = null; + tlink_no_break(r) = null; + } + assert(type(a) == disc_node && subtype(a) == init_disc); + flush_node_list(vlink_no_break(a)); + vlink_no_break(a) = null; + tlink_no_break(a) = null; + flush_node_list(vlink_pre_break(a)); + vlink_pre_break(a) = null; + tlink_pre_break(a) = null; + flush_node_list(vlink_post_break(a)); + vlink_post_break(a) = null; + tlink_post_break(a) = null; + break; + case init_disc: + assert(type(v) == disc_node && subtype(v) == select_disc); + subtype(v) = syllable_disc; + flush_node_list(vlink_no_break(v)); + vlink_no_break(v) = vlink_post_break(r); + tlink_no_break(v) = tlink_post_break(r); + vlink_post_break(r) = null; + tlink_post_break(r) = null; + default: + if (vlink_no_break(r) != null) { + flush_node_list(vlink_no_break(r)); + vlink_no_break(r) = null; + tlink_no_break(r) = null; + } + if (vlink_pre_break(r) != null) { + couple_nodes(a, vlink_pre_break(r)); + couple_nodes(tlink_pre_break(r), r); + vlink_pre_break(r) = null; + tlink_pre_break(r) = null; + } + } + if (vlink_post_break(r) != null) { + couple_nodes(r, vlink_post_break(r)); + couple_nodes(tlink_post_break(r), v); + vlink_post_break(r) = null; + tlink_post_break(r) = null; + post_disc_break = true; + } + disc_break = true; + } else if (type(r) == kern_node) { + width(r) = 0; + } + /*tex Adjust the dir stack based on dir nodes in this line. */ + if (have_directional) { + halfword e, p; + for (e = vlink(temp_head); e != null && e != cur_break(cur_p); e = vlink(e)) { + if (type(e) == dir_node) { + if (subtype(e) == normal_dir) { + dir_ptr = do_push_dir_node(dir_ptr, e); + } else if (dir_ptr != null && dir_dir(dir_ptr) == dir_dir(e)) { + dir_ptr = do_pop_dir_node(dir_ptr); + } + } + } + assert(e == cur_break(cur_p)); + /*tex Insert dir nodes at the end of the current line. */ + e = vlink(r); + for (p = dir_ptr; p != null; p = vlink(p)) { + halfword s = new_dir(dir_dir(p)); + subtype(s) = cancel_dir; + delete_attribute_ref(node_attr(s)); + node_attr(s) = node_attr(r); + add_node_attr_ref(node_attr(s)); + couple_nodes(r, s); + try_couple_nodes(s, e); + r = s; + } + } + if (passive_right_box(cur_p) != null) { + halfword s = copy_node_list(passive_right_box(cur_p)); + halfword e = vlink(r); + couple_nodes(r, s); + try_couple_nodes(s, e); + r = s; + } + if (q == null) { + q = r; + } + /*tex + Now [q] refers to the last node on the line and therefore the + rightmost breakpoint. The only exception is the case of a + discretionary break with non-empty |pre_break|, then |q| has been + changed to the last node of the |pre_break| list. If the par ends + with a \break command, the last line is utterly empty. That is the + case of |q==temp_head|. + */ + if (q != temp_head && protrude_chars > 0) { + halfword p, ptmp; + if (disc_break && (is_char_node(q) || (type(q) != disc_node))) { + /*tex |q| is reset to the last node of |pre_break| */ + p = q; + ptmp = p; + } else { + /*tex get |vlink(p) = q| */ + p = alink(q); + assert(vlink(p) == q); + ptmp = p; + } + p = find_protchar_right(vlink(temp_head), p); + w = char_pw(p, right_side); + if (w != 0) { + /*tex we have found a marginal kern, append it after |ptmp| */ + k = new_margin_kern(-w, last_rightmost_char, right_side); + delete_attribute_ref(node_attr(k)); + node_attr(k) = node_attr(p); + add_node_attr_ref(node_attr(k)); + try_couple_nodes(k, vlink(ptmp)); + couple_nodes(ptmp,k); + if (ptmp == q) + q = vlink(q); + } + } + /*tex + If |q| was not a breakpoint at glue and has been reset to |rightskip| + then we append |rightskip| after |q| now. + */ + if (!glue_break) { + /*tex Put the \.{\\rightskip} glue after node |q|. */ + halfword r1 = new_glue((right_skip == null ? zero_glue : right_skip)); + subtype(r1) = right_skip_code+1; + try_couple_nodes(r1,vlink(q)); + delete_attribute_ref(node_attr(r1)); + node_attr(r1) = node_attr(q); + add_node_attr_ref(node_attr(r1)); + couple_nodes(q,r1); + q = r1; + } + /*tex + Modify the end of the line to reflect the nature of the break and to + include \.{\\rightskip}; also set the proper value of |disc_break|; + Also put the \.{\\leftskip} glue at the left and detach this line. + + The following code begins with |q| at the end of the list to be + justified. It ends with |q| at the beginning of that list, and with + |vlink(temp_head)| pointing to the remainder of the paragraph, if + any. + */ + r = vlink(q); + vlink(q) = null; + + q = vlink(temp_head); + try_couple_nodes(temp_head, r); + if (passive_left_box(cur_p) != null && passive_left_box(cur_p) != 0) { + halfword s; + r = copy_node_list(passive_left_box(cur_p)); + s = vlink(q); + couple_nodes(r,q); + q = r; + if ((cur_line == cur_list.pg_field + 1) && (s != null)) { + if (type(s) == hlist_node) { + if (list_ptr(s) == null) { + q = vlink(q); + try_couple_nodes(r,vlink(s)); + try_couple_nodes(s, r); + } + } + } + } + /*tex + At this point |q| is the leftmost node; all discardable nodes have + been discarded + */ + if (protrude_chars > 0) { + halfword p; + p = q; + p = find_protchar_left(p, false); + w = char_pw(p, left_side); + if (w != 0) { + k = new_margin_kern(-w, last_leftmost_char, left_side); + delete_attribute_ref(node_attr(k)); + node_attr(k) = node_attr(q); + add_node_attr_ref(node_attr(k)); + couple_nodes(k,q); + q = k; + } + } + if (! glue_is_zero(left_skip)) { + r = new_glue(left_skip); + subtype(r) = left_skip_code+1; + delete_attribute_ref(node_attr(r)); + node_attr(r) = node_attr(q); + add_node_attr_ref(node_attr(r)); + couple_nodes(r,q); + q = r; + } + /*tex + Put the \.{\\leftskip} glue at the left and detach this line. Call + the packaging subroutine, setting |just_box| to the justified box. + Now |q| points to the hlist that represents the current line of the + paragraph. We need to compute the appropriate line width, pack the + line into a box of this size, and shift the box by the appropriate + amount of indentation. + */ + if (cur_line > last_special_line) { + cur_width = second_width; + cur_indent = second_indent; + } else if (par_shape_ptr == null) { + cur_width = first_width; + cur_indent = first_indent; + } else { + cur_indent = varmem[(par_shape_ptr + 2 * cur_line)].cint; + cur_width = varmem[(par_shape_ptr + 2 * cur_line + 1)].cint; + /* needs checking */ + cur_indent = swap_parshape_indent(cur_indent,cur_width); + } + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + if (adjust_spacing > 0) { + just_box = hpack(q, cur_width, cal_expand_ratio, paragraph_dir); + } else { + just_box = hpack(q, cur_width, exactly, paragraph_dir); + } + shift_amount(just_box) = cur_indent; + subtype(just_box) = line_list; + /*tex + Call the packaging subroutine, setting |just_box| to the justified + box. + */ + if ((vlink(contrib_head) != null)) + checked_break_filter(pre_box); + if (pre_adjust_head != pre_adjust_tail) { + append_list(pre_adjust_head, pre_adjust_tail); + checked_break_filter(pre_adjust); + } + pre_adjust_tail = null; + append_to_vlist(just_box,lua_key_index(post_linebreak)); + checked_break_filter(box); + if (adjust_head != adjust_tail) { + append_list(adjust_head, adjust_tail); + checked_break_filter(adjust); + } + adjust_tail = null; + /*tex + Append the new box to the current vertical list, followed by the list + of special nodes taken out of the box by the packager. Append a + penalty node, if a nonzero penalty is appropriate. Penalties between + the lines of a paragraph come from club and widow lines, from the + |inter_line_penalty| parameter, and from lines that end at + discretionary breaks. Breaking between lines of a two-line paragraph + gets both club-line and widow-line penalties. The local variable + |pen| will be set to the sum of all relevant penalties for the + current line, except that the final line is never penalized. + */ + if (cur_line + 1 != best_line) { + q = inter_line_penalties_ptr; + if (q != null) { + r = cur_line; + if (r > penalty(q)) + r = penalty(q); + pen = penalty(q + r); + } else { + if (passive_pen_inter(cur_p) != 0) { + pen = passive_pen_inter(cur_p); + } else { + pen = inter_line_penalty; + } + } + q = club_penalties_ptr; + if (q != null) { + /*tex prevgraf */ + r = cur_line - cur_list.pg_field; + if (r > penalty(q)) + r = penalty(q); + pen += penalty(q + r); + } else if (cur_line == cur_list.pg_field + 1) { + /*tex prevgraf */ + pen += club_penalty; + } + q = widow_penalties_ptr; + if (q != null) { + r = best_line - cur_line - 1; + if (r > penalty(q)) + r = penalty(q); + pen += penalty(q + r); + } else if (cur_line + 2 == best_line) { + pen += widow_penalty; + } + if (disc_break) { + if (passive_pen_broken(cur_p) != 0) { + pen += passive_pen_broken(cur_p); + } else { + pen += broken_penalty; + } + } + if (pen != 0) { + r = new_penalty(pen,linebreak_penalty); + couple_nodes(cur_list.tail_field, r); + cur_list.tail_field = r; + } + } + /*tex + Append a penalty node, if a nonzero penalty is appropriate. Justify + the line ending at breakpoint |cur_p|, and append it to the current + vertical list, together with associated penalties and other + insertions. + */ + incr(cur_line); + cur_p = next_break(cur_p); + if (cur_p != null && !post_disc_break) { + /*tex + Prune unwanted nodes at the beginning of the next line. Glue and + penalty and kern and math nodes are deleted at the beginning of a + line, except in the anomalous case that the node to be deleted is + actually one of the chosen breakpoints. Otherwise the pruning + done here is designed to match the lookahead computation in + |try_break|, where the |break_width| values are computed for + non-discretionary breakpoints. + */ + r = temp_head; + /*tex + Normally we have a matching math open and math close node but + when we cross a line the open node is removed, including any glue + or penalties following it. This is however not that nice for + callbacks that rely on symmetry. Of course this only counts for + one liners, as we can still have only a begin or end node on a + line. The end_of_math lua helper is made robust against this + although there you should be aware of the fact that one can end + up in the middle of math in callbacks that don't work on whole + paragraphs, but at least this branch makes sure that some proper + analysis is possible. (todo: check if math glyphs have the + subtype marked done). + */ + while (1) { + q = vlink(r); + /* + if (q == cur_break(cur_p) || is_char_node(q)) + break; + if (!((type(q) == local_par_node))) { + if (non_discardable(q) || (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern)) + break; + } + */ + if (type(q) == math_node) { + /*tex begin mathskip code */ + surround(q) = 0 ; + reset_glue_to_zero(q); + /*tex end mathskip code */ + } + if (q == cur_break(cur_p)) { + break; + } else if (is_char_node(q)) { + break; + } else if (type(q) == local_par_node) { + /*tex weird, in the middle somewhere */ + } else if (non_discardable(q)) { + break; + } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) { + break; + } + r = q; + } + if (r != temp_head) { + vlink(r) = null; + flush_node_list(vlink(temp_head)); + try_couple_nodes(temp_head, q); + } + } + } while (cur_p != null); + if ((cur_line != best_line) || (vlink(temp_head) != null)) + confusion("line breaking"); + /*tex prevgraf */ + cur_list.pg_field = best_line - 1; + /*tex |dir_save| */ + cur_list.dirs_field = dir_ptr; + dir_ptr = null; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h new file mode 100644 index 00000000000..4aee1492c71 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.h @@ -0,0 +1,50 @@ +/* postlinebreak.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef POSTLINEBREAK_H +# define POSTLINEBREAK_H + +# define append_list(A,B) do { \ + couple_nodes(cur_list.tail_field,vlink((A))); \ + cur_list.tail_field = (B); \ +} while (0) + +void ext_post_line_break(int paragraph_dir, + int right_skip, + int left_skip, + int protrude_chars, + halfword par_shape_ptr, + int adjust_spacing, + halfword inter_line_penalties_ptr, + int inter_line_penalty, + int club_penalty, + halfword club_penalties_ptr, + halfword widow_penalties_ptr, + int widow_penalty, + int broken_penalty, + halfword final_par_glue, + halfword best_bet, + halfword last_special_line, + scaled second_width, + scaled second_indent, + scaled first_width, + scaled first_indent, halfword best_line); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/primitive.c b/Build/source/texk/web2c/luatexdir/tex/primitive.c new file mode 100644 index 00000000000..f16d69a333c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/primitive.c @@ -0,0 +1,785 @@ +/* + +primitive.w + +Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +Control sequences are stored and retrieved by means of a fairly standard hash +table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C in +{\sl The Art of Computer Programming\/}). Once a control sequence enters the +table, it is never removed, because there are complicated situations involving +\.{\\gdef} where the removal of a control sequence at the end of a group would be +a mistake preventable only by the introduction of a complicated reference-count +mechanism. + +The actual sequence of letters forming a control sequence identifier is stored in +the |str_pool| array together with all the other strings. An auxiliary array +|hash| consists of items with two halfword fields per word. The first of these, +called |next(p)|, points to the next identifier belonging to the same coalesced +list as the identifier corresponding to~|p|; and the other, called |text(p)|, +points to the |str_start| entry for |p|'s identifier. If position~|p| of the hash +table is empty, we have |text(p)=0|; if position |p| is either empty or the end +of a coalesced hash list, we have |next(p)=0|. An auxiliary pointer variable +called |hash_used| is maintained in such a way that all locations |p>=hash_used| +are nonempty. The global variable |cs_count| tells how many multiletter control +sequences have been defined, if statistics are being kept. + +A global boolean variable called |no_new_control_sequence| is set to |true| +during the time that new hash table entries are forbidden. + +*/ + +/*tex The hash table: */ + +two_halves *hash; + +/*tex Allocation pointer for |hash|: */ + +halfword hash_used; + +/*tex |hash_extra=hash| above |eqtb_size|: */ + +int hash_extra; + +/*tex Maximum of the hash array: */ + +halfword hash_top; + +/*tex Pointer to next high hash location: */ + +halfword hash_high; + +/*tex Are new identifiers legal? */ + +boolean no_new_control_sequence; + +/*tex Total number of known identifiers: */ + +int cs_count; + +/*tex Test if all positions are occupied: */ + +#define hash_is_full (hash_used==hash_base) + +/*tex + + \.{\\primitive} support needs a few extra variables and definitions, + like: + +*/ + +#define prim_base 1 + +/*tex + +The arrays |prim| and |prim_eqtb| are used for name -> cmd,chr lookups. The are +modelled after |hash| and |eqtb|, except that primitives do not have an +|eq_level|, that field is replaced by |origin|. + +*/ + +/*tex Link for coalesced lists: */ + +#define prim_next(a) prim[(a)].lhfield + +/*tex String number for control sequence name: */ + +#define prim_text(a) prim[(a)].rh + +/*tex Test if all positions are occupied: */ + +#define prim_is_full (prim_used==prim_base) + +#define prim_origin_field(a) (a).hh.b1 + +#define prim_eq_type_field(a) (a).hh.b0 + +#define prim_equiv_field(a) (a).hh.rh + +/*tex Level of definition: */ + +#define prim_origin(a) prim_origin_field(prim_eqtb[(a)]) + +/*tex Command code for equivalent: */ + +#define prim_eq_type(a) prim_eq_type_field(prim_eqtb[(a)]) + +/*tex Equivalent value: */ + +#define prim_equiv(a) prim_equiv_field(prim_eqtb[(a)]) + +/*tex Allocation pointer for |prim|: */ + +static pointer prim_used; + +/*tex The primitives table: */ + +static two_halves prim[(prim_size + 1)]; + +static memory_word prim_eqtb[(prim_size + 1)]; + +/*tex + +The array |prim_data| works the other way around, it is used for cmd,chr -> name +lookups. + +*/ + +typedef struct prim_info { + /*tex Number of name entries: */ + halfword subids; + /*tex Offset to be used for |chr_code|s: */ + halfword offset; + /*tex Array of names: */ + str_number *names; +} prim_info; + +static prim_info prim_data[(last_cmd + 1)]; + +/*tex + +Initialize the memory arrays: + +*/ + +void init_primitives(void) +{ + int k; + memset(prim_data, 0, (sizeof(prim_info) * (last_cmd + 1))); + memset(prim, 0, (sizeof(two_halves) * (prim_size + 1))); + memset(prim_eqtb, 0, (sizeof(memory_word) * (prim_size + 1))); + for (k = 0; k <= prim_size; k++) { + prim_eq_type(k) = undefined_cs_cmd; + } +} + +/*tex Nothing is used (yet). */ + +void ini_init_primitives(void) +{ + prim_used = prim_size; +} + + +/*tex + +The value of |hash_prime| should be roughly 85\%! of |hash_size|, and it should +be a prime number. The theory of hashing tells us to expect fewer than two table +probes, on the average, when the search is successful. [See J.~S. Vitter, {\sl +Journal of the ACM\/ \bf30} (1983), 231--258.] @^Vitter, Jeffrey Scott@> + +*/ + +static halfword compute_hash(const char *j, unsigned int l, halfword prime_number) +{ + int k; + halfword h = (unsigned char) *j; + for (k = 1; k <= (int)(l - 1); k++) { + h = h + h + (unsigned char) *(j + k); + while (h >= prime_number) { + h = h - prime_number; + } + } + return h; +} + +/*tex + +Here is the subroutine that searches the primitive table for an identifier. + +*/ + +pointer prim_lookup(str_number s) +{ + /*tex The hash code: */ + int h; + /*tex The index in the |hash| array: */ + pointer p; + unsigned char *j; + unsigned l; + if (s < STRING_OFFSET) { + p = s; + if ((p < 0) || (get_prim_eq_type(p) == undefined_cs_cmd)) { + p = undefined_primitive; + } + } else { + j = str_string(s); + l = (unsigned) str_length(s); + h = compute_hash((char *) j, l, prim_prime); + /*tex We start searching here; note that |0<=h<hash_prime|. */ + p = h + prim_base; + while (1) { + if (prim_text(p) > 0) + if (str_length(prim_text(p)) == l) + if (str_eq_str(prim_text(p), s)) + goto FOUND; + if (prim_next(p) == 0) { + if (no_new_control_sequence) { + p = undefined_primitive; + } else { + /*tex Insert a new primitive after |p|, then make |p| point to it. */ + if (prim_text(p) > 0) { + do { + /*tex Search for an empty location in |prim| */ + if (prim_is_full) { + overflow("primitive size", prim_size); + } + decr(prim_used); + } while (prim_text(prim_used) != 0); + prim_next(p) = prim_used; + p = prim_used; + } + prim_text(p) = s; + } + goto FOUND; + } + p = prim_next(p); + } + } + FOUND: + return p; +} + +/*tex + +How to test a csname for primitive-ness? + +*/ + +boolean is_primitive(str_number csname) +{ + int n, m; + char *ss; + m = prim_lookup(csname); + ss = makecstring(csname); + n = string_lookup(ss, str_length(csname)); + free(ss); + return ((n != undefined_cs_cmd) && (m != undefined_primitive) && + (eq_type(n) == prim_eq_type(m)) && (equiv(n) == prim_equiv(m))); +} + + +/*tex + +A few simple accessors. + +*/ + +quarterword get_prim_eq_type(int p) +{ + return prim_eq_type(p); +} + +quarterword get_prim_origin(int p) +{ + return prim_origin(p); +} + +halfword get_prim_equiv(int p) +{ + return prim_equiv(p); +} + +str_number get_prim_text(int p) +{ + return prim_text(p); +} + + +/*tex + +Dumping and undumping. + +*/ + +void dump_primitives(void) +{ + int p, q; + for (p = 0; p <= prim_size; p++) { + dump_hh(prim[p]); + } + for (p = 0; p <= prim_size; p++) { + dump_wd(prim_eqtb[p]); + } + for (p = 0; p <= last_cmd; p++) { + dump_int(prim_data[p].offset); + dump_int(prim_data[p].subids); + for (q = 0; q < prim_data[p].subids; q++) { + dump_int(prim_data[p].names[q]); + } + } +} + +void undump_primitives(void) +{ + int p, q; + for (p = 0; p <= prim_size; p++) { + undump_hh(prim[p]); + } + for (p = 0; p <= prim_size; p++) { + undump_wd(prim_eqtb[p]); + } + for (p = 0; p <= last_cmd; p++) { + undump_int(prim_data[p].offset); + undump_int(prim_data[p].subids); + if (prim_data[p].subids > 0) { + prim_data[p].names = (str_number *) xmalloc((unsigned) ((unsigned) prim_data[p].subids * sizeof(str_number *))); + for (q = 0; q < prim_data[p].subids; q++) { + undump_int(prim_data[p].names[q]); + } + } + } +} + +/*tex + +We need to put \TeX's ``primitive'' control sequences into the hash table, +together with their command code (which will be the |eq_type|) and an operand +(which will be the |equiv|). The |primitive| procedure does this, in a way that +no \TeX\ user can. The global value |cur_val| contains the new |eqtb| pointer +after |primitive| has acted. + +Because the definitions of the actual user-accessible name of a primitive can be +postponed until runtime, the function |primitive_def| is needed that does nothing +except creating the control sequence name. + +*/ + +void primitive_def(const char *s, size_t l, quarterword c, halfword o) +{ + int nncs = no_new_control_sequence; + no_new_control_sequence = false; + /*tex This creates the |text()| string: */ + cur_val = string_lookup(s, l); + no_new_control_sequence = nncs; + eq_level(cur_val) = level_one; + eq_type(cur_val) = c; + equiv(cur_val) = o; +} + +/*tex + +The function |store_primitive_name| sets up the bookkeeping for the reverse +lookup. It is quite paranoid, because it is easy to mess this up accidentally. + +The |offset| is needed because sometimes character codes (in |o|) are indices +into |eqtb| or are offset by a magical value to make sure they do not conflict +with something else. We don't want the |prim_data[c].names| to have too many +entries as it will just be wasted room, so |offset| is substracted from |o| +because creating or accessing the array. The |assert(idx<=0xFFFF)| is not +strictly needed, but it helps catch errors of this kind. + +*/ + +static void store_primitive_name(str_number s, quarterword c, halfword o, halfword offset) +{ + int idx; + /* + if (prim_data[c].offset != 0 && prim_data[c].offset != offset) { + assert(false); + } + */ + prim_data[c].offset = offset; + idx = ((int) o - offset); + /* + assert(idx >= 0); + assert(idx <= 0xFFFF); + */ + if (prim_data[c].subids < (idx + 1)) { + str_number *new = (str_number *) xcalloc((unsigned) (idx + 1), sizeof(str_number *)); + if (prim_data[c].names != NULL) { + /* + assert(prim_data[c].subids); + */ + memcpy(new, (prim_data[c].names), (unsigned) (prim_data[c].subids) * sizeof(str_number)); + free(prim_data[c].names); + } + prim_data[c].names = new; + prim_data[c].subids = idx + 1; + } + prim_data[c].names[idx] = s; +} + +/*tex + +Compared to tex82, |primitive| has two extra parameters. The |off| is an offset +that will be passed on to |store_primitive_name|, the |cmd_origin| is the bit +that is used to group primitives by originator. + +*/ + +void primitive(const char *thes, quarterword c, halfword o, halfword off, int cmd_origin) +{ + /*tex Needed to fill |prim_eqtb|: */ + int prim_val; + str_number ss; + ss = maketexstring(thes); + if (cmd_origin == tex_command || cmd_origin == core_command) { + primitive_def(thes, strlen(thes), c, o); + } + prim_val = prim_lookup(ss); + prim_origin(prim_val) = (quarterword) cmd_origin; + prim_eq_type(prim_val) = c; + prim_equiv(prim_val) = o; + store_primitive_name(ss, c, o, off); +} + +/*tex + +Here is a helper that does the actual hash insertion. This code far from ideal: +the existance of |hash_extra| changes all the potential (short) coalesced lists +into a single (long) one. This will create a slowdown. + +*/ + +static halfword insert_id(halfword p, const unsigned char *j, unsigned int l) +{ + unsigned saved_cur_length; + unsigned saved_cur_string_size; + unsigned char *saved_cur_string; + const unsigned char *k; + if (cs_text(p) > 0) { + if (hash_high < hash_extra) { + incr(hash_high); + /*tex + Can't we use |eqtb_top| here (perhaps because that is not + finalized yet when called from |primitive|? + */ + cs_next(p) = hash_high + eqtb_size; + p = cs_next(p); + } else { + /*tex + Search for an empty location in |hash|. + */ + do { + if (hash_is_full) + overflow("hash size", (unsigned) (hash_size + hash_extra)); + decr(hash_used); + } while (cs_text(hash_used) != 0); + cs_next(p) = hash_used; + p = hash_used; + } + } + saved_cur_length = cur_length; + saved_cur_string = cur_string; + saved_cur_string_size = cur_string_size; + reset_cur_string(); + for (k = j; k <= j + l - 1; k++) { + append_char(*k); + } + cs_text(p) = make_string(); + cur_length = saved_cur_length; + xfree(cur_string); + cur_string = saved_cur_string; + cur_string_size = saved_cur_string_size; + incr(cs_count); + return p; +} + + +/*tex + +Here is the subroutine that searches the hash table for an identifier that +matches a given string of length |l>1| appearing in |buffer[j.. (j+l-1)]|. If the +identifier is found, the corresponding hash table address is returned. Otherwise, +if the global variable |no_new_control_sequence| is |true|, the dummy address +|undefined_control_sequence| is returned. Otherwise the identifier is inserted +into the hash table and its location is returned. + +*/ + +pointer id_lookup(int j, int l) +{ + /*tex The hash code: */ + int h; + /*tex The index in |hash| array: */ + pointer p; + h = compute_hash((char *) (buffer + j), (unsigned) l, hash_prime); + /*tex We start searching here. Note that |0<=h<hash_prime|: */ + p = h + hash_base; + while (1) { + if (cs_text(p) > 0) + if (str_length(cs_text(p)) == (unsigned) l) + if (str_eq_buf(cs_text(p), j)) + goto FOUND; + if (cs_next(p) == 0) { + if (no_new_control_sequence) { + p = undefined_control_sequence; + } else { + p = insert_id(p, (buffer + j), (unsigned) l); + } + goto FOUND; + } + p = cs_next(p); + } + FOUND: + return p; +} + +/*tex + +Here is a similar subroutine for finding a primitive in the hash. +This one is based on a C string. + +*/ + +pointer string_lookup(const char *s, size_t l) +{ + /*tex The hash code: */ + int h; + /*tex The index in |hash| array: */ + pointer p; + h = compute_hash(s, (unsigned) l, hash_prime); + /*tex We start searching here. Note that |0<=h<hash_prime|: */ + p = h + hash_base; + while (1) { + if (cs_text(p) > 0) + if (str_eq_cstr(cs_text(p), s, l)) + goto FOUND; + if (cs_next(p) == 0) { + if (no_new_control_sequence) { + p = undefined_control_sequence; + } else { + p = insert_id(p, (const unsigned char *) s, (unsigned) l); + } + goto FOUND; + } + p = cs_next(p); + } + FOUND: + return p; +} + +/*tex + +The |print_cmd_chr| routine prints a symbolic interpretation of a command code +and its modifier. This is used in certain `\.{You can\'t}' error messages, and in +the implementation of diagnostic routines like \.{\\show}. + +The body of |print_cmd_chr| use to be a rather tedious listing of print commands, +and most of it was essentially an inverse to the |primitive| routine that enters +a \TeX\ primitive into |eqtb|. + +Thanks to |prim_data|, there is no need for all that tediousness. What is left of +|primt_cnd_chr| are just the exceptions to the general rule that the +|cmd,chr_code| pair represents in a single primitive command. + +*/ + +#define chr_cmd(A) do { tprint(A); print(chr_code); } while (0) + +static void prim_cmd_chr(quarterword cmd, halfword chr_code) +{ + int idx = chr_code - prim_data[cmd].offset; + if (cmd <= last_cmd && + idx >= 0 && idx < prim_data[cmd].subids && + prim_data[cmd].names != NULL && prim_data[cmd].names[idx] != 0) { + tprint_esc(""); + print(prim_data[cmd].names[idx]); + } else { + /* \TEX82 didn't print the |cmd,idx| information, but it may be useful. */ + tprint("[unknown command code! ("); + print_int(cmd); + tprint(", "); + print_int(idx); + tprint(")]"); + } +} + +void print_cmd_chr(quarterword cmd, halfword chr_code) +{ + int n; + switch (cmd) { + case left_brace_cmd: + chr_cmd("begin-group character "); + break; + case right_brace_cmd: + chr_cmd("end-group character "); + break; + case math_shift_cmd: + chr_cmd("math shift character "); + break; + case mac_param_cmd: + if (chr_code == tab_mark_cmd_code) + tprint_esc("alignmark"); + else + chr_cmd("macro parameter character "); + break; + case sup_mark_cmd: + chr_cmd("superscript character "); + break; + case sub_mark_cmd: + chr_cmd("subscript character "); + break; + case endv_cmd: + tprint("end of alignment template"); + break; + case spacer_cmd: + chr_cmd("blank space "); + break; + case letter_cmd: + chr_cmd("the letter "); + break; + case other_char_cmd: + chr_cmd("the character "); + break; + case tab_mark_cmd: + if (chr_code == span_code) + tprint_esc("span"); + else if (chr_code == tab_mark_cmd_code) + tprint_esc("aligntab"); + else + chr_cmd("alignment tab character "); + break; + case if_test_cmd: + if (chr_code >= unless_code) + tprint_esc("unless"); + prim_cmd_chr(cmd, (chr_code % unless_code)); + break; + case char_given_cmd: + tprint_esc("char"); + print_qhex(chr_code); + break; + case math_given_cmd: + /*tex + Okay, it's better for old macro packages that mess with meaning + to report a traditional value. A compromise. + */ + tprint_esc("mathchar"); + show_mathcode_value_old(chr_code); + break; + case xmath_given_cmd: + tprint_esc("Umathchar"); + show_mathcode_value(mathchar_from_integer(chr_code, umath_mathcode)); + break; + case lua_expandable_call_cmd: + tprint("expandable luacall "); + print_int(chr_code); + break; + case lua_local_call_cmd: + tprint("local luacall "); + print_int(chr_code); + break; + case lua_call_cmd: + tprint("luacall "); + print_int(chr_code); + break; + case set_font_cmd: + tprint("select font "); + tprint(font_name(chr_code)); + if (font_size(chr_code) != font_dsize(chr_code)) { + tprint(" at "); + print_scaled(font_size(chr_code)); + tprint("pt"); + } + break; + case undefined_cs_cmd: + tprint("undefined"); + break; + case call_cmd: + case long_call_cmd: + case outer_call_cmd: + case long_outer_call_cmd: + n = cmd - call_cmd; + if (token_info(token_link(chr_code)) == protected_token) + n = n + 4; + if (odd(n / 4)) + tprint_esc("protected"); + if (odd(n)) + tprint_esc("long"); + if (odd(n / 2)) + tprint_esc("outer"); + if (n > 0) + tprint(" "); + tprint("macro"); + break; + case assign_glue_cmd: + case assign_mu_glue_cmd: + if (chr_code < skip_base) { + prim_cmd_chr(cmd, chr_code); + } else if (chr_code < mu_skip_base) { + tprint_esc("skip"); + print_int(chr_code - skip_base); + } else { + tprint_esc("muskip"); + print_int(chr_code - mu_skip_base); + } + break; + case assign_toks_cmd: + if (chr_code >= toks_base) { + tprint_esc("toks"); + print_int(chr_code - toks_base); + } else { + prim_cmd_chr(cmd, chr_code); + } + break; + case assign_int_cmd: + if (chr_code < count_base) { + prim_cmd_chr(cmd, chr_code); + } else { + tprint_esc("count"); + print_int(chr_code - count_base); + } + break; + case assign_attr_cmd: + tprint_esc("attribute"); + print_int(chr_code - attribute_base); + break; + case assign_dimen_cmd: + if (chr_code < scaled_base) { + prim_cmd_chr(cmd, chr_code); + } else { + tprint_esc("dimen"); + print_int(chr_code - scaled_base); + } + break; + case normal_cmd: + if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) { + prim_cmd_chr(cmd, chr_code); + } else { + tprint("[unknown command! ("); + print_int(chr_code); + tprint(")]"); + } + break; + case extension_cmd: + if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) { + prim_cmd_chr(cmd, chr_code); + } else { + tprint("[unknown extension! ("); + print_int(chr_code); + tprint(")]"); + + } + break; + case node_cmd: + tprint("node "); + print_int(chr_code); + break; + default: + /*tex These are most commands, actually. */ + prim_cmd_chr(cmd, chr_code); + break; + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/primitive.h b/Build/source/texk/web2c/luatexdir/tex/primitive.h new file mode 100644 index 00000000000..b6b13c74f73 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/primitive.h @@ -0,0 +1,89 @@ +/* primitive.h + + Copyright 2008-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef LUATEX_PRIMITIVE_H +# define LUATEX_PRIMITIVE_H 1 + +/* This enum is a list of origins for primitive commands */ + +typedef enum { + /* Can be modified as suggested ?*/ + tex_command = 1, /* 1 */ + etex_command = 2, /* 2 */ + pdftex_command = 16, /* 4 */ /* obsolete */ + luatex_command = 32, /* 8 */ + core_command = 64, /* 16 */ + no_command = 128, /* 32 */ + umath_command = 256, /* 64 */ /* obsolete */ +} command_origin; + +# define hash_size 65536 /* maximum number of control sequences; it should be at most about |(fix_mem_max-fix_mem_min)/10| */ +# define hash_prime 55711 /* a prime number equal to about 85\pct! of |hash_size| */ + +extern two_halves *hash; /* the hash table */ +extern halfword hash_used; /* allocation pointer for |hash| */ +extern int hash_extra; /* |hash_extra=hash| above |eqtb_size| */ +extern halfword hash_top; /* maximum of the hash array */ +extern halfword hash_high; /* pointer to next high hash location */ +extern boolean no_new_control_sequence; /* are new identifiers legal? */ +extern int cs_count; /* total number of known identifiers */ + +# define cs_next(a) hash[(a)].lhfield /* link for coalesced lists */ +# define cs_text(a) hash[(a)].rh + /* string number for control sequence name */ + +# define undefined_primitive 0 +# define prim_size 2100 /* maximum number of primitives */ +# define prim_prime 1777 /* about 85\pct! of |primitive_size| */ + +extern void init_primitives(void); +extern void ini_init_primitives(void); + +extern halfword compute_pool_hash(pool_pointer j, pool_pointer l, + halfword prime_number); +extern pointer prim_lookup(str_number s); + +extern boolean is_primitive(str_number csname); + +extern quarterword get_prim_eq_type(int p); +extern halfword get_prim_equiv(int p); +extern str_number get_prim_text(int p); +extern quarterword get_prim_origin(int p); + +extern void dump_primitives(void); +extern void undump_primitives(void); + +# define primitive_tex(a,b,c,d) primitive((a),(b),(c),(d),tex_command) +# define primitive_etex(a,b,c,d) primitive((a),(b),(c),(d),etex_command) +# define primitive_pdftex(a,b,c,d) primitive((a),(b),(c),(d),pdftex_command) +# define primitive_luatex(a,b,c,d) primitive((a),(b),(c),(d),luatex_command) +# define primitive_umath(a,b,c,d) primitive((a),(b),(c),(d),umath_command) +# define primitive_core(a,b,c,d) primitive((a),(b),(c),(d),core_command) +# define primitive_no(a,b,c,d) primitive((a),(b),(c),(d),no_command) + +extern void primitive(const char *ss, quarterword c, halfword o, halfword off, + int cmd_origin); +extern void primitive_def(const char *s, size_t l, quarterword c, halfword o); +extern void print_cmd_chr(quarterword cmd, halfword chr_code); + +extern pointer string_lookup(const char *s, size_t l); +extern pointer id_lookup(int j, int l); + +#endif /* LUATEX_PRIMITIVE_H */ diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.c b/Build/source/texk/web2c/luatexdir/tex/printing.c new file mode 100644 index 00000000000..31125d2ebca --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/printing.c @@ -0,0 +1,1350 @@ +/* + +printing.w + +Copyright 2009-2013 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#include "lua/luatex-api.h" + +#define wlog(A) fputc(A,log_file) +#define wterm(A) fputc(A,term_out) + +int new_string_line = 0; +int escape_controls = 1; + +/*tex + +Messages that are sent to a user's terminal and to the transcript-log file are +produced by several `|print|' procedures. These procedures will direct their +output to a variety of places, based on the setting of the global variable +|selector|, which has the following possible values: + +\startitemize + +\startitem + |term_and_log|, the normal setting, prints on the terminal and on the + transcript file. +\stopitem + +\startitem + |log_only|, prints only on the transcript file. +\stopitem + +\startitem + |term_only|, prints only on the terminal. +\stopitem + +\startitem + |no_print|, doesn't print at all. This is used only in rare cases before the + transcript file is open. +\stopitem + +\startitem + |pseudo|, puts output into a cyclic buffer that is used by the |show_context| + routine; when we get to that routine we shall discuss the reasoning behind + this curious mode. +\stopitem + +\startitem + |new_string|, appends the output to the current string in the string pool. +\stopitem + +\startitem + 0 to 15, prints on one of the sixteen files for \.{\\write} output. +\stopitem + +\stopitemize + +The symbolic names `|term_and_log|', etc., have been assigned numeric codes that +satisfy the convenient relations |no_print+1=term_only|, |no_print+2=log_only|, +|term_only+2=log_only+1=term_and_log|. + +Three additional global variables, |tally| and |term_offset| and |file_offset|, +record the number of characters that have been printed since they were most +recently cleared to zero. We use |tally| to record the length of (possibly very +long) stretches of printing; |term_offset| and |file_offset|, on the other hand, +keep track of how many characters have appeared so far on the current line that +has been output to the terminal or to the transcript file, respectively. + +*/ + +/*tex transcript of \TeX\ session */ + +alpha_file log_file; + +/*tex where to print a message */ + +int selector = term_only; + +/*tex digits in a number being output */ + +int dig[23]; + +/*tex the number of characters recently printed */ + +int tally = 0; + +/*tex the number of characters on the current terminal line */ + +int term_offset = 0; + +/*tex the number of characters on the current file line */ + +int file_offset = 0; + +/*tex circular buffer for pseudoprinting */ + +packed_ASCII_code trick_buf[(ssup_error_line + 1)]; + +/*tex threshold for pseudoprinting, explained later */ + +int trick_count; + +/*tex another variable for pseudoprinting */ + +int first_count; + +/*tex for minor adjustments to |show_token_list| */ + +boolean inhibit_par_tokens = false; + +/*tex + +To end a line of text output, we call |print_ln| + +*/ + +void print_ln(void) +{ + switch (selector) { + case no_print: + break; + case term_only: + wterm_cr(); + term_offset = 0; + break; + case log_only: + wlog_cr(); + file_offset = 0; + break; + case term_and_log: + wterm_cr(); + wlog_cr(); + term_offset = 0; + file_offset = 0; + break; + case pseudo: + break; + case new_string: + if (new_string_line > 0) + print_char(new_string_line); + break; + default: + fprintf(write_file[selector], "\n"); + break; + } + /*tex |tally| is not affected */ +} + +/*tex + +The |print_char| procedure sends one byte to the desired destination. All +printing comes through |print_ln| or |print_char|, except for the case of +|tprint| (see below). + +The checking of the line length is an inheritance from previosu engines and we +might drop it after release 1.0. We're not too picky about the exact match of +that length because we have utf output so length is then a bit fuzzy anyway. + +*/ + +#define needs_escaping(A) \ + ((! escape_controls) || (A>=0x20) || (A==0x0A) || (A==0x0D) || (A==0x09)) + +#define escaped_char(A) \ + A+64 + +#define wterm_char(A) \ + if (needs_escaping(A)) { \ + wterm(A); \ + } else { \ + if (term_offset+2>=max_print_line) { \ + wterm_cr(); \ + term_offset=0; \ + } \ + wterm('^'); \ + wterm('^'); \ + wterm(escaped_char(A)); \ + term_offset += 2; \ + } + +#define needs_wrapping(A,B) \ + ( (A>=0xC0) && \ + (((A>=0xF0) && (B+4>=max_print_line)) || \ + ((A>=0xE0) && (B+3>=max_print_line)) || \ + ( (B+2>=max_print_line))) \ + ) + +#define fix_term_offset(A) \ + if (needs_wrapping(A,term_offset)){ \ + wterm_cr(); \ + term_offset=0; \ + } + +#define fix_log_offset(A) \ + if (needs_wrapping(A,file_offset)){ \ + wlog_cr(); \ + file_offset=0; \ + } + +void print_char(int s) +{ + if (s < 0 || s > 255) { + formatted_warning("print","weird character %i",s); + return; + } + if (s == new_line_char_par) { + if (selector < pseudo) { + print_ln(); + return; + } + } + switch (selector) { + case no_print: + break; + case term_only: + fix_term_offset(s); + wterm_char(s); + incr(term_offset); + if (term_offset == max_print_line) { + wterm_cr(); + term_offset = 0; + } + break; + case log_only: + fix_log_offset(s); + wlog(s); + incr(file_offset); + if (file_offset == max_print_line) { + wlog_cr(); + file_offset = 0; + } + break; + case term_and_log: + fix_term_offset(s); + fix_log_offset(s); + wterm_char(s); + wlog(s); + incr(term_offset); + incr(file_offset); + if (term_offset == max_print_line) { + wterm_cr(); + term_offset = 0; + } + if (file_offset == max_print_line) { + wlog_cr(); + file_offset = 0; + } + break; + case pseudo: + if (tally < trick_count) + trick_buf[tally % error_line] = (packed_ASCII_code) s; + break; + case new_string: + append_char(s); + break; + default: + fprintf(write_file[selector], "%c", s); + } + incr(tally); +} + +/*tex + +An entire string is output by calling |print|. Note that if we are outputting the +single standard ASCII character \.c, we could call |print("c")|, since |"c"=99| +is the number of a single-character string, as explained above. But +|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char| routine +when it knows that this is safe. (The present implementation assumes that it is +always safe to print a visible ASCII character.) + +The first 256 entries above the 17th unicode plane are used for a special trick: +when \TeX\ has to print items in that range, it will instead print the character +that results from substracting 0x110000 from that value. This allows +byte-oriented output to things like \.{\\specials} and \.{\\pdfextension +literals}. Todo: Perhaps it would be useful to do the same substraction while +typesetting. + + +*/ + +void print(int s) +{ + if (s >= str_ptr) { + normal_warning("print","bad string pointer"); + return; + } else if (s < STRING_OFFSET) { + if (s < 0) { + normal_warning("print","bad string offset"); + } else { + /*tex We're not sure about this so it's disabled for now! */ + if ((false) && (selector > pseudo)) { + /*tex internal strings are not expanded */ + print_char(s); + return; + } + if (s == new_line_char_par) { + if (selector < pseudo) { + print_ln(); + return; + } + } + if (s <= 0x7F) { + print_char(s); + } else if (s <= 0x7FF) { + print_char(0xC0 + (s / 0x40)); + print_char(0x80 + (s % 0x40)); + } else if (s <= 0xFFFF) { + print_char(0xE0 + (s / 0x1000)); + print_char(0x80 + ((s % 0x1000) / 0x40)); + print_char(0x80 + ((s % 0x1000) % 0x40)); + } else if (s >= 0x110000) { + int c = s - 0x110000; + if (c >= 256) { + formatted_warning("print", "bad raw byte to print (c=%d), skipped",c); + } else { + print_char(c); + } + } else { + print_char(0xF0 + (s / 0x40000)); + print_char(0x80 + ((s % 0x40000) / 0x1000)); + print_char(0x80 + (((s % 0x40000) % 0x1000) / 0x40)); + print_char(0x80 + (((s % 0x40000) % 0x1000) % 0x40)); + } + } + return; + } + if (selector == new_string) { + append_string(str_string(s), (unsigned) str_length(s)); + return; + } + lprint(&str_lstring(s)); +} + +void lprint (lstring *ss) { + /*tex current character code position */ + unsigned char *j, *l; + j = ss->s; + l = j + ss->l; + while (j < l) { + /*tex I don't bother checking the last two bytes explicitly */ + /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80 */ + if ((j < l - 4) && (*j == 0xF4) && (*(j + 1) == 0x90)) { + int c = (*(j + 2) - 128) * 64 + (*(j + 3) - 128); + assert(c >= 0 && c < 256); + print_char(c); + j = j + 4; + } else { + print_char(*j); + incr(j); + } + } +} + +/*tex + +The procedure |print_nl| is like |print|, but it makes sure that the string +appears at the beginning of a new line. + +*/ + +/*tex Moev to the beginning of the next line. */ + +void print_nlp(void) +{ + if (new_string_line > 0) { + print_char(new_string_line); + } else if (((term_offset > 0) && (odd(selector))) || + ((file_offset > 0) && (selector >= log_only))) { + print_ln(); + } +} + +/*tex Prints string |s| at beginning of the next line. */ + +void print_nl(str_number s) +{ + print_nlp(); + print(s); +} + +/*tex + +|char *| versions of the same procedures. |tprint| is different because it uses +buffering, which works well because most of the output actually comes through +|tprint|. + +*/ + +#define t_flush_buffer(target,offset) \ + buffer[i++] = '\n'; \ + buffer[i++] = '\0';\ + fputs(buffer, target); \ + i = 0; \ + buffer[0] = '\0'; \ + offset=0; + +void tprint(const char *sss) +{ + char *buffer = NULL; + int i = 0; + int newlinechar = (lua_only == 1) ? 10 : new_line_char_par; + int dolog = 0; + int doterm = 0; + switch (selector) { + case no_print: + return; + break; + case term_only: + doterm = 1; + break; + case log_only: + dolog = 1; + break; + case term_and_log: + dolog = 1; + doterm = 1; + break; + case pseudo: + while (*sss) { + if (tally < trick_count) { + trick_buf[tally % error_line] = (packed_ASCII_code) *sss++; + tally++; + } else { + return; + } + } + return; + break; + case new_string: + append_string((const unsigned char *)sss, (unsigned) strlen(sss)); + return; + break; + default: + { + char *newstr = xstrdup(sss); + char *s; + for (s=newstr;*s;s++) { + if (*s == newlinechar) { + *s = '\n'; + } + } + fputs(newstr, write_file[selector]); + free(newstr); + return; + } + break; + } + /*tex What is left is the 3 term/log settings. */ + if (dolog || doterm) { + buffer = xmalloc(strlen(sss)*3); + /*tex The |wrapup_run| callback acts when the log file is already closed.*/ + if (dolog && log_opened_global) { + const unsigned char *ss = (const unsigned char *) sss; + while (*ss) { + int s = *ss++; + if (needs_wrapping(s,file_offset) || s == newlinechar) { + t_flush_buffer(log_file,file_offset); + } + if (s != newlinechar) { + buffer[i++] = s; + if (file_offset++ == max_print_line) { + t_flush_buffer(log_file,file_offset); + } + } + } + if (*buffer) { + buffer[i++] = '\0'; + fputs(buffer, log_file); + buffer[0] = '\0'; + } + i = 0; + } + if (doterm) { + const unsigned char *ss = (const unsigned char *) sss; + while (*ss) { + int s = *ss++; + if (needs_wrapping(s,term_offset) || s == newlinechar) { + t_flush_buffer(term_out,term_offset); + } + if (s != newlinechar) { + if (needs_escaping(s)) { + buffer[i++] = s; + } else { + buffer[i++] = '^'; + buffer[i++] = '^'; + buffer[i++] = escaped_char(s); + term_offset += 2; + } + if (++term_offset == max_print_line) { + t_flush_buffer(term_out,term_offset); + } + } + } + if (*buffer) { + buffer[i++] = '\0'; + fputs(buffer, term_out); + } + } + free(buffer); + } +} + +void tprint_nl(const char *s) +{ + print_nlp(); + tprint(s); +} + +/*tex + +Here is the very first thing that \TeX\ prints: a headline that identifies the +version number and format package. The |term_offset| variable is temporarily +incorrect, but the discrepancy is not serious since we assume that the banner and +format identifier together will occupy at most |max_print_line| character +positions. + +*/ + +void print_banner(const char *v) +{ + int callback_id = callback_defined(start_run_callback); + if (callback_id == 0) { + fprintf(term_out, "This is " MyName ", Version %s%s ", v, WEB2CVERSION); + if (format_ident > 0) + print(format_ident); + print_ln(); + if (show_luahashchars) { + wterm(' '); + fprintf(term_out,"Number of bits used by the hash function (" my_name "): %d",LUAI_HASHLIMIT); + print_ln(); + } + if (shellenabledp) { + wterm(' '); + if (restrictedshell) { + fprintf(term_out, "restricted "); + } + fprintf(term_out, "system commands enabled.\n"); + } + } else if (callback_id > 0) { + run_callback(callback_id, "->"); + } +} + +void log_banner(const char *v) +{ + const char *months[] = { " ", + "JAN", "FEB", "MAR", "APR", "MAY", "JUN", + "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" + }; + unsigned month = (unsigned) month_par; + if (month > 12) + month = 0; + fprintf(log_file, "This is " MyName ", Version %s%s ", v, WEB2CVERSION); + print(format_ident); + print_char(' '); + print_char(' '); + print_int(day_par); + print_char(' '); + fprintf(log_file, "%s", months[month]); + print_char(' '); + print_int(year_par); + print_char(' '); + print_two(time_par / 60); + print_char(':'); + print_two(time_par % 60); + if (shellenabledp) { + wlog_cr(); + wlog(' '); + if (restrictedshell) + fprintf(log_file, "restricted "); + fprintf(log_file, "system commands enabled."); + } + if (filelineerrorstylep) { + wlog_cr(); + fprintf(log_file, " file:line:error style messages enabled."); + } +} + +void print_version_banner(void) +{ + fprintf(term_out, "%s", luatex_banner); +} + +/*tex + +The procedure |print_esc| prints a string that is preceded by the user's escape +character (which is usually a backslash). + +*/ + +void print_esc(str_number s) +{ + /*tex Set variable |c| to the current escape character: */ + int c = escape_char_par; + if (c >= 0 && c < 0x110000) + print(c); + print(s); +} + +/*tex + +This prints escape character, then |s|. + +*/ + +void tprint_esc(const char *s) +{ + /*tex Set variable |c| to the current escape character: */ + int c = escape_char_par; + if (c >= 0 && c < 0x110000) + print(c); + tprint(s); +} + +/*tex + +An array of digits in the range |0..15| is printed by |print_the_digs|. + +*/ + +void print_the_digs(eight_bits k) +{ + /*tex prints |dig[k-1]|$\,\ldots\,$|dig[0]| */ + while (k-- > 0) { + if (dig[k] < 10) + print_char('0' + dig[k]); + else + print_char('A' - 10 + dig[k]); + } +} + +/*tex + +The following procedure, which prints out the decimal representation of a given +integer |n|, has been written carefully so that it works properly if |n=0| or if +|(-n)| would cause overflow. It does not apply |mod| or |div| to negative +arguments, since such operations are not implemented consistently by all PASCAL +compilers. + +*/ + +void print_int(longinteger n) +{ + /*tex index to current digit; we assume that $|n|<10^{23}$ */ + int k = 0; + /*tex used to negate |n| in possibly dangerous cases */ + longinteger m; + if (n < 0) { + print_char('-'); + if (n > -100000000) { + n = -n; + } else { + m = -1 - n; + n = m / 10; + m = (m % 10) + 1; + k = 1; + if (m < 10) + dig[0] = (int) m; + else { + dig[0] = 0; + incr(n); + } + } + } + do { + dig[k] = (int) (n % 10); + n = n / 10; + incr(k); + } while (n != 0); + print_the_digs((eight_bits) k); +} + +/*tex + +Here is a trivial procedure to print two digits; it is usually called with a +parameter in the range |0<=n<=99|. + +*/ + +void print_two(int n) +{ + n = abs(n) % 100; + print_char('0' + (n / 10)); + print_char('0' + (n % 10)); +} + +/*tex + +Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. + +*/ + +void print_qhex(int n) +{ + /*tex index to current digit; we assume that $0\L n<16^{22}$ */ + int k = 0 ; + print_char('"'); + do { + dig[k] = n % 16; + n = n / 16; + incr(k); + } while (n != 0); + print_the_digs((eight_bits) k); +} + +/*tex + +Roman numerals are produced by the |print_roman_int| routine. Readers who like +puzzles might enjoy trying to figure out how this tricky code works; therefore no +explanation will be given. Notice that 1990 yields \.{mcmxc}, not \.{mxm}. + +*/ + +void print_roman_int(int n) +{ + char *j, *k; + int u, v; + char mystery[] = "m2d5c2l5x2v5i"; + j = (char *) mystery; + v = 1000; + while (1) { + while (n >= v) { + print_char(*j); + n = n - v; + } + if (n <= 0) { + /*tex nonpositive input produces no output */ + return; + } + k = j + 2; + u = v / (*(k - 1) - '0'); + if (*(k - 1) == '2') { + k = k + 2; + u = u / (*(k - 1) - '0'); + } + if (n + u >= v) { + print_char(*k); + n = n + u; + } else { + j = j + 2; + v = v / (*(j - 1) - '0'); + } + } +} + +/*tex + +The |print| subroutine will not print a string that is still being created. The +following procedure will. + +*/ + +void print_current_string(void) +{ + /*tex points to current character code */ + unsigned j = 0; + while (j < cur_length) + print_char(cur_string[j++]); +} + +/*tex + +The procedure |print_cs| prints the name of a control sequence, given a pointer +to its address in |eqtb|. A space is printed after the name unless it is a single +nonletter or an active character. This procedure might be invoked with invalid +data, so it is ``extra robust.'' The individual characters must be printed one at +a time using |print|, since they may be unprintable. + +*/ + +void print_cs(int p) +{ + str_number t = cs_text(p); + if (p < hash_base) { + /* nullcs */ + if (p == null_cs) { + tprint_esc("csname"); + tprint_esc("endcsname"); + print_char(' '); + } else { + tprint_esc("IMPOSSIBLE."); + } + } else if ((p >= undefined_control_sequence) && + ((p <= eqtb_size) || p > eqtb_size + hash_extra)) { + tprint_esc("IMPOSSIBLE."); + } else if (t >= str_ptr) { + tprint_esc("NONEXISTENT."); + } else { + if (is_active_cs(t)) { + print(active_cs_value(t)); + } else { + print_esc(t); + if (single_letter(t)) { + if (get_cat_code(cat_code_table_par, pool_to_unichar(str_string(t))) == letter_cmd) + print_char(' '); + } else { + print_char(' '); + } + } + } +} + +/*tex + +Here is a similar procedure; it avoids the error checks, and it never prints a +space after the control sequence. + +*/ +void sprint_cs(pointer p) +{ + str_number t; + if (p == null_cs) { + tprint_esc("csname"); + tprint_esc("endcsname"); + } else { + t = cs_text(p); + if (is_active_cs(t)) + print(active_cs_value(t)); + else + print_esc(t); + } +} + +void sprint_cs_name(pointer p) +{ + str_number t; + if (p != null_cs) { + t = cs_text(p); + if (is_active_cs(t)) + print(active_cs_value(t)); + else + print(t); + } +} + +/*tex + +This procedure is never called when |interaction<scroll_mode|. + +*/ + +void prompt_input(const char *s) +{ + wake_up_terminal(); + tprint(s); + term_input(); +} + +/*tex + +Then there is a subroutine that prints glue stretch and shrink, possibly followed +by the name of finite units: + +*/ + +void print_glue(scaled d, int order, const char *s) +{ + print_scaled(d); + if ((order < normal) || (order > filll)) { + tprint("foul"); + } else if (order > normal) { + tprint("fi"); + while (order > sfi) { + print_char('l'); + decr(order); + } + } else if (s != NULL) { + tprint(s); + } +} + +/*tex + +The next subroutine prints a whole glue specification + +*/ + +void print_spec(int p, const char *s) +{ + if (p < 0) { + print_char('*'); + } else { + print_scaled(width(p)); + if (s != NULL) + tprint(s); + if (stretch(p) != 0) { + tprint(" plus "); + print_glue(stretch(p), stretch_order(p), s); + } + if (shrink(p) != 0) { + tprint(" minus "); + print_glue(shrink(p), shrink_order(p), s); + } + } +} + +/*tex + +We can reinforce our knowledge of the data structures just introduced by +considering two procedures that display a list in symbolic form. The first of +these, called |short_display|, is used in ``overfull box'' messages to give the +top-level description of a list. The other one, called |show_node_list|, prints a +detailed description of exactly what is in the data structure. + +The philosophy of |short_display| is to ignore the fine points about exactly what +is inside boxes, except that ligatures and discretionary breaks are expanded. As +a result, |short_display| is a recursive procedure, but the recursion is never +more than one level deep. @^recursion@> + +A global variable |font_in_short_display| keeps track of the font code that is +assumed to be present when |short_display| begins; deviations from this font will +be printed. + +*/ + +/*tex An internal font number: */ + +int font_in_short_display; + +/*tex + +Boxes, rules, inserts, whatsits, marks, and things in general that are sort of +``complicated'' are indicated only by printing `\.{[]}'. + +We print a bit more than original \TEX. A value of 0 or 1 or any large value will +behave the same as before. The reason for this extension is that a |name| not +always makes sense. + +\starttyping +0 \foo xyz +1 \foo (bar) +2 <bar> xyz +3 <bar @ ..> xyz +4 <id> +5 <id: bar> +6 <id: bar @ ..> xyz +\stoptyping + +*/ + +void print_font_identifier(internal_font_number f) +{ + str_number fonttext; + fonttext = font_id_text(f); + if (tracing_fonts_par >= 2 && tracing_fonts_par <= 6) { + /*tex < > is less likely to clash with text parenthesis */ + tprint("<"); + if (tracing_fonts_par >= 2 && tracing_fonts_par <= 3) { + print_font_name(f); + if (tracing_fonts_par >= 3 || font_size(f) != font_dsize(f)) { + tprint(" @ "); + print_scaled(font_size(f)); + tprint("pt"); + } + } else if (tracing_fonts_par >= 4 && tracing_fonts_par <= 6) { + print_int(f); + if (tracing_fonts_par >= 5) { + tprint(": "); + print_font_name(f); + if (tracing_fonts_par >= 6 || font_size(f) != font_dsize(f)) { + tprint(" @ "); + print_scaled(font_size(f)); + tprint("pt"); + } + } + } + print_char('>'); + } else { + /*tex old method, inherited from pdftex */ + if (fonttext > 0) { + print_esc(fonttext); + } else { + tprint_esc("FONT"); + print_int(f); + } + if (tracing_fonts_par > 0) { + tprint(" ("); + print_font_name(f); + if (font_size(f) != font_dsize(f)) { + tprint("@"); + print_scaled(font_size(f)); + tprint("pt"); + } + print_char(')'); + } + } +} + +/*tex + We could do this much nicer but then we need to also adapt short_display a + bit and we have to be as compatible as possible in the log for some macro + packages. + + The callback is also responsible for either or not reporting the character + number itself. +*/ + +void print_character_info(halfword p) +{ + int callback_id = callback_defined(glyph_info_callback); + if (callback_id) { + char* str = NULL; + run_callback(callback_id, "N->R", p, &str); + if (str == NULL) { + print_qhex(character(p)); + } else { + tprint(str); + free(str); + } + } else { + print(character(p)); + } +} + +/*tex + This prints highlights of list |p|. +*/ + +void short_display(int p) +{ + while (p != null) { + if (is_char_node(p)) { + if (lig_ptr(p) != null) { + short_display(lig_ptr(p)); + } else { + if (font(p) != font_in_short_display) { + if (!is_valid_font(font(p))) { + print_char('*'); + } else { + print_font_identifier(font(p)); + } + print_char(' '); + font_in_short_display = font(p); + } + print_character_info(p); + } + } else { + /*tex Print a short indication of the contents of node |p| */ + print_short_node_contents(p); + } + p = vlink(p); + } +} + +/*tex + +The |show_node_list| routine requires some auxiliary subroutines: one to print a +font-and-character combination, one to print a token list without its reference +count, and one to print a rule dimension. + +*/ + +/*tex + +This prints |char_node| data. + +*/ + +void print_font_and_char(int p) +{ + if (!is_valid_font(font(p))) + print_char('*'); + else + print_font_identifier(font(p)); + print_char(' '); + print_character_info(p); +} + +/*tex + +This prints token list data in braces + +*/ + +void print_mark(int p) +{ + print_char('{'); + if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) + tprint_esc("CLOBBERED."); + else + show_token_list(token_link(p), null, max_print_line - 10); + print_char('}'); +} + +/*tex + +This prints dimensions of a rule node. + +*/ + +void print_rule_dimen(scaled d) +{ + if (is_running(d)) + print_char('*'); + else + print_scaled(d); +} + +/*tex + +Since boxes can be inside of boxes, |show_node_list| is inherently recursive, +@^recursion@> up to a given maximum number of levels. The history of nesting is +indicated by the current string, which will be printed at the beginning of each +line; the length of this string, namely |cur_length|, is the depth of nesting. + +A global variable called |depth_threshold| is used to record the maximum depth of +nesting for which |show_node_list| will show information. If we have +|depth_threshold=0|, for example, only the top level information will be given +and no sublists will be traversed. Another global variable, called |breadth_max|, +tells the maximum number of items to show at each level; |breadth_max| had better +be positive, or you won't see anything. + +*/ + +/*tex The maximum nesting depth in box displays: */ + +int depth_threshold; + +/*tex The maximum number of items shown at the same list level: */ + +int breadth_max; + +/*tex + +The recursive machinery is started by calling |show_box|. Assign the values +|depth_threshold:=show_box_depth| and |breadth_max:=show_box_breadth| + +*/ + +void show_box(halfword p) +{ + depth_threshold = show_box_depth_par; + breadth_max = show_box_breadth_par; + if (breadth_max <= 0) + breadth_max = 5; + /*tex the show starts at |p| */ + show_node_list(p); + print_ln(); +} + +/*tex + +Helper for debugging purposes. It prints highlights of list |p| + +*/ + +void short_display_n(int p, int m) +{ + int i = 0; + font_in_short_display = null_font; + if (p == null) + return; + while (p != null) { + if (is_char_node(p)) { + if (p <= max_halfword) { + if (font(p) != font_in_short_display) { + if (!is_valid_font(font(p))) + print_char('*'); + else + print_font_identifier(font(p)); + print_char(' '); + font_in_short_display = font(p); + } + print_character_info(p); + } + } else { + if ( (type(p) == glue_node) || + (type(p) == disc_node) || + (type(p) == penalty_node) || + ((type(p) == kern_node) && (subtype(p) == explicit_kern || + subtype(p) == italic_kern ))) { + incr(i); + } + if (i >= m) + return; + if (type(p) == disc_node) { + print_char('|'); + short_display(vlink(pre_break(p))); + print_char('|'); + short_display(vlink(post_break(p))); + print_char('|'); + } else { + /*tex Print a short indication of the contents of node |p| */ + print_short_node_contents(p); + } + } + p = vlink(p); + if (p == null) + return; + } + update_terminal(); +} + +/*tex + +When debugging a macro package, it can be useful to see the exact control +sequence names in the format file. For example, if ten new csnames appear, it's +nice to know what they are, to help pinpoint where they came from. (This isn't a +truly ``basic'' printing procedure, but that's a convenient module in which to +put it.) + +*/ + +void print_csnames(int hstart, int hfinish) +{ + int h; + unsigned char *c, *l; + fprintf(stderr, "fmtdebug:csnames from %d to %d:", (int) hstart, (int) hfinish); + for (h = hstart; h <= hfinish; h++) { + if (cs_text(h) > 0) { + /*tex We have anything at this position. */ + c = str_string(cs_text(h)); + l = c + str_length(cs_text(h)); + while (c < l) { + /*tex Print the characters. */ + fputc(*c++, stderr); + } + fprintf(stderr, "|"); + } + } +} + +/*tex + +A helper for printing file:line:error style messages. Look for a filename in +|full_source_filename_stack|, and if we fail to find one fall back on the +non-file:line:error style. + +*/ + +void print_file_line(void) +{ + int level = in_open; + while ((level > 0) && (full_source_filename_stack[level] == 0)) + decr(level); + if (level == 0) { + tprint_nl("! "); + } else { + tprint_nl(""); + tprint(full_source_filename_stack[level]); + print_char(':'); + if (level == in_open) + print_int(line); + else + print_int(line_stack[level + 1]); + tprint(": "); + } +} + +/*tex + +\TeX\ is occasionally supposed to print diagnostic information that goes only +into the transcript file, unless |tracing_online| is positive. Here are two +routines that adjust the destination of print commands: + +*/ + +void begin_diagnostic(void) +{ + global_old_setting = selector; + if ((tracing_online_par <= 0) && (selector == term_and_log)) { + decr(selector); + if (history == spotless) + history = warning_issued; + } +} + +/*tex + +Restore proper conditions after tracing. + +*/ + +void end_diagnostic(boolean blank_line) +{ + tprint_nl(""); + if (blank_line) + print_ln(); + selector = global_old_setting; +} + +/* + Indentation (based on a suggestion by PO on tex-implementors but adapted to + out situation and with built-in limits and so. +*/ + +void print_input_level(void) +{ + int callback_id = callback_defined(input_level_string_callback); + if (callback_id>0) { + char *s = NULL; + if (run_callback(callback_id, "d->S", input_ptr, &s)) { + if (s && strlen(s) > 0) { + tprint_nl(s); + free(s); + } else { + print_ln(); + } + } else { + print_ln(); + } + } else { + int m = level_max; + if (m) { + int l = input_ptr; + int c = level_chr > 0 ? level_chr : '.'; + if (l > m) { + tprint_nl("["); + print_int((l/m)*m); + print(']'); + l = l % m; + } else { + print_ln(); + } + while (l > 0) { + print(c); + l--; + } + } else { + print_ln(); + } + } +} + +/*tex + +Of course we had better declare another global variable, if the previous routines +are going to work. + +*/ + +int global_old_setting; diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.h b/Build/source/texk/web2c/luatexdir/tex/printing.h new file mode 100644 index 00000000000..0d87f3d4ae7 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/printing.h @@ -0,0 +1,133 @@ +/* printing.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef PRINTING_H +# define PRINTING_H + +#define last_file_selector 127 /* was 15 */ + +/* + Nicer will be to start these with 0 and then use an offset for the write + registers internally. +*/ + +typedef enum { + no_print = last_file_selector + 1, /* 16 */ /* |selector| setting that makes data disappear */ + term_only = last_file_selector + 2, /* 17 */ /* printing is destined for the terminal only */ + log_only = last_file_selector + 3, /* 18 */ /* printing is destined for the transcript file only */ + term_and_log = last_file_selector + 4, /* 19 */ /* normal |selector| setting */ + pseudo = last_file_selector + 5, /* 20 */ /* special |selector| setting for |show_context| */ + new_string = last_file_selector + 6, /* 21 */ /* printing is deflected to the string pool */ +} selector_settings; + +extern int escape_controls; +extern int new_string_line; + +# define ssup_error_line 255 +# define max_selector new_string /* highest selector setting */ + +extern alpha_file log_file; +extern int selector; +extern int dig[23]; +extern int tally; +extern int term_offset; +extern int file_offset; +extern packed_ASCII_code trick_buf[(ssup_error_line + 1)]; +extern int trick_count; +extern int first_count; +extern boolean inhibit_par_tokens; + +/* + Macro abbreviations for output to the terminal and to the log file are + defined here for convenience. Some systems need special conventions + for terminal output, and it is possible to adhere to those conventions + by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section. + @^system dependencies@> +*/ + +# define wterm_cr() fprintf(term_out,"\n") +# define wlog_cr() fprintf(log_file,"\n") + +extern void print_ln(void); +extern void print_char(int s); +extern void print(int s); +extern void lprint (lstring *ss); +extern void print_nl(str_number s); +extern void print_nlp(void); +extern void print_banner(const char *); +extern void log_banner(const char *); +extern void print_version_banner(void); +extern void print_esc(str_number s); +extern void print_the_digs(eight_bits k); +extern void print_int(longinteger n); +extern void print_two(int n); +extern void print_qhex(int n); +extern void print_roman_int(int n); +extern void print_current_string(void); + +# define print_font_name(A) tprint(font_name(A)) + +extern void print_cs(int p); +extern void sprint_cs(pointer p); +extern void sprint_cs_name(pointer p); +extern void tprint(const char *s); +extern void tprint_nl(const char *s); +extern void tprint_esc(const char *s); + +extern void prompt_input(const char *s); + +# define single_letter(A) \ + ((str_length(A)==1)|| \ + ((str_length(A)==4)&&*(str_string((A)))>=0xF0)|| \ + ((str_length(A)==3)&&*(str_string((A)))>=0xE0)|| \ + ((str_length(A)==2)&&*(str_string((A)))>=0xC0)) + +# define is_active_cs(a) \ + (a && str_length(a)>3 && \ + ( *str_string(a) == 0xEF) && \ + (*(str_string(a)+1) == 0xBF) && \ + (*(str_string(a)+2) == 0xBF)) + +# define active_cs_value(A) pool_to_unichar((str_string((A))+3)) + +extern void print_glue(scaled d, int order, const char *s); /* prints a glue component */ +extern void print_spec(int p, const char *s); /* prints a glue specification */ + +extern int font_in_short_display; /* an internal font number */ +extern void print_font_identifier(internal_font_number f); +extern void short_display(int p); /* prints highlights of list |p| */ +extern void print_font_and_char(int p); /* prints |char_node| data */ +extern void print_mark(int p); /* prints token list data in braces */ +extern void print_rule_dimen(scaled d); /* prints dimension in rule node */ +extern int depth_threshold; /* maximum nesting depth in box displays */ +extern int breadth_max; /* maximum number of items shown at the same list level */ +extern void show_box(halfword p); +extern void short_display_n(int p, int m); /* prints highlights of list |p| */ + +extern void print_csnames(int hstart, int hfinish); +extern void print_file_line(void); + +extern void begin_diagnostic(void); +extern void print_input_level(void); +extern void end_diagnostic(boolean blank_line); +extern int global_old_setting; + +extern void print_character_info(halfword p); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/scanning.c b/Build/source/texk/web2c/luatexdir/tex/scanning.c new file mode 100644 index 00000000000..deddbf5649b --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/scanning.c @@ -0,0 +1,2751 @@ +/* + +Copyright 2009-2012 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +static void scan_expr(void); + +/*tex + + Let's turn now to some procedures that \TeX\ calls upon frequently to digest + certain kinds of patterns in the input. Most of these are quite simple; some + are quite elaborate. Almost all of the routines call |get_x_token|, which can + cause them to be invoked recursively. + + The |scan_left_brace| routine is called when a left brace is supposed to be + the next non-blank token. (The term ``left brace'' means, more precisely, a + character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to appear + before the |left_brace|. + +*/ + +/* This reads a mandatory |left_brace|: */ + +void scan_left_brace(void) +{ + /*tex Get the next non-blank non-relax non-call token */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd != left_brace_cmd) { + print_err("Missing { inserted"); + help4( + "A left brace was mandatory here, so I've put one in.", + "You might want to delete and/or insert some corrections", + "so that I will find a matching right brace soon.", + "If you're confused by all this, try typing `I}' now." + ); + back_error(); + cur_tok = left_brace_token + '{'; + cur_cmd = left_brace_cmd; + cur_chr = '{'; + incr(align_state); + } +} + +/*tex + + The |scan_optional_equals| routine looks for an optional `\.=' sign preceded + by optional spaces; `\.{\\relax}' is not ignored here. + +*/ + +void scan_optional_equals(void) +{ + /*tex Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok != other_token + '=') + back_input(); +} + +/*tex + + Here is a procedure that sounds an alarm when mu and non-mu units are being + switched. + +*/ + +static void mu_error(void) +{ + print_err("Incompatible glue units"); + help1("I'm going to assume that 1mu=1pt when they're mixed."); + error(); +} + +/*tex + + The next routine `|scan_something_internal|' is used to fetch internal + numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}' + when expanding constructions like `\.{\\the\\toks0}' and + `\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int| + procedure, which calls |scan_something_internal|; on the other hand, + |scan_something_internal| also calls |scan_int|, for constructions like + `\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we have to + declare |scan_int| as a |forward| procedure. A few other procedures are also + declared at this point. + + \TeX\ doesn't know exactly what to expect when |scan_something_internal| + begins. For example, an integer or dimension or glue value could occur + immediately after `\.{\\hskip}'; and one can even say \.{\\the} with respect + to token lists in constructions like `\.{\\xdef\\o\{\\the\\output\}}'. On the + other hand, only integers are allowed after a construction like + `\.{\\count}'. To handle the various possibilities, |scan_something_internal| + has a |level| parameter, which tells the ``highest'' kind of quantity that + |scan_something_internal| is allowed to produce. Eight levels are + distinguished, namely |int_val|, |attr_val|, |dimen_val|, |glue_val|, + |mu_val|, |dir_val|, |ident_val|, and |tok_val|. + + The output of |scan_something_internal| (and of the other routines + |scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global + variable |cur_val|, and its level is put into |cur_val_level|. The highest + values of |cur_val_level| are special: |mu_val| is used only when |cur_val| + points to something in a ``muskip'' register, or to one of the three + parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip}; |ident_val| + is used only when |cur_val| points to a font identifier; |tok_val| is used + only when |cur_val| points to |null| or to the reference count of a token + list. The last two cases are allowed only when |scan_something_internal| is + called with |level=tok_val|. + + If the output is glue, |cur_val| will point to a glue specification, and the + reference count of that glue will have been updated to reflect this + reference; if the output is a nonempty token list, |cur_val| will point to + its reference count, but in this case the count will not have been updated. + Otherwise |cur_val| will contain the integer or scaled value in question. + +*/ + +/*tex The value returned by numeric scanners: */ + +int cur_val; + +/*tex Delcodes are sometimes 51 digits: */ + +int cur_val1; + +/*tex The level of this value: */ + +int cur_val_level; + +#define scanned_result(A,B) do { \ + cur_val=A; \ + cur_val_level=B; \ +} while (0) + +/*tex + + When a |glue_val| changes to a |dimen_val|, we use the width component of the + glue; there is no need to decrease the reference count, since it has not yet + been increased. When a |dimen_val| changes to an |int_val|, we use scaled + points so that the value doesn't actually change. And when a |mu_val| changes + to a |glue_val|, the value doesn't change either. + +*/ + +static void downgrade_cur_val(boolean delete_glue) +{ + if (cur_val_level == glue_val_level) { + halfword m = cur_val; + cur_val = width(m); + if (delete_glue) + flush_node(m); + } else if (cur_val_level == mu_val_level) { + mu_error(); + } + decr(cur_val_level); +} + +void negate_cur_val(boolean modify_glue) +{ + if (cur_val_level >= glue_val_level) { + if (modify_glue) { + /*tex We modify in-place. */ + } else { + cur_val = new_spec(cur_val); + } + negate(width(cur_val)); + negate(stretch(cur_val)); + negate(shrink(cur_val)); + } else { + negate(cur_val); + } +} + +/*tex + + Some of the internal items can be fetched both routines, and these have been + split off into the next routine, that returns true if the command code was + understood. + +*/ + +static boolean short_scan_something_internal(int cmd, int chr, int level, boolean negative) +{ + /*tex |chr_code| part of the operand token */ + halfword m; + /*tex general purpose index */ + halfword q; + /*tex index into |nest| */ + int p; + int save_cur_chr; + boolean succeeded = true; + m = chr; + switch (cmd) { + case assign_toks_cmd: + scanned_result(equiv(m), tok_val_level); + break; + case assign_int_cmd: + scanned_result(eqtb[m].cint, int_val_level); + break; + case assign_attr_cmd: + scanned_result(eqtb[m].cint, int_val_level); + break; + case assign_dimen_cmd: + scanned_result(eqtb[m].cint, dimen_val_level); + break; + case assign_glue_cmd: + scanned_result(equiv(m), glue_val_level); + break; + case assign_mu_glue_cmd: + scanned_result(equiv(m), mu_val_level); + break; + case assign_direction_cmd: + if (m == (int_base + line_direction_code)) { + m = int_base + text_direction_code; + } + scanned_result(eqtb[m].cint, int_val_level); + break; + case assign_dir_cmd: + if (m == (int_base + line_direction_code)) { + m = int_base + text_direction_code; + } + scanned_result(eqtb[m].cint, dir_val_level); + break; + case math_style_cmd: + scanned_result(m, int_val_level); + break; + case set_aux_cmd: + /*tex Fetch the |space_factor| or the |prev_depth|. */ + if (abs(cur_list.mode_field) != m) { + print_err("Improper "); + print_cmd_chr(set_aux_cmd, m); + help4( + "You can refer to \\spacefactor only in horizontal mode;", + "you can refer to \\prevdepth only in vertical mode; and", + "neither of these is meaningful inside \\write. So", + "I'm forgetting what you said and using zero instead." + ); + error(); + if (level != tok_val_level) + scanned_result(0, dimen_val_level); + else + scanned_result(0, int_val_level); + } else if (m == vmode) { + scanned_result(prev_depth_par, dimen_val_level); + } else { + scanned_result(space_factor_par, int_val_level); + } + break; + case set_prev_graf_cmd: + /*tex Fetch the |prev_graf| */ + if (cur_list.mode_field == 0) { + /*tex |prev_graf=0| within \.{\\write} */ + scanned_result(0, int_val_level); + } else { + p = nest_ptr; + while (abs(nest[p].mode_field) != vmode) + decr(p); + scanned_result(nest[p].pg_field, int_val_level); + } + break; + case set_page_int_cmd: + /*tex Fetch the |dead_cycles| or the |insert_penalties| */ + if (m == 0) + cur_val = dead_cycles; + else if (m == 2) + cur_val = interaction; + else + cur_val = insert_penalties; + cur_val_level = int_val_level; + break; + case set_page_dimen_cmd: + /*tex Fetch something on the |page_so_far|. */ + if ((page_contents == empty) && (!output_active)) { + if (m == 0) + cur_val = max_dimen; + else + cur_val = 0; + } else { + cur_val = page_so_far[m]; + } + cur_val_level = dimen_val_level; + break; + case set_tex_shape_cmd: + /*tex Fetch the |par_shape| size. */ + if (par_shape_par_ptr == null) + cur_val = 0; + else + cur_val = vinfo(par_shape_par_ptr + 1); + cur_val_level = int_val_level; + break; + case set_etex_shape_cmd: + /*tex Fetch a penalties array element. */ + scan_int(); + if ((equiv(m) == null) || (cur_val < 0)) { + cur_val = 0; + } else { + if (cur_val > penalty(equiv(m))) + cur_val = penalty(equiv(m)); + cur_val = penalty(equiv(m) + cur_val); + } + cur_val_level = int_val_level; + break; + case char_given_cmd: + case math_given_cmd: + case xmath_given_cmd: + scanned_result(cur_chr, int_val_level); + break; + case last_item_cmd: + /*tex + + Because the items in this case directly refer to |cur_chr|, it + needs to be saved and restored. + + */ + save_cur_chr = cur_chr; + cur_chr = chr; + /*tex + + Fetch an item in the current node, if appropriate. Here is where + \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\ } are implemented. + The reference count for \.{\\lastskip} will be updated later. + + We also handle \.{\\inputlineno} and \.{\\badness} here, because + they are legal in similar contexts. + + */ + if (m >= input_line_no_code) { + if (m >= eTeX_glue) { + /*tex Process an expression and |return|. */ + if (m < eTeX_mu) { + if (m == mu_to_glue_code) { + scan_mu_glue(); + }; + cur_val_level = glue_val_level; + } else if (m < eTeX_expr) { + if (m == glue_to_mu_code) { + scan_normal_glue(); + } + cur_val_level = mu_val_level; + } else { + cur_val_level = m - eTeX_expr + int_val_level; + scan_expr(); + } + /*tex + + This code for reducing |cur_val_level| and\slash or + negating the result is similar to the one for all the + other cases of |scan_something_internal|; we free a + glue_spec when needed. + + */ + while (cur_val_level > level) { + downgrade_cur_val(true); + } + if (negative) { + /*tex + + We get a new glue spec node with negated values and + the old intermediate is deleted. + + */ + negate_cur_val(true); + } + return succeeded; + } else if (m >= eTeX_dim) { + switch (m) { + case font_char_wd_code: + case font_char_ht_code: + case font_char_dp_code: + case font_char_ic_code: + scan_font_ident(); + q = cur_val; + scan_char_num(); + if (char_exists(q, cur_val)) { + switch (m) { + case font_char_wd_code: + cur_val = char_width(q, cur_val); + break; + case font_char_ht_code: + cur_val = char_height(q, cur_val); + break; + case font_char_dp_code: + cur_val = char_depth(q, cur_val); + break; + case font_char_ic_code: + cur_val = char_italic(q, cur_val); + break; + } + } else { + cur_val = 0; + } + break; + case par_shape_length_code: + case par_shape_indent_code: + case par_shape_dimen_code: + q = cur_chr - par_shape_length_code; + scan_int(); + if ((par_shape_par_ptr == null) || (cur_val <= 0)) { + cur_val = 0; + } else { + if (q == 2) { + q = cur_val % 2; + cur_val = (cur_val + q) / 2; + } + if (cur_val > vinfo(par_shape_par_ptr + 1)) + cur_val = vinfo(par_shape_par_ptr + 1); + cur_val = + varmem[par_shape_par_ptr + 2 * cur_val - q + 1].cint; + } + cur_val_level = dimen_val_level; + break; + case glue_stretch_code: + case glue_shrink_code: + scan_normal_glue(); + q = cur_val; + if (m == glue_stretch_code) + cur_val = stretch(q); + else + cur_val = shrink(q); + flush_node(q); + break; + } + cur_val_level = dimen_val_level; + } else { + switch (m) { + case input_line_no_code: + cur_val = line; + break; + case badness_code: + cur_val = last_badness; + break; + case luatex_version_code: + cur_val = get_luatexversion(); + break; + case last_saved_box_resource_index_code: + cur_val = last_saved_box_index; + break; + case last_saved_image_resource_index_code: + cur_val = last_saved_image_index; + break; + case last_saved_image_resource_pages_code: + cur_val = last_saved_image_pages; + break; + case last_x_pos_code: + cur_val = last_position.h; + break; + case last_y_pos_code: + cur_val = last_position.v; + break; + case random_seed_code: + cur_val = random_seed; + break; + case eTeX_version_code: + cur_val = eTeX_version; + break; + case eTeX_minor_version_code: + cur_val = eTeX_minor_version; + break; + case current_group_level_code: + cur_val = cur_level - level_one; + break; + case current_group_type_code: + cur_val = cur_group; + break; + case current_if_level_code: + q = cond_ptr; + cur_val = 0; + while (q != null) { + incr(cur_val); + q = vlink(q); + } + break; + case current_if_type_code: + if (cond_ptr == null) + cur_val = 0; + else if (cur_if < unless_code) + cur_val = cur_if + 1; + else + cur_val = -(cur_if - unless_code + 1); + break; + case current_if_branch_code: + if ((if_limit == or_code) || (if_limit == else_code)) + cur_val = 1; + else if (if_limit == fi_code) + cur_val = -1; + else + cur_val = 0; + break; + case glue_stretch_order_code: + case glue_shrink_order_code: + case eTeX_glue_stretch_order_code: + case eTeX_glue_shrink_order_code: + scan_normal_glue(); + q = cur_val; + if (m == glue_stretch_order_code || m == eTeX_glue_stretch_order_code) { + cur_val = stretch_order(q); + } else { + cur_val = shrink_order(q); + } + if (cur_val && (m == eTeX_glue_stretch_order_code || m == eTeX_glue_shrink_order_code)) { + cur_val = (cur_val == 1) ? - 1 : cur_val - 1; + } + flush_node(q); + break; + } + cur_val_level = int_val_level; + } + } else { + if (cur_chr == glue_val_level) + cur_val = zero_glue; + else + cur_val = 0; + if (cur_chr == last_node_type_code) { + cur_val_level = int_val_level; + if ((cur_list.tail_field == cur_list.head_field) + || (cur_list.mode_field == 0)) + cur_val = -1; + } else { + /*tex assumes identical values */ + cur_val_level = cur_chr; + } + if ((cur_list.tail_field != contrib_head) && (cur_list.mode_field != 0)) { + switch (cur_chr) { + case lastpenalty_code: + if (type(cur_list.tail_field) == penalty_node) + cur_val = penalty(cur_list.tail_field); + break; + case lastkern_code: + if (type(cur_list.tail_field) == kern_node) + cur_val = width(cur_list.tail_field); + break; + case lastskip_code: + if (type(cur_list.tail_field) == glue_node) + cur_val = cur_list.tail_field; + if (subtype(cur_list.tail_field) == mu_glue) + cur_val_level = mu_val_level; + break; + case last_node_type_code: + cur_val = visible_last_node_type(cur_list.tail_field); + break; + } + } else if ((cur_list.mode_field == vmode) && (cur_list.tail_field == cur_list.head_field)) { + switch (cur_chr) { + case lastpenalty_code: + cur_val = last_penalty; + break; + case lastkern_code: + cur_val = last_kern; + break; + case lastskip_code: + if (last_glue != max_halfword) + cur_val = last_glue; + break; + case last_node_type_code: + cur_val = last_node_type; + break; + } + } + } + cur_chr = save_cur_chr; + break; + default: + succeeded = false; + } + if (succeeded) { + while (cur_val_level > level) { + /*tex Convert |cur_val| to a lower level. */ + downgrade_cur_val(false); + } + /*tex + + Fix the reference count, if any, and negate |cur_val| if |negative|. + If |cur_val| points to a glue specification at this point, the + reference count for the glue does not yet include the reference by + |cur_val|. If |negative| is |true|, |cur_val_level| is known to be + |<=mu_val|. + + */ + if (negative) { + /*tex We create a new (negated) glue spec and keep the old one. */ + negate_cur_val(false); + } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) { + cur_val = new_spec(cur_val); + } + } + return succeeded; +} + +/*tex + + First, here is a short routine that is called from lua code. All the real + work is delegated to |short_scan_something_internal| that is shared between + this routine and |scan_something_internal|. + +*/ + +void scan_something_simple(halfword cmd, halfword subitem) +{ + /*tex Negative is never true. */ + if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) { + /*tex Complain that |texlib| can not do this; give zero result. */ + print_err("You can't use `"); + print_cmd_chr((quarterword) cmd, subitem); + tprint("' as tex library index"); + help1("I'm forgetting what you said and using zero instead."); + error(); + scanned_result(0, int_val_level); + } +} + +/*tex + + OK, we're ready for |scan_something_internal| itself. A second parameter, + |negative|, is set |true| if the value that is found should be negated. It is + assumed that |cur_cmd| and |cur_chr| represent the first token of the + internal quantity to be scanned; an error will be signalled if + |cur_cmd<min_internal| or |cur_cmd>max_internal|. + +*/ + +/*tex Fetch an internal parameter: */ + +void scan_something_internal(int level, boolean negative) +{ + /*tex |chr_code| part of the operand token */ + halfword m; + /*tex accumulators */ + int n, k; + RESTART: + m = cur_chr; + if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) { + switch (cur_cmd) { + case def_char_code_cmd: + /*tex Fetch a character code from some table */ + scan_char_num(); + if (m == math_code_base) { + cur_val1 = get_math_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == lc_code_base) { + cur_val1 = get_lc_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == uc_code_base) { + cur_val1 = get_uc_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == sf_code_base) { + cur_val1 = get_sf_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == cat_code_base) { + cur_val1 = get_cat_code(cat_code_table_par, cur_val); + scanned_result(cur_val1, int_val_level); + } else { + confusion("def_char"); + } + break; + case def_del_code_cmd: + case extdef_del_code_cmd: + /*tex Fetch a character code from some table. */ + scan_char_num(); + cur_val1 = get_del_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + break; + case extdef_math_code_cmd: + /*tex Fetch an extended math code table value. */ + scan_char_num(); + cur_val1 = get_math_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + break; + case toks_register_cmd: + case set_font_cmd: + case def_font_cmd: + case letterspace_font_cmd: + case copy_font_cmd: + /*tex Fetch a token list or font identifier, provided that |level=tok_val|. */ + if (level != tok_val_level) { + print_err("Missing number, treated as zero"); + help3( + "A number should have been here; I inserted `0'.", + "(If you can't figure out why I needed to see a number,", + "look up `weird error' in the index to The TeXbook.)" + ); + back_error(); + scanned_result(0, dimen_val_level); + } else if (cur_cmd == toks_register_cmd) { + scan_register_num(); + m = toks_base + cur_val; + scanned_result(equiv(m), tok_val_level); + } else { + back_input(); + scan_font_ident(); + scanned_result(font_id_base + cur_val, ident_val_level); + } + break; + case set_font_id_cmd: + scan_int(); + scanned_result(font_id_base + cur_val, ident_val_level); + break; + case def_family_cmd: + /*tex Fetch a math font identifier. */ + scan_char_num(); + cur_val1 = fam_fnt(cur_val, m); + scanned_result(font_id_base + cur_val1, ident_val_level); + break; + case set_math_param_cmd: + /*tex Fetch a math parameter. */ + cur_val1 = cur_chr; + get_token(); + if (cur_cmd != math_style_cmd) { + print_err("Missing math style, treated as \\displaystyle"); + help1("A style should have been here; I inserted `\\displaystyle'."); + cur_val = display_style; + back_error(); + } else { + cur_val = cur_chr; + } + if (cur_val1 < math_param_first_mu_glue) { + if (cur_val1 == math_param_radical_degree_raise) { + cur_val1 = get_math_param(cur_val1, cur_chr); + scanned_result(cur_val1, int_val_level); + } else { + cur_val1 = get_math_param(cur_val1, cur_chr); + scanned_result(cur_val1, dimen_val_level); + } + } else { + cur_val1 = get_math_param(cur_val1, cur_chr); + if (cur_val1 == thin_mu_skip_code) + cur_val1 = thin_mu_skip_par; + else if (cur_val1 == med_mu_skip_code) + cur_val1 = med_mu_skip_par; + else if (cur_val1 == thick_mu_skip_code) + cur_val1 = thick_mu_skip_par; + scanned_result(cur_val1, mu_val_level); + } + break; + case assign_box_dir_cmd: + scan_register_num(); + m = cur_val; + if (box(m) != null) + cur_val = box_dir(box(m)); + else + cur_val = 0; + cur_val_level = dir_val_level; + break; + case set_box_dimen_cmd: + /*tex Fetch a box dimension. */ + scan_register_num(); + if (box(cur_val) == null) + cur_val = 0; + else + cur_val = varmem[box(cur_val) + m].cint; + cur_val_level = dimen_val_level; + break; + case assign_font_dimen_cmd: + /*tex Fetch a font dimension. */ + get_font_dimen(); + break; + case assign_font_int_cmd: + /*tex Fetch a font integer. */ + scan_font_ident(); + if (m == 0) { + scanned_result(hyphen_char(cur_val), int_val_level); + } else if (m == 1) { + scanned_result(skew_char(cur_val), int_val_level); + } else if (m == no_lig_code) { + scanned_result(test_no_ligatures(cur_val), int_val_level); + } else { + n = cur_val; + scan_char_num(); + k = cur_val; + switch (m) { + case lp_code_base: + scanned_result(get_lp_code(n, k), int_val_level); + break; + case rp_code_base: + scanned_result(get_rp_code(n, k), int_val_level); + break; + case ef_code_base: + scanned_result(get_ef_code(n, k), int_val_level); + break; + case tag_code: + scanned_result(get_tag_code(n, k), int_val_level); + break; + } + } + break; + case register_cmd: + /*tex Fetch a register */ + scan_register_num(); + switch (m) { + case int_val_level: + cur_val = count(cur_val); + break; + case attr_val_level: + cur_val = attribute(cur_val); + break; + case dimen_val_level: + cur_val = dimen(cur_val); + break; + case glue_val_level: + cur_val = skip(cur_val); + break; + case mu_val_level: + cur_val = mu_skip(cur_val); + break; + } + cur_val_level = m; + break; + case ignore_spaces_cmd: + /*tex Trap unexpandable primitives. */ + if (cur_chr == 1) { + /*tex + + Reset |cur_tok| for unexpandable primitives, goto + restart. This block deals with unexpandable + \.{\\primitive} appearing at a spot where an integer or + an internal values should have been found. It fetches the + next token then resets |cur_cmd|, |cur_cs|, and + |cur_tok|, based on the primitive value of that token. No + expansion takes place, because the next token may be all + sorts of things. This could trigger further expansion + creating new errors. + + */ + get_token(); + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + } else { + cur_cmd = relax_cmd; + cur_chr = 0; + cur_tok = cs_token_flag + frozen_relax; + cur_cs = frozen_relax; + } + goto RESTART; + } + break; + case hyph_data_cmd: + switch (cur_chr) { + case 0: + case 1: + goto DEFAULT; + break; + case 2: + cur_val = get_pre_hyphen_char(language_par); + cur_val_level = int_val_level; + break; + case 3: + cur_val = get_post_hyphen_char(language_par); + cur_val_level = int_val_level; + break; + case 4: + cur_val = get_pre_exhyphen_char(language_par); + cur_val_level = int_val_level; + break; + case 5: + cur_val = get_post_exhyphen_char(language_par); + cur_val_level = int_val_level; + break; + case 6: + cur_val = get_hyphenation_min(language_par); + cur_val_level = int_val_level; + break; + case 7: + scan_int(); + cur_val = get_hj_code(language_par,cur_val); + cur_val_level = int_val_level; + break; + } + break; + default: + DEFAULT: + /*tex Complain that \.{\\the} can not do this; give zero result. */ + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + tprint("' after \\the"); + help1("I'm forgetting what you said and using zero instead."); + error(); + if (level != tok_val_level) + scanned_result(0, dimen_val_level); + else + scanned_result(0, int_val_level); + break; + } + while (cur_val_level > level) { + /*tex Convert |cur_val| to a lower level. */ + downgrade_cur_val(false); + } + /*tex + + Fix the reference count, if any, and negate |cur_val| if |negative|. + If |cur_val| points to a glue specification at this point, the + reference count for the glue does not yet include the reference by + |cur_val|. If |negative| is |true|, |cur_val_level| is known to be + |<=mu_val|. + + */ + if (negative) { + /*tex We create a new (negated) glue spec and keep the old one. */ + negate_cur_val(false); + } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) { + cur_val = new_spec(cur_val); + } + } +} + +/*tex + + It is nice to have routines that say what they do, so the original + |scan_eight_bit_int| is superceded by |scan_register_num| and + |scan_mark_num|. It may become split up even further in the future. + + Many of the |restricted classes| routines are the essentially the same except + for the upper limit and the error message, so it makes sense to combine these + all into one function. + +*/ + +void scan_limited_int(int max, const char *name) +{ + char hlp[80]; + scan_int(); + if ((cur_val < 0) || (cur_val > max)) { + if (name == NULL) { + snprintf(hlp, 80, "Since I expected to read a number between 0 and %d,", max); + print_err("Bad number"); + } else { + char msg[80]; + snprintf(hlp, 80, "A %s must be between 0 and %d.", name, max); + snprintf(msg, 80, "Bad %s", name); + print_err(msg); + } + help2(hlp, "I changed this one to zero."); + int_error(cur_val); + cur_val = 0; + } +} + +void scan_fifteen_bit_int(void) +{ + scan_real_fifteen_bit_int(); + cur_val = ((cur_val / 0x1000) * 0x1000000) + (((cur_val % 0x1000) / 0x100) * 0x10000) + (cur_val % 0x100); +} + +void scan_fifty_one_bit_int(void) +{ + int iiii; + scan_int(); + if ((cur_val < 0) || (cur_val > 0777777777)) { + print_err("Bad delimiter code"); + help2( + "A numeric delimiter (first part) must be between 0 and 2^{27}-1.", + "I changed this one to zero." + ); + int_error(cur_val); + cur_val = 0; + } + iiii = cur_val; + scan_int(); + if ((cur_val < 0) || (cur_val > 0xFFFFFF)) { + print_err("Bad delimiter code"); + help2( + "A numeric delimiter (second part) must be between 0 and 2^{24}-1.", + "I changed this one to zero." + ); + int_error(cur_val); + cur_val = 0; + } + cur_val1 = cur_val; + cur_val = iiii; +} + +/*tex + + An integer number can be preceded by any number of spaces and `\.+' or `\.-' + signs. Then comes either a decimal constant (i.e., radix 10), an octal + constant (i.e., radix 8, preceded by~'), a hexadecimal constant (radix 16, + preceded by~"), an alphabetic constant (preceded by~`), or an internal + variable. After scanning is complete, |cur_val| will contain the answer, + which must be at most $2^{31}-1=2147483647$ in absolute value. The value of + |radix| is set to 10, 8, or 16 in the cases of decimal, octal, or hexadecimal + constants, otherwise |radix| is set to zero. An optional space follows a + constant. + +*/ + +/*tex |scan_int| sets this to 8, 10, 16, or zero */ + +int radix; + +/*tex + + The |scan_int| routine is used also to scan the integer part of a fraction; + for example, the `\.3' in `\.{3.14159}' will be found by |scan_int|. The + |scan_dimen| routine assumes that |cur_tok=point_token| after the integer + part of such a fraction has been scanned by |scan_int|, and that the decimal + point has been backed up to be scanned again. + +*/ + +/*tex Sets |cur_val| to an integer: */ + +void scan_int(void) +{ + /*tex should the answer be negated? */ + boolean negative; + /*tex |$2^{31}$ / radix|, the threshold of danger */ + int m; + /*tex the digit just scanned */ + int d; + /*tex have no digits appeared? */ + boolean vacuous; + /*tex has an error message been issued? */ + boolean OK_so_far; + radix = 0; + OK_so_far = true; + /*tex Get the next non-blank non-sign token; set |negative| appropriately. */ + negative = false; + do { + /*tex Get the next non-blank non-call token. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '-') { + negative = !negative; + cur_tok = other_token + '+'; + } + } while (cur_tok == other_token + '+'); + + RESTART: + if (cur_tok == alpha_token) { + /*tex + + Scan an alphabetic character code into |cur_val|. A space is ignored + after an alphabetic character constant, so that such constants behave + like numeric ones. + + */ + /*tex Suppress macro expansion: */ + get_token(); + if (cur_tok < cs_token_flag) { + cur_val = cur_chr; + if (cur_cmd <= right_brace_cmd) { + if (cur_cmd == right_brace_cmd) + incr(align_state); + else + decr(align_state); + } + } else { + /*tex The value of a csname in this context is its name. */ + str_number txt = cs_text(cur_tok - cs_token_flag); + if (is_active_cs(txt)) + cur_val = active_cs_value(txt); + else if (single_letter(txt)) + cur_val = pool_to_unichar(str_string(txt)); + else + cur_val = (biggest_char + 1); + } + if (cur_val > biggest_char) { + print_err("Improper alphabetic constant"); + help2( + "A one-character control sequence belongs after a ` mark.", + "So I'm essentially inserting \\0 here." + ); + cur_val = '0'; + back_error(); + } else { + /*tex Scan an optional space. */ + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + } + + } else if (cur_tok == cs_token_flag + frozen_primitive) { + /*tex + + Reset |cur_tok| for unexpandable primitives, goto restart This block + deals with unexpandable \.{\\primitive} appearing at a spot where an + integer or an internal values should have been found. It fetches the + next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on + the primitive value of that token. No expansion takes place, because + the next token may be all sorts of things. This could trigger further + expansion creating new errors. + + */ + get_token(); + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + } else { + cur_cmd = relax_cmd; + cur_chr = 0; + cur_tok = cs_token_flag + frozen_relax; + cur_cs = frozen_relax; + } + goto RESTART; + } else if (cur_cmd == math_style_cmd) { + cur_val = cur_chr; + } else if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + scan_something_internal(int_val_level, false); + } else { + /*tex Scan a numeric constant. */ + radix = 10; + m = 214748364; + if (cur_tok == octal_token) { + radix = 8; + m = 02000000000; + get_x_token(); + } else if (cur_tok == hex_token) { + radix = 16; + m = 01000000000; + get_x_token(); + } + vacuous = true; + cur_val = 0; + /*tex Accumulate the constant until |cur_tok| is not a suitable digit. */ + while (1) { + if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token) && (cur_tok <= nine_token)) { + d = cur_tok - zero_token; + } else if (radix == 16) { + if ((cur_tok <= A_token + 5) && (cur_tok >= A_token)) { + d = cur_tok - A_token + 10; + } else if ((cur_tok <= other_A_token + 5) && (cur_tok >= other_A_token)) { + d = cur_tok - other_A_token + 10; + } else { + break; + } + } else { + break; + } + vacuous = false; + if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) { + if (OK_so_far) { + print_err("Number too big"); + help2( + "I can only go up to 2147483647='17777777777=\"7FFFFFFF,", + "so I'm using that number instead of yours." + ); + error(); + cur_val = infinity; + OK_so_far = false; + } + } else { + cur_val = cur_val * radix + d; + } + get_x_token(); + } + if (vacuous) { + /*tex Express astonishment that no number was here */ + print_err("Missing number, treated as zero"); + help3( + "A number should have been here; I inserted `0'.", + "(If you can't figure out why I needed to see a number,", + "look up `weird error' in the index to The TeXbook.)" + ); + back_error(); + } else if (cur_cmd != spacer_cmd) { + back_input(); + } + } + if (negative) + negate(cur_val); +} + +/*tex + + The following code is executed when |scan_something_internal| was called + asking for |mu_val|, when we really wanted a ``mudimen'' instead of + ``muglue.'' + +*/ + +static void coerce_glue(void) +{ + int v; + if (cur_val_level >= glue_val_level) { + v = width(cur_val); + flush_node(cur_val); + cur_val = v; + } +} + +/*tex + + The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to a + |scaled| value, i.e., an integral number of sp. One of its main tasks is + therefore to interpret the abbreviations for various kinds of units and to + convert measurements to scaled points. + + There are three parameters: |mu| is |true| if the finite units must be + `\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed; |inf| is + |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}' are + permitted; and |shortcut| is |true| if |cur_val| already contains an integer + and only the units need to be considered. + + The order of infinity that was found in the case of infinite glue is returned + in the global variable |cur_order|. + +*/ + +/*tex The order of infinity found by |scan_dimen|: */ + +int cur_order; + +/*tex + + Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen| may + begin with |scan_int|. This explains why it is convenient to use |scan_int| + also for the integer part of a decimal fraction. + + Several branches of |scan_dimen| work with |cur_val| as an integer and with + an auxiliary fraction |f|, so that the actual quantity of interest is + $|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked'' + representation is put into the single word |cur_val|, which suddenly switches + significance from |integer| to |scaled|. + + The necessary conversion factors can all be specified exactly as fractions + whose numerator and denominator add to 32768 or less. According to the + definitions here, $\rm2660\,dd\approx1000.33297\,mm$; this agrees well with + the value $\rm1000.333\,mm$ cited by Bosshard \^{Bosshard, Hans Rudolf} in + {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). The Didot + point has been newly standardized in 1978; it's now exactly $\rm + 1\,nd=0.375\,mm$. Conversion uses the equation + $0.375=21681/20320/72.27\cdot25.4$. The new Cicero follows the new Didot + point; $\rm 1\,nc=12\,nd$. These would lead to the ratios $21681/20320$ and + $65043/5080$, respectively. The closest approximations supported by the + algorithm would be $11183/10481$ and $1370/107$. In order to maintain the + relation $\rm 1\,nc=12\,nd$, we pick the ratio $685/642$ for $\rm nd$, + however. + +*/ + +static void scan_dimen_mu_error(void) { + print_err("Illegal unit of measure (mu inserted)"); + help4( + "The unit of measurement in math glue must be mu.", + "To recover gracefully from this error, it's best to", + "delete the erroneous units; e.g., type `2' to delete", + "two letters. (See Chapter 27 of The TeXbook.)" + ); + error(); +} + +static void scan_dimen_unknown_unit_error(void) { + print_err("Illegal unit of measure (pt inserted)"); + help6( + "Dimensions can be in units of em, ex, in, pt, pc,", + "cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!", + "I'll assume that you meant to say pt, for printer's points.", + "To recover gracefully from this error, it's best to", + "delete the erroneous units; e.g., type `2' to delete", + "two letters. (See Chapter 27 of The TeXbook.)" + ); + error(); +} + +static void scan_dimen_out_of_range_error(void) { + print_err("Dimension too large"); + help2( + "I can't work with sizes bigger than about 19 feet.", + "Continue and I'll use the largest value I can." + ); + error(); +} + +#define set_conversion(A,B) do { num=(A); denom=(B); } while(0) + +/*tex + + This function sets |cur_val| to a dimension. It could be optimized a bit more + (but not now, something for luatex > 1). + +*/ + +void scan_dimen(boolean mu, boolean inf, boolean shortcut) +{ + /*tex should the answer be negated? */ + boolean negative = false; + boolean is_true = false; + /*tex numerator of a fraction whose denominator is $2^{16}$ */ + int f = 0; + /*tex conversion ratio for the scanned units */ + int num = 0; + int denom = 0; + /*tex top of decimal digit stack */ + halfword q; + /*tex an internal dimension */ + scaled v; + /*tex temporary storage of |cur_val| */ + int save_cur_val; + arith_error = false; + cur_order = normal; + if (!shortcut) { + /*tex Get the next non-blank non-sign. */ + do { + /*tex Get the next non-blank non-call token. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == minus_token) { + negative = !negative; + cur_tok = plus_token; + } + } while (cur_tok == plus_token); + if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + /*tex Fetch an internal dimension and |goto attach_sign|, or fetch an internal integer. */ + if (mu) { + scan_something_internal(mu_val_level, false); + coerce_glue(); + if (cur_val_level == mu_val_level) { + goto ATTACH_SIGN; + } else if (cur_val_level != int_val_level) { + mu_error(); + } + } else { + scan_something_internal(dimen_val_level, false); + if (cur_val_level == dimen_val_level) { + goto ATTACH_SIGN; + } + } + } else { + back_input(); + if (cur_tok == continental_point_token) { + cur_tok = point_token; + } + if (cur_tok != point_token) { + scan_int(); + } else { + radix = 10; + cur_val = 0; + } + if (cur_tok == continental_point_token) { + cur_tok = point_token; + } + if ((radix == 10) && (cur_tok == point_token)) { + /*tex + + Scan decimal fraction. When the following code is executed, + we have |cur_tok=point_token|, but this token has been backed + up using |back_input|; we must first discard it. It turns out + that a decimal point all by itself is equivalent to + `\.{0.0}'. Let's hope people don't use that fact. + + */ + int k = 0; + halfword p = null; + int kk; + /*tex The |point_token| is being re-scanned. */ + get_token(); + while (1) { + get_x_token(); + if ((cur_tok > nine_token) || (cur_tok < zero_token)) + break; + if (k < 17) { + /*tex Digits for |k>=17| cannot affect the result. */ + q = get_avail(); + set_token_link(q, p); + set_token_info(q, cur_tok - zero_token); + p = q; + incr(k); + } + } + for (kk = k; kk >= 1; kk--) { + dig[kk - 1] = token_info(p); + q = p; + p = token_link(p); + free_avail(q); + } + f = round_decimals(k); + if (cur_cmd != spacer_cmd) { + back_input(); + } + } + } + } + if (cur_val < 0) { + /*tex In this case |f=0|. */ + negative = !negative; + negate(cur_val); + } + /*tex + + Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there + are |x| sp per unit; |goto attach_sign| if the units are internal. Now + comes the harder part: At this point in the program, |cur_val| is a + nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1; + we want to multiply the sum of these two quantities by the appropriate + factor, based on the specified units, in order to produce a |scaled| + result, and we want to do the calculation with fixed point arithmetic + that does not overflow. + + */ + if (inf) { + if (scan_keyword("fi")) { + cur_order = sfi; + if (scan_keyword("l")) { + cur_order = fil; + if (scan_keyword("l")) { + cur_order = fill; + if (scan_keyword("l")) { + cur_order = filll; + } + } + } + goto ATTACH_FRACTION; + } + } + /*tex + + Scan for (u)units that are internal dimensions; |goto attach_sign| with + |cur_val| set if found. + + */ + save_cur_val = cur_val; + /*tex + + Get the next non-blank non-call; a pitty if just backed up the input. + + */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if ((cur_cmd < min_internal_cmd) || (cur_cmd > max_internal_cmd)) { + back_input(); + } else { + /*tex |math_given_cmd|, |xmath_given_cmd| and |last_item_cmd} */ + if (mu) { + scan_something_internal(mu_val_level, false); + coerce_glue(); + if (cur_val_level != mu_val_level) { + mu_error(); + } + } else { + scan_something_internal(dimen_val_level, false); + } + v = cur_val; + goto FOUND; + } + /*tex Bah |true| forces to split the unit scanner. */ + if (mu) { + /*tex Scan for (m)\.{mu} units and |goto attach_fraction|. */ + if (! scan_keyword("mu")) { + scan_dimen_mu_error(); + } + goto ATTACH_FRACTION; + } else if (scan_keyword("em")) { + v = quad(get_cur_font()); + } else if (scan_keyword("ex")) { + v = x_height(get_cur_font()); + } else if (scan_keyword("px")) { + v = px_dimen_par; + } else { + goto PICKUP_UNIT; + } + /*tex Scan an optional space (after em, ex or px) */ + get_x_token(); + if (cur_cmd != spacer_cmd) { + back_input(); + } + FOUND: + cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000)); + goto ATTACH_SIGN; + /*tex + + Scan for (a)all other units and adjust |cur_val| and |f| accordingly; + |goto done| in the case of scaled points. + + */ + PICKUP_UNIT: + if (scan_keyword("pt")) { + /*tex The easy case: */ + goto SCALE_VALUE; + } else if (scan_keyword("mm")) { + set_conversion(7227, 2540); + goto SCALE_VALUE; + } else if (scan_keyword("cm")) { + set_conversion(7227, 254); + goto SCALE_VALUE; + } else if (scan_keyword("sp")) { + goto DONE; + } else if (scan_keyword("bp")) { + set_conversion(7227, 7200); + goto SCALE_VALUE; + } else if (scan_keyword("in")) { + set_conversion(7227, 100); + goto SCALE_VALUE; + } else if (scan_keyword("dd")) { + set_conversion(1238, 1157); + goto SCALE_VALUE; + } else if (scan_keyword("cc")) { + set_conversion(14856, 1157); + goto SCALE_VALUE; + } else if (scan_keyword("pc")) { + set_conversion(12, 1); + goto SCALE_VALUE; + } else if (scan_keyword("nd")) { + set_conversion(685, 642); + goto SCALE_VALUE; + } else if (scan_keyword("nc")) { + set_conversion(1370, 107); + goto SCALE_VALUE; + } else if (!is_true && scan_keyword("true")) { + is_true = true; + goto PICKUP_UNIT; + } + /*tex Complain about unknown unit and |goto done2|. */ + scan_dimen_unknown_unit_error(); + goto BAD_NEWS; + SCALE_VALUE: + /*tex Adjust |f| for the magnification ratio. */ + if (is_true) { + /*tex Maybe at some point we will drop mag completely, even in \DVI\ mode. */ + if (output_mode_used <= OMODE_DVI) { + prepare_mag(); + if (mag_par != 1000) { + cur_val = xn_over_d(cur_val, 1000, mag_par); + f = (1000 * f + 0200000 * tex_remainder) / mag_par; + cur_val = cur_val + (f / 0200000); + f = f % 0200000; + } + } else { + /*tex in \PDF\ mode we're always |true|. */ + one_true_inch = one_inch; + } + } + if (num) { + cur_val = xn_over_d(cur_val, num, denom); + f = (num * f + 0200000 * tex_remainder) / denom; + cur_val = cur_val + (f / 0200000); + f = f % 0200000; + } + BAD_NEWS: + ATTACH_FRACTION: + if (cur_val >= 040000) { + arith_error = true; + } else { + cur_val = cur_val * unity + f; + } + DONE: + /*tex Scan an optional space; this happens too often. */ + get_x_token(); + if (cur_cmd != spacer_cmd) { + back_input(); + } + ATTACH_SIGN: + if (arith_error || (abs(cur_val) >= 010000000000)) { + /*tex Report that this dimension is out of range. */ + scan_dimen_out_of_range_error(); + cur_val = max_dimen; + arith_error = false; + } + if (negative) { + negate(cur_val); + } +} + +/*tex + + The final member of \TeX's value-scanning trio is |scan_glue|, which makes + |cur_val| point to a glue specification. The reference count of that glue + spec will take account of the fact that |cur_val| is pointing to~it. + + The |level| parameter should be either |glue_val| or |mu_val|. + + Since |scan_dimen| was so much more complex than |scan_int|, we might expect + |scan_glue| to be even worse. But fortunately, it is very simple, since most + of the work has already been done. + +*/ + +void scan_glue(int level) +{ + /*tex should the answer be negated? */ + boolean negative = false; + /*tex new glue specification */ + halfword q = null; + /*tex does |level=mu_val|? */ + boolean mu = (level == mu_val_level); + /*tex Get the next non-blank non-sign. */ + do { + /*tex Get the next non-blank non-call token. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == minus_token) { + negative = !negative; + cur_tok = plus_token; + } + } while (cur_tok == plus_token); + if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + scan_something_internal(level, negative); + if (cur_val_level >= glue_val_level) { + if (cur_val_level != level) + mu_error(); + return; + } + if (cur_val_level == int_val_level) + scan_dimen(mu, false, true); + else if (level == mu_val_level) + mu_error(); + } else { + back_input(); + scan_dimen(mu, false, false); + if (negative) + negate(cur_val); + } + /*tex + + Create a new glue specification whose width is |cur_val|; scan for its + stretch and shrink components. + + */ + q = new_spec(zero_glue); + width(q) = cur_val; + if (scan_keyword("plus")) { + scan_dimen(mu, true, false); + stretch(q) = cur_val; + stretch_order(q) = (quarterword) cur_order; + } + if (scan_keyword("minus")) { + scan_dimen(mu, true, false); + shrink(q) = cur_val; + shrink_order(q) = (quarterword) cur_order; + } + cur_val = q; +} + +/*tex + + This procedure is supposed to scan something like `\.{\\skip\\count12}', + i.e., whatever can follow `\.{\\the}', and it constructs a token list + containing something like `\.{-3.0pt minus 0.5fill}'. + +*/ + +halfword the_toks(void) +{ + /*tex holds |selector| setting */ + int old_setting; + /*tex used for copying a token list */ + halfword p, q, r; + /*tex value of |cur_chr| */ + int c; + str_number s; + halfword retval; + /*tex Handle \.{\\unexpanded} or \.{\\detokenize} and |return|. */ + if (odd(cur_chr)) { + c = cur_chr; + scan_general_text(); + if (c == 1) { + return cur_val; + } else { + old_setting = selector; + selector = new_string; + p = get_avail(); + set_token_link(p, token_link(temp_token_head)); + token_show(p); + flush_list(p); + selector = old_setting; + s = make_string(); + retval = str_toks(str_lstring(s)); + flush_str(s); + return retval; + } + } + get_x_token(); + scan_something_internal(tok_val_level, false); + if (cur_val_level >= ident_val_level) { + /*tex Copy the token list */ + p = temp_token_head; + set_token_link(p, null); + if (cur_val_level == ident_val_level) { + store_new_token(cs_token_flag + cur_val); + } else if (cur_val != null) { + /*tex Do not copy the reference count! */ + r = token_link(cur_val); + while (r != null) { + fast_store_new_token(token_info(r)); + r = token_link(r); + } + } + return p; + } else { + old_setting = selector; + selector = new_string; + switch (cur_val_level) { + case int_val_level: + print_int(cur_val); + break; + case attr_val_level: + print_int(cur_val); + break; + case dir_val_level: + print_dir_par(cur_val); + break; + case dimen_val_level: + print_scaled(cur_val); + tprint("pt"); + break; + case glue_val_level: + print_spec(cur_val, "pt"); + flush_node(cur_val); + break; + case mu_val_level: + print_spec(cur_val, "mu"); + flush_node(cur_val); + break; + } + selector = old_setting; + s = make_string(); + retval = str_toks(str_lstring(s)); + flush_str(s); + return retval; + } +} + +str_number the_scanned_result(void) +{ + /*tex holds |selector| setting */ + int old_setting; + /*tex return value */ + str_number r; + old_setting = selector; + selector = new_string; + if (cur_val_level >= ident_val_level) { + if (cur_val != null) { + show_token_list(token_link(cur_val), null, -1); + r = make_string(); + } else { + r = get_nullstr(); + } + } else { + switch (cur_val_level) { + case int_val_level: + print_int(cur_val); + break; + case attr_val_level: + print_int(cur_val); + break; + case dir_val_level: + print_dir_par(cur_val); + break; + case dimen_val_level: + print_scaled(cur_val); + tprint("pt"); + break; + case glue_val_level: + print_spec(cur_val, "pt"); + flush_node(cur_val); + break; + case mu_val_level: + print_spec(cur_val, "mu"); + flush_node(cur_val); + break; + } + r = make_string(); + } + selector = old_setting; + return r; +} + +/*tex + + The following routine is used to implement `\.{\\fontdimen} |n| |f|'. The + boolean parameter |writing| is set |true| if the calling program intends to + change the parameter value. + +*/ + +static void font_param_error(int f) +{ + print_err("Font "); + print_esc(font_id_text(f)); + tprint(" has only "); + print_int(font_params(f)); + tprint(" fontdimen parameters"); + help2( + "To increase the number of font parameters, you must", + "use \\fontdimen immediately after the \\font is loaded." + ); + error(); +} + +void set_font_dimen(void) +{ + internal_font_number f; + int n; + scan_int(); + n = cur_val; + scan_font_ident(); + f = cur_val; + if (n <= 0) { + font_param_error(f); + } else if (n > font_params(f)) { + if (font_used(f)) { + font_param_error(f); + } else { + /*tex Increase the number of parameters in the font. */ + do { + set_font_param(f, (font_params(f) + 1), 0); + } while (n != font_params(f)); + } + } + scan_optional_equals(); + scan_normal_dimen(); + set_font_param(f, n, cur_val); +} + +void get_font_dimen(void) +{ + internal_font_number f; + /*tex The parameter number: */ + int n; + scan_int(); + n = cur_val; + scan_font_ident(); + f = cur_val; + /*tex Initialize return value: */ + cur_val = 0; + if (n <= 0) { + font_param_error(f); + goto EXIT; + } else if (n > font_params(f)) { + if (font_used(f)) { + font_param_error(f); + goto EXIT; + } else { + /*tex Increase the number of parameters in the font. */ + do { + set_font_param(f, (font_params(f) + 1), 0); + } while (n != font_params(f)); + } + } + cur_val = font_param(f, n); + EXIT: + scanned_result(cur_val, dimen_val_level); +} + +/*tex + + Here's a similar procedure that returns a pointer to a rule node. This + routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule}; + therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store + the default rule dimensions in the node, then to override them if + `\.{height}' or `\.{width}' or `\.{depth}' specifications are found (in any + order). + +*/ + +halfword scan_rule_spec(void) +{ + /*tex |width|, |depth|, and |height| all equal |null_flag| now */ + halfword q; + if (cur_cmd == no_vrule_cmd) { + q = new_rule(empty_rule); + cur_cmd = vrule_cmd; + } else if (cur_cmd == no_hrule_cmd) { + q = new_rule(empty_rule); + cur_cmd = hrule_cmd; + } else { + q = new_rule(normal_rule); + } + if (cur_cmd == vrule_cmd) { + width(q) = default_rule; + rule_dir(q) = body_direction_par; + } else { + height(q) = default_rule; + depth(q) = 0; + rule_dir(q) = text_direction_par; + } + RESWITCH: + if (scan_keyword("width")) { + scan_normal_dimen(); + width(q) = cur_val; + goto RESWITCH; + } + if (scan_keyword("height")) { + scan_normal_dimen(); + height(q) = cur_val; + goto RESWITCH; + } + if (scan_keyword("depth")) { + scan_normal_dimen(); + depth(q) = cur_val; + goto RESWITCH; + } + return q; +} + +/*tex Declare procedures that scan font-related stuff. */ + +void scan_font_ident(void) +{ + internal_font_number f; + halfword m; + /*tex Get the next non-blank non-call. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if ((cur_cmd == def_font_cmd) || (cur_cmd == letterspace_font_cmd) || (cur_cmd == copy_font_cmd)) { + f = get_cur_font(); + } else if (cur_cmd == set_font_cmd) { + f = cur_chr; + set_font_touched(f, 1); + } else if (cur_cmd == def_family_cmd) { + m = cur_chr; + scan_math_family_int(); + f = fam_fnt(cur_val, m); + set_font_touched(f, 1); + } else { + print_err("Missing font identifier"); + help2( + "I was looking for a control sequence whose", + "current meaning has been defined by \\font." + ); + back_error(); + f = null_font; + } + cur_val = f; +} + +/*tex + + The |scan_general_text| procedure is much like |scan_toks(false,false)|, but + will be invoked via |expand|, i.e., recursively. + + The token list (balanced text) created by |scan_general_text| begins at + |link(temp_token_head)| and ends at |cur_val|. (If |cur_val=temp_token_head|, + the list is empty.) + +*/ + +void scan_general_text(void) +{ + /*tex Here we save |scanner_status|: */ + int s; + /*tex Here we save |warning_index|: */ + halfword w; + /*tex Here we save |def_ref|: */ + halfword d; + /*tex The tail of the token list being built: */ + halfword p; + /*tex The new node being added to the token list via |store_new_token|: */ + halfword q; + /*tex The number of unmatched left braces: */ + halfword unbalance; + s = scanner_status; + w = warning_index; + d = def_ref; + scanner_status = absorbing; + warning_index = cur_cs; + p = get_avail(); + def_ref = p; + set_token_ref_count(def_ref, 0); + p = def_ref; + /*tex Remove the compulsory left brace. */ + scan_left_brace(); + unbalance = 1; + while (1) { + get_token(); + if (cur_tok < right_brace_limit) { + if (cur_cmd < right_brace_cmd) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + break; + } + } + store_new_token(cur_tok); + } + q = token_link(def_ref); + /*tex Discard reference count. */ + free_avail(def_ref); + if (q == null) + cur_val = temp_token_head; + else + cur_val = p; + set_token_link(temp_token_head, q); + scanner_status = s; + warning_index = w; + def_ref = d; +} + +/*tex + + The |get_x_or_protected| procedure is like |get_x_token| except that + protected macros are not expanded. It sets |cur_cmd|, |cur_chr|, + |cur_tok|,and expands non-protected macros. + +*/ + +void get_x_or_protected(void) +{ + + while (1) { + get_token(); + if (cur_cmd <= max_command_cmd) + return; + if ((cur_cmd >= call_cmd) && (cur_cmd < end_template_cmd)) { + if (token_info(token_link(cur_chr)) == protected_token) + return; + } + expand(); + } +} + +/*tex + + |scan_toks|. This function returns a pointer to the tail of a new token list, + and it also makes |def_ref| point to the reference count at the head of that + list. + + There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| is + true, the goal is to create the token list for a macro definition; otherwise + the goal is to create the token list for some other \TeX\ primitive: + \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase}, \.{\\uppercase}, + \.{\\message}, \.{\\errmessage}, \.{\\write}, or \.{\\special}. In the latter + cases a left brace must be scanned next; this left brace will not be part of + the token list, nor will the matching right brace that comes at the end. If + |xpand| is false, the token list will simply be copied from the input using + |get_token|. Otherwise all expandable tokens will be expanded until + unexpandable tokens are left, except that the results of expanding + `\.{\\the}' are not expanded further. If both |macro_def| and |xpand| are + true, the expansion applies only to the macro body (i.e., to the material + following the first |left_brace| character). + + The value of |cur_cs| when |scan_toks| begins should be the |eqtb| address of + the control sequence to display in ``runaway'' error messages. + +*/ + +halfword scan_toks(boolean macro_def, boolean xpand) +{ + /*tex token representing the highest parameter number */ + halfword t; + /*tex saved token */ + halfword s; + /*tex tail of the token list being built */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex number of unmatched left braces */ + halfword unbalance; + /*tex possible `\.{\#\{}' token */ + halfword hash_brace; + if (macro_def) + scanner_status = defining; + else + scanner_status = absorbing; + warning_index = cur_cs; + p = get_avail(); + def_ref = p; + set_token_ref_count(def_ref, 0); + p = def_ref; + hash_brace = 0; + t = zero_token; + if (macro_def) { + /*tex Scan and build the parameter part of the macro definition. */ + while (1) { + /*tex Set |cur_cmd|, |cur_chr|, |cur_tok|: */ + get_token(); + if (cur_tok < right_brace_limit) + break; + if (cur_cmd == mac_param_cmd) { + /*tex + + If the next character is a parameter number, make + |cur_tok| a |match| token; but if it is a left brace, + store `|left_brace|, |end_match|', set |hash_brace|, and + |goto done|. + + */ + s = match_token + cur_chr; + get_token(); + /* if (cur_cmd == left_brace_cmd) { */ + if (cur_tok < left_brace_limit) { + hash_brace = cur_tok; + store_new_token(cur_tok); + store_new_token(end_match_token); + goto DONE; + } + if (t == nine_token) { + print_err("You already have nine parameters"); + help2( + "I'm going to ignore the # sign you just used,", + "as well as the token that followed it." + ); + error(); + } else { + incr(t); + if (cur_tok != t) { + print_err("Parameters must be numbered consecutively"); + help2( + "I've inserted the digit you should have used after the #.", + "Type `1' to delete what you did use." + ); + back_error(); + } + cur_tok = s; + } + } + store_new_token(cur_tok); + } + store_new_token(end_match_token); + if (cur_cmd == right_brace_cmd) { + /*tex Express shock at the missing left brace; |goto found|. */ + print_err("Missing { inserted"); + incr(align_state); + help2( + "Where was the left brace? You said something like `\\def\\a}',", + "which I'm going to interpret as `\\def\\a{}'." + ); + error(); + goto FOUND; + } + + } else { + /*tex Remove the compulsory left brace. */ + scan_left_brace(); + } + DONE: + /*tex Scan and build the body of the token list; |goto found| when finished. */ + unbalance = 1; + while (1) { + if (xpand) { + /*tex + + Expand the next part of the input. Here we insert an entire token + list created by |the_toks| without expanding it further. + + */ + while (1) { + get_next(); + if (cur_cmd >= call_cmd) { + if (token_info(token_link(cur_chr)) == protected_token) { + cur_cmd = relax_cmd; + cur_chr = no_expand_flag; + } + } + if (cur_cmd <= max_command_cmd) + break; + if (cur_cmd != the_cmd) { + expand(); + } else { + q = the_toks(); + if (token_link(temp_token_head) != null) { + set_token_link(p, token_link(temp_token_head)); + p = q; + } + } + } + x_token(); + } else { + get_token(); + } + if (cur_tok < right_brace_limit) { + if (cur_cmd < right_brace_cmd) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + goto FOUND; + } + } else if (cur_cmd == mac_param_cmd) { + if (macro_def) { + /*tex Look for parameter number or \.{\#\#}. */ + s = cur_tok; + if (xpand) + get_x_token(); + else + get_token(); + if (cur_cmd != mac_param_cmd) { + if ((cur_tok <= zero_token) || (cur_tok > t)) { + print_err("Illegal parameter number in definition of "); + sprint_cs(warning_index); + help3( + "You meant to type ## instead of #, right?", + "Or maybe a } was forgotten somewhere earlier, and things", + "are all screwed up? I'm going to assume that you meant ##." + ); + back_error(); + cur_tok = s; + } else { + cur_tok = out_param_token - '0' + cur_chr; + } + } + } + } + store_new_token(cur_tok); + } + FOUND: + scanner_status = normal; + if (hash_brace != 0) + store_new_token(hash_brace); + return p; +} + +/*tex + + Here we declare two trivial procedures in order to avoid mutually recursive + procedures with parameters. + +*/ + +void scan_normal_glue(void) +{ + scan_glue(glue_val_level); +} + +void scan_mu_glue(void) +{ + scan_glue(mu_val_level); +} + +/*tex + + The |scan_expr| procedure scans and evaluates an expression. Evaluating an + expression is a recursive process: When the left parenthesis of a + subexpression is scanned we descend to the next level of recursion; the + previous level is resumed with the matching right parenthesis. + +*/ + +typedef enum { + /*tex \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */ + expr_none = 0, + /*tex \.( $\langle\it expr\rangle$ \.+ seen */ + expr_add = 1, + /*tex \.( $\langle\it expr\rangle$ \.- seen */ + expr_sub = 2, + /*tex $\langle\it term\rangle$ \.* seen */ + expr_mult = 3, + /*tex $\langle\it term\rangle$ \./ seen */ + expr_div = 4, + /*tex $\langle\it term\rangle$ \.* $\langle\it factor\rangle$ \./ seen */ + expr_scale = 5, +} expression_states; + +/*tex + + We want to make sure that each term and (intermediate) result is in the + proper range. Integer values must not exceed |infinity| ($2^{31}-1$) in + absolute value, dimensions must not exceed |max_dimen| ($2^{30}-1$). We avoid + the absolute value of an integer, because this might fail for the value + $-2^{31}$ using 32-bit arithmetic. + +*/ + +/* Clear a number or dimension and set |arith_error|: */ + +#define num_error(A) do { \ + arith_error=true; \ + A=0; \ +} while (0) + +/*tex Clear a glue spec and set |arith_error|: */ + +#define glue_error(A) do { \ + arith_error=true; \ + reset_glue_to_zero(A); \ +} while (0) + +#define normalize_glue(A) do { \ + if (stretch(A)==0) stretch_order(A)=normal; \ + if (shrink(A)==0) shrink_order(A)=normal; \ +} while (0) + +/*tex + + Parenthesized subexpressions can be inside expressions, and this nesting has + a stack. Seven local variables represent the top of the expression stack: |p| + points to pushed-down entries, if any; |l| specifies the type of expression + currently beeing evaluated; |e| is the expression so far and |r| is the state + of its evaluation; |t| is the term so far and |s| is the state of its + evaluation; finally |n| is the numerator for a combined multiplication and + division, if any. + +*/ + +#define expr_add_sub(A,B,C) add_or_sub((A),(B),(C),(r==expr_sub)) +#define expr_a(A,B) expr_add_sub((A),(B),max_dimen) + +/*tex + + The function |add_or_sub(x,y,max_answer,negative)| computes the sum (for + |negative=false|) or difference (for |negative=true|) of |x| and |y|, + provided the absolute value of the result does not exceed |max_answer|. + +*/ + +inline static int add_or_sub(int x, int y, int max_answer, boolean negative) +{ + int a; + if (negative) + negate(y); + if (x >= 0) { + if (y <= max_answer - x) + a = x + y; + else + num_error(a); + } else if (y >= -max_answer - x) { + a = x + y; + } else { + num_error(a); + } + return a; +} + +#define expr_m(A) A = nx_plus_y((A),f,0) +#define expr_d(A) A=quotient((A),f) + +/*tex + + The function |quotient(n,d)| computes the rounded quotient $q=\lfloor + n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive. + +*/ + +inline static int quotient(int n, int d) +{ + /*tex Should the answer be negated? */ + boolean negative; + /*tex The answer: */ + int a; + if (d == 0) { + num_error(a); + } else { + if (d > 0) { + negative = false; + } else { + negate(d); + negative = true; + } + if (n < 0) { + negate(n); + negative = !negative; + } + a = n / d; + n = n - a * d; + /*tex Avoid certain compiler optimizations! */ + d = n - d; + if (d + n >= 0) + incr(a); + if (negative) + negate(a); + } + return a; +} + +#define expr_s(A) A=fract((A),n,f,max_dimen) + +/*tex + + Finally, the function |fract(x,n,d,max_answer)| computes the integer + $q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive and + the result does not exceed |max_answer|. We can't use floating point + arithmetic since the routine must produce identical results in all cases; and + it would be too dangerous to multiply by~|n| and then divide by~|d|, in + separate operations, since overflow might well occur. Hence this subroutine + simulates double precision arithmetic, somewhat analogous to Metafont's + |make_fraction| and |take_fraction| routines. + +*/ + +int fract(int x, int n, int d, int max_answer) +{ + /*tex should the answer be negated? */ + boolean negative; + /*tex the answer */ + int a; + /*tex a proper fraction */ + int f; + /*tex smallest integer such that |2*h>=d| */ + int h; + /*tex intermediate remainder */ + int r; + /*tex temp variable */ + int t; + if (d == 0) + goto TOO_BIG; + a = 0; + if (d > 0) { + negative = false; + } else { + negate(d); + negative = true; + } + if (x < 0) { + negate(x); + negative = !negative; + } else if (x == 0) { + goto DONE; + } + if (n < 0) { + negate(n); + negative = !negative; + } + t = n / d; + if (t > max_answer / x) + goto TOO_BIG; + a = t * x; + n = n - t * d; + if (n == 0) + goto FOUND; + t = x / d; + if (t > (max_answer - a) / n) + goto TOO_BIG; + a = a + t * n; + x = x - t * d; + if (x == 0) + goto FOUND; + if (x < n) { + t = x; + x = n; + n = t; + } + /*tex + + Now |0<n<=x<d| and we compute $f=\lfloor xn/d+{1\over2}\rfloor$. The loop + here preserves the following invariant relations between |f|, |x|, |n|, + and~|r|: (i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor + x_0n_0/d+{1\over2}\rfloor$; (ii)~|-d<=r<0<n<=x<d|, where $x_0$, $n_0$ are + the original values of~$x$ and $n$. + + Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|, + because the latter could overflow. + + */ + f = 0; + r = (d / 2) - d; + h = -r; + while (1) { + if (odd(n)) { + r = r + x; + if (r >= 0) { + r = r - d; + incr(f); + } + } + n = n / 2; + if (n == 0) + break; + if (x < h) { + x = x + x; + } else { + t = x - d; + x = t + x; + f = f + n; + if (x < n) { + if (x == 0) + break; + t = x; + x = n; + n = t; + } + } + } + if (f > (max_answer - a)) + goto TOO_BIG; + a = a + f; + FOUND: + if (negative) + negate(a); + goto DONE; + TOO_BIG: + num_error(a); + DONE: + return a; +} + +/*tex Scans and evaluates an expression: */ + +static void scan_expr(void) +{ + /*tex saved values of |arith_error| */ + boolean a, b; + /*tex type of expression */ + int l; + /*tex state of expression so far */ + int r; + /*tex state of term so far */ + int s; + /*tex next operation or type of next factor */ + int o; + /*tex expression so far */ + int e; + /*tex term so far */ + int t; + /*tex current factor */ + int f; + /*tex numerator of combined multiplication and division */ + int n; + /*tex top of expression stack */ + halfword p; + /*tex for stack manipulations */ + halfword q; + l = cur_val_level; + a = arith_error; + b = false; + p = null; + /*tex Scan and evaluate an expression |e| of type |l|. */ + RESTART: + r = expr_none; + e = 0; + s = expr_none; + t = 0; + n = 0; + CONTINUE: + if (s == expr_none) + o = l; + else + o = int_val_level; + /*tex + + Scan a factor |f| of type |o| or start a subexpression. Get the next + non-blank non-call token. + + */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if (cur_tok == other_token + '(') { + /*tex Push the expression stack and |goto restart|. */ + q = new_node(expr_node, 0); + vlink(q) = p; + expr_type(q) = (quarterword) l; + expr_state(q) = (quarterword) (4 * s + r); + expr_e_field(q) = e; + expr_t_field(q) = t; + expr_n_field(q) = n; + p = q; + l = o; + goto RESTART; + } + back_input(); + if ((o == int_val_level) || (o == attr_val_level)) + scan_int(); + else if (o == dimen_val_level) + scan_normal_dimen(); + else if (o == glue_val_level) + scan_normal_glue(); + else + scan_mu_glue(); + f = cur_val; + + FOUND: + /*tex + + Scan the next operator and set |o| and Get the next non-blank non-call + token. + + */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if (cur_tok == other_token + '+') { + o = expr_add; + } else if (cur_tok == other_token + '-') { + o = expr_sub; + } else if (cur_tok == other_token + '*') { + o = expr_mult; + } else if (cur_tok == other_token + '/') { + o = expr_div; + } else { + o = expr_none; + if (p == null) { + if (cur_cmd != relax_cmd) + back_input(); + } else if (cur_tok != other_token + ')') { + print_err("Missing ) inserted for expression"); + help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't."); + back_error(); + } + } + arith_error = b; + /*tex Make sure that |f| is in the proper range. */ + if (((l == int_val_level) || (l == attr_val_level)) || (s > expr_sub)) { + if ((f > infinity) || (f < -infinity)) + num_error(f); + } else if (l == dimen_val_level) { + if (abs(f) > max_dimen) + num_error(f); + } else { + if ((abs(width(f)) > max_dimen) || (abs(stretch(f)) > max_dimen) || (abs(shrink(f)) > max_dimen)) + glue_error(f); + } + /*tex Cases for evaluation of the current term. */ + switch (s) { + case expr_none: + /*tex + + Applying the factor |f| to the partial term |t| (with the + operator |s|) is delayed until the next operator |o| has been + scanned. Here we handle the first factor of a partial term. A + glue spec has to be copied unless the next operator is a right + parenthesis; this allows us later on to simply modify the glue + components. + + */ + t = f; + if ((l >= glue_val_level) && (o != expr_none)) { + /*tex Do we really need to copy here? */ + normalize_glue(t); + } else { + t = f; + } + break; + case expr_mult: + /*tex + + If a multiplication is followed by a division, the two operations + are combined into a `scaling' operation. Otherwise the term |t| + is multiplied by the factor |f|. + + */ + if (o == expr_div) { + n = f; + o = expr_scale; + } else if ((l == int_val_level) || (l == attr_val_level)) { + t = mult_integers(t, f); + } else if (l == dimen_val_level) { + expr_m(t); + } else { + expr_m(width(t)); + expr_m(stretch(t)); + expr_m(shrink(t)); + } + break; + case expr_div: + /*tex Here we divide the term |t| by the factor |f|. */ + if (l < glue_val_level) { + expr_d(t); + } else { + expr_d(width(t)); + expr_d(stretch(t)); + expr_d(shrink(t)); + } + break; + case expr_scale: + /*tex Here the term |t| is multiplied by the quotient $n/f$. */ + if ((l == int_val_level) || (l == attr_val_level)) { + t = fract(t, n, f, infinity); + } else if (l == dimen_val_level) { + expr_s(t); + } else { + expr_s(width(t)); + expr_s(stretch(t)); + expr_s(shrink(t)); + } + break; + } + if (o > expr_sub) { + s = o; + } else { + /*tex + + Evaluate the current expression. When a term |t| has been completed + it is copied to, added to, or subtracted from the expression |e|. + + */ + s = expr_none; + if (r == expr_none) { + e = t; + } else if ((l == int_val_level) || (l == attr_val_level)) { + e = expr_add_sub(e, t, infinity); + } else if (l == dimen_val_level) { + e = expr_a(e, t); + } else { + /*tex + + Compute the sum or difference of two glue specs. We know that + |stretch_order(e)>normal| implies |stretch(e)<>0| and + |shrink_order(e)>normal| implies |shrink(e)<>0|. + + */ + width(e) = expr_a(width(e), width(t)); + if (stretch_order(e) == stretch_order(t)) { + stretch(e) = expr_a(stretch(e), stretch(t)); + } else if ((stretch_order(e) < stretch_order(t)) && (stretch(t) != 0)) { + stretch(e) = stretch(t); + stretch_order(e) = stretch_order(t); + } + if (shrink_order(e) == shrink_order(t)) { + shrink(e) = expr_a(shrink(e), shrink(t)); + } else if ((shrink_order(e) < shrink_order(t)) && (shrink(t) != 0)) { + shrink(e) = shrink(t); + shrink_order(e) = shrink_order(t); + } + flush_node(t); + normalize_glue(e); + } + r = o; + } + b = arith_error; + if (o != expr_none) + goto CONTINUE; + if (p != null) { + /*tex Pop the expression stack and |goto found|. */ + f = e; + q = p; + e = expr_e_field(q); + t = expr_t_field(q); + n = expr_n_field(q); + s = expr_state(q) / 4; + r = expr_state(q) % 4; + l = expr_type(q); + p = vlink(q); + flush_node(q); + goto FOUND; + } + + if (b) { + print_err("Arithmetic overflow"); + help2( + "I can't evaluate this expression,", + "since the result is out of range." + ); + error(); + if (l >= glue_val_level) { + reset_glue_to_zero(e); + } else { + e = 0; + } + } + arith_error = a; + cur_val = e; + cur_val_level = l; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/scanning.h b/Build/source/texk/web2c/luatexdir/tex/scanning.h new file mode 100644 index 00000000000..e4189a0f67a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/scanning.h @@ -0,0 +1,109 @@ +/* scanning.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef SCANNING_H +# define SCANNING_H + +typedef enum { + int_val_level = 0, /* integer values */ + attr_val_level, /* integer values */ + dimen_val_level, /* dimension values */ + glue_val_level, /* glue specifications */ + mu_val_level, /* math glue specifications */ + dir_val_level, /* directions */ + ident_val_level, /* font identifier */ + tok_val_level, /* token lists */ +} value_level_code; + +extern void scan_left_brace(void); +extern void scan_optional_equals(void); + +extern int cur_val; /* value returned by numeric scanners */ +extern int cur_val1; /* delcodes are sometimes 51 digits */ +extern int cur_val_level; /* the ``level'' of this value */ + +extern void scan_something_simple(halfword cmd, halfword subitem); +extern void scan_something_internal(int level, boolean negative); + +extern void scan_limited_int(int max, const char *name); + +extern void negate_cur_val(boolean delete_glue); + +# define scan_register_num() scan_limited_int(65535,"register code") +# define scan_mark_num() scan_limited_int(65535,"marks code") +# define scan_char_num() scan_limited_int(biggest_char,"character code") +# define scan_four_bit_int() scan_limited_int(15,NULL) +# define scan_math_family_int() scan_limited_int(255,"math family") +# define scan_real_fifteen_bit_int() scan_limited_int(32767,"mathchar") +# define scan_big_fifteen_bit_int() scan_limited_int(0x7FFFFFF,"extended mathchar") +# define scan_twenty_seven_bit_int() scan_limited_int(0777777777,"delimiter code") + +extern void scan_fifteen_bit_int(void); +extern void scan_fifty_one_bit_int(void); + +# define octal_token (other_token+'\'') /* apostrophe, indicates an octal constant */ +# define hex_token (other_token+'"') /* double quote, indicates a hex constant */ +# define alpha_token (other_token+'`') /* reverse apostrophe, precedes alpha constants */ +# define point_token (other_token+'.') /* decimal point */ +# define comma_token (other_token+',') /* decimal comma */ +# define plus_token (other_token + '+') +# define minus_token (other_token + '-') +# define continental_point_token (other_token+',') /* decimal point, Eurostyle */ +# define infinity 017777777777 /* the largest positive value that \TeX\ knows */ +# define zero_token (other_token+'0') /* zero, the smallest digit */ +# define nine_token (other_token+'9') /* zero, the smallest digit */ +# define A_token (letter_token+'A') /* the smallest special hex digit */ +# define other_A_token (other_token+'A') /* special hex digit of type |other_char| */ +extern int radix; + +extern void scan_int(void); + +extern int cur_order; + +# define scan_normal_dimen() scan_dimen(false,false,false) + +extern void scan_dimen(boolean mu, boolean inf, boolean shortcut); +extern void scan_glue(int level); + +extern halfword the_toks(void); +extern str_number the_scanned_result(void); +extern void set_font_dimen(void); +extern void get_font_dimen(void); + +# define default_rule 26214 /* 0.4\thinspace pt */ + +extern halfword scan_rule_spec(void); + +extern void scan_font_ident(void); +extern void scan_general_text(void); +extern void get_x_or_protected(void); +extern halfword scan_toks(boolean macrodef, boolean xpand); + +extern void scan_normal_glue(void); +extern void scan_mu_glue(void); + +/* extern int add_or_sub(int x, int y, int max_answer, boolean negative); */ +/* extern int quotient(int n, int d); */ +extern int fract(int x, int n, int d, int max_answer); + +/* extern void scan_expr(void); */ + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/stringpool.c b/Build/source/texk/web2c/luatexdir/tex/stringpool.c new file mode 100644 index 00000000000..bc09f814b00 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/stringpool.c @@ -0,0 +1,375 @@ +/* + +stringpool.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +Control sequence names and diagnostic messages are variable-length strings of +eight-bit characters. Since PASCAL did not have a well-developed string +mechanism, \TeX\ did all of its string processing by homegrown methods. + +Elaborate facilities for dynamic strings are not needed, so all of the necessary +operations can be handled with a simple data structure. The array |str_pool| +contains all of the (eight-bit) bytes off all of the strings, and the array +|str_start| contains indices of the starting points of each string. Strings are +referred to by integer numbers, so that string number |s| comprises the +characters |str_pool[j]| for |str_start_macro(s)<=j<str_start_macro(s+1)|. +Additional integer variables |pool_ptr| and |str_ptr| indicate the number of +entries used so far in |str_pool| and |str_start|, respectively; locations +|str_pool[pool_ptr]| and |str_start_macro(str_ptr)| are ready for the next string +to be allocated. + +String numbers 0 to |biggest_char| are reserved for strings that correspond to +single UNICODE characters. This is in accordance with the conventions of \.{WEB} +which converts single-character strings into the ASCII code number of the single +character involved. + +*/ + +/*tex The array of strings: */ + +lstring *string_pool; + +/*tex + This variable lives |STRING_OFFSET| below |string_pool| (handy for debugging: + |_string_pool[str_ptr] == str_string(str_ptr)|: +*/ + +lstring *_string_pool; + +/*tex The number of the current string being created: */ + +str_number str_ptr = (STRING_OFFSET + 1); + +/*tex The starting value of |str_ptr|: */ + +str_number init_str_ptr; + +/*tex + The current string buffer, the current index in that buffer, the malloced + size of |cur_string| and the occupied byte count: +*/ + +unsigned char *cur_string; +unsigned cur_length; +unsigned cur_string_size; +unsigned pool_size; + +/*tex + +Once a sequence of characters has been appended to |cur_string|, it officially +becomes a string when the function |make_string| is called. This function returns +the identification number of the new string as its value. + +*/ + +void reset_cur_string(void) +{ + cur_length = 0; + cur_string_size = 255; + cur_string = (unsigned char *) xmalloc(256); + memset(cur_string, 0, 256); +} + +str_number make_string(void) +{ + if (str_ptr == (max_strings + STRING_OFFSET)) { + overflow( + "number of strings", + (unsigned) (max_strings - init_str_ptr + STRING_OFFSET) + ); + } + str_room(1); + cur_string[cur_length] = '\0'; /* now |lstring.s| is always a valid C string */ + str_string(str_ptr) = (unsigned char *) cur_string; + str_length(str_ptr) = cur_length; + pool_size += cur_length; + reset_cur_string(); + str_ptr++; + return (str_ptr - 1); +} + +int pool_to_unichar(unsigned char *t) +{ + return (int) str2uni(t); +} + +/*tex + +The following subroutine compares string |s| with another string of the same +length that appears in |buffer| starting at position |k|; the result is |true| if +and only if the strings are equal. Empirical tests indicate that |str_eq_buf| is +used in such a way that it tends to return |true| about 80 percent of the time. + +*/ + +boolean str_eq_buf(str_number s, int k) +{ + int a; + if (s < STRING_OFFSET) { + a = buffer_to_unichar(k); + if (a != s) + return false; + } else { + unsigned char *j = str_string(s); + unsigned char *l = j + str_length(s); + while (j < l) { + if (*j++ != buffer[k++]) + return false; + } + } + return true; +} + +/*tex + +Here is a similar routine, but it compares two strings in the string pool, and it +does not assume that they have the same length. + +*/ + +boolean str_eq_str(str_number s, str_number t) +{ + int a = 0; + unsigned char *j, *k, *l; + if (s < STRING_OFFSET) { + if (t >= STRING_OFFSET) { + k = str_string(t); + if (s <= 0x7F && (str_length(t) == 1) && *k == s) + return true; + a = pool_to_unichar(k); + if (a != s) + return false; + } else { + if (t != s) + return false; + } + } else if (t < STRING_OFFSET) { + j = str_string(s); + if (t <= 0x7F && (str_length(s) == 1) && *j == t) + return true; + a = pool_to_unichar(j); + if (a != t) + return false; + } else { + if (str_length(s) != str_length(t)) + return false; + k = str_string(t); + j = str_string(s); + l = j + str_length(s); + while (j < l) { + if (*j++ != *k++) + return false; + } + } + return true; +} + +/*tex A string compare helper: */ + +boolean str_eq_cstr(str_number r, const char *s, size_t l) +{ + if (l != (size_t) str_length(r)) + return false; + return (strncmp((const char *) (str_string(r)), s, l) == 0); +} + + +/*tex + +The initial values of |str_pool|, |str_start|, |pool_ptr|, and |str_ptr| are +computed by the \.{INITEX} program, based in part on the information that \.{WEB} +has output while processing \TeX. + +The first |string_offset| strings are single-characters strings matching Unicode. +There is no point in generating all of these. But |str_ptr| has initialized +properly, otherwise |print_char| cannot see the difference between characters and +strings. + +*/ + +boolean get_strings_started(void) +{ + reset_cur_string(); + return true; +} + +/*tex + +The string recycling routines. \TeX{} uses 2 upto 4 {\it new\/} strings when +scanning a filename in an \.{\\input}, \.{\\openin}, or \.{\\openout} operation. +These strings are normally lost because the reference to them are not saved after +finishing the operation. |search_string| searches through the string pool for the +given string and returns either 0 or the found string number. + +*/ + +str_number search_string(str_number search) +{ + str_number s; + size_t len; + len = str_length(search); + if (len == 0) { + return get_nullstr(); + } else { + /*tex We start the search with newest string below |s|; |search>1|! */ + s = search - 1; + while (s >= STRING_OFFSET) { + /* The first |string_offset| of strings depend on the implementation! */ + if (str_length(s) == len) + if (str_eq_str(s, search)) + return s; + s--; + } + } + return 0; +} + +str_number maketexstring(const char *s) +{ + if (s == NULL || *s == 0) + return get_nullstr(); + return maketexlstring(s, strlen(s)); +} + +str_number maketexlstring(const char *s, size_t l) +{ + if (s == NULL || l == 0) + return get_nullstr(); + str_string(str_ptr) = xmalloc((unsigned) (l + 1)); + memcpy(str_string(str_ptr), s, (l + 1)); + str_length(str_ptr) = (unsigned) l; + str_ptr++; + return (str_ptr - 1); +} + +/*tex + + This appends a C string to a \TEX\ string: + +*/ + +void append_string(const unsigned char *s, unsigned l) +{ + if (s == NULL || *s == 0) + return; + l = (unsigned) strlen((const char *) s); + str_room(l); + memcpy(cur_string + cur_length, s, l); + cur_length += l; + return; +} + +char *makecstring(int s) +{ + size_t l; + return makeclstring(s, &l); +} + +char *makeclstring(int s, size_t * len) +{ + if (s < STRING_OFFSET) { + *len = (size_t) utf8_size(s); + return (char *) uni2str((unsigned) s); + } else { + unsigned l = (unsigned) str_length(s); + char *cstrbuf = xmalloc(l + 1); + memcpy(cstrbuf, str_string(s), l); + cstrbuf[l] = '\0'; + *len = (size_t) l; + return cstrbuf; + } +} + +int dump_string_pool(void) +{ + int j; + int l; + int k = str_ptr; + dump_int(k - STRING_OFFSET); + for (j = STRING_OFFSET + 1; j < k; j++) { + l = (int) str_length(j); + if (str_string(j) == NULL) + l = -1; + dump_int(l); + if (l > 0) + dump_things(*str_string(j), str_length(j)); + } + return (k - STRING_OFFSET); +} + +int undump_string_pool(void) +{ + int j; + int x; + undump_int(str_ptr); + if (max_strings < str_ptr + strings_free) + max_strings = str_ptr + strings_free; + str_ptr += STRING_OFFSET; + if (ini_version) + libcfree(string_pool); + init_string_pool_array((unsigned) max_strings); + for (j = STRING_OFFSET + 1; j < str_ptr; j++) { + undump_int(x); + if (x >= 0) { + str_length(j) = (unsigned) x; + pool_size += (unsigned) x; + str_string(j) = xmallocarray(unsigned char, (unsigned) (x + 1)); + undump_things(*str_string(j), (unsigned) x); + *(str_string(j) + str_length(j)) = '\0'; + } else { + str_length(j) = 0; + } + } + init_str_ptr = str_ptr; + return str_ptr; +} + +void init_string_pool_array(unsigned s) +{ + string_pool = xmallocarray(lstring, s); + _string_pool = string_pool - STRING_OFFSET; + memset(string_pool, 0, s * sizeof(lstring)); + /* seed the null string */ + string_pool[0].s = xmalloc(1); + string_pool[0].s[0] = '\0'; +} + +/*tex + + To destroy an already made string, we say |flush_str|. + +*/ + +void flush_str(str_number s) +{ + if (s > STRING_OFFSET) { + /*tex Don't ever delete the null string! */ + pool_size -= (unsigned) str_length(s); + str_length(s) = 0; + xfree(str_string(s)); + } + while (str_string((str_ptr - 1)) == NULL) + str_ptr--; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/stringpool.h b/Build/source/texk/web2c/luatexdir/tex/stringpool.h new file mode 100644 index 00000000000..2ba156046fd --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/stringpool.h @@ -0,0 +1,142 @@ +/* stringpool.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef STRINGPOOL_H +# define STRINGPOOL_H + +/* Both lua and tex strings can contains null, but C strings cannot, so: */ + +typedef struct { + unsigned char *s; + size_t l; +} lstring; + +typedef struct { + const char *s; + size_t l; +} const_lstring; + +extern lstring *string_pool; + +extern str_number str_ptr; +extern str_number init_str_ptr; + +# define STRING_OFFSET 0x200000 +# define STRING_OFFSET_BITS 21 + +# define get_nullstr() STRING_OFFSET + +# define biggest_char 1114111 /* 0x10FFFF, the largest allowed character number; must be |< max_halfword| */ +# define too_big_char (biggest_char+1) /* 1114112, |biggest_char+1| */ +# define special_char (biggest_char+2) /* 1114113, |biggest_char+2| */ +# define number_chars (biggest_char+3) /* 1114112, |biggest_char+1| */ + +/* + Several of the elementary string operations are performed using + macros instead of procedures, because many of the + operations are done quite frequently and we want to avoid the + overhead of procedure calls. For example, here is + a simple macro that computes the length of a string. +*/ + +# define str_length(a) string_pool[(a)-STRING_OFFSET].l +# define str_string(a) string_pool[(a)-STRING_OFFSET].s +# define str_lstring(a) string_pool[(a)-STRING_OFFSET] + +/* Strings are created by appending character codes to |str_pool|. + The |append_char| macro, defined here, does not check to see if the + value of |pool_ptr| has gotten too high; this test is supposed to be + made before |append_char| is used. There is also a |flush_char| + macro, which erases the last character appended. + + To test if there is room to append |l| more characters to |str_pool|, + we shall write |str_room(l)|, which aborts \TeX\ and gives an + apologetic error message if there isn't enough room. +*/ + +/* The length of the current string is called |cur_length|: */ + +extern unsigned char *cur_string; +extern unsigned cur_length; +extern unsigned cur_string_size; +extern unsigned pool_size; + +# define EXTRA_STRING 500 + +/* put |ASCII_code| \# at the end of |str_pool| */ +# define append_char(A) do { \ + if (cur_string==NULL) reset_cur_string(); \ + else str_room(1); \ + cur_string[cur_length++]=(unsigned char)(A); \ + } while (0) + +# define str_room(wsize) do { \ + unsigned nsize; \ + if ((cur_length+wsize) > cur_string_size) { \ + nsize = cur_string_size + cur_string_size / 5 + EXTRA_STRING; \ + if (nsize < (unsigned)(wsize)) { \ + nsize = wsize + EXTRA_STRING; \ + } \ + cur_string = (unsigned char *) xreallocarray(cur_string, unsigned char, nsize); \ + memset (cur_string+cur_length,0,nsize-cur_length); \ + cur_string_size = nsize; \ + } \ + } while (0) + +# define flush_char() --cur_length /* forget the last character in the pool */ + +extern str_number make_string(void); +extern boolean str_eq_buf(str_number s, int k); +extern boolean str_eq_str(str_number s, str_number t); +extern boolean str_eq_cstr(str_number, const char *, size_t); +extern boolean get_strings_started(void); +extern void reset_cur_string(void); + +# define save_cur_string() (cur_length>0 ? make_string() : 0) + +# define restore_cur_string(u) if (u!=0) { \ + unsigned l = (unsigned)str_length(u); \ + xfree(cur_string); \ + reset_cur_string(); \ + str_room(l); \ + memcpy(cur_string, str_string(u),l); \ + cur_length = l; \ + flush_str(u); \ + u=0; \ + } + + +extern str_number search_string(str_number search); +extern int pool_to_unichar(unsigned char *t); + +extern str_number maketexstring(const char *); +extern str_number maketexlstring(const char *, size_t); +extern void append_string(const unsigned char *s, unsigned l); + +extern char *makecstring(int); +extern char *makeclstring(int, size_t *); + +extern int dump_string_pool(void); +extern int undump_string_pool(void); + +extern void init_string_pool_array(unsigned s); +extern void flush_str(str_number s); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.c b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c new file mode 100644 index 00000000000..1ec4b12bf06 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c @@ -0,0 +1,247 @@ +/* + +texdeffont.w + +Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number to the +user's font~\.{\\f}. Adding this number to |font_id_base| gives the |eqtb| +location of a ``frozen'' control sequence that will always select the font. + +The variable |a| in the following code indicates the global nature of the value +to be set. It's used in the |define| macro. Here we're never global. + +There's not much scanner code here because the other scanners are defined where +they make most sense. + +*/ + +int font_bytes; + +void set_cur_font(internal_font_number f) +{ + int a = 0; + define(cur_font_loc, data_cmd, f); +} + +/*tex + + This prints a scaled real, rounded to five digits. + +*/ + +static char *scaled_to_string(scaled s) +{ + static char result[16]; + int n, k; + /*tex The amount of allowable inaccuracy: */ + scaled delta; + k = 0; + if (s < 0) { + /*tex Only print the sign, if negative */ + result[k++] = '-'; + s = -s; + } + { + int l = 0; + char digs[8] = { 0 }; + n = s / unity; + /*tex Process the integer part: */ + do { + digs[l++] = (char) (n % 10); + n = n / 10;; + } while (n > 0); + while (l > 0) { + result[k++] = (char) (digs[--l] + '0'); + } + } + result[k++] = '.'; + s = 10 * (s % unity) + 5; + delta = 10; + do { + if (delta > unity) { + /*tex Round the last digit: */ + s = s + 0100000 - 050000; + } + result[k++] = (char) ('0' + (s / unity)); + s = 10 * (s % unity); + delta = delta * 10; + } while (s > delta); + result[k] = 0; + return (char *) result; +} + +void tex_def_font(small_number a) +{ + /*tex The user's font identifier. */ + pointer u; + /*tex This runs through existing fonts. */ + internal_font_number f; + /*tex The name for the frozen font identifier. */ + str_number t, d; + /*tex Thos holds the |selector| setting. */ + int old_setting; + /*tex Stated `at' size, or negative of scaled magnification. */ + scaled s = -1000; + /*tex The natural direction of the font. */ + int natural_dir = -1; + char *fn; + if (job_name == 0) { + /*tex Avoid confusing \.{texput} with the font name. */ + open_log_file(); + } + get_r_token(); + u = cur_cs; + if (a >= 4) { + geq_define(u, set_font_cmd, null_font); + } else { + eq_define(u, set_font_cmd, null_font); + } + scan_optional_equals(); + /*tex Get the next non-blank non-call token. */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd != left_brace_cmd) { + back_input(); + scan_file_name(); + if (cur_area != get_nullstr() || cur_ext != get_nullstr()) { + /*tex + Have to do some rescue-ing here, fonts only have a name, no area + nor extension. + */ + old_setting = selector; + selector = new_string; + if (cur_area != get_nullstr()) { + print(cur_area); + flush_str(cur_area); + } + if (cur_name != get_nullstr()) { + print(cur_name); + flush_str(cur_name); + } + if (cur_ext != get_nullstr()) { + print(cur_ext); + flush_str(cur_ext); + } + selector = old_setting; + cur_area = get_nullstr(); + cur_name = make_string(); + cur_ext = get_nullstr(); + } + } else { + back_input(); + (void) scan_toks(false, true); + old_setting = selector; + selector = new_string; + token_show(def_ref); + selector = old_setting; + flush_list(def_ref); + cur_area = get_nullstr(); + cur_name = make_string(); + cur_ext = get_nullstr(); + } + /*tex + Scan the font size specification. The next variable keeps |cur_name| from + being changed + */ + name_in_progress = true; + if (scan_keyword("at")) { + /*tex Put the positive `at' size into |s|. */ + scan_normal_dimen(); + s = cur_val; + if ((s <= 0) || (s >= 01000000000)) { + char err[256]; + const char *errhelp[] = { + "I can only handle fonts at positive sizes that are", + "less than 2048pt, so I've changed what you said to 10pt.", + NULL + }; + snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt", scaled_to_string(s)); + tex_error(err, errhelp); + s = 10 * unity; + } + } else if (scan_keyword("scaled")) { + scan_int(); + s = -cur_val; + if ((cur_val <= 0) || (cur_val > 32768)) { + char err[256]; + const char *errhelp[] = { + "The magnification ratio must be between 1 and 32768.", + NULL + }; + snprintf(err, 255, "Illegal magnification has been changed to 1000 (%d)", (int) cur_val); + tex_error(err, errhelp); + s = -1000; + } + } + /*tex + There is no real reason to support this obsolete key as there are no useful + fonts out there so let's get rid of this overhead. This also means that + |natural_dir| can go away. + */ + /* + if (scan_keyword("naturaldir")) { + scan_direction(); + natural_dir = cur_val; + } + */ + name_in_progress = false; + fn = makecstring(cur_name); + f = read_font_info(u, fn, s, natural_dir); + xfree(fn); + equiv(u) = f; + eqtb[font_id_base + f] = eqtb[u]; + /*tex + + This is tricky: when we redefine a string we loose the old one. So this + will change as it's only used to display the |\fontname| so we can store + that with the font. + + */ + d = cs_text(font_id_base + f); + t = (u >= null_cs) ? cs_text(u) : maketexstring("FONT"); + if (!d) { + /*tex We have a new string. */ + cs_text(font_id_base + f) = t; + } else if ((d!=t) && str_eq_str(d,t)){ + /*tex We have a duplicate string. */ + flush_str(t); + } else if (d!=t){ + d = search_string(t); + if (d) { + /*tex We have already such a string. */ + cs_text(font_id_base + f) = d; + flush_str(t); + } else { + /*tex The old value is lost but still in the pool. */ + cs_text(font_id_base + f) = t; + } + } + if (cur_name == str_ptr-1) { + flush_str(cur_name); + cur_name = get_nullstr(); + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.h b/Build/source/texk/web2c/luatexdir/tex/texdeffont.h new file mode 100644 index 00000000000..24782f5fbe4 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.h @@ -0,0 +1,30 @@ +/* texdeffont.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef TEXDEFFONT_H +# define TEXDEFFONT_H + +extern int font_bytes; + +extern void set_cur_font(internal_font_number f); + +extern void tex_def_font(small_number a); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.c b/Build/source/texk/web2c/luatexdir/tex/texfileio.c new file mode 100644 index 00000000000..4e0677a564b --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texfileio.c @@ -0,0 +1,1509 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +#include <string.h> +#include <kpathsea/absolute.h> + +/*tex + +The bane of portability is the fact that different operating systems treat input +and output quite differently, perhaps because computer scientists have not given +sufficient attention to this problem. People have felt somehow that input and +output are not part of ``real'' programming. Well, it is true that some kinds of +programming are more fun than others. With existing input/output conventions +being so diverse and so messy, the only sources of joy in such parts of the code +are the rare occasions when one can find a way to make the program a little less +bad than it might have been. We have two choices, either to attack I/O now and +get it over with, or to postpone I/O until near the end. Neither prospect is very +attractive, so let's get it over with. + +The basic operations we need to do are (1)~inputting and outputting of text, to +or from a file or the user's terminal; (2)~inputting and outputting of eight-bit +bytes, to or from a file; (3)~instructing the operating system to initiate +(``open'') or to terminate (``close'') input or output from a specified file; +(4)~testing whether the end of an input file has been reached. + +\TeX\ needs to deal with two kinds of files. We shall use the term |alpha_file| +for a file that contains textual data, and the term |byte_file| for a file that +contains eight-bit binary information. These two types turn out to be the same on +many computers, but sometimes there is a significant distinction, so we shall be +careful to distinguish between them. Standard protocols for transferring such +files from computer to computer, via high-speed networks, are now becoming +available to more and more communities of users. + +The program actually makes use also of a third kind of file, called a +|word_file|, when dumping and reloading base information for its own +initialization. We shall define a word file later; but it will be possible for us +to specify simple operations on word files before they are defined. + +We finally did away with |nameoffile| and |namelength|, but the variables have to +be kept otherwise there will be link errors from |openclose.c| in the web2c +library + +*/ + +char *nameoffile; +int namelength; + +/*tex + + When input files are opened via a callback, they will also be read using + callbacks. for that purpose, the |open_read_file_callback| returns an integer + to uniquely identify a callback table. This id replaces the file point |f| in + this case, because the input does not have to be a file in the traditional + sense. + + Signalling this fact is achieved by having two arrays of integers. + +*/ + +int *input_file_callback_id; +int read_file_callback_id[17]; + +/*tex + + Here we handle |-output-directory|. We assume that it is OK to look here + first. Possibly it would be better to replace lookups in "." with lookups in + the |output_directory| followed by "." but to do this requires much more + invasive surgery in libkpathsea. + +*/ + +static char *find_in_output_directory(const char *s) +{ + if (output_directory && !kpse_absolute_p(s, false)) { + FILE *f_ptr; + char *ftemp = concat3(output_directory, DIR_SEP_STRING, s); + /*tex This code is used for input files only. */ + f_ptr = fopen(ftemp, "rb"); + if (f_ptr) { + fclose(f_ptr); + return ftemp; + } else { + free(ftemp); + + } + } + return NULL; +} + +/*tex + + Find an \.{\\input} or \.{\\read} file. |n| differentiates between those + case. + +*/ + +int kpse_available(const char *m) { + if (!kpse_init) { + fprintf(stdout,"missing kpse replacement callback '%s', quitting\n",m); + exit(1); + } + return 1 ; +} + +char *luatex_find_read_file(const char *s, int n, int callback_index) +{ + char *ftemp = NULL; + int callback_id = callback_defined(callback_index); + if (callback_id > 0) { + (void) run_callback(callback_id, "dS->R", n, s, &ftemp); + } else if (kpse_available("find_read_file")) { + /*tex Use kpathsea here. */ + ftemp = find_in_output_directory(s); + if (!ftemp) + ftemp = kpse_find_file(s, kpse_tex_format, 1); + } + if (ftemp) { + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = xstrdup(ftemp); + } + return ftemp; +} + +/*tex Find other files types. */ + +char *luatex_find_file(const char *s, int callback_index) +{ + char *ftemp = NULL; + int callback_id = callback_defined(callback_index); + if (callback_id > 0) { + (void) run_callback(callback_id, "S->R", s, &ftemp); + } else if (kpse_available("find_read_file")) { + /*tex Use kpathsea here. */ + switch (callback_index) { + case find_enc_file_callback: + ftemp = kpse_find_file(s, kpse_enc_format, 0); + break; + case find_map_file_callback: + ftemp = kpse_find_file(s, kpse_fontmap_format, 0); + break; + case find_type1_file_callback: + ftemp = kpse_find_file(s, kpse_type1_format, 0); + break; + case find_truetype_file_callback: + ftemp = kpse_find_file(s, kpse_truetype_format, 0); + break; + case find_opentype_file_callback: + ftemp = kpse_find_file(s, kpse_opentype_format, 0); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_truetype_format, 0); + break; + case find_data_file_callback: + ftemp = find_in_output_directory(s); + if (!ftemp) + ftemp = kpse_find_file(s, kpse_tex_format, 1); + break; + case find_font_file_callback: + ftemp = kpse_find_file(s, kpse_ofm_format, 1); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_tfm_format, 1); + break; + case find_vf_file_callback: + ftemp = kpse_find_file(s, kpse_ovf_format, 0); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_vf_format, 0); + break; + case find_cidmap_file_callback: + ftemp = kpse_find_file(s, kpse_cid_format, 0); + break; + default: + printf("luatex_find_file(): do not know how to handle file %s of type %d\n", s, callback_index); + break; + } + } + return ftemp; +} + +/*tex + + \LUATEX\ used to have private functions for these that did not use kpathsea, + but since the file paranoia tests have to come from kpathsea anyway, that is + no longer useful. The only downside to using luatex is that if one wants to + disable kpathsea via the Lua startup script, it is now an absolute + requirement that all file discovery callbacks are specified. Just using the + find_read_file, but not setting open_read_file, for example, does not work + any more if kpathsea is not to be used at all. + +*/ + +#define openoutnameok(A) kpse_out_name_ok (A) +#define openinnameok(A) kpse_in_name_ok (A) + +/*tex + + Open an input file F, using the kpathsea format FILEFMT and passing + |FOPEN_MODE| to fopen. The filename is in `fn'. We return whether or not the + open succeeded. + +*/ + +boolean luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, const_string fopen_mode, boolean must_exist) +{ + /*tex We haven't found anything yet. */ + string fname = NULL; + *f_ptr = NULL; + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = NULL; + fname = kpse_find_file(fn, (kpse_file_format_type) filefmt, must_exist); + if (fname) { + fullnameoffile = xstrdup(fname); + /*tex + + If we found the file in the current directory, don't leave the `./' + at the beginning of `fn', since it looks dumb when `tex foo' says + `(./foo.tex ... )'. On the other hand, if the user said `tex ./foo', + and that's what we opened, then keep it -- the user specified it, so + we shouldn't remove it. + + */ + if (fname[0] == '.' && IS_DIR_SEP(fname[1]) && (fn[0] != '.' || !IS_DIR_SEP(fn[1]))) { + unsigned i = 0; + while (fname[i + 2] != 0) { + fname[i] = fname[i + 2]; + i++; + } + fname[i] = 0; + } + /*tex This fopen is not allowed to fail. */ + *f_ptr = xfopen(fname, fopen_mode); + } + if (*f_ptr) { + recorder_record_input(fname); + } + return *f_ptr != NULL; +} + +boolean luatex_open_output(FILE ** f_ptr, const char *fn, const_string fopen_mode) +{ + char *fname; + boolean absolute = kpse_absolute_p(fn, false); + /*tex If we have an explicit output directory, use it. */ + if (output_directory && !absolute) { + fname = concat3(output_directory, DIR_SEP_STRING, fn); + } else { + fname = xstrdup(fn); + } + /*tex Is the filename openable as given? */ + *f_ptr = fopen(fname, fopen_mode); + if (!*f_ptr) { + /*tex Can't open as given. Try the envvar. */ + string texmfoutput = kpse_var_value("TEXMFOUTPUT"); + if (texmfoutput && *texmfoutput && !absolute) { + fname = concat3(texmfoutput, DIR_SEP_STRING, fn); + *f_ptr = fopen(fname, fopen_mode); + } + } + if (*f_ptr) { + recorder_record_output(fname); + } + free(fname); + return *f_ptr != NULL; +} + +boolean lua_a_open_in(alpha_file * f, char *fn, int n) +{ + int k; + char *fnam; + int callback_id; + boolean ret = true; + boolean file_ok = true; + if (n == 0) { + input_file_callback_id[iindex] = 0; + } else { + read_file_callback_id[n] = 0; + } + if (*fn == '|') + fnam = fn; + else + fnam = luatex_find_read_file(fn, n, find_read_file_callback); + if (!fnam) + return false; + callback_id = callback_defined(open_read_file_callback); + if (callback_id > 0) { + k = run_and_save_callback(callback_id, "S->", fnam); + if (k > 0) { + ret = true; + if (n == 0) + input_file_callback_id[iindex] = k; + else + read_file_callback_id[n] = k; + } else { + /*tex read failed */ + file_ok = false; + } + } else { + /*tex no read callback */ + if (openinnameok(fn)) { + ret = open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, (n == 0 ? true : false)); + } else { + /*tex open failed */ + file_ok = false; + } + } + if (!file_ok) { + ret = false; + } + return ret; +} + +boolean lua_a_open_out(alpha_file * f, char *fn, int n) +{ + boolean test; + char *fnam = NULL; + int callback_id; + boolean ret = false; + callback_id = callback_defined(find_write_file_callback); + if (callback_id > 0) { + test = run_callback(callback_id, "dS->R", n, fn, &fnam); + if ((test) && (fnam != NULL) && (strlen(fnam) > 0)) { + /*tex + + There is no message here because if that is needed the macro + package should do that in the callback code. As elsewhere, + messaging is left to \LUA\ then. + + */ + ret = open_outfile(f, fnam, FOPEN_W_MODE); + free(fnam); + } + } else { + if (openoutnameok(fn)) { + if (n > 0 && selector != term_only) { + /*tex + + This message to the log is for downward compatibility with + other tex's as there are scripts out there that act on this + message. An alternative is to let a macro package write an + explicit message. + + */ + fprintf(log_file,"\n\\openout%i = %s\n",n-1,fn); + } + ret = open_out_or_pipe(f, fn, FOPEN_W_MODE); + } + } + return ret; +} + +boolean lua_b_open_out(alpha_file * f, char *fn) +{ + boolean test; + char *fnam = NULL; + int callback_id; + boolean ret = false; + callback_id = callback_defined(find_output_file_callback); + if (callback_id > 0) { + test = run_callback(callback_id, "S->R", fn, &fnam); + if ((test) && (fnam != NULL) && (strlen(fnam) > 0)) { + ret = open_outfile(f, fnam, FOPEN_WBIN_MODE); + free(fnam); + } + } else { + if (openoutnameok(fn)) { + ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE); + } + } + return ret; +} + +void lua_a_close_in(alpha_file f, int n) +{ + int callback_id; + if (n == 0) + callback_id = input_file_callback_id[iindex]; + else + callback_id = read_file_callback_id[n]; + if (callback_id > 0) { + run_saved_callback(callback_id, "close", "->"); + destroy_saved_callback(callback_id); + if (n == 0) + input_file_callback_id[iindex] = 0; + else + read_file_callback_id[n] = 0; + } else { + close_file_or_pipe(f); + } +} + +void lua_a_close_out(alpha_file f) +{ + close_file_or_pipe(f); +} + +/*tex + + Binary input and output are done with C's ordinary procedures, so we don't + have to make any other special arrangements for binary~I/O. Text output is + also easy to do with standard routines. The treatment of text input is more + difficult, however, because of the necessary translation to |ASCII_code| + values. \TeX's conventions should be efficient, and they should blend nicely + with the user's operating environment. + + Input from text files is read one line at a time, using a routine called + |lua_input_ln|. This function is defined in terms of global variables called + |buffer|, |first|, and |last| that will be described in detail later; for + now, it suffices for us to know that |buffer| is an array of |ASCII_code| + values, and that |first| and |last| are indices into this array representing + the beginning and ending of a line of text. + +*/ + +/*tex lines of characters being read */ + +packed_ASCII_code *buffer; + +/*tex the first unused position in |buffer| */ + +int first; + +/*tex end of the line just input to |buffer| */ + +int last; + +/*tex largest index used in |buffer| */ + +int max_buf_stack; + +/*tex + + The |lua_input_ln| function brings the next line of input from the specified + file into available positions of the buffer array and returns the value + |true|, unless the file has already been entirely read, in which case it + returns |false| and sets |last:=first|. In general, the |ASCII_code| numbers + that represent the next line of the file are input into |buffer[first]|, + |buffer[first+1]|, \dots, |buffer[last-1]|; and the global variable |last| is + set equal to |first| plus the length of the line. Trailing blanks are removed + from the line; thus, either |last=first| (in which case the line was entirely + blank) or |buffer[last-1]<>" "|. + + An overflow error is given, however, if the normal actions of |lua_input_ln| + would make |last>=buf_size|; this is done so that other parts of \TeX\ can + safely look at the contents of |buffer[last+1]| without overstepping the + bounds of the |buffer| array. Upon entry to |lua_input_ln|, the condition + |first<buf_size| will always hold, so that there is always room for an + ``empty'' line. + + The variable |max_buf_stack|, which is used to keep track of how large the + |buf_size| parameter must be to accommodate the present job, is also kept up + to date by |lua_input_ln|. + + If the |bypass_eoln| parameter is |true|, |lua_input_ln| will do a |get| + before looking at the first character of the line; this skips over an |eoln| + that was in |f^|. The procedure does not do a |get| when it reaches the end + of the line; therefore it can be used to acquire input from the user's + terminal as well as from ordinary text files. + + Since the inner loop of |lua_input_ln| is part of \TeX's ``inner + loop''---each character of input comes in at this place---it is wise to + reduce system overhead by making use of special routines that read in an + entire array of characters at once, if such routines are available. + +*/ + +boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln) +{ + boolean lua_result; + int last_ptr; + int callback_id; + /*tex Todo: variable can be removed: */ + (void) bypass_eoln; + if (n == 0) + callback_id = input_file_callback_id[iindex]; + else + callback_id = read_file_callback_id[n]; + if (callback_id > 0) { + last = first; + last_ptr = first; + lua_result = + run_saved_callback(callback_id, "reader", "->l", &last_ptr); + if ((lua_result == true) && (last_ptr != 0)) { + last = last_ptr; + if (last > max_buf_stack) + max_buf_stack = last; + } else { + lua_result = false; + } + } else { + lua_result = input_ln(f, bypass_eoln); + } + if (lua_result == true) { + /*tex Fix up the input buffer using callbacks */ + if (last >= first) { + callback_id = callback_defined(process_input_buffer_callback); + if (callback_id > 0) { + last_ptr = first; + lua_result = + run_callback(callback_id, "l->l", (last - first), + &last_ptr); + if ((lua_result == true) && (last_ptr != 0)) { + last = last_ptr; + if (last > max_buf_stack) + max_buf_stack = last; + } + } + } + return true; + } + return false; +} + +/*tex + + We need a special routine to read the first line of \TeX\ input from the + user's terminal. This line is different because it is read before we have + opened the transcript file; there is sort of a ``chicken and egg'' problem + here. If the user types `\.{\\input paper}' on the first line, or if some + macro invoked by that line does such an \.{\\input}, the transcript file will + be named `\.{paper.log}'; but if no \.{\\input} commands are performed during + the first line of terminal input, the transcript file will acquire its + default name `\.{texput.log}'. (The transcript file will not contain error + messages generated by the first line before the first \.{\\input} command.) + + The first line is special also because it may be read before \TeX\ has input + a format file. In such cases, normal error messages cannot yet be given. The + following code uses concepts that will be explained later. + + Different systems have different ways to get started. But regardless of what + conventions are adopted, the routine that initializes the terminal should + satisfy the following specifications: + + \startitemize[n] + + \startitem + It should open file |term_in| for input from the terminal. (The file + |term_out| will already be open for output to the terminal.) + \stopitem + + \startitem + If the user has given a command line, this line should be considered + the first line of terminal input. Otherwise the user should be + prompted with `\.{**}', and the first line of input should be + whatever is typed in response. + \stopitem + + \startitem + The first line of input, which might or might not be a command line, + should appear in locations |first| to |last-1| of the |buffer| array. + \stopitem + + \startitem + The global variable |loc| should be set so that the character to be + read next by \TeX\ is in |buffer[loc]|. This character should not be + blank, and we should have |loc<last|. + \stopitem + + \stopitemize + + It may be necessary to prompt the user several times before a non-blank line + comes in. The prompt is `\.{**}' instead of the later `\.*' because the + meaning is slightly different: `\.{\\input}' need not be typed immediately + after~`\.{**}'.) + + The following program does the required initialization. Iff anything has been + specified on the command line, then |t_open_in| will return with |last > + first|. + +*/ + +boolean init_terminal(void) +{ + /*tex This gets the terminal input started. */ + t_open_in(); + if (last > first) { + iloc = first; + while ((iloc < last) && (buffer[iloc] == ' ')) + incr(iloc); + if (iloc < last) { + return true; + } + } + while (1) { + wake_up_terminal(); + fputs("**", term_out); + update_terminal(); + if (!input_ln(term_in, true)) { + /*tex This shouldn't happen. */ + fputs("\n! End of file on the terminal... why?\n", term_out); + return false; + } + iloc = first; + while ((iloc < last) && (buffer[iloc] == ' ')) { + incr(iloc); + } + /*tex Return unless the line was all blank. */ + if (iloc < last) { + return true; + } + fputs("Please type the name of your input file.\n", term_out); + } +} + + +/*tex + + Here is a procedure that asks the user to type a line of input, assuming that + the |selector| setting is either |term_only| or |term_and_log|. The input is + placed into locations |first| through |last-1| of the |buffer| array, and + echoed on the transcript file if appropriate. + +*/ + +void term_input(void) +{ + /*tex Index into |buffer|: */ + int k; + /*tex Now the user sees the prompt for sure: */ + update_terminal(); + if (!input_ln(term_in, true)) + fatal_error("End of file on the terminal!"); + /*tex The user's line ended with \.{<return>}: */ + term_offset = 0; + /*tex Prepare to echo the input. */ + decr(selector); + if (last != first) { + for (k = first; k <= last - 1; k++) + print_char(buffer[k]); + } + print_ln(); + /*tex Restore previous status. */ + incr(selector); +} + +/*tex + + It's time now to fret about file names. Besides the fact that different + operating systems treat files in different ways, we must cope with the fact + that completely different naming conventions are used by different groups of + people. The following programs show what is required for one particular + operating system; similar routines for other systems are not difficult to + devise. + + \TeX\ assumes that a file name has three parts: the name proper; its + ``extension''; and a ``file area'' where it is found in an external file + system. The extension of an input file or a write file is assumed to be + `\.{.tex}' unless otherwise specified; it is `\.{.log}' on the transcript + file that records each run of \TeX; it is `\.{.tfm}' on the font metric files + that describe characters in the fonts \TeX\ uses; it is `\.{.dvi}' on the + output files that specify typesetting information; and it is `\.{.fmt}' on + the format files written by \.{INITEX} to initialize \TeX. The file area can + be arbitrary on input files, but files are usually output to the user's + current area. If an input file cannot be found on the specified area, \TeX\ + will look for it on a special system area; this special area is intended for + commonly used input files like \.{webmac.tex}. + + Simple uses of \TeX\ refer only to file names that have no explicit extension + or area. For example, a person usually says `\.{\\input} \.{paper}' or + `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input} \.{paper.new}' + or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file names are best, + because they make the \TeX\ source files portable; whenever a file name + consists entirely of letters and digits, it should be treated in the same way + by all implementations of \TeX. However, users need the ability to refer to + other files in their environment, especially when responding to error + messages concerning unopenable files; therefore we want to let them use the + syntax that appears in their favorite operating system. + + The following procedures don't allow spaces to be part of file names; but + some users seem to like names that are spaced-out. System-dependent changes + to allow such things should probably be made with reluctance, and only when + an entire file name that includes spaces is ``quoted'' somehow. + + Here are the global values that file names will be scanned into. + +*/ + +/*tex name of file just scanned */ + +str_number cur_name; + +/*tex file area just scanned, or \.{""} */ + +str_number cur_area; + +/*tex file extension just scanned, or \.{""} */ + +str_number cur_ext; + +/*tex + + The file names we shall deal with have the following structure: If the name + contains `\./' or `\.:' (for Amiga only), the file area consists of all + characters up to and including the final such character; otherwise the file + area is null. If the remaining file name contains `\..', the file extension + consists of all such characters from the last `\..' to the end, otherwise the + file extension is null. + + We can scan such file names easily by using two global variables that keep + track of the occurrences of area and extension delimiters: + +*/ + +/*tex the most recent `\./', if any */ + +pool_pointer area_delimiter; + +/*tex the relevant `\..', if any */ + +pool_pointer ext_delimiter; + +/*tex + + Input files that can't be found in the user's area may appear in a standard + system area called |TEX_area|. Font metric files whose areas are not given + explicitly are assumed to appear in a standard system area called + |TEX_font_area|. $\Omega$'s compiled translation process files whose areas + are not given explicitly are assumed to appear in a standard system area. + These system area names will, of course, vary from place to place. + +*/ + +#define append_to_fn(A) do { \ + c=(A); \ + if (c!='"') { \ + if (k<file_name_size) fn[k++]=(unsigned char)(c); \ + } \ + } while (0) + + +char *pack_file_name(str_number n, str_number a, str_number e) +{ + /*tex character being packed */ + ASCII_code c; + /*tex index into |str_pool| */ + unsigned char *j; + /*tex number of positions filled in |fn| */ + int k = 0; + unsigned char *fn = xmallocarray(packed_ASCII_code, str_length(a) + str_length(n) + str_length(e) + 1); + for (j = str_string(a); j < str_string(a) + str_length(a); j++) + append_to_fn(*j); + for (j = str_string(n); j < str_string(n) + str_length(n); j++) + append_to_fn(*j); + for (j = str_string(e); j < str_string(e) + str_length(e); j++) + append_to_fn(*j); + fn[k] = 0; + return (char *) fn; +} + +/*tex + + A messier routine is also needed, since format file names must be scanned + before \TeX's string mechanism has been initialized. We shall use the global + variable |TEX_format_default| to supply the text for default system areas and + extensions related to format files. + + Under \UNIX\ we don't give the area part, instead depending on the path + searching that will happen during file opening. Also, the length will be set + in the main program. + +*/ + +char *TEX_format_default; + + +/*tex + + This part of the program becomes active when a ``virgin'' \TeX\ is trying to + get going, just after the preliminary initialization, or when the user is + substituting another format file by typing `\.\&' after the initial `\.{**}' + prompt. The buffer contains the first line of input in + |buffer[loc..(last-1)]|, where |loc<last| and |buffer[loc]<>" "|. + +*/ + +char *open_fmt_file(void) +{ + /*tex The first space after the format file name: */ + int j; + char *fmt = NULL; + int dist; + j = iloc; + if (buffer[iloc] == '&') { + incr(iloc); + j = iloc; + buffer[last] = ' '; + while (buffer[j] != ' ') + incr(j); + fmt = xmalloc((unsigned) (j - iloc + 1)); + strncpy(fmt, (char *) (buffer + iloc), (size_t) (j - iloc)); + fmt[j - iloc] = 0; + dist = (int) (strlen(fmt) - strlen(DUMP_EXT)); + if (!(strstr(fmt, DUMP_EXT) == fmt + dist)) + fmt = concat(fmt, DUMP_EXT); + if (zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) + goto FOUND; + wake_up_terminal(); + fprintf(stdout, "Sorry, I can't find the format `%s'; will try `%s'.\n", + fmt, TEX_format_default); + update_terminal(); + } + /*tex Now pull out all the stops: try for the system \.{plain} file. */ + fmt = TEX_format_default; + if (!zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) { + wake_up_terminal(); + fprintf(stdout, "I can't find the format file `%s'!\n", + TEX_format_default); + return NULL; + } + FOUND: + iloc = j; + return fmt; +} + +/*tex + + The global variable |name_in_progress| is used to prevent recursive use of + |scan_file_name|, since the |begin_name| and other procedures communicate via + global variables. Recursion would arise only by devious tricks like + `\.{\\input\\input f}'; such attempts at sabotage must be thwarted. + Furthermore, |name_in_progress| prevents \.{\\input} from being initiated + when a font size specification is being scanned. + + Another global variable, |job_name|, contains the file name that was first + \.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}' + and `\.{.fmt}' in the names of \TeX's output files. + +*/ + +/*tex is a file name being scanned? */ + +boolean name_in_progress; + +/*tex principal file name */ + +str_number job_name; + +/*tex has the transcript file been opened? */ + +boolean log_opened_global; + +/*tex + + Initially |job_name=0|; it becomes nonzero as soon as the true name is known. + We have |job_name=0| if and only if the `\.{log}' file has not been opened, + except of course for a short time just after |job_name| has become nonzero. + +*/ + +/*tex full name of the log file */ + +unsigned char *texmf_log_name; + +/*tex + + The |open_log_file| routine is used to open the transcript file and to help + it catch up to what has previously been printed on the terminal. + +*/ + +void open_log_file(void) +{ + /*tex previous |selector| setting */ + int old_setting; + /*tex index into |buffer| */ + int k; + /*tex end of first input line */ + int l; + char *fn; + old_setting = selector; + if (job_name == 0) + job_name = getjobname(maketexstring("texput")); + fn = pack_job_name(".fls"); + recorder_change_filename(fn); + fn = pack_job_name(".log"); + while (!lua_a_open_out(&log_file, fn, 0)) { + /*tex + + Try to get a different log file name. Sometimes |open_log_file| is + called at awkward moments when \TeX\ is unable to print error + messages or even to |show_context|. The |prompt_file_name| routine + can result in a |fatal_error|, but the |error| routine will not be + invoked because |log_opened| will be false. + + The normal idea of |batch_mode| is that nothing at all should be + written on the terminal. However, in the unusual case that no log + file could be opened, we make an exception and allow an explanatory + message to be seen. + + Incidentally, the program always refers to the log file as a + `\.{transcript file}', because some systems cannot use the extension + `\.{.log}' for this file. + */ + selector = term_only; + fn = prompt_file_name("transcript file name", ".log"); + } + texmf_log_name = (unsigned char *) xstrdup(fn); + selector = log_only; + log_opened_global = true; + if (callback_defined(start_run_callback) == 0) { + /*tex Print the banner line, including current date and time. */ + log_banner(luatex_version_string); + /*tex Make sure bottom level is in memory. */ + input_stack[input_ptr] = cur_input; + tprint_nl("**"); + /*tex The last position of first line. */ + l = input_stack[0].limit_field; + if (buffer[l] == end_line_char_par) { + /*tex maybe also handle multichar endlinechar */ + decr(l); + } + for (k = 1; k <= l; k++) { + print_char(buffer[k]); + } + /*tex now the transcript file contains the first line of input */ + print_ln(); + } + /*tex should be done always */ + flush_loggable_info(); + /*tex should be done always */ + selector = old_setting + 2; +} + +/*tex + + This function is needed by synctex to make its log appear in the right spot + when |output_directory| is set. + +*/ + +char *get_full_log_name (void) +{ + if (output_directory) { + char *ret = xmalloc(strlen((char *)texmf_log_name)+2+strlen(output_directory)); + ret = strcpy(ret, output_directory); + strcat(ret, "/"); + strcat(ret, (char *)texmf_log_name); + return ret; + } else { + return xstrdup((const char*)texmf_log_name); + } +} + +/*tex Synctex uses this to get the anchored path of an input file. */ + +char *luatex_synctex_get_current_name (void) +{ + char *pwdbuf = NULL, *ret; + if (kpse_absolute_p(fullnameoffile, false)) { + return xstrdup(fullnameoffile); + } + pwdbuf = xgetcwd(); + ret = concat3(pwdbuf, DIR_SEP_STRING, fullnameoffile); + free(pwdbuf) ; + return ret; +} + +/*tex + + Let's turn now to the procedure that is used to initiate file reading when an + `\.{\\input}' command is being processed. + +*/ + +void start_input(void) +{ + str_number temp_str; + char *fn; + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + back_input(); + if (cur_cmd != left_brace_cmd) { + /*tex Set |cur_name| to desired file name. */ + scan_file_name(); + } else { + scan_file_name_toks(); + } + fn = pack_file_name(cur_name, cur_area, cur_ext); + while (1) { + /*tex Set up |cur_file| and new level of input. */ + begin_file_reading(); + if (lua_a_open_in(&cur_file, fn, 0)) { + break; + } + /*tex Remove the level that didn't work. */ + end_file_reading(); + fn = prompt_file_name("input file name", ""); + } + iname = maketexstring(fullnameoffile); + /*tex + + Now that we have |fullnameoffile|, it is time to post-adjust |cur_name| + and |cur_ext| for trailing |.tex|. + + */ + { + char *n, *p; + n = p = fullnameoffile + strlen(fullnameoffile); + while (p>fullnameoffile) { + p--; + if (IS_DIR_SEP(*p)) { + break; + } + } + if (IS_DIR_SEP(*p)) { + p++; + } + while (n>fullnameoffile) { + n--; + if (*n == '.') { + break; + } + } + if (n>p) { + int q = *n; + cur_ext = maketexstring(n); + *n = 0; + cur_name = maketexstring(p); + *n = q; + } + } + source_filename_stack[in_open] = iname; + full_source_filename_stack[in_open] = xstrdup(fullnameoffile); + /*tex We can try to conserve string pool space now. */ + temp_str = search_string(iname); + if (temp_str > 0) { + flush_str(iname); + iname = temp_str; + } + if (job_name == 0) { + job_name = getjobname(cur_name); + open_log_file(); + } + /*tex + + |open_log_file| doesn't |show_context|, so |limit| and |loc| needn't be + set to meaningful values yet. + + */ + report_start_file(filetype_tex,fullnameoffile); + incr(open_parens); + update_terminal(); + istate = new_line; + /*tex Prepare new file {\sl Sync\TeX} information. */ + if (! synctex_get_no_files()) { + /*tex Give control to the {\sl Sync\TeX} controller. */ + synctexstartinput(); + } + /*tex + + Read the first line of the new file. Here we have to remember to tell the + |lua_input_ln| routine not to start with a |get|. If the file is empty, + it is considered to contain a single blank line. + + */ + line = 1; + if (lua_input_ln(cur_file, 0, false)) { + ; + } + firm_up_the_line(); + if (end_line_char_inactive) + decr(ilimit); + else + buffer[ilimit] = (packed_ASCII_code) end_line_char_par; + first = ilimit + 1; + iloc = istart; +} + +/*tex + + Because the format is zipped we read and write dump files through zlib. + Earlier versions recast |*f| from |FILE *| to |gzFile|, but there is no + guarantee that these have the same size, so a static variable is needed. + +*/ + +static gzFile gz_fmtfile = NULL; + +/*tex + + As distributed, the dump files are architecture dependent; specifically, + BigEndian and LittleEndian architectures produce different files. These + routines always output BigEndian files. This still does not guarantee them to + be architecture-independent, because it is possible to make a format that + dumps a glue ratio, i.e., a floating-point number. Fortunately, none of the + standard formats do that. + +*/ + +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + +/*tex + + This macro is always invoked as a statement. It assumes a variable `temp'. + +*/ + +# define SWAP(x, y) do { temp = x; x = y; y = temp; } while (0) + +/*tex + + Make the NITEMS items pointed at by P, each of size SIZE, be the + opposite-endianness of whatever they are now. + +*/ + +static void swap_items(char *pp, int nitems, int size) +{ + char temp; + unsigned total = (unsigned) (nitems * size); + char *q = xmalloc(total); + char *p = q; + memcpy(p,pp,total); + /*tex + + Since `size' does not change, we can write a while loop for each case, + and avoid testing `size' for each time. + + */ + switch (size) { + case 16: + /*tex + + 16-byte items happen on the DEC Alpha machine when we are not doing + sharable memory dumps. + + */ + while (nitems--) { + SWAP(p[0], p[15]); + SWAP(p[1], p[14]); + SWAP(p[2], p[13]); + SWAP(p[3], p[12]); + SWAP(p[4], p[11]); + SWAP(p[5], p[10]); + SWAP(p[6], p[9]); + SWAP(p[7], p[8]); + p += size; + } + break; + + case 12: + while (nitems--) { + SWAP(p[0], p[11]); + SWAP(p[1], p[10]); + SWAP(p[2], p[9]); + SWAP(p[3], p[8]); + SWAP(p[4], p[7]); + SWAP(p[5], p[6]); + p += size; + } + break; + + case 8: + while (nitems--) { + SWAP(p[0], p[7]); + SWAP(p[1], p[6]); + SWAP(p[2], p[5]); + SWAP(p[3], p[4]); + p += size; + } + break; + + case 4: + while (nitems--) { + SWAP(p[0], p[3]); + SWAP(p[1], p[2]); + p += size; + } + break; + + case 2: + while (nitems--) { + SWAP(p[0], p[1]); + p += size; + } + break; + case 1: + /*tex Nothing to do. */ + break; + default: + FATAL1("Can't swap a %d-byte item for (un)dumping", size); + } + memcpy(pp,q,total); + xfree(q); +} +#endif + +/*tex + + That second swap is to make sure following calls don't get confused in the + case of |dump_things|. + +*/ + +void do_zdump(char *p, int item_size, int nitems, FILE * out_file) +{ + int err; + (void) out_file; + if (nitems == 0) + return; +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items(p, nitems, item_size); +#endif + if (gzwrite(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) != + item_size * nitems) { + fprintf(stderr, "! Could not write %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err)); + uexit(1); + } +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items(p, nitems, item_size); +#endif +} + +void do_zundump(char *p, int item_size, int nitems, FILE * in_file) +{ + int err; + (void) in_file; + if (nitems == 0) + return; + if (gzread(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) <= 0) { + fprintf(stderr, "Could not undump %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err)); + uexit(1); + } +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items(p, nitems, item_size); +#endif +} + +/*tex + + Tests has shown that a level 3 compression is the most optimal tradeoff + between file size and load time. + +*/ + +#define COMPRESSION "R3" + +boolean zopen_w_input(FILE ** f, const char *fname, int format, const_string fopen_mode) +{ + int callbackid; + int res; + char *fnam; + callbackid = callback_defined(find_format_file_callback); + if (callbackid > 0) { + res = run_callback(callbackid, "S->R", fname, &fnam); + if (res && fnam && strlen(fnam) > 0) { + *f = fopen(fnam, fopen_mode); + if (*f == NULL) { + return 0; + } + } else { + return 0; + } + } else { + res = luatex_open_input(f, fname, format, fopen_mode, true); + } + if (res) { + gz_fmtfile = gzdopen(fileno(*f), "rb" COMPRESSION); + } + return res; +} + +boolean zopen_w_output(FILE ** f, const char *s, const_string fopen_mode) +{ + int res = 1; + if (luainit) { + *f = fopen(s, fopen_mode); + if (*f == NULL) { + return 0; + } + } else { + res = luatex_open_output(f, s, fopen_mode); + } + if (res) { + gz_fmtfile = gzdopen(fileno(*f), "wb" COMPRESSION); + } + return res; +} + +void zwclose(FILE * f) +{ + (void) f; + gzclose(gz_fmtfile); +} + +/*tex Create the \DVI\ or \PDF\ file. */ + +int open_outfile(FILE ** f, const char *name, const char *mode) +{ + FILE *res; + res = fopen(name, mode); + if (res != NULL) { + *f = res; + return 1; + } + return 0; +} + +/*tex The caller should set |tfm_buffer=NULL| and |tfm_size=0|. */ + +int readbinfile(FILE * f, unsigned char **tfm_buffer, int *tfm_size) +{ + void *buf; + int size; + if (fseek(f, 0, SEEK_END) == 0) { + size = (int) ftell(f); + if (size > 0) { + buf = xmalloc((unsigned) size); + if (fseek(f, 0, SEEK_SET) == 0) { + if (fread((void *) buf, (size_t) size, 1, f) == 1) { + *tfm_buffer = (unsigned char *) buf; + *tfm_size = size; + return 1; + } + } + } else { + *tfm_buffer = NULL; + *tfm_size = 0; + return 1; + } + } + /*tex Either seek failed or we have a zero-sized file. */ + return 0; +} + +/*tex + + Like |os.execute()|, the |runpopen()| function is called only when + |shellenabledp == 1|. Unlike |os.execute()| we write errors to stderr, since + we have nowhere better to use; and of course we return a file handle (or + NULL) instead of a status indicator. + +*/ + +static FILE *runpopen(char *cmd, const char *mode) +{ + FILE *f = NULL; + char *safecmd = NULL; + char *cmdname = NULL; + int allow; +#ifdef WIN32 + char *pp; + + for (pp = cmd; *pp; pp++) { + if (*pp == '\'') *pp = '"'; + } +#endif + /*tex If |restrictedshell| is zero, any command is allowed. */ + if (restrictedshell == 0) { + allow = 1; + } else { + const char *thecmd = cmd; + allow = shell_cmd_is_allowed(thecmd, &safecmd, &cmdname); + } + if (allow == 1) + f = popen(cmd, mode); + else if (allow == 2) + f = popen(safecmd, mode); + else if (allow == -1) + fprintf(stderr, "\nrunpopen quotation error in command line: %s\n", cmd); + else + fprintf(stderr, "\nrunpopen command not allowed: %s\n", cmdname); + if (safecmd) + free(safecmd); + if (cmdname) + free(cmdname); + return f; +} + +/*tex + + The code that implements |popen()| needs an array for tracking possible pipe + file pointers, because these need to be closed using |pclose()|. + +*/ + +#define NUM_PIPES 16 +static FILE *pipes[NUM_PIPES]; + +#ifdef WIN32 +FILE *Poptr; +#endif + +boolean open_in_or_pipe(FILE ** f_ptr, char *fn, int filefmt, const_string fopen_mode, boolean must_exist) +{ + string fname = NULL; + int i; + /*tex + + Opening a read pipe is straightforward, only have to skip past the pipe + symbol in the file name. filename quoting is assumed to happen elsewhere + (it does :-)) + + */ + if (shellenabledp && *fn == '|') { + /*tex The user requested a pipe. */ + *f_ptr = NULL; + fname = (string) xmalloc((unsigned) (strlen(fn) + 1)); + strcpy(fname, fn); + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = xstrdup(fname); + recorder_record_input(fname + 1); + *f_ptr = runpopen(fname + 1, "r"); + free(fname); + for (i = 0; i < NUM_PIPES; i++) { + if (pipes[i] == NULL) { + pipes[i] = *f_ptr; + break; + } + } + if (*f_ptr) + setvbuf(*f_ptr, (char *) NULL, _IONBF, 0); +#ifdef WIN32 + Poptr = *f_ptr; +#endif + return *f_ptr != NULL; + } + return luatex_open_input(f_ptr, fn, filefmt, fopen_mode, must_exist); +} + + +boolean open_out_or_pipe(FILE ** f_ptr, char *fn, const_string fopen_mode) +{ + string fname; + int i; + /*tex + + Opening a write pipe takes a little bit more work, because TeX will + perhaps have appended ".tex". To avoid user confusion as much as + possible, this extension is stripped only when the command is a bare + word. Some small string trickery is needed to make sure the correct + number of bytes is free()-d afterwards. + */ + if (shellenabledp && *fn == '|') { + /*tex The user requested a pipe. */ + fname = (string) xmalloc((unsigned) (strlen(fn) + 1)); + strcpy(fname, fn); + if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) { + /*tex + + \METAPOST\ and \METAFIONT\ currently do not use this code, but it + is better to be prepared. Hm, what has this todo with \LUATEX ? + + */ + if (STREQ((fname + strlen(fname) - 3), "tex")) + *(fname + strlen(fname) - 4) = 0; + *f_ptr = runpopen(fname + 1, "w"); + *(fname + strlen(fname)) = '.'; + } else { + *f_ptr = runpopen(fname + 1, "w"); + } + recorder_record_output(fname + 1); + free(fname); + for (i = 0; i < NUM_PIPES; i++) { + if (pipes[i] == NULL) { + pipes[i] = *f_ptr; + break; + } + } + if (*f_ptr) + setvbuf(*f_ptr, (char *) NULL, _IONBF, 0); + return *f_ptr != NULL; + } + return luatex_open_output(f_ptr, fn, fopen_mode); +} + + +void close_file_or_pipe(FILE * f) +{ + int i; + if (shellenabledp) { + for (i = 0; i <= 15; i++) { + /*tex If this file was a pipe, |pclose()| it and return. */ + if (pipes[i] == f) { + if (f) { + pclose(f); +#ifdef WIN32 + Poptr = NULL; +#endif + } + pipes[i] = NULL; + return; + } + } + } + close_file(f); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.h b/Build/source/texk/web2c/luatexdir/tex/texfileio.h new file mode 100644 index 00000000000..c6366500766 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texfileio.h @@ -0,0 +1,145 @@ +/* texfileio.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef TEXFILEIO_H +# define TEXFILEIO_H + +extern int *input_file_callback_id; +extern int read_file_callback_id[17]; + +extern char *luatex_find_read_file(const char *s, int n, int callback_index); +extern boolean luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, + const_string fopen_mode, boolean must_exist); +extern boolean luatex_open_output(FILE ** f_ptr, const char *fn, + const_string fopen_mode); + +extern boolean lua_a_open_in(alpha_file * f, char *fn, int n); +extern boolean lua_a_open_out(alpha_file * f, char *fn, int n); +extern boolean lua_b_open_out(alpha_file * f, char *fn); +extern void lua_a_close_in(alpha_file f, int n); +extern void lua_a_close_out(alpha_file f); + +extern packed_ASCII_code *buffer; +extern int first; +extern int last; +extern int max_buf_stack; + +extern boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln); + +/* +The user's terminal acts essentially like other files of text, except +that it is used both for input and for output. When the terminal is +considered an input file, the file variable is called |term_in|, and when it +is considered an output file the file variable is |term_out|. +@^system dependencies@> +*/ + +# define term_in stdin /* the terminal as an input file */ +# define term_out stdout /* the terminal as an output file */ + + +/* +Here is how to open the terminal files. |t_open_out| does nothing. +|t_open_in|, on the other hand, does the work of ``rescanning,'' or getting +any command line arguments the user has provided. It's defined in C. +*/ + +# define t_open_out() /* output already open for text output */ + +/* +Sometimes it is necessary to synchronize the input/output mixture that +happens on the user's terminal, and three system-dependent +procedures are used for this +purpose. The first of these, |update_terminal|, is called when we want +to make sure that everything we have output to the terminal so far has +actually left the computer's internal buffers and been sent. +The second, |clear_terminal|, is called when we wish to cancel any +input that the user may have typed ahead (since we are about to +issue an unexpected error message). The third, |wake_up_terminal|, +is supposed to revive the terminal if the user has disabled it by +some instruction to the operating system. The following macros show how +these operations can be specified with {\mc UNIX}. |update_terminal| +does an |fflush|. |clear_terminal| is redefined +to do nothing, since the user should control the terminal. +@^system dependencies@> +*/ + +# define update_terminal() fflush (term_out) +# define clear_terminal() do { ; } while (0) +# define wake_up_terminal() do { ; } while (0) /* cancel the user's cancellation of output */ + +extern boolean init_terminal(void); +extern void term_input(void); + +extern str_number cur_name; +extern str_number cur_area; +extern str_number cur_ext; +extern pool_pointer area_delimiter; +extern pool_pointer ext_delimiter; + +extern char *pack_file_name(str_number n, str_number a, str_number e); + +# define file_name_size 512 + +# define format_area_length 0 /* length of its area part */ +# define format_ext_length 4 /* length of its `\.{.fmt}' part */ +# define format_extension ".fmt" + /* the extension, as a constant */ + +extern char *TEX_format_default; + +extern char *open_fmt_file(void); + +extern boolean name_in_progress; /* is a file name being scanned? */ +extern str_number job_name; /* principal file name */ +extern boolean log_opened_global; /* has the transcript file been opened? */ + +extern unsigned char *texmf_log_name; /* full name of the log file */ + +extern void open_log_file(void); +extern char *get_full_log_name (void); +extern void start_input(void); + +extern int open_outfile(FILE ** f, const char *name, const char *mode); + +extern boolean zopen_w_input(FILE **, const char *, int, + const_string fopen_mode); +extern boolean zopen_w_output(FILE **, const char *, const_string fopen_mode); +extern void zwclose(FILE *); + +# define read_tfm_file readbinfile +# define read_vf_file readbinfile +# define read_data_file readbinfile + +extern boolean openinnameok(const_string); +extern boolean openoutnameok(const_string); + +# ifdef WIN32 +extern FILE *Poptr; +# endif + +extern boolean open_in_or_pipe(FILE **, char *, int, const_string fopen_mode, + boolean must_exist); +extern boolean open_out_or_pipe(FILE **, char *, const_string fopen_mode); +extern void close_file_or_pipe(FILE *); + +extern char *luatex_synctex_get_current_name(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.c b/Build/source/texk/web2c/luatexdir/tex/texmath.c new file mode 100644 index 00000000000..4d2bf80ecd6 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texmath.c @@ -0,0 +1,2727 @@ +/* + +Copyright 2008-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +#define mode mode_par +#define tail tail_par +#define head head_par +#define dir_save dirs_par + +/*tex + + Concerning display skips, \TEX\ normally always inserts before and only after + when larger than zero. THis can ow be controlled with |\mathdisplayskipmode|: + + \starttabulate + \NC 0 \NC normal \TEX \NC \NR + \NC 1 \NC always \NC \NR + \NC 2 \NC non-zero \NC \NR + \NC 3 \NC ignore \NC \NR + \stoptabulate + +*/ + +#define back_error(A,B) do { \ + OK_to_interrupt=false; \ + back_input(); \ + OK_to_interrupt=true; \ + tex_error(A,B); \ + } while (0) + +int scan_math(pointer, int); +int scan_math_style(pointer, int); +pointer fin_mlist(pointer); + +/*tex + + When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an + {\sl mlist}, which is essentially a tree structure representing that formula. + An mlist is a linear sequence of items, but we can regard it as a tree + structure because mlists can appear within mlists. For example, many of the + entries can be subscripted or superscripted, and such ``scripts'' are mlists + in their own right. + + An entire formula is parsed into such a tree before any of the actual + typesetting is done, because the current style of type is usually not known + until the formula has been fully scanned. For example, when the formula + `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell that + `\.{a+b}' will be in script size until `\.{\\over}' has appeared. + + During the scanning process, each element of the mlist being built is + classified as a relation, a binary operator, an open parenthesis, etc., or as + a construct like `\.{\\sqrt}' that must be built up. This classification + appears in the mlist data structure. + + After a formula has been fully scanned, the mlist is converted to an hlist so + that it can be incorporated into the surrounding text. This conversion is + controlled by a recursive procedure that decides all of the appropriate + styles by a ``top-down'' process starting at the outermost level and working + in towards the subformulas. The formula is ultimately pasted together using + combinations of horizontal and vertical boxes, with glue and penalty nodes + inserted as necessary. + + An mlist is represented internally as a linked list consisting chiefly of + ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat + similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are + allowed to appear in mlists together with the noads; \TeX\ tells the + difference by means of the |type| field, since a noad's |type| is always + greater than that of a node. An mlist does not contain character nodes, hlist + nodes, vlist nodes, math nodes or unset nodes; in particular, each mlist item + appears in the variable-size part of |mem|, so the |type| field is always + present. + + Each noad is five or more words long. The first word contains the |type| and + |subtype| and |link| fields that are already so familiar to us; the second + contains the attribute list pointer, and the third, fourth an fifth words are + called the noad's |nucleus|, |subscr|, and |supscr| fields. (This use of a + combined attribute list is temporary. Eventually, each of fields need their + own list) + + Consider, for example, the simple formula `\.{\$x\^2\$}', which would be + parsed into an mlist containing a single element called an |ord_noad|. The + |nucleus| of this noad is a representation of `\.x', the |subscr| is empty, + and the |supscr| is a representation of `\.2'. + + The |nucleus|, |subscr|, and |supscr| fields are further broken into + subfields. If |p| points to a noad, and if |q| is one of its principal fields + (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the + corresponding attribute of noad |p| is not present). Otherwise, there are + several possibilities for the subfields, depending on the |type| of |q|. + + \startitemize + + \startitem + |type(q)=math_char_node| means that |math_fam(q)| refers to one of + the sixteen font families, and |character(q)| is the number of a + character within a font of that family, as in a character node. + \stopitem + + \startitem + |type(q)=math_text_char_node| is similar, but the character is + unsubscripted and unsuperscripted and it is followed immediately by + another character from the same font. (This |type| setting appears + only briefly during the processing; it is used to suppress unwanted + italic corrections.) + \stopitem + + \startitem + |type(q)=sub_box_node| means that |math_list(q)| points to a box node + (either an |hlist_node| or a |vlist_node|) that should be used as the + value of the field. The |shift_amount| in the subsidiary box node is + the amount by which that box will be shifted downward. + \stopitem + + \startitem + |type(q)=sub_mlist_node| means that |math_list(q)| points to an + mlist; the mlist must be converted to an hlist in order to obtain the + value of this field. + \stopitem + + \startitem + In the latter case, we might have |math_list(q)=null|. This is not + the same as |q=null|; for example, `\.{\$P\_\{\}\$}' and `\.{\$P\$}' + produce different results (the former will not have the ``italic + correction'' added to the width of |P|, but the ``script skip'' will + be added). + \stopitem + + \startitemize + +*/ + +static void unsave_math(void) +{ + unsave(); + decr(save_ptr); + flush_node_list(text_dir_ptr); + assert(saved_type(0) == saved_textdir); + text_dir_ptr = saved_value(0); +} + +/*tex + + Sometimes it is necessary to destroy an mlist. The following subroutine + empties the current list, assuming that |abs(mode)=mmode|. + +*/ + +void flush_math(void) +{ + flush_node_list(vlink(head)); + flush_node_list(incompleat_noad_par); + vlink(head) = null; + tail = head; + incompleat_noad_par = null; +} + +/*tex Before we can do anything in math mode, we need fonts. */ + +#define MATHFONTSTACK 8 +#define MATHFONTDEFAULT 0 + +static sa_tree math_fam_head = NULL; + +int fam_fnt(int fam_id, int size_id) +{ + int n = fam_id + (256 * size_id); + return (int) get_sa_item(math_fam_head, n).int_value; +} + +void def_fam_fnt(int fam_id, int size_id, int f, int lvl) +{ + int n = fam_id + (256 * size_id); + sa_tree_item sa_value = { 0 }; + sa_value.int_value = f; + set_sa_item(math_fam_head, n, sa_value, lvl); + fixup_math_parameters(fam_id, size_id, f, lvl); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + tprint("{assigning"); + print_char(' '); + print_cmd_chr(def_family_cmd, size_id); + print_int(fam_id); + print_char('='); + print_font_identifier(fam_fnt(fam_id, size_id)); + print_char('}'); + end_diagnostic(false); + } +} + +static void unsave_math_fam_data(int gl) +{ + sa_stack_item st; + if (math_fam_head->stack == NULL) + return; + while (math_fam_head->stack_ptr > 0 && + abs(math_fam_head->stack[math_fam_head->stack_ptr].level) + >= (int) gl) { + st = math_fam_head->stack[math_fam_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(math_fam_head, st.code, st.value); + /*tex Now do a trace message, if requested. */ + if (tracing_restores_par > 1) { + int size_id = st.code / 256; + int fam_id = st.code % 256; + begin_diagnostic(); + tprint("{restoring"); + print_char(' '); + print_cmd_chr(def_family_cmd, size_id); + print_int(fam_id); + print_char('='); + print_font_identifier(fam_fnt(fam_id, size_id)); + print_char('}'); + end_diagnostic(false); + } + } + (math_fam_head->stack_ptr)--; + } +} + +/*tex Parameters */ + +#define MATHPARAMSTACK 8 +#define MATHPARAMDEFAULT undefined_math_parameter + +static sa_tree math_param_head = NULL; + +void def_math_param(int param_id, int style_id, scaled value, int lvl) +{ + int n = param_id + (256 * style_id); + sa_tree_item sa_value = { 0 }; + sa_value.int_value = (int) value; + set_sa_item(math_param_head, n, sa_value, lvl); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + tprint("{assigning"); + print_char(' '); + print_cmd_chr(set_math_param_cmd, param_id); + print_cmd_chr(math_style_cmd, style_id); + print_char('='); + print_int(value); + print_char('}'); + end_diagnostic(false); + } +} + +scaled get_math_param(int param_id, int style_id) +{ + int n = param_id + (256 * style_id); + return (scaled) get_sa_item(math_param_head, n).int_value; +} + +static void unsave_math_param_data(int gl) +{ + sa_stack_item st; + if (math_param_head->stack == NULL) + return; + while (math_param_head->stack_ptr > 0 && + abs(math_param_head->stack[math_param_head->stack_ptr].level) + >= (int) gl) { + st = math_param_head->stack[math_param_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(math_param_head, st.code, st.value); + /*tex Do a trace message, if requested. */ + if (tracing_restores_par > 1) { + int param_id = st.code % 256; + int style_id = st.code / 256; + begin_diagnostic(); + tprint("{restoring"); + print_char(' '); + print_cmd_chr(set_math_param_cmd, param_id); + print_cmd_chr(math_style_cmd, style_id); + print_char('='); + print_int(get_math_param(param_id, style_id)); + print_char('}'); + end_diagnostic(false); + } + } + (math_param_head->stack_ptr)--; + } +} + +/*tex Saving and unsaving of both: */ + +void unsave_math_data(int gl) +{ + unsave_math_fam_data(gl); + unsave_math_param_data(gl); +} + +/*tex Dumping and undumping: */ + +void dump_math_data(void) +{ + sa_tree_item sa_value = { 0 }; + if (math_fam_head == NULL) { + sa_value.int_value = MATHFONTDEFAULT; + math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); + } + dump_sa_tree(math_fam_head, "mathfonts"); + if (math_param_head == NULL) { + sa_value.int_value = MATHPARAMDEFAULT; + math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); + } + dump_sa_tree(math_param_head, "mathparameters"); +} + +void undump_math_data(void) +{ + math_fam_head = undump_sa_tree("mathfonts"); + math_param_head = undump_sa_tree("mathparameters"); +} + +void initialize_math(void) +{ + sa_tree_item sa_value = { 0 }; + if (math_fam_head == NULL) { + sa_value.int_value = MATHFONTDEFAULT; + math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); + } + if (math_param_head == NULL) { + sa_value.int_value = MATHPARAMDEFAULT; + math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); + initialize_math_spacing(); + } + return; +} + +/*tex + + Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, Clo, Pun, + or Inn, for purposes of spacing and line breaking. An |ord_noad|, |op_noad|, + |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, |punct_noad|, or + |inner_noad| is used to represent portions of the various types. For example, + an `\.=' sign in a formula leads to the creation of a |rel_noad| whose + |nucleus| field is a representation of an equals sign (usually |fam=0|, + |character=075|). A formula preceded by \.{\\mathrel} also results in a + |rel_noad|. When a |rel_noad| is followed by an |op_noad|, say, and possibly + separated by one or more ordinary nodes (not noads), \TeX\ will insert a + penalty node (with the current |rel_penalty|) just after the formula that + corresponds to the |rel_noad|, unless there already was a penalty immediately + following; and a ``thick space'' will be inserted just before the formula + that corresponds to the |op_noad|. + + A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually has a + |subtype=normal|. The only exception is that an |op_noad| might have + |subtype=limits| or |no_limits|, if the normal positioning of limits has been + overridden for this operator. + + A |radical_noad| also has a |left_delimiter| field, which usually represents + a square root sign. + + A |fraction_noad| has a |right_delimiter| field as well as a + |left_delimiter|. + + Delimiter fields have four subfields called |small_fam|, |small_char|, + |large_fam|, |large_char|. These subfields represent variable-size delimiters + by giving the ``small'' and ``large'' starting characters, as explained in + Chapter~17 of {\sl The \TeX book}. + + A |fraction_noad| is actually quite different from all other noads. It has + |thickness|, |denominator|, and |numerator| fields instead of |nucleus|, + |subscr|, and |supscr|. The |thickness| is a scaled value that tells how + thick to make a fraction rule; however, the special value |default_code| is + used to stand for the |default_rule_thickness| of the current size. The + |numerator| and |denominator| point to mlists that define a fraction; we + always have $$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The + |left_delimiter| and |right_delimiter| fields specify delimiters that will be + placed at the left and right of the fraction. In this way, a |fraction_noad| + is able to represent all of \TeX's operators \.{\\over}, \.{\\atop}, + \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and + \.{\\abovewithdelims}. + + The |new_noad| function creates an |ord_noad| that is completely null + +*/ + +pointer new_noad(void) +{ + pointer p; + p = new_node(simple_noad, ord_noad_type); + /*tex All noad fields are zero after this. */ + return p; +} + +pointer new_sub_box(pointer curbox) +{ + pointer p, q; + p = new_noad(); + q = new_node(sub_box_node, 0); + nucleus(p) = q; + math_list(nucleus(p)) = curbox; + return p; +} + +/*tex + + A few more kinds of noads will complete the set: An |under_noad| has its + nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places + an accent over its nucleus; the accent character appears as + |math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A + |vcenter_noad| centers its nucleus vertically with respect to the axis of the + formula; in such noads we always have |type(nucleus(p))=sub_box|. + + And finally, we have the |fence_noad| type, to implement \TeX's \.{\\left} + and \.{\\right} as well as eTeX's \.{\\middle}. The |nucleus| of such noads + is replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces + a |fence_noad| such that |delimiter(p)| holds the family and character codes + for all left parentheses. A |fence_noad| of subtype |left_noad_side| never + appears in an mlist except as the first element, and a |fence_noad| with + subtype |right_noad_side| never appears in an mlist except as the last + element; furthermore, we either have both a |left_noad_side| and a + |right_noad_side|, or neither one is present. + + Math formulas can also contain instructions like \.{\\textstyle} that + override \TeX's normal style rules. A |style_node| is inserted into the data + structure to record such instructions; it is three words long, so it is + considered a node instead of a noad. The |subtype| is either |display_style| + or |text_style| or |script_style| or |script_script_style|. The second and + third words of a |style_node| are not used, but they are present because a + |choice_node| is converted to a |style_node|. + + \TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles + |display_style|, \dots, |script_script_style|, and adds~1 to get the + ``cramped'' versions of these styles. This gives a numerical order that is + backwards from the convention of Appendix~G in {\sl The \TeX book\/}; i.e., a + smaller style has a larger numerical value. + +*/ + +const char *math_style_names[] = { + "display", "crampeddisplay", + "text", "crampedtext", + "script", "crampedscript", + "scriptscript", "crampedscriptscript", + NULL +}; + +const char *math_param_names[] = { + "quad", "axis", "operatorsize", + "overbarkern", "overbarrule", "overbarvgap", + "underbarkern", "underbarrule", "underbarvgap", + "radicalkern", "radicalrule", "radicalvgap", + "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise", + "stackvgap", "stacknumup", "stackdenomdown", + "fractionrule", "fractionnumvgap", "fractionnumup", + "fractiondenomvgap", "fractiondenomdown", "fractiondelsize", + "skewedfractionhgap", "skewedfractionvgap", + "limitabovevgap", "limitabovebgap", "limitabovekern", + "limitbelowvgap", "limitbelowbgap", "limitbelowkern", + "nolimitsubfactor", "nolimitsupfactor", /* bonus */ + "underdelimitervgap", "underdelimiterbgap", + "overdelimitervgap", "overdelimiterbgap", + "subshiftdrop", "supshiftdrop", "subshiftdown", + "subsupshiftdown", "subtopmax", "supshiftup", + "supbottommin", "supsubbottommax", "subsupvgap", + "spaceafterscript", "connectoroverlapmin", + "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing", + "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing", + "opordspacing", "opopspacing", "opbinspacing", "oprelspacing", + "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing", + "binordspacing", "binopspacing", "binbinspacing", "binrelspacing", + "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing", + "relordspacing", "relopspacing", "relbinspacing", "relrelspacing", + "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing", + "openordspacing", "openopspacing", "openbinspacing", "openrelspacing", + "openopenspacing", "openclosespacing", "openpunctspacing", + "openinnerspacing", + "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing", + "closeopenspacing", "closeclosespacing", "closepunctspacing", + "closeinnerspacing", + "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing", + "punctopenspacing", "punctclosespacing", "punctpunctspacing", + "punctinnerspacing", + "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing", + "inneropenspacing", "innerclosespacing", "innerpunctspacing", + "innerinnerspacing", + NULL +}; + +pointer new_style(small_number s) +{ + m_style = s; + return new_node(style_node, s); +} + +/*tex + + Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which has + special subfields |display_mlist|, |text_mlist|, |script_mlist|, and + |script_script_mlist| pointing to the mlists for each style. + +*/ + +static pointer new_choice(void) +{ + return new_node(choice_node, 0); +} + +/*tex + + Let's consider now the previously unwritten part of |show_node_list| that + displays the things that can only be present in mlists; this program + illustrates how to access the data structures just defined. + + In the context of the following program, |p| points to a node or noad that + should be displayed, and the current string contains the ``recursion + history'' that leads to this point. The recursion history consists of a dot + for each outer level in which |p| is subsidiary to some node, or in which |p| + is subsidiary to the |nucleus| field of some noad; the dot is replaced by + `\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr| or + |supscr| or |denominator| or |numerator| fields of noads. For example, the + current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for |x| + in the (ridiculous) formula `\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over + x+y\}\}\}\}\$}'. + +*/ + +void display_normal_noad(pointer p); +void display_fence_noad(pointer p); +void display_fraction_noad(pointer p); + +void show_math_node(pointer p) +{ + switch (type(p)) { + case style_node: + print_cmd_chr(math_style_cmd, subtype(p)); + break; + case choice_node: + tprint_esc("mathchoice"); + append_char('D'); + show_node_list(display_mlist(p)); + flush_char(); + append_char('T'); + show_node_list(text_mlist(p)); + flush_char(); + append_char('S'); + show_node_list(script_mlist(p)); + flush_char(); + append_char('s'); + show_node_list(script_script_mlist(p)); + flush_char(); + break; + case simple_noad: + case radical_noad: + case accent_noad: + display_normal_noad(p); + break; + case fence_noad: + display_fence_noad(p); + break; + case fraction_noad: + display_fraction_noad(p); + break; + default: + tprint("Unknown node type!"); + break; + } +} + +/*tex Here are some simple routines used in the display of noads. */ + +static void print_fam_and_char(pointer p) +{ + tprint_esc("fam"); + print_int(math_fam(p)); + print_char(' '); + print(math_character(p)); +} + +static void print_delimiter(pointer p) +{ + int a; + if (delimiteroptionset(p)) { + tprint(" [ "); + if (delimiteraxis(p)) + tprint("axis "); + if (delimiternoaxis(p)) + tprint("noaxis "); + if (delimiterexact(p)) + tprint("exact "); + tprint("]"); + } + if (delimiterheight(p)) { + tprint("height="); + print_scaled(delimiterheight(p)); + tprint(" "); + } + if (delimiterdepth(p)) { + tprint("depth="); + print_scaled(delimiterdepth(p)); + tprint(" "); + } + if (delimiterclass(p)) { + tprint("class="); + print_int(delimiterclass(p)); + tprint(" "); + } + if (small_fam(p) < 0) { + /*tex This should never happen. */ + print_int(-1); + } else if (small_fam(p) < 16 && large_fam(p) < 16 && small_char(p) < 256 && large_char(p) < 256) { + /*tex Traditional tex style. */ + a = small_fam(p) * 256 + small_char(p); + a = a * 0x1000 + large_fam(p) * 256 + large_char(p); + print_qhex(a); + } else if ((large_fam(p) == 0 && large_char(p) == 0) || small_char(p) > 65535 || large_char(p) > 65535) { + /*tex \LUATEX\ style. */ + print_qhex(small_fam(p)); + print_qhex(small_char(p)); + } +} + +/*tex + + The next subroutine will descend to another level of recursion when a + subsidiary mlist needs to be displayed. The parameter |c| indicates what + character is to become part of the recursion history. An empty mlist is + distinguished from a missing field, because these are not equivalent (as + explained above). + +*/ + +static void print_subsidiary_data(pointer p, ASCII_code c) +{ + if ((int) cur_length >= depth_threshold) { + if (p != null) + tprint(" []"); + } else { + /*tex Include |c| in the recursion history. */ + append_char(c); + if (p != null) { + switch (type(p)) { + case math_char_node: + print_ln(); + print_current_string(); + print_fam_and_char(p); + break; + case sub_box_node: + show_node_list(math_list(p)); + break; + case sub_mlist_node: + if (math_list(p) == null) { + print_ln(); + print_current_string(); + tprint("{}"); + } else { + show_node_list(math_list(p)); + } + break; + } + } + /*tex Remove |c| from the recursion history. */ + flush_char(); + } +} + +void display_normal_noad(pointer p) +{ + switch (type(p)) { + case simple_noad: + switch (subtype(p)) { + case ord_noad_type: + tprint_esc("mathord"); + break; + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + tprint_esc("mathop"); + if (subtype(p) == op_noad_type_limits) + tprint_esc("limits"); + else if (subtype(p) == op_noad_type_no_limits) + tprint_esc("nolimits"); + break; + case bin_noad_type: + tprint_esc("mathbin"); + break; + case rel_noad_type: + tprint_esc("mathrel"); + break; + case open_noad_type: + tprint_esc("mathopen"); + break; + case close_noad_type: + tprint_esc("mathclose"); + break; + case punct_noad_type: + tprint_esc("mathpunct"); + break; + case inner_noad_type: + tprint_esc("mathinner"); + break; + case over_noad_type: + tprint_esc("overline"); + break; + case under_noad_type: + tprint_esc("underline"); + break; + case vcenter_noad_type: + tprint_esc("vcenter"); + break; + default: + tprint("<unknown noad type!>"); + break; + } + break; + case radical_noad: + if (subtype(p) == 7) + tprint_esc("Uhextensible"); + else if (subtype(p) == 6) + tprint_esc("Udelimiterover"); + else if (subtype(p) == 5) + tprint_esc("Udelimiterunder"); + else if (subtype(p) == 4) + tprint_esc("Uoverdelimiter"); + else if (subtype(p) == 3) + tprint_esc("Uunderdelimiter"); + else if (subtype(p) == 2) + tprint_esc("Uroot"); + else + tprint_esc("radical"); + print_delimiter(left_delimiter(p)); + if (degree(p) != null) { + print_subsidiary_data(degree(p), '/'); + } + if (radicalwidth(p)) { + tprint("width="); + print_scaled(radicalwidth(p)); + tprint(" "); + } + if (radicaloptionset(p)) { + tprint(" [ "); + if (radicalexact(p)) + tprint("exact "); + if (radicalleft(p)) + tprint("left "); + if (radicalmiddle(p)) + tprint("middle "); + if (radicalright(p)) + tprint("right "); + tprint("]"); + } + break; + case accent_noad: + if (top_accent_chr(p) != null) { + if (bot_accent_chr(p) != null) { + tprint_esc("Umathaccent both"); + } else { + tprint_esc("Umathaccent"); + } + } else if (bot_accent_chr(p) != null) { + tprint_esc("Umathaccent bottom"); + } else { + tprint_esc("Umathaccent overlay"); + } + if (accentfraction(p)) { + tprint(" fraction="); + print_int(accentfraction(p)); + tprint(" "); + } + switch (subtype(p)) { + case 0: + if (top_accent_chr(p) != null) { + if (bot_accent_chr(p) != null) { + print_fam_and_char(top_accent_chr(p)); + print_fam_and_char(bot_accent_chr(p)); + } else { + print_fam_and_char(top_accent_chr(p)); + } + } else if (bot_accent_chr(p) != null) { + print_fam_and_char(bot_accent_chr(p)); + } else { + print_fam_and_char(overlay_accent_chr(p)); + } + break; + case 1: + if (top_accent_chr(p) != null) { + tprint(" fixed "); + print_fam_and_char(top_accent_chr(p)); + if (bot_accent_chr(p) != null) { + print_fam_and_char(bot_accent_chr(p)); + } + } else { + confusion("display_accent_noad"); + } + break; + case 2: + if (bot_accent_chr(p) != null) { + if (top_accent_chr(p) != null) { + print_fam_and_char(top_accent_chr(p)); + } + tprint(" fixed "); + print_fam_and_char(bot_accent_chr(p)); + } else{ + confusion("display_accent_noad"); + } + break; + case 3: + if (top_accent_chr(p) != null && bot_accent_chr(p) != null) { + tprint(" fixed "); + print_fam_and_char(top_accent_chr(p)); + tprint(" fixed "); + print_fam_and_char(bot_accent_chr(p)); + } else { + confusion("display_accent_noad"); + } + break; + } + break; + } + print_subsidiary_data(nucleus(p), '.'); + print_subsidiary_data(supscr(p), '^'); + print_subsidiary_data(subscr(p), '_'); +} + +void display_fence_noad(pointer p) +{ + if (subtype(p) == right_noad_side) + tprint_esc("right"); + else if (subtype(p) == left_noad_side) + tprint_esc("left"); + else + tprint_esc("middle"); + print_delimiter(delimiter(p)); +} + +void display_fraction_noad(pointer p) +{ + tprint_esc("fraction, thickness "); + if (thickness(p) == default_code) + tprint("= default"); + else + print_scaled(thickness(p)); + if ((left_delimiter(p) != null) && + ((small_fam(left_delimiter(p)) != 0) || (small_char(left_delimiter(p)) != 0) || + (large_fam(left_delimiter(p)) != 0) || (large_char(left_delimiter(p)) != 0))) { + tprint(", left-delimiter "); + print_delimiter(left_delimiter(p)); + } + if ((right_delimiter(p) != null) && + ((small_fam(right_delimiter(p)) != 0) || (small_char(right_delimiter(p)) != 0) || + (large_fam(right_delimiter(p)) != 0) || (large_char(right_delimiter(p)) != 0))) { + tprint(", right-delimiter "); + print_delimiter(right_delimiter(p)); + } + print_subsidiary_data(numerator(p), '\\'); + print_subsidiary_data(denominator(p), '/'); +} + +/*tex + + The routines that \TeX\ uses to create mlists are similar to those we have + just seen for the generation of hlists and vlists. But it is necessary to + make ``noads'' as well as nodes, so the reader should review the discussion + of math mode data structures before trying to make sense out of the following + program. + + Here is a little routine that needs to be done whenever a subformula is about + to be processed. The parameter is a code like |math_group|. + +*/ + +static void new_save_level_math(group_code c) +{ + set_saved_record(0, saved_textdir, 0, text_dir_ptr); + text_dir_ptr = new_dir(math_direction_par); + incr(save_ptr); + new_save_level(c); + eq_word_define(int_base + body_direction_code, math_direction_par); + eq_word_define(int_base + par_direction_code, math_direction_par); + eq_word_define(int_base + text_direction_code, math_direction_par); +} + +static void push_math(group_code c, int mstyle) +{ + if (math_direction_par != text_direction_par) + dir_math_save = true; + push_nest(); + mode = -mmode; + incompleat_noad_par = null; + m_style = mstyle; + new_save_level_math(c); +} + +static void enter_ordinary_math(void) +{ + push_math(math_shift_group, text_style); + eq_word_define(int_base + cur_fam_code, -1); + if (every_math_par != null) + begin_token_list(every_math_par, every_math_text); +} + +void enter_display_math(void); + +/*tex + + We get into math mode from horizontal mode when a `\.\$' (i.e., a + |math_shift| character) is scanned. We must check to see whether this `\.\$' + is immediately followed by another, in case display math mode is called for. + +*/ + +void init_math(void) +{ + if (cur_cmd == math_shift_cmd) { + /*tex |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ + get_token(); + if ((cur_cmd == math_shift_cmd) && (mode > 0)) { + enter_display_math(); + } else { + back_input(); + enter_ordinary_math(); + } + } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) { + enter_display_math(); + } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) { + enter_ordinary_math(); + } else { + you_cant(); + } +} + +/*tex + + We get into ordinary math mode from display math mode when `\.{\\eqno}' or + `\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; + the value of |cur_chr| is placed onto |save_stack| for safe keeping. + + When \TeX\ is in display math mode, |cur_group=math_shift_group|, so it is + not necessary for the |start_eq_no| procedure to test for this condition. + +*/ + +void start_eq_no(void) +{ + set_saved_record(0, saved_eqno, 0, cur_chr); + incr(save_ptr); + enter_ordinary_math(); +} + +/*tex + + Subformulas of math formulas cause a new level of math mode to be entered, on + the semantic nest as well as the save stack. These subformulas arise in + several ways: (1)~A left brace by itself indicates the beginning of a + subformula that will be put into a box, thereby freezing its glue and + preventing line breaks. (2)~A subscript or superscript is treated as a + subformula if it is not a single character; the same applies to the nucleus + of things like \.{\\underline}. (3)~The \.{\\left} primitive initiates a + subformula that will be terminated by a matching \.{\\right}. The group codes + placed on |save_stack| in these three cases are |math_group|, |math_group|, + and |math_left_group|, respectively. + + Here is the code that handles case (1); the other cases are not quite as + trivial, so we shall consider them later. + +*/ + +void math_left_brace(void) +{ + pointer q; + tail_append(new_noad()); + q = new_node(math_char_node, 0); + nucleus(tail) = q; + back_input(); + (void) scan_math(nucleus(tail), m_style); +} + +/*tex + + If the inline directions of \.{\\pardir} and \.{\\mathdir} are opposite, then + this function will return true. Discovering that fact is somewhat odd because + it needs traversal of the |save_stack|. The occurance of displayed equations + is weird enough that this is probably still better than having yet another + field in the |input_stack| structures. + + None of this makes much sense if the inline direction of either one of + \.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current math + machinery is ill suited anyway so I do not bother to test that. + +*/ + +static boolean math_and_text_reversed_p(void) +{ + int i = save_ptr - 1; + while (save_type(i) != level_boundary) + i--; + while (i < save_ptr) { + if (save_type(i) == restore_old_value && + save_value(i) == int_base + par_direction_code) { + if (textdir_opposite(math_direction_par, save_value(i - 1))) + return true; + } + i++; + } + return false; +} + +/*tex + + When we enter display math mode, we need to call |line_break| to process the + partial paragraph that has just been interrupted by the display. Then we can + set the proper values of |display_width| and |display_indent| and + |pre_display_size|. + +*/ + +void enter_display_math(void) +{ + /*tex new or partial |pre_display_size| */ + scaled w; + /*tex new |display_width| */ + scaled l; + /*tex new |display_indent| */ + scaled s; + pointer p; + /*tex scope of paragraph shape specification */ + int n; + /*tex + + `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' or the 2nd of \.{\$\${ }\$\$} + \.{\$\${ }\$\$} + + */ + if (head == tail || + (vlink(head) == tail && + type(tail) == local_par_node && vlink(tail) == null)) { + if (vlink(head) == tail) { + /*tex + + |resume_after_display| inserts a |local_par_node|, but if there + is another display immediately following, we have to get rid of + that node. + + */ + flush_node(tail); + } + pop_nest(); + w = -max_dimen; + } else { + line_break(true, math_shift_group); + w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par); + } + /*tex + + Now we are in vertical mode, working on the list that will contain the + display. A displayed equation is considered to be three lines long, so we + calculate the length and offset of line number |prev_graf+2|. + + */ + if (par_shape_par_ptr == null) { + if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) { + halfword used_hang_indent = swap_hang_indent(hang_indent_par); + l = hsize_par - abs(used_hang_indent); + if (used_hang_indent > 0) + s = used_hang_indent; + else + s = 0; + } else { + l = hsize_par; + s = 0; + } + } else { + n = vinfo(par_shape_par_ptr + 1); + if (prev_graf_par + 2 >= n) + p = par_shape_par_ptr + 2 * n + 1; + else + p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1; + s = varmem[(p - 1)].cint; + l = varmem[p].cint; + s = swap_parshape_indent(s,l); + } + push_math(math_shift_group, display_style); + mode = mmode; + eq_word_define(int_base + cur_fam_code, -1); + eq_word_define(dimen_base + pre_display_size_code, w); + eq_word_define(dimen_base + display_width_code, l); + eq_word_define(dimen_base + display_indent_code, s); + eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0)); + if (every_display_par != null) + begin_token_list(every_display_par, every_display_text); + if (nest_ptr == 1) { + checked_page_filter(before_display); + build_page(); + } +} + +/*tex + + The next routine parses all variations of a delimiter code. The |extcode| + tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the |doclass| + tells whether or not read a math class also (for \.{\\delimiter} c.s.). (the + class is passed on for conversion to \.{\\mathchar}). + +*/ + +static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) +{ + const char *hlp[] = { + "I'm going to use 0 instead of that illegal code value.", + NULL + }; + delcodeval d; + int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; + if (extcode == tex_mathcode) { + /*tex + + \.{\\delcode}, this is the easiest + + */ + scan_int(); + /*tex "MFCCFCC or "FCCFCC */ + if (doclass) { + mcls = (cur_val / 0x1000000); + cur_val = (cur_val & 0xFFFFFF); + } + if (cur_val > 0xFFFFFF) { + tex_error("Invalid delimiter code", hlp); + cur_val = 0; + } + msfam = (cur_val / 0x100000); + mschr = (cur_val % 0x100000) / 0x1000; + mlfam = (cur_val & 0xFFF) / 0x100; + mlchr = (cur_val % 0x100); + } else if (extcode == umath_mathcode) { + /*tex + + \.{\\Udelcode}: <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> + + */ + if (doclass) { + scan_int(); + mcls = cur_val; + } + scan_int(); + msfam = cur_val; + scan_char_num(); + mschr = cur_val; + if (msfam < 0 || msfam > 255) { + tex_error("Invalid delimiter code", hlp); + msfam = 0; + mschr = 0; + } + mlfam = 0; + mlchr = 0; + } else if (extcode == umathnum_mathcode) { + /*tex + + \.{\\Udelcodenum}:"FF<21bits>; the largest numeric value is $2^29-1$, + but the top of bit 21 can't be used as it contains invalid USV's. + + */ + if (doclass) { /* such a primitive doesn't exist */ + confusion("umathnum_mathcode"); + } + scan_int(); + msfam = (cur_val / 0x200000); + mschr = cur_val & 0x1FFFFF; + if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) { + tex_error("Invalid delimiter code", hlp); + msfam = 0; + mschr = 0; + } + mlfam = 0; + mlchr = 0; + } else { + /*tex Something's gone wrong! */ + confusion("unknown_extcode"); + } + d.class_value = mcls; + d.small_family_value = msfam; + d.small_character_value = mschr; + d.large_family_value = mlfam; + d.large_character_value = mlchr; + return d; +} + +void scan_extdef_del_code(int level, int extcode) +{ + delcodeval d; + int p; + scan_char_num(); + p = cur_val; + scan_optional_equals(); + d = do_scan_extdef_del_code(extcode, false); + set_del_code(p, d.small_family_value, d.small_character_value, + d.large_family_value, d.large_character_value, (quarterword) (level)); +} + +mathcodeval scan_mathchar(int extcode) +{ + char errstr[255] = { 0 }; + const char *hlp[] = { + "I'm going to use 0 instead of that illegal code value.", + NULL + }; + mathcodeval d; + int mcls = 0, mfam = 0, mchr = 0; + if (extcode == tex_mathcode) { + /*tex \.{\\mathcode}: "TFCC */ + scan_int(); + if (cur_val > 0x8000) { + /*tex Needed for latex: fallback to umathnum_mathcode. */ + mfam = (cur_val / 0x200000) & 0x7FF; + mcls = mfam % 0x08; + mfam = mfam / 0x08; + mchr = cur_val & 0x1FFFFF; + if (mchr > 0x10FFFF) { + tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp); + mcls = 0; + mfam = 0; + mchr = 0; + } + } else { + if (cur_val < 0) { + snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val); + tex_error(errstr, hlp); + cur_val = 0; + } + mcls = (cur_val / 0x1000); + mfam = ((cur_val % 0x1000) / 0x100); + mchr = (cur_val % 0x100); + } + } else if (extcode == umath_mathcode) { + /*tex <0-0x7> <0-0xFF> <0-0x10FFFF> */ + scan_int(); + mcls = cur_val; + scan_int(); + mfam = cur_val; + scan_char_num(); + mchr = cur_val; + if (mcls < 0 || mcls > 7 || mfam > 255) { + tex_error("Invalid math code", hlp); + mchr = 0; + mfam = 0; + mcls = 0; + } + } else if (extcode == umathnum_mathcode) { + /*tex + + "FFT<21bits>: the largest numeric value is $2^32-1$, but the top of + bit 21 can't be used as it contains invalid USV's + + Note: |scan_int| won't accept families 128-255 because these use bit 32 + + */ + scan_int(); + mfam = (cur_val / 0x200000) & 0x7FF; + mcls = mfam % 0x08; + mfam = mfam / 0x08; + mchr = cur_val & 0x1FFFFF; + if (mchr > 0x10FFFF) { + tex_error("Invalid math code", hlp); + mcls = 0; + mfam = 0; + mchr = 0; + } + } else { + /*tex Something's gone wrong. */ + confusion("unknown_extcode"); + } + d.class_value = mcls; + d.family_value = mfam; + d.character_value = mchr; + return d; +} + +void scan_extdef_math_code(int level, int extcode) +{ + mathcodeval d; + int p; + scan_char_num(); + p = cur_val; + scan_optional_equals(); + d = scan_mathchar(extcode); + set_math_code(p, d.class_value, d.family_value, d.character_value, (quarterword) (level)); +} + +/*tex This reads in a delcode when actually a mathcode is needed. */ + +mathcodeval scan_delimiter_as_mathchar(int extcode) +{ + delcodeval dval; + mathcodeval mval; + dval = do_scan_extdef_del_code(extcode, true); + mval.class_value = dval.class_value; + mval.family_value = dval.small_family_value; + mval.character_value = dval.small_character_value; + return mval; +} + +/*tex + + Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are broken + down into subfields called |type| and either |math_list| or + |(math_fam,math_character)|. The job of |scan_math| is to figure out what to + place in one of these principal fields; it looks at the subformula that comes + next in the input, and places an encoding of that subformula into a given + word of |mem|. + +*/ + +#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) + +int scan_math_style(pointer p, int mstyle) +{ + get_next_nb_nr(); + back_input(); + scan_left_brace(); + set_saved_record(0, saved_math, 0, p); + incr(save_ptr); + push_math(math_group, mstyle); + return 1; +} + +int scan_math(pointer p, int mstyle) +{ + mathcodeval mval = { 0, 0, 0 }; + assert(p != null); + RESTART: + get_next_nb_nr(); + RESWITCH: + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + case char_given_cmd: + mval = get_math_code(cur_chr); + if (mval.class_value == 8) { + /*tex An active character that is an |outer_call| is allowed here. */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + goto RESTART; + } + break; + case char_num_cmd: + scan_char_num(); + cur_chr = cur_val; + cur_cmd = char_given_cmd; + goto RESWITCH; + break; + case math_char_num_cmd: + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(umath_mathcode); + else if (cur_chr == 2) + mval = scan_mathchar(umathnum_mathcode); + else + confusion("scan_math"); + break; + case math_given_cmd: + mval = mathchar_from_integer(cur_chr, tex_mathcode); + break; + case xmath_given_cmd: + mval = mathchar_from_integer(cur_chr, umath_mathcode); + break; + case delim_num_cmd: + if (cur_chr == 0) + mval = scan_delimiter_as_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_delimiter_as_mathchar(umath_mathcode); + else + confusion("scan_math"); + break; + default: + /*tex + + The pointer |p| is placed on |save_stack| while a complex subformula + is being scanned. + + */ + back_input(); + scan_left_brace(); + set_saved_record(0, saved_math, 0, p); + incr(save_ptr); + push_math(math_group, mstyle); + return 1; + } + type(p) = math_char_node; + math_character(p) = mval.character_value; + if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(p) = cur_fam_par; + else + math_fam(p) = mval.family_value; + return 0; +} + +/*tex + + The |set_math_char| procedure creates a new noad appropriate to a given math + code, and appends it to the current mlist. However, if the math code is + sufficiently large, the |cur_chr| is treated as an active character and + nothing is appended. + +*/ + +#define math_class_to_type(target,source) \ + switch (source) { \ + case 0: target = ord_noad_type; break; \ + case 1: target = op_noad_type_normal; break; \ + case 2: target = bin_noad_type; break; \ + case 3: target = rel_noad_type; break; \ + case 4: target = open_noad_type; break; \ + case 5: target = close_noad_type; break; \ + case 6: target = punct_noad_type; break; \ + } + +void set_math_char(mathcodeval mval) +{ + pointer p; + if (mval.class_value == 8) { + /*tex An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + } else { + pointer q; + p = new_noad(); + q = new_node(math_char_node, 0); + nucleus(p) = q; + math_character(nucleus(p)) = mval.character_value; + math_fam(nucleus(p)) = mval.family_value; + if (mval.class_value == math_use_current_family_code) { + if (cur_fam_par_in_range) + math_fam(nucleus(p)) = cur_fam_par; + subtype(p) = ord_noad_type; + } else { + math_class_to_type(subtype(p),mval.class_value); + } + tail_append(p); + } +} + +/*tex + + The |math_char_in_text| procedure creates a new node representing a math char + in text code, and appends it to the current list. However, if the math code + is sufficiently large, the |cur_chr| is treated as an active character and + nothing is appended. + +*/ + +void math_char_in_text(mathcodeval mval) +{ + pointer p; + if (mval.class_value == 8) { + /*tex An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + } else { + p = new_char(fam_fnt(mval.family_value, text_size), mval.character_value); + tail_append(p); + } +} + +void math_math_comp(void) +{ + pointer q; + tail_append(new_noad()); + subtype(tail) = (quarterword) cur_chr; + q = new_node(math_char_node, 0); + nucleus(tail) = q; + if (cur_chr == over_noad_type) + (void) scan_math(nucleus(tail), cramped_style(m_style)); + else + (void) scan_math(nucleus(tail), m_style); +} + +void math_limit_switch(void) +{ + const char *hlp[] = { + "I'm ignoring this misplaced \\limits or \\nolimits command.", + NULL + }; + if (head != tail) { + if (type(tail) == simple_noad && + (subtype(tail) == op_noad_type_normal || + subtype(tail) == op_noad_type_limits || + subtype(tail) == op_noad_type_no_limits)) { + subtype(tail) = (quarterword) cur_chr; + return; + } + } + tex_error("Limit controls must follow a math operator", hlp); +} + +/*tex + + Delimiter fields of noads are filled in by the |scan_delimiter| routine. The + first parameter of this procedure is the |mem| address where the delimiter + is to be placed; the second tells if this delimiter follows \.{\\radical} or + not. + +*/ + +static void scan_delimiter(pointer p, int r) +{ + delcodeval dval = { 0, 0, 0, 0, 0 }; + if (r == tex_mathcode) { + /*tex \.{\\radical} */ + dval = do_scan_extdef_del_code(tex_mathcode, true); + } else if (r == umath_mathcode) { + /*tex \.{\\Uradical} */ + dval = do_scan_extdef_del_code(umath_mathcode, false); + } else if (r == no_mathcode) { + get_next_nb_nr(); + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + dval = get_del_code(cur_chr); + break; + case delim_num_cmd: + if (cur_chr == 0) { + /*tex \.{\\delimiter} */ + dval = do_scan_extdef_del_code(tex_mathcode, true); + } else if (cur_chr == 1) { + /*tex \.{\\Udelimiter} */ + dval = do_scan_extdef_del_code(umath_mathcode, true); + } else { + confusion("scan_delimiter1"); + } + break; + default: + dval.small_family_value = -1; + break; + } + } else { + confusion("scan_delimiter2"); + } + if (p == null) + return; + if (dval.small_family_value < 0) { + const char *hlp[] = { + "I was expecting to see something like `(' or `\\{' or", + "`\\}' here. If you typed, e.g., `{' instead of `\\{', you", + "should probably delete the `{' by typing `1' now, so that", + "braces don't get unbalanced. Otherwise just proceed", + "Acceptable delimiters are characters whose \\delcode is", + "nonnegative, or you can use `\\delimiter <delimiter code>'.", + NULL + }; + back_error("Missing delimiter (. inserted)", hlp); + small_fam(p) = 0; + small_char(p) = 0; + large_fam(p) = 0; + large_char(p) = 0; + } else { + small_fam(p) = dval.small_family_value; + small_char(p) = dval.small_character_value; + large_fam(p) = dval.large_family_value; + large_char(p) = dval.large_character_value; + } + return; +} + +void math_radical(void) +{ + halfword q; + int chr_code = cur_chr; + halfword options = 0; + tail_append(new_node(radical_noad, chr_code)); + q = new_node(delim_node, 0); + left_delimiter(tail) = q; + while (1) { + if (scan_keyword("width")) { + scan_dimen(false,false,false); + radicalwidth(tail) = cur_val ; + } else if (scan_keyword("left")) { + options = options | noad_option_left ; + } else if (scan_keyword("middle")) { + options = options | noad_option_middle ; + } else if (scan_keyword("right")) { + options = options | noad_option_right ; + } else { + break; + } + } + radicaloptions(tail) = options; + if (chr_code == 0) + /*tex \.{\\radical} */ + scan_delimiter(left_delimiter(tail), tex_mathcode); + else if (chr_code == 1) + /*tex \.{\\Uradical} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 2) + /*tex \.{\\Uroot} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 3) + /*tex \.{\\Uunderdelimiter} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 4) + /*tex \.{\\Uoverdelimiter} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 5) + /*tex \.{\\Udelimiterunder} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 6) + /*tex \.{\\Udelimiterover} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 7) + /*tex \.{\\Uhextensible} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else + confusion("math_radical"); + if (chr_code == 7) { + /*tex type will change */ + q = new_node(sub_box_node, 0); + nucleus(tail) = q; + return; + } else if (chr_code == 2) { + /*tex + + The trick with the |vlink(q)| is used by |scan_math| to decide + whether it needs to go on. + + */ + q = new_node(math_char_node, 0); + vlink(q) = tail; + degree(tail) = q; + if (!scan_math(degree(tail), sup_sup_style(m_style))) { + vlink(degree(tail)) = null; + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); + } + } else { + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); + } +} + +void math_ac(void) +{ + halfword q; + mathcodeval t = { 0, 0, 0 }; + mathcodeval b = { 0, 0, 0 }; + mathcodeval o = { 0, 0, 0 }; + if (cur_cmd == accent_cmd) { + const char *hlp[] = { + "I'm changing \\accent to \\mathaccent here; wish me luck.", + "(Accents are not the same in formulas as they are in text.)", + NULL + }; + tex_error("Please use \\mathaccent for accents in math mode", hlp); + } + tail_append(new_node(accent_noad, 0)); + if (cur_chr == 0) { + /*tex \.{\\mathaccent} */ + t = scan_mathchar(tex_mathcode); + } else if (cur_chr == 1) { + /*tex \.{\\Umathaccent} */ + if (scan_keyword("fixed")) { + /*tex top */ + subtype(tail) = 1; + t = scan_mathchar(umath_mathcode); + } else if (scan_keyword("both")) { + /*tex top bottom */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + t = scan_mathchar(umath_mathcode); + if (scan_keyword("fixed")) { + subtype(tail) += 2; + } + b = scan_mathchar(umath_mathcode); + } else if (scan_keyword("bottom")) { + /*tex bottom */ + if (scan_keyword("fixed")) { + subtype(tail) = 2; + } + b = scan_mathchar(umath_mathcode); + } else if (scan_keyword("top")) { + /*tex top */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + t = scan_mathchar(umath_mathcode); + } else if (scan_keyword("overlay")) { + /* overlay */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + o = scan_mathchar(umath_mathcode); + } else { + /*tex top */ + t = scan_mathchar(umath_mathcode); + } + if (scan_keyword("fraction")) { + scan_int(); + accentfraction(tail) = cur_val; + } + } else { + confusion("mathaccent"); + } + if (!(t.character_value == 0 && t.family_value == 0)) { + q = new_node(math_char_node, 0); + top_accent_chr(tail) = q; + math_character(top_accent_chr(tail)) = t.character_value; + if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(top_accent_chr(tail)) = cur_fam_par; + else + math_fam(top_accent_chr(tail)) = t.family_value; + } + if (!(b.character_value == 0 && b.family_value == 0)) { + q = new_node(math_char_node, 0); + bot_accent_chr(tail) = q; + math_character(bot_accent_chr(tail)) = b.character_value; + if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(bot_accent_chr(tail)) = cur_fam_par; + else + math_fam(bot_accent_chr(tail)) = b.family_value; + } + if (!(o.character_value == 0 && o.family_value == 0)) { + q = new_node(math_char_node, 0); + overlay_accent_chr(tail) = q; + math_character(overlay_accent_chr(tail)) = o.character_value; + if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(overlay_accent_chr(tail)) = cur_fam_par; + else + math_fam(overlay_accent_chr(tail)) = o.family_value; + } + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); +} + +pointer math_vcenter_group(pointer p) +{ + pointer q, r; + q = new_noad(); + subtype(q) = vcenter_noad_type; + r = new_node(sub_box_node, 0); + nucleus(q) = r; + math_list(nucleus(q)) = p; + return q; +} + +/*tex + + The routine that scans the four mlists of a \.{\\mathchoice} is very much + like the routine that builds discretionary nodes. + +*/ + +void append_choices(void) +{ + tail_append(new_choice()); + incr(save_ptr); + set_saved_record(-1, saved_choices, 0, 0); + push_math(math_choice_group, display_style); + scan_left_brace(); +} + +void build_choices(void) +{ + pointer p; + int prev_style; + prev_style = m_style; + unsave_math(); + p = fin_mlist(null); + assert(saved_type(-1) == saved_choices); + switch (saved_value(-1)) { + case 0: + display_mlist(tail) = p; + break; + case 1: + text_mlist(tail) = p; + break; + case 2: + script_mlist(tail) = p; + break; + case 3: + script_script_mlist(tail) = p; + decr(save_ptr); + return; + break; + } + set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1)); + push_math(math_choice_group, (prev_style + 2)); + scan_left_brace(); +} + +/*tex + + Subscripts and superscripts are attached to the previous nucleus by the + action procedure called |sub_sup|. + +*/ + +static void do_sub_sup(int no) +{ + pointer q; + if (tail == head || (!scripts_allowed(tail))) { + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + } + if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { + /*tex |super_sub_script| */ + if (supscr(tail) != null) { + const char *hlp[] = { + "I treat `x^1^2' essentially like `x^1{}^2'.", NULL + }; + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + tex_error("Double superscript", hlp); + } + if (no) { + noadoptions(tail) = noadoptions(tail) | noad_option_no_super_script ; + } + q = new_node(math_char_node, 0); + supscr(tail) = q; + (void) scan_math(supscr(tail), sup_style(m_style)); + } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) { + if (subscr(tail) != null) { + const char *hlp[] = { + "I treat `x_1_2' essentially like `x_1{}_2'.", NULL + }; + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + tex_error("Double subscript", hlp); + } + if (no) { + noadoptions(tail) = noadoptions(tail) | noad_option_no_sub_script ; + } + q = new_node(math_char_node, 0); + subscr(tail) = q; + (void) scan_math(subscr(tail), sub_style(m_style)); + } +} + +void sub_sup(void) +{ + do_sub_sup(0); +} + +void no_sub_sup(void) +{ + do_sub_sup(1); +} + +/*tex + + An operation like `\.{\\over}' causes the current mlist to go into a state of + suspended animation: |incompleat_noad| points to a |fraction_noad| that + contains the mlist-so-far as its numerator, while the denominator is yet to + come. Finally when the mlist is finished, the denominator will go into the + incompleat fraction noad, and that noad will become the whole formula, unless + it is surrounded by `\.{\\left}' and `\.{\\right}' delimiters. + +*/ + +void math_fraction(void) +{ + /*tex The type of generalized fraction we are scanning: */ + halfword c; + pointer q; + halfword options = 0; + halfword temp_value; + c = cur_chr; + if (incompleat_noad_par != null) { + const char *hlp[] = { + "I'm ignoring this fraction specification, since I don't", + "know whether a construction like `x \\over y \\over z'", + "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.", + NULL + }; + if (c >= delimited_code) { + scan_delimiter(null, no_mathcode); + scan_delimiter(null, no_mathcode); + } + if ((c % delimited_code) == above_code) + scan_normal_dimen(); + tex_error("Ambiguous; you need another { and }", hlp); + } else { + incompleat_noad_par = new_node(fraction_noad, 0); + temp_value = new_node(sub_mlist_node, 0); + numerator(incompleat_noad_par) = temp_value; + math_list(numerator(incompleat_noad_par)) = vlink(head); + vlink(head) = null; + tail = head; + m_style = cramped_style(m_style); + + if ((c % delimited_code) == skewed_code) { + q = new_node(delim_node, 0); + middle_delimiter(incompleat_noad_par) = q; + scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode); + } + if (c >= delimited_code) { + q = new_node(delim_node, 0); + left_delimiter(incompleat_noad_par) = q; + q = new_node(delim_node, 0); + right_delimiter(incompleat_noad_par) = q; + scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode); + scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode); + } + switch (c % delimited_code) { + case above_code: + while (1) { + if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else if (scan_keyword("norule")) { + options = options | noad_option_no_rule ; + } else { + break; + } + } + fractionoptions(incompleat_noad_par) = options; + scan_normal_dimen(); + thickness(incompleat_noad_par) = cur_val; + break; + case over_code: + thickness(incompleat_noad_par) = default_code; + break; + case atop_code: + thickness(incompleat_noad_par) = 0; + break; + case skewed_code: + while (1) { + if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else if (scan_keyword("noaxis")) { + options = options | noad_option_no_axis ; + } else { + break; + } + } + fractionoptions(incompleat_noad_par) = options; + thickness(incompleat_noad_par) = 0; + break; + } + } +} + +/*tex + + At the end of a math formula or subformula, the |fin_mlist| routine is called + upon to return a pointer to the newly completed mlist, and to pop the nest + back to the enclosing semantic level. The parameter to |fin_mlist|, if not + null, points to a |fence_noad| that ends the current mlist; this |fence_noad| + has not yet been appended. + +*/ + +pointer fin_mlist(pointer p) +{ + pointer q; + if (incompleat_noad_par != null) { + if (denominator(incompleat_noad_par) != null) { + type(denominator(incompleat_noad_par)) = sub_mlist_node; + } else { + q = new_node(sub_mlist_node, 0); + denominator(incompleat_noad_par) = q; + } + math_list(denominator(incompleat_noad_par)) = vlink(head); + if (p == null) { + q = incompleat_noad_par; + } else { + q = math_list(numerator(incompleat_noad_par)); + if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) || (delim_par == null)) + confusion("right"); + math_list(numerator(incompleat_noad_par)) = vlink(delim_par); + vlink(delim_par) = incompleat_noad_par; + vlink(incompleat_noad_par) = p; + } + } else { + vlink(tail) = p; + q = vlink(head); + } + pop_nest(); + return q; +} + +/*tex + + Now at last we're ready to see what happens when a right brace occurs in a + math formula. Two special cases are simplified here: Braces are effectively + removed when they surround a single Ord without sub/superscripts, or when + they surround an accent that is the nucleus of an Ord atom. + +*/ + +void close_math_group(pointer p) +{ + int old_style = m_style; + unsave_math(); + decr(save_ptr); + assert(saved_type(0) == saved_math); + type(saved_value(0)) = sub_mlist_node; + p = fin_mlist(null); + math_list(saved_value(0)) = p; + if (p != null && vlink(p) == null) { + if (type(p) == simple_noad) { + if (subscr(p) == null && supscr(p) == null) { + /*tex (subtype(p) == ord_noad_type) */ + int flatten = 0; + int modepar = math_flatten_mode_par; + switch (subtype(p)) { + case ord_noad_type : + flatten = (modepar & 1) == 1; + break; + case bin_noad_type : + flatten = (modepar & 2) == 2; + break; + case rel_noad_type : + flatten = (modepar & 4) == 4; + break; + case punct_noad_type : + flatten = (modepar & 8) == 8; + break; + case inner_noad_type : + flatten = (modepar & 16) == 16; + break; + default: + break; + } + if (flatten) { + type(saved_value(0)) = type(nucleus(p)); + if (type(nucleus(p)) == math_char_node) { + math_fam(saved_value(0)) = math_fam(nucleus(p)); + math_character(saved_value(0)) = + math_character(nucleus(p)); + } else { + math_list(saved_value(0)) = math_list(nucleus(p)); + math_list(nucleus(p)) = null; + } + delete_attribute_ref(node_attr(saved_value(0))); + node_attr(saved_value(0)) = node_attr(nucleus(p)); + node_attr(nucleus(p)) = null; + flush_node(p); + } + } + } else if (type(p) == accent_noad) { + if (saved_value(0) == nucleus(tail) && type(tail) == simple_noad && subtype(tail) == ord_noad_type) { + pointer q = head; + while (vlink(q) != tail) + q = vlink(q); + vlink(q) = p; + nucleus(tail) = null; + subscr(tail) = null; + supscr(tail) = null; + delete_attribute_ref(node_attr(p)); + node_attr(p) = node_attr(tail); + node_attr(tail) = null; + flush_node(tail); + tail = p; + } + } + } + if (vlink(saved_value(0)) > 0) { + pointer q; + q = new_node(math_char_node, 0); + nucleus(vlink(saved_value(0))) = q; + vlink(saved_value(0)) = null; + saved_value(0) = q; + (void) scan_math(saved_value(0), old_style); + /*tex restart */ + } +} + +/*tex + + We have dealt with all constructions of math mode except `\.{\\left}' and + `\.{\\right}', so the picture is completed by the following sections of the + program. The |middle| feature of eTeX allows one ore several \.{\\middle} + delimiters to appear between \.{\\left} and \.{\\right}. + +*/ + +void math_left_right(void) +{ + /*tex |left_noad_side| .. |right_noad_side| */ + halfword t; + /*tex new noad */ + pointer p; + /*tex resulting mlist */ + pointer q; + /*tex temporary */ + pointer r; + halfword ht = 0; + halfword dp = 0; + halfword options = 0; + halfword type = -1 ; + t = cur_chr; + if (t > 10) { + /*tex we have \Uleft \Uright \Umiddle */ + t = t - 10; + while (1) { + if (scan_keyword("height")) { + scan_dimen(false,false,false); + ht = cur_val ; + } else if (scan_keyword("depth")) { + scan_dimen(false,false,false); + dp = cur_val ; + } else if (scan_keyword("axis")) { + options = options | noad_option_axis ; + } else if (scan_keyword("noaxis")) { + options = options | noad_option_no_axis ; + } else if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else if (scan_keyword("class")) { + scan_int(); + math_class_to_type(type,cur_val); + } else { + break; + } + } + } + if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) { + if (cur_group == math_shift_group) { + scan_delimiter(null, no_mathcode); + if (t == middle_noad_side) { + const char *hlp[] = { + "I'm ignoring a \\middle that had no matching \\left.", + NULL + }; + tex_error("Extra \\middle", hlp); + } else { + const char *hlp[] = { + "I'm ignoring a \\right that had no matching \\left.", + NULL + }; + tex_error("Extra \\right", hlp); + } + } else { + off_save(); + } + } else { + p = new_noad(); + type(p) = fence_noad; + subtype(p) = (quarterword) t; + r = new_node(delim_node, 0); + delimiter(p) = r; + delimiterheight(p) = ht; + delimiterdepth(p) = dp; + delimiteroptions(p) = options; + delimiterclass(p) = type; + delimiteritalic(p) = 0; + delimitersamesize(p) = 0; + scan_delimiter(delimiter(p), no_mathcode); + if (t == no_noad_side) { + tail_append(new_noad()); + subtype(tail) = inner_noad_type; + r = new_node(sub_mlist_node, 0); + nucleus(tail) = r; + math_list(nucleus(tail)) = p; + return ; + } + if (t == left_noad_side) { + q = p; + } else { + q = fin_mlist(p); + unsave_math(); + } + if (t != right_noad_side) { + push_math(math_left_group, m_style); + vlink(head) = q; + tail = p; + delim_par = p; + } else { + tail_append(new_noad()); + subtype(tail) = inner_noad_type; + r = new_node(sub_mlist_node, 0); + nucleus(tail) = r; + math_list(nucleus(tail)) = q; + } + } +} + +/*tex + + \TeX\ gets to the following part of the program when the first `\.\$' ending + a display has been scanned. + +*/ + +static void check_second_math_shift(void) +{ + get_x_token(); + if (cur_cmd != math_shift_cmd) { + const char *hlp[] = { + "The `$' that I just saw supposedly matches a previous `$$'.", + "So I shall assume that you typed `$$' both times.", + NULL + }; + back_error("Display math should end with $$", hlp); + } +} + +static void check_display_math_end(void) +{ + if (cur_chr != cramped_display_style) { + const char *hlp[] = { + "I shall assume that you typed that.", + NULL + }; + tex_error("Display math should end with \\Ustopdisplaymath", hlp); + } +} + +static void check_inline_math_end(void) +{ + if (cur_chr != cramped_text_style) { + const char *hlp[] = { + "I shall assume that you typed that.", + NULL + }; + tex_error("Inline math should end with \\Ustopmath", hlp); + } +} + +static void resume_after_display(void) +{ + if (cur_group != math_shift_group) + confusion("display"); + unsave_math(); + prev_graf_par = prev_graf_par + 3; + push_nest(); + mode = hmode; + space_factor_par = 1000; + /*tex This needs to be intercepted in the display math start! */ + tail_append(make_local_par_node(penalty_par_code)); + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + if (nest_ptr == 1) { + normal_page_filter(after_display); + build_page(); + } +} + +/*tex + + The fussiest part of math mode processing occurs when a displayed formula is + being centered and placed with an optional equation number. + + At this time we are in vertical mode (or internal vertical mode). + + \starttabulate + \NC \type {p} \NC points to the mlist for the formula \NC \NR + \NC \type {a} \NC is either |null| or it points to a box containing the equation number \NC \NR + \NC \type {l} \NC is true if there was an \.{\\leqno}/ (so |a| is a horizontal box) \NC \NR + \stoptabulate + +*/ + +#define inject_display_skip_before(g) \ + if (g > 0) { \ + switch (display_skip_mode_par) { \ + case 0 : \ + /*tex normal tex | always */ \ + case 1 : \ + /*tex always */ \ + tail_append(new_param_glue(g)); \ + break; \ + case 2 : \ + /*tex non-zero */ \ + if (! glue_is_zero(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ + case 3: \ + /*tex ignore */ \ + break; \ + default: \ + /*tex > 3 reserved for future use */ \ + tail_append(new_param_glue(g)); \ + break; \ + } \ + } + +#define inject_display_skip_after(g) \ + if (g > 0) { \ + switch (display_skip_mode_par) { \ + case 0 : \ + /*tex normal tex | always */ \ + case 1 : \ + /*tex always */ \ + tail_append(new_param_glue(g)); \ + break; \ + case 2 : \ + /*tex non-zero */ \ + if (! glue_is_zero(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ + case 3: \ + /*tex ignore */ \ + break; \ + default: \ + /*tex > 3 reserved for future use */ \ + tail_append(new_param_glue(g)); \ + break; \ + } \ + } + +static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) +{ + /*tex box containing the equation */ + pointer eq_box; + /*tex width of the equation */ + scaled eq_w; + /*tex width of the line */ + scaled line_w; + /*tex width of equation number */ + scaled eqno_w; + /*tex width of equation number plus space to separate from equation */ + scaled eqno_w2; + /*tex move the line right this much */ + scaled line_s; + /*tex displacement of equation in the line */ + scaled d; + /*tex glue parameter codes for before and after */ + small_number g1, g2; + /*tex kern nodes used to position the display */ + pointer r,s; + /*tex tail of adjustment list */ + pointer t; + /*tex tail of pre-adjustment list */ + pointer pre_t; + /*tex true if the math and surrounding text dirs are opposed */ + boolean swap_dir; + scaled eqno_width; + swap_dir = (pre_display_direction_par < 0 ? true : false ); + if (eqno_box != null && swap_dir) + l = !l; + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + eq_box = hpack(p, 0, additional, -1); + subtype(eq_box) = equation_list; + build_attribute_list(eq_box); + p = list_ptr(eq_box); + t = adjust_tail; + adjust_tail = null; + pre_t = pre_adjust_tail; + pre_adjust_tail = null; + eq_w = width(eq_box); + line_w = display_width_par; + line_s = display_indent_par; + if (eqno_box == null) { + eqno_w = 0; + eqno_width = 0; + eqno_w2 = 0; + } else { + eqno_w = width(eqno_box); + eqno_width = eqno_w; + eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000); + subtype(eqno_box) = equation_number_list; + /*tex build_attribute_list(eqno_box); */ + } + if (eq_w + eqno_w2 > line_w) { + /*tex + + The user can force the equation number to go on a separate line by + causing its width to be zero. + + */ + if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w) + || (total_shrink[sfi] != 0) || (total_shrink[fil] != 0) + || (total_shrink[fill] != 0) || (total_shrink[filll] != 0))) { + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w - eqno_w2, exactly, -1); + subtype(eq_box) = equation_list; + build_attribute_list(eq_box); + } else { + eqno_w = 0; + if (eq_w > line_w) { + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w, exactly, -1); + subtype(eq_box) = equation_list; + build_attribute_list(eq_box); + } + } + eq_w = width(eq_box); + } + /*tex + + We try first to center the display without regard to the existence of the + equation number. If that would make it too close (where ``too close'' + means that the space between display and equation number is less than the + width of the equation number), we either center it in the remaining space + or move it as far from the equation number as possible. The latter + alternative is taken only if the display begins with glue, since we + assume that the user put glue there to control the spacing precisely. + + */ + d = half(line_w - eq_w); + if ((eqno_w > 0) && (d < 2 * eqno_w)) { + /*tex too close */ + d = half(line_w - eq_w - eqno_w); + if (p != null) + if (!is_char_node(p)) + if (type(p) == glue_node) + d = 0; + } + tail_append(new_penalty(pre_display_penalty_par,after_display_penalty)); + if ((d + line_s <= pre_display_size_par) || l) { + /*tex not enough clearance */ + g1 = above_display_skip_code; + g2 = below_display_skip_code; + } else { + g1 = above_display_short_skip_code; + g2 = below_display_short_skip_code; + } + /*tex + + If the equation number is set on a line by itself, either before or after + the formula, we append an infinite penalty so that no page break will + separate the display from its number; and we use the same size and + displacement for all three potential lines of the display, even though + `\.{\\parshape}' may specify them differently. + + \.{\\leqno} on a forced single line due to |width=0|; it follows that |type(a)=hlist_node| + + */ + if (eqno_box && l && (eqno_w == 0)) { + /* if (math_direction_par==dir_TLT) { */ + shift_amount(eqno_box) = 0; + /* } else { */ + /* } */ + append_to_vlist(eqno_box,lua_key_index(equation_number)); + tail_append(new_penalty(inf_penalty,equation_number_penalty)); + } else { + inject_display_skip_before(g1); + } + if (eqno_w != 0) { + r = new_kern(line_w - eq_w - eqno_w - d); + if (l) { + if (swap_dir) { + if (math_direction_par==dir_TLT) { + /*tex TRT + TLT + \eqno: (swap_dir=true, math_direction_par=TLT, l=true) */ + s = new_kern(width(r) + eqno_w); + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + try_couple_nodes(eq_box,s); + } else { + /*tex TLT + TRT + \eqno: (swap_dir=true, math_direction_par=TRT, l=true) */ + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + } + } else { + if (math_direction_par==dir_TLT) { + /*tex TLT + TLT + \leqno: (swap_dir=false, math_direction_par=TLT, l=true) */ + s = new_kern(width(r) + eqno_w); + } else { + /*tex TRT + TRT + \leqno: (swap_dir=false, math_direction_par=TRT, l=true) */ + s = new_kern(width(r)); + } + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + try_couple_nodes(eq_box,s); + } + eq_box = eqno_box; + } else { + if (swap_dir) { + if (math_direction_par==dir_TLT) { + /*tex TRT + TLT + \leqno: (swap_dir=true, math_direction_par=TLT, l=false) */ + } else { + /*tex TLT + TRT + \leqno: (swap_dir=true, math_direction_par=TRT, l=false) */ + } + try_couple_nodes(eq_box,r); + try_couple_nodes(r,eqno_box); + } else { + if (math_direction_par==dir_TLT) { + /*tex TLT + TLT + \eqno: (swap_dir=false, math_direction_par=TLT, l=false) */ + s = new_kern(d); + } else { + /*tex TRT + TRT + \eqno: (swap_dir=false, math_direction_par=TRT, l=false) */ + s = new_kern(width(r) + eqno_w); + } + try_couple_nodes(s,eq_box); + try_couple_nodes(eq_box,r); + try_couple_nodes(r,eqno_box); + eq_box = s; + } + } + eq_box = hpack(eq_box, 0, additional, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + shift_amount(eq_box) = line_s; + } else { + shift_amount(eq_box) = line_s + d; + } + /*tex check for prev: */ + append_to_vlist(eq_box,lua_key_index(equation)); + if ((eqno_box != null) && (eqno_w == 0) && !l) { + tail_append(new_penalty(inf_penalty,equation_number_penalty)); + /* if (math_direction_par==dir_TLT) { */ + shift_amount(eqno_box) = line_s + line_w - eqno_width ; + /* } else { */ + /* } */ + append_to_vlist(eqno_box,lua_key_index(equation_number)); + g2 = 0; + } + if (t != adjust_head) { + /*tex migrating material comes after equation number */ + vlink(tail) = vlink(adjust_head); + alink(adjust_tail) = alink(tail); + tail = t; + } + if (pre_t != pre_adjust_head) { + vlink(tail) = vlink(pre_adjust_head); + alink(pre_adjust_tail) = alink(tail); + tail = pre_t; + } + tail_append(new_penalty(post_display_penalty_par,after_display_penalty)); + inject_display_skip_after(g2); + resume_after_display(); +} + +void after_math(void) +{ + /*tex |mmode| or |-mmode| */ + int m; + /*tex the formula */ + pointer p; + /*tex box containing equation number */ + pointer a = null; + /*tex `\.{\\leqno}' instead of `\.{\\eqno}' */ + boolean l = false; + m = mode; + /*tex this pops the nest */ + p = fin_mlist(null); + if (cur_cmd == math_shift_cs_cmd && + (cur_chr == text_style || cur_chr == display_style)) { + you_cant(); + } + if (mode == -m) { + /*tex end of equation number */ + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + run_mlist_to_hlist(p, false, text_style); + a = hpack(vlink(temp_head), 0, additional, -1); + build_attribute_list(a); + unsave_math(); + /*tex now |cur_group=math_shift_group| */ + decr(save_ptr); + assert(saved_type(0) == saved_eqno); + if (saved_value(0) == 1) + l = true; + m = mode; + p = fin_mlist(null); + + } + if (m < 0) { + /*tex + + The |unsave| is done after everything else here; hence an appearance + of `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing + at these particular \.\$'s. This is consistent with the conventions + of `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display + affects the space above that display. + + */ + if (cur_cmd == math_shift_cs_cmd) { + check_inline_math_end(); + } + tail_append(new_math(math_surround_par, before)); + /*tex begin mathskip code */ + switch (math_skip_mode) { + case 0 : + /*tex obey mathsurround when zero glue */ + if (! glue_is_zero(math_skip_par)) { + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + } + break ; + case 1 : + /*tex always left */ + case 3 : + /*tex always both */ + case 6 : + /*tex only when skip */ + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + break ; + case 2 : + /*tex only right */ + surround(tail) = 0; + break ; + case 4 : + /*tex ignore, obey marthsurround */ + break ; + case 5: + /*tex all spacing disabled */ + surround(tail) = 0; + break ; + } + /*tex end mathskip code */ + if (dir_math_save) { + tail_append(new_dir(math_direction_par)); + } + run_mlist_to_hlist(p, (mode > 0), text_style); + try_couple_nodes(tail,vlink(temp_head)); + + while (vlink(tail) != null) { + tail = vlink(tail); + } + if (dir_math_save) { + tail_append(new_dir(math_direction_par)); + subtype(tail) = cancel_dir; + } + dir_math_save = false; + tail_append(new_math(math_surround_par, after)); + /*tex begin mathskip code */ + switch (math_skip_mode) { + case 0 : + /*tex obey mathsurround when zero glue */ + if (! glue_is_zero(math_skip_par)) { + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + } + break ; + case 2 : + /*tex always right */ + case 3 : + /*tex always both */ + case 6 : + /*tex only when skip */ + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + break ; + case 1 : + /*tex only left */ + surround(tail) = 0; + break ; + case 4 : + /*tex ignore, obey marthsurround */ + break ; + case 5: + /*tex all spacing disabled */ + surround(tail) = 0; + break ; + } + /*tex end mathskip code */ + space_factor_par = 1000; + unsave_math(); + } else { + if (a == null) { + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + } + run_mlist_to_hlist(p, false, display_style); + finish_displayed_math(l, a, vlink(temp_head)); + } +} + +/*tex + + When \.{\\halign} appears in a display, the alignment routines operate + essentially as they do in vertical mode. Then the following program is + activated, with |p| and |q| pointing to the beginning and end of the + resulting list, and with |aux_save| holding the |prev_depth| value. + +*/ + +void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) +{ + do_assignments(); + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + pop_nest(); + tail_append(new_penalty(pre_display_penalty_par,before_display_penalty)); + inject_display_skip_before(above_display_skip_code); + vlink(tail) = p; + if (p != null) + tail = q; + tail_append(new_penalty(post_display_penalty_par,after_display_penalty)); + inject_display_skip_after(below_display_skip_code); + cur_list.prev_depth_field = saved_prevdepth; + resume_after_display(); +} + +/*tex Interface to \.{\\Umath} and \.{\\mathstyle}: */ + +void setup_math_style(void) +{ + pointer q; + tail_append(new_noad()); + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math_style(nucleus(tail), num_style(m_style)); +} + +void print_math_style(void) +{ + if (abs(mode) == mmode) + print_int(m_style); + else + print_int(-1); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.h b/Build/source/texk/web2c/luatexdir/tex/texmath.h new file mode 100644 index 00000000000..23fc5a0e42e --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texmath.h @@ -0,0 +1,306 @@ +/* texmath.h + + Copyright 1996-2006 Han The Thanh <thanh@pdftex.org> + Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef TEXMATH_H +# define TEXMATH_H 1 + +# define empty 0 + +extern pointer new_noad(void); + +extern void show_math_node(halfword); +extern void flush_math(void); +extern void math_left_brace(void); +extern void finish_display_alignment(halfword, halfword, halfword); +extern halfword new_sub_box(halfword); + +# define math_reset(p) do { if (p!=null) flush_node(p); p = null; } while (0) + +# define scripts_allowed(A) ((type((A))>=simple_noad)&&(type((A))<fence_noad)) + +# define default_code 010000000000 /* denotes |default_rule_thickness| */ + +extern void initialize_math(void); +extern void initialize_math_spacing(void); +extern halfword math_vcenter_group(halfword); +extern void build_choices(void); +extern void close_math_group(halfword); +extern void init_math(void); +extern void start_eq_no(void); +extern void set_math_char(mathcodeval); +extern void math_char_in_text(mathcodeval); +extern void math_math_comp(void); +extern void math_limit_switch(void); +extern void math_radical(void); +extern void math_ac(void); +extern pointer new_style(small_number); +extern void append_choices(void); +extern void sub_sup(void); +extern void no_sub_sup(void); +extern void math_fraction(void); +extern void math_left_right(void); +extern void after_math(void); + +extern void scan_extdef_del_code(int level, int extcode); +extern void scan_extdef_math_code(int level, int extcode); + +extern int fam_fnt(int fam_id, int size_id); +extern void def_fam_fnt(int fam_id, int size_id, int f, int lvl); +extern void dump_math_data(void); +extern void undump_math_data(void); +void unsave_math_data(int lvl); + +/* + @ We also need to compute the change in style between mlists and their + subsidiaries. The following macros define the subsidiary style for + an overlined nucleus (|cramped_style|), for a subscript or a superscript + (|sub_style| or |sup_style|), or for a numerator or denominator (|num_style| + or |denom_style|). +*/ + +extern const char *math_style_names[]; + +# define cramped 1 /* add this to an uncramped style if you want to cramp it */ + +# define cramped_style(A) 2*((A)/2)+cramped /* cramp the style */ +# define sub_style(A) 2*((A)/4)+script_style+cramped /* smaller and cramped */ +# define sup_style(A) 2*((A)/4)+script_style+((A)%2) /* smaller */ +# define num_style(A) (A)+2-2*((A)/6) /* smaller unless already script-script */ +# define denom_style(A) 2*((A)/2)+cramped+2-2*((A)/6) /* smaller, cramped */ +# define sup_sup_style(A) sup_style(sup_style((A))) /* smaller */ + +void setup_math_style(void); +void print_math_style(void); + +# define text_size 0 +# define script_size 1 +# define script_script_size 2 + +# define dir_math_save cur_list.math_field +# define m_style cur_list.math_style_field +# define init_math_fields() do { \ + dir_math_save=false; \ + m_style=-1; \ + } while (0) + + +# define null_font 0 +# define min_quarterword 0 + +# define undefined_math_parameter max_dimen + +typedef enum { + math_param_quad, + math_param_axis, + math_param_operator_size, + math_param_overbar_kern, + math_param_overbar_rule, + math_param_overbar_vgap, + math_param_underbar_kern, + math_param_underbar_rule, + math_param_underbar_vgap, + math_param_radical_kern, + math_param_radical_rule, + math_param_radical_vgap, + math_param_radical_degree_before, + math_param_radical_degree_after, + math_param_radical_degree_raise, + math_param_stack_vgap, + math_param_stack_num_up, + math_param_stack_denom_down, + math_param_fraction_rule, + math_param_fraction_num_vgap, + math_param_fraction_num_up, + math_param_fraction_denom_vgap, + math_param_fraction_denom_down, + math_param_fraction_del_size, + math_param_skewed_fraction_hgap, + math_param_skewed_fraction_vgap, + math_param_limit_above_vgap, + math_param_limit_above_bgap, + math_param_limit_above_kern, + math_param_limit_below_vgap, + math_param_limit_below_bgap, + math_param_limit_below_kern, + math_param_nolimit_sub_factor, /* bonus */ + math_param_nolimit_sup_factor, /* bonus */ + math_param_under_delimiter_vgap, + math_param_under_delimiter_bgap, + math_param_over_delimiter_vgap, + math_param_over_delimiter_bgap, + math_param_sub_shift_drop, + math_param_sup_shift_drop, + math_param_sub_shift_down, + math_param_sub_sup_shift_down, + math_param_sub_top_max, + math_param_sup_shift_up, + math_param_sup_bottom_min, + math_param_sup_sub_bottom_max, + math_param_subsup_vgap, + math_param_space_after_script, + math_param_connector_overlap_min, + math_param_ord_ord_spacing, + math_param_ord_op_spacing, + math_param_ord_bin_spacing, + math_param_ord_rel_spacing, + math_param_ord_open_spacing, + math_param_ord_close_spacing, + math_param_ord_punct_spacing, + math_param_ord_inner_spacing, + math_param_op_ord_spacing, + math_param_op_op_spacing, + math_param_op_bin_spacing, + math_param_op_rel_spacing, + math_param_op_open_spacing, + math_param_op_close_spacing, + math_param_op_punct_spacing, + math_param_op_inner_spacing, + math_param_bin_ord_spacing, + math_param_bin_op_spacing, + math_param_bin_bin_spacing, + math_param_bin_rel_spacing, + math_param_bin_open_spacing, + math_param_bin_close_spacing, + math_param_bin_punct_spacing, + math_param_bin_inner_spacing, + math_param_rel_ord_spacing, + math_param_rel_op_spacing, + math_param_rel_bin_spacing, + math_param_rel_rel_spacing, + math_param_rel_open_spacing, + math_param_rel_close_spacing, + math_param_rel_punct_spacing, + math_param_rel_inner_spacing, + math_param_open_ord_spacing, + math_param_open_op_spacing, + math_param_open_bin_spacing, + math_param_open_rel_spacing, + math_param_open_open_spacing, + math_param_open_close_spacing, + math_param_open_punct_spacing, + math_param_open_inner_spacing, + math_param_close_ord_spacing, + math_param_close_op_spacing, + math_param_close_bin_spacing, + math_param_close_rel_spacing, + math_param_close_open_spacing, + math_param_close_close_spacing, + math_param_close_punct_spacing, + math_param_close_inner_spacing, + math_param_punct_ord_spacing, + math_param_punct_op_spacing, + math_param_punct_bin_spacing, + math_param_punct_rel_spacing, + math_param_punct_open_spacing, + math_param_punct_close_spacing, + math_param_punct_punct_spacing, + math_param_punct_inner_spacing, + math_param_inner_ord_spacing, + math_param_inner_op_spacing, + math_param_inner_bin_spacing, + math_param_inner_rel_spacing, + math_param_inner_open_spacing, + math_param_inner_close_spacing, + math_param_inner_punct_spacing, + math_param_inner_inner_spacing, + math_param_last +} math_parameters; + +extern const char *math_param_names[]; + +# define math_param_first_mu_glue math_param_ord_ord_spacing + +extern void def_math_param(int param_code, int style_code, scaled value, + int lvl); +extern scaled get_math_param(int param_code, int style_code); + + +typedef enum { + ScriptPercentScaleDown = 1, + ScriptScriptPercentScaleDown, + DelimitedSubFormulaMinHeight, + DisplayOperatorMinHeight, + MathLeading, + AxisHeight, + AccentBaseHeight, + FlattenedAccentBaseHeight, + SubscriptShiftDown, + SubscriptTopMax, + SubscriptBaselineDropMin, + SuperscriptShiftUp, + SuperscriptShiftUpCramped, + SuperscriptBottomMin, + SuperscriptBaselineDropMax, + SubSuperscriptGapMin, + SuperscriptBottomMaxWithSubscript, + SpaceAfterScript, + UpperLimitGapMin, + UpperLimitBaselineRiseMin, + LowerLimitGapMin, + LowerLimitBaselineDropMin, + StackTopShiftUp, + StackTopDisplayStyleShiftUp, + StackBottomShiftDown, + StackBottomDisplayStyleShiftDown, + StackGapMin, + StackDisplayStyleGapMin, + StretchStackTopShiftUp, + StretchStackBottomShiftDown, + StretchStackGapAboveMin, + StretchStackGapBelowMin, + FractionNumeratorShiftUp, + FractionNumeratorDisplayStyleShiftUp, + FractionDenominatorShiftDown, + FractionDenominatorDisplayStyleShiftDown, + FractionNumeratorGapMin, + FractionNumeratorDisplayStyleGapMin, + FractionRuleThickness, + FractionDenominatorGapMin, + FractionDenominatorDisplayStyleGapMin, + SkewedFractionHorizontalGap, + SkewedFractionVerticalGap, + OverbarVerticalGap, + OverbarRuleThickness, + OverbarExtraAscender, + UnderbarVerticalGap, + UnderbarRuleThickness, + UnderbarExtraDescender, + RadicalVerticalGap, + RadicalDisplayStyleVerticalGap, + RadicalRuleThickness, + RadicalExtraAscender, + RadicalKernBeforeDegree, + RadicalKernAfterDegree, + RadicalDegreeBottomRaisePercent, + MinConnectorOverlap, + SubscriptShiftDownWithSuperscript, + FractionDelimiterSize, + FractionDelimiterDisplayStyleSize, + NoLimitSubFactor, + NoLimitSupFactor, + MATH_param_last, +} MATH_param_codes; + +# define MATH_param_max MATH_param_last + +extern const char *MATH_param_names[]; + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.c b/Build/source/texk/web2c/luatexdir/tex/texnodes.c new file mode 100644 index 00000000000..02e117a876b --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.c @@ -0,0 +1,4840 @@ +/* + +Copyright 2006-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#include "lua/luatex-api.h" + +/*tex + + This module started out using NDEBUG to trigger checking invalid node usage, + something that is needed because users can mess up nodes in Lua. At some + point that code was always enabled so it is now always on but still can be + recognized as additional code. And as the performance hit is close to zero so + disabling makes no sense, not even to make it configureable. There is a + little more memory used but that is neglectable compared to other memory + usage. + +*/ + +#define MAX_CHAIN_SIZE 13 /* why not a bit larger */ +#define CHECK_NODE_USAGE 1 /* this triggers checking */ + +memory_word *volatile varmem = NULL; + +#ifdef CHECK_NODE_USAGE + char *varmem_sizes = NULL; +#endif + +halfword var_mem_max = 0; +halfword rover = 0; + +halfword free_chain[MAX_CHAIN_SIZE] = { null }; + +static int my_prealloc = 0; + +/*tex Used in font and lang: */ + +int fix_node_lists = 1; + +/*tex Defined below. */ + +halfword slow_get_node(int s); + +#define fake_node 100 +#define fake_node_size 2 +#define fake_node_name "fake" + +#define variable_node_size 2 + +/*tex + + The following definitions are used for keys at the \LUA\ end and + provide an efficient way to share hashed strings. + +*/ + +# define init_node_key(target,n,key) \ + target[n].lua = luaS_##key##_index; \ + target[n].name = luaS_##key##_ptr; + +# define init_field_key(target,n,key) \ + target[n].lua = luaS_##key##_index; \ + target[n].name = luaS_##key##_ptr; + +# define init_field_nop(target,n) \ + target[n].lua = 0; \ + target[n].name = NULL; + +/*tex The fields of nodes. */ + +field_info node_fields_accent[10]; +field_info node_fields_adjust[3]; +field_info node_fields_attribute[3]; +field_info node_fields_attribute_list[1]; +field_info node_fields_boundary[3]; +field_info node_fields_choice[6]; +field_info node_fields_delim[6]; +field_info node_fields_dir[4]; +field_info node_fields_disc[6]; +field_info node_fields_fence[8]; +field_info node_fields_fraction[10]; +field_info node_fields_glue[8]; +field_info node_fields_glue_spec[6]; +field_info node_fields_glyph[16]; +field_info node_fields_insert[7]; +field_info node_fields_inserting[9]; +field_info node_fields_kern[4]; +field_info node_fields_list[11]; +field_info node_fields_local_par[9]; +field_info node_fields_margin_kern[4]; +field_info node_fields_mark[4]; +field_info node_fields_math[8]; +field_info node_fields_math_char[4]; +field_info node_fields_math_text_char[4]; +field_info node_fields_noad[5]; +field_info node_fields_penalty[3]; +field_info node_fields_radical[9]; +field_info node_fields_rule[9]; +field_info node_fields_splitup[6]; +field_info node_fields_style[3]; +field_info node_fields_sub_box[3]; +field_info node_fields_sub_mlist[3]; +field_info node_fields_unset[12]; + +/*tex The fields of whatsit nodes. */ + +field_info node_fields_whatsit_close[3]; +field_info node_fields_whatsit_late_lua[6]; +field_info node_fields_whatsit_open[6]; +field_info node_fields_whatsit_save_pos[2]; +field_info node_fields_whatsit_special[3]; +field_info node_fields_whatsit_user_defined[5]; +field_info node_fields_whatsit_write[4]; + +field_info node_fields_whatsit_pdf_action[7]; +field_info node_fields_whatsit_pdf_annot[7]; +field_info node_fields_whatsit_pdf_colorstack[5]; +field_info node_fields_whatsit_pdf_dest[10]; +field_info node_fields_whatsit_pdf_end_link[2]; +field_info node_fields_whatsit_pdf_end_thread[2]; +field_info node_fields_whatsit_pdf_literal[4]; +field_info node_fields_whatsit_pdf_refobj[3]; +field_info node_fields_whatsit_pdf_restore[2]; +field_info node_fields_whatsit_pdf_link_state[3]; +field_info node_fields_whatsit_pdf_save[2]; +field_info node_fields_whatsit_pdf_setmatrix[3]; +field_info node_fields_whatsit_pdf_start_link[8]; +field_info node_fields_whatsit_pdf_start_thread[8]; +field_info node_fields_whatsit_pdf_thread[8]; + + +/*tex The values of fields. */ + +subtype_info node_values_dir[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { 3, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_pdf_destination[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { 3, NULL, 0 }, + { 4, NULL, 0 }, + { 5, NULL, 0 }, + { 6, NULL, 0 }, + { 7, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_pdf_action[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { 3, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_pdf_window[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_color_stack[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { 3, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_fill[] = { + { normal, NULL, 0 }, + { sfi, NULL, 0 }, + { fil, NULL, 0 }, + { fill, NULL, 0 }, + { filll, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_pdf_literal[] = { + { set_origin, NULL, 0 }, + { direct_page, NULL, 0 }, + { direct_always, NULL, 0 }, + { direct_raw, NULL, 0 }, + { direct_text, NULL, 0 }, + { direct_font, NULL, 0 }, + { scan_special, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info other_values_page_states[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { -1, NULL, 0 }, +}; + +/*tex The subtypes of nodes (most have one). */ + +subtype_info node_subtypes_dir[] = { + { normal_dir, NULL, 0 }, + { cancel_dir, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_glue[] = { + { user_skip_glue, NULL, 0 }, + { line_skip_glue, NULL, 0 }, + { baseline_skip_glue, NULL, 0 }, + { par_skip_glue, NULL, 0 }, + { above_display_skip_glue, NULL, 0 }, + { below_display_skip_glue, NULL, 0 }, + { above_display_short_skip_glue, NULL, 0 }, + { below_display_short_skip_glue, NULL, 0 }, + { left_skip_glue, NULL, 0 }, + { right_skip_glue, NULL, 0 }, + { top_skip_glue, NULL, 0 }, + { split_top_skip_glue, NULL, 0 }, + { tab_skip_glue, NULL, 0 }, + { space_skip_glue, NULL, 0 }, + { xspace_skip_glue, NULL, 0 }, + { par_fill_skip_glue, NULL, 0 }, + { math_skip_glue, NULL, 0 }, + { thin_mu_skip_glue, NULL, 0 }, + { med_mu_skip_glue, NULL, 0 }, + { thick_mu_skip_glue, NULL, 0 }, + /* math */ + { cond_math_glue, NULL, 0 }, + { mu_glue, NULL, 0 }, + /* leaders */ + { a_leaders, NULL, 0 }, + { c_leaders, NULL, 0 }, + { x_leaders, NULL, 0 }, + { g_leaders, NULL, 0 }, + { -1, NULL, 0 }, +}; + +/*tex + + Math glue and leaders have special numbers. At some point we might decide to + move them down so best don't use hard coded numbers! + +*/ + +subtype_info node_subtypes_mathglue[] = { /* 98+ */ + { cond_math_glue, NULL, 0 }, + { mu_glue, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_leader[] = { /* 100+ */ + { a_leaders, NULL, 0 }, + { c_leaders, NULL, 0 }, + { x_leaders, NULL, 0 }, + { g_leaders, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_boundary[] = { + { cancel_boundary, NULL, 0 }, + { user_boundary, NULL, 0 }, + { protrusion_boundary, NULL, 0 }, + { word_boundary, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_penalty[] = { + { user_penalty, NULL, 0 }, + { linebreak_penalty, NULL, 0 }, + { line_penalty, NULL, 0 }, + { word_penalty, NULL, 0 }, + { final_penalty, NULL, 0 }, + { noad_penalty, NULL, 0 }, + { before_display_penalty, NULL, 0 }, + { after_display_penalty, NULL, 0 }, + { equation_number_penalty, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_kern[] = { + { font_kern, NULL, 0 }, + { explicit_kern, NULL, 0 }, + { accent_kern, NULL, 0 }, + { italic_kern, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_rule[] = { + { normal_rule, NULL, 0 }, + { box_rule, NULL, 0 }, + { image_rule, NULL, 0 }, + { empty_rule, NULL, 0 }, + { user_rule, NULL, 0 }, + { math_over_rule, NULL, 0 }, + { math_under_rule, NULL, 0 }, + { math_fraction_rule, NULL, 0 }, + { math_radical_rule, NULL, 0 }, + { outline_rule, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_glyph[] = { + { glyph_unset, NULL, 0 }, + { glyph_character, NULL, 0 }, + { glyph_ligature, NULL, 0 }, + { glyph_ghost, NULL, 0 }, + { glyph_left, NULL, 0 }, + { glyph_right, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_disc[] = { + { discretionary_disc, NULL, 0 }, + { explicit_disc, NULL, 0 }, + { automatic_disc, NULL, 0 }, + { syllable_disc, NULL, 0 }, + { init_disc, NULL, 0 }, + { select_disc, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_marginkern[] = { + { left_side, NULL, 0 }, + { right_side, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_list[] = { + { unknown_list, NULL, 0 }, + { line_list, NULL, 0 }, + { hbox_list, NULL, 0 }, + { indent_list, NULL, 0 }, + { align_row_list, NULL, 0 }, + { align_cell_list, NULL, 0 }, + { equation_list, NULL, 0 }, + { equation_number_list, NULL, 0 }, + { math_list_list, NULL, 0 }, + { math_char_list, NULL, 0 }, + { math_h_extensible_list, NULL, 0 }, + { math_v_extensible_list, NULL, 0 }, + { math_h_delimiter_list, NULL, 0 }, + { math_v_delimiter_list, NULL, 0 }, + { math_over_delimiter_list, NULL, 0 }, + { math_under_delimiter_list, NULL, 0 }, + { math_numerator_list, NULL, 0 }, + { math_denominator_list, NULL, 0 }, + { math_limits_list, NULL, 0 }, + { math_fraction_list, NULL, 0 }, + { math_nucleus_list, NULL, 0 }, + { math_sup_list, NULL, 0 }, + { math_sub_list, NULL, 0 }, + { math_degree_list, NULL, 0 }, + { math_scripts_list, NULL, 0 }, + { math_over_list, NULL, 0 }, + { math_under_list, NULL, 0 }, + { math_accent_list, NULL, 0 }, + { math_radical_list, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_adjust[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_math[] = { + { before, NULL, 0 }, + { after, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_noad[] = { + { ord_noad_type, NULL, 0 }, + { op_noad_type_normal, NULL, 0 }, + { op_noad_type_limits, NULL, 0 }, + { op_noad_type_no_limits, NULL, 0 }, + { bin_noad_type, NULL, 0 }, + { rel_noad_type, NULL, 0 }, + { open_noad_type, NULL, 0 }, + { close_noad_type, NULL, 0 }, + { punct_noad_type, NULL, 0 }, + { inner_noad_type, NULL, 0 }, + { under_noad_type, NULL, 0 }, + { over_noad_type, NULL, 0 }, + { vcenter_noad_type, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_radical[] = { + { radical_noad_type, NULL, 0 }, + { uradical_noad_type, NULL, 0 }, + { uroot_noad_type, NULL, 0 }, + { uunderdelimiter_noad_type, NULL, 0 }, + { uoverdelimiter_noad_type, NULL, 0 }, + { udelimiterunder_noad_type, NULL, 0 }, + { udelimiterover_noad_type, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_accent[] = { + { bothflexible_accent, NULL, 0 }, + { fixedtop_accent, NULL, 0 }, + { fixedbottom_accent, NULL, 0 }, + { fixedboth_accent, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_fence[] = { + { unset_noad_side, NULL, 0 }, + { left_noad_side, NULL, 0 }, + { middle_noad_side, NULL, 0 }, + { right_noad_side, NULL, 0 }, + { no_noad_side, NULL, 0 }, + { -1, NULL, 0 }, +}; + +/*tes This brings all together */ + +node_info node_data[] = { + { hlist_node, box_node_size, node_subtypes_list, node_fields_list, NULL, 1, 0 }, + { vlist_node, box_node_size, node_subtypes_list, node_fields_list, NULL, 2, 0 }, + { rule_node, rule_node_size, node_subtypes_rule, node_fields_rule, NULL, 3, 0 }, + { ins_node, ins_node_size, NULL, node_fields_insert, NULL, 4, 0 }, + { mark_node, mark_node_size, NULL, node_fields_mark, NULL, 5, 0 }, + { adjust_node, adjust_node_size, node_subtypes_adjust, node_fields_adjust, NULL, 6, 0 }, + { boundary_node, boundary_node_size, node_subtypes_boundary, node_fields_boundary, NULL, -1, 0 }, + { disc_node, disc_node_size, node_subtypes_disc, node_fields_disc, NULL, 8, 0 }, + { whatsit_node, -1, NULL, NULL, NULL, 9, 0 }, + { local_par_node, local_par_size, NULL, node_fields_local_par, NULL, -1, 0 }, + { dir_node, dir_node_size, node_subtypes_dir, node_fields_dir, NULL, -1, 0 }, + { math_node, math_node_size, node_subtypes_math, node_fields_math, NULL, 10, 0 }, + { glue_node, glue_node_size, node_subtypes_glue, node_fields_glue, NULL, 11, 0 }, + { kern_node, kern_node_size, node_subtypes_kern, node_fields_kern, NULL, 12, 0 }, + { penalty_node, penalty_node_size, node_subtypes_penalty, node_fields_penalty, NULL, 13, 0 }, + { unset_node, box_node_size, NULL, node_fields_unset, NULL, 14, 0 }, + { style_node, style_node_size, NULL, node_fields_style, NULL, 15, 0 }, + { choice_node, style_node_size, NULL, node_fields_choice, NULL, 15, 0 }, + { simple_noad, noad_size, node_subtypes_noad, node_fields_noad, NULL, 15, 0 }, + { radical_noad, radical_noad_size, node_subtypes_radical, node_fields_radical, NULL, 15, 0 }, + { fraction_noad, fraction_noad_size, NULL, node_fields_fraction, NULL, 15, 0 }, + { accent_noad, accent_noad_size, node_subtypes_accent, node_fields_accent, NULL, 15, 0 }, + { fence_noad, fence_noad_size, node_subtypes_fence, node_fields_fence, NULL, 15, 0 }, + { math_char_node, math_kernel_node_size, NULL, node_fields_math_char, NULL, 15, 0 }, + { sub_box_node, math_kernel_node_size, NULL, node_fields_sub_box, NULL, 15, 0 }, + { sub_mlist_node, math_kernel_node_size, NULL, node_fields_sub_mlist, NULL, 15, 0 }, + { math_text_char_node, math_kernel_node_size, NULL, node_fields_math_text_char, NULL, 15, 0 }, + { delim_node, math_shield_node_size, NULL, node_fields_delim, NULL, 15, 0 }, + { margin_kern_node, margin_kern_node_size, node_subtypes_marginkern, node_fields_margin_kern, NULL, -1, 0 }, + { glyph_node, glyph_node_size, node_subtypes_glyph, node_fields_glyph, NULL, 0, 0 }, + { align_record_node, box_node_size, NULL, NULL, NULL, -1, 0 }, + { pseudo_file_node, pseudo_file_node_size, NULL, NULL, NULL, -1, 0 }, + { pseudo_line_node, variable_node_size, NULL, NULL, NULL, -1, 0 }, + { inserting_node, page_ins_node_size, NULL, node_fields_inserting, NULL, -1, 0 }, + { split_up_node, page_ins_node_size, NULL, node_fields_splitup, NULL, -1, 0 }, + { expr_node, expr_node_size, NULL, NULL, NULL, -1, 0 }, + { nesting_node, nesting_node_size, NULL, NULL, NULL, -1, 0 }, + { span_node, span_node_size, NULL, NULL, NULL, -1, 0 }, + { attribute_node, attribute_node_size, NULL, node_fields_attribute, NULL, -1, 0 }, + { glue_spec_node, glue_spec_size, NULL, node_fields_glue_spec, NULL, -1, 0 }, + { attribute_list_node, attribute_node_size, NULL, node_fields_attribute_list, NULL, -1, 0 }, + { temp_node, temp_node_size, NULL, NULL, NULL, -1, 0 }, + { align_stack_node, align_stack_node_size, NULL, NULL, NULL, -1, 0 }, + { movement_node, movement_node_size, NULL, NULL, NULL, -1, 0 }, + { if_node, if_node_size, NULL, NULL, NULL, -1, 0 }, + { unhyphenated_node, active_node_size, NULL, NULL, NULL, -1, 0 }, + { hyphenated_node, active_node_size, NULL, NULL, NULL, -1, 0 }, + { delta_node, delta_node_size, NULL, NULL, NULL, -1, 0 }, + { passive_node, passive_node_size, NULL, NULL, NULL, -1, 0 }, + { shape_node, variable_node_size, NULL, NULL, NULL, -1, 0 }, + { -1, -1, NULL, NULL, NULL, -1, 0 } +}; + +void l_set_node_data(void) { + init_node_key(node_data, hlist_node, hlist) + init_node_key(node_data, vlist_node, vlist) + init_node_key(node_data, rule_node, rule) + init_node_key(node_data, ins_node, ins) + init_node_key(node_data, mark_node, mark) + init_node_key(node_data, adjust_node, adjust) + init_node_key(node_data, boundary_node, boundary) + init_node_key(node_data, disc_node, disc) + init_node_key(node_data, whatsit_node, whatsit) + init_node_key(node_data, local_par_node, local_par) + init_node_key(node_data, dir_node, dir) + init_node_key(node_data, math_node, math) + init_node_key(node_data, glue_node, glue) + init_node_key(node_data, kern_node, kern) + init_node_key(node_data, penalty_node, penalty) + init_node_key(node_data, unset_node, unset) + init_node_key(node_data, style_node, style) + init_node_key(node_data, choice_node, choice) + init_node_key(node_data, simple_noad, noad) + init_node_key(node_data, radical_noad, radical) + init_node_key(node_data, fraction_noad, fraction) + init_node_key(node_data, accent_noad, accent) + init_node_key(node_data, fence_noad, fence) + init_node_key(node_data, math_char_node, math_char) + init_node_key(node_data, sub_box_node, sub_box) + init_node_key(node_data, sub_mlist_node, sub_mlist) + init_node_key(node_data, math_text_char_node, math_text_char) + init_node_key(node_data, delim_node, delim) + init_node_key(node_data, margin_kern_node, margin_kern) + init_node_key(node_data, glyph_node, glyph) + init_node_key(node_data, align_record_node, align_record) + init_node_key(node_data, pseudo_file_node, pseudo_file) + init_node_key(node_data, pseudo_line_node, pseudo_line) + init_node_key(node_data, inserting_node, page_insert) + init_node_key(node_data, split_up_node, split_insert) + init_node_key(node_data, expr_node, expr_stack) + init_node_key(node_data, nesting_node, nested_list) + init_node_key(node_data, span_node, span) + init_node_key(node_data, attribute_node, attribute) + init_node_key(node_data, glue_spec_node, glue_spec) + init_node_key(node_data, attribute_list_node, attribute_list) + init_node_key(node_data, temp_node, temp) + init_node_key(node_data, align_stack_node, align_stack) + init_node_key(node_data, movement_node, movement_stack) + init_node_key(node_data, if_node, if_stack) + init_node_key(node_data, unhyphenated_node, unhyphenated) + init_node_key(node_data, hyphenated_node, hyphenated) + init_node_key(node_data, delta_node, delta) + init_node_key(node_data, passive_node, passive) + init_node_key(node_data, shape_node, shape) + + init_node_key(node_subtypes_dir, normal_dir, normal) + init_node_key(node_subtypes_dir, cancel_dir, cancel) + + init_node_key(node_subtypes_glue, user_skip_glue, userskip) + init_node_key(node_subtypes_glue, line_skip_glue, lineskip) + init_node_key(node_subtypes_glue, baseline_skip_glue, baselineskip) + init_node_key(node_subtypes_glue, par_skip_glue, parskip) + init_node_key(node_subtypes_glue, above_display_skip_glue, abovedisplayskip) + init_node_key(node_subtypes_glue, below_display_skip_glue, belowdisplayskip) + init_node_key(node_subtypes_glue, above_display_short_skip_glue, abovedisplayshortskip) + init_node_key(node_subtypes_glue, below_display_short_skip_glue, belowdisplayshortskip) + init_node_key(node_subtypes_glue, left_skip_glue, leftskip) + init_node_key(node_subtypes_glue, right_skip_glue, rightskip) + init_node_key(node_subtypes_glue, top_skip_glue, topskip) + init_node_key(node_subtypes_glue, split_top_skip_glue, splittopskip) + init_node_key(node_subtypes_glue, tab_skip_glue, tabskip) + init_node_key(node_subtypes_glue, space_skip_glue, spaceskip) + init_node_key(node_subtypes_glue, xspace_skip_glue, xspaceskip) + init_node_key(node_subtypes_glue, par_fill_skip_glue, parfillskip) + init_node_key(node_subtypes_glue, math_skip_glue, mathskip) + init_node_key(node_subtypes_glue, thin_mu_skip_glue, thinmuskip) + init_node_key(node_subtypes_glue, med_mu_skip_glue, medmuskip) + init_node_key(node_subtypes_glue, thick_mu_skip_glue, thickmuskip) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 1, conditionalmathskip) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 2, muglue) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 3, leaders) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 4, cleaders) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 5, xleaders) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 6, gleaders) + + init_node_key(node_subtypes_mathglue, 0, conditionalmathskip) + init_node_key(node_subtypes_mathglue, 1, muglue) + + init_node_key(node_subtypes_leader, 0, leaders) + init_node_key(node_subtypes_leader, 1, cleaders) + init_node_key(node_subtypes_leader, 2, xleaders) + init_node_key(node_subtypes_leader, 3, gleaders) + + init_node_key(node_subtypes_boundary, cancel_boundary, cancel) + init_node_key(node_subtypes_boundary, user_boundary, user) + init_node_key(node_subtypes_boundary, protrusion_boundary, protrusion) + init_node_key(node_subtypes_boundary, word_boundary, word) + + init_node_key(node_subtypes_penalty, user_penalty, userpenalty) + init_node_key(node_subtypes_penalty, linebreak_penalty, linebreakpenalty) + init_node_key(node_subtypes_penalty, line_penalty, linepenalty) + init_node_key(node_subtypes_penalty, word_penalty, wordpenalty) + init_node_key(node_subtypes_penalty, final_penalty, finalpenalty) + init_node_key(node_subtypes_penalty, noad_penalty, noadpenalty) + init_node_key(node_subtypes_penalty, before_display_penalty, beforedisplaypenalty) + init_node_key(node_subtypes_penalty, after_display_penalty, afterdisplaypenalty) + init_node_key(node_subtypes_penalty, equation_number_penalty, equationnumberpenalty) + + init_node_key(node_subtypes_kern, font_kern, fontkern) + init_node_key(node_subtypes_kern, explicit_kern, userkern) + init_node_key(node_subtypes_kern, accent_kern, accentkern) + init_node_key(node_subtypes_kern, italic_kern, italiccorrection) + + init_node_key(node_subtypes_rule, normal_rule, normal) + init_node_key(node_subtypes_rule, box_rule, box) + init_node_key(node_subtypes_rule, image_rule, image) + init_node_key(node_subtypes_rule, empty_rule, empty) + init_node_key(node_subtypes_rule, user_rule, user) + init_node_key(node_subtypes_rule, math_over_rule, over) + init_node_key(node_subtypes_rule, math_under_rule, under) + init_node_key(node_subtypes_rule, math_fraction_rule, fraction) + init_node_key(node_subtypes_rule, math_radical_rule, radical) + init_node_key(node_subtypes_rule, outline_rule, outline) + + init_node_key(node_subtypes_glyph, 0, unset) + init_node_key(node_subtypes_glyph, 1, character) + init_node_key(node_subtypes_glyph, 2, ligature) + init_node_key(node_subtypes_glyph, 3, ghost) + init_node_key(node_subtypes_glyph, 4, left) + init_node_key(node_subtypes_glyph, 5, right) + + init_node_key(node_subtypes_disc, discretionary_disc, discretionary) + init_node_key(node_subtypes_disc, explicit_disc, explicit) + init_node_key(node_subtypes_disc, automatic_disc, automatic) + init_node_key(node_subtypes_disc, syllable_disc, regular) + init_node_key(node_subtypes_disc, init_disc, first) + init_node_key(node_subtypes_disc, select_disc, second) + + init_node_key(node_subtypes_fence, unset_noad_side, unset) + init_node_key(node_subtypes_fence, left_noad_side, left) + init_node_key(node_subtypes_fence, middle_noad_side, middle) + init_node_key(node_subtypes_fence, right_noad_side, right) + init_node_key(node_subtypes_fence, no_noad_side, no) + + init_node_key(node_subtypes_list, unknown_list, unknown) + init_node_key(node_subtypes_list, line_list, line) + init_node_key(node_subtypes_list, hbox_list, box) + init_node_key(node_subtypes_list, indent_list, indent) + init_node_key(node_subtypes_list, align_row_list, alignment) + init_node_key(node_subtypes_list, align_cell_list, cell) + init_node_key(node_subtypes_list, equation_list, equation) + init_node_key(node_subtypes_list, equation_number_list, equationnumber) + init_node_key(node_subtypes_list, math_list_list, math) + init_node_key(node_subtypes_list, math_char_list, mathchar) + init_node_key(node_subtypes_list, math_h_extensible_list, hextensible) + init_node_key(node_subtypes_list, math_v_extensible_list, vextensible) + init_node_key(node_subtypes_list, math_h_delimiter_list, hdelimiter) + init_node_key(node_subtypes_list, math_v_delimiter_list, vdelimiter) + init_node_key(node_subtypes_list, math_over_delimiter_list, overdelimiter) + init_node_key(node_subtypes_list, math_under_delimiter_list, underdelimiter) + init_node_key(node_subtypes_list, math_numerator_list, numerator) + init_node_key(node_subtypes_list, math_denominator_list, denominator) + init_node_key(node_subtypes_list, math_limits_list, limits) + init_node_key(node_subtypes_list, math_fraction_list, fraction) + init_node_key(node_subtypes_list, math_nucleus_list, nucleus) + init_node_key(node_subtypes_list, math_sup_list, sup) + init_node_key(node_subtypes_list, math_sub_list, sub) + init_node_key(node_subtypes_list, math_degree_list, degree) + init_node_key(node_subtypes_list, math_scripts_list, scripts) + init_node_key(node_subtypes_list, math_over_list, over) + init_node_key(node_subtypes_list, math_under_list, under) + init_node_key(node_subtypes_list, math_accent_list, accent) + init_node_key(node_subtypes_list, math_radical_list, radical) + + init_node_key(node_subtypes_math, before, beginmath) + init_node_key(node_subtypes_math, after, endmath) + + init_node_key(node_subtypes_marginkern, left_side, left) + init_node_key(node_subtypes_marginkern, right_side, right) + + init_node_key(node_subtypes_adjust, 0, normal) + init_node_key(node_subtypes_adjust, 1, pre) + + init_node_key(node_subtypes_noad, ord_noad_type, ord) + init_node_key(node_subtypes_noad, op_noad_type_normal, opdisplaylimits) + init_node_key(node_subtypes_noad, op_noad_type_limits, oplimits) + init_node_key(node_subtypes_noad, op_noad_type_no_limits, opnolimits) + init_node_key(node_subtypes_noad, bin_noad_type, bin) + init_node_key(node_subtypes_noad, rel_noad_type, rel) + init_node_key(node_subtypes_noad, open_noad_type, open) + init_node_key(node_subtypes_noad, close_noad_type, close) + init_node_key(node_subtypes_noad, punct_noad_type, punct) + init_node_key(node_subtypes_noad, inner_noad_type, inner) + init_node_key(node_subtypes_noad, under_noad_type, under) + init_node_key(node_subtypes_noad, over_noad_type, over) + init_node_key(node_subtypes_noad, vcenter_noad_type, vcenter) + + init_node_key(node_subtypes_radical, radical_noad_type, radical) + init_node_key(node_subtypes_radical, uradical_noad_type, uradical) + init_node_key(node_subtypes_radical, uroot_noad_type, uroot) + init_node_key(node_subtypes_radical, uunderdelimiter_noad_type, uunderdelimiter) + init_node_key(node_subtypes_radical, uoverdelimiter_noad_type, uoverdelimiter) + init_node_key(node_subtypes_radical, udelimiterunder_noad_type, udelimiterunder) + init_node_key(node_subtypes_radical, udelimiterover_noad_type, udelimiterover) + + init_node_key(node_subtypes_accent, bothflexible_accent, bothflexible) + init_node_key(node_subtypes_accent, fixedtop_accent, fixedtop) + init_node_key(node_subtypes_accent, fixedbottom_accent, fixedbottom) + init_node_key(node_subtypes_accent, fixedboth_accent, fixedboth) + + init_node_key(node_values_fill, normal, normal) + init_node_key(node_values_fill, sfi, fi) + init_node_key(node_values_fill, fil, fil) + init_node_key(node_values_fill, fill, fill) + init_node_key(node_values_fill, filll, filll) + + init_node_key(node_values_dir, 0, TLT) + init_node_key(node_values_dir, 1, TRT) + init_node_key(node_values_dir, 2, LTL) + init_node_key(node_values_dir, 3, RTT) + + init_node_key(other_values_page_states, 0, empty) + init_node_key(other_values_page_states, 1, box_there) + init_node_key(other_values_page_states, 2, inserts_only) + + init_field_key(node_fields_accent, 0, attr); + init_field_key(node_fields_accent, 1, nucleus); + init_field_key(node_fields_accent, 2, sub); + init_field_key(node_fields_accent, 3, sup); + init_field_key(node_fields_accent, 4, accent); + init_field_key(node_fields_accent, 5, bot_accent); + init_field_key(node_fields_accent, 6, top_accent); + init_field_key(node_fields_accent, 7, overlay_accent); + init_field_key(node_fields_accent, 8, fraction); + init_field_nop(node_fields_accent, 9); + + init_field_key(node_fields_adjust, 0, attr); + init_field_key(node_fields_adjust, 1, head); + init_field_nop(node_fields_adjust, 2); + + init_field_key(node_fields_attribute, 0, number); + init_field_key(node_fields_attribute, 1, value); + init_field_nop(node_fields_attribute, 2); + + init_field_nop(node_fields_attribute_list,0); + + init_field_key(node_fields_boundary, 0, attr); + init_field_key(node_fields_boundary, 1, value); + init_field_nop(node_fields_boundary, 2); + + init_field_key(node_fields_choice, 0, attr); + init_field_key(node_fields_choice, 1, display); + init_field_key(node_fields_choice, 2, text); + init_field_key(node_fields_choice, 3, script); + init_field_key(node_fields_choice, 4, scriptscript); + init_field_nop(node_fields_choice, 5); + + init_field_key(node_fields_delim, 0, attr); + init_field_key(node_fields_delim, 1, small_fam); + init_field_key(node_fields_delim, 2, small_char); + init_field_key(node_fields_delim, 3, large_fam); + init_field_key(node_fields_delim, 4, large_char); + init_field_nop(node_fields_delim, 5); + + init_field_key(node_fields_dir, 0, attr); + init_field_key(node_fields_dir, 1, dir); + init_field_key(node_fields_dir, 2, level); + init_field_nop(node_fields_dir, 3); + + init_field_key(node_fields_disc, 0, attr); + init_field_key(node_fields_disc, 1, pre); + init_field_key(node_fields_disc, 2, post); + init_field_key(node_fields_disc, 3, replace); + init_field_key(node_fields_disc, 4, penalty); + init_field_nop(node_fields_disc, 5); + + init_field_key(node_fields_fence, 0, attr); + init_field_key(node_fields_fence, 1, delim); + init_field_key(node_fields_fence, 2, italic); + init_field_key(node_fields_fence, 3, height); + init_field_key(node_fields_fence, 4, depth); + init_field_key(node_fields_fence, 5, options); + init_field_key(node_fields_fence, 6, class); + init_field_nop(node_fields_fence, 7); + + init_field_key(node_fields_fraction, 0, attr); + init_field_key(node_fields_fraction, 1, width); + init_field_key(node_fields_fraction, 2, num); + init_field_key(node_fields_fraction, 3, denom); + init_field_key(node_fields_fraction, 4, left); + init_field_key(node_fields_fraction, 5, right); + init_field_key(node_fields_fraction, 6, middle); + init_field_key(node_fields_fraction, 7, fam); + init_field_key(node_fields_fraction, 8, options); + init_field_nop(node_fields_fraction, 9); + + init_field_key(node_fields_glue, 0, attr); + init_field_key(node_fields_glue, 1, leader); + init_field_key(node_fields_glue, 2, width); + init_field_key(node_fields_glue, 3, stretch); + init_field_key(node_fields_glue, 4, shrink); + init_field_key(node_fields_glue, 5, stretch_order); + init_field_key(node_fields_glue, 6, shrink_order); + init_field_nop(node_fields_glue, 7); + + init_field_key(node_fields_glue_spec, 0, width); + init_field_key(node_fields_glue_spec, 1, stretch); + init_field_key(node_fields_glue_spec, 2, shrink); + init_field_key(node_fields_glue_spec, 3, stretch_order); + init_field_key(node_fields_glue_spec, 4, shrink_order); + init_field_nop(node_fields_glue_spec, 5); + + init_field_key(node_fields_glyph, 0, attr); + init_field_key(node_fields_glyph, 1, char); + init_field_key(node_fields_glyph, 2, font); + init_field_key(node_fields_glyph, 3, lang); + init_field_key(node_fields_glyph, 4, left); + init_field_key(node_fields_glyph, 5, right); + init_field_key(node_fields_glyph, 6, uchyph); + init_field_key(node_fields_glyph, 7, components); + init_field_key(node_fields_glyph, 8, xoffset); + init_field_key(node_fields_glyph, 9, yoffset); + init_field_key(node_fields_glyph, 10, width); + init_field_key(node_fields_glyph, 11, height); + init_field_key(node_fields_glyph, 12, depth); + init_field_key(node_fields_glyph, 13, expansion_factor); + init_field_key(node_fields_glyph, 14, data); + init_field_nop(node_fields_glyph, 15); + + init_field_key(node_fields_insert, 0, attr); + init_field_key(node_fields_insert, 1, cost); + init_field_key(node_fields_insert, 2, depth); + init_field_key(node_fields_insert, 3, height); + init_field_key(node_fields_insert, 4, spec); + init_field_key(node_fields_insert, 5, head); + init_field_nop(node_fields_insert, 6); + + init_field_key(node_fields_inserting, 0, height); + init_field_key(node_fields_inserting, 1, last_ins_ptr); + init_field_key(node_fields_inserting, 2, best_ins_ptr); + init_field_key(node_fields_inserting, 3, width); + init_field_key(node_fields_inserting, 4, stretch); + init_field_key(node_fields_inserting, 5, shrink); + init_field_key(node_fields_inserting, 6, stretch_order); + init_field_key(node_fields_inserting, 7, shrink_order); + init_field_nop(node_fields_inserting, 8); + + init_field_key(node_fields_kern, 0, attr); + init_field_key(node_fields_kern, 1, kern); + init_field_key(node_fields_kern, 2, expansion_factor); + init_field_nop(node_fields_kern, 3); + + init_field_key(node_fields_list, 0, attr); + init_field_key(node_fields_list, 1, width); + init_field_key(node_fields_list, 2, depth); + init_field_key(node_fields_list, 3, height); + init_field_key(node_fields_list, 4, dir); + init_field_key(node_fields_list, 5, shift); + init_field_key(node_fields_list, 6, glue_order); + init_field_key(node_fields_list, 7, glue_sign); + init_field_key(node_fields_list, 8, glue_set); + init_field_key(node_fields_list, 9, head); + init_field_nop(node_fields_list, 10); + + init_field_key(node_fields_local_par, 0, attr); + init_field_key(node_fields_local_par, 1, pen_inter); + init_field_key(node_fields_local_par, 2, pen_broken); + init_field_key(node_fields_local_par, 3, dir); + init_field_key(node_fields_local_par, 4, box_left); + init_field_key(node_fields_local_par, 5, box_left_width); + init_field_key(node_fields_local_par, 6, box_right); + init_field_key(node_fields_local_par, 7, box_right_width); + init_field_nop(node_fields_local_par, 8); + + init_field_key(node_fields_margin_kern, 0, attr); + init_field_key(node_fields_margin_kern, 1, width); + init_field_key(node_fields_margin_kern, 2, glyph); + init_field_nop(node_fields_margin_kern, 3); + + init_field_key(node_fields_mark, 0, attr); + init_field_key(node_fields_mark, 1, class); + init_field_key(node_fields_mark, 2, mark); + init_field_nop(node_fields_mark, 3); + + init_field_key(node_fields_math, 0, attr); + init_field_key(node_fields_math, 1, surround); + init_field_key(node_fields_math, 2, width); + init_field_key(node_fields_math, 3, stretch); + init_field_key(node_fields_math, 4, shrink); + init_field_key(node_fields_math, 5, stretch_order); + init_field_key(node_fields_math, 6, shrink_order); + init_field_nop(node_fields_math, 7); + + init_field_key(node_fields_math_char, 0, attr); + init_field_key(node_fields_math_char, 1, fam); + init_field_key(node_fields_math_char, 2, char); + init_field_nop(node_fields_math_char, 3); + + init_field_key(node_fields_math_text_char, 0, attr); + init_field_key(node_fields_math_text_char, 1, fam); + init_field_key(node_fields_math_text_char, 2, char); + init_field_nop(node_fields_math_text_char, 3); + + init_field_key(node_fields_noad, 0, attr); + init_field_key(node_fields_noad, 1, nucleus); + init_field_key(node_fields_noad, 2, sub); + init_field_key(node_fields_noad, 3, sup); + init_field_nop(node_fields_noad, 4); + + init_field_key(node_fields_penalty, 0, attr); + init_field_key(node_fields_penalty, 1, penalty); + init_field_nop(node_fields_penalty, 2); + + init_field_key(node_fields_radical, 0, attr); + init_field_key(node_fields_radical, 1, nucleus); + init_field_key(node_fields_radical, 2, sub); + init_field_key(node_fields_radical, 3, sup); + init_field_key(node_fields_radical, 4, left); + init_field_key(node_fields_radical, 5, degree); + init_field_key(node_fields_radical, 6, width); + init_field_key(node_fields_radical, 7, options); + init_field_nop(node_fields_radical, 8); + + init_field_key(node_fields_rule, 0, attr); + init_field_key(node_fields_rule, 1, width); + init_field_key(node_fields_rule, 2, depth); + init_field_key(node_fields_rule, 3, height); + init_field_key(node_fields_rule, 4, dir); + init_field_key(node_fields_rule, 5, index); + init_field_key(node_fields_rule, 6, left); + init_field_key(node_fields_rule, 7, right); + init_field_nop(node_fields_rule, 8); + + init_field_key(node_fields_splitup, 0, height); + init_field_key(node_fields_splitup, 1, last_ins_ptr); + init_field_key(node_fields_splitup, 2, best_ins_ptr); + init_field_key(node_fields_splitup, 3, broken_ptr); + init_field_key(node_fields_splitup, 4, broken_ins); + init_field_nop(node_fields_splitup, 5); + + init_field_key(node_fields_style, 0, attr); + init_field_key(node_fields_style, 1, style); + init_field_nop(node_fields_style, 2); + + init_field_key(node_fields_sub_box, 0, attr); + init_field_key(node_fields_sub_box, 1, head); + init_field_nop(node_fields_sub_box, 2); + + init_field_key(node_fields_sub_mlist, 0, attr); + init_field_key(node_fields_sub_mlist, 1, head); + init_field_nop(node_fields_sub_mlist, 2); + + init_field_key(node_fields_unset, 0, attr); + init_field_key(node_fields_unset, 1, width); + init_field_key(node_fields_unset, 2, depth); + init_field_key(node_fields_unset, 3, height); + init_field_key(node_fields_unset, 4, dir); + init_field_key(node_fields_unset, 5, shrink); + init_field_key(node_fields_unset, 6, glue_order); + init_field_key(node_fields_unset, 7, glue_sign); + init_field_key(node_fields_unset, 8, stretch); + init_field_key(node_fields_unset, 9, span); + init_field_key(node_fields_unset, 10, head); + init_field_nop(node_fields_unset, 11); + +} + +node_info whatsit_node_data[] = { + + /*tex These are always there. The fake nodes are historical. */ + + { open_node, open_node_size, NULL, node_fields_whatsit_open, NULL, -1, 0 }, + { write_node, write_node_size, NULL, node_fields_whatsit_write, NULL, -1, 0 }, + { close_node, close_node_size, NULL, node_fields_whatsit_close, NULL, -1, 0 }, + { special_node, special_node_size, NULL, node_fields_whatsit_special, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { save_pos_node, save_pos_node_size, NULL, node_fields_whatsit_save_pos, NULL, -1, 0 }, + { late_lua_node, late_lua_node_size, NULL, node_fields_whatsit_late_lua, NULL, -1, 0 }, + { user_defined_node, user_defined_node_size, NULL, node_fields_whatsit_user_defined, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + + /*tex Here starts the \DVI\ backend section, todo: a separate list. */ + + /*tex {\em There is nothing here.} */ + + /*tex Here starts the \PDF\ backend section, todo: a separate list. */ + + { pdf_literal_node, write_node_size, NULL, node_fields_whatsit_pdf_literal, NULL, -1, 0 }, + { pdf_refobj_node, pdf_refobj_node_size, NULL, node_fields_whatsit_pdf_refobj, NULL, -1, 0 }, + { pdf_annot_node, pdf_annot_node_size, NULL, node_fields_whatsit_pdf_annot, NULL, -1, 0 }, + { pdf_start_link_node, pdf_annot_node_size, NULL, node_fields_whatsit_pdf_start_link, NULL, -1, 0 }, + { pdf_end_link_node, pdf_end_link_node_size, NULL, node_fields_whatsit_pdf_end_link, NULL, -1, 0 }, + { pdf_dest_node, pdf_dest_node_size, NULL, node_fields_whatsit_pdf_dest, NULL, -1, 0 }, + { pdf_action_node, pdf_action_size, NULL, node_fields_whatsit_pdf_action, NULL, -1, 0 }, + { pdf_thread_node, pdf_thread_node_size, NULL, node_fields_whatsit_pdf_thread, NULL, -1, 0 }, + { pdf_start_thread_node, pdf_thread_node_size, NULL, node_fields_whatsit_pdf_start_thread, NULL, -1, 0 }, + { pdf_end_thread_node, pdf_end_thread_node_size, NULL, node_fields_whatsit_pdf_end_thread, NULL, -1, 0 }, + { pdf_thread_data_node, pdf_thread_node_size, NULL, NULL, NULL, -1, 0 }, + { pdf_link_data_node, pdf_annot_node_size, NULL, NULL, NULL, -1, 0 }, + { pdf_colorstack_node, pdf_colorstack_node_size, NULL, node_fields_whatsit_pdf_colorstack, NULL, -1, 0 }, + { pdf_setmatrix_node, pdf_setmatrix_node_size, NULL, node_fields_whatsit_pdf_setmatrix, NULL, -1, 0 }, + { pdf_save_node, pdf_save_node_size, NULL, node_fields_whatsit_pdf_save, NULL, -1, 0 }, + { pdf_restore_node, pdf_restore_node_size, NULL, node_fields_whatsit_pdf_restore, NULL, -1, 0 }, + { pdf_link_state_node, pdf_link_state_node_size, NULL, node_fields_whatsit_pdf_link_state, NULL, -1, 0 }, + + /*tex That's it. */ + + { -1, -1, NULL, NULL, NULL, -1, 0 }, + +}; + +void l_set_whatsit_data(void) { + init_node_key(whatsit_node_data, open_node, open) + init_node_key(whatsit_node_data, write_node, write) + init_node_key(whatsit_node_data, close_node, close) + init_node_key(whatsit_node_data, special_node, special) + init_node_key(whatsit_node_data, save_pos_node, save_pos) + init_node_key(whatsit_node_data, late_lua_node, late_lua) + init_node_key(whatsit_node_data, user_defined_node, user_defined) + + init_field_key(node_fields_whatsit_close, 0, attr); + init_field_key(node_fields_whatsit_close, 1, stream); + init_field_nop(node_fields_whatsit_close, 2); + + init_field_key(node_fields_whatsit_late_lua, 0, attr); + init_field_key(node_fields_whatsit_late_lua, 1, reg); + init_field_key(node_fields_whatsit_late_lua, 2, data); + init_field_key(node_fields_whatsit_late_lua, 3, name); + init_field_key(node_fields_whatsit_late_lua, 4, string); + init_field_nop(node_fields_whatsit_late_lua, 5); + + init_field_key(node_fields_whatsit_open, 0, attr); + init_field_key(node_fields_whatsit_open, 1, stream); + init_field_key(node_fields_whatsit_open, 2, name); + init_field_key(node_fields_whatsit_open, 3, area); + init_field_key(node_fields_whatsit_open, 4, ext); + init_field_nop(node_fields_whatsit_open, 5); + + init_field_key(node_fields_whatsit_save_pos, 0, attr); + init_field_nop(node_fields_whatsit_save_pos, 1); + + init_field_key(node_fields_whatsit_special, 0, attr); + init_field_key(node_fields_whatsit_special, 1, data); + init_field_nop(node_fields_whatsit_special, 2); + + init_field_key(node_fields_whatsit_user_defined, 0, attr); + init_field_key(node_fields_whatsit_user_defined, 1, user_id); + init_field_key(node_fields_whatsit_user_defined, 2, type); + init_field_key(node_fields_whatsit_user_defined, 3, value); + init_field_nop(node_fields_whatsit_user_defined, 4); + + init_field_key(node_fields_whatsit_write, 0, attr); + init_field_key(node_fields_whatsit_write, 1, stream); + init_field_key(node_fields_whatsit_write, 2, data); + init_field_nop(node_fields_whatsit_write, 3); + + init_node_key(whatsit_node_data, pdf_literal_node, pdf_literal) + init_node_key(whatsit_node_data, pdf_refobj_node, pdf_refobj) + init_node_key(whatsit_node_data, pdf_annot_node, pdf_annot) + init_node_key(whatsit_node_data, pdf_start_link_node, pdf_start_link) + init_node_key(whatsit_node_data, pdf_end_link_node, pdf_end_link) + init_node_key(whatsit_node_data, pdf_dest_node, pdf_dest) + init_node_key(whatsit_node_data, pdf_action_node, pdf_action) + init_node_key(whatsit_node_data, pdf_thread_node, pdf_thread) + init_node_key(whatsit_node_data, pdf_start_thread_node,pdf_start_thread) + init_node_key(whatsit_node_data, pdf_end_thread_node, pdf_end_thread) + init_node_key(whatsit_node_data, pdf_thread_data_node, pdf_thread_data) + init_node_key(whatsit_node_data, pdf_link_data_node, pdf_link_data) + init_node_key(whatsit_node_data, pdf_colorstack_node, pdf_colorstack) + init_node_key(whatsit_node_data, pdf_setmatrix_node, pdf_setmatrix) + init_node_key(whatsit_node_data, pdf_save_node, pdf_save) + init_node_key(whatsit_node_data, pdf_restore_node, pdf_restore) + init_node_key(whatsit_node_data, pdf_link_state_node, pdf_link_state) + + init_node_key(node_values_pdf_destination, 0, xyz) + init_node_key(node_values_pdf_destination, 1, fit) + init_node_key(node_values_pdf_destination, 2, fith) + init_node_key(node_values_pdf_destination, 3, fitv) + init_node_key(node_values_pdf_destination, 4, fitb) + init_node_key(node_values_pdf_destination, 5, fitbh) + init_node_key(node_values_pdf_destination, 6, fitbv) + init_node_key(node_values_pdf_destination, 7, fitr) + + init_node_key(node_values_pdf_literal, set_origin, origin) + init_node_key(node_values_pdf_literal, direct_page, page) + init_node_key(node_values_pdf_literal, direct_always, always) + init_node_key(node_values_pdf_literal, direct_raw, raw) + init_node_key(node_values_pdf_literal, direct_text, text) + init_node_key(node_values_pdf_literal, direct_font, font) + init_node_key(node_values_pdf_literal, scan_special, special) + + init_node_key(node_values_pdf_action, 0, page) + init_node_key(node_values_pdf_action, 1, goto) + init_node_key(node_values_pdf_action, 2, thread) + init_node_key(node_values_pdf_action, 3, user) + + init_node_key(node_values_pdf_window, 0, unset) + init_node_key(node_values_pdf_window, 1, new) + init_node_key(node_values_pdf_window, 2, nonew) + + init_node_key(node_values_color_stack, 0, set) + init_node_key(node_values_color_stack, 1, push) + init_node_key(node_values_color_stack, 2, pop) + init_node_key(node_values_color_stack, 3, current) + + init_field_key(node_fields_whatsit_pdf_action, 0, action_type); + init_field_key(node_fields_whatsit_pdf_action, 1, named_id); + init_field_key(node_fields_whatsit_pdf_action, 2, action_id); + init_field_key(node_fields_whatsit_pdf_action, 3, file); + init_field_key(node_fields_whatsit_pdf_action, 4, new_window); + init_field_key(node_fields_whatsit_pdf_action, 5, data); + init_field_nop(node_fields_whatsit_pdf_action, 6); + + init_field_key(node_fields_whatsit_pdf_annot, 0, attr); + init_field_key(node_fields_whatsit_pdf_annot, 1, width); + init_field_key(node_fields_whatsit_pdf_annot, 2, depth); + init_field_key(node_fields_whatsit_pdf_annot, 3, height); + init_field_key(node_fields_whatsit_pdf_annot, 4, objnum); + init_field_key(node_fields_whatsit_pdf_annot, 5, data); + init_field_nop(node_fields_whatsit_pdf_annot, 6); + + init_field_key(node_fields_whatsit_pdf_colorstack, 0, attr); + init_field_key(node_fields_whatsit_pdf_colorstack, 1, stack); + init_field_key(node_fields_whatsit_pdf_colorstack, 2, cmd); + init_field_key(node_fields_whatsit_pdf_colorstack, 3, data); + init_field_nop(node_fields_whatsit_pdf_colorstack, 4); + + init_field_key(node_fields_whatsit_pdf_dest, 0, attr); + init_field_key(node_fields_whatsit_pdf_dest, 1, width); + init_field_key(node_fields_whatsit_pdf_dest, 2, depth); + init_field_key(node_fields_whatsit_pdf_dest, 3, height); + init_field_key(node_fields_whatsit_pdf_dest, 4, named_id); + init_field_key(node_fields_whatsit_pdf_dest, 5, dest_id); + init_field_key(node_fields_whatsit_pdf_dest, 6, dest_type); + init_field_key(node_fields_whatsit_pdf_dest, 7, xyz_zoom); + init_field_key(node_fields_whatsit_pdf_dest, 8, objnum); + init_field_nop(node_fields_whatsit_pdf_dest, 9); + + init_field_key(node_fields_whatsit_pdf_end_link, 0, attr); + init_field_nop(node_fields_whatsit_pdf_end_link, 1); + + init_field_key(node_fields_whatsit_pdf_link_state, 0, attr); + init_field_key(node_fields_whatsit_pdf_link_state, 1, value); + init_field_nop(node_fields_whatsit_pdf_link_state, 2); + + init_field_key(node_fields_whatsit_pdf_end_thread, 0, attr); + init_field_nop(node_fields_whatsit_pdf_end_thread, 1); + + init_field_key(node_fields_whatsit_pdf_literal, 0, attr); + init_field_key(node_fields_whatsit_pdf_literal, 1, mode); + init_field_key(node_fields_whatsit_pdf_literal, 2, data); + init_field_nop(node_fields_whatsit_pdf_literal, 3); + + init_field_key(node_fields_whatsit_pdf_refobj, 0, attr); + init_field_key(node_fields_whatsit_pdf_refobj, 1, objnum); + init_field_nop(node_fields_whatsit_pdf_refobj, 2); + + init_field_key(node_fields_whatsit_pdf_restore, 0, attr); + init_field_nop(node_fields_whatsit_pdf_restore, 1); + + init_field_key(node_fields_whatsit_pdf_save, 0, attr); + init_field_nop(node_fields_whatsit_pdf_save, 1); + + init_field_key(node_fields_whatsit_pdf_setmatrix, 0, attr); + init_field_key(node_fields_whatsit_pdf_setmatrix, 1, data); + init_field_nop(node_fields_whatsit_pdf_setmatrix, 2); + + init_field_key(node_fields_whatsit_pdf_start_link, 0, attr); + init_field_key(node_fields_whatsit_pdf_start_link, 1, width); + init_field_key(node_fields_whatsit_pdf_start_link, 2, depth); + init_field_key(node_fields_whatsit_pdf_start_link, 3, height); + init_field_key(node_fields_whatsit_pdf_start_link, 4, objnum); + init_field_key(node_fields_whatsit_pdf_start_link, 5, link_attr); + init_field_key(node_fields_whatsit_pdf_start_link, 6, action); + init_field_nop(node_fields_whatsit_pdf_start_link, 7); + + init_field_key(node_fields_whatsit_pdf_start_thread, 0, attr); + init_field_key(node_fields_whatsit_pdf_start_thread, 1, width); + init_field_key(node_fields_whatsit_pdf_start_thread, 2, depth); + init_field_key(node_fields_whatsit_pdf_start_thread, 3, height); + init_field_key(node_fields_whatsit_pdf_start_thread, 4, named_id); + init_field_key(node_fields_whatsit_pdf_start_thread, 5, thread_id); + init_field_key(node_fields_whatsit_pdf_start_thread, 6, thread_attr); + init_field_nop(node_fields_whatsit_pdf_start_thread, 7); + + init_field_key(node_fields_whatsit_pdf_thread, 0, attr); + init_field_key(node_fields_whatsit_pdf_thread, 1, width); + init_field_key(node_fields_whatsit_pdf_thread, 2, depth); + init_field_key(node_fields_whatsit_pdf_thread, 3, height); + init_field_key(node_fields_whatsit_pdf_thread, 4, named_id); + init_field_key(node_fields_whatsit_pdf_thread, 5, thread_id); + init_field_key(node_fields_whatsit_pdf_thread, 6, thread_attr); + init_field_nop(node_fields_whatsit_pdf_thread, 7); + +} + +#define last_whatsit_node pdf_restore_node + +/*tex + + When we copy a node list, there are several possibilities: we do the same as + a new node, we copy the entry to the table in properties (a reference), we do + a deep copy of a table in the properties, we create a new table and give it + the original one as a metatable. After some experiments (that also included + timing) with these scenarios I decided that a deep copy made no sense, nor + did nilling. In the end both the shallow copy and the metatable variant were + both ok, although the second ons is slower. The most important aspect to keep + in mind is that references to other nodes in properties no longer can be + valid for that copy. We could use two tables (one unique and one shared) or + metatables but that only complicates matters. + + When defining a new node, we could already allocate a table but it is rather + easy to do that at the lua end e.g. using a metatable __index method. That + way it is under macro package control. + + When deleting a node, we could keep the slot (e.g. setting it to false) but + it could make memory consumption raise unneeded when we have temporary large + node lists and after that only small lists. + + So, in the end this is what we ended up with. For the record, I also + experimented with the following: + + \startitemize + + \startitem + Copy attributes to the properties so that we have fast access at the + lua end: in the end the overhead is not compensated by speed and + convenience, in fact, attributes are not that slow when it comes to + accessing them. + \stopitem + + \startitem + A bitset in the node but again the gain compared to attributes is + neglectable and it also demands a pretty string agreement over what + bit represents what, and this is unlikely to succeed in the tex + community (I could use it for font handling, which is cross package, + but decided that it doesn't pay off. + \stopitem + + \stopitemize + + In case one wonders why properties make sense then, well, it is not so much + speed that we gain, but more convenience: storing all kind of (temporary) + data in attributes is no fun and this mechanism makes sure that properties + are cleaned up when a node is freed. Also, the advantage of a more or less + global properties table is that we stay at the lua end. An alternative is to + store a reference in the node itself but that is complicated by the fact that + the register has some limitations (no numeric keys) and we also don't want to + mess with it too much. + +*/ + +int lua_properties_level = 0 ; +int lua_properties_enabled = 0 ; +int lua_properties_use_metatable = 0 ; + +/*tex + + We keep track of nesting so that we don't oveflow the stack, and, what is + more important, don't keep resolving the registry index. + +*/ + +#define lua_properties_push do { \ + if (lua_properties_enabled) { \ + lua_properties_level = lua_properties_level + 1 ; \ + if (lua_properties_level == 1) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + } \ + } \ +} while(0) + +#define lua_properties_pop do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 1) \ + lua_pop(Luas,1); \ + lua_properties_level = lua_properties_level - 1 ; \ + } \ +} while(0) + +/*tex No setting is needed: */ + +#define lua_properties_set(target) do { \ +} while(0) + +/*tex Resetting boils down to nilling. */ + +#define lua_properties_reset(target) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_pushnil(Luas); \ + lua_rawseti(Luas,-2,target); \ + lua_pop(Luas,1); \ + } else { \ + lua_pushnil(Luas); \ + lua_rawseti(Luas,-2,target); \ + } \ + } \ +} while(0) + +/*tex + + For a moment I considered supporting all kind of data types but in practice + that makes no sense. So we stick to a cheap shallow copy with as option a + metatable. BTW, a deep copy would look like this: + + \starttyping + static void copy_lua_table(lua_State* L, int index) { + lua_newtable(L); + lua_pushnil(L); + while(lua_next(L, index-1) != 0) { + lua_pushvalue(L, -2); + lua_insert(L, -2); + if (lua_type(L,-1)==LUA_TTABLE) + copy_lua_table(L,-1); + lua_settable(L, -4); + } + lua_pop(L,1); + } + + #define lua_properties_copy(target, source) do { \ + if (lua_properties_enabled) { \ + lua_pushinteger(Luas,source); \ + lua_rawget(Luas,-2); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + copy_lua_table(Luas,-1); \ + lua_pushinteger(Luas,target); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas,-3); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } while(0) + \stoptyping + +*/ + +/*tex Isn't there a faster way to metatable? */ + +/*tex + + \starttyping + #define lua_properties_copy(target,source) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setfield(Luas,-2,"__index"); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + lua_pop(Luas,1); \ + } else { \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setfield(Luas,-2,"__index"); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } \ + } while(0) + \stoptyping + +*/ + +/*tex + + A simple testrun on many pages of dumb text shows 1% gain (of course it + depends on how properties are used but some other tests confirm it). + +*/ + +#define lua_properties_copy(target,source) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_push_string_by_name(Luas,__index); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas, -3); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + lua_pop(Luas,1); \ + } else { \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_push_string_by_name(Luas,__index); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas, -3); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } \ +} while(0) + +/*tex Here end the property handlers. */ + +int valid_node(halfword p) +{ + if (p > my_prealloc && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + if (varmem_sizes[p] > 0) { + return 1; + } +#else + return 1; +#endif + } + return 0; +} + +static int test_count = 1; + +#define dorangetest(a,b,c) do { \ + if (!(b>=0 && b<c)) { \ + fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \ + (int)a, (int)b, (int)c, __LINE__,test_count); \ + confusion("node range test failed"); \ + } } while (0) + +#define dotest(a,b,c) do { \ + if (b!=c) { \ + fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \ + (int)a, (int)b, (int)c, __LINE__,test_count); \ + confusion("node test failed"); \ + } } while (0) + +#define check_action_ref(a) { dorangetest(p,a,var_mem_max); } +#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); } + +/*tex hm, we can just pass |p| then. */ + +#define check_token_ref(p) { \ + if (type(p) == whatsit_node) { \ + formatted_error("nodes","fuzzy token cleanup in whatsit node with type %s and subtype %i",node_data[type(p)].name,subtype(p)); \ + } else { \ + formatted_error("nodes","fuzzy token cleanup in node with type %s",node_data[type(p)].name); \ + } \ +} + +#ifdef CHECK_NODE_USAGE + +static void check_static_node_mem(void) +{ + dotest(zero_glue, width(zero_glue), 0); + dotest(zero_glue, type(zero_glue), glue_spec_node); + dotest(zero_glue, vlink(zero_glue), null); + dotest(zero_glue, stretch(zero_glue), 0); + dotest(zero_glue, stretch_order(zero_glue), normal); + dotest(zero_glue, shrink(zero_glue), 0); + dotest(zero_glue, shrink_order(zero_glue), normal); + + dotest(sfi_glue, width(sfi_glue), 0); + dotest(sfi_glue, type(sfi_glue), glue_spec_node); + dotest(sfi_glue, vlink(sfi_glue), null); + dotest(sfi_glue, stretch(sfi_glue), 0); + dotest(sfi_glue, stretch_order(sfi_glue), sfi); + dotest(sfi_glue, shrink(sfi_glue), 0); + dotest(sfi_glue, shrink_order(sfi_glue), normal); + + dotest(fil_glue, width(fil_glue), 0); + dotest(fil_glue, type(fil_glue), glue_spec_node); + dotest(fil_glue, vlink(fil_glue), null); + dotest(fil_glue, stretch(fil_glue), unity); + dotest(fil_glue, stretch_order(fil_glue), fil); + dotest(fil_glue, shrink(fil_glue), 0); + dotest(fil_glue, shrink_order(fil_glue), normal); + + dotest(fill_glue, width(fill_glue), 0); + dotest(fill_glue, type(fill_glue), glue_spec_node); + dotest(fill_glue, vlink(fill_glue), null); + dotest(fill_glue, stretch(fill_glue), unity); + dotest(fill_glue, stretch_order(fill_glue), fill); + dotest(fill_glue, shrink(fill_glue), 0); + dotest(fill_glue, shrink_order(fill_glue), normal); + + dotest(ss_glue, width(ss_glue), 0); + dotest(ss_glue, type(ss_glue), glue_spec_node); + dotest(ss_glue, vlink(ss_glue), null); + dotest(ss_glue, stretch(ss_glue), unity); + dotest(ss_glue, stretch_order(ss_glue), fil); + dotest(ss_glue, shrink(ss_glue), unity); + dotest(ss_glue, shrink_order(ss_glue), fil); + + dotest(fil_neg_glue, width(fil_neg_glue), 0); + dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node); + dotest(fil_neg_glue, vlink(fil_neg_glue), null); + dotest(fil_neg_glue, stretch(fil_neg_glue), -unity); + dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil); + dotest(fil_neg_glue, shrink(fil_neg_glue), 0); + dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal); +} + +static void node_mem_dump(halfword p) +{ + halfword r; + for (r = my_prealloc + 1; r < var_mem_max; r++) { + if (vlink(r) == p) { + halfword s = r; + while (s > my_prealloc && varmem_sizes[s] == 0) { + s--; + } + if (s != null + && s != my_prealloc + && s != var_mem_max + && (r - s) < get_node_size(type(s), subtype(s)) + && alink(s) != p) { + if (type(s) == disc_node) { + fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", + get_node_name(type(s), subtype(s)), (int) s, + (int) vlink(s), (int) alink(s)); + fprintf(stdout, "pre_break(%d,%d,%d), ", + (int) vlink_pre_break(s), (int) tlink(pre_break(s)), + (int) alink(pre_break(s))); + fprintf(stdout, "post_break(%d,%d,%d), ", + (int) vlink_post_break(s), + (int) tlink(post_break(s)), + (int) alink(post_break(s))); + fprintf(stdout, "no_break(%d,%d,%d)", + (int) vlink_no_break(s), (int) tlink(no_break(s)), + (int) alink(no_break(s))); + fprintf(stdout, "\n"); + } else { + if (vlink(s) == p + || (type(s) == glyph_node && lig_ptr (s) == p) + || (type(s) == vlist_node && list_ptr(s) == p) + || (type(s) == hlist_node && list_ptr(s) == p) + || (type(s) == unset_node && list_ptr(s) == p) + || (type(s) == ins_node && ins_ptr (s) == p) + ) { + fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", + get_node_name(type(s), subtype(s)), (int) s, + (int) vlink(s), (int) alink(s)); + if (type(s) == glyph_node) { + fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); + } else if (type(s) == vlist_node || type(s) == hlist_node) { + fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); + } + fprintf(stdout, "\n"); + } else { + if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) { + fprintf(stdout, " pointed to from %s node %d\n", + get_node_name(type(s), subtype(s)), (int) s); + } + } + } + } + } + } +} + +#endif + +static int free_error(halfword p) +{ + if (p > my_prealloc && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + int i; + if (varmem_sizes[p] == 0) { + check_static_node_mem(); + for (i = (my_prealloc + 1); i < var_mem_max; i++) { + if (varmem_sizes[i] > 0) { + check_node(i); + } + } + test_count++; + if (type(p) == glyph_node) { + formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p); + } else { + formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); + } + node_mem_dump(p); + return 1; + } +#endif + } else { + formatted_error("nodes", "attempt to free an impossible node %d", (int) p); + return 1; + } + return 0; +} + +static int copy_error(halfword p) +{ + if (p >= 0 && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + if (p > my_prealloc && varmem_sizes[p] == 0) { + if (type(p) == glyph_node) { + formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p); + } else { + formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); + } + return 1; + } +#endif + } else { + formatted_error("nodes", "attempt to copy an impossible node %d", (int) p); + return 1; + } + return 0; +} + +/*tex + + Because of the 5-10\% overhead that \SYNTEX\ creates some options have been + implemented controlled by |synctex_anyway_mode|. + + \startabulate + \NC \type {1} \NC all but glyphs \NC \NR + \NC \type {2} \NC also glyphs \NC \NR + \NC \type {3} \NC glyphs and glue \NC \NR + \NC \type {4} \NC only glyphs \NC \NR + \stoptabulate + +*/ + +static halfword synctex_anyway_mode = 0; +static halfword synctex_line_field = 0; +static halfword synctex_no_files = 0; + +void synctex_set_mode(int m) +{ + synctex_anyway_mode = m; +}; + +int synctex_get_mode(void) +{ + return synctex_anyway_mode; +}; + +void synctex_set_no_files(int f) +{ + synctex_no_files = f; +}; + +int synctex_get_no_files(void) +{ + return (int) synctex_no_files ; +}; + +void synctex_set_tag(int t) +{ + cur_input.synctex_tag_field = t; +}; + +int synctex_get_tag(void) +{ + return (int) cur_input.synctex_tag_field; +}; + +int synctex_get_line(void) +{ + return (int) synctex_line_field; +}; + +static int forced_tag = 0; +static int forced_line = 0; + +void synctex_force_tag(int t) +{ + forced_tag = t; +}; + +void synctex_force_line(int t) +{ + forced_line = t; +}; + +void synctex_set_line(int l) +{ + synctex_line_field = l; +}; + +/*tex |if_stack| is called a lot so maybe optimize that one. */ + +halfword new_node(int i, int j) +{ + int s = get_node_size(i, j); + halfword n = get_node(s); + /*tex + + It should be possible to do this memset at |free_node()|. Both type() and + subtype() will be set below, and vlink() is set to null by |get_node()|, + so we can do we clearing one word less than |s|. + + */ + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1))); + switch (i) { + case glyph_node: + init_lang_data(n); + break; + case hlist_node: + case vlist_node: + box_dir(n) = -1; + break; + case disc_node: + pre_break(n) = pre_break_head(n); + type(pre_break(n)) = nesting_node; + subtype(pre_break(n)) = pre_break_head(0); + post_break(n) = post_break_head(n); + type(post_break(n)) = nesting_node; + subtype(post_break(n)) = post_break_head(0); + no_break(n) = no_break_head(n); + type(no_break(n)) = nesting_node; + subtype(no_break(n)) = no_break_head(0); + break; + case rule_node: + depth(n) = null_flag; + height(n) = null_flag; + width(n) = null_flag; + rule_dir(n) = -1; + rule_index(n) = 0; + rule_transform(n) = 0; + break; + case whatsit_node: + if (j == open_node) { + open_name(n) = get_nullstr(); + open_area(n) = open_name(n); + open_ext(n) = open_name(n); + } + break; + case unset_node: + width(n) = null_flag; + break; + case pseudo_line_node: + case shape_node: + /*tex + + This is a trick that makes |pseudo_files| slightly slower, but + the overall allocation faster then an explicit test at the top of + |new_node()|. + + */ + if (j>0) { + free_node(n, variable_node_size); + n = slow_get_node(j); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1))); + } + break; + case fraction_noad: + fraction_fam(n) = -1; + break; + case simple_noad: + noad_fam(n) = -1; + break; + default: + break; + } + if (synctex_anyway_mode) { + /*tex See table above. */ + switch (i) { + case glyph_node: + if (synctex_anyway_mode > 1) { + synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case glue_node: + if (synctex_anyway_mode < 4) { + synctex_tag_glue(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glue(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case kern_node: + if (synctex_anyway_mode < 3) { + synctex_tag_kern(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_kern(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case hlist_node: + case vlist_node: + case unset_node: + /*tex Rather useless: */ + if (synctex_anyway_mode < 3) { + synctex_tag_box(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_box(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case rule_node: + if (synctex_anyway_mode < 3) { + synctex_tag_rule(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_rule(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case math_node: + /*tex Noads probably make more sense but let's not change that. */ + if (synctex_anyway_mode < 3) { + synctex_tag_math(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_math(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + } + } else if (synctex_par) { + /*tex Handle the \SYNTEX\ extension. */ + switch (i) { + case glue_node: + synctex_tag_glue(n) = cur_input.synctex_tag_field; + synctex_line_glue(n) = line; + break; + case kern_node: + if (j != 0) { + synctex_tag_kern(n) = cur_input.synctex_tag_field; + synctex_line_kern(n) = line; + } + break; + case hlist_node: + case vlist_node: + case unset_node: + synctex_tag_box(n) = cur_input.synctex_tag_field; + synctex_line_box(n) = line; + break; + case rule_node: + synctex_tag_rule(n) = cur_input.synctex_tag_field; + synctex_line_rule(n) = line; + break; + case math_node: + synctex_tag_math(n) = cur_input.synctex_tag_field; + synctex_line_math(n) = line; + break; + } + } + /*tex Take care of attributes. */ + if (nodetype_has_attributes(i)) { + build_attribute_list(n); + /*tex No need for |lua_properties_set|. */ + } + type(n) = (quarterword) i; + subtype(n) = (quarterword) j; + return n; +} + +halfword raw_glyph_node(void) +{ + register halfword n = get_node(glyph_node_size); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); + if (synctex_anyway_mode > 1) { + synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + type(n) = glyph_node; + subtype(n) = 0; + return n; +} + +halfword new_glyph_node(void) +{ + register halfword n = get_node(glyph_node_size); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); + if (synctex_anyway_mode > 1) { + synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + type(n) = glyph_node; + subtype(n) = 0; + build_attribute_list(n); + /*tex No need for |lua_properties_set|. */ + return n; +} + +/*tex + + This makes a duplicate of the node list that starts at |p| and returns a + pointer to the new list. + +*/ + +halfword do_copy_node_list(halfword p, halfword end) +{ + /*tex previous position in new list */ + halfword q = null; + /*tex head of the list */ + halfword h = null; + register halfword s ; + /*tex saves stack and time */ + lua_properties_push; + while (p != end) { + s = copy_node(p); + if (h == null) { + h = s; + } else { + couple_nodes(q, s); + } + q = s; + p = vlink(p); + } + /*tex saves stack and time */ + lua_properties_pop; + return h; +} + +halfword copy_node_list(halfword p) +{ + return do_copy_node_list(p, null); +} + +#define copy_sub_list(target,source) do { \ + if (source != null) { \ + s = do_copy_node_list(source, null); \ + target = s; \ + } else { \ + target = null; \ + } \ + } while (0) + +#define copy_sub_node(target,source) do { \ + if (source != null) { \ + s = copy_node(source); \ + target = s ; \ + } else { \ + target = null; \ + } \ +} while (0) + +/*tex Make a dupe of a single node. */ + +static void copy_node_wrapup_core(halfword p, halfword r) +{ + halfword s ; + switch (subtype(p)) { + case write_node: + case special_node: + add_token_ref(write_tokens(p)); + break; + case late_lua_node: + copy_late_lua(r, p); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + add_node_attr_ref(user_node_value(p)); + break; + case 'l': + copy_user_lua(r, p); + break; + case 'n': + s = copy_node_list(user_node_value(p)); + user_node_value(r) = s; + break; + case 's': + /* |add_string_ref(user_node_value(p));| */ + break; + case 't': + add_token_ref(user_node_value(p)); + break; + } + break; + default: + break ; + } +} + +void copy_node_wrapup_dvi(halfword p, halfword r) +{ +} + +void copy_node_wrapup_pdf(halfword p, halfword r) +{ + switch(subtype(p)) { + case pdf_literal_node: + copy_pdf_literal(r, p); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + add_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + add_token_ref(pdf_setmatrix_data(p)); + break; + case pdf_annot_node: + add_token_ref(pdf_annot_data(p)); + break; + case pdf_start_link_node: + if (pdf_link_attr(r) != null) + add_token_ref(pdf_link_attr(r)); + add_action_ref(pdf_link_action(r)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + add_token_ref(pdf_dest_id(p)); + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + add_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + add_token_ref(pdf_thread_attr(p)); + break; + default: + break; + } +} + +halfword copy_node(const halfword p) +{ + /*tex current node being fabricated for new list */ + halfword r; + /*tex whatsit subtype */ + halfword w; + /*tex type of node */ + halfword t; + /*tex a helper variable for copying into variable mem */ + register halfword s; + register int i; + if (copy_error(p)) { + r = new_node(temp_node, 0); + return r; + } + t = type(p); + i = get_node_size(t,subtype(p)); + r = get_node(i); + (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i)); + /*tex A possible speedup: + + \starttyping + if t == glue_spec) { + return r; + } + \stoptyping + + */ + if (synctex_anyway_mode) { + /*tex Not: + + \starttyping + if (t == glyph_node) { + if (synctex_anyway_mode > 1) { + synctex_tag_glyph(r) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glyph(r) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + } + \stoptyping + */ + } else if (synctex_par) { + /*tex Handle synctex extension. */ + switch (t) { + case math_node: + synctex_tag_math(r) = cur_input.synctex_tag_field; + synctex_line_math(r) = line; + break; + case kern_node: + synctex_tag_kern(r) = cur_input.synctex_tag_field; + synctex_line_kern(r) = line; + break; + } + } + if (nodetype_has_attributes(t)) { + add_node_attr_ref(node_attr(p)); + alink(r) = null; + lua_properties_copy(r,p); + } + vlink(r) = null; + switch (t) { + case glyph_node: + copy_sub_list(lig_ptr(r),lig_ptr(p)) ; + break; + case glue_node: + copy_sub_list(leader_ptr(r),leader_ptr(p)) ; + break; + case hlist_node: + case vlist_node: + case unset_node: + copy_sub_list(list_ptr(r),list_ptr(p)) ; + break; + case disc_node: + pre_break(r) = pre_break_head(r); + if (vlink_pre_break(p) != null) { + s = copy_node_list(vlink_pre_break(p)); + alink(s) = pre_break(r); + tlink_pre_break(r) = tail_of_list(s); + vlink_pre_break(r) = s; + } else { + assert(tlink(pre_break(r)) == null); + } + post_break(r) = post_break_head(r); + if (vlink_post_break(p) != null) { + s = copy_node_list(vlink_post_break(p)); + alink(s) = post_break(r); + tlink_post_break(r) = tail_of_list(s); + vlink_post_break(r) = s; + } else { + assert(tlink_post_break(r) == null); + } + no_break(r) = no_break_head(r); + if (vlink(no_break(p)) != null) { + s = copy_node_list(vlink_no_break(p)); + alink(s) = no_break(r); + tlink_no_break(r) = tail_of_list(s); + vlink_no_break(r) = s; + } else { + assert(tlink_no_break(r) == null); + } + break; + case math_node: + break; + case ins_node: + copy_sub_list(ins_ptr(r),ins_ptr(p)) ; + break; + case margin_kern_node: + copy_sub_node(margin_char(r),margin_char(p)); + break; + case mark_node: + add_token_ref(mark_ptr(p)); + break; + case adjust_node: + copy_sub_list(adjust_ptr(r),adjust_ptr(p)); + break; + case choice_node: + copy_sub_list(display_mlist(r),display_mlist(p)) ; + copy_sub_list(text_mlist(r),text_mlist(p)) ; + copy_sub_list(script_mlist(r),script_mlist(p)) ; + copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ; + break; + case simple_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + break; + case radical_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + copy_sub_node(left_delimiter(r),left_delimiter(p)) ; + copy_sub_list(degree(r),degree(p)) ; + break; + case accent_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ; + copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ; + copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ; + break; + case fence_noad: + copy_sub_node(delimiter(r),delimiter(p)) ; + break; + case sub_box_node: + case sub_mlist_node: + copy_sub_list(math_list(r),math_list(p)) ; + break; + case fraction_noad: + copy_sub_list(numerator(r),numerator(p)) ; + copy_sub_list(denominator(r),denominator(p)) ; + copy_sub_node(left_delimiter(r),left_delimiter(p)) ; + copy_sub_node(right_delimiter(r),right_delimiter(p)) ; + break; + case glue_spec_node: + break; + case dir_node: + break; + case local_par_node: + copy_sub_list(local_box_left(r),local_box_left(p)); + copy_sub_list(local_box_right(r),local_box_right(p)); + case boundary_node: + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + copy_node_wrapup_pdf(p,r); + } else if (w >= backend_first_dvi_whatsit) { + copy_node_wrapup_dvi(p,r); + } else { + copy_node_wrapup_core(p,r); + } + break; + } + return r; +} + +#define free_sub_list(source) if (source != null) flush_node_list(source); +#define free_sub_node(source) if (source != null) flush_node(source); + +static void flush_node_wrapup_core(halfword p) +{ + switch (subtype(p)) { + case open_node: + case write_node: + case close_node: + case save_pos_node: + break; + case special_node: + delete_token_ref(write_tokens(p)); + break; + case late_lua_node: + free_late_lua(p); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + delete_attribute_ref(user_node_value(p)); + break; + case 'd': + break; + case 'l': + free_user_lua(p); + break; + case 'n': + flush_node_list(user_node_value(p)); + break; + case 's': + /*tex |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */ + break; + case 't': + delete_token_ref(user_node_value(p)); + break; + default: + { + const char *hlp[] = { + "The type of the value in a user defined whatsit node should be one", + "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),", + "or 't' (tokenlist). Yours has an unknown type, and therefore I don't", + "know how to free the node's value. A memory leak may result.", + NULL + }; + tex_error("Unidentified user defined whatsit", hlp); + } + break; + } + break; + } +} + +void flush_node_wrapup_dvi(halfword p) +{ +} + +void flush_node_wrapup_pdf(halfword p) +{ + switch(subtype(p)) { + case pdf_save_node: + case pdf_restore_node: + case pdf_link_state_node: + case pdf_refobj_node: + case pdf_end_link_node: + case pdf_end_thread_node: + break; + case pdf_literal_node: + free_pdf_literal(p); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + delete_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + delete_token_ref(pdf_setmatrix_data(p)); + break; + case pdf_annot_node: + delete_token_ref(pdf_annot_data(p)); + break; + case pdf_link_data_node: + break; + case pdf_start_link_node: + if (pdf_link_attr(p) != null) + delete_token_ref(pdf_link_attr(p)); + delete_action_ref(pdf_link_action(p)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + delete_token_ref(pdf_dest_id(p)); + break; + case pdf_action_node: + if (pdf_action_type(p) == pdf_action_user) { + delete_token_ref(pdf_action_tokens(p)); + } else { + if (pdf_action_file(p) != null) + delete_token_ref(pdf_action_file(p)); + if (pdf_action_type(p) == pdf_action_page) + delete_token_ref(pdf_action_tokens(p)); + else if (pdf_action_named_id(p) > 0) + delete_token_ref(pdf_action_id(p)); + } + break; + case pdf_thread_data_node: + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + delete_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + delete_token_ref(pdf_thread_attr(p)); + break; + } +} + +void flush_node(halfword p) +{ + halfword w; + if (p == null){ + /*tex legal, but no-op. */ + return; + } + if (free_error(p)) + return; + switch (type(p)) { + case glyph_node: + free_sub_list(lig_ptr(p)); + break; + case glue_node: + free_sub_list(leader_ptr(p)); + break; + case hlist_node: + case vlist_node: + case unset_node: + free_sub_list(list_ptr(p)); + break; + case disc_node: + /*tex Watch the start at temp node hack! */ + free_sub_list(vlink(pre_break(p))); + free_sub_list(vlink(post_break(p))); + free_sub_list(vlink(no_break(p))); + break; + case rule_node: + case kern_node: + case penalty_node: + case math_node: + break; + case glue_spec_node: + /*tex This allows free-ing of lua-allocated glue specs. */ + break ; + case dir_node: + break; + case local_par_node: + free_sub_list(local_box_left(p)); + free_sub_list(local_box_right(p)); + break; + case boundary_node: + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + flush_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + flush_node_wrapup_dvi(p); + } else { + flush_node_wrapup_core(p); + } + break; + case ins_node: + flush_node_list(ins_ptr(p)); + break; + case margin_kern_node: + flush_node(margin_char(p)); + break; + case mark_node: + delete_token_ref(mark_ptr(p)); + break; + case adjust_node: + flush_node_list(adjust_ptr(p)); + break; + case style_node: + /*tex Nothing to do. */ + break; + case choice_node: + free_sub_list(display_mlist(p)); + free_sub_list(text_mlist(p)); + free_sub_list(script_mlist(p)); + free_sub_list(script_script_mlist(p)); + break; + case simple_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + break; + case radical_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + free_sub_node(left_delimiter(p)); + free_sub_list(degree(p)); + break; + case accent_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + free_sub_list(top_accent_chr(p)); + free_sub_list(bot_accent_chr(p)); + free_sub_list(overlay_accent_chr(p)); + break; + case fence_noad: + free_sub_list(delimiter(p)); + break; + case delim_node: + case math_char_node: + case math_text_char_node: + /*tex Nothing to do. */ + break; + case sub_box_node: + case sub_mlist_node: + free_sub_list(math_list(p)); + break; + case fraction_noad: + free_sub_list(numerator(p)); + free_sub_list(denominator(p)); + free_sub_node(left_delimiter(p)); + free_sub_node(right_delimiter(p)); + break; + case pseudo_file_node: + free_sub_list(pseudo_lines(p)); + break; + case pseudo_line_node: + case shape_node: + free_node(p, subtype(p)); + return; + break; + case align_stack_node: + case span_node: + case movement_node: + case if_node: + case nesting_node: + case unhyphenated_node: + case hyphenated_node: + case delta_node: + case passive_node: + case inserting_node: + case split_up_node: + case expr_node: + case attribute_node: + case attribute_list_node: + case temp_node: + break; + default: + formatted_error("nodes","flushing weird node type %d", type(p)); + return; + } + if (nodetype_has_attributes(type(p))) { + delete_attribute_ref(node_attr(p)); + lua_properties_reset(p); + } + free_node(p, get_node_size(type(p), subtype(p))); + return; +} + +/*tex Erase the list of nodes starting at |pp|. */ + +void flush_node_list(halfword pp) +{ + register halfword p = pp; + if (p == null) { + /*tex Legal, but no-op. */ + return; + } + if (free_error(p)) + return; + /*tex Saves stack and time. */ + lua_properties_push; + while (p != null) { + register halfword q = vlink(p); + flush_node(p); + p = q; + } + /*tex Saves stack and time. */ + lua_properties_pop; +} + +static void check_node_wrapup_core(halfword p) +{ + switch (subtype(p)) { + /*tex Frontend code. */ + case special_node: + check_token_ref(p); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + check_attribute_ref(user_node_value(p)); + break; + case 't': + check_token_ref(p); + break; + case 'n': + dorangetest(p, user_node_value(p), var_mem_max); + break; + case 's': + case 'd': + case 'l': + break; + default: + confusion("unknown user node type"); + break; + } + break; + case open_node: + case write_node: + case close_node: + case save_pos_node: + break; + } +} + +void check_node_wrapup_dvi(halfword p) +{ +} + +void check_node_wrapup_pdf(halfword p) +{ + switch (subtype(p)) { + case pdf_literal_node: + if (pdf_literal_type(p) == normal) + check_token_ref(p); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + check_token_ref(p); + break; + case pdf_setmatrix_node: + check_token_ref(p); + break; + case late_lua_node: + if (late_lua_name(p) > 0) + check_token_ref(p); + if (late_lua_type(p) == normal) + check_token_ref(p); + break; + case pdf_annot_node: + check_token_ref(p); + break; + case pdf_start_link_node: + if (pdf_link_attr(p) != null) + check_token_ref(p); + check_action_ref(pdf_link_action(p)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + check_token_ref(p); + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + check_token_ref(p); + if (pdf_thread_attr(p) != null) + check_token_ref(p); + break; + case pdf_save_node: + case pdf_restore_node: + case pdf_link_state_node: + case pdf_refobj_node: + case pdf_end_link_node: + case pdf_end_thread_node: + break; + default: + confusion("wrapup pdf nodes"); + break; + } +} + +void check_node(halfword p) +{ + halfword w ; + switch (type(p)) { + case glyph_node: + dorangetest(p, lig_ptr(p), var_mem_max); + break; + case glue_node: + dorangetest(p, leader_ptr(p), var_mem_max); + break; + case hlist_node: + case vlist_node: + case unset_node: + case align_record_node: + dorangetest(p, list_ptr(p), var_mem_max); + break; + case ins_node: + dorangetest(p, ins_ptr(p), var_mem_max); + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + check_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + check_node_wrapup_dvi(p); + } else { + check_node_wrapup_core(p); + } + break; + case margin_kern_node: + check_node(margin_char(p)); + break; + case math_node: + break; + case disc_node: + dorangetest(p, vlink(pre_break(p)), var_mem_max); + dorangetest(p, vlink(post_break(p)), var_mem_max); + dorangetest(p, vlink(no_break(p)), var_mem_max); + break; + case adjust_node: + dorangetest(p, adjust_ptr(p), var_mem_max); + break; + case pseudo_file_node: + dorangetest(p, pseudo_lines(p), var_mem_max); + break; + case pseudo_line_node: + case shape_node: + break; + case choice_node: + dorangetest(p, display_mlist(p), var_mem_max); + dorangetest(p, text_mlist(p), var_mem_max); + dorangetest(p, script_mlist(p), var_mem_max); + dorangetest(p, script_script_mlist(p), var_mem_max); + break; + case fraction_noad: + dorangetest(p, numerator(p), var_mem_max); + dorangetest(p, denominator(p), var_mem_max); + dorangetest(p, left_delimiter(p), var_mem_max); + dorangetest(p, right_delimiter(p), var_mem_max); + break; + case simple_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + break; + case radical_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + dorangetest(p, degree(p), var_mem_max); + dorangetest(p, left_delimiter(p), var_mem_max); + break; + case accent_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + dorangetest(p, top_accent_chr(p), var_mem_max); + dorangetest(p, bot_accent_chr(p), var_mem_max); + dorangetest(p, overlay_accent_chr(p), var_mem_max); + break; + case fence_noad: + dorangetest(p, delimiter(p), var_mem_max); + break; + case local_par_node: + dorangetest(p, local_box_left(p), var_mem_max); + dorangetest(p, local_box_right(p), var_mem_max); + break; + /*tex + + There is no need for useless cases: + + \starttyping + case rule_node: + case kern_node: + case penalty_node: + case mark_node: + case style_node: + case attribute_list_node: + case attribute_node: + case glue_spec_node: + case temp_node: + case align_stack_node: + case movement_node: + case if_node: + case nesting_node: + case span_node: + case unhyphenated_node: + case hyphenated_node: + case delta_node: + case passive_node: + case expr_node: + case dir_node: + case boundary_node: + break; + default: + fprintf(stdout, "check_node: type is %d\n", type(p)); + \stoptyping + + */ + } +} + +halfword fix_node_list(halfword head) +{ + halfword next, tail; + if (head == null) + return null; + tail = head; + next = vlink(head); + while (next != null) { + alink(next) = tail; + tail = next; + next = vlink(tail); + } + return tail; +} + +halfword get_node(int s) +{ + register halfword r; + + if (s < MAX_CHAIN_SIZE) { + r = free_chain[s]; + if (r != null) { + free_chain[s] = vlink(r); +#ifdef CHECK_NODE_USAGE + varmem_sizes[r] = (char) s; +#endif + vlink(r) = null; + /*tex Maintain usage statistics. */ + var_used += s; + return r; + } + /*tex This is the end of the \quote {inner loop}. */ + return slow_get_node(s); + } else { + normal_error("nodes","there is a problem in getting a node, case 1"); + return null; + } +} + +void free_node(halfword p, int s) +{ + if (p <= my_prealloc) { + formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p)); + return; + } +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + if (s < MAX_CHAIN_SIZE) { + vlink(p) = free_chain[s]; + free_chain[s] = p; + } else { + /*tex Todo: it is perhaps possible to merge this node with an existing rover? */ + node_size(p) = s; + vlink(p) = rover; + while (vlink(rover) != vlink(p)) { + rover = vlink(rover); + } + vlink(rover) = p; + } + /*tex Maintain statistics. */ + var_used -= s; +} + +static void free_node_chain(halfword q, int s) +{ + register halfword p = q; + while (vlink(p) != null) { +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + var_used -= s; + p = vlink(p); + } + var_used -= s; +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + vlink(p) = free_chain[s]; + free_chain[s] = q; +} + +/*tex + + At the start of the node memory area we reserve some special nodes, for + instance frequently used glue specifications. We could as well just use + new_glue here but for the moment we stick to the traditional approach. + +*/ + +#define initialize_glue(n,wi,st,sh,sto,sho) \ + vlink(n) = null; \ + type(n) = glue_spec_node; \ + width(n) = wi; \ + stretch(n) = st; \ + shrink(n) = sh; \ + stretch_order(n) = sto; \ + shrink_order(n) = sho; + +#define initialize_whatever(n,t) \ + vinfo(n) = 0; \ + type(n) = t; \ + vlink(n) = null; \ + alink(n) = null; + +#define initialize_point(n) \ + type(n) = glyph_node; \ + subtype(n) = 0; \ + vlink(n) = null; \ + vinfo(n + 1) = null; \ + alink(n) = null; \ + font(n) = 0; \ + character(n) = '.'; \ + vinfo(n + 3) = 0; \ + vlink(n + 3) = 0; \ + vinfo(n + 4) = 0; \ + vlink(n + 4) = 0; + +void init_node_mem(int t) +{ + my_prealloc = var_mem_stat_max; + + varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t); + if (varmem == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word)); +#ifdef CHECK_NODE_USAGE + varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t); + if (varmem_sizes == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t); +#endif + var_mem_max = t; + rover = var_mem_stat_max + 1; + vlink(rover) = rover; + node_size(rover) = (t - rover); + var_used = 0; + + /*tex Initialize static glue specs. */ + + initialize_glue(zero_glue,0,0,0,0,0); + initialize_glue(sfi_glue,0,0,0,sfi,0); + initialize_glue(fil_glue,0,unity,0,fil,0); + initialize_glue(fill_glue,0,unity,0,fill,0); + initialize_glue(ss_glue,0,unity,unity,fil,fil); + initialize_glue(fil_neg_glue,0,-unity,0,fil,0); + + /*tex Initialize node list heads. */ + + initialize_whatever(page_ins_head,temp_node); + initialize_whatever(contrib_head,temp_node); + initialize_whatever(page_head,temp_node); + initialize_whatever(temp_head,temp_node); + initialize_whatever(hold_head,temp_node); + initialize_whatever(adjust_head,temp_node); + initialize_whatever(pre_adjust_head,temp_node); + initialize_whatever(align_head,temp_node); + + initialize_whatever(active,unhyphenated_node); + initialize_whatever(end_span,span_node); + + initialize_point(begin_point); + initialize_point(end_point); +} + +void dump_node_mem(void) +{ + dump_int(var_mem_max); + dump_int(rover); + dump_things(varmem[0], var_mem_max); +#ifdef CHECK_NODE_USAGE + dump_things(varmem_sizes[0], var_mem_max); +#endif + dump_things(free_chain[0], MAX_CHAIN_SIZE); + dump_int(var_used); + dump_int(my_prealloc); +} + +/*tex + + It makes sense to enlarge the varmem array immediately. + +*/ + +void undump_node_mem(void) +{ + int x; + undump_int(x); + undump_int(rover); + var_mem_max = (x < 100000 ? 100000 : x); + varmem = xmallocarray(memory_word, (unsigned) var_mem_max); + undump_things(varmem[0], x); +#ifdef CHECK_NODE_USAGE + varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); + memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char)); + undump_things(varmem_sizes[0], x); +#endif + undump_things(free_chain[0], MAX_CHAIN_SIZE); + undump_int(var_used); + undump_int(my_prealloc); + if (var_mem_max > x) { + /*tex Todo: is it perhaps possible to merge the new node with an existing rover? */ + vlink(x) = rover; + node_size(x) = (var_mem_max - x); + while (vlink(rover) != vlink(x)) { + rover = vlink(rover); + } + vlink(rover) = x; + } +} + +halfword slow_get_node(int s) +{ + register int t; + + RETRY: + t = node_size(rover); + if (vlink(rover) < var_mem_max && vlink(rover) != 0) { + if (t > s) { + /*tex Allocating from the bottom helps decrease page faults. */ + register halfword r = rover; + rover += s; + vlink(rover) = vlink(r); + node_size(rover) = node_size(r) - s; + if (vlink(rover) != r) { + /*tex The list is longer than one. */ + halfword q = r; + while (vlink(q) != r) { + q = vlink(q); + } + vlink(q) += s; + } else { + vlink(rover) += s; + } + if (vlink(rover) < var_mem_max) { +#ifdef CHECK_NODE_USAGE + varmem_sizes[r] = (char) (s > 127 ? 127 : s); +#endif + vlink(r) = null; + /*tex Maintain usage statistics. */ + var_used += s; + /*tex This is the only exit. */ + return r; + } else { + normal_error("nodes","there is a problem in getting a node, case 2"); + return null; + } + } else { + /*tex Attempt to keep the free list small. */ + int x; + if (vlink(rover) != rover) { + if (t < MAX_CHAIN_SIZE) { + halfword l = vlink(rover); + vlink(rover) = free_chain[t]; + free_chain[t] = rover; + rover = l; + while (vlink(l) != free_chain[t]) { + l = vlink(l); + } + vlink(l) = rover; + goto RETRY; + } else { + halfword l = rover; + while (vlink(rover) != l) { + if (node_size(rover) > s) { + goto RETRY; + } + rover = vlink(rover); + } + } + } + /*tex If we are still here, it was apparently impossible to get a match. */ + x = (var_mem_max >> 2) + s; + varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x)); + if (varmem == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word)); +#ifdef CHECK_NODE_USAGE + varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x)); + if (varmem_sizes == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char)); +#endif + /*tex Todo: is it perhaps possible to merge the new memory with an existing rover? */ + vlink(var_mem_max) = rover; + node_size(var_mem_max) = x; + while (vlink(rover) != vlink(var_mem_max)) { + rover = vlink(rover); + } + vlink(rover) = var_mem_max; + rover = var_mem_max; + var_mem_max += x; + goto RETRY; + } + } else { + normal_error("nodes","there is a problem in getting a node, case 3"); + return null; + } +} + +char *sprint_node_mem_usage(void) +{ + char *s; +#ifdef CHECK_NODE_USAGE + char *ss; + int i; + int b = 0; + char msg[256]; + int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; + s = strdup(""); + for (i = (var_mem_max - 1); i > my_prealloc; i--) { + if (varmem_sizes[i] > 0) { + if (type(i) > last_normal_node + last_whatsit_node) { + node_counts[last_normal_node + last_whatsit_node + 1]++; + } else if (type(i) == whatsit_node) { + node_counts[(subtype(i) + last_normal_node + 1)]++; + } else { + node_counts[type(i)]++; + } + } + } + for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) { + if (node_counts[i] > 0) { + int j = + (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0); + snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], + get_node_name((i > last_normal_node ? whatsit_node : i), j)); + ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1)); + strcpy(ss, s); + strcat(ss, msg); + free(s); + s = ss; + b = 1; + } + } +#else + s = strdup(""); +#endif + return s; +} + +halfword list_node_mem_usage(void) +{ + halfword q = null; +#ifdef CHECK_NODE_USAGE + halfword p = null; + halfword i, j; + char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); + memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max); + for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { + if (saved_varmem_sizes[i] > 0) { + j = copy_node(i); + if (p == null) { + q = j; + } else { + vlink(p) = j; + } + p = j; + } + } + free(saved_varmem_sizes); +#endif + return q; +} + +void print_node_mem_stats(void) +{ + int i, b; + halfword j; + char msg[256]; + char *s; + int free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; + snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc)); + tprint_nl(msg); + s = sprint_node_mem_usage(); + tprint_nl(" "); + tprint(s); + free(s); + tprint(" nodes"); + tprint_nl(" avail lists: "); + b = 0; + for (i = 1; i < MAX_CHAIN_SIZE; i++) { + for (j = free_chain[i]; j != null; j = vlink(j)) + free_chain_counts[i]++; + if (free_chain_counts[i] > 0) { + snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]); + tprint(msg); + b = 1; + } + } + /*tex A newline, if needed: */ + print_nlp(); +} + +halfword new_span_node(halfword n, int s, scaled w) +{ + halfword p = new_node(span_node, 0); + span_link(p) = n; + span_span(p) = s; + width(p) = w; + return p; +} + +/* Now comes some attribute stuff. */ + +static halfword new_attribute_node(unsigned int i, int v) +{ + register halfword r = get_node(attribute_node_size); + type(r) = attribute_node; + attribute_id(r) = (halfword) i; + attribute_value(r) = v; + /* not used but nicer in print */ + subtype(r) = 0; + return r; +} + +halfword copy_attribute_list(halfword n) +{ + halfword q = get_node(attribute_node_size); + register halfword p = q; + type(p) = attribute_list_node; + attr_list_ref(p) = 0; + n = vlink(n); + while (n != null) { + register halfword r = get_node(attribute_node_size); + /*tex The link will be fixed automatically in the next loop. */ + (void) memcpy((void *) (varmem + r), (void *) (varmem + n), (sizeof(memory_word) * attribute_node_size)); + vlink(p) = r; + p = r; + n = vlink(n); + } + return q; +} + +void update_attribute_cache(void) +{ + halfword p; + register int i; + attr_list_cache = get_node(attribute_node_size); + type(attr_list_cache) = attribute_list_node; + attr_list_ref(attr_list_cache) = 0; + p = attr_list_cache; + for (i = 0; i <= max_used_attr; i++) { + register int v = attribute(i); + if (v > UNUSED_ATTRIBUTE) { + register halfword r = new_attribute_node((unsigned) i, v); + vlink(p) = r; + p = r; + } + } + if (vlink(attr_list_cache) == null) { + free_node(attr_list_cache, attribute_node_size); + attr_list_cache = null; + } + return; +} + +void build_attribute_list(halfword b) +{ + if (max_used_attr >= 0) { + if (attr_list_cache == cache_disabled|| attr_list_cache == null) { + update_attribute_cache(); + if (attr_list_cache == null) + return; + } + attr_list_ref(attr_list_cache)++; + node_attr(b) = attr_list_cache; + } +} + +halfword current_attribute_list(void) +{ + if (max_used_attr >= 0) { + if (attr_list_cache == cache_disabled) { + update_attribute_cache(); + } + return attr_list_cache ; + } + return null ; +} + +void reassign_attribute(halfword n, halfword new) +{ + halfword old; + old = node_attr(n); + if (new == null) { + /*tex There is nothing to assign but we need to check for an old value. */ + if (old != null) { + /*tex This also nulls |attr| field of |n|. */ + delete_attribute_ref(old); + } + } else if (old == null) { + /*tex Nothing is assigned so we just do that now. */ + assign_attribute_ref(n,new); + } else if (old != new) { + /*tex Something is assigned so we need to clean up and assign then. */ + delete_attribute_ref(old); + assign_attribute_ref(n,new); + } + /*tex The same value so there is no need to assign and change the refcount. */ + node_attr(n) = new ; +} + +void delete_attribute_ref(halfword b) +{ + if (b != null) { + if (type(b) == attribute_list_node){ + attr_list_ref(b)--; + if (attr_list_ref(b) == 0) { + if (b == attr_list_cache) + attr_list_cache = cache_disabled; + free_node_chain(b, attribute_node_size); + } + /*tex Maintain sanity. */ + if (attr_list_ref(b) < 0) { + attr_list_ref(b) = 0; + } + } else { + normal_error("nodes","trying to delete an attribute reference of a non attribute node"); + } + } +} + +void reset_node_properties(halfword b) +{ + if (b != null) { + lua_properties_reset(b); + } +} + +/*tex Here |p| is an attr list head, or zero. */ + +halfword do_set_attribute(halfword p, int i, int val) +{ + register halfword q; + register int j = 0; + if (p == null) { + /*tex Add a new head \& node. */ + q = get_node(attribute_node_size); + type(q) = attribute_list_node; + attr_list_ref(q) = 1; + p = new_attribute_node((unsigned) i, val); + vlink(q) = p; + return q; + } + q = p; + if (vlink(p) != null) { + while (vlink(p) != null) { + int t = attribute_id(vlink(p)); + if (t == i && attribute_value(vlink(p)) == val) { + /*tex There is no need to do anything. */ + return q; + } + if (t >= i) + break; + j++; + p = vlink(p); + } + p = q; + while (j-- > 0) + p = vlink(p); + if (attribute_id(vlink(p)) == i) { + attribute_value(vlink(p)) = val; + } else { + /*tex Add a new node. */ + halfword r = new_attribute_node((unsigned) i, val); + vlink(r) = vlink(p); + vlink(p) = r; + } + return q; + } else { + normal_error("nodes","trying to set an attribute fails, case 1"); + return null ; + } +} + +void set_attribute(halfword n, int i, int val) +{ + register halfword p; + register int j = 0; + /*tex Not all nodes can have an attribute list. */ + if (!nodetype_has_attributes(type(n))) + return; + /*tex If we have no list, we create one and quit. */ + p = node_attr(n); + if (p == null) { /* add a new head \& node */ + p = get_node(attribute_node_size); + type(p) = attribute_list_node; + attr_list_ref(p) = 1; + node_attr(n) = p; + p = new_attribute_node((unsigned) i, val); + vlink(node_attr(n)) = p; + return; + } + /*tex We check if we have this attribute already and quit if the value stays the same. */ + if (vlink(p) != null) { + while (vlink(p) != null) { + int t = attribute_id(vlink(p)); + if (t == i && attribute_value(vlink(p)) == val) + return; + if (t >= i) + break; + j++; + p = vlink(p); + } + /*tex If found |j| has now the position and we assume a sorted list ! */ + p = node_attr(n); + if (attr_list_ref(p) == 0 ) { + /*tex The list is invalid i.e. freed already. */ + formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n); + /*tex The still dangling list gets ref count 1. */ + attr_list_ref(p) = 1; + } else if (attr_list_ref(p) == 1) { + /*tex This can really happen! */ + if (p == attr_list_cache) { + /*tex + + We can invalidate the cache setting with |attr_list_cache = + cache_disabled| or or save the list, as done below. + + */ + p = copy_attribute_list(p); + node_attr(n) = p; + /*tex The copied list gets ref count 1. */ + attr_list_ref(p) = 1; + } + } else { + /*tex The list is used multiple times so we make a copy. */ + p = copy_attribute_list(p); + /*tex We decrement the ref count or the original. */ + delete_attribute_ref(node_attr(n)); + node_attr(n) = p; + /*tex The copied list gets ref count 1. */ + attr_list_ref(p) = 1; + } + /*tex We go to position |j| in the list. */ + while (j-- > 0) + p = vlink(p); + /*tex If we have a hit we just set the value otherwise we add a new node. */ + if (attribute_id(vlink(p)) == i) { + attribute_value(vlink(p)) = val; + } else { + /*tex Add a new node. */ + halfword r = new_attribute_node((unsigned) i, val); + vlink(r) = vlink(p); + vlink(p) = r; + } + } else { + normal_error("nodes","trying to set an attribute fails, case 2"); + } +} + +int unset_attribute(halfword n, int i, int val) +{ + register halfword p; + register int t; + register int j = 0; + if (!nodetype_has_attributes(type(n))) + return null; + p = node_attr(n); + if (p == null) + return UNUSED_ATTRIBUTE; + if (attr_list_ref(p) == 0) { + formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n); + return UNUSED_ATTRIBUTE; + } + if (vlink(p) != null) { + while (vlink(p) != null) { + t = attribute_id(vlink(p)); + if (t > i) + return UNUSED_ATTRIBUTE; + if (t == i) { + p = vlink(p); + break; + } + j++; + p = vlink(p); + } + if (attribute_id(p) != i) + return UNUSED_ATTRIBUTE; + /*tex If we are still here, the attribute exists. */ + p = node_attr(n); + if (attr_list_ref(p) > 1 || p == attr_list_cache) { + halfword q = copy_attribute_list(p); + if (attr_list_ref(p) > 1) { + delete_attribute_ref(node_attr(n)); + } + attr_list_ref(q) = 1; + node_attr(n) = q; + } + p = vlink(node_attr(n)); + while (j-- > 0) + p = vlink(p); + t = attribute_value(p); + if (val == UNUSED_ATTRIBUTE || t == val) { + attribute_value(p) = UNUSED_ATTRIBUTE; + } + return t; + } else { + normal_error("nodes","trying to unset an attribute fails"); + return null; + } +} + +int has_attribute(halfword n, int i, int val) +{ + register halfword p; + if (!nodetype_has_attributes(type(n))) + return UNUSED_ATTRIBUTE; + p = node_attr(n); + if (p == null || vlink(p) == null) + return UNUSED_ATTRIBUTE; + p = vlink(p); + while (p != null) { + if (attribute_id(p) == i) { + int ret = attribute_value(p); + if (val == UNUSED_ATTRIBUTE || val == ret) + return ret; + return UNUSED_ATTRIBUTE; + } else if (attribute_id(p) > i) { + return UNUSED_ATTRIBUTE; + } + p = vlink(p); + } + return UNUSED_ATTRIBUTE; +} + +void print_short_node_contents(halfword p) +{ + switch (type(p)) { + case hlist_node: + case vlist_node: + case ins_node: + case whatsit_node: + case mark_node: + case adjust_node: + case unset_node: + print_char('['); + print_char(']'); + break; + case rule_node: + print_char('|'); + break; + case glue_node: + if (! glue_is_zero(p)) + print_char(' '); + break; + case math_node: + print_char('$'); + break; + case disc_node: + short_display(vlink(pre_break(p))); + short_display(vlink(post_break(p))); + break; + } +} + +static void show_pdftex_whatsit_rule_spec(int p) +{ + tprint("("); + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); +} + +/*tex + + Each new type of node that appears in our data structure must be capable of + being displayed, copied, destroyed, and so on. The routines that we need for + write-oriented whatsits are somewhat like those for mark nodes; other + extensions might, of course, involve more subtlety here. + +*/ + +static void print_write_whatsit(const char *s, pointer p) +{ + tprint_esc(s); + if (write_stream(p) < 16) + print_int(write_stream(p)); + else if (write_stream(p) == 16) + print_char('*'); + else + print_char('-'); +} + +static void show_node_wrapup_core(int p) +{ + switch (subtype(p)) { + case open_node: + print_write_whatsit("openout", p); + print_char('='); + print_file_name(open_name(p), open_area(p), open_ext(p)); + break; + case write_node: + print_write_whatsit("write", p); + print_mark(write_tokens(p)); + break; + case close_node: + print_write_whatsit("closeout", p); + break; + case special_node: + tprint_esc("special"); + print_mark(write_tokens(p)); + break; + case late_lua_node: + show_late_lua(p); + break; + case save_pos_node: + tprint_esc("savepos"); + break; + case user_defined_node: + tprint_esc("whatsit"); + print_int(user_node_id(p)); + print_char('='); + switch (user_node_type(p)) { + case 'a': + tprint("<>"); + break; + case 'n': + tprint("["); + show_node_list(user_node_value(p)); + tprint("]"); + break; + case 's': + print_char('"'); + print(user_node_value(p)); + print_char('"'); + break; + case 't': + print_mark(user_node_value(p)); + break; + default: /* only 'd' */ + print_int(user_node_value(p)); + break; + } + break; + } +} + +void show_node_wrapup_dvi(int p) +{ +} + +void show_node_wrapup_pdf(int p) +{ + switch (subtype(p)) { + case pdf_literal_node: + show_pdf_literal(p); + break; + case pdf_colorstack_node: + tprint_esc("pdfcolorstack "); + print_int(pdf_colorstack_stack(p)); + switch (pdf_colorstack_cmd(p)) { + case colorstack_set: + tprint(" set "); + break; + case colorstack_push: + tprint(" push "); + break; + case colorstack_pop: + tprint(" pop"); + break; + case colorstack_current: + tprint(" current"); + break; + default: + confusion("colorstack"); + break; + } + if (pdf_colorstack_cmd(p) <= colorstack_data) + print_mark(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + tprint_esc("pdfsetmatrix"); + print_mark(pdf_setmatrix_data(p)); + break; + case pdf_save_node: + tprint_esc("pdfsave"); + break; + case pdf_restore_node: + tprint_esc("pdfrestore"); + break; + case pdf_link_state_node: + tprint_esc("pdflinkstate "); + print_int(pdf_link_state(p)); + break; + case pdf_refobj_node: + tprint_esc("pdfrefobj"); + if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) { + if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { + tprint(" attr"); + lua_rawgeti(Luas, LUA_REGISTRYINDEX, obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p))); + print_char(' '); + tprint((const char *) lua_tostring(Luas, -1)); + lua_pop(Luas, 1); + } + tprint(" stream"); + } + if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p))) + tprint(" file"); + if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { + lua_rawgeti(Luas, LUA_REGISTRYINDEX, obj_obj_data(static_pdf, pdf_obj_objnum(p))); + print_char(' '); + tprint((const char *) lua_tostring(Luas, -1)); + lua_pop(Luas, 1); + } + break; + case pdf_annot_node: + tprint_esc("pdfannot"); + show_pdftex_whatsit_rule_spec(p); + print_mark(pdf_annot_data(p)); + break; + case pdf_start_link_node: + tprint_esc("pdfstartlink"); + show_pdftex_whatsit_rule_spec(p); + if (pdf_link_attr(p) != null) { + tprint(" attr"); + print_mark(pdf_link_attr(p)); + } + tprint(" action"); + if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) { + tprint(" user"); + print_mark(pdf_action_tokens(pdf_link_action(p))); + return; + } + if (pdf_action_file(pdf_link_action(p)) != null) { + tprint(" file"); + print_mark(pdf_action_file(pdf_link_action(p))); + } + switch (pdf_action_type(pdf_link_action(p))) { + case pdf_action_goto: + if (pdf_action_named_id(pdf_link_action(p)) > 0) { + tprint(" goto name"); + print_mark(pdf_action_id(pdf_link_action(p))); + } else { + tprint(" goto num"); + print_int(pdf_action_id(pdf_link_action(p))); + } + break; + case pdf_action_page: + tprint(" page"); + print_int(pdf_action_id(pdf_link_action(p))); + print_mark(pdf_action_tokens(pdf_link_action(p))); + break; + case pdf_action_thread: + if (pdf_action_named_id(pdf_link_action(p)) > 0) { + tprint(" thread name"); + print_mark(pdf_action_id(pdf_link_action(p))); + } else { + tprint(" thread num"); + print_int(pdf_action_id(pdf_link_action(p))); + } + break; + default: + normal_error("pdf backend", "unknown action type for link"); + break; + } + break; + case pdf_end_link_node: + tprint_esc("pdfendlink"); + break; + case pdf_dest_node: + tprint_esc("pdfdest"); + if (pdf_dest_named_id(p) > 0) { + tprint(" name"); + print_mark(pdf_dest_id(p)); + } else { + tprint(" num"); + print_int(pdf_dest_id(p)); + } + print_char(' '); + switch (pdf_dest_type(p)) { + case pdf_dest_xyz: + tprint("xyz"); + if (pdf_dest_xyz_zoom(p) != null) { + tprint(" zoom"); + print_int(pdf_dest_xyz_zoom(p)); + } + break; + case pdf_dest_fitbh: + tprint("fitbh"); + break; + case pdf_dest_fitbv: + tprint("fitbv"); + break; + case pdf_dest_fitb: + tprint("fitb"); + break; + case pdf_dest_fith: + tprint("fith"); + break; + case pdf_dest_fitv: + tprint("fitv"); + break; + case pdf_dest_fitr: + tprint("fitr"); + show_pdftex_whatsit_rule_spec(p); + break; + case pdf_dest_fit: + tprint("fit"); + break; + default: + tprint("unknown!"); + break; + } + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (subtype(p) == pdf_thread_node) + tprint_esc("pdfthread"); + else + tprint_esc("pdfstartthread"); + tprint("("); + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); + if (pdf_thread_attr(p) != null) { + tprint(" attr"); + print_mark(pdf_thread_attr(p)); + } + if (pdf_thread_named_id(p) > 0) { + tprint(" name"); + print_mark(pdf_thread_id(p)); + } else { + tprint(" num"); + print_int(pdf_thread_id(p)); + } + break; + case pdf_end_thread_node: + tprint_esc("pdfendthread"); + break; + default: + break; + } +} + +/*tex + + Now we are ready for |show_node_list| itself. This procedure has been written + to be ``extra robust'' in the sense that it should not crash or get into a + loop even if the data structures have been messed up by bugs in the rest of + the program. You can safely call its parent routine |show_box(p)| for + arbitrary values of |p| when you are debugging \TeX. However, in the presence + of bad data, the procedure may fetch a |memory_word| whose variant is + different from the way it was stored; for example, it might try to read + |mem[p].hh| when |mem[p]| contains a scaled integer, if |p| is a pointer that + has been clobbered or chosen at random. + +*/ + +#define node_list_display(A) do { \ + append_char('.'); \ + show_node_list(A); \ + flush_char(); \ +} while (0) + +#define node_list_display_x(A,B) do { \ + if ((B) != null) { \ + append_char('.'); \ + append_char(A); \ + append_char(' '); \ + show_node_list(B); \ + flush_char(); \ + flush_char(); \ + flush_char(); \ + } \ +} while (0) + +/*tex Print a node list symbolically: */ + +void show_node_list(int p) +{ + /*tex The number of items already printed at this level: */ + int n = 0; + halfword w; + /*tex A glue ratio, as a floating point number: */ + real g; + if ((int) cur_length > depth_threshold) { + if (p > null) { + /*tex Indicate that there's been some truncation. */ + tprint(" []"); + } + return; + } + while (p != null) { + print_ln(); + print_current_string(); + /*tex Display the nesting history. */ + if (tracing_online_par < -2) + print_int(p); + incr(n); + if (n > breadth_max) { + /*tex Time to stop. */ + tprint("etc."); + return; + } + /*tex Display node |p|. */ + if (is_char_node(p)) { + print_font_and_char(p); + if (is_ligature(p)) { + /*tex Display ligature |p|. */ + tprint(" (ligature "); + if (is_leftboundary(p)) + print_char('|'); + font_in_short_display = font(p); + short_display(lig_ptr(p)); + if (is_rightboundary(p)) + print_char('|'); + print_char(')'); + } + } else { + switch (type(p)) { + case hlist_node: + case vlist_node: + case unset_node: + /*tex Display box |p|. */ + if (type(p) == hlist_node) + tprint_esc("h"); + else if (type(p) == vlist_node) + tprint_esc("v"); + else + tprint_esc("unset"); + tprint("box("); + print_scaled(height(p)); + print_char('+'); + print_scaled(depth(p)); + tprint(")x"); + print_scaled(width(p)); + if (type(p) == unset_node) { + /*tex Display special fields of the unset node |p|. */ + if (span_count(p) != min_quarterword) { + tprint(" ("); + print_int(span_count(p) + 1); + tprint(" columns)"); + } + if (glue_stretch(p) != 0) { + tprint(", stretch "); + print_glue(glue_stretch(p), glue_order(p), NULL); + } + if (glue_shrink(p) != 0) { + tprint(", shrink "); + print_glue(glue_shrink(p), glue_sign(p), NULL); + } + } else { + /*tex + + Display the value of |glue_set(p)|. The code will + have to change in this place if |glue_ratio| is a + structured type instead of an ordinary |real|. Note + that this routine should avoid arithmetic errors even + if the |glue_set| field holds an arbitrary random + value. The following code assumes that a properly + formed nonzero |real| number has absolute value + $2^{20}$ or more when it is regarded as an integer; + this precaution was adequate to prevent floating + point underflow on the author's computer. + + */ + g = (real) (glue_set(p)); + if ((g != 0.0) && (glue_sign(p) != normal)) { + tprint(", glue set "); + if (glue_sign(p) == shrinking) + tprint("- "); + if (g > 20000.0 || g < -20000.0) { + if (g > 0.0) + print_char('>'); + else + tprint("< -"); + print_glue(20000 * unity, glue_order(p), NULL); + } else { + print_glue(round(unity * g), glue_order(p), NULL); + } + } + if (shift_amount(p) != 0) { + tprint(", shifted "); + print_scaled(shift_amount(p)); + } + tprint(", direction "); + print_dir_par(box_dir(p)); + } + /*tex Recursive call: */ + node_list_display(list_ptr(p)); + break; + case rule_node: + /*tex Display rule |p|. */ + if (subtype(p) == normal_rule) { + tprint_esc("rule("); + } else if (subtype(p) == empty_rule) { + tprint_esc("norule("); + } else if (subtype(p) == user_rule) { + tprint_esc("userrule("); + } else if (subtype(p) == box_rule) { + tprint_esc("box("); + } else if (subtype(p) == image_rule) { + tprint_esc("image("); + } + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); + break; + case ins_node: + /*tex Display insertion |p|. */ + tprint_esc("insert"); + print_int(subtype(p)); + tprint(", natural size "); + print_scaled(height(p)); + tprint("; split("); + print_spec(split_top_ptr(p), NULL); + print_char(','); + print_scaled(depth(p)); + tprint("); float cost "); + print_int(float_cost(p)); + /*tex Recursive call. */ + node_list_display(ins_ptr(p)); + break; + case dir_node: + if (subtype(p) == cancel_dir) { + tprint_esc("enddir"); + } else { + tprint_esc("begindir"); + } + print_char(' '); + print_dir_par(dir_dir(p)); + break; + case local_par_node: + tprint_esc("localpar"); + append_char('.'); + print_ln(); + print_current_string(); + tprint_esc("localinterlinepenalty"); + print_char('='); + print_int(local_pen_inter(p)); + print_ln(); + print_current_string(); + tprint_esc("localbrokenpenalty"); + print_char('='); + print_int(local_pen_broken(p)); + print_ln(); + print_current_string(); + tprint_esc("localleftbox"); + if (local_box_left(p) == null) { + tprint("=null"); + } else { + append_char('.'); + show_node_list(local_box_left(p)); + decr(cur_length); + } + print_ln(); + print_current_string(); + tprint_esc("localrightbox"); + if (local_box_right(p) == null) { + tprint("=null"); + } else { + append_char('.'); + show_node_list(local_box_right(p)); + decr(cur_length); + } + decr(cur_length); + break; + case boundary_node: + if (subtype(p)==0) { + tprint_esc("noboundary"); + } else { + switch (subtype(p)) { + case 1: + tprint_esc("boundary"); + break; + case 2: + tprint_esc("protrusionboundary"); + break; + case 3: + tprint_esc("wordboundary"); + break; + default: + tprint_esc("boundary"); + print_char(':'); + print_int(subtype(p)); + break; + } + print_char('='); + print_int(boundary_value(p)); + } + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + show_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + show_node_wrapup_dvi(p); + } else { + show_node_wrapup_core(p); + } + break; + case glue_node: + /*tex Display glue |p|. */ + if (subtype(p) >= a_leaders) { + /*tex Display leaders |p|. */ + tprint_esc(""); + switch (subtype(p)) { + case a_leaders: + break; + case c_leaders: + print_char('c'); + break; + case x_leaders: + print_char('x'); + break; + case g_leaders: + print_char('g'); + break; + default: + normal_warning("nodes","weird glue leader subtype ignored"); + } + tprint("leaders "); + print_spec(p, NULL); + /*tex Recursive call: */ + node_list_display(leader_ptr(p)); + } else { + tprint_esc("glue"); + if (subtype(p) != normal) { + print_char('('); + if ((subtype(p) - 1) < thin_mu_skip_code) { + print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1)); + } else if (subtype(p) < cond_math_glue) { + print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1)); + } else if (subtype(p) == cond_math_glue) { + tprint_esc("nonscript"); + } else { + tprint_esc("mskip"); + } + print_char(')'); + } + if (subtype(p) != cond_math_glue) { + print_char(' '); + if (subtype(p) < cond_math_glue) + print_spec(p, NULL); + else + print_spec(p, "mu"); + } + } + break; + case margin_kern_node: + tprint_esc("kern"); + print_scaled(width(p)); + if (subtype(p) == left_side) + tprint(" (left margin)"); + else + tprint(" (right margin)"); + break; + case kern_node: + /*tex Display kern |p| */ + if (subtype(p) != mu_glue) { + tprint_esc("kern"); + /*tex + + \starttyping + if (subtype(p) != normal) { + print_char(' '); + } + \stoptyping + + */ + print_scaled(width(p)); + if (subtype(p) == font_kern) + tprint(" (font)"); + else if (subtype(p) == italic_kern) + tprint(" (italic)"); + else if (subtype(p) == accent_kern) + tprint(" (accent)"); + } else { + tprint_esc("mkern"); + print_scaled(width(p)); + tprint("mu"); + } + break; + case math_node: + /*tex Display math node |p|. */ + tprint_esc("math"); + if (subtype(p) == before) + tprint("on"); + else + tprint("off"); + if (!glue_is_zero(p)) { + tprint(", glued "); + print_spec(p, NULL); + } else if (surround(p) != 0) { + tprint(", surrounded "); + print_scaled(surround(p)); + } + break; + case penalty_node: + /*tex Display penalty |p|. */ + tprint_esc("penalty "); + print_int(penalty(p)); + break; + case disc_node: + /*tex + + Display discretionary |p|. The |post_break| list of a + discretionary node is indicated by a prefixed + `\.{\char'174}' instead of the `\..' before the + |pre_break| list. + + We're not compatible anyway so ... + + \starttyping + tprint_esc("discretionary"); + print_int(disc_penalty(p)); + print_char('|'); + if (vlink(no_break(p)) != null) { + tprint(" replacing "); + node_list_display(vlink(no_break(p))); + } + node_list_display(vlink(pre_break(p))); + append_char('|'); + show_node_list(vlink(post_break(p))); + flush_char(); + \stoptyping + + has become: + + */ + tprint_esc("discretionary"); + tprint(" (penalty "); + print_int(disc_penalty(p)); + print_char(')'); + node_list_display_x('<',vlink(pre_break(p))); + node_list_display_x('>',vlink(post_break(p))); + node_list_display_x('=',vlink(no_break(p))); + break; + case mark_node: + /*tex Display mark |p|. */ + tprint_esc("mark"); + if (mark_class(p) != 0) { + print_char('s'); + print_int(mark_class(p)); + } + print_mark(mark_ptr(p)); + break; + case adjust_node: + /*tex Display adjustment |p|. */ + tprint_esc("vadjust"); + if (subtype(p) != 0) + tprint(" pre "); + /*tex Recursive call. */ + node_list_display(adjust_ptr(p)); + break; + case glue_spec_node: + tprint("<glue_spec "); + print_spec(p, NULL); + tprint(">"); + break; + default: + show_math_node(p); + break; + } + } + p = vlink(p); + } +} + +/*tex + + This routine finds the 'base' width of a horizontal box, using the same logic + that \TeX82 used for \.{\\predisplaywidth}. + +*/ + +static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width) +{ + /*tex increment to |v| */ + scaled d; + /*tex calculated |size| */ + scaled w = -max_dimen; + /*tex |w| plus possible glue amount */ + scaled v = initial_width; + while (p != null) { + if (is_char_node(p)) { + d = glyph_width(p); + goto FOUND; + } + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + d = width(p); + goto FOUND; + break; + case margin_kern_node: + d = width(p); + break; + case kern_node: + d = width(p); + break; + case disc_node: + /*tex At the end of the line we should actually take the |pre|. */ + if (no_break(p) != null) { + d = get_actual_box_width(r,vlink_no_break(p),0); + if (d <= -max_dimen || d >= max_dimen) { + d = 0; + } + } else { + d = 0; + } + goto FOUND; + break; + case math_node: + /*tex Begin mathskip code. */ + if (glue_is_zero(p)) { + d = surround(p); + break; + } else { + /*tex Fall through. */ + } + /*tex End mathskip code. */ + case glue_node: + /*tex + + We need to be careful that |w|, |v|, and |d| do not depend on + any |glue_set| values, since such values are subject to + system-dependent rounding. System-dependent numbers are not + allowed to infiltrate parameters like |pre_display_size|, + since \TeX82 is supposed to make the same decisions on all + machines. + + */ + d = width(p); + if (glue_sign(r) == stretching) { + if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0)) + v = max_dimen; + } else if (glue_sign(r) == shrinking) { + if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0)) + v = max_dimen; + } + if (subtype(p) >= a_leaders) + goto FOUND; + break; + default: + d = 0; + break; + } + if (v < max_dimen) + v = v + d; + goto NOT_FOUND; + FOUND: + if (v < max_dimen) { + v = v + d; + w = v; + } else { + w = max_dimen; + break; + } + NOT_FOUND: + p = vlink(p); + } + return w; +} + +pointer actual_box_width(pointer r, scaled base_width) +{ + /*tex + + Often this is the same as: + + \starttyping + return + shift_amount(r) + base_width + + natural_sizes(list_ptr(r),null,(glue_ratio) glue_set(r),glue_sign(r),glue_order(r),box_dir(r)); + \stoptyping + */ + return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width); +} + +halfword tail_of_list(halfword p) +{ + halfword q = p; + while (vlink(q) != null) + q = vlink(q); + return q; +} + +int var_used; + +/*tex + + Attribute lists need two extra globals to increase processing efficiency. + |max_used_attr| limits the test loop that checks for set attributes, and + |attr_list_cache| contains a pointer to an already created attribute list. It + is set to the special value |cache_disabled| when the current value can no + longer be trusted: after an assignment to an attribute register, and after a + group has ended. + +*/ + +/*tex The maximum assigned attribute id: */ + +int max_used_attr; +halfword attr_list_cache; + +/*tex + + From the computer's standpoint, \TeX's chief mission is to create horizontal + and vertical lists. We shall now investigate how the elements of these lists + are represented internally as nodes in the dynamic memory. + + A horizontal or vertical list is linked together by |link| fields in the + first word of each node. Individual nodes represent boxes, glue, penalties, + or special things like discretionary hyphens; because of this variety, some + nodes are longer than others, and we must distinguish different kinds of + nodes. We do this by putting a `|type|' field in the first word, together + with the link and an optional `|subtype|'. + + Character nodes appear only in horizontal lists, never in vertical lists. + + An |hlist_node| stands for a box that was made from a horizontal list. Each + |hlist_node| is seven words long, and contains the following fields (in + addition to the mandatory |type| and |link|, which we shall not mention + explicitly when discussing the other node types): The |height| and |width| + and |depth| are scaled integers denoting the dimensions of the box. There is + also a |shift_amount| field, a scaled integer indicating how much this box + should be lowered (if it appears in a horizontal list), or how much it should + be moved to the right (if it appears in a vertical list). There is a + |list_ptr| field, which points to the beginning of the list from which this + box was fabricated; if |list_ptr| is |null|, the box is empty. Finally, there + are three fields that represent the setting of the glue: |glue_set(p)| is a + word of type |glue_ratio| that represents the proportionality constant for + glue setting; |glue_sign(p)| is |stretching| or |shrinking| or |normal| + depending on whether or not the glue should stretch or shrink or remain + rigid; and |glue_order(p)| specifies the order of infinity to which glue + setting applies (|normal|, |sfi|, |fil|, |fill|, or |filll|). The |subtype| + field is not used. + + The |new_null_box| function returns a pointer to an |hlist_node| in which all + subfields have the values corresponding to `\.{\\hbox\{\}}'. The |subtype| + field is set to |min_quarterword|, since that's the desired |span_count| + value if this |hlist_node| is changed to an |unset_node|. + +*/ + +/*tex Create a new box node. */ + +halfword new_null_box(void) +{ + halfword p = new_node(hlist_node, min_quarterword); + box_dir(p) = text_direction_par; + return p; +} + +/*tex + + A |vlist_node| is like an |hlist_node| in all respects except that it + contains a vertical list. + + A |rule_node| stands for a solid black rectangle; it has |width|, |depth|, + and |height| fields just as in an |hlist_node|. However, if any of these + dimensions is $-2^{30}$, the actual value will be determined by running the + rule up to the boundary of the innermost enclosing box. This is called a + ``running dimension.'' The |width| is never running in an hlist; the |height| + and |depth| are never running in a~vlist. + + A new rule node is delivered by the |new_rule| function. It makes all the + dimensions ``running,'' so you have to change the ones that are not allowed + to run. + +*/ + +halfword new_rule(int s) +{ + halfword p = new_node(rule_node,s); + return p; +} + +/*tex + + Insertions are represented by |ins_node| records, where the |subtype| + indicates the corresponding box number. For example, `\.{\\insert 250}' leads + to an |ins_node| whose |subtype| is |250+min_quarterword|. The |height| field + of an |ins_node| is slightly misnamed; it actually holds the natural height + plus depth of the vertical list being inserted. The |depth| field holds the + |split_max_depth| to be used in case this insertion is split, and the + |split_top_ptr| points to the corresponding |split_top_skip|. The + |float_cost| field holds the |floating_penalty| that will be used if this + insertion floats to a subsequent page after a split insertion of the same + class. There is one more field, the |ins_ptr|, which points to the beginning + of the vlist for the insertion. + + A |mark_node| has a |mark_ptr| field that points to the reference count of a + token list that contains the user's \.{\\mark} text. In addition there is a + |mark_class| field that contains the mark class. + + An |adjust_node|, which occurs only in horizontal lists, specifies material + that will be moved out into the surrounding vertical list; i.e., it is used + to implement \TeX's `\.{\\vadjust}' operation. The |adjust_ptr| field points + to the vlist containing this material. + + A |glyph_node|, which occurs only in horizontal lists, specifies a glyph in a + particular font, along with its attribute list. Older versions of \TeX\ could + use token memory for characters, because the font,char combination would fit + in a single word (both values were required to be strictly less than + $2^{16}$). In LuaTeX, room is needed for characters that are larger than + that, as well as a pointer to a potential attribute list, and the two + displacement values. + + In turn, that made the node so large that it made sense to merge ligature + glyphs as well, as that requires only one extra pointer. A few extra classes + of glyph nodes will be introduced later. The unification of all those types + makes it easier to manipulate lists of glyphs. The subtype differentiates + various glyph kinds. + + First, here is a function that returns a pointer to a glyph node for a given + glyph in a given font. If that glyph doesn't exist, |null| is returned + instead. Nodes of this subtype are directly created only for accents and + their base (through |make_accent|), and math nucleus items (in the conversion + from |mlist| to |hlist|). + +*/ + +halfword new_glyph(int f, int c) +{ + halfword p = null; + if ((f == 0) || (char_exists(f, c))) { + p = new_glyph_node(); + set_to_glyph(p); + font(p) = f; + character(p) = c; + } + return p; +} + +/*tex + + A subset of the glyphs nodes represent ligatures: characters fabricated from + the interaction of two or more actual characters. The characters that + generated the ligature have not been forgotten, since they are needed for + diagnostic messages; the |lig_ptr| field points to a linked list of character + nodes for all original characters that have been deleted. (This list might be + empty if the characters that generated the ligature were retained in other + nodes.) + + The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if the + original source of the ligature included implicit left and/or right + boundaries. These nodes are created by the C function |new_ligkern|. + + A third general type of glyphs could be called a character, as it only + appears in lists that are not yet processed by the ligaturing and kerning + steps of the program. + + |main_control| inserts these, and they are later converted to + |subtype_normal| by |new_ligkern|. + +*/ + +quarterword norm_min(int h) +{ + if (h <= 0) + return 1; + else if (h >= 255) + return 255; + else + return (quarterword) h; +} + +halfword new_char(int f, int c) +{ + halfword p; + p = new_glyph_node(); + set_to_character(p); + font(p) = f; + character(p) = c; + lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par); + return p; +} + +/*tex + + Left and right ghost glyph nodes are the result of \.{\\leftghost} and + \.{\\rightghost}, respectively. They are going to be removed by + |new_ligkern|, at the end of which they are no longer needed. + + Here are a few handy helpers used by the list output routines. The yoffset + has some history but we now give some control over its treatment: + + 0: what we had before + 1: compensate height and depth + 2: compensate height and depth, take max + 3: we keep height and depth + + The modes are controlled by a variable because we need to retain downward + compatibility. + +*/ + +scaled glyph_width(halfword p) +{ + scaled w = char_width(font(p), character(p)); + return w; +} + +scaled glyph_height(halfword p) +{ + scaled h = char_height(font(p), character(p)); + scaled y = y_displace(p); + if ((glyph_dimensions_par == 0) || (glyph_dimensions_par == 1) || (glyph_dimensions_par == 2 && y > 0)) + h += y; + if (h < 0) + h = 0; + return h; +} + +scaled glyph_depth(halfword p) /* not used */ +{ + scaled d = char_depth(font(p), character(p)); + scaled y = y_displace(p); + if ((glyph_dimensions_par == 0 && y > 0) || (glyph_dimensions_par == 1) || (glyph_dimensions_par == 2 && y < 0)) + d -= y; + if (d < 0) + d = 0; + return d; +} + +/*tex + + A |disc_node|, which occurs only in horizontal lists, specifies a + ``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the + text that starts at |pre_break(p)| will precede the break, the text that + starts at |post_break(p)| will follow the break, and text that appears in + |no_break(p)| nodes will be ignored. For example, an ordinary discretionary + hyphen, indicated by `\.{\\-}', yields a |disc_node| with |pre_break| + pointing to a |char_node| containing a hyphen, |post_break=null|, and + |no_break=null|. + + If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for + this break. Otherwise the |hyphen_penalty| will be charged. The texts will + actually be substituted into the list by the line-breaking algorithm if it + decides to make the break, and the discretionary node will disappear at that + time; thus, the output routine sees only discretionaries that were not + chosen. + +*/ + +halfword new_disc(void) +{ + halfword p = new_node(disc_node, 0); + disc_penalty(p) = hyphen_penalty_par; + return p; +} + +/* + + A |whatsit_node| is a wild card reserved for extensions to \TeX. The + |subtype| field in its first word says what `\\{whatsit}' it is, and + implicitly determines the node size (which must be 2 or more) and the format + of the remaining words. When a |whatsit_node| is encountered in a list, + special actions are invoked; knowledgeable people who are careful not to mess + up the rest of \TeX\ are able to make \TeX\ do new things by adding code at + the end of the program. For example, there might be a `\TeX nicolor' + extension to specify different colors of ink, and the whatsit node might + contain the desired parameters. + + The present implementation of \TeX\ treats the features associated with + `\.{\\write}' and `\.{\\special}' as if they were extensions, in order to + illustrate how such routines might be coded. We shall defer further + discussion of extensions until the end of this program. + + A |math_node|, which occurs only in horizontal lists, appears before and + after mathematical formulas. The |subtype| field is |before| before the + formula and |after| after it. There is a |surround| field, which represents + the amount of surrounding space inserted by \.{\\mathsurround}. + +*/ + +halfword new_math(scaled w, int s) +{ + halfword p = new_node(math_node, s); + surround(p) = w; + return p; +} + +/*tex + + \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, |rule_node|, + |ins_node|, |mark_node|, |adjust_node|, |disc_node|, |whatsit_node|, and + |math_node| are at the low end of the type codes, by permitting a break at + glue in a list if and only if the |type| of the previous node is less than + |math_node|. Furthermore, a node is discarded after a break if its type is + |math_node| or~more. + + A |glue_node| represents glue in a list. However, it is really only a pointer + to a separate glue specification, since \TeX\ makes use of the fact that many + essentially identical nodes of glue are usually present. If |p| points to a + |glue_node|, |glue_ptr(p)| points to another packet of words that specify the + stretch and shrink components, etc. + + Glue nodes also serve to represent leaders; the |subtype| is used to + distinguish between ordinary glue (which is called |normal|) and the three + kinds of leaders (which are called |a_leaders|, |c_leaders|, and + |x_leaders|). The |leader_ptr| field points to a rule node or to a box node + containing the leaders; it is set to |null| in ordinary glue nodes. + + Many kinds of glue are computed from \TeX's ``skip'' parameters, and it is + helpful to know which parameter has led to a particular glue node. Therefore + the |subtype| is set to indicate the source of glue, whenever it originated + as a parameter. We will be defining symbolic names for the parameter numbers + later (e.g., |line_skip_code=0|, |baseline_skip_code=1|, etc.); it suffices + for now to say that the |subtype| of parametric glue will be the same as the + parameter number, plus~one. + + In math formulas there are two more possibilities for the |subtype| in a glue + node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu} + instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}' + feature that cancels the glue node immediately following if it appears in a + subscript. + + A glue specification has a halfword reference count in its first word, + representing |null| plus the number of glue nodes that point to it (less + one). Note that the reference count appears in the same position as the + |link| field in list nodes; this is the field that is initialized to |null| + when a node is allocated, and it is also the field that is flagged by + |empty_flag| in empty nodes. + + Glue specifications also contain three |scaled| fields, for the |width|, + |stretch|, and |shrink| dimensions. Finally, there are two one-byte fields + called |stretch_order| and |shrink_order|; these contain the orders of + infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|) corresponding to the + stretch and shrink values. + + Here is a function that returns a pointer to a copy of a glue spec. The + reference count in the copy is |null|, because there is assumed to be exactly + one reference to the new specification. + +*/ + +halfword new_spec(halfword q) +{ + if (q == null) { + return copy_node(zero_glue); + } else if (type(q) == glue_spec_node) { + return copy_node(q); + } else if (type(q) == glue_node) { + halfword p = copy_node(zero_glue); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; + } else { + /*tex Alternatively we can issue a warning. */ + return copy_node(zero_glue); + } +} + +/*tex + + And here's a function that creates a glue node for a given parameter + identified by its code number; for example, |new_param_glue(line_skip_code)| + returns a pointer to a glue node for the current \.{\\lineskip}. + +*/ + +halfword new_param_glue(int n) +{ + halfword p = new_node(glue_node, n + 1); + halfword q = glue_par(n); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; +} + +/*tex + + Glue nodes that are more or less anonymous are created by |new_glue|, whose + argument points to a glue specification. + +*/ + +halfword new_glue(halfword q) +{ + halfword p = new_node(glue_node, normal); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; +} + +/*tex + + Still another subroutine is needed: This one is sort of a combination of + |new_param_glue| and |new_glue|. It creates a glue node for one of the + current glue parameters, but it makes a fresh copy of the glue specification, + since that specification will probably be subject to change, while the + parameter will stay put. + + The global variable |temp_ptr| is set to the address of the new spec. + +*/ + +halfword new_skip_param(int n) +{ + halfword p = new_node(glue_node, n + 1); + halfword q = glue_par(n); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; +} + +/*tex + + A |kern_node| has a |width| field to specify a (normally negative) amount of + spacing. This spacing correction appears in horizontal lists between letters + like A and V when the font designer said that it looks better to move them + closer together or further apart. A kern node can also appear in a vertical + list, when its `|width|' denotes additional spacing in the vertical + direction. The |subtype| is either |normal| (for kerns inserted from font + information or math mode calculations) or |explicit| (for kerns inserted from + \.{\\kern} and \.{\\/} commands) or |acc_kern| (for kerns inserted from + non-math accents) or |mu_glue| (for kerns inserted from \.{\\mkern} + specifications in math formulas). + + The |new_kern| function creates a kern node having a given width. + +*/ + +halfword new_kern(scaled w) +{ + halfword p = new_node(kern_node, normal); + width(p) = w; + return p; +} + +/*tex + + A |penalty_node| specifies the penalty associated with line or page breaking, + in its |penalty| field. This field is a fullword integer, but the full range + of integer values is not used: Any penalty |>=10000| is treated as infinity, + and no break will be allowed for such high values. Similarly, any penalty + |<=-10000| is treated as negative infinity, and a break will be forced. + + Anyone who has been reading the last few sections of the program will be able + to guess what comes next. + +*/ + +halfword new_penalty(int m, int s) +{ + halfword p = new_node(penalty_node, 0); + penalty(p) = m; + subtype(p) = s; + return p; +} + +/*tex + + You might think that we have introduced enough node types by now. Well, + almost, but there is one more: An |unset_node| has nearly the same format as + an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} or + \.{\\valign} that are not yet in their final form, since the box dimensions + are their ``natural'' sizes before any glue adjustment has been made. The + |glue_set| word is not present; instead, we have a |glue_stretch| field, + which contains the total stretch of order |glue_order| that is present in the + hlist or vlist being boxed. Similarly, the |shift_amount| field is replaced + by a |glue_shrink| field, containing the total shrink of order |glue_sign| + that is present. The |subtype| field is called |span_count|; an unset box + typically contains the data for |qo(span_count)+1| columns. Unset nodes will + be changed to box nodes when alignment is completed. + + In fact, there are still more types coming. When we get to math formula + processing we will see that a |style_node| has |type=14|; and a number of + larger type codes will also be defined, for use in math mode only. + + Warning: If any changes are made to these data structure layouts, such as + changing any of the node sizes or even reordering the words of nodes, the + |copy_node_list| procedure and the memory initialization code below may have + to be changed. Such potentially dangerous parts of the program are listed in + the index under `data structure assumptions'. However, other references to + the nodes are made symbolically in terms of the \.{WEB} macro definitions + above, so that format changes will leave \TeX's other algorithms intact. + +*/ + +halfword make_local_par_node(int mode) +{ + int callback_id; + halfword q; + halfword p = new_node(local_par_node,mode); + local_pen_inter(p) = local_inter_line_penalty_par; + local_pen_broken(p) = local_broken_penalty_par; + if (local_left_box_par != null) { + q = copy_node_list(local_left_box_par); + local_box_left(p) = q; + local_box_left_width(p) = width(local_left_box_par); + } + if (local_right_box_par != null) { + q = copy_node_list(local_right_box_par); + local_box_right(p) = q; + local_box_right_width(p) = width(local_right_box_par); + } + local_par_dir(p) = par_direction_par; + /*tex Callback with node passed: */ + callback_id = callback_defined(insert_local_par_callback); + if (callback_id > 0) { + /*tex Todo: use general mechanism/wrapper. */ + int sfix = lua_gettop(Luas); + if (!get_callback(Luas, callback_id)) { + lua_settop(Luas, sfix); + } else { + int i; + nodelist_to_lua(Luas, p); + lua_push_local_par_mode(Luas,mode) + /*tex 2 arg, 0 result */ + i = lua_pcall(Luas, 2, 0, 0); + if (i != 0) { + lua_gc(Luas, LUA_GCCOLLECT, 0); + Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + } + lua_settop(Luas, sfix); + } + } + return p; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.h b/Build/source/texk/web2c/luatexdir/tex/texnodes.h new file mode 100644 index 00000000000..9742ca4966c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.h @@ -0,0 +1,1188 @@ +/* texnodes.h + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#include <stdarg.h> + +#ifndef __NODES_H__ +# define __NODES_H__ + +# define varmemcast(a) (memory_word *)(a) + +extern memory_word *volatile varmem; +extern halfword var_mem_max; + +extern halfword get_node(int s); +extern void free_node(halfword p, int s); +extern void init_node_mem(int s); +extern void dump_node_mem(void); +extern void undump_node_mem(void); + +# define max_halfword 0x3FFFFFFF +# define max_dimen 0x3FFFFFFF +# ifndef null +# define null 0 +# endif +# define null_flag -0x40000000 +# define zero_glue 0 +# define normal 0 + +# define vinfo(a) varmem[(a)].hh.v.LH +# define vlink(a) varmem[(a)].hh.v.RH +# define type(a) varmem[(a)].hh.u.B0 +# define subtype(a) varmem[(a)].hh.u.B1 +# define node_attr(a) vinfo((a)+1) +# define alink(a) vlink((a)+1) + +# define node_size(a) varmem[(a)].hh.v.LH + +# define rlink(a) vlink((a)+1) /* aka alink() */ +# define tlink(a) vinfo((a)+1) /* overlaps with node_attr() */ + +/* really special head node pointers that only need links */ + +# define temp_node_size 2 + +/* attribute lists */ + +# define UNUSED_ATTRIBUTE -0x7FFFFFFF /* as low as it goes */ + +/* it is convenient to have attribute list nodes and attribute node + * be the same size + */ + +# define attribute_node_size 2 +# define cache_disabled max_halfword + +# define attr_list_ref(a) vinfo((a)+1) /* the reference count */ +# define attribute_id(a) vinfo((a)+1) +# define attribute_value(a) vlink((a)+1) + +# define assign_attribute_ref(n,p) do { \ + node_attr(n) = p;attr_list_ref(p)++; \ +} while (0) + +# define add_node_attr_ref(a) do { \ + if (a!=null) attr_list_ref((a))++; \ +} while (0) + +# define replace_attribute_list(a,b) do { \ + delete_attribute_ref(node_attr(a)); \ + node_attr(a)=b; \ +} while (0) + +extern void update_attribute_cache(void); +extern halfword copy_attribute_list(halfword n); +extern halfword do_set_attribute(halfword p, int i, int val); + +# define width_offset 2 +# define depth_offset 3 +# define height_offset 4 +# define list_offset 6 + +typedef enum { + user_skip_glue, + line_skip_glue, + baseline_skip_glue, + par_skip_glue, + above_display_skip_glue, + below_display_skip_glue, + above_display_short_skip_glue, + below_display_short_skip_glue, + left_skip_glue, + right_skip_glue, + top_skip_glue, + split_top_skip_glue, + tab_skip_glue, + space_skip_glue, + xspace_skip_glue, + par_fill_skip_glue, + math_skip_glue, + thin_mu_skip_glue, + med_mu_skip_glue, + thick_mu_skip_glue, + /* math */ + cond_math_glue = 98, /* special |subtype| to suppress glue in the next node */ + mu_glue, /* |subtype| for math glue */ + /* leaders */ + a_leaders, /* |subtype| for aligned leaders */ + c_leaders, /* |subtype| for centered leaders */ + x_leaders, /* |subtype| for expanded leaders */ + g_leaders /* |subtype| for global (page) leaders */ +} glue_subtypes; + +/* normal nodes */ + +# define inf_bad 10000 /* infinitely bad value */ +# define inf_penalty inf_bad /* ``infinite'' penalty value */ +# define eject_penalty -(inf_penalty) /* ``negatively infinite'' penalty value */ + +# define penalty_node_size 3 +# define penalty(a) vlink((a)+2) + +typedef enum { + user_penalty, + linebreak_penalty, /* includes widow, club, broken ect */ + line_penalty, + word_penalty, + final_penalty, + noad_penalty, + before_display_penalty, + after_display_penalty, + equation_number_penalty, +} penalty_subtypes ; + +/* +# define glue_node_size 4 +# define glue_ptr(a) vinfo((a)+2) +# define leader_ptr(a) vlink((a)+2) +# define synctex_tag_glue(a) vinfo((a)+3) +# define synctex_line_glue(a) vlink((a)+3) +*/ + +# define glue_node_size 7 +# define glue_spec_size 5 +/* define width(a) vinfo((a)+2) */ +/* define leader_ptr(a) vlink((a)+2) */ +# define shrink(a) vinfo((a)+3) +# define stretch(a) vlink((a)+3) +# define stretch_order(a) vinfo((a)+4) +# define shrink_order(a) vlink((a)+4) +# define leader_ptr(a) vlink((a)+5) /* should be in vlink((a)+2) but fails */ +# define synctex_tag_glue(a) vinfo((a)+6) +# define synctex_line_glue(a) vlink((a)+6) + +#define glue_is_zero(p) \ + ((p == null) || (width(p) == 0 && stretch(p) == 0 && shrink(p) == 0)) + +#define glue_is_positive(p) \ + ((p == null) || (width(p) > 0)) + +#define reset_glue_to_zero(p) \ + if (p != null) { \ + width(p) = 0; \ + stretch(p) = 0; \ + shrink(p) = 0; \ + stretch_order(p) = 0; \ + shrink_order(p) = 0; \ + } + +#define copy_glue_values(p,q) \ + if (q == null) { \ + width(p) = 0; \ + stretch(p) = 0; \ + shrink(p) = 0; \ + stretch_order(p) = 0; \ + shrink_order(p) = 0; \ + } else { \ + width(p) = width(q); \ + stretch(p) = stretch(q); \ + shrink(p) = shrink(q); \ + stretch_order(p) = stretch_order(q); \ + shrink_order(p) = shrink_order(q); \ + } + + +/* + disc nodes could eventually be smaller, because the indirect + pointers are not really needed (8 instead of 10). +*/ + +typedef enum { + discretionary_disc = 0, + explicit_disc, + automatic_disc, + syllable_disc, + init_disc, /* first of a duo of syllable_discs */ + select_disc, /* second of a duo of syllable_discs */ +} discretionary_subtypes; + +# define disc_node_size 11 +# define pre_break_head(a) ((a)+5) +# define post_break_head(a) ((a)+7) +# define no_break_head(a) ((a)+9) + +# define disc_penalty(a) vlink((a)+2) +# define pre_break(a) vinfo((a)+3) +# define post_break(a) vlink((a)+3) +# define no_break(a) vlink((a)+4) /* we have vinfo((a)+4) for later usage */ + +# define vlink_pre_break(a) vlink(pre_break_head(a)) +# define vlink_post_break(a) vlink(post_break_head(a)) +# define vlink_no_break(a) vlink(no_break_head(a)) + +# define tlink_pre_break(a) tlink(pre_break_head(a)) +# define tlink_post_break(a) tlink(post_break_head(a)) +# define tlink_no_break(a) tlink(no_break_head(a)) + +typedef enum { + font_kern = 0, + explicit_kern, /* |subtype| of kern nodes from \.{\\kern} and \.{\\/} */ + accent_kern, /* |subtype| of kern nodes from accents */ + italic_kern, +} kern_subtypes; + +# define kern_node_size 5 +# define ex_kern(a) vinfo((a)+3) /* expansion factor (hz) */ +# define synctex_tag_kern(a) vinfo((a)+4) +# define synctex_line_kern(a) vlink((a)+4) + +# define box_node_size 9 + +/* todo, make an enum of this */ + +typedef enum { + unknown_list = 0, + line_list = 1, /* paragraph lines */ + hbox_list = 2, /* \.{\\hbox} */ + indent_list = 3, /* indentation box */ + align_row_list = 4, /* row from a \.{\\halign} or \.{\\valign} */ + align_cell_list = 5, /* cell from a \.{\\halign} or \.{\\valign} */ + equation_list = 6, /* display equation */ + equation_number_list = 7, /* display equation number */ + math_list_list = 8, + math_char_list = 9, + math_h_extensible_list = 10, + math_v_extensible_list = 11, + math_h_delimiter_list = 12, + math_v_delimiter_list = 13, + math_over_delimiter_list = 14, + math_under_delimiter_list = 15, + math_numerator_list = 16, + math_denominator_list = 17, + math_limits_list = 18, + math_fraction_list = 19, + math_nucleus_list = 20, + math_sup_list = 21, + math_sub_list = 22, + math_degree_list = 23, + math_scripts_list = 24, + math_over_list = 25, + math_under_list = 26, + math_accent_list = 27, + math_radical_list = 28, +} list_subtypes ; + +# define width(a) varmem[(a)+2].cint +# define depth(a) varmem[(a)+3].cint +# define height(a) varmem[(a)+4].cint +# define shift_amount(a) vlink((a)+5) +# define box_dir(a) vinfo((a)+5) +# define list_ptr(a) vlink((a)+6) +# define glue_order(a) subtype((a)+6) +# define glue_sign(a) type((a)+6) +# define glue_set(a) varmem[(a)+7].gr +# define synctex_tag_box(a) vinfo((a)+8) +# define synctex_line_box(a) vlink((a)+8) + +/* unset nodes */ + +# define glue_stretch(a) varmem[(a)+7].cint +# define glue_shrink shift_amount +# define span_count subtype + +typedef enum { + normal_rule = 0, + box_rule, + image_rule, + empty_rule, + user_rule, + math_over_rule, + math_under_rule, + math_fraction_rule, + math_radical_rule, + outline_rule, +} rule_subtypes; + +# define rule_node_size 9 +# define rule_dir(a) vlink((a)+5) +# define rule_index(a) vinfo((a)+6) +# define rule_transform(a) vlink((a)+6) +# define rule_left(a) vinfo((a)+7) +# define rule_right(a) vlink((a)+7) +# define synctex_tag_rule(a) vinfo((a)+8) +# define synctex_line_rule(a) vlink((a)+8) + +# define rule_math_size rule_index +# define rule_math_font rule_transform + +# define mark_node_size 3 +# define mark_ptr(a) vlink((a)+2) +# define mark_class(a) vinfo((a)+2) + +# define adjust_node_size 3 +# define adjust_pre subtype +# define adjust_ptr(a) vlink(a+2) + +# define glyph_node_size 7 +# define character(a) vinfo((a)+2) +# define font(a) vlink((a)+2) +# define lang_data(a) vinfo((a)+3) +# define lig_ptr(a) vlink((a)+3) +# define x_displace(a) vinfo((a)+4) +# define y_displace(a) vlink((a)+4) +# define ex_glyph(a) vinfo((a)+5) /* expansion factor (hz) */ +# define glyph_node_data(a) vlink((a)+5) +# define synctex_tag_glyph(a) vinfo((a)+6) +# define synctex_line_glyph(a) vlink((a)+6) + +# define is_char_node(a) (a!=null && type(a)==glyph_node) + +# define char_lang(a) ((const int)(signed short)(((signed int)((unsigned)lang_data(a)&0x7FFF0000)<<1)>>17)) +# define char_lhmin(a) ((const int)(((unsigned)lang_data(a) & 0x0000FF00)>>8)) +# define char_rhmin(a) ((const int)(((unsigned)lang_data(a) & 0x000000FF))) +# define char_uchyph(a) ((const int)(((unsigned)lang_data(a) & 0x80000000)>>31)) + +# define make_lang_data(a,b,c,d) (a>0 ? (1<<31): 0)+ \ + (b<<16)+ (((c>0 && c<256) ? c : 255)<<8)+(((d>0 && d<256) ? d : 255)) + +# define init_lang_data(a) lang_data(a)=256+1 + +# define set_char_lang(a,b) lang_data(a)=make_lang_data(char_uchyph(a),b,char_lhmin(a),char_rhmin(a)) +# define set_char_lhmin(a,b) lang_data(a)=make_lang_data(char_uchyph(a),char_lang(a),b,char_rhmin(a)) +# define set_char_rhmin(a,b) lang_data(a)=make_lang_data(char_uchyph(a),char_lang(a),char_lhmin(a),b) +# define set_char_uchyph(a,b) lang_data(a)=make_lang_data(b,char_lang(a),char_lhmin(a),char_rhmin(a)) + +# define margin_kern_node_size 4 +# define margin_char(a) vlink((a)+3) + +/*@# {|subtype| of marginal kerns}*/ + +typedef enum { + left_side = 0, + right_side +} margin_kern_subtypes ; + +typedef enum { + before = 0, + after +} math_subtypes ; + +# define math_node_size 7 +/* define width(a) vinfo((a)+2) */ +/* overlaps width */ +/* define shrink(a) vinfo((a)+3) */ +/* define stretch(a) vlink((a)+3) */ +/* define stretch_order(a) vinfo((a)+4) */ +/* define shrink_order(a) vlink((a)+4) */ +/* leader_ptr slot */ +# define surround(a) vinfo((a)+5) +# define synctex_tag_math(a) vinfo((a)+6) +# define synctex_line_math(a) vlink((a)+6) + +# define ins_node_size 6 +# define float_cost(a) varmem[(a)+2].cint +# define ins_ptr(a) vinfo((a)+5) +# define split_top_ptr(a) vlink((a)+5) + +# define page_ins_node_size 5 + +/* height = 4 */ + +typedef enum { + hlist_node = 0, + vlist_node, + rule_node, + ins_node, + mark_node, + adjust_node, + boundary_node, + disc_node, + whatsit_node, +#define last_preceding_break_node whatsit_node + local_par_node, + dir_node, +#define last_non_discardable_node dir_node + math_node, + glue_node, + kern_node, + penalty_node, + unset_node, + style_node, + choice_node, + simple_noad, + radical_noad, + fraction_noad, + accent_noad, + fence_noad, + math_char_node, /* kernel fields */ + sub_box_node, + sub_mlist_node, + math_text_char_node, + delim_node, /* shield fields */ + margin_kern_node, + glyph_node, /* this and below have attributes */ + align_record_node, + pseudo_file_node, + pseudo_line_node, + inserting_node, + split_up_node, + expr_node, + nesting_node, + span_node, + attribute_node, + glue_spec_node, + attribute_list_node, + temp_node, + align_stack_node, + movement_node, + if_node, + unhyphenated_node, + hyphenated_node, + delta_node, + passive_node, + shape_node, +} node_types; + +# define MAX_NODE_TYPE shape_node /* 60 */ + +/* + TH: these two defines still need checking. The node ordering in luatex is not + quite the same as in tex82, HH: but it's probably ok +*/ + +/* + + # define precedes_break(a) \ + # (type(a)<math_node && \ + # (type(a)!=whatsit_node || (subtype(a)!=dir_node && subtype(a)!=local_par_node))) + +*/ + +/* + # define precedes_break(a) (type(a)<math_node) + # define non_discardable(a) (type(a)<math_node || type(a) == dir_node || type(a) == local_par_node) +*/ + +# define precedes_break(a) (type(a)<=last_preceding_break_node) +# define precedes_kern(a) ((type(a) == kern_node) && (subtype(a) == font_kern || subtype(a) == accent_kern)) +# define precedes_dir(a) ((type(a) == dir_node) && (break_after_dir_mode_par == 1)) +# define non_discardable(a) (type(a)<=last_non_discardable_node) + +# define known_node_type(i) ( i >= 0 && i <= MAX_NODE_TYPE) + +# define last_known_node temp_node /* used by \lastnodetype */ + +# define movement_node_size 3 +# define if_node_size 2 +# define align_stack_node_size 6 +# define nesting_node_size 2 + +# define expr_node_size 3 +# define expr_type(A) type((A)+1) +# define expr_state(A) subtype((A)+1) /* enum defined in scanning.w */ +# define expr_e_field(A) vlink((A)+1) /* saved expression so far */ +# define expr_t_field(A) vlink((A)+2) /* saved term so far */ +# define expr_n_field(A) vinfo((A)+2) /* saved numerator */ + +# define span_node_size 3 +# define span_span(a) vlink((a)+1) +# define span_link(a) vinfo((a)+1) + +# define pseudo_file_node_size 2 +# define pseudo_lines(a) vlink((a)+1) + +# define nodetype_has_attributes(t) (((t)<=glyph_node) && ((t)!=unset_node)) + +# define nodetype_has_subtype(t) ((t)!=attribute_list_node && (t)!=attribute_node && (t)!=glue_spec_node) +# define nodetype_has_prev(t) nodetype_has_subtype((t)) + +/* + style and choice nodes; style nodes can be smaller, the information is encoded in + |subtype|, but choice nodes are on-the-spot converted to style nodes +*/ + +# define style_node_size 4 +# define display_mlist(a) vinfo((a)+2) /* mlist to be used in display style */ +# define text_mlist(a) vlink((a)+2) /* mlist to be used in text style */ +# define script_mlist(a) vinfo((a)+3) /* mlist to be used in script style */ +# define script_script_mlist(a) vlink((a)+3) /* mlist to be used in scriptscript style */ + +/* + because noad types get changed when processing we need to make sure some if the node + sizes match and that we don't share slots with different properties + + once it's sorted out we can go smaller and also adapt the flush etc code to it +*/ + +/* regular noads */ + +# define noad_size 8 +# define new_hlist(a) vlink((a)+2) /* the translation of an mlist */ +# define nucleus(a) vinfo((a)+2) /* the |nucleus| field of a noad */ +# define supscr(a) vlink((a)+3) /* the |supscr| field of a noad */ +# define subscr(a) vinfo((a)+3) /* the |subscr| field of a noad */ +# define noaditalic(a) vlink((a)+4) +# define noadwidth(a) vinfo((a)+4) +# define noadheight(a) vlink((a)+5) +# define noaddepth(a) vinfo((a)+5) +# define noadextra1(a) vlink((a)+6) /* we need to match delimiter (saves copy) */ +# define noadoptions(a) vinfo((a)+6) +# define noadextra3(a) vlink((a)+7) /* see (!) below */ +# define noadextra4(a) vinfo((a)+7) /* used to store samesize */ + +# define noad_fam(a) vlink((a)+6) /* noadextra1 */ + +typedef enum { + ord_noad_type = 0, + op_noad_type_normal, + op_noad_type_limits, + op_noad_type_no_limits, + bin_noad_type, + rel_noad_type, + open_noad_type, + close_noad_type, + punct_noad_type, + inner_noad_type, + under_noad_type, + over_noad_type, + vcenter_noad_type, +} noad_types; + +/* accent noads */ + +# define accent_noad_size 8 +# define top_accent_chr(a) vinfo((a)+6) /* the |top_accent_chr| field of an accent noad */ +# define bot_accent_chr(a) vlink((a)+6) /* the |bot_accent_chr| field of an accent noad */ +# define overlay_accent_chr(a) vinfo((a)+7) /* the |overlay_accent_chr| field of an accent noad */ +# define accentfraction(a) vlink((a)+7) + +typedef enum { + bothflexible_accent, + fixedtop_accent, + fixedbottom_accent, + fixedboth_accent, +} math_accent_subtypes ; + +/* left and right noads */ + +# define fence_noad_size 8 /* needs to match noad size */ +# define delimiteritalic(a) vlink((a)+4) +/* define delimiterwidth(a) vinfo((a)+4) */ +# define delimiterheight(a) vlink((a)+5) +# define delimiterdepth(a) vinfo((a)+5) +# define delimiter(a) vlink((a)+6) /* |delimiter| field in left and right noads */ +# define delimiteroptions(a) vinfo((a)+6) +# define delimiterclass(a) vlink((a)+7) /* (!) we could probably pack some more in 6 */ +# define delimitersamesize(a) vinfo((a)+7) /* set by engine */ + +/* when dimensions then axis else noaxis */ + +typedef enum { + noad_option_set = 0x08, + noad_option_unused_1 = 0x00 + 0x08, + noad_option_unused_2 = 0x01 + 0x08, + noad_option_axis = 0x02 + 0x08, + noad_option_no_axis = 0x04 + 0x08, + noad_option_exact = 0x10 + 0x08, + noad_option_left = 0x11 + 0x08, + noad_option_middle = 0x12 + 0x08, + noad_option_right = 0x14 + 0x08, + noad_option_no_sub_script = 0x21 + 0x08, + noad_option_no_super_script = 0x22 + 0x08, + noad_option_no_script = 0x23 + 0x08, + noad_option_no_rule = 0x24 + 0x08, +} delimiter_options ; + +# define delimiteroptionset(a) ((delimiteroptions(a) & noad_option_set ) == noad_option_set ) +# define delimiteraxis(a) ((delimiteroptions(a) & noad_option_axis ) == noad_option_axis ) +# define delimiternoaxis(a) ((delimiteroptions(a) & noad_option_no_axis) == noad_option_no_axis) +# define delimiterexact(a) ((delimiteroptions(a) & noad_option_exact ) == noad_option_exact ) + +# define noadoptionnosubscript(a) ( (type(a) == simple_noad) && ( \ + ((delimiteroptions(a) & noad_option_no_sub_script ) == noad_option_no_sub_script) || \ + ((delimiteroptions(a) & noad_option_no_script ) == noad_option_no_script ) )) +# define noadoptionnosupscript(a) ( (type(a) == simple_noad) && ( \ + ((delimiteroptions(a) & noad_option_no_super_script) == noad_option_no_super_script) || \ + ((delimiteroptions(a) & noad_option_no_script ) == noad_option_no_script ) )) + +typedef enum { + noad_delimiter_mode_noshift = 0x01, + noad_delimiter_mode_italics = 0x02, + noad_delimiter_mode_ordinal = 0x04, + noad_delimiter_mode_samenos = 0x08, + noad_delimiter_mode_charnos = 0x10, +} delimiter_modes ; + +# define delimitermodenoshift ((math_delimiters_mode_par & noad_delimiter_mode_noshift) == noad_delimiter_mode_noshift) +# define delimitermodeitalics ((math_delimiters_mode_par & noad_delimiter_mode_italics) == noad_delimiter_mode_italics) +# define delimitermodeordinal ((math_delimiters_mode_par & noad_delimiter_mode_ordinal) == noad_delimiter_mode_ordinal) +# define delimitermodesamenos ((math_delimiters_mode_par & noad_delimiter_mode_samenos) == noad_delimiter_mode_samenos) +# define delimitermodecharnos ((math_delimiters_mode_par & noad_delimiter_mode_charnos) == noad_delimiter_mode_charnos) + +/* subtype of fence noads */ + +/* +# define left_noad_side 1 +# define middle_noad_side 2 +# define right_noad_side 3 +# define no_noad_side 4 +*/ + +typedef enum { + unset_noad_side = 0, + left_noad_side = 1, + middle_noad_side = 2, + right_noad_side = 3, + no_noad_side = 4, +} fence_subtypes ; + +/* fraction noads */ + +# define fraction_noad_size 8 +# define thickness(a) vlink((a)+2) /* |thickness| field in a fraction noad */ +# define numerator(a) vlink((a)+3) /* |numerator| field in a fraction noad */ +# define denominator(a) vinfo((a)+3) /* |denominator| field in a fraction noad */ +# define left_delimiter(a) vlink((a)+5) /* first delimiter field of a noad */ +# define right_delimiter(a) vinfo((a)+5) /* second delimiter field of a fraction noad */ +# define middle_delimiter(a) vlink((a)+6) +# define fractionoptions(a) vinfo((a)+6) +# define fraction_fam(a) vlink((a)+7) + +# define fractionoptionset(a) ((fractionoptions(a) & noad_option_set ) == noad_option_set ) +# define fractionexact(a) ((fractionoptions(a) & noad_option_exact ) == noad_option_exact ) +# define fractionnoaxis(a) ((fractionoptions(a) & noad_option_no_axis) == noad_option_no_axis) +# define fractionnorule(a) ((fractionoptions(a) & noad_option_no_rule) == noad_option_no_rule) + +/* radical noads */ +/* this is like a fraction, but it only stores a |left_delimiter| */ + +# define radical_noad_size 7 +# define radicalwidth(a) vinfo((a)+4) +# define degree(a) vlink((a)+6) /* the root degree in a radical noad */ +# define radicaloptions(a) vinfo((a)+6) + +# define radicaloptionset(a) ((radicaloptions(a) & noad_option_set ) == noad_option_set) +# define radicalexact(a) ((radicaloptions(a) & noad_option_exact ) == noad_option_exact) +# define radicalleft(a) ((radicaloptions(a) & noad_option_left ) == noad_option_left) +# define radicalmiddle(a) ((radicaloptions(a) & noad_option_middle) == noad_option_middle) +# define radicalright(a) ((radicaloptions(a) & noad_option_right ) == noad_option_right) + +typedef enum { + radical_noad_type, + uradical_noad_type, + uroot_noad_type, + uunderdelimiter_noad_type, + uoverdelimiter_noad_type, + udelimiterunder_noad_type, + udelimiterover_noad_type, +} radical_subtypes; + +/* accessors for the |nucleus|-style node fields */ + +# define math_kernel_node_size 3 +# define math_fam(a) vinfo((a)+2) +# define math_character(a) vlink((a)+2) +# define math_list(a) vlink((a)+2) + +/* accessors for the |delimiter|-style two-word subnode fields */ + +# define math_shield_node_size 4 /* not used yet */ + +# define small_fam(A) vinfo((A)+2) /* |fam| for ``small'' delimiter */ +# define small_char(A) vlink((A)+2) /* |character| for ``small'' delimiter */ +# define large_fam(A) vinfo((A)+3) /* |fam| for ``large'' delimiter */ +# define large_char(A) vlink((A)+3) /* |character| for ``large'' delimiter */ + +/* we should have the codes in a separate enum: extension_codes */ + +/* be careful: must be in sync with whatsit_node_data[] ! (at least) */ + +typedef enum { + /* core */ + open_node = 0, + write_node, + close_node, + special_node, + use_box_resource_node, + use_image_resource_node, + save_pos_node, + late_lua_node, + user_defined_node, + /* dvi backend */ + dvi_literal_node = 15, + /* pdf backend */ + pdf_literal_node = 16, + pdf_refobj_node, + pdf_annot_node, + pdf_start_link_node, + pdf_end_link_node, + pdf_dest_node, + pdf_action_node, + pdf_thread_node, + pdf_start_thread_node, + pdf_end_thread_node, + pdf_thread_data_node, + pdf_link_data_node, + pdf_colorstack_node, + pdf_setmatrix_node, + pdf_save_node, + pdf_restore_node, + pdf_link_state_node, +} whatsit_types; + +# define first_common_whatsit 0 +# define last_common_whatsit user_defined_node +# define backend_first_dvi_whatsit 15 +# define backend_last_dvi_whatsit 15 +# define backend_first_pdf_whatsit 16 +# define backend_last_pdf_whatsit 32 + +/* add some slack for the future */ + +# define MAX_WHATSIT_TYPE 34 + +# define known_whatsit_type(i) ( \ + (i >= first_common_whatsit && i <= last_common_whatsit) || \ + (i >= backend_first_dvi_whatsit && i <= backend_last_dvi_whatsit) || \ + (i >= backend_first_pdf_whatsit && i <= backend_last_pdf_whatsit) \ +) + +# define get_node_size(i,j) (i!=whatsit_node ? node_data[i].size : whatsit_node_data[j].size) +# define get_node_name(i,j) (i!=whatsit_node ? node_data[i].name : whatsit_node_data[j].name) +# define get_etex_code(i) (node_data[i].etex) + +# define GLYPH_CHARACTER (1 << 0) +# define GLYPH_LIGATURE (1 << 1) +# define GLYPH_GHOST (1 << 2) +# define GLYPH_LEFT (1 << 3) +# define GLYPH_RIGHT (1 << 4) + +typedef enum { + glyph_unset = 0, + glyph_character = GLYPH_CHARACTER, + glyph_ligature = GLYPH_LIGATURE, + glyph_ghost = GLYPH_GHOST, + glyph_left = GLYPH_LEFT, + glyph_right = GLYPH_RIGHT, +} glyph_subtypes; + +# define is_character(p) ((subtype(p)) & GLYPH_CHARACTER) +# define is_ligature(p) ((subtype(p)) & GLYPH_LIGATURE ) +# define is_ghost(p) ((subtype(p)) & GLYPH_GHOST ) + +# define is_simple_character(p) (is_character(p) && !is_ligature(p) && !is_ghost(p)) + +# define is_leftboundary(p) (is_ligature(p) && ((subtype(p)) & GLYPH_LEFT )) +# define is_rightboundary(p) (is_ligature(p) && ((subtype(p)) & GLYPH_RIGHT )) +# define is_leftghost(p) (is_ghost(p) && ((subtype(p)) & GLYPH_LEFT )) +# define is_rightghost(p) (is_ghost(p) && ((subtype(p)) & GLYPH_RIGHT )) + + +# define set_is_glyph(p) subtype(p) = (quarterword) (subtype(p) & ~GLYPH_CHARACTER) +# define set_is_character(p) subtype(p) = (quarterword) (subtype(p) | GLYPH_CHARACTER) +# define set_is_ligature(p) subtype(p) = (quarterword) (subtype(p) | GLYPH_LIGATURE) +# define set_is_ghost(p) subtype(p) = (quarterword) (subtype(p) |GLYPH_GHOST) + +# define set_to_glyph(p) subtype(p) = (quarterword)(subtype(p) & 0xFF00) +# define set_to_character(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_CHARACTER) +# define set_to_ligature(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_LIGATURE) +# define set_to_ghost(p) subtype(p) = (quarterword)((subtype(p) & 0xFF00) | GLYPH_GHOST) + +# define set_is_leftboundary(p) { set_to_ligature(p); subtype(p) |= GLYPH_LEFT; } +# define set_is_rightboundary(p) { set_to_ligature(p); subtype(p) |= GLYPH_RIGHT; } +# define set_is_leftghost(p) { set_to_ghost(p); subtype(p) |= GLYPH_LEFT; } +# define set_is_rightghost(p) { set_to_ghost(p); subtype(p) |= GLYPH_RIGHT; } + +typedef enum { + cancel_boundary = 0, + user_boundary, + protrusion_boundary, + word_boundary, +} boundary_subtypes ; + +# define boundary_node_size 3 +# define boundary_value(a) vinfo((a)+2) + +# define special_node_size 3 + +typedef enum { + normal_dir = 0, + cancel_dir, +} dir_subtypes ; + +# define dir_node_size 5 +# define dir_dir(a) vinfo((a)+2) +# define dir_level(a) vlink((a)+2) +# define dir_refpos_h(a) vinfo((a)+3) +# define dir_refpos_v(a) vlink((a)+3) +# define dir_cur_h(a) vinfo((a)+4) +# define dir_cur_v(a) vlink((a)+4) + +# define write_node_size 3 +# define close_node_size 3 +# define write_tokens(a) vlink(a+2) +# define write_stream(a) vinfo(a+2) + +# define open_node_size 4 +# define open_name(a) vlink((a)+2) +# define open_area(a) vinfo((a)+3) +# define open_ext(a) vlink((a)+3) + +# define late_lua_node_size 4 +# define late_lua_data(a) vlink((a)+2) +# define late_lua_reg(a) vinfo((a)+2) +# define late_lua_name(a) vlink((a)+3) +# define late_lua_type(a) subtype((a)+3) + +# define save_pos_node_size 3 + +# define local_par_size 6 + +# define local_pen_inter(a) vinfo((a)+2) +# define local_pen_broken(a) vlink((a)+2) +# define local_box_left(a) vlink((a)+3) +# define local_box_left_width(a) vinfo((a)+3) +# define local_box_right(a) vlink((a)+4) +# define local_box_right_width(a) vinfo((a)+4) +# define local_par_dir(a) vinfo((a)+5) + +/* type of literal data */ + +# define lua_refid_literal 1 /* not a |normal| string */ +# define lua_refid_call 2 /* not a |normal| string */ + +/* begin of pdf backend nodes */ + +/* literal ctm types */ + +typedef enum { + set_origin = 0, + direct_page, + direct_always, + direct_raw, + direct_text, + direct_font, + scan_special, +} ctm_transform_modes; + +# define pdf_refobj_node_size 3 + +# define pdf_obj_objnum(a) vinfo((a) + 2) + +# define pdf_annot_node_size 8 +# define pdf_dest_node_size 8 +# define pdf_thread_node_size 8 + +/* + when a whatsit node representing annotation is created, words |1..3| are + width, height and depth of this annotation; after shipping out words |1..4| + are rectangle specification of annotation. For whatsit node representing + destination |pdf_ann_left| and |pdf_ann_top| are used for some types of destinations +*/ + +/* + coordinates of destinations/threads/annotations (in whatsit node) +*/ + +# define pdf_ann_left(a) varmem[(a) + 2].cint +# define pdf_ann_top(a) varmem[(a) + 3].cint +# define pdf_ann_right(a) varmem[(a) + 4].cint +# define pdf_ann_bottom(a) varmem[(a) + 5].cint + +# define pdf_literal_data(a) vlink((a)+2) +# define pdf_literal_mode(a) type((a)+2) +# define pdf_literal_type(a) subtype((a)+2) + +# define pdf_annot_data(a) vinfo((a) + 6) +# define pdf_link_attr(a) vinfo((a) + 6) +# define pdf_link_action(a) vlink((a) + 6) +# define pdf_annot_objnum(a) varmem[(a) + 7].cint +# define pdf_link_objnum(a) varmem[(a) + 7].cint + +# define pdf_dest_type(a) type((a) + 6) +# define pdf_dest_named_id(a) subtype((a) + 6) +# define pdf_dest_id(a) vlink((a) + 6) +# define pdf_dest_xyz_zoom(a) vinfo((a) + 7) +# define pdf_dest_objnum(a) vlink((a) + 7) + +# define pdf_thread_named_id(a) subtype((a) + 6) +# define pdf_thread_id(a) vlink((a) + 6) +# define pdf_thread_attr(a) vinfo((a) + 7) + +# define pdf_end_link_node_size 3 +# define pdf_end_thread_node_size 3 + +# define pdf_setmatrix_node_size 3 +# define pdf_save_node_size 3 +# define pdf_restore_node_size 3 + +# define pdf_link_state_node_size 3 + +# define pdf_link_state(a) vinfo((a) + 2) + +# define pdf_colorstack_node_size 4 +# define pdf_colorstack_stack(a) vlink((a)+2) +# define pdf_colorstack_cmd(a) vinfo((a)+2) +# define pdf_colorstack_data(a) vlink((a)+3) +# define pdf_setmatrix_data(a) vlink((a)+2) + +typedef enum { + pdf_action_page = 0, + pdf_action_goto, + pdf_action_thread, + pdf_action_user +} pdf_action_type; + +typedef enum { + pdf_window_notset, + pdf_window_new, + pdf_window_nonew, +} pdf_window_type; + +# define pdf_action_size 6 + +# define pdf_action_type(a) vlink((a)+2) /* enum pdf_action_type */ +# define pdf_action_named_id(a) vinfo((a)+2) /* boolean */ +# define pdf_action_id(a) vlink((a)+3) /* number or toks */ +# define pdf_action_file(a) vinfo((a)+3) /* toks */ +# define pdf_action_new_window(a) vlink((a)+4) /* enum pdf_window_type */ +# define pdf_action_tokens(a) vinfo((a)+4) /* toks */ +# define pdf_action_refcount(a) vlink((a)+5) /* number */ + +typedef enum { + colorstack_set = 0, + colorstack_push, + colorstack_pop, + colorstack_current +} colorstack_commands; + +# define colorstack_data colorstack_push /* last value where data field is set */ + +/* end of pdf backend nodes */ + +/* user defined nodes */ + +# define user_defined_node_size 4 +# define user_node_type(a) vinfo((a)+2) +# define user_node_id(a) vlink((a)+2) +# define user_node_value(a) vinfo((a)+3) + +/* node sused in the parbuilder */ + +# define active_node_size 4 /*number of words in extended active nodes */ +# define fitness subtype /*|very_loose_fit..tight_fit| on final line for this break */ +# define break_node(a) vlink((a)+1) /*pointer to the corresponding passive node */ +# define line_number(a) vinfo((a)+1) /*line that begins at this breakpoint */ +# define total_demerits(a) varmem[(a)+2].cint /* the quantity that \TeX\ minimizes */ +# define active_short(a) vinfo(a+3) /* |shortfall| of this line */ +# define active_glue(a) vlink(a+3) /*corresponding glue stretch or shrink */ + +# define passive_node_size 7 +# define cur_break(a) vlink((a)+1) /*in passive node, points to position of this breakpoint */ +# define prev_break(a) vinfo((a)+1) /*points to passive node that should precede this one */ +# define passive_pen_inter(a) vinfo((a)+2) +# define passive_pen_broken(a) vlink((a)+2) +# define passive_left_box(a) vlink((a)+3) +# define passive_left_box_width(a) vinfo((a)+3) +# define passive_last_left_box(a) vlink((a)+4) +# define passive_last_left_box_width(a) vinfo((a)+4) +# define passive_right_box(a) vlink((a)+5) +# define passive_right_box_width(a) vinfo((a)+5) +# define serial(a) vlink((a)+6) /* serial number for symbolic identification */ + +# define delta_node_size 10 /* 8 fields, stored in a+1..9 */ + +/* helpers */ + +# define couple_nodes(a,b) {assert(b!=null);vlink(a)=b;alink(b)=a;} +# define try_couple_nodes(a,b) if (b==null) vlink(a)=b; else {couple_nodes(a,b);} +# define uncouple_node(a) {assert(a!=null);vlink(a)=null;alink(a)=null;} + +# define cache_disabled max_halfword + +extern void delete_attribute_ref(halfword b); +extern void reset_node_properties(halfword b); +extern void reassign_attribute(halfword n,halfword new); +extern void build_attribute_list(halfword b); +extern halfword current_attribute_list(void); + +extern int unset_attribute(halfword n, int c, int w); +extern void set_attribute(halfword n, int c, int w); +extern int has_attribute(halfword n, int c, int w); + +extern halfword new_span_node(halfword n, int c, scaled w); + +extern void print_short_node_contents(halfword n); +extern void show_node_list(int i); +extern pointer actual_box_width(pointer r, scaled base_width); + +typedef struct _subtype_info { + int id; + const char *name; + int lua; +} subtype_info; + +typedef struct _field_info { + const char *name; + int lua; +} field_info; + +typedef struct _node_info { + int id; + int size; + subtype_info *subtypes; + field_info *fields; + const char *name; + int etex; + int lua; +} node_info; + +extern node_info node_data[]; +extern node_info whatsit_node_data[]; + +extern subtype_info node_subtypes_dir[]; +extern subtype_info node_subtypes_glue[]; +extern subtype_info node_subtypes_mathglue[]; +extern subtype_info node_subtypes_leader[]; +extern subtype_info node_subtypes_boundary[]; +extern subtype_info node_subtypes_penalty[]; +extern subtype_info node_subtypes_kern[]; +extern subtype_info node_subtypes_rule[]; +extern subtype_info node_subtypes_glyph[]; +extern subtype_info node_subtypes_disc[]; +extern subtype_info node_subtypes_marginkern[]; +extern subtype_info node_subtypes_list[]; +extern subtype_info node_subtypes_adjust[]; +extern subtype_info node_subtypes_math[]; +extern subtype_info node_subtypes_noad[]; +extern subtype_info node_subtypes_radical[]; +extern subtype_info node_subtypes_accent[]; +extern subtype_info node_subtypes_fence[]; + +extern subtype_info node_values_pdf_destination[]; +extern subtype_info node_values_pdf_literal[]; +extern subtype_info node_values_pdf_literal[]; +extern subtype_info node_values_pdf_action[]; +extern subtype_info node_values_pdf_window[]; + +extern subtype_info node_values_fill[]; +extern subtype_info node_values_dir[]; +extern subtype_info node_values_color_stack[]; + +extern subtype_info other_values_page_states[]; + +extern halfword new_node(int i, int j); +extern void flush_node_list(halfword); +extern void flush_node(halfword); +extern halfword do_copy_node_list(halfword, halfword); +extern halfword copy_node_list(halfword); +extern halfword copy_node(const halfword); +extern void check_node(halfword); +extern halfword fix_node_list(halfword); +extern int fix_node_lists; +extern char *sprint_node_mem_usage(void); +extern halfword raw_glyph_node(void); +extern halfword new_glyph_node(void); +extern int valid_node(halfword); + +extern void flush_node_wrapup_dvi(halfword); +extern void flush_node_wrapup_pdf(halfword); +extern void copy_node_wrapup_dvi(halfword, halfword); /* original target */ +extern void copy_node_wrapup_pdf(halfword, halfword); /* original target */ +extern void check_node_wrapup_dvi(halfword); /* DEBUG_NODES mode */ +extern void check_node_wrapup_pdf(halfword); /* DEBUG_NODES mode */ +extern void show_node_wrapup_dvi(halfword); +extern void show_node_wrapup_pdf(halfword); + +typedef enum { + normal_g = 0, /* normal */ + sfi, + fil, + fill, + filll +} glue_orders; + +# define zero_glue 0 +# define sfi_glue zero_glue+glue_spec_size +# define fil_glue sfi_glue+glue_spec_size +# define fill_glue fil_glue+glue_spec_size +# define ss_glue fill_glue+glue_spec_size +# define fil_neg_glue ss_glue+glue_spec_size +# define page_ins_head fil_neg_glue+glue_spec_size + +# define contrib_head page_ins_head+temp_node_size +# define page_head contrib_head+temp_node_size +# define temp_head page_head+temp_node_size +# define hold_head temp_head+temp_node_size +# define adjust_head hold_head+temp_node_size +# define pre_adjust_head adjust_head+temp_node_size +# define active pre_adjust_head+temp_node_size +# define align_head active+active_node_size +# define end_span align_head+temp_node_size +# define begin_point end_span+span_node_size +# define end_point begin_point+glyph_node_size +# define var_mem_stat_max (end_point+glyph_node_size-1) + +# define stretching 1 +# define shrinking 2 + +# define last_normal_node shape_node +# define last_whatsit_node pdf_restore_node + +# define is_running(A) ((A)==null_flag) /* tests for a running dimension */ + +extern halfword tail_of_list(halfword p); + +extern int var_used; + +# define cache_disabled max_halfword + +extern int max_used_attr; +extern halfword attr_list_cache; + +extern halfword new_null_box(void); +extern halfword new_rule(int s); +extern halfword new_glyph(int f, int c); +extern quarterword norm_min(int h); +extern halfword new_char(int f, int c); +extern scaled glyph_width(halfword p); +extern scaled glyph_height(halfword p); +extern scaled glyph_depth(halfword p); +extern halfword new_disc(void); +extern halfword new_math(scaled w, int s); +extern halfword new_spec(halfword p); +extern halfword new_param_glue(int n); +extern halfword new_glue(halfword q); +extern halfword new_skip_param(int n); +extern halfword new_kern(scaled w); +extern halfword new_penalty(int m, int s); + +extern int lua_properties_enabled ; +extern int lua_properties_level ; +extern int lua_properties_use_metatable ; + +extern halfword make_local_par_node(int mode); + +extern void synctex_set_mode(int mode); +extern int synctex_get_mode(void); +extern void synctex_set_tag(int tag); +extern void synctex_set_line(int line); +extern void synctex_force_tag(int tag); +extern void synctex_force_line(int tag); +extern int synctex_get_tag(void); +extern void synctex_set_no_files(int flag); +extern int synctex_get_no_files(void); +extern int synctex_get_line(void); + +extern void l_set_node_data(void) ; +extern void l_set_whatsit_data(void) ; + +#endif + diff --git a/Build/source/texk/web2c/luatexdir/tex/textcodes.c b/Build/source/texk/web2c/luatexdir/tex/textcodes.c new file mode 100644 index 00000000000..a6a0b3a55e3 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/textcodes.c @@ -0,0 +1,530 @@ +/* + +textcodes.w + +Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +Contrary to traditional \TEX\ we have catcode tables so that we can switch +catcode regimes very fast. We can have many such regimes and they're stored in +trees. + +*/ + +#define CATCODESTACK 8 +#define CATCODEDEFAULT 12 +#define CATCODE_MAX 32767 + +static sa_tree *catcode_heads = NULL; +static int catcode_max = 0; +static unsigned char *catcode_valid = NULL; + +void set_cat_code(int h, int n, halfword v, quarterword gl) +{ + sa_tree_item sa_value = { 0 }; + sa_tree s = catcode_heads[h]; + if (h > catcode_max) + catcode_max = h; + if (s == NULL) { + sa_value.int_value = CATCODEDEFAULT; + s = new_sa_tree(CATCODESTACK, 1, sa_value); + catcode_heads[h] = s; + } + sa_value.int_value = (int) v; + set_sa_item(s, n, sa_value, gl); +} + +halfword get_cat_code(int h, int n) +{ + sa_tree_item sa_value = { 0 }; + sa_tree s = catcode_heads[h]; + if (h > catcode_max) + catcode_max = h; + if (s == NULL) { + sa_value.int_value = CATCODEDEFAULT; + s = new_sa_tree(CATCODESTACK, 1, sa_value); + catcode_heads[h] = s; + } + return (halfword) get_sa_item(s, n).int_value; +} + +void unsave_cat_codes(int h, quarterword gl) +{ + int k; + if (h > catcode_max) + catcode_max = h; + for (k = 0; k <= catcode_max; k++) { + if (catcode_heads[k] != NULL) + restore_sa_stack(catcode_heads[k], gl); + } +} + +static void initializecatcodes(void) +{ + sa_tree_item sa_value = { 0 }; + catcode_max = 0; + catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1)); + catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1)); + memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1)); + memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1)); + catcode_valid[0] = 1; + sa_value.int_value = CATCODEDEFAULT; + catcode_heads[0] = new_sa_tree(CATCODESTACK, 1, sa_value); +} + +static void dumpcatcodes(void) +{ + int total = 0; + int k; + for (k = 0; k <= catcode_max; k++) { + if (catcode_valid[k]) { + total++; + } + } + dump_int(catcode_max); + dump_int(total); + for (k = 0; k <= catcode_max; k++) { + if (catcode_valid[k]) { + dump_int(k); + dump_sa_tree(catcode_heads[k],"catcodes"); + } + } +} + +static void undumpcatcodes(void) +{ + int total, k, x; + xfree(catcode_heads); + xfree(catcode_valid); + catcode_heads = Mxmalloc_array(sa_tree, (CATCODE_MAX + 1)); + catcode_valid = Mxmalloc_array(unsigned char, (CATCODE_MAX + 1)); + memset(catcode_heads, 0, sizeof(sa_tree) * (CATCODE_MAX + 1)); + memset(catcode_valid, 0, sizeof(unsigned char) * (CATCODE_MAX + 1)); + undump_int(catcode_max); + undump_int(total); + for (k = 0; k < total; k++) { + undump_int(x); + catcode_heads[x] = undump_sa_tree("catcodes"); + catcode_valid[x] = 1; + } +} + +int valid_catcode_table(int h) +{ + if (h <= CATCODE_MAX && h >= 0 && catcode_valid[h]) { + return 1; + } + return 0; +} + +void copy_cat_codes(int from, int to) +{ + if (from < 0 || from > CATCODE_MAX || catcode_valid[from] == 0) { + uexit(1); + } + if (to > catcode_max) + catcode_max = to; + destroy_sa_tree(catcode_heads[to]); + catcode_heads[to] = copy_sa_tree(catcode_heads[from]); + catcode_valid[to] = 1; +} + +void initex_cat_codes(int h) +{ + int k; + if (h > catcode_max) + catcode_max = h; + destroy_sa_tree(catcode_heads[h]); + catcode_heads[h] = NULL; + set_cat_code(h, '\r', car_ret_cmd, 1); + set_cat_code(h, ' ', spacer_cmd, 1); + set_cat_code(h, '\\', escape_cmd, 1); + set_cat_code(h, '%', comment_cmd, 1); + set_cat_code(h, 127, invalid_char_cmd, 1); + set_cat_code(h, 0, ignore_cmd, 1); + set_cat_code(h, 0xFEFF, ignore_cmd, 1); + for (k = 'A'; k <= 'Z'; k++) { + set_cat_code(h, k, letter_cmd, 1); + set_cat_code(h, k + 'a' - 'A', letter_cmd, 1); + } + catcode_valid[h] = 1; +} + +static void freecatcodes(void) +{ + int k; + for (k = 0; k <= catcode_max; k++) { + if (catcode_valid[k]) { + destroy_sa_tree(catcode_heads[k]); + } + } + xfree(catcode_heads); + xfree(catcode_valid); +} + +/*tex + +The lowercase mapping codes are also stored in a tree. + +*/ + +#define LCCODESTACK 8 +#define LCCODEDEFAULT 0 + +static sa_tree lccode_head = NULL; + +void set_lc_code(int n, halfword v, quarterword gl) +{ + sa_tree_item sa_value = { 0 }; + sa_value.int_value = (int) v; + set_sa_item(lccode_head, n, sa_value, gl); +} + +halfword get_lc_code(int n) +{ + return (halfword) get_sa_item(lccode_head, n).int_value; +} + +static void unsavelccodes(quarterword gl) +{ + restore_sa_stack(lccode_head, gl); +} + +static void initializelccodes(void) +{ + sa_tree_item sa_value = { 0 }; + sa_value.int_value = LCCODEDEFAULT; + lccode_head = new_sa_tree(LCCODESTACK, 1, sa_value); +} + +static void dumplccodes(void) +{ + dump_sa_tree(lccode_head,"lccodes"); +} + +static void undumplccodes(void) +{ + lccode_head = undump_sa_tree("lccodes"); +} + +static void freelccodes(void) +{ + destroy_sa_tree(lccode_head); +} + +/*tex + +And the uppercase mapping codes are again stored in a tree. + +*/ + +#define UCCODESTACK 8 +#define UCCODEDEFAULT 0 + +static sa_tree uccode_head = NULL; + +void set_uc_code(int n, halfword v, quarterword gl) +{ + sa_tree_item sa_value = { 0 }; + sa_value.int_value = (int) v; + set_sa_item(uccode_head, n, sa_value, gl); +} + +halfword get_uc_code(int n) +{ + return (halfword) get_sa_item(uccode_head, n).int_value; +} + +static void unsaveuccodes(quarterword gl) +{ + restore_sa_stack(uccode_head, gl); +} + +static void initializeuccodes(void) +{ + sa_tree_item sa_value = { 0 }; + sa_value.int_value = UCCODEDEFAULT; + uccode_head = new_sa_tree(UCCODESTACK, 1, sa_value); +} + +static void dumpuccodes(void) +{ + dump_sa_tree(uccode_head,"uccodes"); +} + +static void undumpuccodes(void) +{ + uccode_head = undump_sa_tree("uccodes"); +} + +static void freeuccodes(void) +{ + destroy_sa_tree(uccode_head); +} + +/*tex + +By now it will be no surprise that the space factors get stored in a tree. + +*/ + +#define SFCODESTACK 8 +#define SFCODEDEFAULT 1000 + +static sa_tree sfcode_head = NULL; + +void set_sf_code(int n, halfword v, quarterword gl) +{ + sa_tree_item sa_value = { 0 }; + sa_value.int_value = (int) v; + set_sa_item(sfcode_head, n, sa_value, gl); +} + +halfword get_sf_code(int n) +{ + return (halfword) get_sa_item(sfcode_head, n).int_value; +} + +static void unsavesfcodes(quarterword gl) +{ + restore_sa_stack(sfcode_head, gl); +} + +static void initializesfcodes(void) +{ + sa_tree_item sa_value = { 0 }; + sa_value.int_value = SFCODEDEFAULT; + sfcode_head = new_sa_tree(SFCODESTACK, 1, sa_value); +} + +static void dumpsfcodes(void) +{ + dump_sa_tree(sfcode_head,"sfcodes"); +} + +static void undumpsfcodes(void) +{ + sfcode_head = undump_sa_tree("sfcodes"); +} + +static void freesfcodes(void) +{ + destroy_sa_tree(sfcode_head); +} + +/*tex + +The hyphenation codes are indeed stored in a tree and are used instead of +lowercase codes when deciding what characters to take into acccount when +hyphenating. They are bound to upto |HJCODE_MAX| languages. + +*/ + +#define HJCODESTACK 8 +#define HJCODEDEFAULT 0 +#define HJCODE_MAX 16383 + +static sa_tree *hjcode_heads = NULL; +static int hjcode_max = 0; +static unsigned char *hjcode_valid = NULL; + +/*tex + +Here we set codes but we don't initialize from lccodes. + +*/ + +void set_hj_code(int h, int n, halfword v, quarterword gl) +{ + sa_tree_item sa_value = { 0 }; + sa_tree s = hjcode_heads[h]; + if (h > hjcode_max) + hjcode_max = h; + if (s == NULL) { + sa_value.int_value = HJCODEDEFAULT; + s = new_sa_tree(HJCODESTACK, 1, sa_value); + hjcode_heads[h] = s; + } + sa_value.int_value = (int) v; + set_sa_item(s, n, sa_value, gl); +} + +/*tex + +We just return the lccodes when nothing is set. + +*/ + +halfword get_hj_code(int h, int n) +{ + sa_tree s = hjcode_heads[h]; + if (s == NULL) { + s = lccode_head; + } + return (halfword) get_sa_item(s, n).int_value; +} + +/*tex + +We don't restore as we can have more languages in a paragraph and hyphenation +takes place at a later stage so we would get weird grouping side effects .. so, +one can overload settings but management is then upto the used, so no: + +*/ + +/* + static void unsavehjcodes(quarterword gl) { } +*/ + +static void initializehjcodes(void) +{ + /* + sa_tree_item sa_value = { 0 }; + */ + hjcode_max = 0; + hjcode_heads = Mxmalloc_array(sa_tree, (HJCODE_MAX + 1)); + hjcode_valid = Mxmalloc_array(unsigned char, (HJCODE_MAX + 1)); + memset(hjcode_heads, 0, sizeof(sa_tree) * (HJCODE_MAX + 1)); + memset(hjcode_valid, 0, sizeof(unsigned char) * (HJCODE_MAX + 1)); + /* + hjcode_valid[0] = 1; + sa_value.int_value = HJCODEDEFAULT; + hjcode_heads[0] = new_sa_tree(HJCODESTACK, 1, sa_value); + */ +} + +void hj_codes_from_lc_codes(int h) +{ + sa_tree_item sa_value = { 0 }; + sa_tree s = hjcode_heads[h]; + if (s != NULL) { + destroy_sa_tree(s); + } else if (h > hjcode_max) { + hjcode_max = h; + } + if (s == NULL) { + sa_value.int_value = HJCODEDEFAULT; + s = new_sa_tree(HJCODESTACK, 1, sa_value); + hjcode_heads[h] = s; + } + hjcode_heads[h] = copy_sa_tree(lccode_head); + hjcode_valid[h] = 1; +} + +static void dumphjcodes(void) +{ + int total = 0; + int k; + for (k = 0; k <= hjcode_max; k++) { + if (hjcode_valid[k]) { + total++; + } + } + dump_int(hjcode_max); + dump_int(total); + for (k = 0; k <= hjcode_max; k++) { + if (hjcode_valid[k]) { + dump_int(k); + dump_sa_tree(hjcode_heads[k],"hjcodes"); + } + } +} + +static void undumphjcodes(void) +{ + int total, k, x; + xfree(hjcode_heads); + xfree(hjcode_valid); + hjcode_heads = Mxmalloc_array(sa_tree, (HJCODE_MAX + 1)); + hjcode_valid = Mxmalloc_array(unsigned char, (HJCODE_MAX + 1)); + memset(hjcode_heads, 0, sizeof(sa_tree) * (HJCODE_MAX + 1)); + memset(hjcode_valid, 0, sizeof(unsigned char) * (HJCODE_MAX + 1)); + undump_int(hjcode_max); + undump_int(total); + for (k = 0; k < total; k++) { + undump_int(x); + hjcode_heads[x] = undump_sa_tree("hjcodes"); + hjcode_valid[x] = 1; + } +} + +static void freehjcodes(void) +{ + int k; + for (k = 0; k <= hjcode_max; k++) { + if (hjcode_valid[k]) { + destroy_sa_tree(hjcode_heads[k]); + } + } + xfree(hjcode_heads); + xfree(hjcode_valid); +} + +/*tex + +The public management functions. + +*/ + +void unsave_text_codes(quarterword grouplevel) +{ + unsavelccodes(grouplevel); + unsaveuccodes(grouplevel); + unsavesfcodes(grouplevel); +} + +void initialize_text_codes(void) +{ + initializecatcodes(); + initializelccodes(); + initializeuccodes(); + initializesfcodes(); + initializehjcodes(); +} + +void free_text_codes(void) +{ + freecatcodes(); + freelccodes(); + freeuccodes(); + freesfcodes(); + freehjcodes(); +} + +void dump_text_codes(void) +{ + dumpcatcodes(); + dumplccodes(); + dumpuccodes(); + dumpsfcodes(); + dumphjcodes(); +} + +void undump_text_codes(void) +{ + undumpcatcodes(); + undumplccodes(); + undumpuccodes(); + undumpsfcodes(); + undumphjcodes(); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/textcodes.h b/Build/source/texk/web2c/luatexdir/tex/textcodes.h new file mode 100644 index 00000000000..759d9ee740e --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/textcodes.h @@ -0,0 +1,54 @@ +/* textcodes.h + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef TEXTCODES_H +# define TEXTCODES_H + +void set_cat_code(int h, int n, halfword v, quarterword gl); +halfword get_cat_code(int h, int n); +int valid_catcode_table(int h); +void unsave_cat_codes(int h, quarterword gl); +void copy_cat_codes(int from, int to); +void initex_cat_codes(int h); + +void set_lc_code(int n, halfword v, quarterword gl); +halfword get_lc_code(int n); + +void set_uc_code(int n, halfword v, quarterword gl); +halfword get_uc_code(int n); + +void set_sf_code(int n, halfword v, quarterword gl); +halfword get_sf_code(int n); + +void set_hj_code(int l, int n, halfword v, quarterword gl); +halfword get_hj_code(int l, int n); +void hj_codes_from_lc_codes(int h); + +void initialize_text_codes(void); +void unsave_text_codes(quarterword grouplevel); + +void dump_text_codes(void); +void undump_text_codes(void); + +void free_math_codes(void); +void free_text_codes(void); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.c b/Build/source/texk/web2c/luatexdir/tex/textoken.c new file mode 100644 index 00000000000..e28626bbd26 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.c @@ -0,0 +1,3920 @@ +/* + +Copyright 2006-2011 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +#define detokenized_line() (line_catcode_table==NO_CAT_TABLE) + +#define do_get_cat_code(a,b) do { \ + if (line_catcode_table==DEFAULT_CAT_TABLE) \ + a=get_cat_code(cat_code_table_par,b); \ + else if (line_catcode_table>-0xFF) \ + a=get_cat_code(line_catcode_table,b); \ + else \ + a= - line_catcode_table - 0xFF ; \ + } while (0) + + +/*tex + + The \TeX\ system does nearly all of its own memory allocation, so that it can + readily be transported into environments that do not have automatic + facilities for strings, garbage collection, etc., and so that it can be in + control of what error messages the user receives. The dynamic storage + requirements of \TeX\ are handled by providing two large arrays called + |fixmem| and |varmem| in which consecutive blocks of words are used as nodes + by the \TeX\ routines. + + Pointer variables are indices into this array, or into another array called + |eqtb| that will be explained later. A pointer variable might also be a + special flag that lies outside the bounds of |mem|, so we allow pointers to + assume any |halfword| value. The minimum halfword value represents a null + pointer. \TeX\ does not assume that |mem[null]| exists. + + Locations in |fixmem| are used for storing one-word records; a conventional + \.{AVAIL} stack is used for allocation in this array. + +*/ + +/*tex the big dynamic storage area */ + +smemory_word *fixmem; + +/*tex the smallest location of one-word memory in use */ + +unsigned fix_mem_min; + +/*tex the largest location of one-word memory in use */ + +unsigned fix_mem_max; + +/*tex + + In order to study the memory requirements of particular applications, it is + possible to prepare a version of \TeX\ that keeps track of current and + maximum memory usage. When code between the delimiters |stat| $\ldots$ + |tats| is not commented out, \TeX\ will run a bit slower but it will report + these statistics when |tracing_stats| is sufficiently large. + +*/ + +/*tex how much memory is in use */ + +int dyn_used; + +/*tex head of the list of available one-word nodes */ + +halfword avail; + +/*tex the last one-word node used in |mem| */ + +unsigned fix_mem_end; + +/*tex head of a junk list, write only */ + +halfword garbage; + +/*tex head of a temporary list of some kind */ + +halfword temp_token_head; + +/*tex head of a temporary list of another kind */ + +halfword hold_token_head; + +/*tex a constant token list */ + +halfword omit_template; + +/*tex permanently empty list */ + +halfword null_list; + +/*tex head of token list built by |scan_keyword| */ + +halfword backup_head; + +void initialize_tokens(void) +{ + halfword p; + avail = null; + fix_mem_end = 0; + p = get_avail(); + temp_token_head = p; + set_token_info(temp_token_head, 0); + p = get_avail(); + hold_token_head = p; + set_token_info(hold_token_head, 0); + p = get_avail(); + omit_template = p; + set_token_info(omit_template, 0); + p = get_avail(); + null_list = p; + set_token_info(null_list, 0); + p = get_avail(); + backup_head = p; + set_token_info(backup_head, 0); + p = get_avail(); + garbage = p; + set_token_info(garbage, 0); + dyn_used = 0; +} + +/*tex + + The function |get_avail| returns a pointer to a new one-word node whose + |link| field is null. However, \TeX\ will halt if there is no more room left. + + If the available-space list is empty, i.e., if |avail=null|, we try first to + increase |fix_mem_end|. If that cannot be done, i.e., if + |fix_mem_end=fix_mem_max|, we try to reallocate array |fixmem|. If, that + doesn't work, we have to quit. + + Single-word node allocation: +*/ + +halfword get_avail(void) +{ + /*tex The new node being got: */ + unsigned p; + unsigned t; + /*tex Get top location in the |avail| stack. */ + p = (unsigned) avail; + if (p != null) { + /*tex Pop it off. */ + avail = token_link(avail); + } else if (fix_mem_end < fix_mem_max) { + /*tex Go into virgin territory. */ + incr(fix_mem_end); + p = fix_mem_end; + } else { + /*tex The big dynamic storage area. */ + smemory_word *new_fixmem; + t = (fix_mem_max / 5); + new_fixmem = fixmemcast(realloc(fixmem, sizeof(smemory_word) * (fix_mem_max + t + 1))); + if (new_fixmem == NULL) { + /*tex If memory is exhausted, display possible runaway text. */ + runaway(); + overflow("token memory size", fix_mem_max); + } else { + fixmem = new_fixmem; + } + memset(voidcast(fixmem + fix_mem_max + 1), 0, t * sizeof(smemory_word)); + fix_mem_max += t; + p = ++fix_mem_end; + } + /*tex Provide an oft-desired initialization of the new node. */ + token_link(p) = null; + /*tex Maintain statistics. */ + incr(dyn_used); + return (halfword) p; +} + +/*tex + + The procedure |flush_list(p)| frees an entire linked list of one-word nodes + that starts at position |p|. + + This makes list of single-word nodes available: + +*/ + +void flush_list(halfword p) +{ + halfword q, r; + if (p != null) { + r = p; + do { + q = r; + r = token_link(r); + decr(dyn_used); + } while (r != null); + /*tex Now |q| is the last node on the list. */ + token_link(q) = avail; + avail = p; + } +} + +/*tex + + A \TeX\ token is either a character or a control sequence, and it is + represented internally in one of two ways: (1)~A character whose ASCII code + number is |c| and whose command code is |m| is represented as the number + $2^{21}m+c$; the command code is in the range |1<=m<=14|. (2)~A control + sequence whose |eqtb| address is |p| is represented as the number + |cs_token_flag+p|. Here |cs_token_flag=t=| $2^{25}-1$ is larger than + $2^{21}m+c$, yet it is small enough that |cs_token_flag+p< max_halfword|; + thus, a token fits comfortably in a halfword. + + A token |t| represents a |left_brace| command if and only if + |t<left_brace_limit|; it represents a |right_brace| command if and only if we + have |left_brace_limit<=t<right_brace_limit|; and it represents a |match| or + |end_match| command if and only if |match_token<=t<=end_match_token|. The + following definitions take care of these token-oriented constants and a few + others. + + A token list is a singly linked list of one-word nodes in |mem|, where each + word contains a token and a link. Macro definitions, output-routine + definitions, marks, \.{\\write} texts, and a few other things are remembered + by \TeX\ in the form of token lists, usually preceded by a node with a + reference count in its |token_ref_count| field. The token stored in location + |p| is called |info(p)|. + + Three special commands appear in the token lists of macro definitions. When + |m=match|, it means that \TeX\ should scan a parameter for the current macro; + when |m=end_match|, it means that parameter matching should end and \TeX\ + should start reading the macro text; and when |m=out_param|, it means that + \TeX\ should insert parameter number |c| into the text at this point. + + The enclosing \.{\char'173} and \.{\char'175} characters of a macro + definition are omitted, but the final right brace of an output routine is + included at the end of its token list. + + Here is an example macro definition that illustrates these conventions. After + \TeX\ processes the text + + \starttyping + \def\mac a#1#2 \b {#1\-a ##1#2 \#2\} + \stoptyping + + the definition of \.{\\mac} is represented as a token list containing + + \starttyping + (reference count) |letter|a |match|# |match|# |spacer| \b |end_match| + |out_param|1 \- |letter|a |spacer|, |mac_param|# |other_char|1 + |out_param|2 |spacer| |out_param|2 + \stoptyping + + The procedure |scan_toks| builds such token lists, and |macro_call| does the + parameter matching. + + Examples such as \type{\def\m{\def\m{a} b}} explain why reference + counts would be needed even if \TeX\ had no \.{\\let} operation: When the + token list for \.{\\m} is being read, the redefinition of \.{\\m} changes the + |eqtb| entry before the token list has been fully consumed, so we dare not + simply destroy a token list when its control sequence is being redefined. + + If the parameter-matching part of a definition ends with `\.{\#\{}', the + corresponding token list will have `\.\{' just before the `|end_match|' and + also at the very end. The first `\.\{' is used to delimit the parameter; the + second one keeps the first from disappearing. + + The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in symbolic + form, including the expansion of a macro or mark. + +*/ + +void print_meaning(void) +{ + /*tex + + This would make sense but some macro packages don't like it: + + \starttyping + if (cur_cmd == math_given_cmd) { + cur_cmd = xmath_given_cmd ; + } + \stoptyping + + */ + print_cmd_chr((quarterword) cur_cmd, cur_chr); + if (cur_cmd >= call_cmd) { + print_char(':'); + print_ln(); + token_show(cur_chr); + } else { + /*tex Show the meaning of a mark node. */ + if ((cur_cmd == top_bot_mark_cmd) && (cur_chr < marks_code)) { + print_char(':'); + print_ln(); + switch (cur_chr) { + case first_mark_code: + token_show(first_mark(0)); + break; + case bot_mark_code: + token_show(bot_mark(0)); + break; + case split_first_mark_code: + token_show(split_first_mark(0)); + break; + case split_bot_mark_code: + token_show(split_bot_mark(0)); + break; + default: + token_show(top_mark(0)); + break; + } + } + } +} + +/*tex + + The procedure |show_token_list|, which prints a symbolic form of the token + list that starts at a given node |p|, illustrates these conventions. The + token list being displayed should not begin with a reference count. However, + the procedure is intended to be robust, so that if the memory links are awry + or if |p| is not really a pointer to a token list, nothing catastrophic will + happen. + + An additional parameter |q| is also given; this parameter is either null or + it points to a node in the token list where a certain magic computation takes + place that will be explained later. (Basically, |q| is non-null when we are + printing the two-line context information at the time of an error message; + |q| marks the place corresponding to where the second line should begin.) + + For example, if |p| points to the node containing the first \.a in the token + list above, then |show_token_list| will print the string $$\hbox{`\.{a\#1\#2\ + \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$ and if |q| points to the node containing + the second \.a, the magic computation will be performed just before the + second \.a is printed. + + The generation will stop, and `\.{\\ETC.}' will be printed, if the length of + printing exceeds a given limit~|l|. Anomalous entries are printed in the form + of control sequences that are not followed by a blank space, e.g., + `\.{\\BAD.}'; this cannot be confused with actual control sequences because a + real control sequence named \.{BAD} would come out `\.{\\BAD\ }'. + +*/ + +#define not_so_bad(p) \ + switch (m) { \ + case assign_int_cmd: \ + if (c >= (backend_int_base) && c <= (backend_int_last)) \ + p("[internal backend integer]"); \ + break; \ + case assign_dimen_cmd: \ + if (c >= (backend_dimen_base) && c <= (backend_dimen_last)) \ + p("[internal backend dimension]"); \ + break; \ + case assign_toks_cmd: \ + if (c >= (backend_toks_base) && c <= (backend_toks_last)) \ + p("[internal backend tokenlist]"); \ + break; \ + case node_cmd: \ + p("[internal node pointer]"); \ + break; \ + case lua_call_cmd: \ + p("[internal lua function call]"); \ + break; \ + case lua_expandable_call_cmd: \ + p("[internal expandable lua function call]"); \ + break; \ + case lua_local_call_cmd: \ + p("[internal local lua function call]"); \ + break; \ + default: \ + p("BAD"); \ + break; \ + } + +void show_token_list(int p, int q, int l) +{ + /*tex pieces of a token */ + int m, c; + /*tex character used in a `|match|' */ + ASCII_code match_chr = '#'; + /*tex the highest parameter number, as an ASCII digit */ + ASCII_code n = '0'; + tally = 0; + if (l < 0) + l = 0x3FFFFFFF; + while ((p != null) && (tally < l)) { + if (p == q) { + /*tex Do magic computation. */ + set_trick_count(); + } + /*tex Display token |p|, and |return| if there are problems. */ + if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) { + tprint_esc("CLOBBERED."); + return; + } + if (token_info(p) >= cs_token_flag) { + if (!((inhibit_par_tokens) && (token_info(p) == par_token))) + print_cs(token_info(p) - cs_token_flag); + } else { + m = token_cmd(token_info(p)); + c = token_chr(token_info(p)); + if (token_info(p) < 0) { + tprint_esc("BAD"); + } else { + /* + Display the token \type {(|m|,|c|)}. The procedure usually + ``learns'' the character code used for macro parameters by + seeing one in a |match| command before it runs into any + |out_param| commands. + + */ + switch (m) { + case left_brace_cmd: + case right_brace_cmd: + case math_shift_cmd: + case tab_mark_cmd: + case sup_mark_cmd: + case sub_mark_cmd: + case spacer_cmd: + case letter_cmd: + case other_char_cmd: + print(c); + break; + case mac_param_cmd: + if (!in_lua_escape && (is_in_csname==0)) + print(c); + print(c); + break; + case out_param_cmd: + print(match_chr); + if (c <= 9) { + print_char(c + '0'); + } else { + print_char('!'); + return; + } + break; + case match_cmd: + match_chr = c; + print(c); + incr(n); + print_char(n); + if (n > '9') + return; + break; + case end_match_cmd: + if (c == 0) + tprint("->"); + break; + default: + not_so_bad(tprint); + break; + } + } + } + p = token_link(p); + } + if (p != null) + tprint_esc("ETC."); +} + +#define do_buffer_to_unichar(a,b) do { \ + a = (halfword)str2uni(buffer+b); \ + b += utf8_size(a); \ +} while (0) + +/*tex + + Here's the way we sometimes want to display a token list, given a pointer to + its reference count; the pointer may be null. + +*/ + +void token_show(halfword p) +{ + if (p != null) + show_token_list(token_link(p), null, 10000000); +} + +/*tex + + |delete_token_ref|, is called when a pointer to a token list's reference + count is being removed. This means that the token list should disappear if + the reference count was |null|, otherwise the count should be decreased by + one. + + |p| points to the reference count of a token list that is losing one + reference. + +*/ + +void delete_token_ref(halfword p) +{ + if (token_ref_count(p) == 0) + flush_list(p); + else + decr(token_ref_count(p)); +} + +int get_char_cat_code(int curchr) +{ + int a; + do_get_cat_code(a,curchr); + return a; +} + +static void invalid_character_error(void) +{ + const char *hlp[] = { + "A funny symbol that I can't read has just been input.", + "Continue, and I'll forget that it ever happened.", + NULL + }; + deletions_allowed = false; + tex_error("Text line contains an invalid character", hlp); + deletions_allowed = true; +} + +static boolean process_sup_mark(void); + +static int scan_control_sequence(void); + +typedef enum { + next_line_ok, + next_line_return, + next_line_restart +} next_line_retval; + +static next_line_retval next_line(void); + +/*tex + + In case you are getting bored, here is a slightly less trivial routine: Given + a string of lowercase letters, like `\.{pt}' or `\.{plus}' or `\.{width}', + the |scan_keyword| routine checks to see whether the next tokens of input + match this string. The match must be exact, except that uppercase letters + will match their lowercase counterparts; uppercase equivalents are determined + by subtracting |"a"-"A"|, rather than using the |uc_code| table, since \TeX\ + uses this routine only for its own limited set of keywords. + + If a match is found, the characters are effectively removed from the input + and |true| is returned. Otherwise |false| is returned, and the input is left + essentially unchanged (except for the fact that some macros may have been + expanded, etc.). + +*/ + +boolean scan_keyword(const char *s) +{ + /*tex tail of the backup list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex index into |str_pool| */ + const char *k; + halfword save_cur_cs = cur_cs; + if (strlen(s) == 0) { + /*tex but not with newtokenlib zero keyword simply doesn't match */ + return false ; + } + p = backup_head; + token_link(p) = null; + k = s; + while (*k) { + /*tex Recursion is possible here! */ + get_x_token(); + if ((cur_cs == 0) && ((cur_chr == *k) || (cur_chr == *k - 'a' + 'A'))) { + store_new_token(cur_tok); + k++; + } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) { + back_input(); + if (p != backup_head) { + begin_token_list(token_link(backup_head), backed_up); + } + cur_cs = save_cur_cs; + return false; + } + } + if (token_link(backup_head) != null) + flush_list(token_link(backup_head)); + cur_cs = save_cur_cs; + return true; +} + +boolean scan_keyword_case_sensitive(const char *s) +{ + halfword p; + halfword q; + const char *k; + halfword save_cur_cs = cur_cs; + if (strlen(s) == 0) + return false ; + p = backup_head; + token_link(p) = null; + k = s; + while (*k) { + get_x_token(); + if ((cur_cs == 0) && (cur_chr == *k)) { + store_new_token(cur_tok); + k++; + } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) { + back_input(); + if (p != backup_head) { + begin_token_list(token_link(backup_head), backed_up); + } + cur_cs = save_cur_cs; + return false; + } + } + if (token_link(backup_head) != null) + flush_list(token_link(backup_head)); + cur_cs = save_cur_cs; + return true; +} + +/*tex + + We can not return |undefined_control_sequence| under some conditions (inside + |shift_case|, for example). This needs thinking. + +*/ + +/*static char * FFFF = "\xEF\xBF\xBF";*/ /* 0xFFFF */ + +halfword active_to_cs(int curchr, int force) +{ + halfword curcs; + int nncs = no_new_control_sequence; + if (force) { + no_new_control_sequence = false; + } + if (curchr > 0) { + char *b = (char *) uni2str((unsigned) curchr); + char *utfbytes = xmalloc(8); + utfbytes = strcpy(utfbytes, "\xEF\xBF\xBF"); + utfbytes = strcat(utfbytes, b); + free(b); + curcs = string_lookup(utfbytes, utf8_size(curchr)+3); + free(utfbytes); + } else { + /*tex 0xFFFF ... why not 3 ? */ + curcs = string_lookup("\xEF\xBF\xBF", 4); + } + no_new_control_sequence = nncs; + return curcs; +} + +/* + + static unsigned char *uni2csstr(unsigned unic) + { + unsigned char *buf = xmalloc(8); + unsigned char *pt = buf; + *pt++ = 239; *pt++ = 191; *pt++ = 191; // 0xFFFF + if (unic < 0x80) + *pt++ = (unsigned char) unic; + else if (unic < 0x800) { + *pt++ = (unsigned char) (0xc0 | (unic >> 6)); + *pt++ = (unsigned char) (0x80 | (unic & 0x3f)); + } else if (unic >= 0x110000) { + *pt++ = (unsigned char) (unic - 0x110000); + } else if (unic < 0x10000) { + *pt++ = (unsigned char) (0xe0 | (unic >> 12)); + *pt++ = (unsigned char) (0x80 | ((unic >> 6) & 0x3f)); + *pt++ = (unsigned char) (0x80 | (unic & 0x3f)); + } else { + int u, z, y, x; + unsigned val = unic - 0x10000; + u = (int) (((val & 0xf0000) >> 16) + 1); + z = (int) ((val & 0x0f000) >> 12); + y = (int) ((val & 0x00fc0) >> 6); + x = (int) (val & 0x0003f); + *pt++ = (unsigned char) (0xf0 | (u >> 2)); + *pt++ = (unsigned char) (0x80 | ((u & 3) << 4) | z); + *pt++ = (unsigned char) (0x80 | y); + *pt++ = (unsigned char) (0x80 | x); + } + *pt = '\0'; + return buf; + } + + halfword active_to_cs(int curchr, int force) + { + halfword curcs; + int nncs = no_new_control_sequence; + if (force) { + no_new_control_sequence = false; + } + if (curchr > 0) { + char * utfbytes = (char *) uni2csstr((unsigned) curchr); + curcs = string_lookup(utfbytes, utf8_size(curchr)+3); + free(utfbytes); + } else { + curcs = string_lookup(FFFF, 4); // 0xFFFF ... why not 3 ? + } + no_new_control_sequence = nncs; + return curcs; + } + +*/ + +/*tex + + Maybe this function should listen to \.{\\escapechar} but we can do without. + +*/ + +static char *cs_to_string(halfword p) +{ + const char *s; + char *sh; + int k = 0; + static char ret[256] = { 0 }; + if (p == 0 || p == null_cs) { + ret[k++] = '\\'; + s = "csname"; + while (*s) { + ret[k++] = *s++; + } + ret[k++] = '\\'; + s = "endcsname"; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + + } else { + str_number txt = cs_text(p); + sh = makecstring(txt); + s = sh; + if (is_active_cs(txt)) { + s = s + 3; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + } else { + ret[k++] = '\\'; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + } + free(sh); + } + return (char *) ret; +} + +/*tex This is sort of a hack. */ + +static char *cmd_chr_to_string(int cmd, int chr) +{ + char *s; + str_number str; + int sel = selector; + selector = new_string; + print_cmd_chr((quarterword) cmd, chr); + str = make_string(); + s = makecstring(str); + selector = sel; + flush_str(str); + return s; +} + +/*tex + + The heart of \TeX's input mechanism is the |get_next| procedure, which we + shall develop in the next few sections of the program. Perhaps we shouldn't + actually call it the ``heart,'' however, because it really acts as \TeX's + eyes and mouth, reading the source files and gobbling them up. And it also + helps \TeX\ to regurgitate stored token lists that are to be processed again. + + The main duty of |get_next| is to input one token and to set |cur_cmd| and + |cur_chr| to that token's command code and modifier. Furthermore, if the + input token is a control sequence, the |eqtb| location of that control + sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero. + + Underlying this simple description is a certain amount of complexity because + of all the cases that need to be handled. However, the inner loop of + |get_next| is reasonably short and fast. + + When |get_next| is asked to get the next token of a \.{\\read} line, it sets + |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens appear on that + line. (There might not be any tokens at all, if the |end_line_char| has + |ignore| as its catcode.) + + The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity is + needed because a blank line of input is supposed to be exactly equivalent to + the appearance of \.{\\par}; we must set |cur_cs:=par_loc| when detecting a + blank line. + +*/ + +/*tex + + The location of `\.{\\par}' in |eqtb| adn the token representing `\.{\\par}. + +*/ + +halfword par_loc; +halfword par_token; + +/*tex + + Parts |get_next| are executed more often than any other instructions of \TeX. + + The global variable |force_eof| is normally |false|; it is set |true| by an + \.{\\endinput} command. |luacstrings| is the number of lua print statements + waiting to be input, it is changed by |luatokencall|. + + +*/ + +/*tex Should the next \.{\\input} be aborted early? */ +boolean force_eof; + +/*tex How many lua strings are waiting to be input? */ + +int luacstrings; + +/*tex + + If the user has set the |pausing| parameter to some positive value, and if + nonstop mode has not been selected, each line of input is displayed on the + terminal and the transcript file, followed by `\.{=>}'. \TeX\ waits for a + response. If the response is simply |carriage_return|, the line is accepted + as it stands, otherwise the line typed is used instead of the line in the + file. + +*/ + +void firm_up_the_line(void) +{ + int k; + ilimit = last; + if (pausing_par > 0) { + if (interaction > nonstop_mode) { + wake_up_terminal(); + print_ln(); + if (istart < ilimit) { + for (k = istart; k <= ilimit - 1; k++) + print_char(buffer[k]); + } + first = ilimit; + prompt_input("=>"); + if (last > first) { + /*tex Move line down in buffer. */ + for (k = first; k < +last - 1; k++) + buffer[k + istart - first] = buffer[k]; + ilimit = istart + last - first; + } + } + } +} + +/*tex + + Before getting into |get_next|, let's consider the subroutine that is called + when an `\.{\\outer}' control sequence has been scanned or when the end of a + file has been reached. These two cases are distinguished by |cur_cs|, which + is zero at the end of a file. + +*/ + +void check_outer_validity(void) +{ + /*tex points to inserted token list */ + halfword p; + /*tex auxiliary pointer */ + halfword q; + if (suppress_outer_error_par) + return; + if (scanner_status != normal) { + deletions_allowed = false; + /*tex + + Back up an outer control sequence so that it can be reread. An + outer control sequence that occurs in a \.{\\read} will not be + reread, since the error recovery for \.{\\read} is not very powerful. + + */ + if (cur_cs != 0) { + if ((istate == token_list) || (iname < 1) || (iname > 17)) { + p = get_avail(); + token_info(p) = cs_token_flag + cur_cs; + /*tex prepare to read the control sequence again */ + begin_token_list(p, backed_up); + } + /*tex replace it by a space */ + cur_cmd = spacer_cmd; + cur_chr = ' '; + } + if (scanner_status > skipping) { + const char *errhlp[] = { + "I suspect you have forgotten a `}', causing me", + "to read past where you wanted me to stop.", + "I'll try to recover; but if the error is serious,", + "you'd better type `E' or `X' now and fix your file.", + NULL + }; + char errmsg[318]; + const char *startmsg; + const char *scannermsg; + /*tex + + Tell the user what has run away and try to recover Print a + definition, argument, or preamble. + + */ + runaway(); + if (cur_cs == 0) { + startmsg = "File ended"; + } else { + cur_cs = 0; + startmsg = "Forbidden control sequence found"; + } + /*tex + + Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or + `\.{text}', and insert tokens that should lead to recovery. The + recovery procedure can't be fully understood without knowing more + about the \TeX\ routines that should be aborted, but we can + sketch the ideas here: For a runaway definition we will insert a + right brace; for a runaway preamble, we will insert a special + \.{\\cr} token and a right brace; and for a runaway argument, we + will set |long_state| to |outer_call| and insert \.{\\par}. + + */ + p = get_avail(); + switch (scanner_status) { + case defining: + scannermsg = "definition"; + token_info(p) = right_brace_token + '}'; + break; + case matching: + scannermsg = "use"; + token_info(p) = par_token; + long_state = outer_call_cmd; + break; + case aligning: + scannermsg = "preamble"; + token_info(p) = right_brace_token + '}'; + q = p; + p = get_avail(); + token_link(p) = q; + token_info(p) = cs_token_flag + frozen_cr; + align_state = -1000000; + break; + case absorbing: + scannermsg = "text"; + token_info(p) = right_brace_token + '}'; + break; + default: + scannermsg = "unknown"; + break; + } + begin_token_list(p, inserted); + snprintf(errmsg, 318, "%s while scanning %s of %s", startmsg, scannermsg, cs_to_string(warning_index)); + tex_error(errmsg, errhlp); + } else { + char errmsg[256]; + const char *errhlp_no[] = { + "The file ended while I was skipping conditional text.", + "This kind of error happens when you say `\\if...' and forget", + "the matching `\\fi'. I've inserted a `\\fi'; this might work.", + NULL + }; + const char *errhlp_cs[] = { + "A forbidden control sequence occurred in skipped text.", + "This kind of error happens when you say `\\if...' and forget", + "the matching `\\fi'. I've inserted a `\\fi'; this might work.", + NULL + }; + const char **errhlp = (const char **) errhlp_no; + char *ss; + if (cur_cs != 0) { + errhlp = errhlp_cs; + cur_cs = 0; + } + ss = cmd_chr_to_string(if_test_cmd, cur_if); + snprintf(errmsg, 255, "Incomplete %s; all text was ignored after line %d", + ss, (int) skip_line); + free(ss); + /*tex Incomplete |\if...| */ + cur_tok = cs_token_flag + frozen_fi; + /*tex back up one inserted token and call |error|. */ + { + OK_to_interrupt = false; + back_input(); + token_type = inserted; + OK_to_interrupt = true; + tex_error(errmsg, errhlp); + } + } + deletions_allowed = true; + } +} + +#if 0 + +/*tex + + The other variant gives less clutter in tracing cache usage when profiling + and for some files (like the manual) also a bit of a speedup. + +*/ + +static boolean get_next_file(void) +{ + SWITCH: + if (iloc <= ilimit) { + /*tex current line not yet finished */ + do_buffer_to_unichar(cur_chr, iloc); + RESWITCH: + if (detokenized_line()) { + cur_cmd = (cur_chr == ' ' ? 10 : 12); + } else { + do_get_cat_code(cur_cmd, cur_chr); + } + /*tex + + Change state if necessary, and |goto switch| if the current + character should be ignored, or |goto reswitch| if the current + character changes to another; + + The following 48-way switch accomplishes the scanning quickly, + assuming that a decent C compiler has translated the code. Note that + the numeric values for |mid_line|, |skip_blanks|, and |new_line| are + spaced apart from each other by |max_char_code+1|, so we can add a + character's command code to the state to get a single number that + characterizes both. + + Remark [ls/hh]: checking performance indicated that this switch was + the cause of many branch prediction errors but changing it to: + + \starttyping + c = istate + cur_cmd; + if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) { + return true; + } else if (c >= new_line) { + switch (c) { + } + } else if (c >= skip_blanks) { + switch (c) { + } + } else if (c >= mid_line) { + switch (c) { + } + } else { + istate = mid_line; + return true; + } + \stoptyping + + gives as many prediction errors. So, we can indeed assume that the + compiler does the right job, or that there is simply no other way. + + */ + + switch (istate + cur_cmd) { + case mid_line + ignore_cmd: + case skip_blanks + ignore_cmd: + case new_line + ignore_cmd: + case skip_blanks + spacer_cmd: + case new_line + spacer_cmd: + /*tex Cases where character is ignored. */ + goto SWITCH; + break; + case mid_line + escape_cmd: + case new_line + escape_cmd: + case skip_blanks + escape_cmd: + /*tex Scan a control sequence. */ + istate = (unsigned char) scan_control_sequence(); + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + break; + case mid_line + active_char_cmd: + case new_line + active_char_cmd: + case skip_blanks + active_char_cmd: + /*tex Process an active-character. */ + cur_cs = active_to_cs(cur_chr, false); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + istate = mid_line; + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + break; + case mid_line + sup_mark_cmd: + case new_line + sup_mark_cmd: + case skip_blanks + sup_mark_cmd: + /*tex If this |sup_mark| starts. */ + if (process_sup_mark()) + goto RESWITCH; + else + istate = mid_line; + break; + case mid_line + invalid_char_cmd: + case new_line + invalid_char_cmd: + case skip_blanks + invalid_char_cmd: + /*tex Decry the invalid character and |goto restart|. */ + invalid_character_error(); + /*tex Because state may be |token_list| now: */ + return false; + break; + case mid_line + spacer_cmd: + /*tex Enter |skip_blanks| state, emit a space. */ + istate = skip_blanks; + cur_chr = ' '; + break; + case mid_line + car_ret_cmd: + /*tex + + Finish line, emit a space. When a character of type |spacer| + gets through, its character code is changed to $\.{"\ + "}=040$. This means that the ASCII codes for tab and space, + and for the space inserted at the end of a line, will be + treated alike when macro parameters are being matched. We do + this since such characters are indistinguishable on most + computer terminal displays. + + */ + iloc = ilimit + 1; + cur_cmd = spacer_cmd; + cur_chr = ' '; + break; + case skip_blanks + car_ret_cmd: + case mid_line + comment_cmd: + case new_line + comment_cmd: + case skip_blanks + comment_cmd: + /*tex Finish line, |goto switch|; */ + iloc = ilimit + 1; + goto SWITCH; + break; + case new_line + car_ret_cmd: + /*tex Finish line, emit a \.{\\par}; */ + iloc = ilimit + 1; + cur_cs = par_loc; + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + break; + case skip_blanks + left_brace_cmd: + case new_line + left_brace_cmd: + istate = mid_line; + /*tex Fall through. */ + case mid_line + left_brace_cmd: + align_state++; + break; + case skip_blanks + right_brace_cmd: + case new_line + right_brace_cmd: + istate = mid_line; + /*tex Fall through. */ + case mid_line + right_brace_cmd: + align_state--; + break; + case mid_line + math_shift_cmd: + case mid_line + tab_mark_cmd: + case mid_line + mac_param_cmd: + case mid_line + sub_mark_cmd: + case mid_line + letter_cmd: + case mid_line + other_char_cmd: + break; + /* + case skip_blanks + math_shift: + case skip_blanks + tab_mark: + case skip_blanks + mac_param: + case skip_blanks + sub_mark: + case skip_blanks + letter: + case skip_blanks + other_char: + case new_line + math_shift: + case new_line + tab_mark: + case new_line + mac_param: + case new_line + sub_mark: + case new_line + letter: + case new_line + other_char: + */ + default: + istate = mid_line; + break; + } + } else { + if (iname != 21) + istate = new_line; + /*tex + + Move to next line of file, or |goto restart| if there is no next line, + or |return| if a \.{\\read} line has finished; + + */ + do { + next_line_retval r = next_line(); + if (r == next_line_return) { + return true; + } else if (r == next_line_restart) { + return false; + } + } while (0); + check_interrupt(); + goto SWITCH; + } + return true; +} + +#else + +/*tex + + This variant gives 10 times less Bim in vallgrind! + +*/ + +static boolean get_next_file(void) +{ + int c = 0; + SWITCH: + if (iloc <= ilimit) { + /*tex current line not yet finished */ + do_buffer_to_unichar(cur_chr, iloc); + RESWITCH: + if (detokenized_line()) { + cur_cmd = (cur_chr == ' ' ? 10 : 12); + } else { + do_get_cat_code(cur_cmd, cur_chr); + } + /*tex + + Change state if necessary, and |goto switch| if the current character + should be ignored, or |goto reswitch| if the current character changes + to another. + + */ + c = istate + cur_cmd; + if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) { + return true; + } else if (c >= new_line) { + switch (c-new_line) { + case escape_cmd: + istate = (unsigned char) scan_control_sequence(); + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + return true; + case left_brace_cmd: + istate = mid_line; + align_state++; + return true; + case right_brace_cmd: + istate = mid_line; + align_state--; + return true; + case math_shift_cmd: + istate = mid_line; + return true; + case tab_mark_cmd: + istate = mid_line; + return true; + case car_ret_cmd: + /*tex Finish line, emit a \.{\\par}. */ + iloc = ilimit + 1; + cur_cs = par_loc; + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + return true; + case mac_param_cmd: + istate = mid_line; + return true; + case sup_mark_cmd: + if (process_sup_mark()) + goto RESWITCH; + else + istate = mid_line; + return true; + case sub_mark_cmd: + istate = mid_line; + return true; + case ignore_cmd: + goto SWITCH; + return true; + case spacer_cmd: + /*tex Cases where character is ignored. */ + goto SWITCH; + case letter_cmd: + istate = mid_line; + return true; + case other_char_cmd: + istate = mid_line; + return true; + case active_char_cmd: + cur_cs = active_to_cs(cur_chr, false); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + istate = mid_line; + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + return true; + case comment_cmd: + iloc = ilimit + 1; + goto SWITCH; + case invalid_char_cmd: + /*tex Because state may be |token_list| now. */ + invalid_character_error(); + return false; + default: + istate = mid_line; + return true; + } + } else if (c >= skip_blanks) { + switch (c-skip_blanks) { + case escape_cmd: + /*tex Scan a control sequence. */ + istate = (unsigned char) scan_control_sequence(); + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + return true; + case left_brace_cmd: + istate = mid_line; + align_state++; + return true; + case right_brace_cmd: + istate = mid_line; + align_state--; + return true; + case math_shift_cmd: + istate = mid_line; + return true; + case tab_mark_cmd: + istate = mid_line; + return true; + case car_ret_cmd: + iloc = ilimit + 1; + goto SWITCH; + case mac_param_cmd: + istate = mid_line; + return true; + case sup_mark_cmd: + if (process_sup_mark()) + goto RESWITCH; + else + istate = mid_line; + return true; + case sub_mark_cmd: + istate = mid_line; + return true; + case ignore_cmd: + goto SWITCH; + case spacer_cmd: + goto SWITCH; + case letter_cmd: + istate = mid_line; + return true; + case other_char_cmd: + istate = mid_line; + return true; + case active_char_cmd: + cur_cs = active_to_cs(cur_chr, false); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + istate = mid_line; + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + return true; + case comment_cmd: + /*tex Finish line, |goto switch|. */ + iloc = ilimit + 1; + goto SWITCH; + case invalid_char_cmd: + /*tex Decry the invalid character and |goto restart|. */ + invalid_character_error(); + /*tex Because state may be |token_list| now. */ + return false; + default: + istate = mid_line; + return true; + } + } else if (c >= mid_line) { + switch (c-mid_line) { + case escape_cmd: + istate = (unsigned char) scan_control_sequence(); + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + return true; + case left_brace_cmd: + align_state++; + return true; + case right_brace_cmd: + align_state--; + return true; + case math_shift_cmd: + return true; + case tab_mark_cmd: + return true; + case car_ret_cmd: + /*tex + + Finish line, emit a space. When a character of type + |spacer| gets through, its character code is changed to + $\.{"\ "}=040$. This means that the ASCII codes for tab + and space, and for the space inserted at the end of a + line, will be treated alike when macro parameters are + being matched. We do this since such characters are + indistinguishable on most computer terminal displays. + + */ + iloc = ilimit + 1; + cur_cmd = spacer_cmd; + cur_chr = ' '; + return true; + case mac_param_cmd: + return true; + case sup_mark_cmd: + if (process_sup_mark()) + goto RESWITCH; + else + istate = mid_line; + return true; + case sub_mark_cmd: + return true; + case ignore_cmd: + goto SWITCH; + case spacer_cmd: + /*tex Enter |skip_blanks| state, emit a space. */ + istate = skip_blanks; + cur_chr = ' '; + return true; + case letter_cmd: + istate = mid_line; + return true; + case other_char_cmd: + istate = mid_line; + return true; + case active_char_cmd: + cur_cs = active_to_cs(cur_chr, false); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + istate = mid_line; + if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) + check_outer_validity(); + return true; + case comment_cmd: + iloc = ilimit + 1; + goto SWITCH; + case invalid_char_cmd: + /*tex Because state may be |token_list| now. */ + invalid_character_error(); + return false; + default: + istate = mid_line; + return true; + } + } else { + istate = mid_line; + return true; + } + } else { + if (iname != 21) { + istate = new_line; + } + /*tex + + Move to next line of file, or |goto restart| if there is no next + line, or |return| if a \.{\\read} line has finished; + + */ + do { + next_line_retval r = next_line(); + if (r == next_line_return) { + return true; + } else if (r == next_line_restart) { + return false; + } + } while (0); + check_interrupt(); + goto SWITCH; + } + return true; +} + +#endif + +/*tex + + Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit. + We only support a limited set: + + \starttyping + ^^^^^^XXXXXX + ^^^^XXXXXX + ^^XX ^^<char> + \stoptyping + +*/ + +#define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f')) + +#define add_nybble(c) \ + if (c<='9') { \ + cur_chr=(cur_chr<<4)+c-'0'; \ + } else { \ + cur_chr=(cur_chr<<4)+c-'a'+10; \ + } + +#define set_nybble(c) \ + if (c<='9') { \ + cur_chr=c-'0'; \ + } else { \ + cur_chr=c-'a'+10; \ + } + +#define one_hex_to_cur_chr(c1) \ + set_nybble(c1); + +#define two_hex_to_cur_chr(c1,c2) \ + set_nybble(c1); \ + add_nybble(c2); + +#define four_hex_to_cur_chr(c1,c2,c3,c4) \ + two_hex_to_cur_chr(c1,c2); \ + add_nybble(c3); \ + add_nybble(c4); + +#define six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6) \ + four_hex_to_cur_chr(c1,c2,c3,c4); \ + add_nybble(c5); \ + add_nybble(c6); + +static boolean process_sup_mark(void) +{ + if (cur_chr == buffer[iloc]) { + if (iloc < ilimit) { + if ((cur_chr == buffer[iloc + 1]) && (cur_chr == buffer[iloc + 2])) { + if ((cur_chr == buffer[iloc + 3]) && (cur_chr == buffer[iloc + 4])) { + /*tex |^^^^^^XXXXXX| */ + if ((iloc + 10) <= ilimit) { + int c1 = buffer[iloc + 5]; + int c2 = buffer[iloc + 6]; + int c3 = buffer[iloc + 7]; + int c4 = buffer[iloc + 8]; + int c5 = buffer[iloc + 9]; + int c6 = buffer[iloc + 10]; + if (is_hex(c1) && is_hex(c2) && is_hex(c3) && + is_hex(c4) && is_hex(c5) && is_hex(c6)) { + iloc = iloc + 11; + six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6); + return true; + } else { + tex_error("^^^^^^ needs six hex digits", NULL); + } + } else { + tex_error("^^^^^^ needs six hex digits, end of input", NULL); + } + } else { + /*tex |^^^^XXXX| */ + if ((iloc + 6) <= ilimit) { + int c1 = buffer[iloc + 3]; + int c2 = buffer[iloc + 4]; + int c3 = buffer[iloc + 5]; + int c4 = buffer[iloc + 6]; + if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4)) { + iloc = iloc + 7; + four_hex_to_cur_chr(c1,c2,c3,c4); + return true; + } else { + tex_error("^^^^ needs four hex digits", NULL); + } + } else { + tex_error("^^^^ needs four hex digits, end of input", NULL); + } + } + } else { + /*tex |^^XX| */ + if ((iloc + 2) <= ilimit) { + int c1 = buffer[iloc + 1]; + int c2 = buffer[iloc + 2]; + if (is_hex(c1) && is_hex(c2)) { + iloc = iloc + 3; + two_hex_to_cur_chr(c1,c2); + return true; + } + } + /*tex Go on, no error, good old \TEX . */ + } + } + /*tex The rest. */ + { + int c1 = buffer[iloc + 1]; + if (c1 < 0200) { + iloc = iloc + 2; + if (is_hex(c1) && (iloc <= ilimit)) { + int c2 = buffer[iloc]; + if (is_hex(c2)) { + incr(iloc); + two_hex_to_cur_chr(c1,c2); + return true; + } + } + cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100); + return true; + } + } + } + return false; +} + +/*tex + + Control sequence names are scanned only when they appear in some line of a + file; once they have been scanned the first time, their |eqtb| location + serves as a unique identification, so \TeX\ doesn't need to refer to the + original name any more except when it prints the equivalent in symbolic form. + + The program that scans a control sequence has been written carefully in order + to avoid the blowups that might otherwise occur if a malicious user tried + something like `\.{\\catcode\'15=0}'. The algorithm might look at + |buffer[ilimit+1]|, but it never looks at |buffer[ilimit+2]|. + + If expanded characters like `\.{\^\^A}' or `\.{\^\^df}' appear in or just + following a control sequence name, they are converted to single characters in + the buffer and the process is repeated, slowly but surely. + +*/ + +static boolean check_expanded_code(int *kk); /* below */ + +static int scan_control_sequence(void) +{ + int retval = mid_line; + if (iloc > ilimit) { + /*tex |state| is irrelevant in this case. */ + cur_cs = null_cs; + } else { + /*tex |cat_code(cur_chr)|, usually: */ + register int cat; + while (1) { + int k = iloc; + do_buffer_to_unichar(cur_chr, k); + do_get_cat_code(cat, cur_chr); + if (cat != letter_cmd || k > ilimit) { + retval = (cat == spacer_cmd ? skip_blanks : mid_line); + /*tex If an expanded \unknown */ + if (cat == sup_mark_cmd && check_expanded_code(&k)) + continue; + } else { + retval = skip_blanks; + do { + do_buffer_to_unichar(cur_chr, k); + do_get_cat_code(cat, cur_chr); + } while (cat == letter_cmd && k <= ilimit); + /*tex If an expanded \unknown */ + if (cat == sup_mark_cmd && check_expanded_code(&k)) + continue; + if (cat != letter_cmd) { + /*tex Backtrack one character which can be \UTF. */ + if (cur_chr <= 0x7F) { + k -= 1; /* in most cases */ + } else if (cur_chr > 0xFFFF) { + k -= 4; + } else if (cur_chr > 0x7FF) { + k -= 3; + } else /* if (cur_chr > 0x7F) */ { + k -= 2; + } + /*tex Now |k| points to first nonletter. */ + } + } + cur_cs = id_lookup(iloc, k - iloc); + iloc = k; + break; + } + } + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + return retval; +} + +/*tex + + Whenever we reach the following piece of code, we will have + |cur_chr=buffer[k-1]| and |k<=ilimit+1| and + |cat=get_cat_code(cat_code_table,cur_chr)|. If an expanded code like + \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)..(k+1)]| or + |buffer[(k-1)..(k+2)]|, we will store the corresponding code in |buffer[k-1]| + and shift the rest of the buffer left two or three places. + +*/ + +static boolean check_expanded_code(int *kk) +{ + int l; + int k = *kk; + int d = 1; + if (buffer[k] == cur_chr && k < ilimit) { + if ((cur_chr == buffer[k + 1]) && (cur_chr == buffer[k + 2])) { + if ((cur_chr == buffer[k + 3]) && (cur_chr == buffer[k + 4])) { + if ((k + 10) <= ilimit) { + int c1 = buffer[k + 6 - 1]; + int c2 = buffer[k + 6]; + int c3 = buffer[k + 6 + 1]; + int c4 = buffer[k + 6 + 2]; + int c5 = buffer[k + 6 + 3]; + int c6 = buffer[k + 6 + 4]; + if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4) && is_hex(c5) && is_hex(c6)) { + d = 6; + six_hex_to_cur_chr(c1,c2,c3,c4,c5,c6); + } else { + tex_error("^^^^^^ needs six hex digits", NULL); + } + } else { + tex_error("^^^^^^ needs six hex digits, end of input", NULL); + } + } else { + if ((k + 6) <= ilimit) { + int c1 = buffer[k + 4 - 1]; + int c2 = buffer[k + 4]; + int c3 = buffer[k + 4 + 1]; + int c4 = buffer[k + 4 + 2]; + if (is_hex(c1) && is_hex(c2) && is_hex(c3) && is_hex(c4)) { + d = 4; + four_hex_to_cur_chr(c1,c2,c3,c4); + } else { + tex_error("^^^^ needs four hex digits", NULL); + } + } else { + tex_error("^^^^ needs four hex digits, end of input", NULL); + } + } + } else { + int c1 = buffer[k + 1]; + if (c1 < 0200) { + d = 1; + if (is_hex(c1) && (k + 2) <= ilimit) { + int c2 = buffer[k + 2]; + if (is_hex(c2)) { + d = 2; + two_hex_to_cur_chr(c1,c2); + } else { + cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100); + } + } else { + cur_chr = (c1 < 0100 ? c1 + 0100 : c1 - 0100); + } + } + } + if (d > 2) + d = 2 * d - 1; + else + d++; + if (cur_chr <= 0x7F) { + buffer[k - 1] = (packed_ASCII_code) cur_chr; + } else if (cur_chr <= 0x7FF) { + buffer[k - 1] = (packed_ASCII_code) (0xC0 + cur_chr / 0x40); + k++; + d--; + buffer[k - 1] = (packed_ASCII_code) (0x80 + cur_chr % 0x40); + } else if (cur_chr <= 0xFFFF) { + buffer[k - 1] = (packed_ASCII_code) (0xE0 + cur_chr / 0x1000); + k++; + d--; + buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) / 0x40); + k++; + d--; + buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x1000) % 0x40); + } else { + buffer[k - 1] = (packed_ASCII_code) (0xF0 + cur_chr / 0x40000); + k++; + d--; + buffer[k - 1] = (packed_ASCII_code) (0x80 + (cur_chr % 0x40000) / 0x1000); + k++; + d--; + buffer[k - 1] = (packed_ASCII_code) (0x80 + ((cur_chr % 0x40000) % 0x1000) / 0x40); + k++; + d--; + buffer[k - 1] = (packed_ASCII_code) (0x80 + ((cur_chr % 0x40000) % 0x1000) % 0x40); + } + l = k; + ilimit = ilimit - d; + while (l <= ilimit) { + buffer[l] = buffer[l + d]; + l++; + } + *kk = k; + return true; + } + return false; +} + +/*tex + + All of the easy branches of |get_next| have now been taken care of. There is + one more branch. + +*/ + +static next_line_retval next_line(void) +{ + /*tex A way to end a pseudo file without trailing space: */ + boolean inhibit_eol = false; + if (iname > 17) { + /*tex + + Read next line of file into |buffer|, or |goto restart| if the file + has ended. + + */ + incr(line); + first = istart; + if (!force_eof) { + if (iname <= 20) { + if (pseudo_input()) { + /*tex Not end of file; set |ilimit|. */ + firm_up_the_line(); + line_catcode_table = DEFAULT_CAT_TABLE; + if ((iname == 19) && (pseudo_lines(pseudo_files) == null)) + inhibit_eol = true; + } else if ((every_eof_par != null) && !eof_seen[iindex]) { + ilimit = first - 1; + /*tex Fake one empty line. */ + eof_seen[iindex] = true; + if (iname != 19) + begin_token_list(every_eof_par, every_eof_text); + return next_line_restart; + } else { + force_eof = true; + } + } else { + if (iname == 21) { + halfword n = null; + int t = luacstring_input(&n); + switch (t) { + case 0: + force_eof = true; + break; + case 1: + /*tex string */ + firm_up_the_line(); + line_catcode_table = (short) luacstring_cattable(); + line_partial = (signed char) luacstring_partial(); + if (luacstring_final_line() || line_partial || line_catcode_table == NO_CAT_TABLE) + inhibit_eol = true; + if (!line_partial) + istate = new_line; + break; + case 2: + /*tex token */ + cur_tok = n; + back_input(); + /*tex Needs checking. */ + return next_line_restart; + break; + case 3: + /*tex node */ + if (n < biggest_char) { + /*tex |0x10FFFF == 1114111| */ + cur_tok = token_val(node_cmd, n); + back_input(); + /*tex Needs checking. */ + return next_line_restart; + } else { + normal_warning("nodes","unable to store reference from lua in tex"); + force_eof = true; + } + break; + default: + force_eof = true; + break; + } + } else if (lua_input_ln(cur_file, 0, true)) { + /*tex Not end of file, set |ilimit|. */ + firm_up_the_line(); + line_catcode_table = DEFAULT_CAT_TABLE; + } else if ((every_eof_par != null) && (!eof_seen[iindex])) { + ilimit = first - 1; + /* tex Fake one empty line. */ + eof_seen[iindex] = true; + begin_token_list(every_eof_par, every_eof_text); + return next_line_restart; + } else { + force_eof = true; + } + } + } + if (force_eof) { + if (tracing_nesting_par > 0) + if ((grp_stack[in_open] != cur_boundary) || (if_stack[in_open] != cond_ptr)) + if (!((iname == 19) || (iname == 21))) { + /*tex Give warning for some unfinished groups and/or conditionals. */ + file_warning(); + } + if ((iname > 21) || (iname == 20)) { + report_stop_file(filetype_tex); + decr(open_parens); + } + force_eof = false; + /*tex \LUA\ input or \.{\\scantextokens} */ + if (iname == 21 || iname == 19) { + end_file_reading(); + } else { + end_file_reading(); + if (! suppress_outer_error_par) + check_outer_validity(); + } + return next_line_restart; + } + if (inhibit_eol || end_line_char_inactive) + ilimit--; + else + buffer[ilimit] = (packed_ASCII_code) end_line_char_par; + first = ilimit + 1; + iloc = istart; + /*tex We're ready to read. */ + } else { + if (!terminal_input) { + /*tex \.{\\read} line has ended */ + cur_cmd = 0; + cur_chr = 0; + return next_line_return; + } + if (input_ptr > 0) { + /*tex Text was inserted during error recovery. */ + end_file_reading(); + /*tex Resume previous level. */ + return next_line_restart; + } + if (selector < log_only) + open_log_file(); + if (interaction > nonstop_mode) { + if (end_line_char_inactive) + ilimit++; + if (ilimit == istart) { + /*tex Previous line was empty. */ + tprint_nl("(Please type a command or say `\\end')"); + } + print_ln(); + first = istart; + /*tex Input on-line into |buffer| */ + prompt_input("*"); + ilimit = last; + if (end_line_char_inactive) + ilimit--; + else + buffer[ilimit] = (packed_ASCII_code) end_line_char_par; + first = ilimit + 1; + iloc = istart; + } else { + /*tex + + Nonstop mode, which is intended for overnight batch processing, + never waits for on-line input. + + */ + fatal_error("*** (job aborted, no legal \\end found)"); + } + } + return next_line_ok; +} + +/*tex + + Let's consider now what happens when |get_next| is looking at a token list. + +*/ + +static boolean get_next_tokenlist(void) +{ + register halfword t = token_info(iloc); + /*tex Move to next. */ + iloc = token_link(iloc); + if (t >= cs_token_flag) { + /*tex A control sequence token */ + cur_cs = t - cs_token_flag; + cur_cmd = eq_type(cur_cs); + if (cur_cmd >= outer_call_cmd) { + if (cur_cmd == dont_expand_cmd) { + /*tex + + Get the next token, suppressing expansion. The present point + in the program is reached only when the |expand| routine has + inserted a special marker into the input. In this special + case, |token_info(iloc)| is known to be a control sequence + token, and |token_link(iloc)=null|. + + */ + cur_cs = token_info(iloc) - cs_token_flag; + iloc = null; + cur_cmd = eq_type(cur_cs); + if (cur_cmd > max_command_cmd) { + cur_cmd = relax_cmd; + cur_chr = no_expand_flag; + return true; + } + } else if (! suppress_outer_error_par) { + check_outer_validity(); + } + } + cur_chr = equiv(cur_cs); + } else { + cur_cmd = token_cmd(t); + cur_chr = token_chr(t); + switch (cur_cmd) { + case left_brace_cmd: + align_state++; + break; + case right_brace_cmd: + align_state--; + break; + case out_param_cmd: + /*tex Insert macro parameter and |goto restart|. */ + begin_token_list(param_stack[param_start + cur_chr - 1], parameter); + return false; + break; + } + } + return true; +} + +/*tex + + Now we're ready to take the plunge into |get_next| itself. Parts of this + routine are executed more often than any other instructions of \TeX. + + This sets |cur_cmd|, |cur_chr|, |cur_cs| to next token: + +*/ + +void get_next(void) +{ + RESTART: + cur_cs = 0; + if (istate != token_list) { + /*tex Input from external file, |goto restart| if no input found. */ + if (!get_next_file()) + goto RESTART; + } else { + if (iloc == null) { + end_token_list(); + /*tex List exhausted, resume previous level. */ + goto RESTART; + } else if (!get_next_tokenlist()) { + /*tex Parameter needs to be expanded. */ + goto RESTART; + } + } + /*tex If an alignment entry has just ended, take appropriate action. */ + if ((cur_cmd == tab_mark_cmd || cur_cmd == car_ret_cmd) && align_state == 0) { + insert_vj_template(); + goto RESTART; + } +} + +/*tex + + Since |get_next| is used so frequently in \TeX, it is convenient to define + three related procedures that do a little more: + + \startitemize + \startitem + |get_token| not only sets |cur_cmd| and |cur_chr|, it also sets + |cur_tok|, a packed halfword version of the current token. + \stopitem + \startitem + |get_x_token|, meaning ``get an expanded token,'' is like + |get_token|, but if the current token turns out to be a user-defined + control sequence (i.e., a macro call), or a conditional, or something + like \.{\\topmark} or \.{\\expandafter} or \.{\\csname}, it is + eliminated from the input by beginning the expansion of the macro or + the evaluation of the conditional. + \stopitem + \startitem + |x_token| is like |get_x_token| except that it assumes that + |get_next| has already been called. + \stopitem + \stopitemize + + In fact, these three procedures account for almost every use of |get_next|. + No new control sequences will be defined except during a call of |get_token|, + or when \.{\\csname} compresses a token list, because + |no_new_control_sequence| is always |true| at other times. + + This sets |cur_cmd|, |cur_chr|, |cur_tok|: + +*/ + +void get_token(void) +{ + no_new_control_sequence = false; + get_next(); + no_new_control_sequence = true; + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + +/*tex This changes the string |s| to a token list. */ + +halfword string_to_toks(const char *ss) +{ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex token being appended */ + halfword t; + const char *s = ss; + const char *se = ss + strlen(s); + p = temp_token_head; + set_token_link(p, null); + while (s < se) { + t = (halfword) str2uni((const unsigned char *) s); + s += utf8_size(t); + if (t == ' ') + t = space_token; + else + t = other_token + t; + fast_store_new_token(t); + } + return token_link(temp_token_head); +} + +/*tex + + The token lists for macros and for other things like \.{\\mark} and + \.{\\output} and \.{\\write} are produced by a procedure called |scan_toks|. + + Before we get into the details of |scan_toks|, let's consider a much simpler + task, that of converting the current string into a token list. The |str_toks| + function does this; it classifies spaces as type |spacer| and everything else + as type |other_char|. + + The token list created by |str_toks| begins at |link(temp_token_head)| and + ends at the value |p| that is returned. (If |p=temp_token_head|, the list is + empty.) + + |lua_str_toks| is almost identical, but it also escapes the three symbols + that |lua| considers special while scanning a literal string. + + This changes the string |str_pool[b..pool_ptr]| to a token list: + +*/ + +halfword lua_str_toks(lstring b) +{ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex token being appended */ + halfword t; + /*tex index into string */ + unsigned char *k; + p = temp_token_head; + set_token_link(p, null); + k = (unsigned char *) b.s; + while (k < (unsigned char *) b.s + b.l) { + t = pool_to_unichar(k); + k += utf8_size(t); + if (t == ' ') { + t = space_token; + } else { + if ((t == '\\') || (t == '"') || (t == '\'') || (t == 10) || (t == 13)) + fast_store_new_token(other_token + '\\'); + if (t == 10) + t = 'n'; + if (t == 13) + t = 'r'; + t = other_token + t; + } + fast_store_new_token(t); + } + return p; +} + +/*tex + + Incidentally, the main reason for wanting |str_toks| is the function + |the_toks|, which has similar input/output characteristics. + + This changes the string |str_pool[b..pool_ptr]| to a token list: + +*/ + +halfword str_toks(lstring s) +{ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex token being appended */ + halfword t; + /*tex index into string */ + unsigned char *k, *l; + p = temp_token_head; + set_token_link(p, null); + k = s.s; + l = k + s.l; + while (k < l) { + t = pool_to_unichar(k); + k += utf8_size(t); + if (t == ' ') + t = space_token; + else + t = other_token + t; + fast_store_new_token(t); + } + return p; +} + +/*tex + + Most of the converter is similar to the one i made for macro so at some point + I can make a helper; also todo: there is no need to go through the pool. + +*/ + +/*tex Change the string |str_pool[b..pool_ptr]| to a token list. */ + +halfword str_scan_toks(int ct, lstring s) +{ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex token being appended */ + halfword t; + /*tex index into string */ + unsigned char *k, *l; + int cc; + p = temp_token_head; + set_token_link(p, null); + k = s.s; + l = k + s.l; + while (k < l) { + t = pool_to_unichar(k); + k += utf8_size(t); + cc = get_cat_code(ct,t); + if (cc == 0) { + /*tex We have a potential control sequence so we check for it. */ + int _lname = 0 ; + int _s = 0 ; + int _c = 0 ; + halfword _cs = null ; + unsigned char *_name = k ; + while (k < l) { + t = (halfword) str2uni((const unsigned char *) k); + _s = utf8_size(t); + _c = get_cat_code(ct,t); + if (_c == 11) { + k += _s ; + _lname = _lname + _s ; + } else if (_c == 10) { + /*tex We ignore a trailing space like normal scanning does. */ + k += _s ; + break ; + } else { + break ; + } + } + if (_s > 0) { + /*tex We have a potential |\cs|. */ + _cs = string_lookup((const char *) _name, _lname); + if (_cs == undefined_control_sequence) { + /* let's play safe and backtrack */ + t = cc * (1<<21) + t ; + k = _name ; + } else { + t = cs_token_flag + _cs; + } + } else { + /*tex + + Just a character with some meaning, so |\unknown| becomes + effectively |\unknown| assuming that |\\| has some useful + meaning of course. + + */ + t = cc * (1<<21) + t ; + k = _name ; + } + + } else { + /*tex + + Whatever token, so for instance $x^2$ just works given a \TEX\ + catcode regime. + + */ + t = cc * (1<<21) + t ; + } + fast_store_new_token(t); + + } + return p; +} + +/*tex + + Here's part of the |expand| subroutine that we are now ready to complete: + +*/ + +void ins_the_toks(void) +{ + (void) the_toks(); + ins_list(token_link(temp_token_head)); +} + +#define set_toks_register(n,t,g) do { \ + int a = (g>0) ? 4 : 0; \ + halfword ref = get_avail(); \ + set_token_ref_count(ref, 0); \ + set_token_link(ref, token_link(t)); \ + define(n + toks_base, call_cmd, ref); \ +} while (0) + +#define append_copied_toks_list(s,t) do { \ + halfword p; \ + halfword q; \ + p = temp_token_head; \ + set_token_link(p, null); \ + while (s != null) { \ + fast_store_new_token(token_info(s)); \ + s = token_link(s); \ + } \ + while (t != null) { \ + fast_store_new_token(token_info(t)); \ + t = token_link(t); \ + } \ + } while (0) + + +/*tex + + \starttabulate[|T||T||] + \NC 0 \NC \type {toksapp} \NC 1 \NC \type {etoksapp} \NC \NR + \NC 2 \NC \type {tokspre} \NC 3 \NC \type {etokspre} \NC \NR + \NC 4 \NC \type {gtoksapp} \NC 5 \NC \type {xtoksapp} \NC \NR + \NC 6 \NC \type {gtokspre} \NC 7 \NC \type {xtokspre} \NC \NR + \stoptabulate + +*/ + +# define immediate_permitted(n,target) ((eq_level(toks_base + n) == cur_level) && (token_ref_count(target) == 0)) + +void combine_the_toks(void) +{ + halfword how = cur_chr; + halfword source = null; + halfword target = null; + halfword append = (how == 0) || (how == 1) || (how == 4) || (how == 5); + halfword expand = odd(how); + halfword global = how > 3; + halfword nt, ns, s, t, p, q, h; + get_x_token(); + /*tex The target. */ + if (cur_cmd == assign_toks_cmd) { + /*tex Check range. */ + nt = equiv(cur_cs) - toks_base; + } else { + back_input(); + scan_register_num(); + nt = cur_val; + } + /*tex The source. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_cmd == left_brace_cmd) { + back_input(); + scan_toks(false,expand); + source = def_ref; + /*tex The action. */ + if (source != null) { + target = toks(nt); + if (target == null) { + set_toks_register(nt,source,global); + token_link(source) = null; + } else { + s = token_link(source); + if (s != null) { + t = token_link(target); + if (t == null) { + /*tex Can this happen? */ + set_token_link(target, s); + token_link(source) = null; + } else if (append) { + /*tex Append. */ + if (immediate_permitted(nt,target)) { + p = t; + while (token_link(p) != null) { + p = token_link(p); + } + while (s != null) { + fast_store_new_token(token_info(s)); + s = token_link(s); + } + } else { + append_copied_toks_list(t,s); + set_toks_register(nt,temp_token_head,global); + } + } else { + /* prepend */ + if (immediate_permitted(nt,target)) { + h = null; + p = null ; + while (s != null) { + fast_store_new_token(token_info(s)); + if (h == null) { + h = p; + } + s = token_link(s); + } + set_token_link(p,t); + set_token_link(target,h); + } else { + append_copied_toks_list(s,t); + set_toks_register(nt,temp_token_head,global); + } + } + } + } + flush_list(source); + } + } else { + if (cur_cmd == assign_toks_cmd) { + ns = equiv(cur_cs) - toks_base; + /*tex Check range. */ + } else { + scan_register_num(); + ns = cur_val; + } + /*tex The action. */ + source = toks(ns); + if (source != null) { + target = toks(nt); + if (target == null) { + /*tex The assign. */ + token_ref_count(source)++; + equiv(toks_base+nt) = source; + return; + } + s = token_link(source); + t = token_link(target); + if (append) { + /*tex Append. */ + if (immediate_permitted(nt,target)) { + p = t; + while (token_link(p) != null) { + p = token_link(p); + } + while (s != null) { + fast_store_new_token(token_info(s)); + s = token_link(s); + } + } else { + append_copied_toks_list(t,s); + set_toks_register(nt,temp_token_head,global); + } + } else { + /*tex Prepend. */ + if (immediate_permitted(nt,target)) { + h = null; + p = null; + while (s != null) { + fast_store_new_token(token_info(s)); + if (h == null) { + h = p; + } + s = token_link(s); + } + set_token_link(p,t); + set_token_link(target,h); + } else { + append_copied_toks_list(s,t); + set_toks_register(nt,temp_token_head,global); + } + } + } + } +} + +/*tex + + This routine, used in the next one, prints the job name, possibly modified by + the |process_jobname| callback. + +*/ + +static void print_job_name(void) +{ + if (job_name) { + /*tex C strings for jobname before and after processing. */ + char *s, *ss; + int callback_id, lua_retval; + s = (char*)str_string(job_name); + callback_id = callback_defined(process_jobname_callback); + if (callback_id > 0) { + lua_retval = run_callback(callback_id, "S->S", s, &ss); + if ((lua_retval == true) && (ss != NULL)) + s = ss; + } + tprint(s); + } else { + print(job_name); + } +} + +/*tex + + Here is a routine that print the result of a convert command, using the + argument |i|. It returns |false | if it does not know to print the code |c|. + The function exists because lua code and tex code can both call it to convert + something. + + Parse optional \LUA\ state integer, or an instance name to be stored in |sn| + and get the next non-blank non-relax non-call token. + +*/ + +int scan_lua_state(void) +{ + int sn = 0; + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + back_input(); + if (cur_cmd != left_brace_cmd) { + if (scan_keyword("name")) { + (void) scan_toks(false, true); + sn = def_ref; + } else { + scan_register_num(); + if (get_lua_name(cur_val)) + sn = (cur_val - 65536); + } + } + return sn; +} + +/*tex + + The procedure |conv_toks| uses |str_toks| to insert the token list for + |convert| functions into the scanner; `\.{\\outer}' control sequences are + allowed to follow `\.{\\string}' and `\.{\\meaning}'. + + The extra temp string |u| is needed because |pdf_scan_ext_toks| incorporates + any pending string in its output. In order to save such a pending string, we + have to create a temporary string that is destroyed immediately after. + +*/ + +#define push_selector { \ + old_setting = selector; \ + selector = new_string; \ +} + +#define pop_selector { \ + selector = old_setting; \ +} + +static int do_variable_dvi(halfword c) +{ + return 0; +} + +#define do_variable_backend_int(i) \ + cur_cmd = assign_int_cmd; \ + cur_val = backend_int_base + i; \ + cur_tok = token_val(cur_cmd, cur_val); \ + back_input(); + +#define do_variable_backend_dimen(i) \ + cur_cmd = assign_dimen_cmd; \ + cur_val = backend_dimen_base + i; \ + cur_tok = token_val(cur_cmd, cur_val); \ + back_input(); + +#define do_variable_backend_toks(i) \ + cur_cmd = assign_toks_cmd; \ + cur_val = backend_toks_base + i ; \ + cur_tok = token_val(cur_cmd, cur_val); \ + back_input(); + +static int do_variable_pdf(halfword c) +{ + if (scan_keyword("compresslevel")) { do_variable_backend_int(c_pdf_compress_level); } + else if (scan_keyword("decimaldigits")) { do_variable_backend_int(c_pdf_decimal_digits); } + else if (scan_keyword("imageresolution")) { do_variable_backend_int(c_pdf_image_resolution); } + else if (scan_keyword("pkresolution")) { do_variable_backend_int(c_pdf_pk_resolution); } + else if (scan_keyword("uniqueresname")) { do_variable_backend_int(c_pdf_unique_resname); } + else if (scan_keyword("majorversion")) { do_variable_backend_int(c_pdf_major_version); } + else if (scan_keyword("minorversion")) { do_variable_backend_int(c_pdf_minor_version); } + else if (scan_keyword("pagebox")) { do_variable_backend_int(c_pdf_pagebox); } + else if (scan_keyword("inclusionerrorlevel")) { do_variable_backend_int(c_pdf_inclusion_errorlevel); } + else if (scan_keyword("ignoreunknownimages")) { do_variable_backend_int(c_pdf_ignore_unknown_images); } + else if (scan_keyword("gamma")) { do_variable_backend_int(c_pdf_gamma); } + else if (scan_keyword("imageapplygamma")) { do_variable_backend_int(c_pdf_image_apply_gamma); } + else if (scan_keyword("imagegamma")) { do_variable_backend_int(c_pdf_image_gamma); } + else if (scan_keyword("imagehicolor")) { do_variable_backend_int(c_pdf_image_hicolor); } + else if (scan_keyword("imageaddfilename")) { do_variable_backend_int(c_pdf_image_addfilename); } + else if (scan_keyword("objcompresslevel")) { do_variable_backend_int(c_pdf_obj_compress_level); } + else if (scan_keyword("inclusioncopyfonts")) { do_variable_backend_int(c_pdf_inclusion_copy_font); } + else if (scan_keyword("gentounicode")) { do_variable_backend_int(c_pdf_gen_tounicode); } + else if (scan_keyword("pkfixeddpi")) { do_variable_backend_int(c_pdf_pk_fixed_dpi); } + else if (scan_keyword("suppressoptionalinfo")) { do_variable_backend_int(c_pdf_suppress_optional_info); } + else if (scan_keyword("omitcidset")) { do_variable_backend_int(c_pdf_omit_cidset); } + else if (scan_keyword("omitcharset")) { do_variable_backend_int(c_pdf_omit_charset); } + else if (scan_keyword("recompress")) { do_variable_backend_int(c_pdf_recompress); } + + else if (scan_keyword("horigin")) { do_variable_backend_dimen(d_pdf_h_origin); } + else if (scan_keyword("vorigin")) { do_variable_backend_dimen(d_pdf_v_origin); } + else if (scan_keyword("threadmargin")) { do_variable_backend_dimen(d_pdf_thread_margin); } + else if (scan_keyword("destmargin")) { do_variable_backend_dimen(d_pdf_dest_margin); } + else if (scan_keyword("linkmargin")) { do_variable_backend_dimen(d_pdf_link_margin); } + else if (scan_keyword("xformmargin")) { do_variable_backend_dimen(d_pdf_xform_margin); } + + else if (scan_keyword("pageattr")) { do_variable_backend_toks(t_pdf_page_attr); } + else if (scan_keyword("pageresources")) { do_variable_backend_toks(t_pdf_page_resources); } + else if (scan_keyword("pagesattr")) { do_variable_backend_toks(t_pdf_pages_attr); } + else if (scan_keyword("xformattr")) { do_variable_backend_toks(t_pdf_xform_attr); } + else if (scan_keyword("xformresources")) { do_variable_backend_toks(t_pdf_xform_resources); } + else if (scan_keyword("pkmode")) { do_variable_backend_toks(t_pdf_pk_mode); } + else if (scan_keyword("trailerid")) { do_variable_backend_toks(t_pdf_trailer_id); } + + else + return 0; + return 1; +} + +static int do_feedback_dvi(halfword c) +{ + return 0; +} + +/*tex Codes not really needed but cleaner when testing */ + +#define pdftex_version 140 /* these values will not change any more */ +#define pdftex_revision "0" /* these values will not change any more */ + +static int do_feedback_pdf(halfword c) +{ + /*tex holds |selector| setting */ + int old_setting; + /*tex |scanner_status| upon entry */ + int save_scanner_status; + /*tex |def_ref| upon entry, important if inside `\.{\\message}' */ + halfword save_def_ref; + halfword save_warning_index; + /*tex temp boolean */ + boolean bool; + /*tex first temp string */ + str_number s; + /*tex for use with |set_ff| */ + int ff; + /*tex third temp string, will become non-nil if a string is already being built */ + str_number u = 0; + /*tex color stack init str */ + char *str; + if (scan_keyword("lastlink")) { + push_selector; + print_int(pdf_last_link); + pop_selector; + } else if (scan_keyword("retval")) { + push_selector; + print_int(pdf_retval); + pop_selector; + } else if (scan_keyword("lastobj")) { + push_selector; + print_int(pdf_last_obj); + pop_selector; + } else if (scan_keyword("lastannot")) { + push_selector; + print_int(pdf_last_annot); + pop_selector; + } else if (scan_keyword("xformname")) { + scan_int(); + check_obj_type(static_pdf, obj_type_xform, cur_val); + push_selector; + print_int(obj_info(static_pdf, cur_val)); + pop_selector; + } else if (scan_keyword("creationdate")) { + ins_list(string_to_toks(getcreationdate(static_pdf))); + /*tex No further action. */ + return 2; + } else if (scan_keyword("fontname")) { + scan_font_ident(); + if (cur_val == null_font) + normal_error("pdf backend", "invalid font identifier when asking 'fontname'"); + pdf_check_vf(cur_val); + if (!font_used(cur_val)) + pdf_init_font(static_pdf, cur_val); + push_selector; + set_ff(cur_val); + print_int(obj_info(static_pdf, pdf_font_num(ff))); + pop_selector; + } else if (scan_keyword("fontobjnum")) { + scan_font_ident(); + if (cur_val == null_font) + normal_error("pdf backend", "invalid font identifier when asking 'objnum'"); + pdf_check_vf(cur_val); + if (!font_used(cur_val)) + pdf_init_font(static_pdf, cur_val); + push_selector; + set_ff(cur_val); + print_int(pdf_font_num(ff)); + pop_selector; + } else if (scan_keyword("fontsize")) { + scan_font_ident(); + if (cur_val == null_font) + normal_error("pdf backend", "invalid font identifier when asking 'fontsize'"); + push_selector; + print_scaled(font_size(cur_val)); + tprint("pt"); + pop_selector; + } else if (scan_keyword("pageref")) { + scan_int(); + if (cur_val <= 0) + normal_error("pdf backend", "invalid page number when asking 'pageref'"); + push_selector; + print_int(pdf_get_obj(static_pdf, obj_type_page, cur_val, false)); + pop_selector; + } else if (scan_keyword("colorstackinit")) { + bool = scan_keyword("page"); + if (scan_keyword("direct")) + cur_val = direct_always; + else if (scan_keyword("page")) + cur_val = direct_page; + else if (scan_keyword("text")) + cur_val = direct_text; + else if (scan_keyword("raw")) + cur_val = direct_raw; + else if (scan_keyword("origin")) + cur_val = set_origin; + else + cur_val = set_origin; + save_scanner_status = scanner_status; + save_warning_index = warning_index; + save_def_ref = def_ref; + u = save_cur_string(); + scan_toks(false, true); + s = tokens_to_string(def_ref); + delete_token_ref(def_ref); + def_ref = save_def_ref; + warning_index = save_warning_index; + scanner_status = save_scanner_status; + str = makecstring(s); + cur_val = newcolorstack(str, cur_val, bool); + free(str); + flush_str(s); + cur_val_level = int_val_level; + if (cur_val < 0) { + print_err("Too many color stacks"); + help2("The number of color stacks is limited to 32768.", + "I'll use the default color stack 0 here."); + error(); + cur_val = 0; + restore_cur_string(u); + } + push_selector; + print_int(cur_val); + pop_selector; + } else if (scan_keyword("version")) { + push_selector; + print_int(pdftex_version); + pop_selector; + } else if (scan_keyword("revision")) { + ins_list(string_to_toks(pdftex_revision)); + return 2; + } else { + return 0; + } + return 1; +} + +void conv_toks(void) +{ + /*tex holds |selector| setting */ + int old_setting; + halfword p, q; + /*tex |scanner_status| upon entry */ + int save_scanner_status; + /*tex |def_ref| upon entry, important if inside `\.{\\message}' */ + halfword save_def_ref; + halfword save_warning_index; + /*tex temp boolean */ + boolean bool; + /*tex first temp string */ + str_number s; + /*tex lua chunk name */ + int sn; + /*tex third temp string, will become non-nil if a string is already being built */ + str_number u = 0; + /*tex desired type of conversion */ + int c = cur_chr; + str_number str; + int i = 0; + /*tex Scan the argument for command |c|. */ + switch (c) { + case number_code: + scan_int(); + push_selector; + print_int(cur_val); + pop_selector; + break; + case lua_function_code: + scan_int(); + if (cur_val <= 0) { + normal_error("luafunction", "invalid number"); + } else { + u = save_cur_string(); + luacstrings = 0; + luafunctioncall(cur_val); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } + return; + break; + case lua_bytecode_code: + scan_int(); + if (cur_val < 0 || cur_val > 65535) { + normal_error("luabytecode", "invalid number"); + } else { + u = save_cur_string(); + luacstrings = 0; + luabytecodecall(cur_val); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } + return; + break; + case lua_code: + u = save_cur_string(); + save_scanner_status = scanner_status; + save_def_ref = def_ref; + save_warning_index = warning_index; + sn = scan_lua_state(); + scan_toks(false, true); + s = def_ref; + warning_index = save_warning_index; + def_ref = save_def_ref; + scanner_status = save_scanner_status; + luacstrings = 0; + luatokencall(s, sn); + delete_token_ref(s); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + /*tex No further action. */ + return; + break; + case expanded_code: + save_scanner_status = scanner_status; + save_warning_index = warning_index; + save_def_ref = def_ref; + u = save_cur_string(); + scan_toks(false, true); + warning_index = save_warning_index; + scanner_status = save_scanner_status; + ins_list(token_link(def_ref)); + token_link(def_ref) = null; + free_avail(def_ref); + def_ref = save_def_ref; + restore_cur_string(u); + /*tex No further action. */ + return; + break; + case immediate_assignment_code: + case immediate_assigned_code: + /*tex + + This is on-the-road-to-bachotex brain-wave but it needs a bit + more testing. A first variant did more in sequence till a relax + of spacer was seen (like do_assignments). It permits for instance + setting counters in full expansion. + + */ + save_scanner_status = scanner_status; + save_warning_index = warning_index; + save_def_ref = def_ref; + u = save_cur_string(); + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (c == immediate_assignment_code) { + /*tex one-step do_assignment */ + if (cur_cmd > max_non_prefixed_command) { + set_box_allowed = false; + prefixed_command(); + set_box_allowed = true; + } + /*tex done */ + } else { + /*tex pseudo token list do_assignment */ + if (cur_cmd == left_brace_cmd) { + while (1) { + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd == right_brace_cmd) { + break; + } else { + set_box_allowed = false; + prefixed_command(); + set_box_allowed = true; + } + } + } + /*tex done */ + } + warning_index = save_warning_index; + scanner_status = save_scanner_status; + def_ref = save_def_ref; + restore_cur_string(u); + return; + break; + case math_style_code: + push_selector; + print_math_style(); + pop_selector; + break; + case string_code: + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + push_selector; + if (cur_cs != 0) + sprint_cs(cur_cs); + else + print(cur_chr); + pop_selector; + break; + case cs_string_code: + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + push_selector; + if (cur_cs != 0) + sprint_cs_name(cur_cs); + else + print(cur_chr); + pop_selector; + break; + case roman_numeral_code: + scan_int(); + push_selector; + print_roman_int(cur_val); + pop_selector; + break; + case meaning_code: + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + push_selector; + print_meaning(); + pop_selector; + break; + case uchar_code: + scan_char_num(); + push_selector; + print(cur_val); + pop_selector; + break; + case lua_escape_string_code: + { + lstring escstr; + int l = 0; + save_scanner_status = scanner_status; + save_def_ref = def_ref; + save_warning_index = warning_index; + scan_toks(false, true); + bool = in_lua_escape; + in_lua_escape = true; + escstr.s = (unsigned char *) tokenlist_to_cstring(def_ref, false, &l); + escstr.l = (unsigned) l; + in_lua_escape = bool; + delete_token_ref(def_ref); + def_ref = save_def_ref; + warning_index = save_warning_index; + scanner_status = save_scanner_status; + (void) lua_str_toks(escstr); + ins_list(token_link(temp_token_head)); + free(escstr.s); + return; + } + /*tex no further action */ + break; + case font_id_code: + scan_font_ident(); + push_selector; + print_int(cur_val); + pop_selector; + break; + case font_name_code: + scan_font_ident(); + push_selector; + append_string((unsigned char *) font_name(cur_val),(unsigned) strlen(font_name(cur_val))); + if (font_size(cur_val) != font_dsize(cur_val)) { + tprint(" at "); + print_scaled(font_size(cur_val)); + tprint("pt"); + } + pop_selector; + break; + case left_margin_kern_code: + scan_int(); + if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node)) + normal_error("marginkern", "a non-empty hbox expected"); + push_selector; + p = list_ptr(box(cur_val)); + while ((p != null) && (type(p) == glue_node)) { + p = vlink(p); + } + if ((p != null) && (type(p) == margin_kern_node) && (subtype(p) == left_side)) + print_scaled(width(p)); + else + print_char('0'); + tprint("pt"); + pop_selector; + break; + case right_margin_kern_code: + scan_int(); + if ((box(cur_val) == null) || (type(box(cur_val)) != hlist_node)) + normal_error("marginkern", "a non-empty hbox expected"); + push_selector; + p = list_ptr(box(cur_val)); + if (p != null) { + p = tail_of_list(p); + /*tex + + There can be a leftskip, rightskip, penalty and yes, also a + disc node with a nesting node that points to glue spec ... + and we don't want to analyze that messy lot. + + */ + while ((p != null) && (type(p) == glue_node)) { + p = alink(p); + } + if ((p != null) && ! ((type(p) == margin_kern_node) && (subtype(p) == right_side))) { + if (type(p) == disc_node) { + q = alink(p); + if ((q != null) && ((type(q) == margin_kern_node) && (subtype(q) == right_side))) { + p = q; + } else { + /*tex + + Officially we should look in the replace but + currently protrusion doesn't work anyway with + "foo\discretionary{}{}{bar-} " (no following + char) so we don't need it now. + */ + } + } + } + } + if ((p != null) && (type(p) == margin_kern_node) && (subtype(p) == right_side)) + print_scaled(width(p)); + else + print_char('0'); + tprint("pt"); + pop_selector; + break; + case uniform_deviate_code: + scan_int(); + push_selector; + print_int(unif_rand(cur_val)); + pop_selector; + break; + case normal_deviate_code: + push_selector; + print_int(norm_rand()); + pop_selector; + break; + case math_char_class_code: + { + mathcodeval mval; + scan_int(); + mval = get_math_code(cur_val); + push_selector; + print_int(mval.class_value); + pop_selector; + } + break; + case math_char_fam_code: + { + mathcodeval mval; + scan_int(); + mval = get_math_code(cur_val); + push_selector; + print_int(mval.family_value); + pop_selector; + } + break; + case math_char_slot_code: + { + mathcodeval mval; + scan_int(); + mval = get_math_code(cur_val); + push_selector; + print_int(mval.character_value); + pop_selector; + } + break; + case insert_ht_code: + scan_register_num(); + push_selector; + i = cur_val; + p = page_ins_head; + while (i >= subtype(vlink(p))) + p = vlink(p); + if (subtype(p) == i) + print_scaled(height(p)); + else + print_char('0'); + tprint("pt"); + pop_selector; + break; + case job_name_code: + if (job_name == 0) + open_log_file(); + push_selector; + print_job_name(); + pop_selector; + break; + case format_name_code: + if (job_name == 0) + open_log_file(); + push_selector; + print(format_name); + pop_selector; + break; + case luatex_banner_code: + push_selector; + tprint(luatex_banner); + pop_selector; + break; + case luatex_revision_code: + push_selector; + print(get_luatexrevision()); + pop_selector; + break; + case etex_code: + push_selector; + tprint(eTeX_version_string); + pop_selector; + break; + case eTeX_revision_code: + push_selector; + tprint(eTeX_revision); + pop_selector; + break; + case font_identifier_code: + confusion("convert"); + break; + default: + confusion("convert"); + break; + } + str = make_string(); + (void) str_toks(str_lstring(str)); + flush_str(str); + ins_list(token_link(temp_token_head)); +} + +void do_feedback(void) +{ + int c = cur_chr; + str_number str; + int done = 1; + switch (c) { + case dvi_feedback_code: + if (get_o_mode() == OMODE_DVI) { + done = do_feedback_dvi(c); + } else { + tex_error("unexpected use of \\dvifeedback",null); + return ; + } + if (done==0) { + /* we recover */ + normal_warning("dvi backend","unexpected use of \\dvifeedback"); + return; + } else if (done==2) { + return; + } + break; + case pdf_feedback_code: + if (get_o_mode() == OMODE_PDF) { + done = do_feedback_pdf(c); + } else { + tex_error("unexpected use of \\pdffeedback",null); + return ; + } + if (done==0) { + /*tex We recover. */ + normal_warning("pdf backend","unexpected use of \\pdffeedback"); + return; + } else if (done==2) { + return; + } + break; + default: + confusion("feedback"); + break; + } + str = make_string(); + (void) str_toks(str_lstring(str)); + flush_str(str); + ins_list(token_link(temp_token_head)); +} + +void do_variable(void) +{ + int c = cur_chr; + int done = 1; + switch (c) { + case dvi_variable_code: + done = do_variable_dvi(c); + if (done==0) { + /*tex We recover. */ + normal_warning("dvi backend","unexpected use of \\dvivariable"); + } + return; + break; + case pdf_variable_code: + done = do_variable_pdf(c); + if (done==0) { + /*tex We recover. */ + normal_warning("pdf backend","unexpected use of \\pdfvariable"); + } + return; + break; + default: + confusion("variable"); + break; + } +} + +/*tex This boolean is keeping track of the lua string escape state */ + +boolean in_lua_escape; + +static int the_convert_string_dvi(halfword c, int i) +{ + return 0 ; +} + +static int the_convert_string_pdf(halfword c, int i) +{ + int ff; + if (get_o_mode() != OMODE_PDF) { + return 0; + } else if (scan_keyword("lastlink")) { + print_int(pdf_last_link); + } else if (scan_keyword("retval")) { + print_int(pdf_retval); + } else if (scan_keyword("lastobj")) { + print_int(pdf_last_obj); + } else if (scan_keyword("lastannot")) { + print_int(pdf_last_annot); + } else if (scan_keyword("xformname")) { + print_int(obj_info(static_pdf, i)); + } else if (scan_keyword("creationdate")) { + return 0; + } else if (scan_keyword("fontname")) { + set_ff(i); + print_int(obj_info(static_pdf, pdf_font_num(ff))); + } else if (scan_keyword("fontobjnum")) { + set_ff(i); + print_int(pdf_font_num(ff)); + } else if (scan_keyword("fontsize")) { + print_scaled(font_size(i)); + tprint("pt"); + } else if (scan_keyword("pageref")) { + print_int(pdf_get_obj(static_pdf, obj_type_page, i, false)); + } else if (scan_keyword("colorstackinit")) { + return 0; + } else { + return 0; + } + return 1; +} + +str_number the_convert_string(halfword c, int i) +{ + int old_setting; + str_number ret = 0; + boolean done = true ; + old_setting = selector; + selector = new_string; + switch (c) { + case number_code: + print_int(i); + break; + /* case lua_function_code: */ + /* case lua_code: */ + /* case expanded_code: */ + case math_style_code: + print_math_style(); + break; + /* case string_code: */ + /* case cs_string_code: */ + case roman_numeral_code: + print_roman_int(i); + break; + /* case meaning_code: */ + case uchar_code: + print(i); + break; + /* lua_escape_string_code: */ + case font_id_code: + print_int(i); + break; + case font_name_code: + append_string((unsigned char *) font_name(i),(unsigned) strlen(font_name(i))); + if (font_size(i) != font_dsize(i)) { + tprint(" at "); + print_scaled(font_size(i)); + tprint("pt"); + } + break; + /* left_margin_kern_code: */ + /* right_margin_kern_code: */ + case uniform_deviate_code: + print_int(unif_rand(i)); + break; + case normal_deviate_code: + print_int(norm_rand()); + break; + /* math_char_class_code: */ + /* math_char_fam_code: */ + /* math_char_slot_code: */ + /* insert_ht_code: */ + case job_name_code: + print_job_name(); + break; + case format_name_code: + print(format_name); + break; + case luatex_banner_code: + tprint(luatex_banner); + break; + case luatex_revision_code: + print(get_luatexrevision()); + break; + case etex_code: + tprint(eTeX_version_string); + break; + case eTeX_revision_code: + tprint(eTeX_revision); + break; + case font_identifier_code: + print_font_identifier(i); + break; + /*tex Backend: this might become obsolete! */ + case dvi_feedback_code: + done = the_convert_string_dvi(c,i); + break; + case pdf_feedback_code: + done = the_convert_string_pdf(c,i); + break; + /*tex done */ + default: + done = false; + break; + } + if (done) { + ret = make_string(); + } + selector = old_setting; + return ret; +} + +/*tex + + Another way to create a token list is via the \.{\\read} command. The sixteen + files potentially usable for reading appear in the following global + variables. The value of |read_open[n]| will be |closed| if stream number |n| + has not been opened or if it has been fully read; |just_open| if an + \.{\\openin} but not a \.{\\read} has been done; and |normal| if it is open + and ready to read the next line. + +*/ + +/*tex used for \.{\\read} */ + +FILE *read_file[16]; + +/*tex state of |read_file[n]| */ + +int read_open[17]; + +void initialize_read(void) +{ + int k; + for (k = 0; k <= 16; k++) + read_open[k] = closed; +} + +/*tex + + The |read_toks| procedure constructs a token list like that for any macro + definition, and makes |cur_val| point to it. Parameter |r| points to the + control sequence that will receive this token list. + +*/ + +void read_toks(int n, halfword r, halfword j) +{ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex saved value of |align_state| */ + int s; + /*tex stream number */ + int m; + scanner_status = defining; + warning_index = r; + p = get_avail(); + def_ref = p; + set_token_ref_count(def_ref, 0); + /*tex the reference count */ + p = def_ref; + store_new_token(end_match_token); + if ((n < 0) || (n > 15)) + m = 16; + else + m = n; + s = align_state; + /*tex disable tab marks, etc. */ + align_state = 1000000; + do { + /*tex Input and store tokens from the next line of the file. */ + begin_file_reading(); + iname = m + 1; + if (read_open[m] == closed) { + /*tex + + Input for \.{\\read} from the terminal. We input on-line into the + |buffer| array, prompting the user explicitly if |n>=0|. The + value of |n| is set negative so that additional prompts will not + be given in the case of multi-line input. + + */ + if (interaction > nonstop_mode) { + if (n < 0) { + prompt_input(""); + } else { + wake_up_terminal(); + print_ln(); + sprint_cs(r); + prompt_input(" ="); + n = -1; + } + } else { + fatal_error + ("*** (cannot \\read from terminal in nonstop modes)"); + } + + } else if (read_open[m] == just_open) { + /*tex + + Input the first line of |read_file[m]|. The first line of a file + must be treated specially, since |lua_input_ln| must be told not + to start with |get|. + + */ + if (lua_input_ln(read_file[m], (m + 1), false)) { + read_open[m] = normal; + } else { + lua_a_close_in(read_file[m], (m + 1)); + read_open[m] = closed; + } + + } else { + /*tex + + Input the next line of |read_file[m]|. An empty line is appended + at the end of a |read_file|. + + */ + if (!lua_input_ln(read_file[m], (m + 1), true)) { + lua_a_close_in(read_file[m], (m + 1)); + read_open[m] = closed; + if (align_state != 1000000) { + runaway(); + print_err("File ended within \\read"); + help1("This \\read has unbalanced braces."); + align_state = 1000000; + error(); + } + } + + } + ilimit = last; + if (end_line_char_inactive) + decr(ilimit); + else + buffer[ilimit] = (packed_ASCII_code) end_line_char_par; + first = ilimit + 1; + iloc = istart; + istate = new_line; + /*tex Handle \.{\\readline} and |goto done|. */ + if (j == 1) { + while (iloc <= ilimit) { + /*tex Current line not yet finished. */ + do_buffer_to_unichar(cur_chr, iloc); + if (cur_chr == ' ') + cur_tok = space_token; + else + cur_tok = cur_chr + other_token; + store_new_token(cur_tok); + } + } else { + while (1) { + get_token(); + if (cur_tok == 0) { + /*tex |cur_cmd=cur_chr=0| will occur at the end of the line. */ + break; + } + if (align_state < 1000000) { + /*tex Unmatched right brace aborts the line. */ + do { + get_token(); + } while (cur_tok != 0); + align_state = 1000000; + break; + } + store_new_token(cur_tok); + } + } + end_file_reading(); + + } while (align_state != 1000000); + cur_val = def_ref; + scanner_status = normal; + align_state = s; +} + +/*tex Return a string from tokens list: */ + +str_number tokens_to_string(halfword p) +{ + int old_setting; + if (selector == new_string) + normal_error("tokens","tokens_to_string() called while selector = new_string"); + old_setting = selector; + selector = new_string; + show_token_list(token_link(p), null, -1); + selector = old_setting; + return make_string(); +} + +/*tex + + Values like 512 and 128 also work ok. There is not much to gain in + optimization here. + +*/ + +#define alloci_default 1024 +#define alloci_step 256 + +#define make_room(a) \ + if ((unsigned)i+a+1>alloci) { \ + ret = xrealloc(ret,(alloci+alloci_step)); \ + alloci = alloci + alloci_step; \ + } + +#define append_i_byte(a) ret[i++] = (char)(a) + +#define Print_char(a) make_room(1); append_i_byte(a) + +#define Print_uchar(s) { \ + make_room(4); \ + if (s<=0x7F) { \ + append_i_byte(s); \ + } else if (s<=0x7FF) { \ + append_i_byte(0xC0 + (s / 0x40)); \ + append_i_byte(0x80 + (s % 0x40)); \ + } else if (s<=0xFFFF) { \ + append_i_byte(0xE0 + (s / 0x1000)); \ + append_i_byte(0x80 + ((s % 0x1000) / 0x40)); \ + append_i_byte(0x80 + ((s % 0x1000) % 0x40)); \ + } else if (s>=0x110000) { \ + append_i_byte(s-0x11000); \ + } else { \ + append_i_byte(0xF0 + (s / 0x40000)); \ + append_i_byte(0x80 + ((s % 0x40000) / 0x1000)); \ + append_i_byte(0x80 + (((s % 0x40000) % 0x1000) / 0x40)); \ + append_i_byte(0x80 + (((s % 0x40000) % 0x1000) % 0x40)); \ + } } + +#define Print_esc(b) { \ + const char *v = b; \ + if (e>0 && e<STRING_OFFSET) { \ + Print_uchar (e); \ + } \ + make_room(strlen(v)); \ + while (*v) { append_i_byte(*v); v++; } \ + } + +#define Print_str(b) { \ + const char *v = b; \ + make_room(strlen(v)); \ + while (*v) { append_i_byte(*v); v++; } \ + } + +#define is_cat_letter(a) \ + (get_char_cat_code(pool_to_unichar(str_string((a)))) == 11) + +/*tex + + The actual token conversion in this function is now functionally equivalent + to |show_token_list|, except that it always prints the whole token list. + TODO: check whether this causes problems in the lua library. + +*/ + +char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) +{ + register int p, c, m; + int q; + int infop; + char *s, *sh; + int e = 0; + char *ret; + int match_chr = '#'; + int n = '0'; + unsigned alloci = alloci_default; + int i = 0; + p = pp; + if (p == null) { + if (siz != NULL) + *siz = 0; + return NULL; + } + ret = xmalloc(alloci); + /*tex Skip refcount. */ + p = token_link(p); + if (p != null) { + e = escape_char_par; + } + while (p != null) { + if (p < (int) fix_mem_min || p > (int) fix_mem_end) { + Print_esc("CLOBBERED."); + break; + } + infop = token_info(p); + if (infop >= cs_token_flag) { + if (!(inhibit_par && infop == par_token)) { + q = infop - cs_token_flag; + if (q < hash_base) { + if (q == null_cs) { + Print_esc("csname"); + Print_esc("endcsname"); + } else { + Print_esc("IMPOSSIBLE."); + } + } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) { + Print_esc("IMPOSSIBLE."); + } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) { + Print_esc("NONEXISTENT."); + } else { + str_number txt = cs_text(q); + sh = makecstring(txt); + s = sh; + if (is_active_cs(txt)) { + s = s + 3; + while (*s) { + Print_char(*s); + s++; + } + } else { + if (e>=0 && e<0x110000) Print_uchar(e); + while (*s) { + Print_char(*s); + s++; + } + if ((!single_letter(txt)) || is_cat_letter(txt)) { + Print_char(' '); + } + } + free(sh); + } + } + } else { + if (infop < 0) { + Print_esc("BAD"); + } else { + m = token_cmd(infop); + c = token_chr(infop); + switch (m) { + case left_brace_cmd: + case right_brace_cmd: + case math_shift_cmd: + case tab_mark_cmd: + case sup_mark_cmd: + case sub_mark_cmd: + case spacer_cmd: + case letter_cmd: + case other_char_cmd: + Print_uchar(c); + break; + case mac_param_cmd: + if (!in_lua_escape && (is_in_csname==0)) + Print_uchar(c); + Print_uchar(c); + break; + case out_param_cmd: + Print_uchar(match_chr); + if (c <= 9) { + Print_char(c + '0'); + } else { + Print_char('!'); + goto EXIT; + } + break; + case match_cmd: + match_chr = c; + Print_uchar(c); + n++; + Print_char(n); + if (n > '9') + goto EXIT; + break; + case end_match_cmd: + if (c == 0) { + Print_char('-'); + Print_char('>'); + } + break; + default: + not_so_bad(Print_esc); + break; + } + } + } + p = token_link(p); + } + EXIT: + ret[i] = '\0'; + if (siz != NULL) + *siz = i; + return ret; +} + +char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz) +{ + register int p, c, m; + int q; + int infop; + char *s, *sh; + int e = 0; + char *ret; + int match_chr = '#'; + int n = '0'; + unsigned alloci = alloci_default; + int i = 0; + int skipping = 1; + p = pp; + if (p == null) { + if (siz != NULL) + *siz = 0; + return NULL; + } + ret = xmalloc(alloci); + /*tex Skip refcount. */ + p = token_link(p); + if (p != null) { + e = escape_char_par; + } + while (p != null) { + if (p < (int) fix_mem_min || p > (int) fix_mem_end) { + /*tex Nothing done. */ + break; + } + infop = token_info(p); + if (infop >= cs_token_flag) { + if (!(inhibit_par && infop == par_token)) { + q = infop - cs_token_flag; + if (q < hash_base) { + /*tex Nothing done. */ + } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) { + /*tex Nothing done. */ + } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) { + /*tex Nothing done. */ + } else if (!skipping) { + str_number txt = cs_text(q); + sh = makecstring(txt); + s = sh; + if (is_active_cs(txt)) { + s = s + 3; + while (*s) { + Print_char(*s); + s++; + } + } else { + if (e>=0 && e<0x110000) Print_uchar(e); + while (*s) { + Print_char(*s); + s++; + } + if ((!single_letter(txt)) || is_cat_letter(txt)) { + Print_char(' '); + } + } + free(sh); + } + } + } else { + if (infop < 0) { + /*tex Nothing done. */ + } else { + m = token_cmd(infop); + c = token_chr(infop); + switch (m) { + case left_brace_cmd: + case right_brace_cmd: + case math_shift_cmd: + case tab_mark_cmd: + case sup_mark_cmd: + case sub_mark_cmd: + case spacer_cmd: + case letter_cmd: + case other_char_cmd: + if (!skipping) { + Print_uchar(c); + } + break; + case mac_param_cmd: + if (!skipping) { + if (!in_lua_escape && (is_in_csname==0)) + Print_uchar(c); + Print_uchar(c); + } + break; + case out_param_cmd: + if (!skipping) { + Print_uchar(match_chr); + } + if (c <= 9) { + if (!skipping) { + Print_char(c + '0'); + } + } else { + /*tex Nothing done. */ + goto EXIT; + } + break; + case match_cmd: + match_chr = c; + if (!skipping) { + Print_uchar(c); + } + n++; + if (!skipping) { + Print_char(n); + } + if (n > '9') + goto EXIT; + break; + case end_match_cmd: + if (c == 0) { + if (!skipping) { + Print_char('-'); + Print_char('>'); + } + i = 0; + skipping = 0 ; + } + break; + default: + /*tex Nothing done. */ + break; + } + } + } + p = token_link(p); + } + EXIT: + ret[i] = '\0'; + if (siz != NULL) + *siz = i; + return ret; +} + +lstring *tokenlist_to_lstring(int pp, int inhibit_par) +{ + int siz; + lstring *ret = xmalloc(sizeof(lstring)); + ret->s = (unsigned char *) tokenlist_to_cstring(pp, inhibit_par, &siz); + ret->l = (size_t) siz; + return ret; +} + +void free_lstring(lstring * ls) +{ + if (ls == NULL) + return; + if (ls->s != NULL) + free(ls->s); + free(ls); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.h b/Build/source/texk/web2c/luatexdir/tex/textoken.h new file mode 100644 index 00000000000..248a12168cf --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.h @@ -0,0 +1,189 @@ +/* textoken.h + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + + +#ifndef TEXTOKEN_H +# define TEXTOKEN_H + +# define token_list 0 +# define null 0 +# define cs_token_flag 0x1FFFFFFF + +# define left_brace_token 0x0200000 /* $2^{21}\cdot|left_brace|$ */ +# define right_brace_token 0x0400000 /* $2^{21}\cdot|right_brace|$ */ +# define left_brace_limit 0x0400000 /* $2^{21}\cdot(|left_brace|+1)$ */ +# define right_brace_limit 0x0600000 /* $2^{21}\cdot(|right_brace|+1)$ */ +# define math_shift_token 0x0600000 /* $2^{21}\cdot|math_shift|$ */ +# define tab_token 0x0800000 /* $2^{21}\cdot|tab_mark|$ */ +# define out_param_token 0x0A00000 /* $2^{21}\cdot|out_param|$ */ +# define space_token 0x1400020 /* $2^{21}\cdot|spacer|+|" "|$ */ +# define letter_token 0x1600000 /* $2^{21}\cdot|letter|$ */ +# define other_token 0x1800000 /* $2^{21}\cdot|other_char|$ */ +# define match_token 0x1A00000 /* $2^{21}\cdot|match|$ */ +# define end_match_token 0x1C00000 /* $2^{21}\cdot|end_match|$ */ +# define protected_token 0x1C00001 /* $2^{21}\cdot|end_match|+1$ */ + +# include "tex/stringpool.h" + +typedef struct smemory_word_ { +# ifdef WORDS_BIGENDIAN + halfword hhrh; + halfword hhlh; +# else + halfword hhlh; + halfword hhrh; +# endif +} smemory_word; + +# define fix_mem_init 10000 + +extern smemory_word *fixmem; +extern unsigned fix_mem_min; +extern unsigned fix_mem_max; + +extern halfword garbage; /* head of a junk list, write only */ +extern halfword temp_token_head; /* head of a temporary list of some kind */ +extern halfword hold_token_head; /* head of a temporary list of another kind */ +extern halfword omit_template; /* a constant token list */ +extern halfword null_list; /* permanently empty list */ +extern halfword backup_head; /* head of token list built by |scan_keyword| */ + +extern void initialize_tokens(void); + +extern int dyn_used; + +# define token_info(a) fixmem[(a)].hhlh +# define token_link(a) fixmem[(a)].hhrh +# define set_token_info(a,b) fixmem[(a)].hhlh=(b) +# define set_token_link(a,b) fixmem[(a)].hhrh=(b) + +extern halfword avail; /* head of the list of available one-word nodes */ +extern unsigned fix_mem_end; /* the last one-word node used in |mem| */ + +extern halfword get_avail(void); + +/* A one-word node is recycled by calling |free_avail|. +This routine is part of \TeX's ``inner loop,'' so we want it to be fast. +*/ + +# define free_avail(A) do { /* single-word node liberation */ \ + token_link(A)=avail; avail=(A); decr(dyn_used); \ + } while (0) + +/* +There's also a |fast_get_avail| routine, which saves the procedure-call +overhead at the expense of extra programming. This routine is used in +the places that would otherwise account for the most calls of |get_avail|. +*/ + +# define fast_get_avail(A) do { \ + (A)=avail; /* avoid |get_avail| if possible, to save time */ \ + if ((A)==null) { (A)=get_avail(); } \ + else { avail=token_link((A)); token_link((A))=null; incr(dyn_used); } \ + } while (0) + +extern void print_meaning(void); + +extern void flush_list(halfword p); +extern void show_token_list(int p, int q, int l); +extern void token_show(halfword p); + +# define token_ref_count(a) token_info((a)) /* reference count preceding a token list */ +# define set_token_ref_count(a,b) token_info((a))=b +# define add_token_ref(a) token_ref_count(a)++ /* new reference to a token list */ + +# define store_new_token(a) do { \ + q=get_avail(); token_link(p)=q; token_info(q)=(a); p=q; \ + } while (0) + +# define fast_store_new_token(a) do { \ + fast_get_avail(q); token_link(p)=q; token_info(q)=(a); p=q; \ + } while (0) + +extern void delete_token_ref(halfword p); + +extern void make_token_table(lua_State * L, int cmd, int chr, int cs); + +# define NO_CAT_TABLE -2 +# define DEFAULT_CAT_TABLE -1 + +extern void get_next(void); +extern void check_outer_validity(void); +extern boolean scan_keyword(const char *); +extern boolean scan_keyword_case_sensitive(const char *); +extern halfword active_to_cs(int, int); +extern void get_token_lua(void); +halfword string_to_toks(const char *); +extern int get_char_cat_code(int); + +/* +|no_expand_flag| is a special character value that is inserted by +|get_next| if it wants to suppress expansion. +*/ + +# define no_expand_flag special_char + +extern halfword par_loc; +extern halfword par_token; +extern boolean force_eof; +extern int luacstrings; + +extern void firm_up_the_line(void); +extern void get_token(void); + +extern halfword str_toks(lstring b); +extern halfword str_scan_toks(int c, lstring b); +extern void ins_the_toks(void); +extern void combine_the_toks(void); + +extern int scan_lua_state(void); + +extern void conv_toks(void); +extern str_number the_convert_string(halfword c, int i); +extern void do_variable(void); +extern void do_feedback(void); +extern void do_mathoption(void); + +extern halfword lua_str_toks(lstring b); +extern boolean in_lua_escape; + +# define closed 2 /* not open, or at end of file */ +# define just_open 1 /* newly opened, first line not yet read */ + +extern FILE *read_file[16]; /* used for \.{\\read} */ +extern int read_open[17]; /* state of |read_file[n]| */ + +extern void initialize_read(void); + +extern void read_toks(int n, halfword r, halfword j); + +extern str_number tokens_to_string(halfword p); /* return a string from tokens list */ + +extern char *tokenlist_to_xstring(int p, int inhibit_par, int *siz); +extern char *tokenlist_to_cstring(int p, int inhibit_par, int *siz); +extern lstring *tokenlist_to_lstring(int pp, int inhibit_par); +extern void free_lstring(lstring * ls); + +# define token_cmd(A) ((A) >> STRING_OFFSET_BITS) +# define token_chr(A) ((A) & (STRING_OFFSET - 1)) +# define token_val(A,B) (((A)<<STRING_OFFSET_BITS)+(B)) + +extern void l_set_token_data(void) ; + +#endif |