diff options
author | Luigi Scarso <luigi.scarso@gmail.com> | 2018-09-05 21:57:30 +0000 |
---|---|---|
committer | Luigi Scarso <luigi.scarso@gmail.com> | 2018-09-05 21:57:30 +0000 |
commit | ecd1605a23ef54d2a4a69e1a361172d350e03b3b (patch) | |
tree | 8ea1d9745c24dbc5e755f13d801fa01e88944650 /Build/source/texk/web2c/luatexdir/tex | |
parent | 04563248956e7059aad4fc4f6bf7da18562f7b8c (diff) |
luatex: correct automake file for pplib; replaced cweb files with c files.
git-svn-id: svn://tug.org/texlive/trunk@48594 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex')
48 files changed, 28433 insertions, 24049 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/align.c b/Build/source/texk/web2c/luatexdir/tex/align.c new file mode 100644 index 00000000000..b2384cc6290 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/align.c @@ -0,0 +1,1297 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +void fin_align(void); +void init_row(void); +void init_col(void); + +#define noDEBUG + +/*tex + + It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because + they cut across so many of the control structures of \TeX. Therefore the + present page is probably not the best place for a beginner to start reading + this program; it is better to master everything else first. + + Let us focus our thoughts on an example of what the input might be, in order + to get some idea about how the alignment miracle happens. The example doesn't + do anything useful, but it is sufficiently general to indicate all of the + special cases that must be dealt with; please do not be disturbed by its + apparent complexity and meaninglessness. + + \starttyping + \tabskip 2pt plus 3pt + \halign to 300pt{u1#v1& + \hskip 50pt \tabskip 1pt plus 1fil u2#v2& + \hskip 50pt u3#v3\cr + \hskip 25pt a1&\omit a2&\vrule\cr + \hskip 25pt \noalign\{\vskip 3pt} + \hskip 25pt b1\span b2\cr + \hskip 25pt \omit&c2\span\omit\cr} + \stoptyping + + Here's what happens: + + \startitemize + + \startitem + When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine + places the 300pt dimension onto the |save_stack|, and an + |align_group| code is placed above it. This will make it possible to + complete the alignment when the matching `\.\}' is found. + \stopitem + + \startitem + The preamble is scanned next. Macros in the preamble are not + expanded, except as part of a tabskip specification. For example, if + \.{u2} had been a macro in the preamble above, it would have been + expanded, since \TeX\ must look for `\.{minus...}' as part of the + tabskip glue. A ``preamble list'' is constructed based on the user's + preamble; in our case it contains the following seven items: + + \starttabulate + \NC \type{\glue 2pt plus 3pt} \NC the tabskip preceding column 1 \NC \NR + \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 1 \NC \NR + \NC \type{\glue 2pt plus 3pt} \NC the tabskip between columns 1 and 2 \NC \NR + \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 2 \NC \NR + \NC \type{\glue 1pt plus 1fil} \NC the tabskip between columns 2 and 3 \NC \NR + \NC \type{\alignrecord} of width $-\infty$ \NC preamble info for column 3 \NC \NR + \NC \type{\glue 1pt plus 1fil} \NC the tabskip following column 3 \NC \NR + \stoptabulate + + These ``alignrecord'' entries have the same size as an |unset_node|, + since they will later be converted into such nodes. These alignrecord + nodes have no |depth| field; this is split into |u_part| and + |v_part|, and they point to token lists for the templates of the + alignment. For example, the |u_part| field in the first alignrecord + points to the token list `\.{u1}', i.e., the template preceding the + `\.\#' for column~1. Furthermore, They have a |span_ptr| instead of a + |node_attr| field, and these |span_ptr| fields are initially set to + the value |end_span|, for reasons explained below. + \stopitem + + \startitem + \TeX\ now looks at what follows the \.{\\cr} that ended the preamble. + It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back + to be read again, and the template `\.{u1}' is fed to the scanner. + Just before reading `\.{u1}', \TeX\ goes into restricted horizontal + mode. Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then + (when the {\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans + an |endv| token, indicating the end of a column. At this point an + |unset_node| is created, containing the contents of the current hlist + (i.e., `\.{u1a1v1}'). The natural width of this unset node replaces + the |width| field of the alignrecord for column~1; in general, the + alignrecords will record the maximum natural width that has occurred + so far in a given column. + \stopitem + + \startitem + Since `\.{\\omit}' follows the `\.\&', the templates for column~2 are + now bypassed. Again \TeX\ goes into restricted horizontal mode and + makes an |unset_node| from the resulting hlist; but this time the + hlist contains simply `\.{a2}'. The natural width of the new unset + box is remembered in the |width| field of the alignrecord for + column~2. + \stopitem + + \startitem + A third |unset_node| is created for column 3, using essentially the + mechanism that worked for column~1; this unset box contains + `\.{u3\\vrule v3}'. The vertical rule in this case has running + dimensions that will later extend to the height and depth of the + whole first row, since each |unset_node| in a row will eventually + inherit the height and depth of its enclosing box. + \stopitem + + \startitem + The first row has now ended; it is made into a single unset box + comprising the following seven items: + + \starttyping + \glue 2pt plus 3pt + \unsetbox for 1 column: u1a1v1 + \glue 2pt plus 3pt + \unsetbox for 1 column: a2 + \glue 1pt plus 1fil + \unsetbox for 1 column: u3\vrule v3 + \glue 1pt plus 1fil + \stoptyping + + The width of this unset row is unimportant, but it has the correct + height and depth, so the correct baselineskip glue will be computed + as the row is inserted into a vertical list. + \stopitem + + \startitem + Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends + additional material (in this case \.{\\vskip 3pt}) to the vertical + list. While processing this material, \TeX\ will be in internal + vertical mode, and |no_align_group| will be on |save_stack|. + \stopitem + + \startitem + The next row produces an unset box that looks like this: + + \starttyping + \glue 2pt plus 3pt + \unsetbox for 2 columns: u1b1v1u2b2v2 + \glue 1pt plus 1fil + \unsetbox for 1 column: {(empty)} + \glue 1pt plus 1fil + \stoptyping + + The natural width of the unset box that spans columns 1~and~2 is + stored in a ``span node,'' which we will explain later; the + |span_ptr| field of the alignrecord for column~1 now points to the + new span node, and the |span_ptr| of the span node points to + |end_span|. + \stopitem + + \startitem + + The final row produces the unset box + + \starttyping + \glue 2pt plus 3pt\cr + \unsetbox for 1 column: {(empty)} + \glue 2pt plus 3pt\cr + \unsetbox for 2 columns: u2c2v2 + \glue 1pt plus 1fil + \stoptyping + + A new span node is attached to the alignrecord for column 2. + \stopitem + + \startitem + The last step is to compute the true column widths and to change all + the unset boxes to hboxes, appending the whole works to the vertical + list that encloses the \.{\\halign}. The rules for deciding on the + final widths of each unset column box will be explained below. + \stopitem + + \stopitemize + + Note that as \.{\\halign} is being processed, we fearlessly give up control + to the rest of \TeX. At critical junctures, an alignment routine is called + upon to step in and do some little action, but most of the time these + routines just lurk in the background. It's something like post-hypnotic + suggestion. + + We have mentioned that alignrecords contain no |height| or |depth| fields. + Their |glue_sign| and |glue_order| are pre-empted as well, since it is + necessary to store information about what to do when a template ends. This + information is called the |extra_info| field. + +*/ + +/*tex The pointer to \<u_j> token list: */ + +#define u_part(A) vlink((A)+depth_offset) + +/*tex The pointer to \<v_j> token list */ + +#define v_part(A) vinfo((A)+depth_offset) + +/*tex A column spanning list */ + +#define span_ptr(A) vinfo((A)+1) + +/*tex Info to remember during template */ + +#define extra_info(A) vinfo((A)+list_offset) + +/*tex + + Alignments can occur within alignments, so a small stack is used to access + the alignrecord information. At each level we have a |preamble| pointer, + indicating the beginning of the preamble list; a |cur_align| pointer, + indicating the current position in the preamble list; a |cur_span| pointer, + indicating the value of |cur_align| at the beginning of a sequence of spanned + columns; a |cur_loop| pointer, indicating the tabskip glue before an + alignrecord that should be copied next if the current list is extended; and + the |align_state| variable, which indicates the nesting of braces so that + \.{\\cr} and \.{\\span} and tab marks are properly intercepted. There also + are pointers |cur_head| and |cur_tail| to the head and tail of a list of + adjustments being moved out from horizontal mode to vertical~mode, and alike + |cur_pre_head| and |cur_pre_tail| for pre-adjust lists. + + The current values of these nine quantities appear in global variables; when + they have to be pushed down, they are stored in 6-word nodes, and |align_ptr| + points to the topmost such node. + +*/ + +/*tex This could be in |texnodes.h| but it's documented here. */ + +/*tex The current preamble list: */ + +#define preamble vlink(align_head) + +/*tex The current position in the preamble list: */ + +pointer cur_align = null; + +/*tex The start of the currently spanned columns in the preamble list: */ + +pointer cur_span = null; + +/*tex A place to copy when extending a periodic preamble: */ + +pointer cur_loop = null; + +/*tex The most recently pushed-down alignment stack node: */ + +pointer align_ptr = null; + +/*tex Adjustment list pointers: */ + +pointer cur_head = null, cur_tail = null; + +/*tex Pre-adjustment list pointers: */ + +pointer cur_pre_head = null, cur_pre_tail = null; + +/*tex + + The |align_state| and |preamble| variables are initialized elsewhere. + + Alignment stack maintenance is handled by a pair of trivial routines called + |push_alignment| and |pop_alignment|. + + (HH:) It makes not much sense to add support for an \.{attr} keyword to + \.{\\halign} and \.{\\valign} because then we need to decide if we tag rows + or cells or both or come up with \.{cellattr} and \.{rowattr} and such. But + then it even makes sense to have explicit commands (in addition to the + seperator) to tags individual cells. Too muss hassle for now and the + advantages are not that large. + +*/ + +static void push_alignment(void) +{ + /*tex The new alignment stack node: */ + pointer p; + p = new_node(align_stack_node, 0); + vinfo(p + 1) = align_ptr; + vlink(p + 1) = cur_align; + vinfo(p + 2) = preamble; + vlink(p + 2) = cur_span; + vinfo(p + 3) = cur_loop; + vlink(p + 3) = align_state; + vinfo(p + 4) = cur_head; + vlink(p + 4) = cur_tail; + vinfo(p + 5) = cur_pre_head; + vlink(p + 5) = cur_pre_tail; + align_ptr = p; + cur_head = new_node(temp_node, 0); + cur_pre_head = new_node(temp_node, 0); +} + +static void pop_alignment(void) +{ + /*tex The top alignment stack node: */ + pointer p; + flush_node(cur_head); + flush_node(cur_pre_head); + p = align_ptr; + cur_pre_tail = vlink(p + 5); + cur_pre_head = vinfo(p + 5); + cur_tail = vlink(p + 4); + cur_head = vinfo(p + 4); + align_state = vlink(p + 3); + cur_loop = vinfo(p + 3); + cur_span = vlink(p + 2); + preamble = vinfo(p + 2); + cur_align = vlink(p + 1); + align_ptr = vinfo(p + 1); + flush_node(p); +} + +/*tex + + \TeX\ has eight procedures that govern alignments: |init_align| and + |fin_align| are used at the very beginning and the very end; |init_row| and + |fin_row| are used at the beginning and end of individual rows; |init_span| + is used at the beginning of a sequence of spanned columns (possibly involving + only one column); |init_col| and |fin_col| are used at the beginning and end + of individual columns; and |align_peek| is used after \.{\\cr} to see whether + the next item is \.{\\noalign}. + + We shall consider these routines in the order they are first used during the + course of a complete \.{\\halign}, namely |init_align|, |align_peek|, + |init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|. + + The preamble is copied directly, except that \.{\\tabskip} causes a change to + the tabskip glue, thereby possibly expanding macros that immediately follow + it. An appearance of \.{\\span} also causes such an expansion. + + Note that if the preamble contains `\.{\\global\\tabskip}', the + `\.{\\global}' token survives in the preamble and the `\.{\\tabskip}' defines + new tabskip glue (locally). + +*/ + +static void get_preamble_token(void) +{ + RESTART: + get_token(); + while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) { + /*tex This token will be expanded once. */ + get_token(); + if (cur_cmd > max_command_cmd) { + expand(); + get_token(); + } + } + if (cur_cmd == endv_cmd) + fatal_error("(interwoven alignment preambles are not allowed)"); + if ((cur_cmd == assign_glue_cmd) + && (cur_chr == glue_base + tab_skip_code)) { + scan_optional_equals(); + scan_glue(glue_val_level); + if (global_defs_par > 0) + geq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val); + else + eq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val); + goto RESTART; + } +} + +/*tex + + When \.{\\halign} or \.{\\valign} has been scanned in an appropriate mode, + \TeX\ calls |init_align|, whose task is to get everything off to a good + start. This mostly involves scanning the preamble and putting its information + into the preamble list. + +*/ + +void init_align(void) +{ + /*tex |warning_index| value for error messages */ + pointer save_cs_ptr; + /*tex for short-term temporary use */ + pointer p, r; + /*tex \.{\\halign} or \.{\\valign}, usually */ + save_cs_ptr = cur_cs; + push_alignment(); + /*tex enter a new alignment level */ + align_state = -1000000; + /*tex + + When \.{\\halign} is used as a displayed formula, there should be no + other pieces of mlists present. + + */ + if ((cur_list.mode_field == mmode) && ((cur_list.tail_field != cur_list.head_field) || (incompleat_noad_par != null))) { + const char *hlp[] = { + "Displays can use special alignments (like \\eqalignno)", + "only if nothing but the alignment itself is between $$'s.", + "So I've deleted the formulas that preceded this alignment.", + NULL + }; + tex_error("Improper \\halign inside $$'s", hlp); + flush_math(); + } + /*tex Enter a new semantic level. */ + push_nest(); + /*tex + + In vertical modes, |prev_depth| already has the correct value. But if we + are in |mmode| (displayed formula mode), we reach out to the enclosing + vertical mode for the |prev_depth| value that produces the correct + baseline calculations. + */ + if (cur_list.mode_field == mmode) { + cur_list.mode_field = -vmode; + prev_depth_par = nest[nest_ptr - 2].prev_depth_field; + } else if (cur_list.mode_field > 0) { + cur_list.mode_field = -(cur_list.mode_field); + } + scan_spec(align_group); + /*tex Scan the preamble. */ + preamble = null; + cur_align = align_head; + cur_loop = null; + scanner_status = aligning; + warning_index = save_cs_ptr; + align_state = -1000000; + /*tex At this point, |cur_cmd=left_brace|. */ + while (true) { + /*tex Append the current tabskip glue to the preamble list. */ + r = new_param_glue(tab_skip_code); + vlink(cur_align) = r; + cur_align = vlink(cur_align); + if (cur_cmd == car_ret_cmd) { + /*tex \.{\\cr} ends the preamble. */ + break; + } + /*tex + + Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| and + then scan the template \<u_j>, putting the resulting token list in + |hold_token_head|. Spaces are eliminated from the beginning of a + template. + + */ + p = hold_token_head; + token_link(p) = null; + while (1) { + get_preamble_token(); + if (cur_cmd == mac_param_cmd) + break; + if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) + && (align_state == -1000000)) { + if ((p == hold_token_head) && (cur_loop == null) && (cur_cmd == tab_mark_cmd)) { + cur_loop = cur_align; + } else { + const char *hlp[] = { + "There should be exactly one # between &'s, when an", + "\\halign or \\valign is being set up. In this case you had", + "none, so I've put one in; maybe that will work.", + NULL + }; + back_input(); + tex_error("Missing # inserted in alignment preamble", hlp); + break; + } + } else if ((cur_cmd != spacer_cmd) || (p != hold_token_head)) { + r = get_avail(); + token_link(p) = r; + p = token_link(p); + token_info(p) = cur_tok; + } + } + r = new_node(align_record_node, 0); + vlink(cur_align) = r; + /*tex A new align record: */ + cur_align = vlink(cur_align); + span_ptr(cur_align) = end_span; + width(cur_align) = null_flag; + u_part(cur_align) = token_link(hold_token_head); + /*tex + + Scan the template \<v_j>, putting the resulting token list in + |hold_token_head|. + + */ + p = hold_token_head; + token_link(p) = null; + while (1) { + CONTINUE: + get_preamble_token(); + if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) && (align_state == -1000000)) + break; + if (cur_cmd == mac_param_cmd) { + const char *hlp[] = { + "There should be exactly one # between &'s, when an", + "\\halign or \\valign is being set up. In this case you had", + "more than one, so I'm ignoring all but the first.", + NULL + }; + tex_error("Only one # is allowed per tab", hlp); + goto CONTINUE; + } + r = get_avail(); + token_link(p) = r; + p = token_link(p); + token_info(p) = cur_tok; + } + r = get_avail(); + token_link(p) = r; + p = token_link(p); + /*tex Put \.{\\endtemplate} at the end: */ + token_info(p) = end_template_token; + v_part(cur_align) = token_link(hold_token_head); + } + scanner_status = normal; + new_save_level(align_group); + if (every_cr_par != null) + begin_token_list(every_cr_par, every_cr_text); + /*tex Look for \.{\\noalign} or \.{\\omit}. */ + align_peek(); +} + +/*tex + + The tricky part about alignments is getting the templates into the scanner at + the right time, and recovering control when a row or column is finished. + + We usually begin a row after each \.{\\cr} has been sensed, unless that + \.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates + the alignment. The |align_peek| routine is used to look ahead and do the + right thing; it either gets a new row started, or gets a \.{\\noalign} + started, or finishes off the alignment. + +*/ + +void align_peek(void) +{ + RESTART: + align_state = 1000000; + do { + get_x_or_protected(); + } while (cur_cmd == spacer_cmd); + if (cur_cmd == no_align_cmd) { + scan_left_brace(); + new_save_level(no_align_group); + if (cur_list.mode_field == -vmode) + normal_paragraph(); + } else if (cur_cmd == right_brace_cmd) { + fin_align(); + } else if ((cur_cmd == car_ret_cmd) && (cur_chr == cr_cr_code)) { + /*tex Ignore \.{\\crcr}. */ + goto RESTART; + } else { + /*tex Start a new row. */ + init_row(); + /*tex Start a new column and replace what we peeked at. */ + init_col(); + } +} + + +/*tex + + The parameter to |init_span| is a pointer to the alignrecord where the next + column or group of columns will begin. A new semantic level is entered, so + that the columns will generate a list for subsequent packaging. + +*/ + +static void init_span(pointer p) +{ + push_nest(); + if (cur_list.mode_field == -hmode) { + space_factor_par = 1000; + } else { + prev_depth_par = ignore_depth; + normal_paragraph(); + } + cur_span = p; +} + +/*tex + + To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'), + we enter a new semantic level, copy the first tabskip glue, and change from + internal vertical mode to restricted horizontal mode or vice versa. The + |space_factor| and |prev_depth| are not used on this semantic level, but we + clear them to zero just to be tidy. + +*/ + +void init_row(void) +{ + push_nest(); + cur_list.mode_field = (-hmode - vmode) - cur_list.mode_field; + if (cur_list.mode_field == -hmode) + space_factor_par = 0; + else + prev_depth_par = 0; + tail_append(new_glue(preamble)); + subtype(cur_list.tail_field) = tab_skip_code + 1; + cur_align = vlink(preamble); + cur_tail = cur_head; + cur_pre_tail = cur_pre_head; + init_span(cur_align); +} + +/*tex + + When a column begins, we assume that |cur_cmd| is either |omit| or else the + current token should be put back into the input until the \<u_j> template has + been scanned. (Note that |cur_cmd| might be |tab_mark| or |car_ret|.) We also + assume that |align_state| is approximately 1000000 at this time. We remain in + the same mode, and start the template if it is called for. + +*/ + +void init_col(void) +{ + extra_info(cur_align) = cur_cmd; + if (cur_cmd == omit_cmd) + align_state = 0; + else { + back_input(); + begin_token_list(u_part(cur_align), u_template); + } + /*tex now |align_state=1000000| */ +} + + +/*tex + + The scanner sets |align_state| to zero when the \<u_j> template ends. When a + subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|, + the scanner activates the following code, which fires up the \<v_j> template. + We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|, + |span_code|, or a character code, depending on how the column text has ended. + + This part of the program had better not be activated when the preamble to + another alignment is being scanned, or when no alignment preamble is active. + +*/ + +void insert_vj_template(void) +{ + if ((scanner_status == aligning) || (cur_align == null)) + fatal_error("(interwoven alignment preambles are not allowed)"); + cur_cmd = extra_info(cur_align); + extra_info(cur_align) = cur_chr; + if (cur_cmd == omit_cmd) + begin_token_list(omit_template, v_template); + else + begin_token_list(v_part(cur_align), v_template); + align_state = 1000000; +} + +/*tex Determine the stretch order */ + +#define determine_stretch_order() do { \ + if (total_stretch[filll]!= 0) o = filll; \ + else if (total_stretch[fill] != 0) o = fill; \ + else if (total_stretch[fil] != 0) o = fil; \ + else if (total_stretch[sfi] != 0) o = sfi; \ + else o=normal; \ +} while (0) + +/*tex Determine the shrink order */ + +#define determine_shrink_order() do { \ + if (total_shrink[filll] != 0) o = filll; \ + else if (total_shrink[fill] != 0) o = fill; \ + else if (total_shrink[fil] != 0) o = fil; \ + else if (total_shrink[sfi] != 0) o = sfi; \ + else o=normal; \ +} while (0) + +/*tex + + When the |endv| command at the end of a \<v_j> template comes through the + scanner, things really start to happen; and it is the |fin_col| routine that + makes them happen. This routine returns |true| if a row as well as a column + has been finished. + +*/ + +boolean fin_col(void) +{ + /*tex the alignrecord after the current one */ + pointer p; + /*tex temporary pointers for list manipulation */ + pointer q, r; + /*tex a new span node */ + pointer s; + /*tex a new unset box */ + pointer u; + /*tex natural width */ + scaled w; + /*tex order of infinity */ + unsigned char o; + /*tex span counter */ + halfword n; + if (cur_align == null) + confusion("endv"); + q = vlink(cur_align); + if (q == null) + confusion("endv"); + if (align_state < 500000) + fatal_error("(interwoven alignment preambles are not allowed)"); + p = vlink(q); + /*tex If the preamble list has been traversed, check that the row has ended. */ + if ((p == null) && (extra_info(cur_align) < cr_code)) { + if (cur_loop != null) { + /*tex Lengthen the preamble periodically: */ + r = new_node(align_record_node, 0); + vlink(q) = r; + /*tex A new align record: */ + p = vlink(q); + span_ptr(p) = end_span; + width(p) = null_flag; + cur_loop = vlink(cur_loop); + /*tex Copy the templates from node |cur_loop| into node |p|. */ + q = hold_token_head; + r = u_part(cur_loop); + while (r != null) { + s = get_avail(); + token_link(q) = s; + q = token_link(q); + token_info(q) = token_info(r); + r = token_link(r); + } + token_link(q) = null; + u_part(p) = token_link(hold_token_head); + q = hold_token_head; + r = v_part(cur_loop); + while (r != null) { + s = get_avail(); + token_link(q) = s; + q = token_link(q); + token_info(q) = token_info(r); + r = token_link(r); + } + token_link(q) = null; + v_part(p) = token_link(hold_token_head); + cur_loop = vlink(cur_loop); + r = new_glue(cur_loop); + vlink(p) = r; + } else { + const char *hlp[] = { + "You have given more \\span or & marks than there were", + "in the preamble to the \\halign or \\valign now in progress.", + "So I'll assume that you meant to type \\cr instead.", + NULL + }; + extra_info(cur_align) = cr_code; + tex_error("Extra alignment tab has been changed to \\cr", hlp); + } + } + if (extra_info(cur_align) != span_code) { + unsave(); + new_save_level(align_group); + /*tex Package an unset box for the current column and record its width. */ + if (cur_list.mode_field == -hmode) { + adjust_tail = cur_tail; + pre_adjust_tail = cur_pre_tail; + u = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, additional, align_set_group, -1, 0, 0); + w = width(u); + cur_tail = adjust_tail; + adjust_tail = null; + cur_pre_tail = pre_adjust_tail; + pre_adjust_tail = null; + } else { + u = filtered_vpackage(vlink(cur_list.head_field), 0, additional, 0, align_set_group, -1, 0, 0); + w = height(u); + } + /*tex This represents a span count of 1: */ + n = min_quarterword; + if (cur_span != cur_align) { + /*tex Update width entry for spanned columns. */ + q = cur_span; + do { + incr(n); + q = vlink(vlink(q)); + } while (q != cur_align); + if (n > max_quarterword) { + /*tex This can happen, but won't. */ + confusion("too many spans"); + } + q = cur_span; + while (span_span(span_ptr(q)) < n) { + q = span_ptr(q); + } + if (span_span(span_ptr(q)) > n) { + s = new_span_node(span_ptr(q), n, w); + span_ptr(q) = s; + } else if (width(span_ptr(q)) < w) { + width(span_ptr(q)) = w; + } + } else if (w > width(cur_align)) { + width(cur_align) = w; + } + type(u) = unset_node; + span_count(u) = (quarterword) n; + determine_stretch_order(); + glue_order(u) = o; + glue_stretch(u) = total_stretch[o]; + determine_shrink_order(); + glue_sign(u) = o; + glue_shrink(u) = total_shrink[o]; + pop_nest(); + vlink(cur_list.tail_field) = u; + cur_list.tail_field = u; + /*tex Copy the tabskip glue between columns. */ + tail_append(new_glue(vlink(cur_align))); + subtype(cur_list.tail_field) = tab_skip_code + 1; + if (extra_info(cur_align) >= cr_code) { + return true; + } + init_span(p); + } + align_state = 1000000; + do { + get_x_or_protected(); + } while (cur_cmd == spacer_cmd); + cur_align = p; + init_col(); + return false; +} + +/*tex + + A span node is a 3-word record containing |width|, |span_span|, and + |span_ptr| fields. The |span_span| field indicates the number of spanned + columns; the |span_ptr| field points to a span node for the same starting + column, having a greater extent of spanning, or to |end_span|, which has the + largest possible |span_span| field; the |width| field holds the largest + natural width corresponding to a particular set of spanned columns. + + A list of the maximum widths so far, for spanned columns starting at a given + column, begins with the |span_ptr| field of the alignrecord for that column. + The code has to make sure that there is room for |span_ptr| in both the + alignrecord and the span nodes, which is why |span_ptr| replaces |node_attr|. + + The |new_span_node| function is defined in |texnodes.c|. + +*/ + +/*tex This is normally |alink|: */ + +#ifndef span_span +# define span_span(A) vlink((A)+1) +#endif + +/*tex + + At the end of a row, we append an unset box to the current vlist (for + \.{\\halign}) or the current hlist (for \.{\\valign}). This unset box + contains the unset boxes for the columns, separated by the tabskip glue. + Everything will be set later. + +*/ + +void fin_row(void) +{ + /*tex The new unset box: */ + pointer p; + if (cur_list.mode_field == -hmode) { + p = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, + additional, fin_row_group, -1, 0, 0); + pop_nest(); + if (cur_pre_head != cur_pre_tail) + append_list(cur_pre_head, cur_pre_tail); + append_to_vlist(p,lua_key_index(alignment)); + if (cur_head != cur_tail) + append_list(cur_head, cur_tail); + } else { + p = filtered_vpackage(vlink(cur_list.head_field), + 0, additional, max_depth_par, fin_row_group, -1, 0, 0); + pop_nest(); + vlink(cur_list.tail_field) = p; + cur_list.tail_field = p; + space_factor_par = 1000; + } + type(p) = unset_node; + glue_stretch(p) = 0; + if (every_cr_par != null) + begin_token_list(every_cr_par, every_cr_text); + align_peek(); + /*tex Note that |glue_shrink(p)=0| since |glue_shrink==shift_amount|. */ +} + +/*tex + + Finally, we will reach the end of the alignment, and we can breathe a sigh of + relief that memory hasn't overflowed. All the unset boxes will now be set so + that the columns line up, taking due account of spanned columns. + +*/ + +void fin_align(void) +{ + /*tex registers for the list operations */ + pointer p, q, r, s, u, rr; + /*tex width of column */ + scaled t, w; + /*tex shift offset for unset boxes */ + scaled o; + /*tex matching span amount */ + halfword n; + /*tex temporary storage for |overfull_rule| */ + scaled rule_save; + /*tex temporary storage for |prev_depth| */ + halfword pd; + /*tex temporary storage for |new_glue| */ + halfword ng; + /*tex The |align_group| was for individual entries: */ + if (cur_group != align_group) + confusion("align1"); + unsave(); + /*tex The |align_group| was for the whole alignment: */ + if (cur_group != align_group) + confusion("align0"); + unsave(); + if (nest[nest_ptr - 1].mode_field == mmode) { + o = display_indent_par; + } else { + o = 0; + } + /*tex + + Go through the preamble list, determining the column widths and + changing the alignrecords to dummy unset boxes. + + It's time now to dismantle the preamble list and to compute the + column widths. Let $w_{ij}$ be the maximum of the natural widths of + all entries that span columns $i$ through $j$, inclusive. The + alignrecord for column~$i$ contains $w_{ii}$ in its |width| field, + and there is also a linked list of the nonzero $w_{ij}$ for + increasing $j$, accessible via the |info| field; these span nodes + contain the value $j-i+|min_quarterword|$ in their |link| fields. The + values of $w_{ii}$ were initialized to |null_flag|, which we regard + as $-\infty$. + + The final column widths are defined by the formula $$w_j=\max_{1\L + i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$ where $t_k$ is + the natural width of the tabskip glue between columns $k$ and~$k+1$. + However, if $w_{ij}=-\infty$ for all |i| in the range |1<=i<=j| + (i.e., if every entry that involved column~|j| also involved + column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after + column~|j|. + + \TeX\ computes these values by using the following scheme: First + $w_1=w_{11}$. Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, + for all $j>1$. Then $w_2=w_{22}$. Then replace $w_{3j}$ by + $\max(w_{3j},w_{2j}-t_2-w_2)$ for all $j>2$; and so on. If any $w_j$ + turns out to be $-\infty$, its value is changed to zero and so is the + next tabskip. + + */ + q = vlink(preamble); + do { + flush_list(u_part(q)); + flush_list(v_part(q)); + p = vlink(vlink(q)); + if (width(q) == null_flag) { + /*tex Nullify |width(q)| and the tabskip glue following this column. */ + width(q) = 0; + r = vlink(q); + reset_glue_to_zero(r); + } + if (span_ptr(q) != end_span) { + /*tex + + Merge the widths in the span nodes of |q| with those of |p|, + destroying the span nodes of |q|. + + Merging of two span-node lists is a typical exercise in the + manipulation of linearly linked data structures. The essential + invariant in the following |repeat| loop is that we want to + dispense with node |r|, in |q|'s list, and |u| is its successor; + all nodes of |p|'s list up to and including |s| have been + processed, and the successor of |s| matches |r| or precedes |r| + or follows |r|, according as |link(r)=n| or |link(r)>n| or + |link(r)<n|. + + */ + t = width(q) + width(vlink(q)); + r = span_ptr(q); + s = end_span; + span_ptr(s) = p; + n = min_quarterword + 1; + do { + width(r) = width(r) - t; + u = span_ptr(r); + while (span_span(r) > n) { + s = span_ptr(s); + n = span_span(span_ptr(s)) + 1; + } + if (span_span(r) < n) { + span_ptr(r) = span_ptr(s); + span_ptr(s) = r; + decr(span_span(r)); + s = r; + } else { + if (width(r) > width(span_ptr(s))) + width(span_ptr(s)) = width(r); + flush_node(r); + } + r = u; + } while (r != end_span); + } + type(q) = unset_node; + span_count(q) = min_quarterword; + height(q) = 0; + depth(q) = 0; + glue_order(q) = normal; + glue_sign(q) = normal; + glue_stretch(q) = 0; + glue_shrink(q) = 0; + q = p; + } while (q != null); + /*tex + + Package the preamble list, to determine the actual tabskip glue amounts, + and let |p| point to this prototype box. + + Now the preamble list has been converted to a list of alternating unset + boxes and tabskip glue, where the box widths are equal to the final + column sizes. In case of \.{\\valign}, we change the widths to heights, + so that a correct error message will be produced if the alignment is + overfull or underfull. + + */ + decr(save_ptr); + pack_begin_line = -cur_list.ml_field; + if (cur_list.mode_field == -vmode) { + rule_save = overfull_rule_par; + /*tex Prevent the rule from being packaged. */ + overfull_rule_par = 0; + p = hpack(preamble, saved_value(0), saved_level(0), -1); + overfull_rule_par = rule_save; + } else { + q = vlink(preamble); + do { + height(q) = width(q); + width(q) = 0; + q = vlink(vlink(q)); + } while (q != null); + p = filtered_vpackage(preamble, + saved_value(0), saved_level(0), max_depth_par, preamble_group, -1, 0, 0); + q = vlink(preamble); + do { + width(q) = height(q); + height(q) = 0; + q = vlink(vlink(q)); + } while (q != null); + } + pack_begin_line = 0; + /*tex Set the glue in all the unset boxes of the current list. */ + q = vlink(cur_list.head_field); + s = cur_list.head_field; + while (q != null) { + if (!is_char_node(q)) { + if (type(q) == unset_node) { + /*tex + + We set the unset box |q| and the unset boxes in it. The unset + box |q| represents a row that contains one or more unset + boxes, depending on how soon \.{\\cr} occurred in that row. + + */ + if (cur_list.mode_field == -vmode) { + type(q) = hlist_node; + subtype(q) = align_row_list; + width(q) = width(p); + } else { + type(q) = vlist_node; + subtype(q) = align_row_list; + height(q) = height(p); + } + glue_order(q) = glue_order(p); + glue_sign(q) = glue_sign(p); + glue_set(q) = glue_set(p); + shift_amount(q) = o; + r = vlink(list_ptr(q)); + assert (type(r) == unset_node); + s = vlink(list_ptr(p)); + do { + /*tex + + We set the glue in node |r| and change it from an unset + node. A box made from spanned columns will be followed by + tabskip glue nodes and by empty boxes as if there were no + spanning. This permits perfect alignment of subsequent + entries, and it prevents values that depend on floating + point arithmetic from entering into the dimensions of any + boxes. + + */ + n = span_count(r); + t = width(s); + w = t; + u = hold_head; + while (n > min_quarterword) { + decr(n); + /*tex + + Append tabskip glue and an empty box to list |u|, and + update |s| and |t| as the prototype nodes are passed. + + */ + s = vlink(s); + ng = new_glue(s); + vlink(u) = ng; + u = vlink(u); + subtype(u) = tab_skip_code + 1; + t = t + width(s); + if (glue_sign(p) == stretching) { + if (stretch_order(s) == glue_order(p)) + t = t + round(float_cast(glue_set(p)) * float_cast(stretch(s))); + } else if (glue_sign(p) == shrinking) { + if (shrink_order(s) == glue_order(p)) + t = t - round(float_cast(glue_set(p)) * float_cast(shrink(s))); + } + s = vlink(s); + rr = new_null_box(); + vlink(u) = rr; + u = vlink(u); + t = t + width(s); + subtype(u) = align_cell_list; + if (cur_list.mode_field == -vmode) { + width(u) = width(s); + } else { + type(u) = vlist_node; + height(u) = width(s); + } + } + if (cur_list.mode_field == -vmode) { + /*tex + + Make the unset node |r| into an |hlist_node| of width + |w|, setting the glue as if the width were |t|. + + */ + height(r) = height(q); + depth(r) = depth(q); + if (t == width(r)) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } else if (t > width(r)) { + glue_sign(r) = stretching; + if (glue_stretch(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else + glue_set(r) = unfloat((double) (t - width(r)) / glue_stretch(r)); + } else { + glue_order(r) = glue_sign(r); + glue_sign(r) = shrinking; + if (glue_shrink(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else if ((glue_order(r) == normal) && (width(r) - t > glue_shrink(r))) + set_glue_ratio_one(glue_set(r)); + else + glue_set(r) = unfloat((double) (width(r) - t) / glue_shrink(r)); + } + width(r) = w; + type(r) = hlist_node; + subtype(r) = align_cell_list; + + } else { + /*tex + + Make the unset node |r| into a |vlist_node| of height + |w|, setting the glue as if the height were |t|. + + */ + width(r) = width(q); + if (t == height(r)) { + glue_sign(r) = normal; + glue_order(r) = normal; + set_glue_ratio_zero(glue_set(r)); + } else if (t > height(r)) { + glue_sign(r) = stretching; + if (glue_stretch(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else + glue_set(r) = unfloat((t - height(r)) / glue_stretch(r)); + } else { + glue_order(r) = glue_sign(r); + glue_sign(r) = shrinking; + if (glue_shrink(r) == 0) + set_glue_ratio_zero(glue_set(r)); + else if ((glue_order(r) == normal) && (height(r) - t > glue_shrink(r))) + set_glue_ratio_one(glue_set(r)); + else + glue_set(r) = unfloat((height(r) - t) / glue_shrink(r)); + } + height(r) = w; + type(r) = vlist_node; + subtype(r) = align_cell_list; + } + shift_amount(r) = 0; + if (u != hold_head) { + /*tex Append blank boxes to account for spanned nodes. */ + vlink(u) = vlink(r); + vlink(r) = vlink(hold_head); + r = u; + } + + r = vlink(vlink(r)); + s = vlink(vlink(s)); + } while (r != null); + + } else if (type(q) == rule_node) { + /*tex + + Make the running dimensions in rule |q| extend to the + boundaries of the alignment. + + */ + if (is_running(width(q))) + width(q) = width(p); + if (is_running(height(q))) + height(q) = height(p); + if (is_running(depth(q))) + depth(q) = depth(p); + if (o != 0) { + r = vlink(q); + vlink(q) = null; + q = hpack(q, 0, additional, -1); + shift_amount(q) = o; + subtype(q) = align_cell_list; + vlink(q) = r; + vlink(s) = q; + } + } + } + s = q; + q = vlink(q); + } + flush_node_list(p); + pop_alignment(); + /*tex + + We now have a completed alignment, in the list that starts at + |cur_list.head_field| and ends at |cur_list.tail_field|. This list will + be merged with the one that encloses it. (In case the enclosing mode is + |mmode|, for displayed formulas, we will need to insert glue before and + after the display; that part of the program will be deferred until we're + more familiar with such operations.) + + */ + pd = prev_depth_par; + p = vlink(cur_list.head_field); + q = cur_list.tail_field; + pop_nest(); + if (cur_list.mode_field == mmode) { + finish_display_alignment(p, q, pd); + } else { + prev_depth_par = pd; + vlink(cur_list.tail_field) = p; + if (p != null) + cur_list.tail_field = q; + if (cur_list.mode_field == vmode) { + if (!output_active) + lua_node_filter_s(buildpage_filter_callback,lua_key_index(alignment)); + build_page(); + } + } +} + +/*tex + + The token list |omit_template| just referred to is a constant token list that + contains the special control sequence \.{\\endtemplate} only. + +*/ + +void initialize_alignments(void) +{ + token_info(omit_template) = end_template_token; + span_span(end_span) = max_quarterword + 1; + span_ptr(end_span) = null; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/align.w b/Build/source/texk/web2c/luatexdir/tex/align.w deleted file mode 100644 index dd0ed10338e..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/align.w +++ /dev/null @@ -1,1144 +0,0 @@ -% align.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -\def\<#1>{$#1$} - -@ @c - - -#include "ptexlib.h" - -@ @c -void fin_align(void); -void init_row(void); -void init_col(void); - -#define noDEBUG - -@ It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because -they cut across so many of the control structures of \TeX. - -Therefore the present page is probably not the best place for a beginner to -start reading this program; it is better to master everything else first. - -Let us focus our thoughts on an example of what the input might be, in order -to get some idea about how the alignment miracle happens. The example doesn't -do anything useful, but it is sufficiently general to indicate all of the -special cases that must be dealt with; please do not be disturbed by its -apparent complexity and meaninglessness. -$$\vbox{\halign{\.{#}\hfil\cr -{}\\tabskip 2pt plus 3pt\cr -{}\\halign to 300pt\{u1\#v1\&\cr -\hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr -\hskip 50pt u3\#v3\\cr\cr -\hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr -\hskip 25pt \\noalign\{\\vskip 3pt\}\cr -\hskip 25pt b1\\span b2\\cr\cr -\hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$ -Here's what happens: - -\yskip -(0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine -places the 300pt dimension onto the |save_stack|, and an |align_group| -code is placed above it. This will make it possible to complete the alignment -when the matching `\.\}' is found. - -(1) The preamble is scanned next. Macros in the preamble are not expanded, -@^preamble@> -except as part of a tabskip specification. For example, if \.{u2} had been -a macro in the preamble above, it would have been expanded, since \TeX\ -must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list'' -is constructed based on the user's preamble; in our case it contains the -following seven items: -$$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr -{}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr -{}\\alignrecord, width $-\infty$&preamble info for column 1\cr -{}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr -{}\\alignrecord, width $-\infty$&preamble info for column 2\cr -{}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr -{}\\alignrecord, width $-\infty$&preamble info for column 3\cr -{}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$ -These ``alignrecord'' entries have the same size as an |unset_node|, -since they will later be converted into such nodes. These alignrecord -nodes have no |depth| field; this is split into |u_part| and |v_part|, -and they point to token lists for the templates of the alignment. For -example, the |u_part| field in the first alignrecord points to the -token list `\.{u1}', i.e., the template preceding the `\.\#' for -column~1. Furthermore, They have a |span_ptr| instead of a |node_attr| -field, and these |span_ptr| fields are initially set to the value -|end_span|, for reasons explained below. - -(2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble. -It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to -be read again, and the template `\.{u1}' is fed to the scanner. Just -before reading `\.{u1}', \TeX\ goes into restricted horizontal mode. -Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the -{\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv| -token, indicating the end of a column. At this point an |unset_node| is -created, containing the contents of the current hlist (i.e., `\.{u1a1v1}'). -The natural width of this unset node replaces the |width| field of the -alignrecord for column~1; in general, the alignrecords will record the -maximum natural width that has occurred so far in a given column. - -(3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2 -are now bypassed. Again \TeX\ goes into restricted horizontal mode and -makes an |unset_node| from the resulting hlist; but this time the -hlist contains simply `\.{a2}'. The natural width of the new unset box -is remembered in the |width| field of the alignrecord for column~2. - -(4) A third |unset_node| is created for column 3, using essentially the -mechanism that worked for column~1; this unset box contains `\.{u3\\vrule -v3}'. The vertical rule in this case has running dimensions that will later -extend to the height and depth of the whole first row, since each |unset_node| -in a row will eventually inherit the height and depth of its enclosing box. - -(5) The first row has now ended; it is made into a single unset box -comprising the following seven items: -$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr -{}\\glue 2pt plus 3pt\cr -{}\\unsetbox for 1 column: u1a1v1\cr -{}\\glue 2pt plus 3pt\cr -{}\\unsetbox for 1 column: a2\cr -{}\\glue 1pt plus 1fil\cr -{}\\unsetbox for 1 column: u3\\vrule v3\cr -{}\\glue 1pt plus 1fil\cr}}$$ -The width of this unset row is unimportant, but it has the correct height -and depth, so the correct baselineskip glue will be computed as the row -is inserted into a vertical list. - -(6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends -additional material (in this case \.{\\vskip 3pt}) to the vertical list. -While processing this material, \TeX\ will be in internal vertical -mode, and |no_align_group| will be on |save_stack|. - -(7) The next row produces an unset box that looks like this: -$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr -{}\\glue 2pt plus 3pt\cr -{}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr -{}\\glue 1pt plus 1fil\cr -{}\\unsetbox for 1 column: {\rm(empty)}\cr -{}\\glue 1pt plus 1fil\cr}}$$ -The natural width of the unset box that spans columns 1~and~2 is stored -in a ``span node,'' which we will explain later; the |span_ptr| field of the -alignrecord for column~1 now points to the new span node, and the |span_ptr| -of the span node points to |end_span|. - -(8) The final row produces the unset box -$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr -{}\\glue 2pt plus 3pt\cr -{}\\unsetbox for 1 column: {\rm(empty)}\cr -{}\\glue 2pt plus 3pt\cr -{}\\unsetbox for 2 columns: u2c2v2\cr -{}\\glue 1pt plus 1fil\cr}}$$ -A new span node is attached to the alignrecord for column 2. - -(9) The last step is to compute the true column widths and to change all the -unset boxes to hboxes, appending the whole works to the vertical list that -encloses the \.{\\halign}. The rules for deciding on the final widths of -each unset column box will be explained below. - -\yskip\noindent -Note that as \.{\\halign} is being processed, we fearlessly give up control -to the rest of \TeX. At critical junctures, an alignment routine is -called upon to step in and do some little action, but most of the time -these routines just lurk in the background. It's something like -post-hypnotic suggestion. - -@ We have mentioned that alignrecords contain no |height| or |depth| fields. -Their |glue_sign| and |glue_order| are pre-empted as well, since it -is necessary to store information about what to do when a template ends. -This information is called the |extra_info| field. - -@c -/* could be in texnodes.h, but documented here*/ - -#define u_part(A) vlink((A)+depth_offset) /* pointer to \<u_j> token list */ -#define v_part(A) vinfo((A)+depth_offset) /* pointer to \<v_j> token list */ -#define span_ptr(A) vinfo((A)+1) /* column spanning list */ -#define extra_info(A) vinfo((A)+list_offset) /* info to remember during template */ - -@ Alignments can occur within alignments, so a small stack is used to access -the alignrecord information. At each level we have a |preamble| pointer, -indicating the beginning of the preamble list; a |cur_align| pointer, -indicating the current position in the preamble list; a |cur_span| pointer, -indicating the value of |cur_align| at the beginning of a sequence of -spanned columns; a |cur_loop| pointer, indicating the tabskip glue before -an alignrecord that should be copied next if the current list is extended; -and the |align_state| variable, which indicates the nesting of braces so -that \.{\\cr} and \.{\\span} and tab marks are properly intercepted. -There also are pointers |cur_head| and |cur_tail| to the head and tail -of a list of adjustments being moved out from horizontal mode to -vertical~mode, and alike |cur_pre_head| and |cur_pre_tail| for pre-adjust -lists. - -The current values of these nine quantities appear in global variables; -when they have to be pushed down, they are stored in 6-word nodes, and -|align_ptr| points to the topmost such node. - -@c -/* could be in texnodes.h but documented here*/ - -#define preamble vlink(align_head) /* the current preamble list */ - -pointer cur_align = null; /* current position in preamble list */ -pointer cur_span = null; /* start of currently spanned columns in preamble list */ -pointer cur_loop = null; /* place to copy when extending a periodic preamble */ -pointer align_ptr = null; /* most recently pushed-down alignment stack node */ -pointer cur_head = null, cur_tail = null; /* adjustment list pointers */ -pointer cur_pre_head = null, cur_pre_tail = null; /* pre-adjustment list pointers */ - -/* The |align_state| and |preamble| variables are initialized elsewhere. */ - -@ Alignment stack maintenance is handled by a pair of trivial routines -called |push_alignment| and |pop_alignment|. - -(HH:) It makes not much sense to add support for an \.{attr} keyword to -\.{\\halign} and \.{\\valign} because then we need to decide if we tag -rows or cells or both or come up with \.{cellattr} and \.{rowattr} and -such. But then it even makes sense to have explicit commands (in addition -to the seperator) to tags individual cells. Too muss hassle for now and the -advantages are not that large. - -@c -static void push_alignment(void) -{ - pointer p; /* the new alignment stack node */ - p = new_node(align_stack_node, 0); - vinfo(p + 1) = align_ptr; - vlink(p + 1) = cur_align; - vinfo(p + 2) = preamble; - vlink(p + 2) = cur_span; - vinfo(p + 3) = cur_loop; - vlink(p + 3) = align_state; - vinfo(p + 4) = cur_head; - vlink(p + 4) = cur_tail; - vinfo(p + 5) = cur_pre_head; - vlink(p + 5) = cur_pre_tail; - align_ptr = p; - cur_head = new_node(temp_node, 0); - cur_pre_head = new_node(temp_node, 0); -} - -static void pop_alignment(void) -{ - pointer p; /* the top alignment stack node */ - flush_node(cur_head); - flush_node(cur_pre_head); - p = align_ptr; - cur_pre_tail = vlink(p + 5); - cur_pre_head = vinfo(p + 5); - cur_tail = vlink(p + 4); - cur_head = vinfo(p + 4); - align_state = vlink(p + 3); - cur_loop = vinfo(p + 3); - cur_span = vlink(p + 2); - preamble = vinfo(p + 2); - cur_align = vlink(p + 1); - align_ptr = vinfo(p + 1); - flush_node(p); -} - - -@ \TeX\ has eight procedures that govern alignments: |init_align| and -|fin_align| are used at the very beginning and the very end; |init_row| and -|fin_row| are used at the beginning and end of individual rows; |init_span| -is used at the beginning of a sequence of spanned columns (possibly involving -only one column); |init_col| and |fin_col| are used at the beginning and -end of individual columns; and |align_peek| is used after \.{\\cr} to see -whether the next item is \.{\\noalign}. - -We shall consider these routines in the order they are first used during -the course of a complete \.{\\halign}, namely |init_align|, |align_peek|, -|init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|. - - -@ The preamble is copied directly, except that \.{\\tabskip} causes a change -to the tabskip glue, thereby possibly expanding macros that immediately -follow it. An appearance of \.{\\span} also causes such an expansion. - -Note that if the preamble contains `\.{\\global\\tabskip}', the `\.{\\global}' -token survives in the preamble and the `\.{\\tabskip}' defines new -tabskip glue (locally). - -@c -static void get_preamble_token(void) -{ - RESTART: - get_token(); - while ((cur_chr == span_code) && (cur_cmd == tab_mark_cmd)) { - get_token(); /* this token will be expanded once */ - if (cur_cmd > max_command_cmd) { - expand(); - get_token(); - } - } - if (cur_cmd == endv_cmd) - fatal_error("(interwoven alignment preambles are not allowed)"); - if ((cur_cmd == assign_glue_cmd) - && (cur_chr == glue_base + tab_skip_code)) { - scan_optional_equals(); - scan_glue(glue_val_level); - if (global_defs_par > 0) - geq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val); - else - eq_define(glue_base + tab_skip_code, glue_ref_cmd, cur_val); - goto RESTART; - } -} - - - -@ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate -mode, \TeX\ calls |init_align|, whose task is to get everything off to a -good start. This mostly involves scanning the preamble and putting its -information into the preamble list. -@^preamble@> - -@c -void init_align(void) -{ - /* label done, done1, done2, continue; */ - pointer save_cs_ptr; /* |warning_index| value for error messages */ - pointer p, r; /* for short-term temporary use */ - save_cs_ptr = cur_cs; /* \.{\\halign} or \.{\\valign}, usually */ - push_alignment(); - align_state = -1000000; /* enter a new alignment level */ - - /* When \.{\\halign} is used as a displayed formula, there should be - no other pieces of mlists present. */ - - if ((cur_list.mode_field == mmode) - && ((cur_list.tail_field != cur_list.head_field) - || (incompleat_noad_par != null))) { - const char *hlp[] = - { "Displays can use special alignments (like \\eqalignno)", - "only if nothing but the alignment itself is between $$'s.", - "So I've deleted the formulas that preceded this alignment.", - NULL - }; - tex_error("Improper \\halign inside $$'s", hlp); - flush_math(); - } - push_nest(); /* enter a new semantic level */ - /* In vertical modes, |prev_depth| already has the correct value. But - if we are in |mmode| (displayed formula mode), we reach out to the - enclosing vertical mode for the |prev_depth| value that produces the - correct baseline calculations. */ - if (cur_list.mode_field == mmode) { - cur_list.mode_field = -vmode; - prev_depth_par = nest[nest_ptr - 2].prev_depth_field; - } else if (cur_list.mode_field > 0) { - cur_list.mode_field = -(cur_list.mode_field); - } - scan_spec(align_group); - /* Scan the preamble */ - preamble = null; - cur_align = align_head; - cur_loop = null; - scanner_status = aligning; - warning_index = save_cs_ptr; - align_state = -1000000; - /* at this point, |cur_cmd=left_brace| */ - while (true) { - /* Append the current tabskip glue to the preamble list */ - r = new_param_glue(tab_skip_code); - vlink(cur_align) = r; - cur_align = vlink(cur_align); - - if (cur_cmd == car_ret_cmd) - break; /* \.{\\cr} ends the preamble */ - - /* Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret| */ - /* Scan the template \<u_j>, putting the resulting token list in |hold_token_head| */ - /* Spaces are eliminated from the beginning of a template. */ - - p = hold_token_head; - token_link(p) = null; - while (1) { - get_preamble_token(); - if (cur_cmd == mac_param_cmd) - break; - if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) - && (align_state == -1000000)) { - if ((p == hold_token_head) && (cur_loop == null) - && (cur_cmd == tab_mark_cmd)) { - cur_loop = cur_align; - } else { - const char *hlp[] = - { "There should be exactly one # between &'s, when an", - "\\halign or \\valign is being set up. In this case you had", - "none, so I've put one in; maybe that will work.", - NULL - }; - back_input(); - tex_error("Missing # inserted in alignment preamble", hlp); - break; - } - } else if ((cur_cmd != spacer_cmd) || (p != hold_token_head)) { - r = get_avail(); - token_link(p) = r; - p = token_link(p); - token_info(p) = cur_tok; - } - } - r = new_node(align_record_node, 0); - vlink(cur_align) = r; - cur_align = vlink(cur_align); /* a new alignrecord */ - span_ptr(cur_align) = end_span; - width(cur_align) = null_flag; - u_part(cur_align) = token_link(hold_token_head); - /* Scan the template \<v_j>, putting the resulting token list in |hold_token_head| */ - - p = hold_token_head; - token_link(p) = null; - while (1) { - CONTINUE: - get_preamble_token(); - if ((cur_cmd <= car_ret_cmd) && (cur_cmd >= tab_mark_cmd) - && (align_state == -1000000)) - break; - if (cur_cmd == mac_param_cmd) { - const char *hlp[] = - { "There should be exactly one # between &'s, when an", - "\\halign or \\valign is being set up. In this case you had", - "more than one, so I'm ignoring all but the first.", - NULL - }; - tex_error("Only one # is allowed per tab", hlp); - goto CONTINUE; - } - r = get_avail(); - token_link(p) = r; - p = token_link(p); - token_info(p) = cur_tok; - } - r = get_avail(); - token_link(p) = r; - p = token_link(p); - token_info(p) = end_template_token; /* put \.{\\endtemplate} at the end */ - - v_part(cur_align) = token_link(hold_token_head); - } - scanner_status = normal; - - new_save_level(align_group); - if (every_cr_par != null) - begin_token_list(every_cr_par, every_cr_text); - align_peek(); /* look for \.{\\noalign} or \.{\\omit} */ -} - - -@ The tricky part about alignments is getting the templates into the -scanner at the right time, and recovering control when a row or column -is finished. - -We usually begin a row after each \.{\\cr} has been sensed, unless that -\.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates -the alignment. The |align_peek| routine is used to look ahead and do -the right thing; it either gets a new row started, or gets a \.{\\noalign} -started, or finishes off the alignment. - -@c -void align_peek(void) -{ - RESTART: - align_state = 1000000; - do { - get_x_or_protected(); - } while (cur_cmd == spacer_cmd); - if (cur_cmd == no_align_cmd) { - scan_left_brace(); - new_save_level(no_align_group); - if (cur_list.mode_field == -vmode) - normal_paragraph(); - } else if (cur_cmd == right_brace_cmd) { - fin_align(); - } else if ((cur_cmd == car_ret_cmd) && (cur_chr == cr_cr_code)) { - goto RESTART; /* ignore \.{\\crcr} */ - } else { - init_row(); /* start a new row */ - init_col(); /* start a new column and replace what we peeked at */ - } -} - - -@ The parameter to |init_span| is a pointer to the alignrecord where the -next column or group of columns will begin. A new semantic level is -entered, so that the columns will generate a list for subsequent packaging. - -@c -static void init_span(pointer p) -{ - push_nest(); - if (cur_list.mode_field == -hmode) { - space_factor_par = 1000; - } else { - prev_depth_par = ignore_depth; - normal_paragraph(); - } - cur_span = p; -} - - -@ To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'), -we enter a new semantic level, copy the first tabskip glue, and change -from internal vertical mode to restricted horizontal mode or vice versa. -The |space_factor| and |prev_depth| are not used on this semantic level, -but we clear them to zero just to be tidy. - -@c -void init_row(void) -{ - push_nest(); - cur_list.mode_field = (-hmode - vmode) - cur_list.mode_field; - if (cur_list.mode_field == -hmode) - space_factor_par = 0; - else - prev_depth_par = 0; - tail_append(new_glue(preamble)); - subtype(cur_list.tail_field) = tab_skip_code + 1; - cur_align = vlink(preamble); - cur_tail = cur_head; - cur_pre_tail = cur_pre_head; - init_span(cur_align); -} - - -@ When a column begins, we assume that |cur_cmd| is either |omit| or else -the current token should be put back into the input until the \<u_j> -template has been scanned. (Note that |cur_cmd| might be |tab_mark| or -|car_ret|.) We also assume that |align_state| is approximately 1000000 at -this time. We remain in the same mode, and start the template if it is -called for. - -@c -void init_col(void) -{ - extra_info(cur_align) = cur_cmd; - if (cur_cmd == omit_cmd) - align_state = 0; - else { - back_input(); - begin_token_list(u_part(cur_align), u_template); - } /* now |align_state=1000000| */ -} - - -@ The scanner sets |align_state| to zero when the \<u_j> template ends. When -a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|, -the scanner activates the following code, which fires up the \<v_j> template. -We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|, -|span_code|, or a character code, depending on how the column text has ended. - -This part of the program had better not be activated when the preamble -to another alignment is being scanned, or when no alignment preamble is active. - -@c -void insert_vj_template(void) -{ - if ((scanner_status == aligning) || (cur_align == null)) - fatal_error("(interwoven alignment preambles are not allowed)"); - cur_cmd = extra_info(cur_align); - extra_info(cur_align) = cur_chr; - if (cur_cmd == omit_cmd) - begin_token_list(omit_template, v_template); - else - begin_token_list(v_part(cur_align), v_template); - align_state = 1000000; -} - -/* Determine the stretch order */ -#define determine_stretch_order() do { \ - if (total_stretch[filll]!=0) o=filll; \ - else if (total_stretch[fill]!=0) o=fill; \ - else if (total_stretch[fil]!=0) o=fil; \ - else if (total_stretch[sfi]!=0) o=sfi; \ - else o=normal; \ - } while (0) - - -/* Determine the shrink order */ -#define determine_shrink_order() do { \ - if (total_shrink[filll]!=0) o=filll; \ - else if (total_shrink[fill]!=0) o=fill; \ - else if (total_shrink[fil]!=0) o=fil; \ - else if (total_shrink[sfi]!=0) o=sfi; \ - else o=normal; \ - } while (0) - - - -@ When the |endv| command at the end of a \<v_j> template comes through the -scanner, things really start to happen; and it is the |fin_col| routine -that makes them happen. This routine returns |true| if a row as well as a -column has been finished. - -@c -boolean fin_col(void) -{ - pointer p; /* the alignrecord after the current one */ - pointer q, r; /* temporary pointers for list manipulation */ - pointer s; /* a new span node */ - pointer u; /* a new unset box */ - scaled w; /* natural width */ - unsigned char o; /* order of infinity */ - halfword n; /* span counter */ - if (cur_align == null) - confusion("endv"); - q = vlink(cur_align); - if (q == null) - confusion("endv"); - if (align_state < 500000) - fatal_error("(interwoven alignment preambles are not allowed)"); - p = vlink(q); - /* If the preamble list has been traversed, check that the row has ended */ - if ((p == null) && (extra_info(cur_align) < cr_code)) { - if (cur_loop != null) { - /* Lengthen the preamble periodically */ - r = new_node(align_record_node, 0); - vlink(q) = r; - p = vlink(q); /* a new alignrecord */ - span_ptr(p) = end_span; - width(p) = null_flag; - cur_loop = vlink(cur_loop); - - /* Copy the templates from node |cur_loop| into node |p| */ - q = hold_token_head; - r = u_part(cur_loop); - while (r != null) { - s = get_avail(); - token_link(q) = s; - q = token_link(q); - token_info(q) = token_info(r); - r = token_link(r); - } - token_link(q) = null; - u_part(p) = token_link(hold_token_head); - q = hold_token_head; - r = v_part(cur_loop); - while (r != null) { - s = get_avail(); - token_link(q) = s; - q = token_link(q); - token_info(q) = token_info(r); - r = token_link(r); - } - token_link(q) = null; - v_part(p) = token_link(hold_token_head); - - cur_loop = vlink(cur_loop); - r = new_glue(cur_loop); - vlink(p) = r; - } else { - const char *hlp[] = - { "You have given more \\span or & marks than there were", - "in the preamble to the \\halign or \\valign now in progress.", - "So I'll assume that you meant to type \\cr instead.", - NULL - }; - extra_info(cur_align) = cr_code; - tex_error("Extra alignment tab has been changed to \\cr", hlp); - } - } - if (extra_info(cur_align) != span_code) { - unsave(); - new_save_level(align_group); - /* Package an unset box for the current column and record its width */ - if (cur_list.mode_field == -hmode) { - adjust_tail = cur_tail; - pre_adjust_tail = cur_pre_tail; - u = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, - additional, align_set_group, -1, 0, 0); - w = width(u); - cur_tail = adjust_tail; - adjust_tail = null; - cur_pre_tail = pre_adjust_tail; - pre_adjust_tail = null; - } else { - u = filtered_vpackage(vlink(cur_list.head_field), - 0, additional, 0, align_set_group, -1, 0, 0); - w = height(u); - } - n = min_quarterword; /* this represents a span count of 1 */ - if (cur_span != cur_align) { - /* Update width entry for spanned columns */ - q = cur_span; - do { - incr(n); - q = vlink(vlink(q)); - } while (q != cur_align); - if (n > max_quarterword) - confusion("too many spans"); /* this can happen, but won't */ - q = cur_span; - while (span_span(span_ptr(q)) < n) { - q = span_ptr(q); - } - if (span_span(span_ptr(q)) > n) { - s = new_span_node(span_ptr(q), n, w); - span_ptr(q) = s; - } else if (width(span_ptr(q)) < w) { - width(span_ptr(q)) = w; - } - - } else if (w > width(cur_align)) { - width(cur_align) = w; - } - type(u) = unset_node; - span_count(u) = (quarterword) n; - determine_stretch_order(); - glue_order(u) = o; - glue_stretch(u) = total_stretch[o]; - determine_shrink_order(); - glue_sign(u) = o; - glue_shrink(u) = total_shrink[o]; - pop_nest(); - vlink(cur_list.tail_field) = u; - cur_list.tail_field = u; - - /* Copy the tabskip glue between columns */ - tail_append(new_glue(vlink(cur_align))); - subtype(cur_list.tail_field) = tab_skip_code + 1; - - if (extra_info(cur_align) >= cr_code) { - return true; - } - init_span(p); - } - align_state = 1000000; - do { - get_x_or_protected(); - } while (cur_cmd == spacer_cmd); - cur_align = p; - init_col(); - return false; -} - - - -@ A span node is a 3-word record containing |width|, |span_span|, and -|span_ptr| fields. The |span_span| field indicates the number of -spanned columns; the |span_ptr| field points to a span node for the same -starting column, having a greater extent of spanning, or to -|end_span|, which has the largest possible |span_span| field; the |width| -field holds the largest natural width corresponding to a particular -set of spanned columns. - -A list of the maximum widths so far, for spanned columns starting at a -given column, begins with the |span_ptr| field of the alignrecord for -that column. The code has to make sure that there is room for -|span_ptr| in both the alignrecord and the span nodes, which is why -|span_ptr| replaces |node_attr|. -@^data structure assumptions@> - -The |new_span_node| function is defined in |texnodes.c|. - -@c -#ifndef span_span -# define span_span(A) vlink((A)+1) /* that is normally |alink| */ -#endif - - -@ At the end of a row, we append an unset box to the current vlist (for -\.{\\halign}) or the current hlist (for \.{\\valign}). This unset box -contains the unset boxes for the columns, separated by the tabskip glue. -Everything will be set later. - -@c -void fin_row(void) -{ - pointer p; /* the new unset box */ - if (cur_list.mode_field == -hmode) { - p = filtered_hpack(cur_list.head_field, cur_list.tail_field, 0, - additional, fin_row_group, -1, 0, 0); - pop_nest(); - if (cur_pre_head != cur_pre_tail) - append_list(cur_pre_head, cur_pre_tail); - append_to_vlist(p,lua_key_index(alignment)); - if (cur_head != cur_tail) - append_list(cur_head, cur_tail); - } else { - p = filtered_vpackage(vlink(cur_list.head_field), - 0, additional, max_depth_par, fin_row_group, -1, 0, 0); - pop_nest(); - vlink(cur_list.tail_field) = p; - cur_list.tail_field = p; - space_factor_par = 1000; - } - type(p) = unset_node; - glue_stretch(p) = 0; - if (every_cr_par != null) - begin_token_list(every_cr_par, every_cr_text); - align_peek(); - /* note that |glue_shrink(p)=0| since |glue_shrink==shift_amount| */ -} - - -@ Finally, we will reach the end of the alignment, and we can breathe a -sigh of relief that memory hasn't overflowed. All the unset boxes will now be -set so that the columns line up, taking due account of spanned columns. - -@c -void fin_align(void) -{ - pointer p, q, r, s, u, rr; /* registers for the list operations */ - scaled t, w; /* width of column */ - scaled o; /* shift offset for unset boxes */ - halfword n; /* matching span amount */ - scaled rule_save; /* temporary storage for |overfull_rule| */ - halfword pd; /* temporary storage for |prev_depth| */ - halfword ng; /* temporary storage for |new_glue| */ - if (cur_group != align_group) - confusion("align1"); - unsave(); /* that |align_group| was for individual entries */ - if (cur_group != align_group) - confusion("align0"); - unsave(); /* that |align_group| was for the whole alignment */ - if (nest[nest_ptr - 1].mode_field == mmode) - o = display_indent_par; - else - o = 0; - /* Go through the preamble list, determining the column widths and - * changing the alignrecords to dummy unset boxes - */ - -/* It's time now to dismantle the preamble list and to compute the column -widths. Let $w_{ij}$ be the maximum of the natural widths of all entries -that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$ -contains $w_{ii}$ in its |width| field, and there is also a linked list of -the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field; -these span nodes contain the value $j-i+|min_quarterword|$ in their -|link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which -we regard as $-\infty$. - -The final column widths are defined by the formula -$$w_j=\max_{1\L i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$ -where $t_k$ is the natural width of the tabskip glue between columns -$k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range -|1<=i<=j| (i.e., if every entry that involved column~|j| also involved -column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after -column~|j|. - -\TeX\ computes these values by using the following scheme: First $w_1=w_{11}$. -Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$. -Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$ -for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its -value is changed to zero and so is the next tabskip. -*/ - q = vlink(preamble); - do { - flush_list(u_part(q)); - flush_list(v_part(q)); - p = vlink(vlink(q)); - if (width(q) == null_flag) { - /* Nullify |width(q)| and the tabskip glue following this column */ - width(q) = 0; - r = vlink(q); - reset_glue_to_zero(r); /* is a lready copy */ - } - if (span_ptr(q) != end_span) { - /* Merge the widths in the span nodes of |q| with those of |p|, - destroying the span nodes of |q| */ - /* - Merging of two span-node lists is a typical exercise in the manipulation of - linearly linked data structures. The essential invariant in the following - |repeat| loop is that we want to dispense with node |r|, in |q|'s list, - and |u| is its successor; all nodes of |p|'s list up to and including |s| - have been processed, and the successor of |s| matches |r| or precedes |r| - or follows |r|, according as |link(r)=n| or |link(r)>n| or |link(r)<n|. - */ - t = width(q) + width(vlink(q)); - r = span_ptr(q); - s = end_span; - span_ptr(s) = p; - n = min_quarterword + 1; - do { - width(r) = width(r) - t; - u = span_ptr(r); - while (span_span(r) > n) { - s = span_ptr(s); - n = span_span(span_ptr(s)) + 1; - } - if (span_span(r) < n) { - span_ptr(r) = span_ptr(s); - span_ptr(s) = r; - decr(span_span(r)); - s = r; - } else { - if (width(r) > width(span_ptr(s))) - width(span_ptr(s)) = width(r); - flush_node(r); - } - r = u; - } while (r != end_span); - } - type(q) = unset_node; - span_count(q) = min_quarterword; - height(q) = 0; - depth(q) = 0; - glue_order(q) = normal; - glue_sign(q) = normal; - glue_stretch(q) = 0; - glue_shrink(q) = 0; - q = p; - } while (q != null); - - /* Package the preamble list, to determine the actual tabskip glue amounts, - and let |p| point to this prototype box */ - /* Now the preamble list has been converted to a list of alternating unset - boxes and tabskip glue, where the box widths are equal to the final - column sizes. In case of \.{\\valign}, we change the widths to heights, - so that a correct error message will be produced if the alignment is - overfull or underfull. - */ - - decr(save_ptr); - pack_begin_line = -cur_list.ml_field; - if (cur_list.mode_field == -vmode) { - rule_save = overfull_rule_par; - overfull_rule_par = 0; /* prevent rule from being packaged */ - p = hpack(preamble, saved_value(0), saved_level(0), -1); - overfull_rule_par = rule_save; - } else { - q = vlink(preamble); - do { - height(q) = width(q); - width(q) = 0; - q = vlink(vlink(q)); - } while (q != null); - p = filtered_vpackage(preamble, - saved_value(0), saved_level(0), max_depth_par, preamble_group, -1, 0, 0); - q = vlink(preamble); - do { - width(q) = height(q); - height(q) = 0; - q = vlink(vlink(q)); - } while (q != null); - } - pack_begin_line = 0; - - /* Set the glue in all the unset boxes of the current list */ - q = vlink(cur_list.head_field); - s = cur_list.head_field; - while (q != null) { - if (!is_char_node(q)) { - if (type(q) == unset_node) { - /* Set the unset box |q| and the unset boxes in it */ - /* The unset box |q| represents a row that contains one or more unset boxes, - depending on how soon \.{\\cr} occurred in that row. */ - - if (cur_list.mode_field == -vmode) { - type(q) = hlist_node; - subtype(q) = align_row_list; - width(q) = width(p); - } else { - type(q) = vlist_node; - subtype(q) = align_row_list; - height(q) = height(p); - } - glue_order(q) = glue_order(p); - glue_sign(q) = glue_sign(p); - glue_set(q) = glue_set(p); - shift_amount(q) = o; - r = vlink(list_ptr(q)); - assert (type(r) == unset_node); - s = vlink(list_ptr(p)); - do { - /* Set the glue in node |r| and change it from an unset node */ - /* A box made from spanned columns will be followed by tabskip glue nodes and - by empty boxes as if there were no spanning. This permits perfect alignment - of subsequent entries, and it prevents values that depend on floating point - arithmetic from entering into the dimensions of any boxes. - */ - n = span_count(r); - t = width(s); - w = t; - u = hold_head; - while (n > min_quarterword) { - decr(n); - /* Append tabskip glue and an empty box to list |u|, - and update |s| and |t| as the prototype nodes are passed */ - - s = vlink(s); - ng = new_glue(s); - vlink(u) = ng; - u = vlink(u); - subtype(u) = tab_skip_code + 1; - t = t + width(s); - if (glue_sign(p) == stretching) { - if (stretch_order(s) == glue_order(p)) - t = t + round(float_cast(glue_set(p)) * float_cast(stretch(s))); - } else if (glue_sign(p) == shrinking) { - if (shrink_order(s) == glue_order(p)) - t = t - round(float_cast(glue_set(p)) * float_cast(shrink(s))); - } - s = vlink(s); - rr = new_null_box(); - vlink(u) = rr; - u = vlink(u); - t = t + width(s); - subtype(u) = align_cell_list; - if (cur_list.mode_field == -vmode) { - width(u) = width(s); - } else { - type(u) = vlist_node; - height(u) = width(s); - } - - } - if (cur_list.mode_field == -vmode) { - /* Make the unset node |r| into an |hlist_node| of width |w|, - setting the glue as if the width were |t| */ - - height(r) = height(q); - depth(r) = depth(q); - if (t == width(r)) { - glue_sign(r) = normal; - glue_order(r) = normal; - set_glue_ratio_zero(glue_set(r)); - } else if (t > width(r)) { - glue_sign(r) = stretching; - if (glue_stretch(r) == 0) - set_glue_ratio_zero(glue_set(r)); - else - glue_set(r) = - unfloat((double) (t - width(r)) / - glue_stretch(r)); - } else { - glue_order(r) = glue_sign(r); - glue_sign(r) = shrinking; - if (glue_shrink(r) == 0) - set_glue_ratio_zero(glue_set(r)); - else if ((glue_order(r) == normal) - && (width(r) - t > glue_shrink(r))) - set_glue_ratio_one(glue_set(r)); - else - glue_set(r) = - unfloat((double) (width(r) - t) / - glue_shrink(r)); - } - width(r) = w; - type(r) = hlist_node; - subtype(r) = align_cell_list; - - } else { - /* Make the unset node |r| into a |vlist_node| of height |w|, - setting the glue as if the height were |t| */ - - width(r) = width(q); - if (t == height(r)) { - glue_sign(r) = normal; - glue_order(r) = normal; - set_glue_ratio_zero(glue_set(r)); - } else if (t > height(r)) { - glue_sign(r) = stretching; - if (glue_stretch(r) == 0) - set_glue_ratio_zero(glue_set(r)); - else - glue_set(r) = - unfloat((t - height(r)) / glue_stretch(r)); - } else { - glue_order(r) = glue_sign(r); - glue_sign(r) = shrinking; - if (glue_shrink(r) == 0) - set_glue_ratio_zero(glue_set(r)); - else if ((glue_order(r) == normal) - && (height(r) - t > glue_shrink(r))) - set_glue_ratio_one(glue_set(r)); - else - glue_set(r) = - unfloat((height(r) - t) / glue_shrink(r)); - } - height(r) = w; - type(r) = vlist_node; - subtype(r) = align_cell_list; - - } - /* subtype(r) = 0; */ - shift_amount(r) = 0; - if (u != hold_head) { /* append blank boxes to account for spanned nodes */ - vlink(u) = vlink(r); - vlink(r) = vlink(hold_head); - r = u; - } - - r = vlink(vlink(r)); - s = vlink(vlink(s)); - } while (r != null); - - } else if (type(q) == rule_node) { - /* Make the running dimensions in rule |q| extend to the - boundaries of the alignment */ - if (is_running(width(q))) - width(q) = width(p); - if (is_running(height(q))) - height(q) = height(p); - if (is_running(depth(q))) - depth(q) = depth(p); - if (o != 0) { - r = vlink(q); - vlink(q) = null; - q = hpack(q, 0, additional, -1); - shift_amount(q) = o; - subtype(q) = align_cell_list; - vlink(q) = r; - vlink(s) = q; - } - } - } - s = q; - q = vlink(q); - } - flush_node_list(p); - pop_alignment(); - /* Insert the current list into its environment */ - /* We now have a completed alignment, in the list that starts at |cur_list.head_field| - and ends at |cur_list.tail_field|. This list will be merged with the one that encloses - it. (In case the enclosing mode is |mmode|, for displayed formulas, - we will need to insert glue before and after the display; that part of the - program will be deferred until we're more familiar with such operations.) - */ - pd = prev_depth_par; - p = vlink(cur_list.head_field); - q = cur_list.tail_field; - pop_nest(); - if (cur_list.mode_field == mmode) { - finish_display_alignment(p, q, pd); - } else { - prev_depth_par = pd; /* aux:=aux_save; */ - vlink(cur_list.tail_field) = p; - if (p != null) - cur_list.tail_field = q; - if (cur_list.mode_field == vmode) { - if (!output_active) - lua_node_filter_s(buildpage_filter_callback,lua_key_index(alignment)); - build_page(); - } - } -} - -@ The token list |omit_template| just referred to is a constant token -list that contains the special control sequence \.{\\endtemplate} only. - -@c -void initialize_alignments(void) -{ - token_info(omit_template) = end_template_token; /* |link(omit_template)=null| */ - span_span(end_span) = max_quarterword + 1; - span_ptr(end_span) = null; -} diff --git a/Build/source/texk/web2c/luatexdir/tex/arithmetic.c b/Build/source/texk/web2c/luatexdir/tex/arithmetic.c new file mode 100644 index 00000000000..2782c253400 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/arithmetic.c @@ -0,0 +1,815 @@ +/* + +arithmetic.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +The principal computations performed by \TeX\ are done entirely in terms of +integers less than $2^{31}$ in magnitude; and divisions are done only when both +dividend and divisor are nonnegative. Thus, the arithmetic specified in this +program can be carried out in exactly the same way on a wide variety of +computers, including some small ones. Why? Because the arithmetic calculations +need to be spelled out precisely in order to guarantee that \TeX\ will produce +identical output on different machines. If some quantities were rounded +differently in different implementations, we would find that line breaks and even +page breaks might occur in different places. Hence the arithmetic of \TeX\ has +been designed with care, and systems that claim to be implementations of \TeX82 +should follow precisely the @:TeX82}{\TeX82@> calculations as they appear in the +present program. + +Actually there are three places where \TeX\ uses |div| with a possibly negative +numerator. These are harmless; see |div| in the index. Also if the user sets the +\.{\\time} or the \.{\\year} to a negative value, some diagnostic information +will involve negative-numerator division. The same remarks apply for |mod| as +well as for |div|. + +Here is a routine that calculates half of an integer, using an unambiguous +convention with respect to signed odd numbers. + +*/ + +int half(int x) +{ + if (odd(x)) + return ((x + 1) / 2); + else + return (x / 2); +} + +/*tex + +The following function is used to create a scaled integer from a given decimal +fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is +given in |dig[i]|, and the calculation produces a correctly rounded result. + +*/ + +scaled round_decimals(int k) +{ + int a = 0; + while (k-- > 0) { + a = (a + dig[k] * two) / 10; + } + return ((a + 1) / 2); +} + +/*tex + +Conversely, here is a procedure analogous to |print_int|. If the output of this +procedure is subsequently read by \TeX\ and converted by the |round_decimals| +routine above, it turns out that the original value will be reproduced exactly; +the ``simplest'' such decimal number is output, but there is always at least one +digit following the decimal point. + +The invariant relation in the \&{repeat} loop is that a sequence of decimal +digits yet to be printed will yield the original number if and only if they form +a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$. We can stop if and only +if $f=0$ satisfies this condition; the loop will terminate before $s$ can +possibly become zero. + +The next one prints a scaled real, rounded to five digits. + +*/ + +void print_scaled(scaled s) +{ + /*tex The amount of allowable inaccuracy: */ + scaled delta; + char buffer[20]; + int i = 0; + if (s < 0) { + /*tex Print the sign, if negative. */ + print_char('-'); + negate(s); + } + /*tex Print the integer part. */ + print_int(s / unity); + buffer[i++] = '.'; + s = 10 * (s % unity) + 5; + delta = 10; + do { + if (delta > unity) { + /*tex Round the last digit. */ + s = s + 0100000 - 50000; + } + buffer[i++] = '0' + (s / unity); + s = 10 * (s % unity); + delta = delta * 10; + } while (s > delta); + buffer[i++] = '\0'; + tprint(buffer); +} + +/*tex + +Physical sizes that a \TeX\ user specifies for portions of documents are +represented internally as scaled points. Thus, if we define an `sp' (scaled +@^sp@> point) as a unit equal to $2^{-16}$ printer's points, every dimension +inside of \TeX\ is an integer number of sp. There are exactly 4,736,286.72 sp per +inch. Users are not allowed to specify dimensions larger than $2^{30}-1$ sp, +which is a distance of about 18.892 feet (5.7583 meters); two such quantities can +be added without overflow on a 32-bit computer. + +The present implementation of \TeX\ does not check for overflow when @^overflow +in arithmetic@> dimensions are added or subtracted. This could be done by +inserting a few dozen tests of the form `\ignorespaces|if x>=010000000000 then +@t\\{report\_overflow}@>|', but the chance of overflow is so remote that such +tests do not seem worthwhile. + +\TeX\ needs to do only a few arithmetic operations on scaled quantities, other +than addition and subtraction, and the following subroutines do most of the work. +A single computation might use several subroutine calls, and it is desirable to +avoid producing multiple error messages in case of arithmetic overflow; so the +routines set the global variable |arith_error| to |true| instead of reporting +errors directly to the user. Another global variable, |tex_remainder|, holds the +remainder after a division. + +*/ + +/*tex Has arithmetic overflow occurred recently? */ + +boolean arith_error; + +/*tex The amount subtracted to get an exact division. */ + +scaled tex_remainder; + +/*tex + + The first arithmetical subroutine we need computes $nx+y$, where |x| +and~|y| are |scaled| and |n| is an integer. We will also use it to +multiply integers. + +*/ + +scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer) +{ + if (n == 0) + return y; + if (n < 0) { + negate(x); + negate(n); + } + if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) { + return (n * x + y); + } else { + arith_error = true; + return 0; + } +} + +/*tex + +We also need to divide scaled dimensions by integers. + +*/ + +scaled x_over_n(scaled x, int n) +{ + /*tex Should |tex_remainder| be negated? */ + boolean negative = false; + if (n == 0) { + arith_error = true; + tex_remainder = x; + return 0; + } else { + if (n < 0) { + negate(x); + negate(n); + negative = true; + } + if (x >= 0) { + tex_remainder = x % n; + if (negative) + negate(tex_remainder); + return (x / n); + } else { + tex_remainder = -((-x) % n); + if (negative) + negate(tex_remainder); + return (-((-x) / n)); + } + } +} + +/*tex + +Then comes the multiplication of a scaled number by a fraction |n/d|, where |n| +and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is positive. It would +be too dangerous to multiply by~|n| and then divide by~|d|, in separate +operations, since overflow might well occur; and it would be too inaccurate to +divide by |d| and then multiply by |n|. Hence this subroutine simulates +1.5-precision arithmetic. + +*/ + +scaled xn_over_d(scaled x, int n, int d) +{ + nonnegative_integer t, u, v, xx, dd; + boolean positive = true; + if (x < 0) { + negate(x); + positive = false; + } + xx = (nonnegative_integer) x; + dd = (nonnegative_integer) d; + t = ((xx % 0100000) * (nonnegative_integer) n); + u = ((xx / 0100000) * (nonnegative_integer) n + (t / 0100000)); + v = (u % dd) * 0100000 + (t % 0100000); + if (u / dd >= 0100000) + arith_error = true; + else + u = 0100000 * (u / dd) + (v / dd); + if (positive) { + tex_remainder = (int) (v % dd); + return (scaled) u; + } else { + /*tex The casts are for ms cl. */ + tex_remainder = -(int) (v % dd); + return -(scaled) (u); + } +} + +/*tex + +The next subroutine is used to compute the ``badness'' of glue, when a total~|t| +is supposed to be made from amounts that sum to~|s|. According to {\sl The \TeX +book}, the badness of this situation is $100(t/s)^3$; however, badness is simply +a heuristic, so we need not squeeze out the last drop of accuracy when computing +it. All we really want is an approximation that has similar properties. +@:TeXbook}{\sl The \TeX book@> + +The actual method used to compute the badness is easier to read from the program +than to describe in words. It produces an integer value that is a reasonably +close approximation to $100(t/s)^3$, and all implementations of \TeX\ should use +precisely this method. Any badness of $2^{13}$ or more is treated as infinitely +bad, and represented by 10000. + +It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s) +>= badness(t,s+1)|}.$$ The badness function defined here is capable of computing +at most 1095 distinct values, but that is plenty. + +*/ + +halfword badness(scaled t, scaled s) +{ + /*tex Approximation to $\alpha t/s$, where $\alpha^3\approx 100\cdot2^{18}$ */ + int r; + if (t == 0) { + return 0; + } else if (s <= 0) { + return inf_bad; + } else { + /*tex $297^3=99.94\times2^{18}$ */ + if (t <= 7230584) { + r = (t * 297) / s; + } else if (s >= 1663497) { + r = t / (s / 297); + } else { + r = t; + } + if (r > 1290) { + /*tex $1290^3<2^{31}<1291^3$ */ + return inf_bad; + } else { + /*tex This is $r^3/2^{18}$, rounded to the nearest integer. */ + return ((r * r * r + 0400000) / 01000000); + } + } +} + +/*tex + +When \TeX\ ``packages'' a list into a box, it needs to calculate the +proportionality ratio by which the glue inside the box should stretch or shrink. +This calculation does not affect \TeX's decision making, so the precise details +of rounding, etc., in the glue calculation are not of critical importance for the +consistency of results on different computers. + +We shall use the type |glue_ratio| for such proportionality ratios. A glue ratio +should take the same amount of memory as an |integer| (usually 32 bits) if it is +to blend smoothly with \TeX's other data structures. Thus |glue_ratio| should be +equivalent to |short_real| in some implementations of PASCAL. Alternatively, it +is possible to deal with glue ratios using nothing but fixed-point arithmetic; +see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the routines cited there must +be modified to allow negative glue ratios.) @^system dependencies@> + +*/ + +/* + +This section is (almost) straight from MetaPost. I (Taco) had to change the types +(use |integer| instead of |fraction|), but that should not have any influence on +the actual calculations (the original comments refer to quantities like +|fraction_four| ($2^{30}$), and that is the same as the numeric representation of +|max_dimen|). + +I've copied the low-level variables and routines that are needed, but only those +(e.g. |m_log|), not the accompanying ones like |m_exp|. Most of the following +low-level numeric routines are only needed within the calculation of |norm_rand|. +I've been forced to rename |make_fraction| to |make_frac| because TeX already has +a routine by that name with a wholly different function (it creates a +|fraction_noad| for math typesetting) + +And now let's complete our collection of numeric utility routines by considering +random number generation. \MP{} generates pseudo-random numbers with the additive +scheme recommended in Section 3.6 of {\sl The Art of Computer Programming}; +however, the results are random fractions between 0 and |fraction_one-1|, +inclusive. + +There's an auxiliary array |randoms| that contains 55 pseudo-random fractions. +Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$, we generate batches +of 55 new $x_n$'s at a time by calling |new_randoms|. The global variable +|j_random| tells which element has most recently been consumed. + +*/ + +/*tex The last 55 random values generated: */ + +static int randoms[55]; + +/*tex The number of unused |randoms|: */ + +static int j_random; + +/*tex The default random seed: */ + +scaled random_seed; + +/*tex A small bit of \METAPOST\ is needed. */ + +#define fraction_half 01000000000 /* $2^{27} $, represents 0.50000000 */ +#define fraction_one 02000000000 /* $2^{28} $, represents 1.00000000 */ +#define fraction_four 010000000000 /* $2^{30} $, represents 4.00000000 */ +#define el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */ + +/*tex + +The |make_frac| routine produces the |fraction| equivalent of |p/q|, given +integers |p| and~|q|; it computes the integer +$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are positive. If |p| and +|q| are both of the same scaled type |t|, the ``type relation'' +|make_frac(t,t)=fraction| is valid; and it's also possible to use the subroutine +``backwards,'' using the relation |make_frac(t,fraction)=t| between scaled types. + +If the result would have magnitude $2^{31}$ or more, |make_frac| sets +|arith_error:=true|. Most of \MP's internal computations have been designed to +avoid this sort of error. + +If this subroutine were programmed in assembly language on a typical machine, we +could simply compute |(@t$2^{28}$@>*p)div q|, since a double-precision product +can often be input to a fixed-point division instruction. But when we are +restricted to PASCAL arithmetic it is necessary either to resort to +multiple-precision maneuvering or to use a simple but slow iteration. The +multiple-precision technique would be about three times faster than the code +adopted here, but it would be comparatively long and tricky, involving about +sixteen additional multiplications and divisions. + +This operation is part of \MP's ``inner loop''; indeed, it will consume nearly +10\%! of the running time (exclusive of input and output) if the code below is +left unchanged. A machine-dependent recoding will therefore make \MP\ run faster. +The present implementation is highly portable, but slow; it avoids multiplication +and division except in the initial stage. System wizards should be careful to +replace it with a routine that is guaranteed to produce identical results in all +cases. @^system dependencies@> + +As noted below, a few more routines should also be replaced by machine-dependent +code, for efficiency. But when a procedure is not part of the ``inner loop,'' +such changes aren't advisable; simplicity and robustness are preferable to +trickery, unless the cost is too high. + +*/ + +static int make_frac(int p, int q) +{ + /*tex The fraction bits, with a leading 1 bit: */ + int f; + /*tex The integer part of $\vert p/q\vert$: */ + int n; + /*tex Disables certain compiler optimizations: */ + register int be_careful; + /*tex Should the result be negated? */ + boolean negative = false; + if (p < 0) { + negate(p); + negative = true; + } + if (q <= 0) { + negate(q); + negative = !negative; + } + n = p / q; + p = p % q; + if (n >= 8) { + arith_error = true; + if (negative) + return (-el_gordo); + else + return el_gordo; + } else { + n = (n - 1) * fraction_one; + /*tex_remainder + + Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$. The |repeat| loop + here preserves the following invariant relations between |f|, |p|, + and~|q|: (i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an + integer and $p_0$ is the original value of~$p$. + + Notice that the computation specifies |(p-q)+p| instead of |(p+p)-q|, + because the latter could overflow. Let us hope that optimizing + compilers do not miss this point; a special variable |be_careful| is + used to emphasize the necessary order of computation. Optimizing + compilers should keep |be_careful| in a register, not store it in + memory. + + */ + f = 1; + do { + be_careful = p - q; + p = be_careful + p; + if (p >= 0) + f = f + f + 1; + else { + f += f; + p = p + q; + } + } while (f < fraction_one); + be_careful = p - q; + if (be_careful + p >= 0) + incr(f); + + if (negative) + return (-(f + n)); + else + return (f + n); + } +} + +static int take_frac(int q, int f) +{ + /*tex The fraction so far: */ + int p; + /*tex Additional multiple of $q$: */ + int n; + /*tex Disables certain compiler optimizations. */ + register int be_careful; + /*tex Should the result be negated? */ + boolean negative = false; + /*tex Reduce to the case that |f>=0| and |q>0|. */ + if (f < 0) { + negate(f); + negative = true; + } + if (q < 0) { + negate(q); + negative = !negative; + } + if (f < fraction_one) { + n = 0; + } else { + n = f / fraction_one; + f = f % fraction_one; + if (q <= el_gordo / n) { + n = n * q; + } else { + arith_error = true; + n = el_gordo; + } + } + f = f + fraction_one; + /*tex + + Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$. The invariant relations + in this case are (i)~$\lfloor(qf+p)/2^k\rfloor =\lfloor + qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and $f_0$ is the + original value of~$f$; (ii)~$2^k\L f<2^{k+1}$. + + Here |p| becomes $2^{27}$; the invariants hold now with $k=28$: + + */ + p = fraction_half; + if (q < fraction_four) { + do { + if (odd(f)) + p = halfp(p + q); + else + p = halfp(p); + f = halfp(f); + } while (f != 1); + } else { + do { + if (odd(f)) + p = p + halfp(q - p); + else + p = halfp(p); + f = halfp(f); + } while (f != 1); + } + be_careful = n - el_gordo; + if (be_careful + p > 0) { + arith_error = true; + n = el_gordo - p; + } + if (negative) + return (-(n + p)); + else + return (n + p); +} + +/*tex + +The subroutines for logarithm and exponential involve two tables. The first is +simple: |two_to_the[k]| equals $2^k$. The second involves a bit more calculation, +which the author claims to have done correctly: |spec_log[k]| is $2^{27}$ times +$\ln\bigl(1/(1-2^{-k})\bigr)= 2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, +rounded to the nearest integer. + +*/ + +/*tex The powers of two: */ + +static int two_to_the[31]; + +/*tex Special logarithms: */ + +static int spec_log[29]; + +void initialize_arithmetic(void) +{ + int k; + two_to_the[0] = 1; + for (k = 1; k <= 30; k++) { + two_to_the[k] = 2 * two_to_the[k - 1]; + } + spec_log [1] = 93032640; + spec_log [2] = 38612034; + spec_log [3] = 17922280; + spec_log [4] = 8662214; + spec_log [5] = 4261238; + spec_log [6] = 2113709; + spec_log [7] = 1052693; + spec_log [8] = 525315; + spec_log [9] = 262400; + spec_log[10] = 131136; + spec_log[11] = 65552; + spec_log[12] = 32772; + spec_log[13] = 16385; + for (k = 14; k <= 27; k++) { + spec_log[k] = two_to_the[27 - k]; + } + spec_log[28] = 1; +} + +static int m_log(int x) +{ + /*tex Auxiliary registers: */ + int y, z; + /*tex Iteration counter: */ + int k; + if (x <= 0) { + /*tex Handle non-positive logarithm. */ + print_err("Logarithm of "); + print_scaled(x); + tprint(" has been replaced by 0"); + help2( + "Since I don't take logs of non-positive numbers,", + "I'm zeroing this one. Proceed, with fingers crossed." + ); + error(); + return 0; + } else { + /*tex $14\times2^{27}\ln2\approx1302456956.421063$ */ + y = 1302456956 + 4 - 100; + /*tex $2^{16}\times .421063\approx 27595$ */ + z = 27595 + 6553600; + while (x < fraction_four) { + x += x; + /*tex $2^{27}\ln2\approx 93032639.74436163$ */ + y = y - 93032639; + /*tex $2^{16}\times.74436163\approx 48782$ */ + z = z - 48782; + } + + y = y + (z / unity); + k = 2; + while (x > fraction_four + 4) { + /*tex + Increase |k| until |x| can be multiplied by a factor of $2^{-k}$, + and adjust $y$ accordingly. Here $z=\lceil x/2^k\rceil$. + */ + z = ((x - 1) / two_to_the[k]) + 1; + while (x < fraction_four + z) { + z = halfp(z + 1); + k = k + 1; + } + y = y + spec_log[k]; + x = x - z; + } + return (y / 8); + } +} + +/*tex + +The following somewhat different subroutine tests rigorously if $ab$ is greater +than, equal to, or less than~$cd$, given integers $(a,b,c,d)$. In most cases a +quick decision is reached. The result is $+1$, 0, or~$-1$ in the three respective +cases. + +*/ + +static int ab_vs_cd(int a, int b, int c, int d) +{ + int q, r; + /*tex Reduce to the case that |a,c>=0| and |b,d>0|. */ + if (a < 0) { + negate(a); + negate(b); + } + if (c < 0) { + negate(c); + negate(d); + } + if (d <= 0) { + if (b >= 0) + return (((a == 0 || b == 0) && (c == 0 || d == 0)) ? 0 : 1); + if (d == 0) + return (a == 0 ? 0 : -1); + q = a; + a = c; + c = q; + q = -b; + b = -d; + d = q; + } else if (b <= 0) { + if (b < 0 && a > 0) + return -1; + return (c == 0 ? 0 : -1); + } + while (1) { + q = a / d; + r = c / b; + if (q != r) + return (q > r ? 1 : -1); + q = a % d; + r = c % b; + if (r == 0) + return (q == 0 ? 0 : 1); + if (q == 0) + return -1; + a = b; + b = q; + c = d; + d = r; + /*tex Now |a>d>0| and |c>b>0|. */ + } +} + +/*tex + +To consume a random integer, the program below will say `|next_random|' and then +it will fetch |randoms[j_random]|. + +*/ + +#define next_random() do { \ + if (j_random==0) \ + new_randoms(); \ + else \ + decr(j_random); \ +} while (0) + +static void new_randoms(void) +{ + /*tex The index into |randoms|. */ + int k; + /*tex The accumulator. */ + int x; + for (k = 0; k <= 23; k++) { + x = randoms[k] - randoms[k + 31]; + if (x < 0) + x = x + fraction_one; + randoms[k] = x; + } + for (k = 24; k <= 54; k++) { + x = randoms[k] - randoms[k - 24]; + if (x < 0) + x = x + fraction_one; + randoms[k] = x; + } + j_random = 54; +} + +/*tex + +To initialize the |randoms| table, we call the following routine. + +*/ + +void init_randoms(int seed) +{ + /*tex Three more or less random integers. */ + int j, jj, k; + /*tex The index into |randoms|. */ + int i; + j = abs(seed); + while (j >= fraction_one) + j = halfp(j); + k = 1; + for (i = 0; i <= 54; i++) { + jj = k; + k = j - k; + j = jj; + if (k < 0) + k = k + fraction_one; + randoms[(i * 21) % 55] = j; + } + /*tex We ``warm up'' the array. */ + new_randoms(); + new_randoms(); + new_randoms(); +} + +/*tex + +To produce a uniform random number in the range |0<=u<x| or |0>=u>x| or |0=u=x|, +given a |scaled| value~|x|, we proceed as shown here. + +Note that the call of |take_frac| will produce the values 0 and~|x| with about +half the probability that it will produce any other particular values between 0 +and~|x|, because it rounds its answers. + +*/ + +int unif_rand(int x) +{ + int y; + next_random(); + y = take_frac(abs(x), randoms[j_random]); + if (y == abs(x)) + return 0; + else if (x > 0) + return y; + else + return -y; +} + +/*tex + +Finally, a normal deviate with mean zero and unit standard deviation can readily +be obtained with the ratio method (Algorithm 3.4.1R in {\sl The Art of Computer +Programming\/}. + +*/ + +int norm_rand(void) +{ + /*tex What the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$. */ + int x, u, l; + do { + do { + next_random(); + x = take_frac(112429, randoms[j_random] - fraction_half); + /*tex Which is $2^{16}\sqrt{8/e}\approx 112428.82793$. */ + next_random(); + u = randoms[j_random]; + } while (abs(x) >= u); + x = make_frac(x, u); + /*tex More fuzzyness: $2^{24}\cdot12\ln2\approx139548959.6165$. */ + l = 139548960 - m_log(u); + } while (ab_vs_cd(1024, l, x, x) < 0); + return x; +} + +/*tex + +This function could also be expressed as a macro, but it is a useful breakpoint +for debugging. + +*/ + +int fix_int(int val, int min, int max) +{ + return (val < min ? min : (val > max ? max : val)); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/arithmetic.w b/Build/source/texk/web2c/luatexdir/tex/arithmetic.w deleted file mode 100644 index 56fb9568acb..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/arithmetic.w +++ /dev/null @@ -1,735 +0,0 @@ -% arithmetic.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -\def\MP{MetaPost} - -@ @c - - -#include "ptexlib.h" - -@ The principal computations performed by \TeX\ are done entirely in terms of -integers less than $2^{31}$ in magnitude; and divisions are done only when both -dividend and divisor are nonnegative. Thus, the arithmetic specified in this -program can be carried out in exactly the same way on a wide variety of -computers, including some small ones. Why? Because the arithmetic -calculations need to be spelled out precisely in order to guarantee that -\TeX\ will produce identical output on different machines. If some -quantities were rounded differently in different implementations, we would -find that line breaks and even page breaks might occur in different places. -Hence the arithmetic of \TeX\ has been designed with care, and systems that -claim to be implementations of \TeX82 should follow precisely the -@:TeX82}{\TeX82@> -calculations as they appear in the present program. - -(Actually there are three places where \TeX\ uses |div| with a possibly negative -numerator. These are harmless; see |div| in the index. Also if the user -sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic -information will involve negative-numerator division. The same remarks -apply for |mod| as well as for |div|.) - -Here is a routine that calculates half of an integer, using an -unambiguous convention with respect to signed odd numbers. - -@c -int half(int x) -{ - if (odd(x)) - return ((x + 1) / 2); - else - return (x / 2); -} - - -@ The following function is used to create a scaled integer from a given decimal -fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is -given in |dig[i]|, and the calculation produces a correctly rounded result. - -@c -scaled round_decimals(int k) -{ /* converts a decimal fraction */ - int a; /* the accumulator */ - a = 0; - while (k-- > 0) { - a = (a + dig[k] * two) / 10; - } - return ((a + 1) / 2); -} - - -@ Conversely, here is a procedure analogous to |print_int|. If the output -of this procedure is subsequently read by \TeX\ and converted by the -|round_decimals| routine above, it turns out that the original value will -be reproduced exactly; the ``simplest'' such decimal number is output, -but there is always at least one digit following the decimal point. - -The invariant relation in the \&{repeat} loop is that a sequence of -decimal digits yet to be printed will yield the original number if and only if -they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$. -We can stop if and only if $f=0$ satisfies this condition; the loop will -terminate before $s$ can possibly become zero. - -@c -void print_scaled(scaled s) -{ /* prints scaled real, rounded to five digits */ - scaled delta; /* amount of allowable inaccuracy */ - char buffer[20]; - int i = 0; - if (s < 0) { - print_char('-'); - negate(s); /* print the sign, if negative */ - } - print_int(s / unity); /* print the integer part */ - buffer[i++] = '.'; - s = 10 * (s % unity) + 5; - delta = 10; - do { - if (delta > unity) - s = s + 0100000 - 50000; /* round the last digit */ - buffer[i++] = '0' + (s / unity); - s = 10 * (s % unity); - delta = delta * 10; - } while (s > delta); - buffer[i++] = '\0'; - tprint(buffer); -} - -@ Physical sizes that a \TeX\ user specifies for portions of documents are -represented internally as scaled points. Thus, if we define an `sp' (scaled -@^sp@> -point) as a unit equal to $2^{-16}$ printer's points, every dimension -inside of \TeX\ is an integer number of sp. There are exactly -4,736,286.72 sp per inch. Users are not allowed to specify dimensions -larger than $2^{30}-1$ sp, which is a distance of about 18.892 feet (5.7583 -meters); two such quantities can be added without overflow on a 32-bit -computer. - -The present implementation of \TeX\ does not check for overflow when -@^overflow in arithmetic@> -dimensions are added or subtracted. This could be done by inserting a -few dozen tests of the form `\ignorespaces|if x>=010000000000 then -@t\\{report\_overflow}@>|', but the chance of overflow is so remote that -such tests do not seem worthwhile. - -\TeX\ needs to do only a few arithmetic operations on scaled quantities, -other than addition and subtraction, and the following subroutines do most of -the work. A single computation might use several subroutine calls, and it is -desirable to avoid producing multiple error messages in case of arithmetic -overflow; so the routines set the global variable |arith_error| to |true| -instead of reporting errors directly to the user. Another global variable, -|tex_remainder|, holds the remainder after a division. - -@c -boolean arith_error; /* has arithmetic overflow occurred recently? */ -scaled tex_remainder; /* amount subtracted to get an exact division */ - - -@ The first arithmetical subroutine we need computes $nx+y$, where |x| -and~|y| are |scaled| and |n| is an integer. We will also use it to -multiply integers. - -@c -scaled mult_and_add(int n, scaled x, scaled y, scaled max_answer) -{ - if (n == 0) - return y; - if (n < 0) { - negate(x); - negate(n); - } - if (((x <= (max_answer - y) / n) && (-x <= (max_answer + y) / n))) { - return (n * x + y); - } else { - arith_error = true; - return 0; - } -} - -@ We also need to divide scaled dimensions by integers. -@c -scaled x_over_n(scaled x, int n) -{ - boolean negative; /* should |tex_remainder| be negated? */ - negative = false; - if (n == 0) { - arith_error = true; - tex_remainder = x; - return 0; - } else { - if (n < 0) { - negate(x); - negate(n); - negative = true; - } - if (x >= 0) { - tex_remainder = x % n; - if (negative) - negate(tex_remainder); - return (x / n); - } else { - tex_remainder = -((-x) % n); - if (negative) - negate(tex_remainder); - return (-((-x) / n)); - } - } -} - - -@ Then comes the multiplication of a scaled number by a fraction |n/d|, -where |n| and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is -positive. It would be too dangerous to multiply by~|n| and then divide -by~|d|, in separate operations, since overflow might well occur; and it -would be too inaccurate to divide by |d| and then multiply by |n|. Hence -this subroutine simulates 1.5-precision arithmetic. - -@c -scaled xn_over_d(scaled x, int n, int d) -{ - nonnegative_integer t, u, v, xx, dd; /* intermediate quantities */ - boolean positive = true; /* was |x>=0|? */ - if (x < 0) { - negate(x); - positive = false; - } - xx = (nonnegative_integer) x; - dd = (nonnegative_integer) d; - t = ((xx % 0100000) * (nonnegative_integer) n); - u = ((xx / 0100000) * (nonnegative_integer) n + (t / 0100000)); - v = (u % dd) * 0100000 + (t % 0100000); - if (u / dd >= 0100000) - arith_error = true; - else - u = 0100000 * (u / dd) + (v / dd); - if (positive) { - tex_remainder = (int) (v % dd); - return (scaled) u; - } else { - /* casts are for ms cl */ - tex_remainder = -(int) (v % dd); - return -(scaled) (u); - } -} - - -@ The next subroutine is used to compute the ``badness'' of glue, when a -total~|t| is supposed to be made from amounts that sum to~|s|. According -to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$; -however, badness is simply a heuristic, so we need not squeeze out the -last drop of accuracy when computing it. All we really want is an -approximation that has similar properties. -@:TeXbook}{\sl The \TeX book@> - -The actual method used to compute the badness is easier to read from the -program than to describe in words. It produces an integer value that is a -reasonably close approximation to $100(t/s)^3$, and all implementations -of \TeX\ should use precisely this method. Any badness of $2^{13}$ or more is -treated as infinitely bad, and represented by 10000. - -It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s) ->=badness(t,s+1)|}.$$ The badness function defined here is capable of -computing at most 1095 distinct values, but that is plenty. - -@c -halfword badness(scaled t, scaled s) -{ /* compute badness, given |t>=0| */ - int r; /* approximation to $\alpha t/s$, where $\alpha^3\approx - 100\cdot2^{18}$ */ - if (t == 0) { - return 0; - } else if (s <= 0) { - return inf_bad; - } else { - if (t <= 7230584) - r = (t * 297) / s; /* $297^3=99.94\times2^{18}$ */ - else if (s >= 1663497) - r = t / (s / 297); - else - r = t; - if (r > 1290) - return inf_bad; /* $1290^3<2^{31}<1291^3$ */ - else - return ((r * r * r + 0400000) / 01000000); - /* that was $r^3/2^{18}$, rounded to the nearest integer */ - } -} - - -@ When \TeX\ ``packages'' a list into a box, it needs to calculate the -proportionality ratio by which the glue inside the box should stretch -or shrink. This calculation does not affect \TeX's decision making, -so the precise details of rounding, etc., in the glue calculation are not -of critical importance for the consistency of results on different computers. - -We shall use the type |glue_ratio| for such proportionality ratios. -A glue ratio should take the same amount of memory as an -|integer| (usually 32 bits) if it is to blend smoothly with \TeX's -other data structures. Thus |glue_ratio| should be equivalent to -|short_real| in some implementations of PASCAL. Alternatively, -it is possible to deal with glue ratios using nothing but fixed-point -arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the -routines cited there must be modified to allow negative glue ratios.) -@^system dependencies@> - - -@ This section is (almost) straight from MetaPost. I had to change -the types (use |integer| instead of |fraction|), but that should -not have any influence on the actual calculations (the original -comments refer to quantities like |fraction_four| ($2^{30}$), and -that is the same as the numeric representation of |max_dimen|). - -I've copied the low-level variables and routines that are needed, but -only those (e.g. |m_log|), not the accompanying ones like |m_exp|. Most -of the following low-level numeric routines are only needed within the -calculation of |norm_rand|. I've been forced to rename |make_fraction| -to |make_frac| because TeX already has a routine by that name with -a wholly different function (it creates a |fraction_noad| for math -typesetting) -- Taco - -And now let's complete our collection of numeric utility routines -by considering random number generation. -\MP{} generates pseudo-random numbers with the additive scheme recommended -in Section 3.6 of {\sl The Art of Computer Programming}; however, the -results are random fractions between 0 and |fraction_one-1|, inclusive. - -There's an auxiliary array |randoms| that contains 55 pseudo-random -fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$, -we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|. -The global variable |j_random| tells which element has most recently -been consumed. - -@c -static int randoms[55]; /* the last 55 random values generated */ -static int j_random; /* the number of unused |randoms| */ -scaled random_seed; /* the default random seed */ - -@ A small bit of metafont is needed. - -@c -#define fraction_half 01000000000 /* $2^{27}$, represents 0.50000000 */ -#define fraction_one 02000000000 /* $2^{28}$, represents 1.00000000 */ -#define fraction_four 010000000000 /* $2^{30}$, represents 4.00000000 */ -#define el_gordo 017777777777 /* $2^{31}-1$, the largest value that \MP\ likes */ - -@ The |make_frac| routine produces the |fraction| equivalent of -|p/q|, given integers |p| and~|q|; it computes the integer -$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are -positive. If |p| and |q| are both of the same scaled type |t|, -the ``type relation'' |make_frac(t,t)=fraction| is valid; -and it's also possible to use the subroutine ``backwards,'' using -the relation |make_frac(t,fraction)=t| between scaled types. - -If the result would have magnitude $2^{31}$ or more, |make_frac| -sets |arith_error:=true|. Most of \MP's internal computations have -been designed to avoid this sort of error. - -If this subroutine were programmed in assembly language on a typical -machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a -double-precision product can often be input to a fixed-point division -instruction. But when we are restricted to PASCAL arithmetic it -is necessary either to resort to multiple-precision maneuvering -or to use a simple but slow iteration. The multiple-precision technique -would be about three times faster than the code adopted here, but it -would be comparatively long and tricky, involving about sixteen -additional multiplications and divisions. - -This operation is part of \MP's ``inner loop''; indeed, it will -consume nearly 10\%! of the running time (exclusive of input and output) -if the code below is left unchanged. A machine-dependent recoding -will therefore make \MP\ run faster. The present implementation -is highly portable, but slow; it avoids multiplication and division -except in the initial stage. System wizards should be careful to -replace it with a routine that is guaranteed to produce identical -results in all cases. -@^system dependencies@> - -As noted below, a few more routines should also be replaced by machine-dependent -code, for efficiency. But when a procedure is not part of the ``inner loop,'' -such changes aren't advisable; simplicity and robustness are -preferable to trickery, unless the cost is too high. - -@c -static int make_frac(int p, int q) -{ - int f; /* the fraction bits, with a leading 1 bit */ - int n; /* the integer part of $\vert p/q\vert$ */ - register int be_careful; /* disables certain compiler optimizations */ - boolean negative = false; /* should the result be negated? */ - if (p < 0) { - negate(p); - negative = true; - } - if (q <= 0) { -#ifdef DEBUG - if (q == 0) - confusion("/"); -#endif - negate(q); - negative = !negative; - } - n = p / q; - p = p % q; - if (n >= 8) { - arith_error = true; - if (negative) - return (-el_gordo); - else - return el_gordo; - } else { - n = (n - 1) * fraction_one; - /* Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$ */ - /* The |repeat| loop here preserves the following invariant relations - between |f|, |p|, and~|q|: - (i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and - $p_0$ is the original value of~$p$. - - Notice that the computation specifies - |(p-q)+p| instead of |(p+p)-q|, because the latter could overflow. - Let us hope that optimizing compilers do not miss this point; a - special variable |be_careful| is used to emphasize the necessary - order of computation. Optimizing compilers should keep |be_careful| - in a register, not store it in memory. - */ - f = 1; - do { - be_careful = p - q; - p = be_careful + p; - if (p >= 0) - f = f + f + 1; - else { - f += f; - p = p + q; - } - } while (f < fraction_one); - be_careful = p - q; - if (be_careful + p >= 0) - incr(f); - - if (negative) - return (-(f + n)); - else - return (f + n); - } -} - -@ @c -static int take_frac(int q, int f) -{ - int p; /* the fraction so far */ - int n; /* additional multiple of $q$ */ - register int be_careful; /* disables certain compiler optimizations */ - boolean negative = false; /* should the result be negated? */ - /* Reduce to the case that |f>=0| and |q>0| */ - if (f < 0) { - negate(f); - negative = true; - } - if (q < 0) { - negate(q); - negative = !negative; - } - - if (f < fraction_one) { - n = 0; - } else { - n = f / fraction_one; - f = f % fraction_one; - if (q <= el_gordo / n) { - n = n * q; - } else { - arith_error = true; - n = el_gordo; - } - } - f = f + fraction_one; - /* Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$ */ - /* The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor - =\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and - $f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$. - */ - p = fraction_half; /* that's $2^{27}$; the invariants hold now with $k=28$ */ - if (q < fraction_four) { - do { - if (odd(f)) - p = halfp(p + q); - else - p = halfp(p); - f = halfp(f); - } while (f != 1); - } else { - do { - if (odd(f)) - p = p + halfp(q - p); - else - p = halfp(p); - f = halfp(f); - } while (f != 1); - } - - be_careful = n - el_gordo; - if (be_careful + p > 0) { - arith_error = true; - n = el_gordo - p; - } - if (negative) - return (-(n + p)); - else - return (n + p); -} - - - -@ The subroutines for logarithm and exponential involve two tables. -The first is simple: |two_to_the[k]| equals $2^k$. The second involves -a bit more calculation, which the author claims to have done correctly: -|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)= -2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the -nearest integer. - -@c -static int two_to_the[31]; /* powers of two */ -static int spec_log[29]; /* special logarithms */ - -@ @c -void initialize_arithmetic(void) -{ - int k; - two_to_the[0] = 1; - for (k = 1; k <= 30; k++) - two_to_the[k] = 2 * two_to_the[k - 1]; - spec_log[1] = 93032640; - spec_log[2] = 38612034; - spec_log[3] = 17922280; - spec_log[4] = 8662214; - spec_log[5] = 4261238; - spec_log[6] = 2113709; - spec_log[7] = 1052693; - spec_log[8] = 525315; - spec_log[9] = 262400; - spec_log[10] = 131136; - spec_log[11] = 65552; - spec_log[12] = 32772; - spec_log[13] = 16385; - for (k = 14; k <= 27; k++) - spec_log[k] = two_to_the[27 - k]; - spec_log[28] = 1; -} - -@ @c -static int m_log(int x) -{ - int y, z; /* auxiliary registers */ - int k; /* iteration counter */ - if (x <= 0) { - /* Handle non-positive logarithm */ - print_err("Logarithm of "); - print_scaled(x); - tprint(" has been replaced by 0"); - help2("Since I don't take logs of non-positive numbers,", - "I'm zeroing this one. Proceed, with fingers crossed."); - error(); - return 0; - } else { - y = 1302456956 + 4 - 100; /* $14\times2^{27}\ln2\approx1302456956.421063$ */ - z = 27595 + 6553600; /* and $2^{16}\times .421063\approx 27595$ */ - while (x < fraction_four) { - x += x; - y = y - 93032639; - z = z - 48782; - } /* $2^{27}\ln2\approx 93032639.74436163$ - and $2^{16}\times.74436163\approx 48782$ */ - y = y + (z / unity); - k = 2; - while (x > fraction_four + 4) { - /* Increase |k| until |x| can be multiplied by a - factor of $2^{-k}$, and adjust $y$ accordingly */ - z = ((x - 1) / two_to_the[k]) + 1; /* $z=\lceil x/2^k\rceil$ */ - while (x < fraction_four + z) { - z = halfp(z + 1); - k = k + 1; - } - y = y + spec_log[k]; - x = x - z; - } - return (y / 8); - } -} - - - -@ The following somewhat different subroutine tests rigorously if $ab$ is -greater than, equal to, or less than~$cd$, -given integers $(a,b,c,d)$. In most cases a quick decision is reached. -The result is $+1$, 0, or~$-1$ in the three respective cases. - -@c -static int ab_vs_cd(int a, int b, int c, int d) -{ - int q, r; /* temporary registers */ - /* Reduce to the case that |a,c>=0|, |b,d>0| */ - if (a < 0) { - negate(a); - negate(b); - } - if (c < 0) { - negate(c); - negate(d); - } - if (d <= 0) { - if (b >= 0) - return (((a == 0 || b == 0) && (c == 0 || d == 0)) ? 0 : 1); - if (d == 0) - return (a == 0 ? 0 : -1); - q = a; - a = c; - c = q; - q = -b; - b = -d; - d = q; - } else if (b <= 0) { - if (b < 0 && a > 0) - return -1; - return (c == 0 ? 0 : -1); - } - - while (1) { - q = a / d; - r = c / b; - if (q != r) - return (q > r ? 1 : -1); - q = a % d; - r = c % b; - if (r == 0) - return (q == 0 ? 0 : 1); - if (q == 0) - return -1; - a = b; - b = q; - c = d; - d = r; /* now |a>d>0| and |c>b>0| */ - } -} - - - -@ To consume a random integer, the program below will say `|next_random|' -and then it will fetch |randoms[j_random]|. - -@c -#define next_random() do { \ - if (j_random==0) new_randoms(); else decr(j_random); \ - } while (0) - -static void new_randoms(void) -{ - int k; /* index into |randoms| */ - int x; /* accumulator */ - for (k = 0; k <= 23; k++) { - x = randoms[k] - randoms[k + 31]; - if (x < 0) - x = x + fraction_one; - randoms[k] = x; - } - for (k = 24; k <= 54; k++) { - x = randoms[k] - randoms[k - 24]; - if (x < 0) - x = x + fraction_one; - randoms[k] = x; - } - j_random = 54; -} - - -@ To initialize the |randoms| table, we call the following routine. - -@c -void init_randoms(int seed) -{ - int j, jj, k; /* more or less random integers */ - int i; /* index into |randoms| */ - j = abs(seed); - while (j >= fraction_one) - j = halfp(j); - k = 1; - for (i = 0; i <= 54; i++) { - jj = k; - k = j - k; - j = jj; - if (k < 0) - k = k + fraction_one; - randoms[(i * 21) % 55] = j; - } - new_randoms(); - new_randoms(); - new_randoms(); /* ``warm up'' the array */ -} - - -@ To produce a uniform random number in the range |0<=u<x| or |0>=u>x| -or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here. - -Note that the call of |take_frac| will produce the values 0 and~|x| -with about half the probability that it will produce any other particular -values between 0 and~|x|, because it rounds its answers. - -@c -int unif_rand(int x) -{ - int y; /* trial value */ - next_random(); - y = take_frac(abs(x), randoms[j_random]); - if (y == abs(x)) - return 0; - else if (x > 0) - return y; - else - return -y; -} - - -@ Finally, a normal deviate with mean zero and unit standard deviation -can readily be obtained with the ratio method (Algorithm 3.4.1R in -{\sl The Art of Computer Programming\/}). - -@c -int norm_rand(void) -{ - int x, u, l; /* what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$ */ - do { - do { - next_random(); - x = take_frac(112429, randoms[j_random] - fraction_half); - /* $2^{16}\sqrt{8/e}\approx 112428.82793$ */ - next_random(); - u = randoms[j_random]; - } while (abs(x) >= u); - x = make_frac(x, u); - l = 139548960 - m_log(u); /* $2^{24}\cdot12\ln2\approx139548959.6165$ */ - } while (ab_vs_cd(1024, l, x, x) < 0); - return x; -} - -@ This function could also be expressed as a macro, but it is a useful - breakpoint for debugging. - -@c -int fix_int(int val, int min, int max) -{ - return (val < min ? min : (val > max ? max : val)); -} diff --git a/Build/source/texk/web2c/luatexdir/tex/backend.c b/Build/source/texk/web2c/luatexdir/tex/backend.c new file mode 100644 index 00000000000..d8c7daed251 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/backend.c @@ -0,0 +1,123 @@ +/* to fill */ + +#include "ptexlib.h" + +scaled max_v = 0; /* maximum height-plus-depth of pages shipped so far */ +scaled max_h = 0; /* maximum width of pages shipped so far */ +boolean doing_leaders = false; /* are we inside a leader box? */ +int cur_s = -1; /* current depth of output box nesting, initially $-1$ */ + +static backend_struct *backend = NULL; +backend_function *backend_out, *backend_out_whatsit, *backend_out_control; + +static void missing_backend_function(PDF pdf, halfword p) +{ + const char *n = get_node_name(type(p), subtype(p)); + if (type(p) == whatsit_node) + formatted_error("pdf backend","no output function for whatsit %s",n); + else + formatted_error("pdf backend","no output function for node %s",n); +} + +static void init_none_backend_functions(void) +{ + backend_struct *p = &backend[OMODE_NONE]; + p->name = strdup("NONE"); +} + +static void init_pdf_backend_functions(void) +{ + backend_struct *p = &backend[OMODE_PDF]; + p->name = strdup("PDF"); + p->node_fu[rule_node] = &pdf_place_rule; + p->node_fu[glyph_node] = &pdf_place_glyph; + p->whatsit_fu[special_node] = &pdf_special; + p->whatsit_fu[pdf_literal_node] = &pdf_out_literal; + p->whatsit_fu[pdf_refobj_node] = &pdf_ref_obj; + p->whatsit_fu[pdf_annot_node] = &do_annot; + p->whatsit_fu[pdf_start_link_node] = &do_link; + p->whatsit_fu[pdf_end_link_node] = &end_link; + p->whatsit_fu[pdf_dest_node] = &do_dest; + p->whatsit_fu[pdf_thread_node] = &do_thread; + p->whatsit_fu[pdf_end_thread_node] = &end_thread; + p->whatsit_fu[late_lua_node] = &late_lua; + p->whatsit_fu[pdf_colorstack_node] = &pdf_out_colorstack; + p->whatsit_fu[pdf_setmatrix_node] = &pdf_out_setmatrix; + p->whatsit_fu[pdf_save_node] = &pdf_out_save; + p->whatsit_fu[pdf_restore_node] = &pdf_out_restore; + p->control_fu[backend_control_push_list] = &pdf_push_list; + p->control_fu[backend_control_pop_list] = &pdf_pop_list; + p->control_fu[backend_control_begin_page] = &pdf_begin_page; + p->control_fu[backend_control_end_page] = &pdf_end_page; + p->control_fu[backend_control_open_file] = &pdf_open_file; + p->control_fu[backend_control_write_header] = &pdf_write_header; + p->control_fu[backend_control_finish_file] = &pdf_finish_file; + p->control_fu[backend_control_set_reference_point] = &pdf_set_reference_point; +} + +static void init_dvi_backend_functions(void) +{ + backend_struct *p = &backend[OMODE_DVI]; + p->name = strdup("DVI"); + p->node_fu[rule_node] = &dvi_place_rule; + p->node_fu[glyph_node] = &dvi_place_glyph; + p->whatsit_fu[special_node] = &dvi_special; + p->whatsit_fu[late_lua_node] = &late_lua; + p->control_fu[backend_control_push_list] = &dvi_push_list; + p->control_fu[backend_control_pop_list] = &dvi_pop_list; + p->control_fu[backend_control_begin_page] = &dvi_begin_page; + p->control_fu[backend_control_end_page] = &dvi_end_page; + p->control_fu[backend_control_open_file] = &dvi_open_file; + p->control_fu[backend_control_write_header] = &dvi_write_header; + p->control_fu[backend_control_finish_file] = &dvi_finish_file; + p->control_fu[backend_control_set_reference_point] = &dvi_set_reference_point; +} + + +void init_backend_functionpointers(output_mode o_mode) +{ + int i, j; + if (backend == NULL) { + backend = xmalloc((MAX_OMODE + 1) * sizeof(backend_struct)); + for (i = 0; i <= MAX_OMODE; i++) { + backend[i].node_fu = xmalloc((MAX_NODE_TYPE + 1) * sizeof(backend_function)); + backend[i].whatsit_fu = xmalloc((MAX_WHATSIT_TYPE + 1) * sizeof(backend_function)); + backend[i].control_fu = xmalloc((MAX_CONTROL_TYPE + 1) * sizeof(backend_function)); + for (j = 0; j < MAX_NODE_TYPE + 1; j++) + backend[i].node_fu[j] = &missing_backend_function; + for (j = 0; j < MAX_WHATSIT_TYPE + 1; j++) + backend[i].whatsit_fu[j] = &missing_backend_function; + for (j = 0; j < MAX_CONTROL_TYPE + 1; j++) + backend[i].control_fu[j] = &missing_backend_function; + } + init_none_backend_functions(); + init_dvi_backend_functions(); + init_pdf_backend_functions(); + } + backend_out = backend[o_mode].node_fu; + backend_out_whatsit = backend[o_mode].whatsit_fu; + backend_out_control = backend[o_mode].control_fu; +} + +output_mode get_o_mode(void) +{ + output_mode o_mode; + if (output_mode_par > 0) { + o_mode = OMODE_PDF; + } else + o_mode = OMODE_DVI; + return o_mode; +} + +void fix_o_mode(void) +{ + output_mode o_mode = get_o_mode(); + if (output_mode_used == OMODE_NONE) { + output_mode_used = o_mode; + /*tex Used by synctex, we need to use output_mode_used there: */ + static_pdf->o_mode = output_mode_used; + } else if (output_mode_used != o_mode) { + normal_error("pdf backend", "\\outputmode can only be changed before anything is written to the output"); + } + init_backend_functionpointers(output_mode_used); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/backend.h b/Build/source/texk/web2c/luatexdir/tex/backend.h new file mode 100644 index 00000000000..5cfbd06fb90 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/backend.h @@ -0,0 +1,54 @@ +/* to fill */ + +#ifndef BACKEND_H +# define BACKEND_H + +#include "ptexlib.h" + +extern scaled max_v; +extern scaled max_h; +extern boolean doing_leaders; +extern int cur_s; + +# define MAX_CONTROL_TYPE 7 + +typedef enum { + backend_control_push_list = 0, + backend_control_pop_list, + backend_control_begin_page, + backend_control_end_page, + backend_control_open_file, + backend_control_write_header, + backend_control_finish_file, + backend_control_set_reference_point +} backend_control_types ; + +typedef void (*backend_function) (); /* variadic arguments */ + +typedef struct { + char *name; /* name of the backend */ + backend_function *node_fu; /* array of node output functions */ + backend_function *whatsit_fu; /* array of whatsit output functions */ + backend_function *control_fu; /* array of whatsit output functions */ +} backend_struct; + +/* extern pos_info_structure pos_info; */ + +extern backend_function *backend_out; +extern backend_function *backend_out_whatsit; +extern backend_function *backend_out_control; + +/* get_o_mode translates from output_mode to output_mode_used */ +/* fix_o_mode freezes output_mode as soon as anything goes through the backend */ + +/* + extern void check_o_mode(PDF pdf, const char *s, int o_mode, boolean errorflag); + extern void ensure_output_file_open(PDF pdf, const char *ext); +*/ + +extern void fix_o_mode(void); +extern output_mode get_o_mode(void); + +extern void init_backend_functionpointers(output_mode o_mode); + +#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/buildpage.c b/Build/source/texk/web2c/luatexdir/tex/buildpage.c new file mode 100644 index 00000000000..81e69dc3b2a --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/buildpage.c @@ -0,0 +1,1164 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + + +#define mode mode_par +#define head head_par +#define tail tail_par + +/*tex + + When \TeX\ appends new material to its main vlist in vertical mode, it uses a + method something like |vsplit| to decide where a page ends, except that the + calculations are done ``on line'' as new items come in. The main complication + in this process is that insertions must be put into their boxes and removed + from the vlist, in a more-or-less optimum manner. + + We shall use the term ``current page'' for that part of the main vlist that + is being considered as a candidate for being broken off and sent to the + user's output routine. The current page starts at |vlink(page_head)|, and it + ends at |page_tail|. We have |page_head=page_tail| if this list is empty. + + Utter chaos would reign if the user kept changing page specifications while a + page is being constructed, so the page builder keeps the pertinent + specifications frozen as soon as the page receives its first box or + insertion. The global variable |page_contents| is |empty| when the current + page contains only mark nodes and content-less whatsit nodes; it is + |inserts_only| if the page contains only insertion nodes in addition to marks + and whatsits. Glue nodes, kern nodes, and penalty nodes are discarded until a + box or rule node appears, at which time |page_contents| changes to + |box_there|. As soon as |page_contents| becomes non-|empty|, the current + |vsize| and |max_depth| are squirreled away into |page_goal| and + |page_max_depth|; the latter values will be used until the page has been + forwarded to the user's output routine. The \.{\\topskip} adjustment is made + when |page_contents| changes to |box_there|. + + Although |page_goal| starts out equal to |vsize|, it is decreased by the + scaled natural height-plus-depth of the insertions considered so far, and by + the \.{\\skip} corrections for those insertions. Therefore it represents the + size into which the non-inserted material should fit, assuming that all + insertions in the current page have been made. + + The global variables |best_page_break| and |least_page_cost| correspond + respectively to the local variables |best_place| and |least_cost| in the + |vert_break| routine that we have already studied; i.e., they record the + location and value of the best place currently known for breaking the current + page. The value of |page_goal| at the time of the best break is stored in + |best_size|. + +*/ + +/*tex the final node on the current page */ + +halfword page_tail; + +/*tex what is on the current page so far? */ + +int page_contents; + +/*tex maximum box depth on page being built */ + +scaled page_max_depth; + +/*tex break here to get the best page known so far */ + +halfword best_page_break; + +/*tex the score for this currently best page */ + +int least_page_cost; + +/*tex its |page_goal| */ + +scaled best_size; + +/*tex + + The page builder has another data structure to keep track of insertions. This + is a list of four-word nodes, starting and ending at |page_ins_head|. That + is, the first element of the list is node |t$_1$=vlink(page_ins_head)|; + node $r_j$ is followed by |t$_{j+1}$=vlink(t$_j$)|; and if there are + |n| items we have |$_{n+1}$>=page_ins_head|. The |subtype| field of each + node in this list refers to an insertion number; for example, `\.{\\insert + 250}' would correspond to a node whose |subtype| is |qi(250)| (the same as + the |subtype| field of the relevant |ins_node|). These |subtype| fields are + in increasing order, and |subtype(page_ins_head)=65535|, so |page_ins_head| + serves as a convenient sentinel at the end of the list. A record is present + for each insertion number that appears in the current page. + + The |type| field in these nodes distinguishes two possibilities that might + occur as we look ahead before deciding on the optimum page break. If + |type(r)=inserting_node|, then |height(r)| contains the total of the + height-plus-depth dimensions of the box and all its inserts seen so far. + |type(r)=split_up_node|, then no more insertions will be made into this box, + because at least one previous insertion was too big to fit on the current + page; |broken_ptr(r)| points to the node where that insertion will be split, + if \TeX\ decides to split it, |broken_ins(r)| points to the insertion node + that was tentatively split, and |height(r)| includes also the natural height + plus depth of the part that would be split off. + + In both cases, |last_ins_ptr(r)| points to the last |ins_node| encountered + for box |qo(subtype(r))| that would be at least partially inserted on the + next page; and |best_ins_ptr(r)| points to the last such |ins_node| that + should actually be inserted, to get the page with minimum badness among all + page breaks considered so far. We have |best_ins_ptr(r)=null| if and only if + no insertion for this box should be made to produce this optimum page. + + Pages are built by appending nodes to the current list in \TeX's vertical + mode, which is at the outermost level of the semantic nest. This vlist is + split into two parts; the ``current page'' that we have been talking so much + about already, and the ``contribution list'' that receives new nodes as they + are created. The current page contains everything that the page builder has + accounted for in its data structures, as described above, while the + contribution list contains other things that have been generated by other + parts of \TeX\ but have not yet been seen by the page builder. The + contribution list starts at |vlink(contrib_head)|, and it ends at the current + node in \TeX's vertical mode. + + When \TeX\ has appended new material in vertical mode, it calls the procedure + |build_page|, which tries to catch up by moving nodes from the contribution + list to the current page. This procedure will succeed in its goal of emptying + the contribution list, unless a page break is discovered, i.e., unless the + current page has grown to the point where the optimum next page break has + been determined. In the latter case, the nodes after the optimum break will + go back onto the contribution list, and control will effectively pass to the + user's output routine. + + We make |type(page_head)=glue_node|, so that an initial glue node on the + current page will not be considered a valid breakpoint. + +*/ + +void initialize_buildpage(void) +{ + subtype(page_ins_head) = 65535; + type(page_ins_head) = split_up_node; + vlink(page_ins_head) = page_ins_head; + + type(page_head) = glue_node; + subtype(page_head) = normal; +} + +/*tex + + An array |page_so_far| records the heights and depths of everything on the + current page. This array contains six |scaled| numbers, like the similar + arrays already considered in |line_break| and |vert_break|; and it also + contains |page_goal| and |page_depth|, since these values are all accessible + to the user via |set_page_dimen| commands. The value of |page_so_far[1]| is + also called |page_total|. The stretch and shrink components of the \.{\\skip} + corrections for each insertion are included in |page_so_far|, but the natural + space components of these corrections are not, since they have been + subtracted from |page_goal|. + + The variable |page_depth| records the depth of the current page; it has been + adjusted so that it is at most |page_max_depth|. The variable |last_glue| + points to the glue specification of the most recent node contributed from the + contribution list, if this was a glue node; otherwise + |last_glue=max_halfword|. (If the contribution list is nonempty, however, the + value of |last_glue| is not necessarily accurate.) The variables + |last_penalty|, |last_kern|, and |last_node_type| are similar. And finally, + |insert_penalties| holds the sum of the penalties associated with all split + and floating insertions. + +*/ + +/*tex height and glue of the current page */ + +scaled page_so_far[8]; + +/*tex used to implement \.{\\lastskip} */ + +halfword last_glue; + +/*tex used to implement \.{\\lastpenalty} */ + +int last_penalty; + +/*tex used to implement \.{\\lastkern} */ + +scaled last_kern; + +/*tex used to implement \.{\\lastnodetype} */ + +int last_node_type; + +/*tex sum of the penalties for held-over insertions */ + +int insert_penalties; + +#define print_plus(A,B) do { \ + if (page_so_far[(A)]!=0) { \ + tprint(" plus "); \ + print_scaled(page_so_far[(A)]); \ + tprint((B)); \ + } \ +} while (0) + +void print_totals(void) +{ + print_scaled(page_total); + print_plus(2, ""); + print_plus(3, "fil"); + print_plus(4, "fill"); + print_plus(5, "filll"); + if (page_shrink != 0) { + tprint(" minus "); + print_scaled(page_shrink); + } +} + +/*tex + + Here is a procedure that is called when the |page_contents| is changing from + |empty| to |inserts_only| or |box_there|. + +*/ + +#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7) +#define set_page_so_far_zero(A) page_so_far[(A)]=0 + +void freeze_page_specs(int s) +{ + page_contents = s; + page_goal = vsize_par; + page_max_depth = max_depth_par; + page_depth = 0; + do_all_six(set_page_so_far_zero); + least_page_cost = awful_bad; + if (tracing_pages_par > 0) { + begin_diagnostic(); + tprint_nl("%% goal height="); + print_scaled(page_goal); + tprint(", max depth="); + print_scaled(page_max_depth); + end_diagnostic(false); + } +} + +/*tex + + The global variable |output_active| is true during the time the user's output + routine is driving \TeX. + +*/ + +/*tex Are we in the midst of an output routine? */ + +boolean output_active; + +/*tex + + The page builder is ready to start a fresh page if we initialize the + following state variables. (However, the page insertion list is initialized + elsewhere.) + +*/ + +void start_new_page(void) +{ + page_contents = empty; + page_tail = page_head; + vlink(page_head) = null; + last_glue = max_halfword; + last_penalty = 0; + last_kern = 0; + last_node_type = -1; + page_depth = 0; + page_max_depth = 0; +} + +/*tex + + At certain times box \.{\\outputbox} is supposed to be void (i.e., |null|), + or an insertion box is supposed to be ready to accept a vertical list. If + not, an error message is printed, and the following subroutine flushes the + unwanted contents, reporting them to the user. + +*/ + +static void box_error(int n) +{ + error(); + begin_diagnostic(); + tprint_nl("The following box has been deleted:"); + show_box(box(n)); + end_diagnostic(true); + flush_node_list(box(n)); + box(n) = null; +} + +/*tex + + The following procedure guarantees that a given box register does not contain + an \.{\\hbox}. + +*/ + +static void ensure_vbox(int n) +{ + halfword p = box(n); + if (p != null && type(p) == hlist_node) { + print_err("Insertions can only be added to a vbox"); + help3( + "Tut tut: You're trying to \\insert into a", + "\\box register that now contains an \\hbox.", + "Proceed, and I'll discard its present contents." + ); + box_error(n); + } +} + +/*tex + + \TeX\ is not always in vertical mode at the time |build_page| is called; the + current mode reflects what \TeX\ should return to, after the contribution + list has been emptied. A call on |build_page| should be immediately followed + by `|goto big_switch|', which is \TeX's central control point. + +*/ + +/*tex Append contributions to the current page. */ + +void build_page(void) +{ + /*tex the node being appended */ + halfword p; + /*tex nodes being examined */ + halfword q, r; + /*tex badness and cost of current page */ + int b, c; + /*tex penalty to be added to the badness */ + int pi = 0; + /*tex insertion box number */ + int n; + /*tex sizes used for insertion calculations */ + scaled delta, h, w; + int id, sk, i; + if ((vlink(contrib_head) == null) || output_active) + return; + do { + CONTINUE: + p = vlink(contrib_head); + /*tex Update the values of |last_glue|, |last_penalty|, and |last_kern|. */ + if (last_glue != max_halfword) { + flush_node(last_glue); + last_glue = max_halfword; + } + last_penalty = 0; + last_kern = 0; + last_node_type = type(p) + 1; + if (type(p) == glue_node) { + last_glue = new_glue(p); + } else if (type(p) == penalty_node) { + last_penalty = penalty(p); + } else if (type(p) == kern_node) { + last_kern = width(p); + } + /*tex + + Move node |p| to the current page; if it is time for a page break, + put the nodes following the break back onto the contribution list, + and |return| to the users output routine if there is one. + + The code here is an example of a many-way switch into routines that + merge together in different places. Some people call this + unstructured programming, but the author doesn't see much wrong with + it, as long as the various labels have a well-understood meaning. + + If the current page is empty and node |p| is to be deleted, |goto + done1|; otherwise use node |p| to update the state of the current + page; if this node is an insertion, |goto contribute|; otherwise if + this node is not a legal breakpoint, |goto contribute| or + |update_heights|; otherwise set |pi| to the penalty associated with + this breakpoint. + + The title of this section is already so long, it seems best to avoid + making it more accurate but still longer, by mentioning the fact that + a kern node at the end of the contribution list will not be + contributed until we know its successor. + + */ + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + if (page_contents < box_there) { + /*tex + + Initialize the current page, insert the \.{\\topskip} + glue ahead of |p|, and |goto continue|. + + */ + if (page_contents == empty) + freeze_page_specs(box_there); + else + page_contents = box_there; + q = new_skip_param(top_skip_code); + if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) { + if (width(q) > depth(p)) + width(q) = width(q) - depth(p); + else + width(q) = 0; + } else { + if (width(q) > height(p)) + width(q) = width(q) - height(p); + else + width(q) = 0; + } + couple_nodes(q, p); + couple_nodes(contrib_head, q); + goto CONTINUE; + } else { + /*tex + + Prepare to move a box or rule node to the current page, + then |goto contribute|. + + */ + if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) { + page_total = page_total + page_depth + depth(p); + page_depth = height(p); + } else { + page_total = page_total + page_depth + height(p); + page_depth = depth(p); + } + goto CONTRIBUTE; + + } + break; + case boundary_node: + case whatsit_node: + goto CONTRIBUTE; + break; + case glue_node: + if (page_contents < box_there) + goto DONE1; + else if (precedes_break(page_tail)) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case kern_node: + if (page_contents < box_there) + goto DONE1; + else if (vlink(p) == null) + goto EXIT; + else if (type(vlink(p)) == glue_node) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case penalty_node: + if (page_contents < box_there) + goto DONE1; + else + pi = penalty(p); + break; + case mark_node: + goto CONTRIBUTE; + break; + case ins_node: + /*tex Append an insertion to the current page and |goto contribute|. */ + if (page_contents == empty) + freeze_page_specs(inserts_only); + n = subtype(p); + r = page_ins_head; + i = 1 ; + while (n >= subtype(vlink(r))) { + r = vlink(r); + i = i + 1 ; + } + if (subtype(r) != n) { + /*tex + + Create a page insertion node with |subtype(r)=qi(n)|, and + include the glue correction for box |n| in the current + page state. + + We take note of the value of \.{\\skip} |n| and the + height plus depth of \.{\\box}~|n| only when the first + \.{\\insert}~|n| node is encountered for a new page. A + user who changes the contents of \.{\\box}~|n| after that + first \.{\\insert}~|n| had better be either extremely + careful or extremely lucky, or both. + + */ + id = callback_defined(build_page_insert_callback); + if (id != 0) { + run_callback(id, "dd->d",n,i,&sk); + } else { + sk = n; + } + q = new_node(inserting_node, n); + try_couple_nodes(q, vlink(r)); + couple_nodes(r, q); + r = q; + ensure_vbox(n); + if (box(n) == null) + height(r) = 0; + else + height(r) = height(box(n)) + depth(box(n)); + best_ins_ptr(r) = null; + q = skip(sk); + if (count(n) == 1000) + h = height(r); + else + h = x_over_n(height(r), 1000) * count(n); + page_goal = page_goal - h - width(q); + if (stretch_order(q) > 1) + page_so_far[1 + stretch_order(q)] = page_so_far[1 + stretch_order(q)] + stretch(q); + else + page_so_far[2 + stretch_order(q)] = page_so_far[2 + stretch_order(q)] + stretch(q); + page_shrink = page_shrink + shrink(q); + if ((shrink_order(q) != normal) && (shrink(q) != 0)) { + print_err("Infinite glue shrinkage inserted from \\skip"); + print_int(n); + help3( + "The correction glue for page breaking with insertions", + "must have finite shrinkability. But you may proceed,", + "since the offensive shrinkability has been made finite." + ); + error(); + } + + } + if (type(r) == split_up_node) { + insert_penalties = insert_penalties + float_cost(p); + } else { + last_ins_ptr(r) = p; + delta = page_goal - page_total - page_depth + page_shrink; + /*tex This much room is left if we shrink the maximum. */ + if (count(n) == 1000) { + h = height(p); + } else { + /*tex This much room is needed. */ + h = x_over_n(height(p), 1000) * count(n); + } + if (((h <= 0) || (h <= delta)) + && (height(p) + height(r) <= dimen(n))) { + page_goal = page_goal - h; + height(r) = height(r) + height(p); + } else { + /*tex + + Find the best way to split the insertion, and change + |type(r)| to |split_up_node|. + + Here is the code that will split a long footnote + between pages, in an emergency. The current situation + deserves to be recapitulated: Node |p| is an + insertion into box |n|; the insertion will not fit, + in its entirety, either because it would make the + total contents of box |n| greater than \.{\\dimen} + |n|, or because it would make the incremental amount + of growth |h| greater than the available space + |delta|, or both. (This amount |h| has been weighted + by the insertion scaling factor, i.e., by \.{\\count} + |n| over 1000.) Now we will choose the best way to + break the vlist of the insertion, using the same + criteria as in the \.{\\vsplit} operation. + + */ + if (count(n) <= 0) { + w = max_dimen; + } else { + w = page_goal - page_total - page_depth; + if (count(n) != 1000) + w = x_over_n(w, count(n)) * 1000; + } + if (w > dimen(n) - height(r)) + w = dimen(n) - height(r); + q = vert_break(ins_ptr(p), w, depth(p)); + height(r) = height(r) + best_height_plus_depth; + if (tracing_pages_par > 0) { + /*tex Display the insertion split cost. */ + begin_diagnostic(); + tprint_nl("% split"); + print_int(n); + tprint(" to "); + print_scaled(w); + print_char(','); + print_scaled(best_height_plus_depth); + tprint(" p="); + if (q == null) + print_int(eject_penalty); + else if (type(q) == penalty_node) + print_int(penalty(q)); + else + print_char('0'); + end_diagnostic(false); + } + if (count(n) != 1000) + best_height_plus_depth = x_over_n(best_height_plus_depth, 1000) * count(n); + page_goal = page_goal - best_height_plus_depth; + type(r) = split_up_node; + broken_ptr(r) = q; + broken_ins(r) = p; + if (q == null) + insert_penalties = insert_penalties + eject_penalty; + else if (type(q) == penalty_node) + insert_penalties = insert_penalties + penalty(q); + } + } + goto CONTRIBUTE; + + break; + default: + formatted_error("pagebuilder","invalid node of type %d in vertical mode", type(p)); + break; + } + /*tex + + Check if node |p| is a new champion breakpoint; then if it is time + for a page break, prepare for output, and either fire up the users + output routine and |return| or ship out the page and |goto done|. + + */ + if (pi < inf_penalty) { + /*tex + + Compute the badness, |b|, of the current page, using |awful_bad| + if the box is too full. + + */ + if (page_total < page_goal) { + if ((page_so_far[3] != 0) || (page_so_far[4] != 0) || + (page_so_far[5] != 0)) + b = 0; + else + b = badness(page_goal - page_total, page_so_far[2]); + } else if (page_total - page_goal > page_shrink) { + b = awful_bad; + } else { + b = badness(page_total - page_goal, page_shrink); + } + if (b < awful_bad) { + if (pi <= eject_penalty) + c = pi; + else if (b < inf_bad) + c = b + pi + insert_penalties; + else + c = deplorable; + } else { + c = b; + } + if (insert_penalties >= 10000) + c = awful_bad; + if (tracing_pages_par > 0) { + /*tex Display the page break cost. */ + begin_diagnostic(); + tprint_nl("%"); + tprint(" t="); + print_totals(); + tprint(" g="); + print_scaled(page_goal); + tprint(" b="); + if (b == awful_bad) + print_char('*'); + else + print_int(b); + tprint(" p="); + print_int(pi); + tprint(" c="); + if (c == awful_bad) + print_char('*'); + else + print_int(c); + if (c <= least_page_cost) + print_char('#'); + end_diagnostic(false); + } + if (c <= least_page_cost) { + best_page_break = p; + best_size = page_goal; + least_page_cost = c; + r = vlink(page_ins_head); + while (r != page_ins_head) { + best_ins_ptr(r) = last_ins_ptr(r); + r = vlink(r); + } + } + if ((c == awful_bad) || (pi <= eject_penalty)) { + /*tex Output the current page at the best place. */ + fire_up(p); + if (output_active) { + /*tex User's output routine will act. */ + goto EXIT; + } + /*tex The page has been shipped out by default output routine. */ + goto DONE; + } + } + if ((type(p) < glue_node) || (type(p) > kern_node)) + goto CONTRIBUTE; + UPDATE_HEIGHTS: + /*tex + + Go here to record glue in the |active_height| table. Update the + current page measurements with respect to the glue or kern specified + by node~|p|. + + */ + if (type(p) != kern_node) { + if (stretch_order(p) > 1) + page_so_far[1 + stretch_order(p)] = page_so_far[1 + stretch_order(p)] + stretch(p); + else + page_so_far[2 + stretch_order(p)] = page_so_far[2 + stretch_order(p)] + stretch(p); + page_shrink = page_shrink + shrink(p); + if ((shrink_order(p) != normal) && (shrink(p) != 0)) { + print_err("Infinite glue shrinkage found on current page"); + help4( + "The page about to be output contains some infinitely", + "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.", + "Such glue doesn't belong there; but you can safely proceed,", + "since the offensive shrinkability has been made finite." + ); + error(); + reset_glue_to_zero(p); + shrink_order(p) = normal; + } + } + page_total = page_total + page_depth + width(p); + page_depth = 0; + CONTRIBUTE: + /*tex + + Go here to link a node into the current page. Make sure that + |page_max_depth| is not exceeded. + + */ + if (page_depth > page_max_depth) { + page_total = page_total + page_depth - page_max_depth; + page_depth = page_max_depth; + } + /*tex Link node |p| into the current page and |goto done|. */ + couple_nodes(page_tail, p); + page_tail = p; + try_couple_nodes(contrib_head,vlink(p)); + vlink(p) = null; + goto DONE; + DONE1: + /*tex Recycle node |p|. */ + try_couple_nodes(contrib_head,vlink(p)); + vlink(p) = null; + if (saving_vdiscards_par > 0) { + if (page_disc == null) { + page_disc = p; + } else { + couple_nodes(tail_page_disc, p); + } + tail_page_disc = p; + } else { + flush_node_list(p); + } + DONE: + ; + } while (vlink(contrib_head) != null); + /*tex Make the contribution list empty by setting its tail to |contrib_head|. */ + contrib_tail = contrib_head; + EXIT: + ; +} + +/*tex + + When the page builder has looked at as much material as could appear before + the next page break, it makes its decision. The break that gave minimum + badness will be used to put a completed ``page'' into box \.{\\outputbox}, + with insertions appended to their other boxes. + + We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The + program uses the fact that |bot_mark(x)<>null| implies |first_mark(x)<>null|; + it also knows that |bot_mark(x)=null| implies + |top_mark(x)=first_mark(x)=null|. + + The |fire_up| subroutine prepares to output the current page at the best + place; then it fires up the user's output routine, if there is one, or it + simply ships out the page. There is one parameter, |c|, which represents the + node that was being contributed to the page when the decision to force an + output was made. + +*/ + +void fire_up(halfword c) +{ + /*tex nodes being examined and/or changed */ + halfword p, q, r, s; + /*tex predecessor of |p| */ + halfword prev_p; + /*tex insertion box number */ + int n; + /*tex should the present insertion be held over? */ + boolean wait; + /*tex saved value of |vbadness| */ + int save_vbadness; + /*tex saved value of |vfuzz| */ + scaled save_vfuzz; + /*tex saved value of |split_top_skip| */ + halfword save_split_top_skip; + /*tex for looping through the marks */ + halfword i; + /*tex Set the value of |output_penalty|. */ + if (type(best_page_break) == penalty_node) { + geq_word_define(int_base + output_penalty_code, + penalty(best_page_break)); + penalty(best_page_break) = inf_penalty; + } else { + geq_word_define(int_base + output_penalty_code, inf_penalty); + } + + for (i = 0; i <= biggest_used_mark; i++) { + if (bot_mark(i) != null) { + if (top_mark(i) != null) + delete_token_ref(top_mark(i)); + set_top_mark(i, bot_mark(i)); + add_token_ref(top_mark(i)); + delete_first_mark(i); + } + } + /*tex + + Put the optimal current page into box |output_box|, update |first_mark| + and |bot_mark|, append insertions to their boxes, and put the remaining + nodes back on the contribution list. + + As the page is finally being prepared for output, pointer |p| runs + through the vlist, with |prev_p| trailing behind; pointer |q| is the tail + of a list of insertions that are being held over for a subsequent page. + + */ + if (c == best_page_break) { + /*tex |c| not yet linked in */ + best_page_break = null; + } + /*tex Ensure that box |output_box| is empty before output. */ + if (box(output_box_par) != null) { + print_err("\\box"); + print_int(output_box_par); + tprint(" is not void"); + help2( + "You shouldn't use \\box\\outputbox except in \\output routines.", + "Proceed, and I'll discard its present contents." + ); + box_error(output_box_par); + } + /*tex This will count the number of insertions held over. */ + insert_penalties = 0; + save_split_top_skip = split_top_skip_par; + if (holding_inserts_par <= 0) { + /*tex + + Prepare all the boxes involved in insertions to act as queues. If + many insertions are supposed to go into the same box, we want to know + the position of the last node in that box, so that we don't need to + waste time when linking further information into it. The + |last_ins_ptr| fields of the page insertion nodes are therefore used + for this purpose during the packaging phase. + + */ + r = vlink(page_ins_head); + while (r != page_ins_head) { + if (best_ins_ptr(r) != null) { + n = subtype(r); + ensure_vbox(n); + if (box(n) == null) + box(n) = new_null_box(); + p = box(n) + list_offset; + while (vlink(p) != null) + p = vlink(p); + last_ins_ptr(r) = p; + } + r = vlink(r); + } + + } + q = hold_head; + vlink(q) = null; + prev_p = page_head; + p = vlink(prev_p); + while (p != best_page_break) { + if (type(p) == ins_node) { + if (holding_inserts_par <= 0) { + /*tex + + Either insert the material specified by node |p| into the + appropriate box, or hold it for the next page; also delete + node |p| from the current page. + + We will set |best_ins_ptr:=null| and package the box + corresponding to insertion node~|r|, just after making the + final insertion into that box. If this final insertion is + `|split_up_node|', the remainder after splitting and pruning + (if any) will be carried over to the next page. + + */ + r = vlink(page_ins_head); + while (subtype(r) != subtype(p)) + r = vlink(r); + if (best_ins_ptr(r) == null) { + wait = true; + } else { + wait = false; + s = last_ins_ptr(r); + vlink(s) = ins_ptr(p); + if (best_ins_ptr(r) == p) { + halfword t; + /*tex + + Wrap up the box specified by node |r|, splitting node + |p| if called for; set |wait:=true| if node |p| holds + a remainder after splitting. + + */ + if (type(r) == split_up_node) { + if ((broken_ins(r) == p) && (broken_ptr(r) != null)) { + while (vlink(s) != broken_ptr(r)) + s = vlink(s); + vlink(s) = null; + split_top_skip_par = split_top_ptr(p); + ins_ptr(p) = + prune_page_top(broken_ptr(r), false); + if (ins_ptr(p) != null) { + t = vpack(ins_ptr(p), 0, additional, -1); + height(p) = height(t) + depth(t); + list_ptr(t) = null; + flush_node(t); + wait = true; + } + } + } + best_ins_ptr(r) = null; + n = subtype(r); + t = list_ptr(box(n)); + list_ptr(box(n)) = null; + flush_node(box(n)); + box(n) = vpack(t, 0, additional, body_direction_par); + + } else { + while (vlink(s) != null) + s = vlink(s); + last_ins_ptr(r) = s; + } + } + /*tex + + Either append the insertion node |p| after node |q|, and + remove it from the current page, or delete |node(p)|. + + */ + try_couple_nodes(prev_p, vlink(p)); + vlink(p) = null; + if (wait) { + couple_nodes(q, p); + q = p; + incr(insert_penalties); + } else { + ins_ptr(p) = null; + flush_node(p); + } + p = prev_p; + + } + } else if (type(p) == mark_node) { + /*tex Update the values of |first_mark| and |bot_mark|. */ + if (first_mark(mark_class(p)) == null) { + set_first_mark(mark_class(p), mark_ptr(p)); + add_token_ref(first_mark(mark_class(p))); + } + if (bot_mark(mark_class(p)) != null) + delete_token_ref(bot_mark(mark_class(p))); + set_bot_mark(mark_class(p), mark_ptr(p)); + add_token_ref(bot_mark(mark_class(p))); + + } + prev_p = p; + p = vlink(prev_p); + } + split_top_skip_par = save_split_top_skip; + /*tex + + Break the current page at node |p|, put it in box~|output_box|, and put + the remaining nodes on the contribution list. + + When the following code is executed, the current page runs from node + |vlink(page_head)| to node |prev_p|, and the nodes from |p| to + |page_tail| are to be placed back at the front of the contribution list. + Furthermore the heldover insertions appear in a list from + |vlink(hold_head)| to |q|; we will put them into the current page list + for safekeeping while the user's output routine is active. We might have + |q=hold_head|; and |p=null| if and only if |prev_p=page_tail|. Error + messages are suppressed within |vpackage|, since the box might appear to + be overfull or underfull simply because the stretch and shrink from the + \.{\\skip} registers for inserts are not actually present in the box. + + */ + if (p != null) { + if (vlink(contrib_head) == null) { + contrib_tail = page_tail; + } + couple_nodes(page_tail,vlink(contrib_head)); + couple_nodes(contrib_head, p); + vlink(prev_p) = null; + } + save_vbadness = vbadness_par; + vbadness_par = inf_bad; + save_vfuzz = vfuzz_par; + /*tex Inhibit error messages. */ + vfuzz_par = max_dimen; + box(output_box_par) = filtered_vpackage(vlink(page_head), + best_size, exactly, page_max_depth, output_group, body_direction_par, 0, 0); + vbadness_par = save_vbadness; + vfuzz_par = save_vfuzz; + if (last_glue != max_halfword) + flush_node(last_glue); + /*tex Start a new current page. This sets |last_glue:=max_halfword|. */ + start_new_page(); + if (q != hold_head) { + vlink(page_head) = vlink(hold_head); + page_tail = q; + } + /*tex Delete the page-insertion nodes. */ + r = vlink(page_ins_head); + while (r != page_ins_head) { + /*tex Todo: couple. */ + q = vlink(r); + flush_node(r); + r = q; + } + vlink(page_ins_head) = page_ins_head; + for (i = 0; i <= biggest_used_mark; i++) { + if ((top_mark(i) != null) && (first_mark(i) == null)) { + set_first_mark(i, top_mark(i)); + add_token_ref(top_mark(i)); + } + } + if (output_routine_par != null) { + if (dead_cycles >= max_dead_cycles_par) { + /*tex Explain that too many dead cycles have occurred in a row. */ + print_err("Output loop---"); + print_int(dead_cycles); + tprint(" consecutive dead cycles"); + help3( + "I've concluded that your \\output is awry; it never does a", + "\\shipout, so I'm shipping \\box\\outputbox out myself. Next time", + "increase \\maxdeadcycles if you want me to be more patient!" + ); + error(); + } else { + /*tex Fire up the users output routine and |return|. */ + output_active = true; + incr(dead_cycles); + push_nest(); + mode = -vmode; + prev_depth_par = ignore_depth; + mode_line_par = -line; + begin_token_list(output_routine_par, output_text); + new_save_level(output_group); + normal_paragraph(); + scan_left_brace(); + return; + } + } + /*tex + + Perform the default output routine. The list of heldover insertions, + running from |vlink(page_head)| to |page_tail|, must be moved to the + contribution list when the user has specified no output routine. + + */ + if (vlink(page_head) != null) { + if (vlink(contrib_head) == null) { + contrib_tail = page_tail; + } else { + vlink(page_tail) = vlink(contrib_head); + } + vlink(contrib_head) = vlink(page_head); + vlink(page_head) = null; + page_tail = page_head; + } + flush_node_list(page_disc); + page_disc = null; + ship_out(static_pdf, box(output_box_par), SHIPPING_PAGE); + box(output_box_par) = null; +} + +/*tex + + When the user's output routine finishes, it has constructed a vlist in + internal vertical mode, and \TeX\ will do the following: + +*/ + +void resume_after_output(void) +{ + if ((iloc != null) || ((token_type != output_text) && (token_type != backed_up))) { + /*tex Recover from an unbalanced output routine */ + print_err("Unbalanced output routine"); + help2( + "Your sneaky output routine has problematic {'s and/or }'s.", + "I can't handle that very well; good luck." + ); + error(); + /*tex Loops forever if reading from a file, since |null=min_halfword<=0|. */ + do { + get_token(); + } while (iloc != null); + } + /*tex Conserve stack space in case more outputs are triggered. */ + end_token_list(); + end_graf(bottom_level); + unsave(); + output_active = false; + insert_penalties = 0; + /*tex Ensure that box |output_box| is empty after output. */ + if (box(output_box_par) != null) { + print_err("Output routine didn't use all of \\box"); + print_int(output_box_par); + help3( + "Your \\output commands should empty \\box\\outputbox,", + "e.g., by saying `\\shipout\\box\\outputbox'.", + "Proceed; I'll discard its present contents." + ); + box_error(output_box_par); + } + if (tail != head) { + /*tex Current list goes after heldover insertions. */ + try_couple_nodes(page_tail, vlink(head)); + page_tail = tail; + } + if (vlink(page_head) != null) { + /* Both go before heldover contributions. */ + if (vlink(contrib_head) == null) + contrib_tail = page_tail; + try_couple_nodes(page_tail, vlink(contrib_head)); + try_couple_nodes(contrib_head, vlink(page_head)); + vlink(page_head) = null; + page_tail = page_head; + } + flush_node_list(page_disc); + page_disc = null; + pop_nest(); + normal_page_filter(after_output); + build_page(); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/buildpage.w b/Build/source/texk/web2c/luatexdir/tex/buildpage.w deleted file mode 100644 index cba07cbf6f6..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/buildpage.w +++ /dev/null @@ -1,1013 +0,0 @@ -% buildpage.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - -#include "ptexlib.h" - -@ @c -#define mode mode_par -#define head head_par -#define tail tail_par - -@ When \TeX\ appends new material to its main vlist in vertical mode, it uses -a method something like |vsplit| to decide where a page ends, except that -the calculations are done ``on line'' as new items come in. -The main complication in this process is that insertions must be put -into their boxes and removed from the vlist, in a more-or-less optimum manner. - -We shall use the term ``current page'' for that part of the main vlist that -is being considered as a candidate for being broken off and sent to the -user's output routine. The current page starts at |vlink(page_head)|, and -it ends at |page_tail|. We have |page_head=page_tail| if this list is empty. -@^current page@> - -Utter chaos would reign if the user kept changing page specifications -while a page is being constructed, so the page builder keeps the pertinent -specifications frozen as soon as the page receives its first box or -insertion. The global variable |page_contents| is |empty| when the -current page contains only mark nodes and content-less whatsit nodes; it -is |inserts_only| if the page contains only insertion nodes in addition to -marks and whatsits. Glue nodes, kern nodes, and penalty nodes are -discarded until a box or rule node appears, at which time |page_contents| -changes to |box_there|. As soon as |page_contents| becomes non-|empty|, -the current |vsize| and |max_depth| are squirreled away into |page_goal| -and |page_max_depth|; the latter values will be used until the page has -been forwarded to the user's output routine. The \.{\\topskip} adjustment -is made when |page_contents| changes to |box_there|. - -Although |page_goal| starts out equal to |vsize|, it is decreased by the -scaled natural height-plus-depth of the insertions considered so far, and by -the \.{\\skip} corrections for those insertions. Therefore it represents -the size into which the non-inserted material should fit, assuming that -all insertions in the current page have been made. - -The global variables |best_page_break| and |least_page_cost| correspond -respectively to the local variables |best_place| and |least_cost| in the -|vert_break| routine that we have already studied; i.e., they record the -location and value of the best place currently known for breaking the -current page. The value of |page_goal| at the time of the best break is -stored in |best_size|. - -@c -halfword page_tail; /* the final node on the current page */ -int page_contents; /* what is on the current page so far? */ -scaled page_max_depth; /* maximum box depth on page being built */ -halfword best_page_break; /* break here to get the best page known so far */ -int least_page_cost; /* the score for this currently best page */ -scaled best_size; /* its |page_goal| */ - -@ The page builder has another data structure to keep track of insertions. -This is a list of four-word nodes, starting and ending at |page_ins_head|. -That is, the first element of the list is node |r@t$_1$@>=vlink(page_ins_head)|; -node $r_j$ is followed by |r@t$_{j+1}$@>=vlink(r@t$_j$@>)|; and if there are -|n| items we have |r@t$_{n+1}$@>=page_ins_head|. The |subtype| field of -each node in this list refers to an insertion number; for example, `\.{\\insert -250}' would correspond to a node whose |subtype| is |qi(250)| -(the same as the |subtype| field of the relevant |ins_node|). These |subtype| -fields are in increasing order, and |subtype(page_ins_head)=65535|, so -|page_ins_head| serves as a convenient sentinel -at the end of the list. A record is present for each insertion number that -appears in the current page. - -The |type| field in these nodes distinguishes two possibilities that -might occur as we look ahead before deciding on the optimum page break. -If |type(r)=inserting_node|, then |height(r)| contains the total of the -height-plus-depth dimensions of the box and all its inserts seen so far. - |type(r)=split_up_node|, then no more insertions will be made into this box, -because at least one previous insertion was too big to fit on the current -page; |broken_ptr(r)| points to the node where that insertion will be -split, if \TeX\ decides to split it, |broken_ins(r)| points to the -insertion node that was tentatively split, and |height(r)| includes also the -natural height plus depth of the part that would be split off. - -In both cases, |last_ins_ptr(r)| points to the last |ins_node| -encountered for box |qo(subtype(r))| that would be at least partially -inserted on the next page; and |best_ins_ptr(r)| points to the last -such |ins_node| that should actually be inserted, to get the page with -minimum badness among all page breaks considered so far. We have -|best_ins_ptr(r)=null| if and only if no insertion for this box should -be made to produce this optimum page. - -@ Pages are built by appending nodes to the current list in \TeX's -vertical mode, which is at the outermost level of the semantic nest. This -vlist is split into two parts; the ``current page'' that we have been -talking so much about already, and the ``contribution list'' that receives -new nodes as they are created. The current page contains everything that -the page builder has accounted for in its data structures, as described -above, while the contribution list contains other things that have been -generated by other parts of \TeX\ but have not yet been -seen by the page builder. -The contribution list starts at |vlink(contrib_head)|, and it ends at the -current node in \TeX's vertical mode. - -When \TeX\ has appended new material in vertical mode, it calls the procedure -|build_page|, which tries to catch up by moving nodes from the contribution -list to the current page. This procedure will succeed in its goal of -emptying the contribution list, unless a page break is discovered, i.e., -unless the current page has grown to the point where the optimum next -page break has been determined. In the latter case, the nodes after the -optimum break will go back onto the contribution list, and control will -effectively pass to the user's output routine. - -We make |type(page_head)=glue_node|, so that an initial glue node on -the current page will not be considered a valid breakpoint. - -@c -void initialize_buildpage(void) -{ - subtype(page_ins_head) = 65535; - type(page_ins_head) = split_up_node; - vlink(page_ins_head) = page_ins_head; - - type(page_head) = glue_node; - subtype(page_head) = normal; -} - - -@ An array |page_so_far| records the heights and depths of everything -on the current page. This array contains six |scaled| numbers, like the -similar arrays already considered in |line_break| and |vert_break|; and it -also contains |page_goal| and |page_depth|, since these values are -all accessible to the user via |set_page_dimen| commands. The -value of |page_so_far[1]| is also called |page_total|. The stretch -and shrink components of the \.{\\skip} corrections for each insertion are -included in |page_so_far|, but the natural space components of these -corrections are not, since they have been subtracted from |page_goal|. - -The variable |page_depth| records the depth of the current page; it has been -adjusted so that it is at most |page_max_depth|. The variable -|last_glue| points to the glue specification of the most recent node -contributed from the contribution list, if this was a glue node; otherwise -|last_glue=max_halfword|. (If the contribution list is nonempty, -however, the value of |last_glue| is not necessarily accurate.) -The variables |last_penalty|, |last_kern|, and |last_node_type| -are similar. And -finally, |insert_penalties| holds the sum of the penalties associated with -all split and floating insertions. - -@c -scaled page_so_far[8]; /* height and glue of the current page */ -halfword last_glue; /* used to implement \.{\\lastskip} */ -int last_penalty; /* used to implement \.{\\lastpenalty} */ -scaled last_kern; /* used to implement \.{\\lastkern} */ -int last_node_type; /* used to implement \.{\\lastnodetype} */ -int insert_penalties; /* sum of the penalties for held-over insertions */ - -#define print_plus(A,B) do { \ - if (page_so_far[(A)]!=0) { \ - tprint(" plus "); \ - print_scaled(page_so_far[(A)]); \ - tprint((B)); \ - } \ -} while (0) - -void print_totals(void) -{ - print_scaled(page_total); - print_plus(2, ""); - print_plus(3, "fil"); - print_plus(4, "fill"); - print_plus(5, "filll"); - if (page_shrink != 0) { - tprint(" minus "); - print_scaled(page_shrink); - } -} - -@ Here is a procedure that is called when the |page_contents| is changing -from |empty| to |inserts_only| or |box_there|. - -@c -#define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7) -#define set_page_so_far_zero(A) page_so_far[(A)]=0 - -void freeze_page_specs(int s) -{ - page_contents = s; - page_goal = vsize_par; - page_max_depth = max_depth_par; - page_depth = 0; - do_all_six(set_page_so_far_zero); - least_page_cost = awful_bad; - if (tracing_pages_par > 0) { - begin_diagnostic(); - tprint_nl("%% goal height="); - print_scaled(page_goal); - tprint(", max depth="); - print_scaled(page_max_depth); - end_diagnostic(false); - } -} - -@ The global variable |output_active| is true during the time the -user's output routine is driving \TeX. - -@c -boolean output_active; /* are we in the midst of an output routine? */ - -@ The page builder is ready to start a fresh page if we initialize -the following state variables. (However, the page insertion list is initialized -elsewhere.) - -@c -void start_new_page(void) -{ - page_contents = empty; - page_tail = page_head; - vlink(page_head) = null; - last_glue = max_halfword; - last_penalty = 0; - last_kern = 0; - last_node_type = -1; - page_depth = 0; - page_max_depth = 0; -} - -@ At certain times box \.{\\outputbox} is supposed to be void (i.e., |null|), -or an insertion box is supposed to be ready to accept a vertical list. -If not, an error message is printed, and the following subroutine -flushes the unwanted contents, reporting them to the user. - -@c -static void box_error(int n) -{ - error(); - begin_diagnostic(); - tprint_nl("The following box has been deleted:"); - show_box(box(n)); - end_diagnostic(true); - flush_node_list(box(n)); - box(n) = null; -} - -@ The following procedure guarantees that a given box register -does not contain an \.{\\hbox}. - -@c -static void ensure_vbox(int n) -{ - halfword p; /* the box register contents */ - p = box(n); - if (p != null && type(p) == hlist_node) { - print_err("Insertions can only be added to a vbox"); - help3("Tut tut: You're trying to \\insert into a", - "\\box register that now contains an \\hbox.", - "Proceed, and I'll discard its present contents."); - box_error(n); - } -} - -@ \TeX\ is not always in vertical mode at the time |build_page| -is called; the current mode reflects what \TeX\ should return to, after -the contribution list has been emptied. A call on |build_page| should -be immediately followed by `|goto big_switch|', which is \TeX's central -control point. - -@c -void build_page(void) -{ /* append contributions to the current page */ - halfword p; /* the node being appended */ - halfword q, r; /* nodes being examined */ - int b, c; /* badness and cost of current page */ - int pi = 0; /* penalty to be added to the badness */ - int n; /* insertion box number */ - scaled delta, h, w; /* sizes used for insertion calculations */ - int id, sk, i; - if ((vlink(contrib_head) == null) || output_active) - return; - do { - CONTINUE: - p = vlink(contrib_head); - /* Update the values of |last_glue|, |last_penalty|, and |last_kern| */ - if (last_glue != max_halfword) { - flush_node(last_glue); - last_glue = max_halfword; - } - last_penalty = 0; - last_kern = 0; - last_node_type = type(p) + 1; - if (type(p) == glue_node) { - last_glue = new_glue(p); - } else if (type(p) == penalty_node) { - last_penalty = penalty(p); - } else if (type(p) == kern_node) { - last_kern = width(p); - } - - /* Move node |p| to the current page; if it is time for a page break, - put the nodes following the break back onto the contribution list, - and |return| to the users output routine if there is one */ - - /* The code here is an example of a many-way switch into routines that - merge together in different places. Some people call this unstructured - programming, but the author doesn't see much wrong with it, as long as - the various labels have a well-understood meaning. - */ - /* If the current page is empty and node |p| is to be deleted, |goto done1|; - otherwise use node |p| to update the state of the current page; - if this node is an insertion, |goto contribute|; otherwise if this node - is not a legal breakpoint, |goto contribute| or |update_heights|; - otherwise set |pi| to the penalty associated with this breakpoint */ - /* The title of this section is already so long, it seems best to avoid - making it more accurate but still longer, by mentioning the fact that a - kern node at the end of the contribution list will not be contributed until - we know its successor. */ - switch (type(p)) { - case hlist_node: - case vlist_node: - case rule_node: - if (page_contents < box_there) { - /* Initialize the current page, insert the \.{\\topskip} glue - ahead of |p|, and |goto continue| */ - if (page_contents == empty) - freeze_page_specs(box_there); - else - page_contents = box_there; - q = new_skip_param(top_skip_code); - if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) { - if (width(q) > depth(p)) - width(q) = width(q) - depth(p); - else - width(q) = 0; - } else { - if (width(q) > height(p)) - width(q) = width(q) - height(p); - else - width(q) = 0; - } - couple_nodes(q, p); - couple_nodes(contrib_head, q); - goto CONTINUE; - - } else { - /* Prepare to move a box or rule node to the current page, - then |goto contribute| */ - if ((type(p) == hlist_node) && is_mirrored(body_direction_par)) { - page_total = page_total + page_depth + depth(p); - page_depth = height(p); - } else { - page_total = page_total + page_depth + height(p); - page_depth = depth(p); - } - goto CONTRIBUTE; - - } - break; - case boundary_node: - case whatsit_node: - goto CONTRIBUTE; - break; - case glue_node: - if (page_contents < box_there) - goto DONE1; - else if (precedes_break(page_tail)) - pi = 0; - else - goto UPDATE_HEIGHTS; - break; - case kern_node: - if (page_contents < box_there) - goto DONE1; - else if (vlink(p) == null) - goto EXIT; - else if (type(vlink(p)) == glue_node) - pi = 0; - else - goto UPDATE_HEIGHTS; - break; - case penalty_node: - if (page_contents < box_there) - goto DONE1; - else - pi = penalty(p); - break; - case mark_node: - goto CONTRIBUTE; - break; - case ins_node: - /* Append an insertion to the current page and |goto contribute| */ - if (page_contents == empty) - freeze_page_specs(inserts_only); - n = subtype(p); - r = page_ins_head; - i = 1 ; - while (n >= subtype(vlink(r))) { - r = vlink(r); - i = i + 1 ; - } - if (subtype(r) != n) { - /* Create a page insertion node with |subtype(r)=qi(n)|, and - include the glue correction for box |n| in the - current page state */ - /* We take note of the value of \.{\\skip} |n| and the height plus depth - of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is - encountered for a new page. A user who changes the contents of \.{\\box}~|n| - after that first \.{\\insert}~|n| had better be either extremely careful - or extremely lucky, or both. */ -id = callback_defined(build_page_insert_callback); -if (id != 0) { - run_callback(id, "dd->d",n,i,&sk); -} else { - sk = n; -} - q = new_node(inserting_node, n); - try_couple_nodes(q, vlink(r)); - couple_nodes(r, q); - r = q; - ensure_vbox(n); - if (box(n) == null) - height(r) = 0; - else - height(r) = height(box(n)) + depth(box(n)); - best_ins_ptr(r) = null; - /* q = skip(n); */ -q = skip(sk); - if (count(n) == 1000) - h = height(r); - else - h = x_over_n(height(r), 1000) * count(n); - page_goal = page_goal - h - width(q); - if (stretch_order(q) > 1) - page_so_far[1 + stretch_order(q)] = page_so_far[1 + stretch_order(q)] + stretch(q); - else - page_so_far[2 + stretch_order(q)] = page_so_far[2 + stretch_order(q)] + stretch(q); - page_shrink = page_shrink + shrink(q); - if ((shrink_order(q) != normal) && (shrink(q) != 0)) { - print_err("Infinite glue shrinkage inserted from \\skip"); - print_int(n); - help3 - ("The correction glue for page breaking with insertions", - "must have finite shrinkability. But you may proceed,", - "since the offensive shrinkability has been made finite."); - error(); - } - - } - if (type(r) == split_up_node) { - insert_penalties = insert_penalties + float_cost(p); - } else { - last_ins_ptr(r) = p; - delta = page_goal - page_total - page_depth + page_shrink; - /* this much room is left if we shrink the maximum */ - if (count(n) == 1000) - h = height(p); - else - h = x_over_n(height(p), 1000) * count(n); /* this much room is needed */ - if (((h <= 0) || (h <= delta)) - && (height(p) + height(r) <= dimen(n))) { - page_goal = page_goal - h; - height(r) = height(r) + height(p); - } else { - /* Find the best way to split the insertion, and change - |type(r)| to |split_up_node| */ - /* Here is the code that will split a long footnote between pages, in an - emergency. The current situation deserves to be recapitulated: Node |p| - is an insertion into box |n|; the insertion will not fit, in its entirety, - either because it would make the total contents of box |n| greater than - \.{\\dimen} |n|, or because it would make the incremental amount of growth - |h| greater than the available space |delta|, or both. (This amount |h| has - been weighted by the insertion scaling factor, i.e., by \.{\\count} |n| - over 1000.) Now we will choose the best way to break the vlist of the - insertion, using the same criteria as in the \.{\\vsplit} operation. - */ - if (count(n) <= 0) { - w = max_dimen; - } else { - w = page_goal - page_total - page_depth; - if (count(n) != 1000) - w = x_over_n(w, count(n)) * 1000; - } - if (w > dimen(n) - height(r)) - w = dimen(n) - height(r); - q = vert_break(ins_ptr(p), w, depth(p)); - height(r) = height(r) + best_height_plus_depth; - if (tracing_pages_par > 0) { - /* Display the insertion split cost */ - begin_diagnostic(); - tprint_nl("% split"); - print_int(n); - tprint(" to "); - print_scaled(w); - print_char(','); - print_scaled(best_height_plus_depth); - tprint(" p="); - if (q == null) - print_int(eject_penalty); - else if (type(q) == penalty_node) - print_int(penalty(q)); - else - print_char('0'); - end_diagnostic(false); - - } - if (count(n) != 1000) - best_height_plus_depth = - x_over_n(best_height_plus_depth, 1000) * count(n); - page_goal = page_goal - best_height_plus_depth; - type(r) = split_up_node; - broken_ptr(r) = q; - broken_ins(r) = p; - if (q == null) - insert_penalties = insert_penalties + eject_penalty; - else if (type(q) == penalty_node) - insert_penalties = insert_penalties + penalty(q); - } - } - goto CONTRIBUTE; - - break; - default: - fprintf(stderr, "type(p)=%d\n", type(p)); - confusion("page"); - break; - } - - /* Check if node |p| is a new champion breakpoint; then if it is time for - a page break, prepare for output, and either fire up the users - output routine and |return| or ship out the page and |goto done| */ - - if (pi < inf_penalty) { - /* Compute the badness, |b|, of the current page, - using |awful_bad| if the box is too full */ - if (page_total < page_goal) { - if ((page_so_far[3] != 0) || (page_so_far[4] != 0) || - (page_so_far[5] != 0)) - b = 0; - else - b = badness(page_goal - page_total, page_so_far[2]); - } else if (page_total - page_goal > page_shrink) { - b = awful_bad; - } else { - b = badness(page_total - page_goal, page_shrink); - } - - if (b < awful_bad) { - if (pi <= eject_penalty) - c = pi; - else if (b < inf_bad) - c = b + pi + insert_penalties; - else - c = deplorable; - } else { - c = b; - } - if (insert_penalties >= 10000) - c = awful_bad; - if (tracing_pages_par > 0) { - /* Display the page break cost */ - begin_diagnostic(); - tprint_nl("%"); - tprint(" t="); - print_totals(); - tprint(" g="); - print_scaled(page_goal); - tprint(" b="); - if (b == awful_bad) - print_char('*'); - else - print_int(b); - tprint(" p="); - print_int(pi); - tprint(" c="); - if (c == awful_bad) - print_char('*'); - else - print_int(c); - if (c <= least_page_cost) - print_char('#'); - end_diagnostic(false); - - } - if (c <= least_page_cost) { - best_page_break = p; - best_size = page_goal; - least_page_cost = c; - r = vlink(page_ins_head); - while (r != page_ins_head) { - best_ins_ptr(r) = last_ins_ptr(r); - r = vlink(r); - } - } - if ((c == awful_bad) || (pi <= eject_penalty)) { - fire_up(p); /* output the current page at the best place */ - if (output_active) - goto EXIT; /* user's output routine will act */ - goto DONE; /* the page has been shipped out by default output routine */ - } - } - - if ((type(p) < glue_node) || (type(p) > kern_node)) - goto CONTRIBUTE; - - UPDATE_HEIGHTS: /* go here to record glue in the |active_height| table */ - - /* Update the current page measurements with respect to the - glue or kern specified by node~|p| */ - if (type(p) != kern_node) { - if (stretch_order(p) > 1) - page_so_far[1 + stretch_order(p)] = page_so_far[1 + stretch_order(p)] + stretch(p); - else - page_so_far[2 + stretch_order(p)] = page_so_far[2 + stretch_order(p)] + stretch(p); - page_shrink = page_shrink + shrink(p); - if ((shrink_order(p) != normal) && (shrink(p) != 0)) { - print_err("Infinite glue shrinkage found on current page"); - help4("The page about to be output contains some infinitely", - "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.", - "Such glue doesn't belong there; but you can safely proceed,", - "since the offensive shrinkability has been made finite."); - error(); - reset_glue_to_zero(p); - shrink_order(p) = normal; - } - } - page_total = page_total + page_depth + width(p); - page_depth = 0; - - CONTRIBUTE: /* go here to link a node into the current page */ - - /* Make sure that |page_max_depth| is not exceeded */ - if (page_depth > page_max_depth) { - page_total = page_total + page_depth - page_max_depth; - page_depth = page_max_depth; - } - - /* Link node |p| into the current page and |goto done| */ - couple_nodes(page_tail, p); - page_tail = p; - try_couple_nodes(contrib_head,vlink(p)); - vlink(p) = null; - goto DONE; - DONE1: - /* Recycle node |p| */ - try_couple_nodes(contrib_head,vlink(p)); - vlink(p) = null; - if (saving_vdiscards_par > 0) { - if (page_disc == null) { - page_disc = p; - } else { - couple_nodes(tail_page_disc, p); - } - tail_page_disc = p; - } else { - flush_node_list(p); - } - DONE: - ; - } while (vlink(contrib_head) != null); - /* Make the contribution list empty by setting its tail to |contrib_head| */ - contrib_tail = contrib_head; - EXIT: - ; -} - -@ When the page builder has looked at as much material as could appear before -the next page break, it makes its decision. The break that gave minimum -badness will be used to put a completed ``page'' into box \.{\\outputbox}, with insertions -appended to their other boxes. - -We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The -program uses the fact that |bot_mark(x)<>null| implies |first_mark(x)<>null|; -it also knows that |bot_mark(x)=null| implies |top_mark(x)=first_mark(x)=null|. - -The |fire_up| subroutine prepares to output the current page at the best -place; then it fires up the user's output routine, if there is one, -or it simply ships out the page. There is one parameter, |c|, which represents -the node that was being contributed to the page when the decision to -force an output was made. - -@c -void fire_up(halfword c) -{ - halfword p, q, r, s; /* nodes being examined and/or changed */ - halfword prev_p; /* predecessor of |p| */ - int n; /* insertion box number */ - boolean wait; /* should the present insertion be held over? */ - int save_vbadness; /* saved value of |vbadness| */ - scaled save_vfuzz; /* saved value of |vfuzz| */ - halfword save_split_top_skip; /* saved value of |split_top_skip| */ - halfword i; /* for looping through the marks */ - - /* Set the value of |output_penalty| */ - if (type(best_page_break) == penalty_node) { - geq_word_define(int_base + output_penalty_code, - penalty(best_page_break)); - penalty(best_page_break) = inf_penalty; - } else { - geq_word_define(int_base + output_penalty_code, inf_penalty); - } - - for (i = 0; i <= biggest_used_mark; i++) { - if (bot_mark(i) != null) { - if (top_mark(i) != null) - delete_token_ref(top_mark(i)); - set_top_mark(i, bot_mark(i)); - add_token_ref(top_mark(i)); - delete_first_mark(i); - } - } - /* Put the optimal current page into box |output_box|, update |first_mark| and - |bot_mark|, append insertions to their boxes, and put the - remaining nodes back on the contribution list; */ - - /* As the page is finally being prepared for output, - pointer |p| runs through the vlist, with |prev_p| trailing behind; - pointer |q| is the tail of a list of insertions that - are being held over for a subsequent page. */ - - if (c == best_page_break) - best_page_break = null; /* |c| not yet linked in */ - /* Ensure that box |output_box| is empty before output */ - if (box(output_box_par) != null) { - print_err("\\box"); - print_int(output_box_par); - tprint(" is not void"); - help2("You shouldn't use \\box\\outputbox except in \\output routines.", - "Proceed, and I'll discard its present contents."); - box_error(output_box_par); - } - - insert_penalties = 0; /* this will count the number of insertions held over */ - save_split_top_skip = split_top_skip_par; - if (holding_inserts_par <= 0) { - /* Prepare all the boxes involved in insertions to act as queues */ - /* If many insertions are supposed to go into the same box, we want to know - the position of the last node in that box, so that we don't need to waste time - when linking further information into it. The |last_ins_ptr| fields of the - page insertion nodes are therefore used for this purpose during the - packaging phase. */ - - r = vlink(page_ins_head); - while (r != page_ins_head) { - if (best_ins_ptr(r) != null) { - n = subtype(r); - ensure_vbox(n); - if (box(n) == null) - box(n) = new_null_box(); - p = box(n) + list_offset; - while (vlink(p) != null) - p = vlink(p); - last_ins_ptr(r) = p; - } - r = vlink(r); - } - - } - q = hold_head; - vlink(q) = null; - prev_p = page_head; - p = vlink(prev_p); - while (p != best_page_break) { - if (type(p) == ins_node) { - if (holding_inserts_par <= 0) { - /* Either insert the material specified by node |p| into the - appropriate box, or hold it for the next page; - also delete node |p| from the current page */ - /* We will set |best_ins_ptr:=null| and package the box corresponding to - insertion node~|r|, just after making the final insertion into that box. - If this final insertion is `|split_up_node|', the remainder after splitting - and pruning (if any) will be carried over to the next page. */ - r = vlink(page_ins_head); - while (subtype(r) != subtype(p)) - r = vlink(r); - if (best_ins_ptr(r) == null) { - wait = true; - } else { - wait = false; - s = last_ins_ptr(r); - vlink(s) = ins_ptr(p); - if (best_ins_ptr(r) == p) { - halfword t; /* was a global temp_ptr */ - /* Wrap up the box specified by node |r|, splitting node |p| if - called for; set |wait:=true| if node |p| holds a remainder after - splitting */ - if (type(r) == split_up_node) { - if ((broken_ins(r) == p) && (broken_ptr(r) != null)) { - while (vlink(s) != broken_ptr(r)) - s = vlink(s); - vlink(s) = null; - split_top_skip_par = split_top_ptr(p); - ins_ptr(p) = - prune_page_top(broken_ptr(r), false); - if (ins_ptr(p) != null) { - t = vpack(ins_ptr(p), 0, additional, -1); - height(p) = height(t) + depth(t); - list_ptr(t) = null; - flush_node(t); - wait = true; - } - } - } - best_ins_ptr(r) = null; - n = subtype(r); - t = list_ptr(box(n)); - list_ptr(box(n)) = null; - flush_node(box(n)); - box(n) = vpack(t, 0, additional, body_direction_par); - - } else { - while (vlink(s) != null) - s = vlink(s); - last_ins_ptr(r) = s; - } - } - /* Either append the insertion node |p| after node |q|, and remove it - from the current page, or delete |node(p)| */ - try_couple_nodes(prev_p, vlink(p)); - vlink(p) = null; - if (wait) { - couple_nodes(q, p); - q = p; - incr(insert_penalties); - } else { - ins_ptr(p) = null; - flush_node(p); - } - p = prev_p; - - } - } else if (type(p) == mark_node) { - /* Update the values of |first_mark| and |bot_mark| */ - if (first_mark(mark_class(p)) == null) { - set_first_mark(mark_class(p), mark_ptr(p)); - add_token_ref(first_mark(mark_class(p))); - } - if (bot_mark(mark_class(p)) != null) - delete_token_ref(bot_mark(mark_class(p))); - set_bot_mark(mark_class(p), mark_ptr(p)); - add_token_ref(bot_mark(mark_class(p))); - - } - prev_p = p; - p = vlink(prev_p); - } - split_top_skip_par = save_split_top_skip; - /* Break the current page at node |p|, put it in box~|output_box|, - and put the remaining nodes on the contribution list */ - /* When the following code is executed, the current page runs from node - |vlink(page_head)| to node |prev_p|, and the nodes from |p| to |page_tail| - are to be placed back at the front of the contribution list. Furthermore - the heldover insertions appear in a list from |vlink(hold_head)| to |q|; we - will put them into the current page list for safekeeping while the user's - output routine is active. We might have |q=hold_head|; and |p=null| if - and only if |prev_p=page_tail|. Error messages are suppressed within - |vpackage|, since the box might appear to be overfull or underfull simply - because the stretch and shrink from the \.{\\skip} registers for inserts - are not actually present in the box. */ - - if (p != null) { - if (vlink(contrib_head) == null) { - contrib_tail = page_tail; - } - couple_nodes(page_tail,vlink(contrib_head)); - couple_nodes(contrib_head, p); - vlink(prev_p) = null; - } - save_vbadness = vbadness_par; - vbadness_par = inf_bad; - save_vfuzz = vfuzz_par; - vfuzz_par = max_dimen; /* inhibit error messages */ - box(output_box_par) = filtered_vpackage(vlink(page_head), - best_size, exactly, page_max_depth, output_group, body_direction_par, 0, 0); - vbadness_par = save_vbadness; - vfuzz_par = save_vfuzz; - if (last_glue != max_halfword) - flush_node(last_glue); - /* Start a new current page */ - start_new_page(); /* this sets |last_glue:=max_halfword| */ - if (q != hold_head) { - vlink(page_head) = vlink(hold_head); - page_tail = q; - } - - /* Delete the page-insertion nodes */ - r = vlink(page_ins_head); - while (r != page_ins_head) { - /* todo: couple */ - q = vlink(r); - flush_node(r); - r = q; - } - vlink(page_ins_head) = page_ins_head; - - for (i = 0; i <= biggest_used_mark; i++) { - if ((top_mark(i) != null) && (first_mark(i) == null)) { - set_first_mark(i, top_mark(i)); - add_token_ref(top_mark(i)); - } - } - if (output_routine_par != null) { - if (dead_cycles >= max_dead_cycles_par) { - /* Explain that too many dead cycles have occurred in a row */ - print_err("Output loop---"); - print_int(dead_cycles); - tprint(" consecutive dead cycles"); - help3("I've concluded that your \\output is awry; it never does a", - "\\shipout, so I'm shipping \\box\\outputbox out myself. Next time", - "increase \\maxdeadcycles if you want me to be more patient!"); - error(); - - } else { - /* Fire up the users output routine and |return| */ - output_active = true; - incr(dead_cycles); - push_nest(); - mode = -vmode; - prev_depth_par = ignore_depth; - mode_line_par = -line; - begin_token_list(output_routine_par, output_text); - new_save_level(output_group); - normal_paragraph(); - scan_left_brace(); - return; - - } - } - /* Perform the default output routine */ - /* The list of heldover insertions, running from |vlink(page_head)| to - |page_tail|, must be moved to the contribution list when the user has - specified no output routine. */ - if (vlink(page_head) != null) { - if (vlink(contrib_head) == null) { - contrib_tail = page_tail; - } else { - vlink(page_tail) = vlink(contrib_head); - } - vlink(contrib_head) = vlink(page_head); - vlink(page_head) = null; - page_tail = page_head; - } - flush_node_list(page_disc); - page_disc = null; - ship_out(static_pdf, box(output_box_par), SHIPPING_PAGE); - box(output_box_par) = null; -} - -@ When the user's output routine finishes, it has constructed a vlist -in internal vertical mode, and \TeX\ will do the following: - -@c -void resume_after_output(void) -{ - if ((iloc != null) - || ((token_type != output_text) && (token_type != backed_up))) { - /* Recover from an unbalanced output routine */ - print_err("Unbalanced output routine"); - help2("Your sneaky output routine has problematic {'s and/or }'s.", - "I can't handle that very well; good luck."); - error(); - do { - get_token(); - } while (iloc != null); - /* loops forever if reading from a file, since |null=min_halfword<=0| */ - - } - end_token_list(); /* conserve stack space in case more outputs are triggered */ - end_graf(bottom_level); - unsave(); - output_active = false; - insert_penalties = 0; - /* Ensure that box |output_box| is empty after output */ - if (box(output_box_par) != null) { - print_err("Output routine didn't use all of \\box"); - print_int(output_box_par); - help3("Your \\output commands should empty \\box\\outputbox,", - "e.g., by saying `\\shipout\\box\\outputbox'.", - "Proceed; I'll discard its present contents."); - box_error(output_box_par); - } - - if (tail != head) { /* current list goes after heldover insertions */ - try_couple_nodes(page_tail, vlink(head)); - page_tail = tail; - } - if (vlink(page_head) != null) { /* and both go before heldover contributions */ - if (vlink(contrib_head) == null) - contrib_tail = page_tail; - try_couple_nodes(page_tail, vlink(contrib_head)); - try_couple_nodes(contrib_head, vlink(page_head)); - vlink(page_head) = null; - page_tail = page_head; - } - flush_node_list(page_disc); - page_disc = null; - pop_nest(); - normal_page_filter(after_output); - build_page(); -} diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.w b/Build/source/texk/web2c/luatexdir/tex/commands.c index 38975eb1699..aceb0eb093d 100644 --- a/Build/source/texk/web2c/luatexdir/tex/commands.w +++ b/Build/source/texk/web2c/luatexdir/tex/commands.c @@ -1,34 +1,36 @@ -% commands.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -\def\eTeX{e-\TeX} - -@ @c +/* +commands.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ The symbolic names for glue parameters are put into \TeX's hash table -by using the routine called |primitive|, defined below. Let us enter them -now, so that we don't have to list all those parameter names anywhere else. +/*tex + +The symbolic names for glue parameters are put into \TeX's hash table by using +the routine called |primitive|, defined below. Let us enter them now, so that we +don't have to list all those parameter names anywhere else. + +*/ -@c void initialize_commands(void) { @@ -53,6 +55,9 @@ void initialize_commands(void) primitive_luatex("mathsurroundskip", assign_glue_cmd, glue_base + math_skip_code, glue_base); primitive_luatex("mathsurroundmode", assign_int_cmd, int_base + math_skip_mode_code, int_base); primitive_luatex("mathscriptboxmode", assign_int_cmd, int_base + math_script_box_mode_code, int_base); + primitive_luatex("mathscriptcharmode", assign_int_cmd, int_base + math_script_char_mode_code, int_base); + primitive_luatex("mathrulethicknessmode", assign_int_cmd, int_base + math_rule_thickness_mode_code, int_base); + primitive_luatex("mathflattenmode", assign_int_cmd, int_base + math_flatten_mode_code, int_base); primitive_tex("output", assign_toks_cmd, output_routine_loc, local_base); primitive_tex("everypar", assign_toks_cmd, every_par_loc, local_base); primitive_tex("everymath", assign_toks_cmd, every_math_loc, local_base); @@ -63,7 +68,7 @@ void initialize_commands(void) primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base); primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base); - /* The integer parameter names must be entered into the hash table */ + /*tex The integer parameter names must be entered into the hash table */ primitive_tex("pretolerance", assign_int_cmd, int_base + pretolerance_code, int_base); primitive_tex("tolerance", assign_int_cmd, int_base + tolerance_code, int_base); @@ -166,11 +171,18 @@ void initialize_commands(void) primitive_luatex("automatichyphenpenalty", assign_int_cmd, int_base + automatic_hyphen_penalty_code, int_base); primitive_luatex("explicithyphenpenalty", assign_int_cmd, int_base + explicit_hyphen_penalty_code, int_base); primitive_luatex("automatichyphenmode", assign_int_cmd, int_base + automatic_hyphen_mode_code, int_base); + primitive_luatex("compoundhyphenmode", assign_int_cmd, int_base + compound_hyphen_mode_code, int_base); primitive_luatex("breakafterdirmode", assign_int_cmd, int_base + break_after_dir_mode_code, int_base); + primitive_luatex("exceptionpenalty", assign_int_cmd, int_base + exception_penalty_code, int_base); + primitive_luatex("fixupboxesmode", assign_int_cmd, int_base + fixup_boxes_code, int_base); + + /*tex + + Many of \TeX's primitives need no |equiv|, since they are identifiable by + their |eq_type| alone. These primitives are loaded into the hash table as + follows: - /* Many of \TeX's primitives need no |equiv|, since they are identifiable - by their |eq_type| alone. These primitives are loaded into the hash table - as follows: */ + */ primitive_tex(" ", ex_space_cmd, 0, 0); primitive_tex("/", ital_corr_cmd, 0, 0); @@ -240,29 +252,27 @@ void initialize_commands(void) primitive_tex("setbox", set_box_cmd, 0, 0); primitive_tex("the", the_cmd, 0, 0); primitive_luatex("toksapp", combine_toks_cmd, 0, 0); - primitive_luatex("tokspre", combine_toks_cmd, 1, 0); - primitive_luatex("etoksapp", combine_toks_cmd, 2, 0); + primitive_luatex("etoksapp", combine_toks_cmd, 1, 0); + primitive_luatex("tokspre", combine_toks_cmd, 2, 0); primitive_luatex("etokspre", combine_toks_cmd, 3, 0); + primitive_luatex("gtoksapp", combine_toks_cmd, 4, 0); + primitive_luatex("xtoksapp", combine_toks_cmd, 5, 0); + primitive_luatex("gtokspre", combine_toks_cmd, 6, 0); + primitive_luatex("xtokspre", combine_toks_cmd, 7, 0); primitive_tex("toks", toks_register_cmd, 0, 0); primitive_tex("vadjust", vadjust_cmd, 0, 0); primitive_tex("valign", valign_cmd, 0, 0); primitive_tex("vcenter", vcenter_cmd, 0, 0); primitive_tex("vrule", vrule_cmd, 0, 0); primitive_luatex("novrule", no_vrule_cmd, 0, 0); - primitive_tex("par", par_end_cmd, too_big_char, too_big_char); /* cf.\ |scan_file_name| */ + primitive_luatex("luafunctioncall", lua_function_call_cmd, 0, 0); + primitive_luatex("luabytecodecall", lua_bytecode_call_cmd, 0, 0); + primitive_luatex("luadef", def_lua_call_cmd, 0, 0); + primitive_tex("par", par_end_cmd, too_big_char, too_big_char); par_loc = cur_val; par_token = cs_token_flag + par_loc; - @<Create a bunch of primitives@>; - @<Create the math param primitives@>; - @<Create another bunch of primitives@>; -} - -@ These are in a separate module due to a CWEAVE limitation. - -@<Create a bunch of primitives@>= - - /* + /*tex The processing of \.{\\input} involves the |start_input| subroutine, which will be declared later; the processing of \.{\\endinput} is trivial. */ @@ -318,7 +328,7 @@ void initialize_commands(void) primitive_tex("number", convert_cmd, number_code, 0); primitive_tex("romannumeral", convert_cmd, roman_numeral_code, 0); primitive_tex("string", convert_cmd, string_code, 0); - primitive_tex("csstring", convert_cmd, cs_string_code, 0); + primitive_luatex("csstring", convert_cmd, cs_string_code, 0); primitive_tex("meaning", convert_cmd, meaning_code, 0); primitive_etex("eTeXVersion", convert_cmd, etex_code, 0); primitive_tex("fontname", convert_cmd, font_name_code, 0); @@ -331,9 +341,12 @@ void initialize_commands(void) primitive_luatex("normaldeviate", convert_cmd, normal_deviate_code, 0); primitive_core("directlua", convert_cmd, lua_code, 0); primitive_luatex("luafunction", convert_cmd, lua_function_code, 0); + primitive_luatex("luabytecode", convert_cmd, lua_bytecode_code, 0); primitive_luatex("luaescapestring", convert_cmd, lua_escape_string_code, 0); primitive_luatex("mathstyle", convert_cmd, math_style_code, 0); primitive_luatex("expanded", convert_cmd, expanded_code, 0); + primitive_luatex("immediateassignment", convert_cmd, immediate_assignment_code, 0); + primitive_luatex("immediateassigned", convert_cmd, immediate_assigned_code, 0); primitive_tex("jobname", convert_cmd, job_name_code, 0); primitive_luatex("formatname", convert_cmd, format_name_code, 0); primitive_luatex("Uchar", convert_cmd, uchar_code, 0); @@ -352,15 +365,15 @@ void initialize_commands(void) primitive_tex("ifmmode", if_test_cmd, if_mmode_code, 0); primitive_tex("ifinner", if_test_cmd, if_inner_code, 0); primitive_tex("ifvoid", if_test_cmd, if_void_code, 0); - primitive_tex("ifhbox", if_test_cmd, if_hbox_code, 0); primitive_tex("ifvbox", if_test_cmd, if_vbox_code, 0); - primitive_tex("ifx", if_test_cmd, ifx_code, 0); + primitive_tex("ifx", if_test_cmd, if_x_code, 0); primitive_tex("ifeof", if_test_cmd, if_eof_code, 0); primitive_tex("iftrue", if_test_cmd, if_true_code, 0); primitive_tex("iffalse", if_test_cmd, if_false_code, 0); primitive_tex("ifcase", if_test_cmd, if_case_code, 0); primitive_luatex("ifprimitive", if_test_cmd, if_primitive_code, 0); + primitive_luatex("ifcondition", if_test_cmd, if_condition_code, 0); primitive_tex("fi", fi_or_else_cmd, fi_code, 0); cs_text(frozen_fi) = maketexstring("fi"); eqtb[frozen_fi] = eqtb[cur_val]; @@ -435,12 +448,13 @@ void initialize_commands(void) primitive_tex("vtop", make_box_cmd, vtop_code, 0); primitive_tex("vbox", make_box_cmd, vtop_code + vmode, 0); primitive_tex("hbox", make_box_cmd, vtop_code + hmode, 0); - primitive_tex("shipout", leader_ship_cmd, a_leaders - 1, 0); /* |ship_out_flag=leader_flag-1| */ + primitive_tex("shipout", leader_ship_cmd, a_leaders - 2, 0); /* |ship_out_flag=leader_flag-2| */ primitive_tex("leaders", leader_ship_cmd, a_leaders, 0); primitive_tex("cleaders", leader_ship_cmd, c_leaders, 0); primitive_tex("xleaders", leader_ship_cmd, x_leaders, 0); primitive_luatex("gleaders", leader_ship_cmd, g_leaders, 0); primitive_luatex("boxdir", assign_box_dir_cmd, 0, 0); + primitive_luatex("boxdirection", assign_box_direction_cmd, 0, 0); primitive_tex("indent", start_par_cmd, 1, 0); primitive_tex("noindent", start_par_cmd, 0, 0); primitive_luatex("quitvmode", start_par_cmd, 2, 0); @@ -514,9 +528,10 @@ void initialize_commands(void) primitive_tex("gdef", def_cmd, 1, 0); primitive_tex("edef", def_cmd, 2, 0); primitive_tex("xdef", def_cmd, 3, 0); - primitive_tex("let", let_cmd, normal, 0); - primitive_tex("futurelet", let_cmd, normal + 1, 0); - primitive_luatex("letcharcode", let_cmd, normal + 2, 0); + primitive_tex("glet", let_cmd, 0, 0); + primitive_tex("let", let_cmd, 1, 0); + primitive_tex("futurelet", let_cmd, 2, 0); + primitive_luatex("letcharcode", let_cmd, 3, 0); primitive_tex("chardef", shorthand_def_cmd, char_def_code, 0); primitive_tex("mathchardef", shorthand_def_cmd, math_char_def_code, 0); primitive_luatex("Umathchardef", shorthand_def_cmd, xmath_char_def_code, 0); @@ -539,9 +554,6 @@ void initialize_commands(void) primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0); primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0); -@ These are in a separate module due to a CWEAVE limitation. - -@<Create the math param primitives@>= primitive_luatex("Umathoperatorsize", set_math_param_cmd, math_param_operator_size, 0); primitive_luatex("Umathoverbarkern", set_math_param_cmd, math_param_overbar_kern, 0); primitive_luatex("Umathoverbarrule", set_math_param_cmd, math_param_overbar_rule, 0); @@ -654,9 +666,6 @@ void initialize_commands(void) primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, math_param_inner_punct_spacing, 0); primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, math_param_inner_inner_spacing, 0); -@ These are in a separate module due to a CWEAVE limitation. - -@<Create another bunch of primitives@>= primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base, math_code_base); primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base, del_code_base); primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 1, math_code_base); @@ -695,6 +704,7 @@ void initialize_commands(void) primitive_tex("write", extension_cmd, write_code, 0); write_loc = cur_val; primitive_tex("closeout", extension_cmd, close_code, 0); + primitive_luatex("endlocalcontrol", extension_cmd, end_local_code, 0); primitive_tex("special", extension_cmd, special_code, 0); cs_text(frozen_special) = maketexstring("special"); eqtb[frozen_special] = eqtb[cur_val]; @@ -720,6 +730,7 @@ void initialize_commands(void) primitive_luatex("initcatcodetable", normal_cmd, init_cat_code_table_code, 0); primitive_luatex("setrandomseed", normal_cmd, set_random_seed_code, 0); primitive_luatex("latelua", normal_cmd, late_lua_code, 0); + primitive_luatex("lateluafunction", normal_cmd, late_lua_call_code, 0); primitive_luatex("insertht", convert_cmd, insert_ht_code, 0); primitive_luatex("dviextension", extension_cmd, dvi_extension_code, 0); primitive_luatex("dvifeedback", feedback_cmd, dvi_feedback_code, 0); @@ -729,6 +740,15 @@ void initialize_commands(void) primitive_luatex("pdfvariable", variable_cmd, pdf_variable_code, 0); primitive_luatex("mathoption", option_cmd, math_option_code, 0); + primitive_luatex("luacopyinputnodes", assign_int_cmd, int_base + copy_lua_input_nodes_code, int_base); + + primitive_luatex("pagedirection", assign_direction_cmd, int_base + page_direction_code, dir_base); + primitive_luatex("bodydirection", assign_direction_cmd, int_base + body_direction_code, dir_base); + primitive_luatex("pardirection", assign_direction_cmd, int_base + par_direction_code, dir_base); + primitive_luatex("textdirection", assign_direction_cmd, int_base + text_direction_code, dir_base); + primitive_luatex("mathdirection", assign_direction_cmd, int_base + math_direction_code, dir_base); + primitive_luatex("linedirection", assign_direction_cmd, int_base + line_direction_code, dir_base); + /* some of the internal integer parameters are not associated with actual primitives at all. @@ -737,8 +757,8 @@ void initialize_commands(void) primitive_no("nolocalwhatsits", assign_int_cmd, int_base + no_local_whatsits_code, int_base); primitive_no("nolocaldirs", assign_int_cmd, int_base + no_local_dirs_code, int_base); +} -@ @c void initialize_etex_commands(void) { primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0); @@ -746,8 +766,10 @@ void initialize_etex_commands(void) primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, 0); primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0); - /* - First we implement the additional \eTeX\ parameters in the table of equivalents. + /*tex + + First we implement the additional \eTeX\ parameters in the table of + equivalents. */ primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base); @@ -795,39 +817,48 @@ void initialize_etex_commands(void) primitive_etex("showgroups", xray_cmd, show_groups, 0); - /* + /*tex + The \.{\\showtokens} command displays a token list. + */ primitive_etex("showtokens", xray_cmd, show_tokens, 0); - /* - The \.{\\unexpanded} primitive prevents expansion of tokens much as - the result from \.{\\the} applied to a token variable. The - \.{\\detokenize} primitive converts a token list into a list of - character tokens much as if the token list were written to a file. We - use the fact that the command modifiers for \.{\\unexpanded} and - \.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe} - are even. + /*tex + + The \.{\\unexpanded} primitive prevents expansion of tokens much as the + result from \.{\\the} applied to a token variable. The \.{\\detokenize} + primitive converts a token list into a list of character tokens much as + if the token list were written to a file. We use the fact that the + command modifiers for \.{\\unexpanded} and \.{\\detokenize} are odd + whereas those for \.{\\the} and \.{\\showthe} are even. + */ primitive_etex("unexpanded", the_cmd, 1, 0); primitive_etex("detokenize", the_cmd, show_tokens, 0); - /* + /*tex + The \.{\\showifs} command displays all currently active conditionals. + */ primitive_etex("showifs", xray_cmd, show_ifs, 0); - /* + /*tex + The \.{\\interactionmode} primitive allows to query and set the interaction mode. + */ primitive_etex("interactionmode", set_page_int_cmd, 2, 0); - /* + /*tex + The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive. + */ primitive_etex("scantokens", input_cmd, 2, 0); @@ -843,7 +874,8 @@ void initialize_etex_commands(void) primitive_luatex("ifabsnum", if_test_cmd, if_abs_num_code, 0); primitive_luatex("ifabsdim", if_test_cmd, if_abs_dim_code, 0); - /* + /*tex + The |protected| feature of \eTeX\ defines the \.{\\protected} prefix command for macro definitions. Such macros are protected against expansions when lists of expanded tokens are built, e.g., for \.{\\edef} @@ -852,8 +884,10 @@ void initialize_etex_commands(void) primitive_etex("protected", prefix_cmd, 8, 0); - /* + /*tex + Here are the additional \eTeX\ primitives for expressions. + */ primitive_etex("numexpr", last_item_cmd, eTeX_expr - int_val_level + int_val_level, 0); @@ -869,21 +903,25 @@ void initialize_etex_commands(void) primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0); primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0); - /* - The \.{\\pagediscards} and \.{\\splitdiscards} commands share the - command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are + /*tex + + The \.{\\pagediscards} and \.{\\splitdiscards} commands share the command + code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are distinguished by their |chr_code| values |last_box_code| and - |vsplit_code|. These |chr_code| values are larger than |box_code| and + |vsplit_code|. These |chr_code| values are larger than |box_code| and |copy_code|. + */ primitive_etex("pagediscards", un_vbox_cmd, last_box_code, 0); primitive_etex("splitdiscards", un_vbox_cmd, vsplit_code, 0); - /* + /*tex + The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties}, and \.{\\displaywidowpenalties} commands allow to define arrays of penalty values to be used instead of the corresponding single values. + */ primitive_etex("interlinepenalties", set_etex_shape_cmd, inter_line_penalties_loc, etex_pen_base); diff --git a/Build/source/texk/web2c/luatexdir/tex/conditional.w b/Build/source/texk/web2c/luatexdir/tex/conditional.c index 318775141a8..948db580340 100644 --- a/Build/source/texk/web2c/luatexdir/tex/conditional.w +++ b/Build/source/texk/web2c/luatexdir/tex/conditional.c @@ -1,67 +1,73 @@ -% conditional.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +conditional.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" +/*tex + @* We consider now the way \TeX\ handles various kinds of \.{\\if} commands. -@ Conditions can be inside conditions, and this nesting has a stack -that is independent of the |save_stack|. - -Four global variables represent the top of the condition stack: -|cond_ptr| points to pushed-down entries, if any; |if_limit| specifies -the largest code of a |fi_or_else| command that is syntactically legal; -|cur_if| is the name of the current type of conditional; and |if_line| -is the line number at which it began. - -If no conditions are currently in progress, the condition stack has the -special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|. -Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and -|link| fields of the first word contain |if_limit|, |cur_if|, and -|cond_ptr| at the next level, and the second word contains the -corresponding |if_line|. - -@c -halfword cond_ptr; /* top of the condition stack */ -int if_limit; /* upper bound on |fi_or_else| codes */ -int cur_if; /* type of conditional being worked on */ -int if_line; /* line where that conditional began */ - -@ When we skip conditional text, we keep track of the line number -where skipping began, for use in error messages. - -@c -int skip_line; /* skipping began here */ - -@ Here is a procedure that ignores text until coming to an \.{\\or}, -\.{\\else}, or \.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$ -nesting. After it has acted, |cur_chr| will indicate the token that -was found, but |cur_tok| will not be set (because this makes the -procedure run faster). - -@c +Conditions can be inside conditions, and this nesting has a stack that is +independent of the |save_stack|. + +Four global variables represent the top of the condition stack: |cond_ptr| points +to pushed-down entries, if any; |if_limit| specifies the largest code of a +|fi_or_else| command that is syntactically legal; |cur_if| is the name of the +current type of conditional; and |if_line| is the line number at which it began. + +If no conditions are currently in progress, the condition stack has the special +state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|. Otherwise +|cond_ptr| points to a two-word node; the |type|, |subtype|, and |link| fields of +the first word contain |if_limit|, |cur_if|, and |cond_ptr| at the next level, +and the second word contains the corresponding |if_line|. + +In |cond_ptr| we keep track of the top of the condition stack while |if_limit| +holds the upper bound on |fi_or_else| codes. The type of conditional being worked +on is stored in cur_if and |if_line| keeps track of the line where that +conditional began. When we skip conditional text, |skip_line| keeps track of the +line number where skipping began, for use in error messages. + +*/ + +halfword cond_ptr; +int if_limit, cur_if, if_line, skip_line; + +/*tex + +Here is a procedure that ignores text until coming to an \.{\\or}, \.{\\else}, or +\.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$ nesting. After it has acted, +|cur_chr| will indicate the token that was found, but |cur_tok| will not be set +(because this makes the procedure run faster). + +With |l| we keep track of the level of $\.{\\if}\ldots\.{\\fi}$ nesting and +|scanner_status| let us return to the entry status. + +*/ + void pass_text(void) { - int l = 0; /* level of $\.{\\if}\ldots\.{\\fi}$ nesting */ - int save_scanner_status = scanner_status; /* |scanner_status| upon entry */ + int l = 0; + int save_scanner_status = scanner_status; scanner_status = skipping; skip_line = line; while (1) { @@ -80,13 +86,16 @@ void pass_text(void) show_cur_cmd_chr(); } -@ When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then -if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if} -condition has been evaluated, \.{\\relax} will be inserted. -For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}' -would otherwise require something after the `\.1'. +/*tex + +When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then if\/ +\.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if} condition +has been evaluated, \.{\\relax} will be inserted. For example, a sequence of +commands like `\.{\\ifvoid1\\else...\\fi}' would otherwise require something +after the `\.1'. + +*/ -@c void push_condition_stack(void) { halfword p = new_node(if_node, 0); @@ -103,9 +112,10 @@ void push_condition_stack(void) void pop_condition_stack(void) { halfword p; - if (if_stack[in_open] == cond_ptr) + if (if_stack[in_open] == cond_ptr) { + /*tex Conditionals are possibly not properly nested with files. */ if_warning(); - /* conditionals possibly not properly nested with files */ + } p = cond_ptr; if_line = if_line_field(p); cur_if = if_limit_subtype(p); @@ -114,14 +124,16 @@ void pop_condition_stack(void) flush_node(p); } -@ Here's a procedure that changes the |if_limit| code corresponding to -a given value of |cond_ptr|. +/*tex + +Here's a procedure that changes the |if_limit| code corresponding to a given +value of |cond_ptr|. + +*/ -@c void change_if_limit(int l, halfword p) { if (p == cond_ptr) { - /* that's the easy case */ if_limit = l; } else { halfword q = cond_ptr; @@ -137,22 +149,29 @@ void change_if_limit(int l, halfword p) } } -@ The conditional \.{\\ifcsname} is equivalent to \.{\\expandafter} -\.{\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new -control sequence will be entered into the hash table (once all tokens -preceding the mandatory \.{\\endcsname} have been expanded). +/*tex + +The conditional \.{\\ifcsname} is equivalent to \.{\\expandafter} +\.{\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new control +sequence will be entered into the hash table (once all tokens preceding the +mandatory \.{\\endcsname} have been expanded). + +*/ -@c static halfword last_tested_cs ; static boolean test_for_cs(void) { - boolean b = false; /*is the condition true? */ - int m, s; /*to be tested against the second operand */ - halfword q; /*for traversing token lists in \.{\\ifx} tests */ + /*tex Is the condition true? */ + boolean b = false; + /*tex To be tested against the second operand: */ + int m, s; + /*tex For traversing token lists in \.{\\ifx} tests: */ + halfword q; halfword n = get_avail(); - halfword p = n; /*head of the list of characters */ -is_in_csname += 1; + /*tex Head of the list of characters: */ + halfword p = n; + is_in_csname += 1; while (1) { get_x_token(); if (cur_cs != 0) @@ -166,13 +185,13 @@ is_in_csname += 1; get_x_token(); } while (cur_cmd != end_cs_name_cmd); flush_list(n); -is_in_csname -= 1; + is_in_csname -= 1; return b; } else { complain_missing_csname(); } } - /* Look up the characters of list |n| in the hash table, and set |cur_cs| */ + /*tex Look up the characters of list |n| in the hash table, and set |cur_cs|. */ m = first; p = token_link(n); while (p != null) { @@ -200,21 +219,26 @@ is_in_csname -= 1; p = token_link(p); } if (m > first) { - cur_cs = id_lookup(first, m - first); /* |no_new_control_sequence| is |true| */ + /*tex |no_new_control_sequence| is |true| */ + cur_cs = id_lookup(first, m - first); } else if (m == first) { - cur_cs = null_cs; /* the list is empty */ + /*tex the list is empty */ + cur_cs = null_cs; } b = (eq_type(cur_cs) != undefined_cs_cmd); flush_list(n); last_cs_name = cur_cs; -is_in_csname -= 1; + is_in_csname -= 1; return b; } -@ An active character will be treated as category 13 following -\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. +/*tex + +An active character will be treated as category 13 following \.{\\if\\noexpand} +or following \.{\\ifcat\\noexpand}. + +*/ -@c #define get_x_token_or_active_char() do { \ get_x_token(); \ if (cur_cmd==relax_cmd && cur_chr==no_expand_flag) { \ @@ -225,36 +249,47 @@ is_in_csname -= 1; } \ } while (0) -@ A condition is started when the |expand| procedure encounters -an |if_test| command; in that case |expand| reduces to |conditional|, -which is a recursive procedure. -@^recursion@> +/*tex + +A condition is started when the |expand| procedure encounters an |if_test| +command; in that case |expand| reduces to |conditional|, which is a recursive +procedure. @^recursion@> + +*/ -@c void conditional(void) { - boolean b = false; /*is the condition true? */ - int r; /*relation to be evaluated */ - int m, n; /*to be tested against the second operand */ - halfword p, q; /*for traversing token lists in \.{\\ifx} tests */ - int save_scanner_status; /*|scanner_status| upon entry */ - halfword save_cond_ptr; /*|cond_ptr| corresponding to this conditional */ - int this_if; /*type of this conditional */ - boolean is_unless; /*was this if preceded by `\.{\\unless}' ? */ - if ((tracing_ifs_par > 0) && (tracing_commands_par <= 1)) + /*tex Is the condition true? */ + boolean b = false; + /*tex The relation to be evaluated: */ + int r; + /*tex To be tested against the second operand: */ + int m, n; + /*tex For traversing token lists in \.{\\ifx} tests: */ + halfword p, q; + /*tex The |scanner_status| upon entry: */ + int save_scanner_status; + /*tex The |cond_ptr| corresponding to this conditional: */ + halfword save_cond_ptr; + /*tex The type of this conditional: */ + int this_if; + /*tex Was this \.{\\if} preceded by \.{\\unless}? */ + boolean is_unless; + if ((tracing_ifs_par > 0) && (tracing_commands_par <= 1)) { show_cur_cmd_chr(); + } push_condition_stack(); save_cond_ptr = cond_ptr; is_unless = (cur_chr >= unless_code); this_if = cur_chr % unless_code; - /* Either process \.{\\ifcase} or set |b| to the value of a boolean condition */ + /*tex Either process \.{\\ifcase} or set |b| to the value of a boolean condition. */ switch (this_if) { case if_char_code: case if_cat_code: - /* Test if two characters match */ + /*tex Test if two characters match. */ get_x_token_or_active_char(); if ((cur_cmd > active_char_cmd) || (cur_chr > biggest_char)) { - /*not a character */ + /*tex It's not a character. */ m = relax_cmd; n = too_big_char; } else { @@ -275,8 +310,10 @@ void conditional(void) case if_dim_code: case if_abs_dim_code: case if_abs_num_code: - /* Test relation between integers or dimensions */ - /* Here we use the fact that |"<"|, |"="|, and |">"| are consecutive ASCII codes. */ + /*tex + Test the relation between integers or dimensions. Here we use the fact + that |<|, |=|, and |>| are consecutive ASCII codes. + */ if (this_if == if_int_code || this_if == if_abs_num_code) scan_int(); else @@ -284,15 +321,10 @@ void conditional(void) n = cur_val; if ((n < 0) && (this_if == if_abs_dim_code || this_if == if_abs_num_code)) negate(n); - /* Get the next non-blank non-call... */ + /*tex Get the next non-blank non-call... */ do { get_x_token(); } while (cur_cmd == spacer_cmd); - /* - if ((cur_tok >= other_token + '<') && (cur_tok <= other_token + '>')) { - r = cur_tok - other_token; - } else { - */ r = cur_tok - other_token; if ((r < '<') || (r > '>')) { print_err("Missing = inserted for "); @@ -318,13 +350,13 @@ void conditional(void) b = (n > cur_val); break; default: - /* can't happen */ + /*tex This can't happen. */ b = false; break; } break; case if_odd_code: - /* Test if an integer is odd */ + /*tex Test if an integer is odd. */ scan_int(); b = odd(cur_val); break; @@ -340,22 +372,6 @@ void conditional(void) case if_inner_code: b = (cur_list.mode_field < 0); break; - /* - case if_void_code: - case if_hbox_code: - case if_vbox_code: - scan_register_num(); - p = box(cur_val); - if (this_if == if_void_code) - b = (p == null); - else if (p == null) - b = false; - else if (this_if == if_hbox_code) - b = (type(p) == hlist_node); - else - b = (type(p) == vlist_node); - break; - */ case if_void_code: scan_register_num(); p = box(cur_val); @@ -371,16 +387,15 @@ void conditional(void) p = box(cur_val); b = (p != null) && (type(p) == vlist_node); break; - case ifx_code: - /* - Test if two tokens match - - Note that `\.{\\ifx}' will declare two macros different if one is \\{long} - or \\{outer} and the other isn't, even though the texts of the macros are - the same. + case if_x_code: + /*tex + Test if two tokens match. Note that `\.{\\ifx}' will declare two + macros different if one is \\{long} or \\{outer} and the other + isn't, even though the texts of the macros are the same. - We need to reset |scanner_status|, since \.{\\outer} control sequences - are allowed, but we might be scanning a macro definition or preamble. + We need to reset |scanner_status|, since \.{\\outer} control + sequences are allowed, but we might be scanning a macro + definition or preamble. */ save_scanner_status = scanner_status; scanner_status = normal; @@ -394,11 +409,10 @@ void conditional(void) } else if (cur_cmd < call_cmd) { b = (cur_chr == q); } else { - /* - Test if two macro texts match - - Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are - different in examples like this: + /*tex + Test if two macro texts match. Note also that `\.{\\ifx}' + decides that macros \.{\\a} and \.{\\b} are different in + examples like this: $$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr {}\\def\\a\{\\c\}& @@ -407,7 +421,7 @@ void conditional(void) {}\\def\\d\{\}\cr}}$$ */ p = token_link(cur_chr); - /*omit reference counts */ + /*tex Omit reference counts. */ q = token_link(equiv(n)); if (p == q) { b = true; @@ -437,9 +451,9 @@ void conditional(void) b = false; break; case if_case_code: - /* Select the appropriate case and |return| or |goto common_ending| */ + /*tex Select the appropriate case and |return| or |goto common_ending|. */ scan_int(); - /* |n| is the number of cases to pass */ + /*tex |n| is the number of cases to pass. */ n = cur_val; if (tracing_commands_par > 1) { begin_diagnostic(); @@ -460,7 +474,7 @@ void conditional(void) } } change_if_limit(or_code, save_cond_ptr); - /*wait for \.{\\or}, \.{\\else}, or \.{\\fi} */ + /*tex Wait for \.{\\or}, \.{\\else}, or \.{\\fi}. */ return; break; case if_primitive_code: @@ -475,10 +489,11 @@ void conditional(void) (cur_chr == get_prim_equiv(m))); break; case if_def_code: - /* - The conditional \.{\\ifdefined} tests if a control sequence is defined. - We need to reset |scanner_status|, since \.{\\outer} control sequences - are allowed, but we might be scanning a macro definition or preamble. + /*tex + The conditional \.{\\ifdefined} tests if a control sequence is + defined. We need to reset |scanner_status|, since \.{\\outer} + control sequences are allowed, but we might be scanning a macro + definition or preamble. */ save_scanner_status = scanner_status; scanner_status = normal; @@ -493,9 +508,9 @@ void conditional(void) b = is_in_csname; break; case if_font_char_code: - /* - The conditional \.{\\iffontchar} tests the existence of a character in - a font. + /*tex + The conditional \.{\\iffontchar} tests the existence of a + character in a font. */ scan_font_ident(); n = cur_val; @@ -503,13 +518,13 @@ void conditional(void) b = char_exists(n, cur_val); break; default: - /* there are no other cases, but for -Wall: */ + /*tex there are no other cases, but we need to please |-Wall|. */ b = false; } if (is_unless) b = !b; if (tracing_commands_par > 1) { - /* Display the value of |b| */ + /*tex Display the value of |b|. */ begin_diagnostic(); if (b) tprint("{true}"); @@ -519,16 +534,15 @@ void conditional(void) } if (b) { change_if_limit(else_code, save_cond_ptr); - /*wait for \.{\\else} or \.{\\fi} */ + /*tex Wait for \.{\\else} or \.{\\fi}. */ return; } - /* - Skip to \.{\\else} or \.{\\fi}, then |goto common_ending| - - In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first + /*tex + Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|. In a + construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first \.{\\else} that we come to after learning that the \.{\\if} is false is - not the \.{\\else} we're looking for. Hence the following curious - logic is needed. + not the \.{\\else} we're looking for. Hence the following curious logic + is needed. */ while (1) { pass_text(); @@ -536,7 +550,9 @@ void conditional(void) if (cur_chr != or_code) goto COMMON_ENDING; print_err("Extra \\or"); - help1("I'm ignoring this; it doesn't match any \\if."); + help1( + "I'm ignoring this; it doesn't match any \\if." + ); error(); } else if (cur_chr == fi_code) { pop_condition_stack(); @@ -546,7 +562,7 @@ void conditional(void) if (cur_chr == fi_code) { pop_condition_stack(); } else { - /*wait for \.{\\fi} */ + /*tex Wait for \.{\\fi}. */ if_limit = fi_code; } } diff --git a/Build/source/texk/web2c/luatexdir/tex/directions.w b/Build/source/texk/web2c/luatexdir/tex/directions.c index c844e934c0d..59b2f7dcd85 100644 --- a/Build/source/texk/web2c/luatexdir/tex/directions.w +++ b/Build/source/texk/web2c/luatexdir/tex/directions.c @@ -1,27 +1,28 @@ -% directions.w -% -% Copyright 2009-2014 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +directions.w + +Copyright 2009-2014 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ @c void scan_direction(void) { int save_cur_cmd = cur_cmd; @@ -53,9 +54,12 @@ void scan_direction(void) cur_chr = save_cur_chr; } -@ the next two are used by postlinebreak.c +/*tex + + The next two are used by |postlinebreak.c|: + +*/ -@c halfword do_push_dir_node(halfword p, halfword a) { halfword n = copy_node(a); @@ -70,18 +74,14 @@ halfword do_pop_dir_node(halfword p) return n; } -@ @c halfword dir_ptr; - halfword text_dir_ptr; -@ There is no need to do anything here at the moment. -@c void initialize_directions(void) { + /*tex There is no need to do anything here at the moment. */ } -@ @c halfword new_dir(int s) { halfword p = new_node(dir_node, 0); @@ -90,55 +90,32 @@ halfword new_dir(int s) return p; } -@ The global static array variable |dir_strings| is also used -by the lua nodelib interface, so it cannot be static. Putting -it here instead of there avoid the nodelib having to know -about the actual values of |dir_TRT| etc. - -@c - -/* -const char *dir_strings[128] = { - "-TLT","???", "???", "???", "-TRT","???", "???", "???", - "???", "-LTL","???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "-RTT","???", "???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "+TLT","???", "???", "???", "+TRT","???", "???", "???", - "???", "+LTL","???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "+RTT","???", "???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???", - "???", "???", "???", "???", "???", "???", "???", "???" +const char *dir_strings_par[4] = { [0] = + "TLT","TRT","LTL","RTT", }; -int dir_swap = 64; -*/ - -const char *dir_strings[8] = { - "-TLT","-TRT","-LTL","-RTT", +const char *dir_strings_text_normal[4] = { [0] = "+TLT","+TRT","+LTL","+RTT", }; -int dir_swap = 4; +const char *dir_strings_text_cancel[4] = { [0] = + "-TLT","-TRT","-LTL","-RTT", +}; -const char *string_dir(int d) +void print_dir_par(int d) { - return (dir_strings[d+dir_swap]+1); + tprint(dir_strings_par[d]); } -@ @c -void print_dir(int d) +void print_dir_text(halfword d) { - tprint(string_dir(d)); + if (subtype(d) == cancel_dir) { + tprint(dir_strings_text_cancel[dir_dir(d)]); + } else { + tprint(dir_strings_text_normal[dir_dir(d)]); + } } -@ @c scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph) { scaled wd = 0; @@ -153,9 +130,7 @@ scaled pack_width(int curdir, int pdir, halfword p, boolean isglyph) } else { wd = glyph_depth(p) + glyph_height(p); } -/* experimental */ -wd += x_advance(p); - } else { /* hlist, vlist, image, form, rule */ + } else { if (textdir_parallel(pdir, curdir)) wd = width(p); else @@ -164,7 +139,6 @@ wd += x_advance(p); return wd; } -@ @c scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isglyph) { scaled_whd whd = { 0, 0, 0 }; @@ -208,7 +182,6 @@ scaled_whd pack_width_height_depth(int curdir, int pdir, halfword p, boolean isg return whd; } -@ @c void update_text_dir_ptr(int val) { if (dir_level(text_dir_ptr) == cur_level) { diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w b/Build/source/texk/web2c/luatexdir/tex/dumpdata.c index 9d06f1c8ed4..0091685fb74 100644 --- a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w +++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.c @@ -1,84 +1,97 @@ -% dumpdata.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +dumpdata.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -/* we start with 907: the sum of the values of the bytes of "don knuth" */ +/*tex + + We use a magic number to register the version of the format. Normally this + number only increments when we add a new primitive of change command codes. + We start with 907 which is the sum of the values of the bytes of \quote + {don knuth}. -#define FORMAT_ID (907+37) +*/ + +#define FORMAT_ID (907+48) #if ((FORMAT_ID>=0) && (FORMAT_ID<=256)) #error Wrong value for FORMAT_ID. #endif +/*tex + +After \.{INITEX} has seen a collection of fonts and macros, it can write all the +necessary information on an auxiliary file so that production versions of \TeX\ +are able to initialize their memory at high speed. The present section of the +program takes care of such output and input. We shall consider simultaneously the +processes of storing and restoring, so that the inverse relation between them is +clear. @.INITEX@> + +The global variable |format_ident| is a string that is printed right after the +|banner| line when \TeX\ is ready to start. For \.{INITEX} this string says +simply `\.{(INITEX)}'; for other versions of \TeX\ it says, for example, +`\.{(preloaded format=plain 1982.11.19)}', showing the year, month, and day that +the format file was created. We have |format_ident=0| before \TeX's tables are +loaded. |FORMAT_ID| is a new field of type int suitable for the identification of +a format: values between 0 and 256 (included) can not be used because in the +previous format they are used for the length of the name of the engine. + +*/ -@ After \.{INITEX} has seen a collection of fonts and macros, it -can write all the necessary information on an auxiliary file so -that production versions of \TeX\ are able to initialize their -memory at high speed. The present section of the program takes -care of such output and input. We shall consider simultaneously -the processes of storing and restoring, -so that the inverse relation between them is clear. -@.INITEX@> - -The global variable |format_ident| is a string that is printed right -after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this -string says simply `\.{(INITEX)}'; for other versions of \TeX\ it says, -for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year, -month, and day that the format file was created. We have |format_ident=0| -before \TeX's tables are loaded. |FORMAT_ID| is a new field of type int -suitable for the identification of a format: values between 0 and 256 -(included) can not be used because in the previous format they are used -for the length of the name of the engine. -@c str_number format_ident; -str_number format_name; /* principal file name */ +str_number format_name; -@ Format files consist of |memory_word| items, and we use the following -macros to dump words of different types: +/*tex -@c -FILE *fmt_file; /* for input or output of format information */ +Format files consist of |memory_word| items, and we use the following macros to +dump words of different types: + +*/ + +FILE *fmt_file; -@ @c void store_fmt_file(void) { - int j, k, l; /* all-purpose indices */ - halfword p; /* all-purpose pointer */ - int x; /* something to dump */ + int j, k, l, x; + halfword p; char *format_engine; - int callback_id; /* |pre_dump| callback */ + int callback_id; char *fmtname = NULL; - /* If dumping is not allowed, abort */ - /* The user is not allowed to dump a format file unless |save_ptr=0|. - This condition implies that |cur_level=level_one|, hence - the |xeq_level| array is constant and it need not be dumped. */ + /*tex + If dumping is not allowed, abort. The user is not allowed to dump a + format file unless |save_ptr=0|. This condition implies that + |cur_level=level_one|, hence the |xeq_level| array is constant and it + need not be dumped. + */ if (save_ptr != 0) { print_err("You can't dump inside a group"); help1("`{...\\dump}' is a no-no."); succumb(); } - - /* Create the |format_ident|, open the format file, and inform the user - that dumping has begun */ + /*tex + Create the |format_ident|, open the format file, and inform the user that + dumping has begun. + */ callback_id = callback_defined(pre_dump_callback); if (callback_id > 0) { (void) run_callback(callback_id, "->"); @@ -97,11 +110,11 @@ void store_fmt_file(void) format_ident = make_string(); print(job_name); format_name = make_string(); - if (interaction == batch_mode) + if (interaction == batch_mode) { selector = log_only; - else + } else { selector = term_and_log; - + } fmtname = pack_job_name(format_extension); while (!zopen_w_output(&fmt_file, fmtname, FOPEN_WBIN_MODE)) { fmtname = prompt_file_name("format file name", format_extension); @@ -111,15 +124,16 @@ void store_fmt_file(void) free(fmtname); tprint_nl(""); print(format_ident); - - /* Dump constants for consistency check */ - /* The next few sections of the program should make it clear how we use the - dump/undump macros. */ - - dump_int(0x57325458); /* Web2C \TeX's magic constant: "W2TX" */ + /*tex + Dump constants for consistency check. The next few sections of the + program should make it clear how we use the dump/undump macros. First + comes Web2C \TeX's magic constant: "W2TX" + */ + dump_int(0x57325458); dump_int(FORMAT_ID); - - /* Align engine to 4 bytes with one or more trailing NUL */ + /*tex + We align |engine_name| to 4 bytes with one or more trailing |NUL|. + */ x = (int) strlen(engine_name); format_engine = xmalloc((unsigned) (x + 4)); strcpy(format_engine, engine_name); @@ -129,28 +143,26 @@ void store_fmt_file(void) dump_int(x); dump_things(format_engine[0], x); xfree(format_engine); - dump_int(0x57325458); /* TODO HM, what checksum would make sense? */ + dump_int(0x57325458); dump_int(max_halfword); dump_int(hash_high); dump_int(eqtb_size); dump_int(hash_prime); - - /* Dump the string pool */ + /*tex Dump the string pool. */ k = dump_string_pool(); print_ln(); print_int(k); tprint(" strings using "); print_int((longinteger) pool_size); tprint(" bytes"); - - /* Dump the dynamic memory */ - /* By sorting the list of available spaces in the variable-size portion of - |mem|, we are usually able to get by without having to dump very much - of the dynamic memory. - - We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid - information even when it has not been gathering statistics. - */ + /*tex + Dump the dynamic memory. By sorting the list of available spaces in the + variable-size portion of |mem|, we are usually able to get by without + having to dump very much of the dynamic memory. + + We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid + information even when it has not been gathering statistics. + */ dump_node_mem(); dump_int(temp_token_head); dump_int(hold_token_head); @@ -180,14 +192,13 @@ void store_fmt_file(void) print_int(var_used); print_char('&'); print_int(dyn_used); - - /* Dump the table of equivalents */ - /* Dump regions 1 to 4 of |eqtb| */ - /*The table of equivalents usually contains repeated information, so we dump it - in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the - format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra - copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. - */ + /*tex + Dump regions 1 to 4 of |eqtb|, the table of equivalents. The table of + equivalents usually contains repeated information, so we dump it in + compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in + the format file represents $n+m$ consecutive entries of |eqtb|, with |m| + extra copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. + */ k = null_cs; do { j = k; @@ -198,7 +209,8 @@ void store_fmt_file(void) incr(j); } l = int_base; - goto DONE1; /* |j=int_base-1| */ + /*tex |j=int_base-1| */ + goto DONE1; FOUND1: incr(j); l = j; @@ -214,8 +226,7 @@ void store_fmt_file(void) k = j + 1; dump_int(k - l); } while (k != int_base); - - /* Dump regions 5 and 6 of |eqtb| */ + /*tex Dump regions 5 and 6 of |eqtb|. */ do { j = k; while (j < eqtb_size) { @@ -224,7 +235,8 @@ void store_fmt_file(void) incr(j); } l = eqtb_size + 1; - goto DONE2; /* |j=eqtb_size| */ + /*tex |j=eqtb_size| */ + goto DONE2; FOUND2: incr(j); l = j; @@ -239,18 +251,20 @@ void store_fmt_file(void) k = j + 1; dump_int(k - l); } while (k <= eqtb_size); - if (hash_high > 0) - dump_things(eqtb[eqtb_size + 1], hash_high); /* dump |hash_extra| part */ - + if (hash_high > 0) { + /*tex Dump the |hash_extra| part: */ + dump_things(eqtb[eqtb_size + 1], hash_high); + } dump_int(par_loc); dump_int(write_loc); dump_math_codes(); dump_text_codes(); - /* Dump the hash table */ - /* A different scheme is used to compress the hash table, since its lower - region is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output - two words, |p| and |hash[p]|. The hash table is, of course, densely packed - for |p>=hash_used|, so the remaining entries are output in a~block. + /*tex + Dump the hash table, A different scheme is used to compress the hash + table, since its lower region is usually sparse. When |text(p)<>0| for + |p<=hash_used|, we output two words, |p| and |hash[p]|. The hash table + is, of course, densely packed for |p>=hash_used|, so the remaining + entries are output in a~block. */ dump_primitives(); dump_int(hash_used); @@ -262,19 +276,18 @@ void store_fmt_file(void) incr(cs_count); } } - dump_things(hash[hash_used + 1], - undefined_control_sequence - 1 - hash_used); - if (hash_high > 0) + dump_things(hash[hash_used + 1],undefined_control_sequence - 1 - hash_used); + if (hash_high > 0) { dump_things(hash[eqtb_size + 1], hash_high); + } dump_int(cs_count); print_ln(); print_int(cs_count); tprint(" multiletter control sequences"); - - /* Dump the font information */ + /*tex Dump the font information. */ dump_int(max_font_id()); for (k = 0; k <= max_font_id(); k++) { - /* Dump the array info for internal font number |k| */ + /*tex Dump the array info for internal font number |k|. */ dump_font(k); tprint_nl("\\font"); print_esc(font_id_text(k)); @@ -293,73 +306,78 @@ void store_fmt_file(void) if (max_font_id() != 1) print_char('s'); dump_math_data(); - - /* Dump the hyphenation tables */ + /*tex Dump the hyphenation tables. */ dump_language_data(); - - /* Dump a couple more things and the closing check word */ + /*tex Dump a couple more things and the closing check word. */ dump_int(interaction); dump_int(format_ident); dump_int(format_name); dump_int(69069); - /* We have already printed a lot of statistics, so we set |tracing_stats:=0| - to prevent them from appearing again. */ + /*tex + We have already printed a lot of statistics, so we set |tracing_stats:=0| + to prevent them from appearing again. + */ tracing_stats_par = 0; - - /* Dump the lua bytecodes */ + /*tex Dump the \LUA\ bytecodes. */ dump_luac_registers(); - - /* Close the format file */ + /*tex Close the format file. */ zwclose(fmt_file); } -@ Corresponding to the procedure that dumps a format file, we have a function -that reads one in. The function returns |false| if the dumped format is -incompatible with the present \TeX\ table sizes, etc. - -@c -#define too_small(A) do { \ - wake_up_terminal(); \ - wterm_cr(); \ - fprintf(term_out,"---! Must increase the %s",(A)); \ - goto BAD_FMT; \ - } while (0) - -@ The inverse macros are slightly more complicated, since we need to check -the range of the values we are reading in. We say `|undump(a)(b)(x)|' to -read an integer value |x| that is supposed to be in the range |a<=x<=b|. - -@c -#define undump(A,B,C) do { \ - undump_int(x); \ - if (x<(A) || x>(B)) goto BAD_FMT; \ - else (C) = x; \ - } while (0) - - -#define format_debug(A,B) do { \ - if (debug_format_file) { \ - fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \ - } \ - } while (0) - -#define undump_size(A,B,C,D) do { \ - undump_int(x); \ - if (x<(A)) goto BAD_FMT; \ - if (x>(B)) too_small(C); \ - else format_debug (C,x); \ - (D) = x; \ - } while (0) - - -@ @c +/*tex + +Corresponding to the procedure that dumps a format file, we have a function that +reads one in. The function returns |false| if the dumped format is incompatible +with the present \TeX\ table sizes, etc. + +*/ + +#define too_small(A) do { \ + wake_up_terminal(); \ + wterm_cr(); \ + fprintf(term_out,"---! Must increase the %s",(A)); \ + goto BAD_FMT; \ +} while (0) + +/*tex + + The inverse macros are slightly more complicated, since we need to check the + range of the values we are reading in. We say `|undump(a)(b)(x)|' to read an + integer value |x| that is supposed to be in the range |a<=x<=b|. + +*/ + +#define undump(A,B,C) do { \ + undump_int(x); \ + if (x<(A) || x>(B)) \ + goto BAD_FMT; \ + else \ + (C) = x; \ +} while (0) + +#define format_debug(A,B) do { \ + if (debug_format_file) { \ + fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \ + } \ +} while (0) + +#define undump_size(A,B,C,D) do { \ + undump_int(x); \ + if (x<(A)) \ + goto BAD_FMT; \ + if (x>(B)) \ + too_small(C); \ + else \ + format_debug (C,x); \ + (D) = x; \ +} while (0) + boolean load_fmt_file(const char *fmtname) { - int j, k; /* all-purpose indices */ - halfword p; /* all-purpose pointer */ - int x; /* something undumped */ + int j, k, x; + halfword p; char *format_engine; - /* Undump constants for consistency check */ + /*tex Undump constants for consistency check .*/ if (ini_version) { libcfree(hash); libcfree(eqtb); @@ -368,22 +386,25 @@ boolean load_fmt_file(const char *fmtname) } undump_int(x); format_debug("format magic number", x); - if (x != 0x57325458) - goto BAD_FMT; /* not a format file */ - + if (x != 0x57325458) { + /*tex it's not a format file. */ + goto BAD_FMT; + } undump_int(x); format_debug("format id", x); - if (x != FORMAT_ID) - goto BAD_FMT; /* FORMAT_ID mismatch */ - + if (x != FORMAT_ID) { + /*tex We have a |FORMAT_ID| mismatch. */ + goto BAD_FMT; + } undump_int(x); format_debug("engine name size", x); - if ((x < 0) || (x > 256)) - goto BAD_FMT; /* corrupted format file */ - + if ((x < 0) || (x > 256)) { + /*tex The format file is corrupt. */ + goto BAD_FMT; + } format_engine = xmalloc((unsigned) x); undump_things(format_engine[0], x); - format_engine[x - 1] = 0; /* force string termination, just in case */ + format_engine[x - 1] = 0; if (strcmp(engine_name, format_engine)) { wake_up_terminal(); wterm_cr(); @@ -394,7 +415,7 @@ boolean load_fmt_file(const char *fmtname) xfree(format_engine); undump_int(x); format_debug("string pool checksum", x); - if (x != 0x57325458) { /* todo: @@\$ *//* check that strings are the same */ + if (x != 0x57325458) { wake_up_terminal(); wterm_cr(); fprintf(term_out, "---! %s was written by a different version", @@ -403,7 +424,7 @@ boolean load_fmt_file(const char *fmtname) } undump_int(x); if (x != max_halfword) - goto BAD_FMT; /* check |max_halfword| */ + goto BAD_FMT; undump_int(hash_high); if ((hash_high < 0) || (hash_high > sup_hash_extra)) goto BAD_FMT; @@ -428,10 +449,9 @@ boolean load_fmt_file(const char *fmtname) undump_int(x); if (x != hash_prime) goto BAD_FMT; - - /* Undump the string pool */ + /*tex Undump the string pool */ str_ptr = undump_string_pool(); - /* Undump the dynamic memory */ + /*tex Undump the dynamic memory */ undump_node_mem(); undump_int(temp_token_head); undump_int(hold_token_head); @@ -447,9 +467,7 @@ boolean load_fmt_file(const char *fmtname) undump_int(avail); undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); undump_int(dyn_used); - - /* Undump the table of equivalents */ - /* Undump regions 1 to 6 of |eqtb| */ + /*tex Undump regions 1 to 6 of the table of equivalents |eqtb|. */ k = null_cs; do { undump_int(x); @@ -464,15 +482,16 @@ boolean load_fmt_file(const char *fmtname) eqtb[j] = eqtb[k - 1]; k = k + x; } while (k <= eqtb_size); - if (hash_high > 0) /* undump |hash_extra| part */ + if (hash_high > 0) { + /*tex undump |hash_extra| part */ undump_things(eqtb[eqtb_size + 1], hash_high); - + } undump(hash_base, hash_top, par_loc); par_token = cs_token_flag + par_loc; undump(hash_base, hash_top, write_loc); undump_math_codes(); undump_text_codes(); - /* Undump the hash table */ + /*tex Undump the hash table */ undump_primitives(); undump(hash_base, frozen_control_sequence, hash_used); p = hash_base - 1; @@ -480,8 +499,7 @@ boolean load_fmt_file(const char *fmtname) undump(p + 1, hash_used, p); undump_hh(hash[p]); } while (p != hash_used); - undump_things(hash[hash_used + 1], - undefined_control_sequence - 1 - hash_used); + undump_things(hash[hash_used + 1], undefined_control_sequence - 1 - hash_used); if (debug_format_file) print_csnames(hash_base, undefined_control_sequence - 1); if (hash_high > 0) { @@ -490,20 +508,17 @@ boolean load_fmt_file(const char *fmtname) print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1)); } undump_int(cs_count); - - /* Undump the font information */ + /*tex Undump the font information */ undump_int(x); set_max_font_id(x); for (k = 0; k <= max_font_id(); k++) { - /* Undump the array info for internal font number |k| */ + /*tex Undump the array info for internal font number |k| */ undump_font(k); } undump_math_data(); - - /* Undump the hyphenation tables */ + /*tex Undump the hyphenation tables */ undump_language_data(); - - /* Undump a couple more things and the closing check word */ + /*tex Undump a couple more things and the closing check word */ undump(batch_mode, error_stop_mode, interaction); if (interactionoption != unspecified_mode) interaction = interactionoption; @@ -512,12 +527,10 @@ boolean load_fmt_file(const char *fmtname) undump_int(x); if (x != 69069) goto BAD_FMT; - - /* Undump the lua bytecodes */ + /*tex Undump the lua bytecodes. */ undump_luac_registers(); - prev_depth_par = ignore_depth; - return true; /* it worked! */ + return true; BAD_FMT: wake_up_terminal(); wterm_cr(); diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.w b/Build/source/texk/web2c/luatexdir/tex/equivalents.c index 271fc67ffbd..3d260d5c5ee 100644 --- a/Build/source/texk/web2c/luatexdir/tex/equivalents.w +++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.c @@ -1,29 +1,31 @@ -% equivalents.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +equivalents.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" halfword last_cs_name = null_cs; -/* |eqtb[p]| has just been restored or retained */ +/*tex |eqtb[p]| has just been restored or retained. */ static void diagnostic_trace(halfword p, const char *s) { @@ -36,60 +38,54 @@ static void diagnostic_trace(halfword p, const char *s) end_diagnostic(false); } -@ @c -void show_eqtb_meaning(halfword n); /* forward */ +void show_eqtb_meaning(halfword n); + +/*tex + +Now that we have studied the data structures for \TeX's semantic routines, we +ought to consider the data structures used by its syntactic routines. In other +words, our next concern will be the tables that \TeX\ looks at when it is +scanning what the user has written. -@ Now that we have studied the data structures for \TeX's semantic routines, -we ought to consider the data structures used by its syntactic routines. In -other words, our next concern will be -the tables that \TeX\ looks at when it is scanning -what the user has written. +The biggest and most important such table is called |eqtb|. It holds the current +``equivalents'' of things; i.e., it explains what things mean or what their +current values are, for all quantities that are subject to the nesting structure +provided by \TeX's grouping mechanism. There are six parts to |eqtb|: -The biggest and most important such table is called |eqtb|. It holds the -current ``equivalents'' of things; i.e., it explains what things mean -or what their current values are, for all quantities that are subject to -the nesting structure provided by \TeX's grouping mechanism. There are six -parts to |eqtb|: +1) |eqtb[null_cs]| holds the current equivalent of the zero-length control +sequence. -\yskip\hang 1) |eqtb[null_cs]| holds the current equivalent of the -zero-length control sequence. +2) |eqtb[hash_base..(glue_base-1)]| holds the current equivalents of single- and +multiletter control sequences. -\yskip\hang 2) |eqtb[hash_base..(glue_base-1)]| holds the current -equivalents of single- and multiletter control sequences. +3) |eqtb[glue_base..(local_base-1)]| holds the current equivalents of glue +parameters like the current baselineskip. -\yskip\hang 3) |eqtb[glue_base..(local_base-1)]| holds the current -equivalents of glue parameters like the current baselineskip. +4) |eqtb[local_base..(int_base-1)]| holds the current equivalents of local +halfword quantities like the current box registers, the current ``catcodes,'' the +current font, and a pointer to the current paragraph shape. -\yskip\hang 4) |eqtb[local_base..(int_base-1)]| holds the current -equivalents of local halfword quantities like the current box registers, -the current ``catcodes,'' the current font, and a pointer to the current -paragraph shape. +5) |eqtb[int_base..(dimen_base-1)]| holds the current equivalents of fullword +integer parameters like the current hyphenation penalty. -\yskip\hang 5) |eqtb[int_base..(dimen_base-1)]| holds the current -equivalents of fullword integer parameters like the current hyphenation -penalty. +6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents of fullword +dimension parameters like the current hsize or amount of hanging indentation. -\yskip\hang 6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents -of fullword dimension parameters like the current hsize or amount of -hanging indentation. +Note that, for example, the current amount of baselineskip glue is determined by +the setting of a particular location in region~3 of |eqtb|, while the current +meaning of the control sequence `\.{\\baselineskip}' (which might have been +changed by \.{\\def} or \.{\\let}) appears in region~2. -\yskip\noindent Note that, for example, the current amount of -baselineskip glue is determined by the setting of a particular location -in region~3 of |eqtb|, while the current meaning of the control sequence -`\.{\\baselineskip}' (which might have been changed by \.{\\def} or -\.{\\let}) appears in region~2. +The last two regions of |eqtb| have fullword values instead of the three fields +|eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary, but \TeX\ needs +to store the |eq_level| information in another array called |xeq_level|. -@ The last two regions of |eqtb| have fullword values instead of the -three fields |eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary, -but \TeX\ needs to store the |eq_level| information in another array -called |xeq_level|. +*/ -@c memory_word *eqtb; -halfword eqtb_top; /* maximum of the |eqtb| */ +halfword eqtb_top; quarterword xeq_level[(eqtb_size + 1)]; -@ @c void initialize_equivalents(void) { int k; @@ -97,48 +93,45 @@ void initialize_equivalents(void) xeq_level[k] = level_one; } -@ The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups -in \TeX\ means that |eqtb| entries valid in outer groups should be saved -and restored later if they are overridden inside the braces. When a new |eqtb| -value is being assigned, the program therefore checks to see if the previous -entry belongs to an outer level. In such a case, the old value is placed -on the |save_stack| just before the new value enters |eqtb|. At the -end of a grouping level, i.e., when the right brace is sensed, the -|save_stack| is used to restore the outer values, and the inner ones are -destroyed. - -Entries on the |save_stack| are of type |save_record|. The top item on -this stack is |save_stack[p]|, where |p=save_ptr-1|; it contains three -fields called |save_type|, |save_level|, and |save_value|, and it is -interpreted in one of four ways: - -\yskip\hang 1) If |save_type(p)=restore_old_value|, then -|save_value(p)| is a location in |eqtb| whose current value should -be destroyed at the end of the current group and replaced by |save_word(p-1)| -(|save_type(p-1)==saved_eqtb|). -Furthermore if |save_value(p)>=int_base|, then |save_level(p)| should -replace the corresponding entry in |xeq_level| (if |save_value(p)<int_base|, -then the level is part of |save_word(p-1)|). - -\yskip\hang 2) If |save_type(p)=restore_zero|, then |save_value(p)| -is a location in |eqtb| whose current value should be destroyed at the end -of the current group, when it should be -replaced by the current value of |eqtb[undefined_control_sequence]|. - -\yskip\hang 3) If |save_type(p)=insert_token|, then |save_value(p)| -is a token that should be inserted into \TeX's input when the current -group ends. - -\yskip\hang 4) If |save_type(p)=level_boundary|, then |save_level(p)| -is a code explaining what kind of group we were previously in, and -|save_value(p)| points to the level boundary word at the bottom of -the entries for that group. Furthermore, |save_value(p-1)| contains the -source line number at which the current level of grouping was entered, -this field has itself a type: |save_type(p-1)==saved_line|. - -Besides this `official' use, various subroutines push temporary -variables on the save stack when it is handy to do so. These all have -an explicit |save_type|, and they are: +/*tex + +The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups in +\TeX\ means that |eqtb| entries valid in outer groups should be saved and +restored later if they are overridden inside the braces. When a new |eqtb| value +is being assigned, the program therefore checks to see if the previous entry +belongs to an outer level. In such a case, the old value is placed on the +|save_stack| just before the new value enters |eqtb|. At the end of a grouping +level, i.e., when the right brace is sensed, the |save_stack| is used to restore +the outer values, and the inner ones are destroyed. + +Entries on the |save_stack| are of type |save_record|. The top item on this stack +is |save_stack[p]|, where |p=save_ptr-1|; it contains three fields called +|save_type|, |save_level|, and |save_value|, and it is interpreted in one of four +ways: + +1) If |save_type(p)=restore_old_value|, then |save_value(p)| is a location in +|eqtb| whose current value should be destroyed at the end of the current group +and replaced by |save_word(p-1)| (|save_type(p-1)==saved_eqtb|). Furthermore if +|save_value(p)>=int_base|, then |save_level(p)| should replace the corresponding +entry in |xeq_level| (if |save_value(p)<int_base|, then the level is part of +|save_word(p-1)|). + +2) If |save_type(p)=restore_zero|, then |save_value(p)| is a location in |eqtb| +whose current value should be destroyed at the end of the current group, when it +should be replaced by the current value of |eqtb[undefined_control_sequence]|. + +3) If |save_type(p)=insert_token|, then |save_value(p)| is a token that should be +inserted into \TeX's input when the current group ends. + +4) If |save_type(p)=level_boundary|, then |save_level(p)| is a code explaining +what kind of group we were previously in, and |save_value(p)| points to the level +boundary word at the bottom of the entries for that group. Furthermore, +|save_value(p-1)| contains the source line number at which the current level of +grouping was entered, this field has itself a type: |save_type(p-1)==saved_line|. + +Besides this `official' use, various subroutines push temporary variables on the +save stack when it is handy to do so. These all have an explicit |save_type|, and +they are: |saved_adjust| signifies an adjustment is beging scanned, |saved_insert| an insertion is being scanned, @@ -154,32 +147,34 @@ an explicit |save_type|, and they are: |saved_boxattr| the box \.{attr} specification, |saved_boxpack| the box \.{pack} specification. -@ The global variable |cur_group| keeps track of what sort of group we are -currently in. Another global variable, |cur_boundary|, points to the -topmost |level_boundary| word. And |cur_level| is the current depth of -nesting. The routines are designed to preserve the condition that no entry -in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|. +The global variable |cur_group| keeps track of what sort of group we are +currently in. Another global variable, |cur_boundary|, points to the topmost +|level_boundary| word. And |cur_level| is the current depth of nesting. The +routines are designed to preserve the condition that no entry in the |save_stack| +or in |eqtb| ever has a level greater than |cur_level|. + +*/ -@c save_record *save_stack; -int save_ptr; /* first unused entry on |save_stack| */ +int save_ptr; /* first unused entry on the save stack */ int max_save_stack; /* maximum usage of save stack */ quarterword cur_level = level_one; /* current nesting level for groups */ group_code cur_group = bottom_level; /* current group type */ int cur_boundary; /* where the current level begins */ -@ At this time it might be a good idea for the reader to review the introduction -to |eqtb| that was given above just before the long lists of parameter names. -Recall that the ``outer level'' of the program is |level_one|, since -undefined control sequences are assumed to be ``defined'' at |level_zero|. +/*tex +At this time it might be a good idea for the reader to review the introduction to +|eqtb| that was given above just before the long lists of parameter names. Recall +that the ``outer level'' of the program is |level_one|, since undefined control +sequences are assumed to be ``defined'' at |level_zero|. +The following macro is used to test if there is room for up to eight more entries +on |save_stack|. By making a conservative test like this, we can get by with +testing for overflow in only a few places. -@ The following macro is used to test if there is room for up to eight more -entries on |save_stack|. By making a conservative test like this, we can -get by with testing for overflow in only a few places. +*/ -@c #define check_full_save_stack() do { \ if (save_ptr>max_save_stack) { \ max_save_stack=save_ptr; \ @@ -188,30 +183,32 @@ get by with testing for overflow in only a few places. } \ } while (0) -@ Procedure |new_save_level| is called when a group begins. The -argument is a group identification code like `|hbox_group|'. After -calling this routine, it is safe to put six more entries on |save_stack|. +/*tex + +Procedure |new_save_level| is called when a group begins. The argument is a group +identification code like `|hbox_group|'. After calling this routine, it is safe +to put six more entries on |save_stack|. -In some cases integer-valued items are placed onto the -|save_stack| just below a |level_boundary| word, because this is a -convenient place to keep information that is supposed to ``pop up'' just -when the group has finished. -For example, when `\.{\\hbox to 100pt}' is being treated, the 100pt -dimension is stored on |save_stack| just before |new_save_level| is -called. +In some cases integer-valued items are placed onto the |save_stack| just below a +|level_boundary| word, because this is a convenient place to keep information +that is supposed to ``pop up'' just when the group has finished. For example, +when `\.{\\hbox to 100pt}' is being treated, the 100pt dimension is stored on +|save_stack| just before |new_save_level| is called. + +*/ -@c void new_save_level(group_code c) -{ /* begin a new level of grouping */ +{ /*tex We begin a new level of grouping. */ check_full_save_stack(); set_saved_record(0, saved_line, 0, line); incr(save_ptr); save_type(save_ptr) = level_boundary; save_level(save_ptr) = cur_group; save_value(save_ptr) = cur_boundary; - if (cur_level == max_quarterword) + if (cur_level == max_quarterword) { overflow("grouping levels", max_quarterword - min_quarterword); - /* quit if |(cur_level+1)| is too big to be stored in |eqtb| */ + } + /*tex We quit if |(cur_level+1)| is too big to be stored in |eqtb|. */ cur_boundary = save_ptr; cur_group = c; if (tracing_groups_par > 0) @@ -220,7 +217,6 @@ void new_save_level(group_code c) incr(save_ptr); } -@ @c static const char *save_stack_type(int v) { const char *s = ""; @@ -243,13 +239,13 @@ static const char *save_stack_type(int v) case saved_boxdir: s = "saved_boxdir"; break; case saved_boxattr: s = "saved_boxattr"; break; case saved_boxpack: s = "saved_boxpack"; break; + case saved_attrlist: s = "saved_attrlist"; break; case saved_eqtb: s = "saved_eqtb"; break; default: break; } return s; } -@ @c void print_save_stack(void) { int i; @@ -274,7 +270,7 @@ void print_save_stack(void) print_int(save_word(i - 1).cint); } else { print_int(eq_type_field(save_word(i - 1))); - print_char(','); /* |print_int(eq_level_field(save_word(i-1)));| */ + print_char(','); print_int(equiv_field(save_word(i - 1))); } i--; @@ -303,17 +299,21 @@ void print_save_stack(void) break; case saved_adjust: tprint(", "); - print_int(save_level(i)); /* vadjust vs vadjust pre */ + /*tex vadjust vs vadjust pre */ + print_int(save_level(i)); break; case saved_insert: tprint(", "); - print_int(save_value(i)); /* insert number */ + /*tex insert number */ + print_int(save_value(i)); break; - case saved_boxtype: /* \.{\\localleftbox} vs \.{\\localrightbox} */ + case saved_boxtype: + /*tex \.{\\localleftbox} vs \.{\\localrightbox} */ tprint(", "); print_int(save_value(i)); break; - case saved_eqno: /* \.{\\eqno} vs \.{\\leqno} */ + case saved_eqno: + /*tex \.{\\eqno} vs \.{\\leqno} */ tprint(", "); print_int(save_value(i)); break; @@ -339,10 +339,11 @@ void print_save_stack(void) case saved_textdir: case saved_boxdir: tprint(", "); - print_dir(dir_dir(save_value(i))); + print_dir_text(save_value(i)); break; case saved_boxattr: case saved_boxpack: + case saved_attrlist: tprint(", "); print_int(save_value(i)); break; @@ -357,27 +358,28 @@ void print_save_stack(void) end_diagnostic(true); } -@ The \.{\\showgroups} command displays all currently active grouping - levels. +/*tex + +The \.{\\showgroups} command displays all currently active grouping levels. + +The modifications of \TeX\ required for the display produced by the +|show_save_groups| procedure were first discussed by Donald~E. Knuth in {\sl +TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990. @^Knuth, Donald Ervin@> -@ The modifications of \TeX\ required for the display produced by the - |show_save_groups| procedure were first discussed by Donald~E. Knuth in - {\sl TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990. - @^Knuth, Donald Ervin@> +In order to understand a group type we also have to know its mode. Since +unrestricted horizontal modes are not associated with grouping, they are skipped +when traversing the semantic nest. - In order to understand a group type we also have to know its mode. - Since unrestricted horizontal modes are not associated with grouping, - they are skipped when traversing the semantic nest. +*/ -@c void show_save_groups(void) { - int p = nest_ptr; /* index into |nest| */ - int m; /* mode */ - save_pointer v = save_ptr; /* saved value of |save_ptr| */ - quarterword l = cur_level; /* saved value of |cur_level| */ - group_code c = cur_group; /* saved value of |cur_group| */ - int a = 1; /* to keep track of alignments */ + int p = nest_ptr; + int m; + save_pointer v = save_ptr; + quarterword l = cur_level; + group_code c = cur_group; + int a = 1; /* to keep track of alignments */ int i; quarterword j; const char *s = NULL; @@ -447,16 +449,21 @@ void show_save_groups(void) break; case disc_group: tprint_esc("discretionary"); - for (i = 1; i < 3; i++) - if (i <= saved_value(-2)) + for (i = 1; i < 3; i++) { + if (i <= saved_value(-2)) { tprint("{}"); + } + } goto FOUND2; break; case math_choice_group: tprint_esc("mathchoice"); - for (i = 1; i < 4; i++) - if (i <= saved_value(-3)) /* different offset because |-2==saved_textdir| */ + for (i = 1; i < 4; i++) { + /*tex A different offset because |-2==saved_textdir|: */ + if (i <= saved_value(-3)) { tprint("{}"); + } + } goto FOUND2; break; case insert_group: @@ -500,7 +507,7 @@ void show_save_groups(void) confusion("showgroups"); break; } - /* Show the box context */ + /*tex Show the box context */ i = saved_value(-5); if (i != 0) { if (i < box_flag) { @@ -528,9 +535,8 @@ void show_save_groups(void) } FOUND1: tprint_esc(s); - /* Show the box packaging info */ + /*tex Show the box packaging info. The offsets may vary. */ { - /* offsets may vary */ int ii = -1; while (saved_type(ii) != saved_boxspec) ii--; @@ -558,18 +564,20 @@ void show_save_groups(void) cur_group = c; } -@ Just before an entry of |eqtb| is changed, the following procedure should -be called to update the other data structures properly. It is important -to keep in mind that reference counts in |mem| include references from -within |save_stack|, so these counts must be handled carefully. -@^reference counts@> +/*tex + +Just before an entry of |eqtb| is changed, the following procedure should be +called to update the other data structures properly. It is important to keep in +mind that reference counts in |mem| include references from within |save_stack|, +so these counts must be handled carefully. @^reference counts@> -@c -/* we don't need to destroy when an assignment has the same node */ +We don't need to destroy when an assignment has the same node: + +*/ void eq_destroy(memory_word w) -{ /* gets ready to forget |w| */ - halfword q; /* |equiv| field of |w| */ +{ + halfword q; switch (eq_type_field(w)) { case call_cmd: case long_call_cmd: @@ -581,10 +589,16 @@ void eq_destroy(memory_word w) flush_node(equiv_field(w)); break; case shape_ref_cmd: - q = equiv_field(w); /* we need to free a \.{\\parshape} block */ - if (q != null) + q = equiv_field(w); + if (q != null) { + /*tex + We need to free a \.{\\parshape} block. Such a block is + |2n+1| words long, where |n=vinfo(q)|. It happens in the + flush function. + */ flush_node(q); - break; /* such a block is |2n+1| words long, where |n=vinfo(q)| */ + } + break; case box_ref_cmd: flush_node_list(equiv_field(w)); break; @@ -593,12 +607,14 @@ void eq_destroy(memory_word w) } } -@ To save a value of |eqtb[p]| that was established at level |l|, we -can use the following subroutine. +/*tex -@c +To save a value of |eqtb[p]| that was established at level |l|, we can use the +following subroutine. + +*/ void eq_save(halfword p, quarterword l) -{ /* saves |eqtb[p]| */ +{ check_full_save_stack(); if (l == level_zero) { save_type(save_ptr) = restore_zero; @@ -613,16 +629,17 @@ void eq_save(halfword p, quarterword l) incr(save_ptr); } -@ The procedure |eq_define| defines an |eqtb| entry having specified -|eq_type| and |equiv| fields, and saves the former value if appropriate. -This procedure is used only for entries in the first four regions of |eqtb|, -i.e., only for entries that have |eq_type| and |equiv| fields. -After calling this routine, it is safe to put four more entries on -|save_stack|, provided that there was room for four more entries before -the call, since |eq_save| makes the necessary test. +/*tex + +The procedure |eq_define| defines an |eqtb| entry having specified |eq_type| and +|equiv| fields, and saves the former value if appropriate. This procedure is used +only for entries in the first four regions of |eqtb|, i.e., only for entries that +have |eq_type| and |equiv| fields. After calling this routine, it is safe to put +four more entries on |save_stack|, provided that there was room for four more +entries before the call, since |eq_save| makes the necessary test. + +*/ -@ new data for |eqtb| -@c void eq_define(halfword p, quarterword t, halfword e) { boolean trace = tracing_assigns_par > 0; @@ -645,11 +662,14 @@ void eq_define(halfword p, quarterword t, halfword e) diagnostic_trace(p, "into"); } -@ The counterpart of |eq_define| for the remaining (fullword) positions in -|eqtb| is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all -|p|, a `|restore_zero|' will never be used in this case. +/*tex + +The counterpart of |eq_define| for the remaining (fullword) positions in |eqtb| +is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all |p|, a +`|restore_zero|' will never be used in this case. + +*/ -@c void eq_word_define(halfword p, int w) { boolean trace = tracing_assigns_par > 0; @@ -669,15 +689,17 @@ void eq_word_define(halfword p, int w) diagnostic_trace(p, "into"); } +/*tex -@ The |eq_define| and |eq_word_define| routines take care of local definitions. -@^global definitions@> -Global definitions are done in almost the same way, but there is no need -to save old values, and the new value is associated with |level_one|. +The |eq_define| and |eq_word_define| routines take care of local definitions. +@^global definitions@> Global definitions are done in almost the same way, but +there is no need to save old values, and the new value is associated with +|level_one|. + +*/ -@c void geq_define(halfword p, quarterword t, halfword e) -{ /* global |eq_define| */ +{ boolean trace = tracing_assigns_par > 0; if (trace) diagnostic_trace(p, "globally changing"); @@ -690,7 +712,7 @@ void geq_define(halfword p, quarterword t, halfword e) } void geq_word_define(halfword p, int w) -{ /* global |eq_word_define| */ +{ boolean trace = tracing_assigns_par > 0; if (trace) diagnostic_trace(p, "globally changing"); @@ -700,9 +722,12 @@ void geq_word_define(halfword p, int w) diagnostic_trace(p, "into"); } -@ Subroutine |save_for_after| puts a token on the stack for save-keeping. +/*tex + +Subroutine |save_for_after| puts a token on the stack for save-keeping. + +*/ -@c void save_for_after(halfword t) { if (cur_level > level_one) { @@ -714,16 +739,20 @@ void save_for_after(halfword t) } } -@ The |unsave| routine goes the other way, taking items off of |save_stack|. -This routine takes care of restoration when a level ends; everything -belonging to the topmost group is cleared off of the save stack. +/*tex + +The |unsave| routine goes the other way, taking items off of |save_stack|. This +routine takes care of restoration when a level ends; everything belonging to the +topmost group is cleared off of the save stack. + +*/ -@c void unsave(void) -{ /* pops the top level off the save stack */ - halfword p; /* position to be restored */ - quarterword l = level_one; /* saved level, if in fullword regions of |eqtb| */ - boolean a = false; /* have we already processed an \.{\\aftergroup} ? */ +{ + halfword p; + quarterword l = level_one; + /*tex Variable |a| registers if we already have processed an \.{\\aftergroup}. */ + boolean a = false; unsave_math_codes(cur_level); unsave_cat_codes(cat_code_table_par, cur_level); unsave_text_codes(cur_level); @@ -731,7 +760,7 @@ void unsave(void) if (cur_level > level_one) { boolean trace = tracing_restores_par > 0; decr(cur_level); - /* Clear off top level from |save_stack| */ + /*tex Clear off top level from |save_stack|. */ while (true) { decr(save_ptr); if (save_type(save_ptr) == level_boundary) @@ -739,7 +768,8 @@ void unsave(void) p = save_value(save_ptr); if (save_type(save_ptr) == insert_token) { reinsert_token(a, p); - a = true; /* always ... always etex now */ + /*tex always |true| as we are always in \ETEX\ now. */ + a = true; } else { if (save_type(save_ptr) == restore_old_value) { l = save_level(save_ptr); @@ -747,21 +777,25 @@ void unsave(void) } else { save_word(save_ptr) = eqtb[undefined_control_sequence]; } - /* Store |save_stack[save_ptr]| in |eqtb[p]|, unless - |eqtb[p]| holds a global value */ - /* A global definition, which sets the level to |level_one|, - will not be undone by |unsave|. If at least one global definition of - |eqtb[p]| has been carried out within the group that just ended, the - last such definition will therefore survive. - */ + /*tex + Store |save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]| + holds a global value A global definition, which sets the + level to |level_one|, will not be undone by |unsave|. If at + least one global definition of |eqtb[p]| has been carried out + within the group that just ended, the last such definition + will therefore survive. + */ if (p < int_base || p > eqtb_size) { if (eq_level(p) == level_one) { - eq_destroy(save_word(save_ptr)); /* destroy the saved value */ + /*tex Destroy the saved value: */ + eq_destroy(save_word(save_ptr)); if (trace) diagnostic_trace(p, "retaining"); } else { - eq_destroy(eqtb[p]); /* destroy the current value */ - eqtb[p] = save_word(save_ptr); /* restore the saved value */ + /*tex Destroy the current value: */ + eq_destroy(eqtb[p]); + /*tex Restore the saved value: */ + eqtb[p] = save_word(save_ptr); if (trace) diagnostic_trace(p, "restoring"); } @@ -776,32 +810,40 @@ void unsave(void) } } } - if (tracing_groups_par > 0) + + if (tracing_groups_par > 0) { group_trace(true); - if (grp_stack[in_open] == cur_boundary) - group_warning(); /* groups possibly not properly nested with files */ + } + if (grp_stack[in_open] == cur_boundary) { + /*tex Groups are possibly not properly nested with files. */ + group_warning(); + } cur_group = save_level(save_ptr); cur_boundary = save_value(save_ptr); decr(save_ptr); } else { - confusion("curlevel"); /* |unsave| is not used when |cur_group=bottom_level| */ + /*tex |unsave| is not used when |cur_group=bottom_level| */ + confusion("curlevel"); } attr_list_cache = cache_disabled; } -@ Most of the parameters kept in |eqtb| can be changed freely, but there's -an exception: The magnification should not be used with two different -values during any \TeX\ job, since a single magnification is applied to an -entire run. The global variable |mag_set| is set to the current magnification -whenever it becomes necessary to ``freeze'' it at a particular value. +/*tex + +Most of the parameters kept in |eqtb| can be changed freely, but there's an +exception: The magnification should not be used with two different values during +any \TeX\ job, since a single magnification is applied to an entire run. The +global variable |mag_set| is set to the current magnification whenever it becomes +necessary to ``freeze'' it at a particular value. -@c -int mag_set = 0; /* if nonzero, this magnification should be used henceforth */ +The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| for +magnification. If nonzero, this magnification should be used henceforth. We might +drop magnifaction at some point. -@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| -for magnification. +*/ + +int mag_set = 0; -@c void prepare_mag(void) { if ((mag_set > 0) && (mag_par != mag_set)) { @@ -809,14 +851,18 @@ void prepare_mag(void) print_int(mag_par); tprint(");"); tprint_nl(" the previous value will be retained"); - help2("I can handle only one magnification ratio per job. So I've", - "reverted to the magnification you used earlier on this run."); + help2( + "I can handle only one magnification ratio per job. So I've", + "reverted to the magnification you used earlier on this run." + ); int_error(mag_set); geq_word_define(int_base + mag_code, mag_set); /* |mag:=mag_set| */ } if ((mag_par <= 0) || (mag_par > 32768)) { print_err("Illegal magnification has been changed to 1000"); - help1("The magnification ratio must be between 1 and 32768."); + help1( + "The magnification ratio must be between 1 and 32768." + ); int_error(mag_par); geq_word_define(int_base + mag_code, 1000); } @@ -829,65 +875,74 @@ void prepare_mag(void) mag_set = mag_par; } -@ Let's pause a moment now and try to look at the Big Picture. -The \TeX\ program consists of three main parts: syntactic routines, -semantic routines, and output routines. The chief purpose of the -syntactic routines is to deliver the user's input to the semantic routines, -one token at a time. The semantic routines act as an interpreter -responding to these tokens, which may be regarded as commands. And the -output routines are periodically called on to convert box-and-glue -lists into a compact set of instructions that will be sent -to a typesetter. We have discussed the basic data structures and utility -routines of \TeX, so we are good and ready to plunge into the real activity by -considering the syntactic routines. - -Our current goal is to come to grips with the |get_next| procedure, -which is the keystone of \TeX's input mechanism. Each call of |get_next| -sets the value of three variables |cur_cmd|, |cur_chr|, and |cur_cs|, -representing the next input token. -$$\vbox{\halign{#\hfil\cr - \hbox{|cur_cmd| denotes a command code from the long list of codes - given above;}\cr - \hbox{|cur_chr| denotes a character code or other modifier of the command - code;}\cr - \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr - \hbox{\qquad if the current token was a control sequence, - otherwise it's zero.}\cr}}$$ -Underlying this external behavior of |get_next| is all the machinery -necessary to convert from character files to tokens. At a given time we -may be only partially finished with the reading of several files (for -which \.{\\input} was specified), and partially finished with the expansion -of some user-defined macros and/or some macro parameters, and partially -finished with the generation of some text in a template for \.{\\halign}, -and so on. When reading a character file, special characters must be -classified as math delimiters, etc.; comments and extra blank spaces must -be removed, paragraphs must be recognized, and control sequences must be -found in the hash table. Furthermore there are occasions in which the -scanning routines have looked ahead for a word like `\.{plus}' but only -part of that word was found, hence a few characters must be put back -into the input and scanned again. - -To handle these situations, which might all be present simultaneously, -\TeX\ uses various stacks that hold information about the incomplete -activities, and there is a finite state control for each level of the -input mechanism. These stacks record the current state of an implicitly -recursive process, but the |get_next| procedure is not recursive. -Therefore it will not be difficult to translate these algorithms into -low-level languages that do not support recursion. - -@c -int cur_cmd; /* current command set by |get_next| */ -halfword cur_chr; /* operand of current command */ -halfword cur_cs; /* control sequence found here, zero if none found */ -halfword cur_tok; /* packed representative of |cur_cmd| and |cur_chr| */ - -@ Here is a procedure that displays the current command. - -@c +/*tex + +Let's pause a moment now and try to look at the Big Picture. The \TeX\ program +consists of three main parts: syntactic routines, semantic routines, and output +routines. The chief purpose of the syntactic routines is to deliver the user's +input to the semantic routines, one token at a time. The semantic routines act as +an interpreter responding to these tokens, which may be regarded as commands. And +the output routines are periodically called on to convert box-and-glue lists into +a compact set of instructions that will be sent to a typesetter. We have +discussed the basic data structures and utility routines of \TeX, so we are good +and ready to plunge into the real activity by considering the syntactic routines. + +Our current goal is to come to grips with the |get_next| procedure, which is the +keystone of \TeX's input mechanism. Each call of |get_next| sets the value of +three variables |cur_cmd|, |cur_chr|, and |cur_cs|, representing the next input +token. + +$$ + \vbox{\halign{#\hfil\cr + \hbox{|cur_cmd| denotes a command code from the long list of codes given above;}\cr + \hbox{|cur_chr| denotes a character code or other modifier of the command code;}\cr + \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr + \hbox{\qquad if the current token was a control sequence, otherwise it's zero.}\cr}} +$$ + +Underlying this external behavior of |get_next| is all the machinery necessary to +convert from character files to tokens. At a given time we may be only partially +finished with the reading of several files (for which \.{\\input} was specified), +and partially finished with the expansion of some user-defined macros and/or some +macro parameters, and partially finished with the generation of some text in a +template for \.{\\halign}, and so on. When reading a character file, special +characters must be classified as math delimiters, etc.; comments and extra blank +spaces must be removed, paragraphs must be recognized, and control sequences must +be found in the hash table. Furthermore there are occasions in which the scanning +routines have looked ahead for a word like `\.{plus}' but only part of that word +was found, hence a few characters must be put back into the input and scanned +again. + +To handle these situations, which might all be present simultaneously, \TeX\ uses +various stacks that hold information about the incomplete activities, and there +is a finite state control for each level of the input mechanism. These stacks +record the current state of an implicitly recursive process, but the |get_next| +procedure is not recursive. Therefore it will not be difficult to translate these +algorithms into low-level languages that do not support recursion. + +In general, |cur_cmd| is the current command as set by |get_next|, while +|cur_chr| is the operand of the current command. The control sequence found here +is registsred in |cur_cs| and is zero if none found. The |cur_tok| variable +contains the packed representative of |cur_cmd| and |cur_chr| and like the other +ones is global. + +*/ + +int cur_cmd; +halfword cur_chr; +halfword cur_cs; +halfword cur_tok; + +/*tex + +Here is a procedure that displays the current command. The variable |n| holds the +level of \.{\\if...\\fi} nesting and |l| the line where \.{\\if} started. + +*/ + void show_cur_cmd_chr(void) { - int n; /* level of \.{\\if...\\fi} nesting */ - int l; /* line where \.{\\if} started */ + int n, l; halfword p; begin_diagnostic(); tprint_nl("{"); @@ -926,23 +981,26 @@ void show_cur_cmd_chr(void) end_diagnostic(false); } -@ Here is a procedure that displays the contents of |eqtb[n]| symbolically. +/*tex + +Here is a procedure that displays the contents of |eqtb[n]| symbolically. + +*/ -@c void show_eqtb(halfword n) { if (n < null_cs) { - /* this can't happen */ - print_char('?'); + /*tex + This can't happen in a pure \TEX\ run, but careless usage of tokens + at the \LUA\ end can make you end up here. + */ + tprint("? bad token, case 1: "); + print_int(n); } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) { - /* - Show equivalent |n|, in region 1 or 2 - - Here is a routine that displays the current meaning of an |eqtb| entry - in region 1 or~2. (Similar routines for the other regions will appear - below.) + /*tex + This routine show the current meaning of |eqtb| entry |n| in region 1 or 2. + Similar routines for the other regions will appear below. */ - sprint_cs(n); print_char('='); print_cmd_chr(eq_type(n), equiv(n)); @@ -951,10 +1009,9 @@ void show_eqtb(halfword n) show_token_list(token_link(equiv(n)), null, 32); } } else if (n < local_base) { - /* - Show equivalent |n|, in region 3 - - All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'. + /*tex + Here we show equivalent |n| in region 3. All glue parameters and registers + are initially `\.{0pt plus0pt minus0pt}'. */ if (n < skip_base) { if (n < glue_base + thin_mu_skip_code) @@ -979,18 +1036,17 @@ void show_eqtb(halfword n) } } else if (n < int_base) { - /* - Show equivalent |n|, in region 4 - - We initialize most things to null or undefined values. An undefined font - is represented by the internal code |font_base|. - - However, the character code tables are given initial values based on the - conventional interpretation of ASCII code. These initial values should - not be changed when \TeX\ is adapted for use with non-English languages; - all changes to the initialization conventions should be made in format - packages, not in \TeX\ itself, so that global interchange of formats is - possible. + /*tex + We're now at equivalent |n| in region 4. First we initialize most + things to null or undefined values. An undefined font is represented + by the internal code |font_base|. + + However, the character code tables are given initial values based on + the conventional interpretation of ASCII code. These initial values + should not be changed when \TeX\ is adapted for use with non-English + languages; all changes to the initialization conventions should be + made in format packages, not in \TeX\ itself, so that global + interchange of formats is possible. */ if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) { if (n == par_shape_loc) @@ -1032,13 +1088,16 @@ void show_eqtb(halfword n) show_node_list(equiv(n)); } } else if (n == cur_font_loc) { - /* Show the font identifier in |eqtb[n]| */ + /*tex + Let's show the font identifier in |eqtb[n]|, that's + |font_id_text(equiv(n))| + */ tprint("current font"); print_char('='); - print_esc(hash[font_id_base + equiv(n)].rh); /* that's |font_id_text(equiv(n))| */ + print_esc(hash[font_id_base + equiv(n)].rh); } } else if (n < dimen_base) { - /* Show equivalent |n|, in region 5 */ + /*tex Show equivalent |n| in region 5: */ if (n < dir_base) { print_cmd_chr(assign_int_cmd, n); print_char('='); @@ -1046,7 +1105,7 @@ void show_eqtb(halfword n) } else if (n < count_base) { print_cmd_chr(assign_dir_cmd, n); print_char(' '); - print_dir(eqtb[n].cint); + print_dir_par(eqtb[n].cint); } else if (n < attribute_base) { tprint_esc("count"); print_int(n - count_base); @@ -1059,7 +1118,7 @@ void show_eqtb(halfword n) print_int(eqtb[n].cint); } } else if (n <= eqtb_size) { - /* Show equivalent |n|, in region 6 */ + /*tex Show equivalent |n| in region 6: */ if (n < scaled_base) { print_cmd_chr(assign_dimen_cmd, n); } else { @@ -1070,31 +1129,28 @@ void show_eqtb(halfword n) print_scaled(eqtb[n].cint); tprint("pt"); } else { - /* this can't happen either */ - print_char('?'); + /*tex This can't happen unless you messed up at the \LUA\ end. */ + tprint("? bad token, case 2: "); + print_int(n); } } -@ @c void show_eqtb_meaning(halfword n) { if (n < null_cs) { - /* this can't happen */ + /*tex This can't happen. */ print_char('?'); } else if ((n < glue_base) || ((n > eqtb_size) && (n <= eqtb_top))) { - /* - Show equivalent |n|, in region 1 or 2 - - Here is a routine that displays the current meaning of an |eqtb| entry - in region 1 or~2. (Similar routines for the other regions will appear - below.) + /*tex + Here is a routine that displays the current meaning of an |eqtb| + entry in region 1 or~2. Similar routines for the other regions will + appear below. */ sprint_cs(n); } else if (n < local_base) { /* - Show equivalent |n|, in region 3 - - All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'. + Show equivalent |n| in region 3. All glue parameters and registers + are initially `\.{0pt plus0pt minus0pt}'. */ if (n < skip_base) { if (n < glue_base + thin_mu_skip_code) @@ -1108,20 +1164,16 @@ void show_eqtb_meaning(halfword n) tprint_esc("muskip"); print_int(n - mu_skip_base); } - } else if (n < int_base) { - /* - Show equivalent |n|, in region 4 - - We initialize most things to null or undefined values. An undefined font - is represented by the internal code |font_base|. - - However, the character code tables are given initial values based on the - conventional interpretation of ASCII code. These initial values should - not be changed when \TeX\ is adapted for use with non-English languages; - all changes to the initialization conventions should be made in format - packages, not in \TeX\ itself, so that global interchange of formats is - possible. + /*tex + Show equivalent |n| in region 4. We initialize most things to null or + undefined values. An undefined font is represented by the internal + code |font_base|. However, the character code tables are given + initial values based on the conventional interpretation of ASCII + code. These initial values should not be changed when \TeX\ is + adapted for use with non-English languages; all changes to the + initialization conventions should be made in format packages, not in + \TeX\ itself, so that global interchange of formats is possible. */ if ((n == par_shape_loc) || ((n >= etex_pen_base) && (n < etex_pens))) { if (n == par_shape_loc) @@ -1137,11 +1189,11 @@ void show_eqtb_meaning(halfword n) tprint_esc("box"); print_int(n - box_base); } else if (n == cur_font_loc) { - /* Show the font identifier in |eqtb[n]| */ + /*tex Show the font identifier in |eqtb[n]|. */ tprint("current font"); } } else if (n < dimen_base) { - /* Show equivalent |n|, in region 5 */ + /*tex Show equivalent |n| in region 5. */ if (n < dir_base) { print_cmd_chr(assign_int_cmd, n); } else if (n < count_base) { @@ -1154,7 +1206,7 @@ void show_eqtb_meaning(halfword n) print_int(n - attribute_base); } } else if (n <= eqtb_size) { - /* Show equivalent |n|, in region 6 */ + /*tex Show equivalent |n| in region 6. */ if (n < scaled_base) { print_cmd_chr(assign_dimen_cmd, n); } else { @@ -1162,7 +1214,7 @@ void show_eqtb_meaning(halfword n) print_int(n - scaled_base); } } else { - /* this can't happen either */ + /*tex This can't happen either. */ print_char('?'); } } diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.c b/Build/source/texk/web2c/luatexdir/tex/errors.c new file mode 100644 index 00000000000..b630422ee98 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/errors.c @@ -0,0 +1,1032 @@ +/* + +errors.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#define edit_var "TEXEDIT" + +/*tex + +When something anomalous is detected, \TeX\ typically does something like this: +$$\vbox{\halign{#\hfil\cr |print_err("Something anomalous has been +detected");|\cr |help3("This is the first line of my offer to help.")|\cr |("This +is the second line. I'm trying to")|\cr |("explain the best way for you to +proceed.");|\cr |error;|\cr}}$$ A two-line help message would be given using +|help2|, etc.; these informal helps should use simple vocabulary that complements +the words used in the official error message that was printed. (Outside the +U.S.A., the help messages should preferably be translated into the local +vernacular. Each line of help is at most 60 characters long, in the present +implementation, so that |max_print_line| will not be exceeded.) + +The |print_err| procedure supplies a `\.!' before the official message, and makes +sure that the terminal is awake if a stop is going to occur. The |error| +procedure supplies a `\..' after the official message, then it shows the location +of the error; and if |interaction=error_stop_mode|, it also enters into a dialog +with the user, during which time the help message may be printed. @^system +dependencies@> + +*/ + +/*tex The current level of interaction: */ + +int interaction; + +/*tex Set from the command line: */ + +int interactionoption; + +char *last_error = NULL; +char *last_lua_error = NULL; +char *last_warning_tag = NULL; +char *last_warning_str = NULL; +char *last_error_context = NULL; + +int err_old_setting = 0 ; +int in_error = 0 ; + +void set_last_error_context(void) +{ + str_number str; + int sel = selector; + int saved_new_line_char; + int saved_new_string_line; + selector = new_string; + saved_new_line_char = new_line_char_par; + saved_new_string_line = new_string_line; + new_line_char_par = 10; + new_string_line = 10; + show_context(); + xfree(last_error_context); + str = make_string(); + last_error_context = makecstring(str); + flush_str(str); + selector = sel; + new_line_char_par = saved_new_line_char; + new_string_line = saved_new_string_line; + return; +} + +void flush_err(void) +{ + str_number s_error; + char *s = NULL; + int callback_id ; + if (in_error) { + selector = err_old_setting; + str_room(1); + s_error = make_string(); + s = makecstring(s_error); + flush_str(s_error); + if (interaction == error_stop_mode) { + wake_up_terminal(); + } + xfree(last_error); + last_error = (string) xmalloc((unsigned) (strlen(s) + 1)); + strcpy(last_error,s); + callback_id = callback_defined(show_error_message_callback); + if (callback_id > 0) { + run_callback(callback_id, "->"); + } else { + tprint(s); + } + in_error = 0 ; + } +} + +void print_err(const char *s) +{ + int callback_id = callback_defined(show_error_message_callback); + if (interaction == error_stop_mode) { + wake_up_terminal(); + } + if (callback_id > 0) { + err_old_setting = selector; + selector = new_string; + in_error = 1 ; + } + if (filelineerrorstylep) { + print_file_line(); + } else { + tprint_nl("! "); + } + tprint(s); + if (callback_id <= 0) { + xfree(last_error); + last_error = (string) xmalloc((unsigned) (strlen(s) + 1)); + strcpy(last_error,s); + } +} + +/*tex + +\TeX\ is careful not to call |error| when the print |selector| setting might be +unusual. The only possible values of |selector| at the time of error messages are + +|no_print| (when |interaction=batch_mode| and |log_file| not yet open); + +|term_only| (when |interaction>batch_mode| and |log_file| not yet open); + +|log_only| (when |interaction=batch_mode| and |log_file| is open); + +|term_and_log| (when |interaction>batch_mode| and |log_file| is open). + +*/ + +void fixup_selector(boolean logopened) +{ + if (interaction == batch_mode) + selector = no_print; + else + selector = term_only; + if (logopened) + selector = selector + 2; +} + +/*tex + +A global variable |deletions_allowed| is set |false| if the |get_next| routine is +active when |error| is called; this ensures that |get_next| and related routines +like |get_token| will never be called recursively. A similar interlock is +provided by |set_box_allowed|. @^recursion@> + +The global variable |history| records the worst level of error that has been +detected. It has four possible values: |spotless|, |warning_issued|, +|error_message_issued|, and |fatal_error_stop|. + +Another global variable, |error_count|, is increased by one when an |error| +occurs without an interactive dialog, and it is reset to zero at the end of every +paragraph. If |error_count| reaches 100, \TeX\ decides that there is no point in +continuing further. + +*/ + +/*tex Is it safe for |error| to call |get_token|? */ + +boolean deletions_allowed; + +/*tex Is it safe to do a \.{\\setbox} assignment? */ + +boolean set_box_allowed; +/*tex Has the source input been clean so far? */ + +int history; + +/*tex The number of scrolled errors since the last paragraph ended. */ + +int error_count; + +/*tex Should \TeX\ pause for instructions? */ + +int interrupt; + +/*tex Should interrupts be observed? */ + +boolean OK_to_interrupt; + +/*tex + +The value of |history| is initially |fatal_error_stop|, but it will be changed to +|spotless| if \TeX\ survives the initialization process. + +*/ + +void initialize_errors(void) +{ + if (interactionoption == unspecified_mode) + interaction = error_stop_mode; + else + interaction = interactionoption; + deletions_allowed = true; + set_box_allowed = true; + OK_to_interrupt = true; +} + +/*tex + +It is possible for |error| to be called recursively if some error arises when +|get_token| is being used to delete a token, and/or if some fatal error occurs +while \TeX\ is trying to fix a non-fatal one. But such recursion @^recursion@> is +never more than two levels deep. + +Individual lines of help are recorded in the array |help_line|. + +*/ + +const char *help_line[7]; + +/*tex + Should the |err_help| list be shown? +*/ + +boolean use_err_help; + +/*tex + +The |jump_out| procedure just cuts across all active procedure levels and exits +the program. It is used when there is no recovery from a particular error. The +exit code can be overloaded. + +*/ + +int defaultexitcode = 0; + +__attribute__ ((noreturn)) +void do_final_end(void) +{ + update_terminal(); + ready_already = 0; + lua_close(Luas); + if ((history != spotless) && (history != warning_issued)) + uexit(1); + else + uexit(defaultexitcode); +} + +__attribute__ ((noreturn)) +void jump_out(void) +{ + close_files_and_terminate(); + do_final_end(); +} + +/*tex + +Here is the function that calls the editor, if one is defined. This is loosely +based on a similar function in kpathsea, but the calling convention is quite +different. + +*/ + +static const_string edit_value = EDITOR; + +#if defined(WIN32) + +static int Isspace (char c) +{ + return (c == ' ' || c == '\t'); +} + +#endif /* WIN32 */ + +__attribute__ ((noreturn)) +static void luatex_calledit (int baseptr, int linenumber) +{ + char *temp, *command, *fullcmd; + char c; + int sdone, ddone, i; + char *filename = makecstring(input_stack[base_ptr].name_field); + int fnlength = strlen(filename); +#ifdef WIN32 + char *fp, *ffp, *env, editorname[256], buffer[256]; + int cnt = 0; + int dontchange = 0; +#endif + sdone = ddone = 0; + /*tex + Close any open input files, since we're going to kill the job. + */ + close_files_and_terminate(); + /*tex + Replace the default with the value of the appropriate environment + variable or config file value, if it's set. + */ + temp = kpse_var_value (edit_var); + if (temp != NULL) + edit_value = temp; + /*tex + Construct the command string. The `11' is the maximum length an + integer might be. + */ + command = xmalloc (strlen (edit_value) + fnlength + 11); + /*tex + So we can construct it as we go. + */ + temp = command; +#ifdef WIN32 + fp = editorname; + if ((isalpha(*edit_value) && *(edit_value + 1) == ':' && IS_DIR_SEP (*(edit_value + 2))) + || (*edit_value == '"' && isalpha(*(edit_value + 1)) + && *(edit_value + 2) == ':' && IS_DIR_SEP (*(edit_value + 3)))) { + dontchange = 1; + } +#endif + while ((c = *edit_value++) != 0) { + if (c == '%') { + switch (c = *edit_value++) { + case 'd': + if (ddone) + FATAL1 ("call_edit: `%%d' appears twice in editor command: `%s'", edit_value); + sprintf (temp, "%ld", (long int)linenumber); + while (*temp != '\0') + temp++; + ddone = 1; + break; + case 's': + if (sdone) + FATAL1 ("call_edit: `%%s' appears twice in editor command: `%s'", edit_value); + for (i =0; i < fnlength; i++) + *temp++ = filename[i]; + sdone = 1; + break; + case '\0': + *temp++ = '%'; + /*tex + Back up to the null to force termination. + */ + edit_value--; + break; + default: + *temp++ = '%'; + *temp++ = c; + break; + } + } else { +#ifdef WIN32 + if (dontchange) { + *temp++ = c; + } else if(Isspace(c) && cnt == 0) { + cnt++; + temp = command; + *temp++ = c; + *fp = '\0'; + } else if(!Isspace(c) && cnt == 0) { + *fp++ = c; + } else { + *temp++ = c; + } +#else + *temp++ = c; +#endif + } + } + *temp = 0; +#ifdef WIN32 + if (dontchange == 0) { + if(editorname[0] == '.' || editorname[0] == '/' || editorname[0] == '\\') { + fprintf(stderr, "%s is not allowed to execute.\n", editorname); + do_final_end(); + } + env = (char *)getenv("PATH"); + if(SearchPath(env, editorname, ".exe", 256, buffer, &ffp)==0) { + if(SearchPath(env, editorname, ".bat", 256, buffer, &ffp)==0) { + fprintf(stderr, "I cannot find %s in the PATH.\n", editorname); + do_final_end(); + } + } + fullcmd = (char *)xmalloc(strlen(buffer)+strlen(command)+5); + strcpy(fullcmd, "\""); + strcat(fullcmd, buffer); + strcat(fullcmd, "\""); + strcat(fullcmd, command); + } +#endif + fullcmd = command; + /*tex Execute the command. */ + if (system (fullcmd) != 0) { + fprintf (stderr, "! Trouble executing `%s'.\n", command); + } + /*tex Quit, since we found an error. */ + do_final_end (); +} + +/*tex + + This completes the job of error reporting. + +*/ + +void error(void) +{ + /*tex What the user types :*/ + ASCII_code c; + int callback_id; + /*tex Used to save global variables when deleting tokens: */ + int s1, s2, s3, s4; + int i; + flush_err(); + if (history < error_message_issued) + history = error_message_issued; + callback_id = callback_defined(show_error_hook_callback); + if (callback_id > 0) { + set_last_error_context(); + run_callback(callback_id, "->"); + } else { + print_char('.'); + show_context(); + } + if (haltonerrorp) { + history = fatal_error_stop; + jump_out(); + } + if (interaction == error_stop_mode) { + /*tex Get user's advice and |return|. */ + while (1) { + CONTINUE: + clear_for_error_prompt(); + prompt_input("? "); + if (last == first) + return; + c = buffer[first]; + if (c >= 'a') + c = c + 'A' - 'a'; + /*tex + Interpret code |c| and |return| if done. It is desirable to + provide an `\.E' option here that gives the user an easy way + to return from \TeX\ to the system editor, with the offending + line ready to be edited. But such an extension requires some + system wizardry, so the present implementation simply types + out the name of the file that should be edited and the + relevant line number. + */ + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (deletions_allowed) { + /*tex + Delete |c-"0"| tokens and |goto continue|. We allow + deletion of up to 99 tokens at a time. + */ + s1 = cur_tok; + s2 = cur_cmd; + s3 = cur_chr; + s4 = align_state; + align_state = 1000000; + OK_to_interrupt = false; + if ((last > first + 1) && (buffer[first + 1] >= '0') + && (buffer[first + 1] <= '9')) + c = c * 10 + buffer[first + 1] - '0' * 11; + else + c = c - '0'; + while (c > 0) { + /*tex One-level recursive call of |error| is possible. */ + get_token(); + decr(c); + } + cur_tok = s1; + cur_cmd = s2; + cur_chr = s3; + align_state = s4; + OK_to_interrupt = true; + help2( + "I have just deleted some text, as you asked.", + "You can now delete more, or insert, or whatever." + ); + show_context(); + goto CONTINUE; + } + break; + case 'E': + if (base_ptr > 0) { + int callback_id = callback_defined(call_edit_callback); + if (callback_id>0) { + (void)run_callback(callback_id, "Sd->", makecstring(input_stack[base_ptr].name_field), line); + /*tex This should not be reached. */ + jump_out(); + } else { + tprint_nl("You want to edit file "); + print(input_stack[base_ptr].name_field); + tprint(" at line "); + print_int(line); + interaction = scroll_mode; + if (kpse_init) { + luatex_calledit(base_ptr, line); + } else { + /*tex This should not be reached. */ + tprint_nl("There is no valid callback defined."); + jump_out(); + } + } + } + break; + case 'H': + /*tex Print the help information and |goto continue| */ + if (use_err_help) { + give_err_help(); + } else { + if (help_line[0] == NULL) { + help2( + "Sorry, I don't know how to help in this situation.", + "Maybe you should try asking a human?" + ); + } + i = 0; + while (help_line[i] != NULL) + tprint_nl(help_line[i++]); + help4( + "Sorry, I already gave what help I could...", + "Maybe you should try asking a human?", + "An error might have occurred before I noticed any problems.", + "``If all else fails, read the instructions.''" + ); + goto CONTINUE; + } + break; + case 'I': + /*tex + + Introduce new material from the terminal and |return|. When + the following code is executed, |buffer[(first+1)..(last-1)]| + may contain the material inserted by the user; otherwise + another prompt will be given. In order to understand this + part of the program fully, you need to be familiar with + \TeX's input stacks. + + We enter a new syntactic level for terminal input: + + */ + begin_file_reading(); + /*tex + Now |state=mid_line|, so an initial blank space will count as + a blank. + */ + if (last > first + 1) { + iloc = first + 1; + buffer[first] = ' '; + } else { + prompt_input("insert>"); + iloc = first; + } + first = last; + /*tex No |end_line_char| ends this line. */ + ilimit = last - 1; + return; + break; + case 'Q': + case 'R': + case 'S': + /*tex + + Change the interaction level and |return|. Here the author of + \TeX\ apologizes for making use of the numerical relation + between |"Q"|, |"R"|, |"S"|, and the desired interaction + settings |batch_mode|, |nonstop_mode|, |scroll_mode|. + + */ + error_count = 0; + interaction = batch_mode + c - 'Q'; + tprint("OK, entering "); + switch (c) { + case 'Q': + tprint_esc("batchmode"); + decr(selector); + break; + case 'R': + tprint_esc("nonstopmode"); + break; + case 'S': + tprint_esc("scrollmode"); + break; + } + tprint("..."); + print_ln(); + update_terminal(); + return; + break; + case 'X': + interaction = scroll_mode; + jump_out(); + break; + default: + break; + } + if (!use_err_help) { + /* Print the menu of available options */ + tprint("Type <return> to proceed, S to scroll future error messages,"); + tprint_nl("R to run without stopping, Q to run quietly,"); + tprint_nl("I to insert something, "); + if (base_ptr > 0) + tprint("E to edit your file,"); + if (deletions_allowed) + tprint_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); + tprint_nl("H for help, X to quit."); + } + use_err_help = false; + } + + } + incr(error_count); + if (error_count == 100) { + tprint_nl("(That makes 100 errors; please try again.)"); + history = fatal_error_stop; + jump_out(); + } + /*tex Put help message on the transcript file. */ + if (interaction > batch_mode) { + /*tex Avoid terminal output: */ + decr(selector); + } + if (use_err_help) { + print_ln(); + give_err_help(); + } else { + int i1 = 0; + while (help_line[i1] != NULL) + tprint_nl(help_line[i1++]); + } + print_ln(); + if (interaction > batch_mode) { + /*tex Re-enable terminal output: */ + incr(selector); + } + print_ln(); +} + +/*tex + +A dozen or so error messages end with a parenthesized integer, so we save a teeny +bit of program space by declaring the following procedure: + +*/ + +void int_error(int n) +{ + tprint(" ("); + print_int(n); + print_char(')'); + error(); +} + +/*tex + +In anomalous cases, the print selector might be in an unknown state; the +following subroutine is called to fix things just enough to keep running a bit +longer. + +*/ + +void normalize_selector(void) +{ + if (log_opened_global) + selector = term_and_log; + else + selector = term_only; + if (job_name == 0) + open_log_file(); + if (interaction == batch_mode) + decr(selector); +} + +/*tex + +The following procedure prints \TeX's last words before dying. + +*/ + +void succumb(void) +{ + if (interaction == error_stop_mode) { + /*tex No more interaction: */ + interaction = scroll_mode; + } + if (log_opened_global) { + error(); + } + history = fatal_error_stop; + /*tex Irrecoverable error: */ + jump_out(); +} + +/*tex + +This prints |s|, and that's it. + +*/ + +void fatal_error(const char *s) +{ + normalize_selector(); + print_err("Emergency stop"); + help1(s); + succumb(); +} + +/*tex + +Here is the most dreaded error message. We stop due to finiteness. + +*/ + +void overflow(const char *s, unsigned int n) +{ + normalize_selector(); + print_err("TeX capacity exceeded, sorry ["); + tprint(s); + print_char('='); + print_int((int) n); + print_char(']'); + if (varmem == NULL) { + print_err("Sorry, I ran out of memory."); + print_ln(); + exit(EXIT_FAILURE); + } + help2( + "If you really absolutely need more capacity,", + "you can ask a wizard to enlarge me." + ); + succumb(); +} + +/*tex + +The program might sometime run completely amok, at which point there is no choice +but to stop. If no previous error has been detected, that's bad news; a message +is printed that is really intended for the \TeX\ maintenance person instead of +the user (unless the user has been particularly diabolical). The index entries +for `this can't happen' may help to pinpoint the problem. @^dry rot@> + +*/ + +void confusion(const char *s) +{ /* consistency check violated; |s| tells where */ + normalize_selector(); + if (history < error_message_issued) { + print_err("This can't happen ("); + tprint(s); + print_char(')'); + help1( + "I'm broken. Please show this to someone who can fix" + ); + } else { + print_err("I can't go on meeting you like this"); + help2( + "One of your faux pas seems to have wounded me deeply...", + "in fact, I'm barely conscious. Please fix it and try again." + ); + } + succumb(); +} + +/*tex + +Users occasionally want to interrupt \TeX\ while it's running. If the runtime +system allows this, one can implement a routine that sets the global variable +|interrupt| to some nonzero value when such an interrupt is signalled. Otherwise +there is probably at least a way to make |interrupt| nonzero using the debugger. +@^system dependencies@> @^debugging@> + +*/ + +void check_interrupt(void) +{ + if (interrupt != 0) + pause_for_instructions(); +} + +/*tex + +When an interrupt has been detected, the program goes into its highest +interaction level and lets the user have nearly the full flexibility of the +|error| routine. \TeX\ checks for interrupts only at times when it is safe to do +this. + +*/ + +void pause_for_instructions(void) +{ + if (OK_to_interrupt) { + interaction = error_stop_mode; + if ((selector == log_only) || (selector == no_print)) + incr(selector); + print_err("Interruption"); + help3( + "You rang?", + "Try to insert some instructions for me (e.g.,`I\\showlists'),", + "unless you just want to quit by typing `X'." + ); + deletions_allowed = false; + error(); + deletions_allowed = true; + interrupt = 0; + } +} + +void tex_error(const char *msg, const char **hlp) +{ + print_err(msg); + if (hlp != NULL) { + int i; + for (i = 0; (hlp[i] != NULL && i <= 5); i++) { + help_line[i] = hlp[i]; + } + help_line[i] = NULL; + } else { + help_line[0] = NULL; + } + error(); +} + +/*tex + +The |back_error| routine is used when we want to replace an offending token just +before issuing an error message. This routine, like |back_input|, requires that +|cur_tok| has been set. We disable interrupts during the call of |back_input| so +that the help message won't be lost. + +*/ + +void back_error(void) +{ + OK_to_interrupt = false; + back_input(); + OK_to_interrupt = true; + error(); +} + +/*tex + + Back up one inserted token and call |error|. +*/ + +void ins_error(void) +{ + OK_to_interrupt = false; + back_input(); + token_type = inserted; + OK_to_interrupt = true; + error(); +} + +/*tex + +When \TeX\ wants to typeset a character that doesn't exist, the character node is +not created; thus the output routine can assume that characters exist when it +sees them. The following procedure prints a warning message unless the user has +suppressed it. + +*/ + +void char_warning(internal_font_number f, int c) +{ + /*tex saved value of |tracing_online| */ + int old_setting; + /* index to current digit; we assume that $0\L n<16^{22}$ */ + int k; + if (tracing_lost_chars_par > 0) { + old_setting = tracing_online_par; + if (tracing_lost_chars_par > 1) + tracing_online_par = 1; + begin_diagnostic(); + tprint_nl("Missing character: There is no "); + print(c); + tprint(" (U+"); + k = 0; + if (c < 16) + print_char('0'); + if (c < 256) + print_char('0'); + if (c < 4096) + print_char('0'); + do { + dig[k] = c % 16; + c = c / 16; + incr(k); + } while (c != 0); + print_the_digs((eight_bits) k); + tprint(") in font "); + print_font_name(f); + print_char('!'); + end_diagnostic(false); + tracing_online_par = old_setting; + } +} + +void wrapup_backend(void) { + ensure_output_state(static_pdf, ST_OMODE_FIX); + if (output_mode_used == OMODE_NONE) { + print_err(" ==> Fatal error occurred, no FMT file produced!"); + } else { + backend_out_control[backend_control_finish_file](static_pdf,history == fatal_error_stop); + } +} + +void normal_error(const char *t, const char *p) +{ + normalize_selector(); + if (interaction == error_stop_mode) { + wake_up_terminal(); + } + if (filelineerrorstylep) { + print_file_line(); + } else { + tprint_nl("! "); + } + tprint("error: "); + if (cur_file_name) { + tprint(" (file "); + tprint(cur_file_name); + tprint(")"); + } + if (t != NULL) { + tprint(" ("); + tprint(t); + tprint(")"); + } + tprint(": "); + if (p != NULL) + tprint(p); + history = fatal_error_stop; + wrapup_backend(); + exit(EXIT_FAILURE); +} + +void normal_warning(const char *t, const char *p) +{ + int report_id ; + if (strcmp(t,"lua") == 0) { + int saved_new_line_char; + saved_new_line_char = new_line_char_par; + new_line_char_par = 10; + report_id = callback_defined(show_lua_error_hook_callback); + if (report_id == 0) { + tprint(p); + help2( + "The lua interpreter ran into a problem, so the", + "remainder of this lua chunk will be ignored." + ); + } else { + (void) run_callback(report_id, "->"); + } + error(); + new_line_char_par = saved_new_line_char; + } else { + report_id = callback_defined(show_warning_message_callback); + if (report_id > 0) { + /*tex Free the last ones, */ + xfree(last_warning_str); + xfree(last_warning_tag); + last_warning_str = (string) xmalloc(strlen(p) + 1); + last_warning_tag = (string) xmalloc(strlen(t) + 1); + strcpy(last_warning_str,p); + strcpy(last_warning_tag,t); + run_callback(report_id, "->"); + } else { + print_ln(); + tprint("warning "); + if (cur_file_name) { + tprint(" (file "); + tprint(cur_file_name); + tprint(")"); + } + if (t != NULL) { + tprint(" ("); + tprint(t); + tprint(")"); + } + tprint(": "); + if (p != NULL) + tprint(p); + print_ln(); + } + if (history == spotless) + history = warning_issued; + } +} + +static char print_buf[PRINTF_BUF_SIZE]; + +__attribute__ ((format(printf, 2,3))) +void formatted_error(const char *t, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); + normal_error(t,print_buf); + va_end(args); +} + +__attribute__ ((format(printf, 2,3))) +void formatted_warning(const char *t, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); + normal_warning(t,print_buf); + va_end(args); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/errors.w b/Build/source/texk/web2c/luatexdir/tex/errors.w deleted file mode 100644 index 93dbe141b91..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/errors.w +++ /dev/null @@ -1,972 +0,0 @@ -% errors.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ TODO: we need to use a formatted normal_error in: - -hyphen.w -luafflib.c - -@ @c -#include "ptexlib.h" -#define edit_var "TEXEDIT" - -@ When something anomalous is detected, \TeX\ typically does something like this: -$$\vbox{\halign{#\hfil\cr -|print_err("Something anomalous has been detected");|\cr -|help3("This is the first line of my offer to help.")|\cr -|("This is the second line. I'm trying to")|\cr -|("explain the best way for you to proceed.");|\cr -|error;|\cr}}$$ -A two-line help message would be given using |help2|, etc.; these informal -helps should use simple vocabulary that complements the words used in the -official error message that was printed. (Outside the U.S.A., the help -messages should preferably be translated into the local vernacular. Each -line of help is at most 60 characters long, in the present implementation, -so that |max_print_line| will not be exceeded.) - -The |print_err| procedure supplies a `\.!' before the official message, -and makes sure that the terminal is awake if a stop is going to occur. -The |error| procedure supplies a `\..' after the official message, then it -shows the location of the error; and if |interaction=error_stop_mode|, -it also enters into a dialog with the user, during which time the help -message may be printed. -@^system dependencies@> - -@c -int interaction; /* current level of interaction */ -int interactionoption; /* set from command line */ - -/* ls-hh: so, new code only kicks in when we have a callback defined */ - -char *last_error = NULL; -char *last_lua_error = NULL; -char *last_warning_tag = NULL; -char *last_warning_str = NULL; -char *last_error_context = NULL; - -int err_old_setting = 0 ; -int in_error = 0 ; - -void set_last_error_context(void) -{ - str_number str; - int sel = selector; - int saved_new_line_char; - int saved_new_string_line; - selector = new_string; - saved_new_line_char = new_line_char_par; - saved_new_string_line = new_string_line; - new_line_char_par = 10; - new_string_line = 10; - show_context(); - xfree(last_error_context); - str = make_string(); - last_error_context = makecstring(str); - flush_str(str); - selector = sel; - new_line_char_par = saved_new_line_char; - new_string_line = saved_new_string_line; - return; -} - -void flush_err(void) -{ - str_number s_error; - char *s = NULL; - int callback_id ; - if (in_error) { - selector = err_old_setting; - str_room(1); - s_error = make_string(); - s = makecstring(s_error); - flush_str(s_error); - if (interaction == error_stop_mode) { - wake_up_terminal(); - } - xfree(last_error); - last_error = (string) xmalloc((unsigned) (strlen(s) + 1)); - strcpy(last_error,s); - callback_id = callback_defined(show_error_message_callback); - if (callback_id > 0) { - run_callback(callback_id, "->"); - } else { - tprint(s); - } - in_error = 0 ; - } -} - -void print_err(const char *s) -{ - int callback_id = callback_defined(show_error_message_callback); - if (interaction == error_stop_mode) { - wake_up_terminal(); - } - if (callback_id > 0) { - err_old_setting = selector; - selector = new_string; - in_error = 1 ; - } - if (filelineerrorstylep) { - print_file_line(); - } else { - tprint_nl("! "); - } - tprint(s); - if (callback_id <= 0) { - xfree(last_error); - last_error = (string) xmalloc((unsigned) (strlen(s) + 1)); - strcpy(last_error,s); - } -} - -@ \TeX\ is careful not to call |error| when the print |selector| setting -might be unusual. The only possible values of |selector| at the time of -error messages are - -\yskip -\hang|no_print| (when |interaction=batch_mode| and |log_file| not yet open); - -\hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open); - -\hang|log_only| (when |interaction=batch_mode| and |log_file| is open); - -\hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open). - -@c -void fixup_selector(boolean logopened) -{ - if (interaction == batch_mode) - selector = no_print; - else - selector = term_only; - if (logopened) - selector = selector + 2; -} - -@ A global variable |deletions_allowed| is set |false| if the |get_next| -routine is active when |error| is called; this ensures that |get_next| -and related routines like |get_token| will never be called recursively. -A similar interlock is provided by |set_box_allowed|. -@^recursion@> - -The global variable |history| records the worst level of error that -has been detected. It has four possible values: |spotless|, |warning_issued|, -|error_message_issued|, and |fatal_error_stop|. - -Another global variable, |error_count|, is increased by one when an -|error| occurs without an interactive dialog, and it is reset to zero at -the end of every paragraph. If |error_count| reaches 100, \TeX\ decides -that there is no point in continuing further. - -@c -boolean deletions_allowed; /* is it safe for |error| to call |get_token|? */ -boolean set_box_allowed; /* is it safe to do a \.{\\setbox} assignment? */ -int history; /* has the source input been clean so far? */ -int error_count; /* the number of scrolled errors since the last paragraph ended */ -int interrupt; /* should \TeX\ pause for instructions? */ -boolean OK_to_interrupt; /* should interrupts be observed? */ - -@ The value of |history| is initially |fatal_error_stop|, but it will -be changed to |spotless| if \TeX\ survives the initialization process. - -@c -void initialize_errors(void) -{ - if (interactionoption == unspecified_mode) - interaction = error_stop_mode; - else - interaction = interactionoption; - deletions_allowed = true; - set_box_allowed = true; - OK_to_interrupt = true; - /* |history| is initialized elsewhere */ -} - -@ It is possible for |error| to be called recursively if some error arises -when |get_token| is being used to delete a token, and/or if some fatal error -occurs while \TeX\ is trying to fix a non-fatal one. But such recursion -@^recursion@> -is never more than two levels deep. - -@ Individual lines of help are recorded in the array |help_line|. - -@c -const char *help_line[7]; /* helps for the next |error| */ -boolean use_err_help; /* should the |err_help| list be shown? */ - -@ The |jump_out| procedure just cuts across all active procedure levels and -exits the program. It is used when there is no recovery from a particular error. - -@c -int defaultexitcode = 0; /* the exit code can be overloaded */ - -__attribute__ ((noreturn)) -void do_final_end(void) -{ - update_terminal(); - ready_already = 0; - lua_close(Luas); /* new per 0.99 */ - if ((history != spotless) && (history != warning_issued)) - uexit(1); - else - uexit(defaultexitcode); -} - -__attribute__ ((noreturn)) -void jump_out(void) -{ - close_files_and_terminate(); - do_final_end(); -} -@ Here is the function that calls the editor, if one is defined. This -is loosely based on a similar function in kpathsea, but the calling -convention is quite different. - -@c -static const_string edit_value = EDITOR; - -#if defined(WIN32) -static int -Isspace (char c) -{ - return (c == ' ' || c == '\t'); -} -#endif /* WIN32 */ - -__attribute__ ((noreturn)) -static void luatex_calledit (int baseptr, int linenumber) -{ - char *temp, *command, *fullcmd; - char c; - int sdone, ddone, i; - char *filename = makecstring(input_stack[base_ptr].name_field); - int fnlength = strlen(filename); - -#ifdef WIN32 - char *fp, *ffp, *env, editorname[256], buffer[256]; - int cnt = 0; - int dontchange = 0; -#endif - - sdone = ddone = 0; - - /* Close any open input files, since we're going to kill the job. */ - close_files_and_terminate(); - - /* Replace the default with the value of the appropriate environment - variable or config file value, if it's set. */ - temp = kpse_var_value (edit_var); - if (temp != NULL) - edit_value = temp; - - /* Construct the command string. The `11' is the maximum length an - integer might be. */ - command = xmalloc (strlen (edit_value) + fnlength + 11); - - /* So we can construct it as we go. */ - temp = command; - -#ifdef WIN32 - fp = editorname; - if ((isalpha(*edit_value) && *(edit_value + 1) == ':' - && IS_DIR_SEP (*(edit_value + 2))) - || (*edit_value == '"' && isalpha(*(edit_value + 1)) - && *(edit_value + 2) == ':' - && IS_DIR_SEP (*(edit_value + 3))) - ) - dontchange = 1; -#endif - - while ((c = *edit_value++) != 0) - { - if (c == '%') - { - switch (c = *edit_value++) - { - case 'd': - if (ddone) - FATAL1 ("call_edit: `%%d' appears twice in editor command: `%s'", edit_value); - sprintf (temp, "%ld", (long int)linenumber); - while (*temp != '\0') - temp++; - ddone = 1; - break; - - case 's': - if (sdone) - FATAL1 ("call_edit: `%%s' appears twice in editor command: `%s'", edit_value); - for (i =0; i < fnlength; i++) - *temp++ = filename[i]; - sdone = 1; - break; - - case '\0': - *temp++ = '%'; - /* Back up to the null to force termination. */ - edit_value--; - break; - - default: - *temp++ = '%'; - *temp++ = c; - break; - } - } - else { -#ifdef WIN32 - if (dontchange) - *temp++ = c; - else { if(Isspace(c) && cnt == 0) { - cnt++; - temp = command; - *temp++ = c; - *fp = '\0'; - } else if(!Isspace(c) && cnt == 0) { - *fp++ = c; - } else { - *temp++ = c; - } - } -#else - *temp++ = c; -#endif - } - } - - *temp = 0; - -#ifdef WIN32 - if (dontchange == 0) { - if(editorname[0] == '.' || - editorname[0] == '/' || - editorname[0] == '\\') { - fprintf(stderr, "%s is not allowed to execute.\n", editorname); - do_final_end(); - } - env = (char *)getenv("PATH"); - if(SearchPath(env, editorname, ".exe", 256, buffer, &ffp)==0) { - if(SearchPath(env, editorname, ".bat", 256, buffer, &ffp)==0) { - fprintf(stderr, "I cannot find %s in the PATH.\n", editorname); - do_final_end(); - } - } - fullcmd = (char *)xmalloc(strlen(buffer)+strlen(command)+5); - strcpy(fullcmd, "\""); - strcat(fullcmd, buffer); - strcat(fullcmd, "\""); - strcat(fullcmd, command); - } else -#endif - fullcmd = command; - - /* Execute the command. */ - if (system (fullcmd) != 0) - fprintf (stderr, "! Trouble executing `%s'.\n", command); - - /* Quit, since we found an error. */ - do_final_end (); -} - - -@ @c -void error(void) -{ /* completes the job of error reporting */ - ASCII_code c; /* what the user types */ - int callback_id; - int s1, s2, s3, s4; /* used to save global variables when deleting tokens */ - int i; - flush_err(); /* hh-ls */ - if (history < error_message_issued) - history = error_message_issued; - callback_id = callback_defined(show_error_hook_callback); - if (callback_id > 0) { - set_last_error_context(); - run_callback(callback_id, "->"); - } else { - print_char('.'); - show_context(); - } - if (haltonerrorp) { - history = fatal_error_stop; - jump_out(); - } - if (interaction == error_stop_mode) { - /* Get user's advice and |return| */ - while (1) { - CONTINUE: - clear_for_error_prompt(); - prompt_input("? "); - if (last == first) - return; - c = buffer[first]; - if (c >= 'a') - c = c + 'A' - 'a'; /* convert to uppercase */ - /* Interpret code |c| and |return| if done */ - - /* It is desirable to provide an `\.E' option here that gives the user - an easy way to return from \TeX\ to the system editor, with the offending - line ready to be edited. But such an extension requires some system - wizardry, so the present implementation simply types out the name of the - file that should be edited and the relevant line number. - - There is a secret `\.D' option available when the debugging routines haven't - been commented~out. */ - - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (deletions_allowed) { - /* Delete |c-"0"| tokens and |goto continue| */ - /* We allow deletion of up to 99 tokens at a time */ - s1 = cur_tok; - s2 = cur_cmd; - s3 = cur_chr; - s4 = align_state; - align_state = 1000000; - OK_to_interrupt = false; - if ((last > first + 1) && (buffer[first + 1] >= '0') - && (buffer[first + 1] <= '9')) - c = c * 10 + buffer[first + 1] - '0' * 11; - else - c = c - '0'; - while (c > 0) { - get_token(); /* one-level recursive call of |error| is possible */ - decr(c); - } - cur_tok = s1; - cur_cmd = s2; - cur_chr = s3; - align_state = s4; - OK_to_interrupt = true; - help2("I have just deleted some text, as you asked.", - "You can now delete more, or insert, or whatever."); - show_context(); - goto CONTINUE; - } - break; -#ifdef DEBUG - case 'D': - debug_help(); - goto CONTINUE; - break; -#endif - case 'E': - if (base_ptr > 0) { - int callback_id = callback_defined(call_edit_callback); - if (callback_id>0) { - (void)run_callback(callback_id, "Sd->", makecstring(input_stack[base_ptr].name_field), line); - jump_out(); /* should not be reached */ - } else { - tprint_nl("You want to edit file "); - print(input_stack[base_ptr].name_field); - tprint(" at line "); - print_int(line); - interaction = scroll_mode; - if (kpse_init) { - luatex_calledit(base_ptr, line); - } else { - tprint_nl("There is no valid callback defined."); - jump_out(); /* should not be reached */ - } - } - } - break; - case 'H': - /* Print the help information and |goto continue| */ - if (use_err_help) { - give_err_help(); - } else { - if (help_line[0] == NULL) { - help2 - ("Sorry, I don't know how to help in this situation.", - "Maybe you should try asking a human?"); - } - i = 0; - while (help_line[i] != NULL) - tprint_nl(help_line[i++]); - help4("Sorry, I already gave what help I could...", - "Maybe you should try asking a human?", - "An error might have occurred before I noticed any problems.", - "``If all else fails, read the instructions.''"); - goto CONTINUE; - } - break; - case 'I': - /* Introduce new material from the terminal and |return| */ - /* When the following code is executed, |buffer[(first+1)..(last-1)]| may - contain the material inserted by the user; otherwise another prompt will - be given. In order to understand this part of the program fully, you need - to be familiar with \TeX's input stacks. */ - - begin_file_reading(); /* enter a new syntactic level for terminal input */ - /* now |state=mid_line|, so an initial blank space will count as a blank */ - if (last > first + 1) { - iloc = first + 1; - buffer[first] = ' '; - } else { - prompt_input("insert>"); - iloc = first; - } - first = last; - ilimit = last - 1; /* no |end_line_char| ends this line */ - return; - break; - case 'Q': - case 'R': - case 'S': - /* Change the interaction level and |return| */ - /* Here the author of \TeX\ apologizes for making use of the numerical - relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings - |batch_mode|, |nonstop_mode|, |scroll_mode|. */ - error_count = 0; - interaction = batch_mode + c - 'Q'; - tprint("OK, entering "); - switch (c) { - case 'Q': - tprint_esc("batchmode"); - decr(selector); - break; - case 'R': - tprint_esc("nonstopmode"); - break; - case 'S': - tprint_esc("scrollmode"); - break; - } - tprint("..."); - print_ln(); - update_terminal(); - return; - break; - case 'X': - interaction = scroll_mode; - jump_out(); - break; - default: - break; - } - if (!use_err_help) { - /* Print the menu of available options */ - tprint("Type <return> to proceed, S to scroll future error messages,"); - tprint_nl("R to run without stopping, Q to run quietly,"); - tprint_nl("I to insert something, "); - if (base_ptr > 0) - tprint("E to edit your file,"); - if (deletions_allowed) - tprint_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); - tprint_nl("H for help, X to quit."); - } - use_err_help = false; - } - - } - incr(error_count); - if (error_count == 100) { - tprint_nl("(That makes 100 errors; please try again.)"); - history = fatal_error_stop; - jump_out(); - } - /* Put help message on the transcript file */ - if (interaction > batch_mode) - decr(selector); /* avoid terminal output */ - if (use_err_help) { - print_ln(); - give_err_help(); - } else { - int i1 = 0; - while (help_line[i1] != NULL) - tprint_nl(help_line[i1++]); - } - print_ln(); - if (interaction > batch_mode) - incr(selector); /* re-enable terminal output */ - print_ln(); -} - -@ A dozen or so error messages end with a parenthesized integer, so we -save a teeny bit of program space by declaring the following procedure: - -@c -void int_error(int n) -{ - tprint(" ("); - print_int(n); - print_char(')'); - error(); -} - -@ In anomalous cases, the print selector might be in an unknown state; -the following subroutine is called to fix things just enough to keep -running a bit longer. - -@c -void normalize_selector(void) -{ - if (log_opened_global) - selector = term_and_log; - else - selector = term_only; - if (job_name == 0) - open_log_file(); - if (interaction == batch_mode) - decr(selector); -} - -@ The following procedure prints \TeX's last words before dying -@c -void succumb(void) -{ - if (interaction == error_stop_mode) - interaction = scroll_mode; /* no more interaction */ - if (log_opened_global) - error(); -#ifdef DEBUG - if (interaction > batch_mode) - debug_help(); -#endif - history = fatal_error_stop; - jump_out(); /* irrecoverable error */ -} - -@ @c -void fatal_error(const char *s) -{ /* prints |s|, and that's it */ - normalize_selector(); - print_err("Emergency stop"); - help1(s); - succumb(); -} - -@ Here is the most dreaded error message -@c -void overflow(const char *s, unsigned int n) -{ /* stop due to finiteness */ - normalize_selector(); - print_err("TeX capacity exceeded, sorry ["); - tprint(s); - print_char('='); - print_int((int) n); - print_char(']'); - if (varmem == NULL) { - print_err("Sorry, I ran out of memory."); - print_ln(); - exit(EXIT_FAILURE); - } - help2("If you really absolutely need more capacity,", - "you can ask a wizard to enlarge me."); - succumb(); -} - -@ The program might sometime run completely amok, at which point there is -no choice but to stop. If no previous error has been detected, that's bad -news; a message is printed that is really intended for the \TeX\ -maintenance person instead of the user (unless the user has been -particularly diabolical). The index entries for `this can't happen' may -help to pinpoint the problem. -@^dry rot@> - -@c -void confusion(const char *s) -{ /* consistency check violated; |s| tells where */ - normalize_selector(); - if (history < error_message_issued) { - print_err("This can't happen ("); - tprint(s); - print_char(')'); - help1("I'm broken. Please show this to someone who can fix can fix"); - } else { - print_err("I can't go on meeting you like this"); - help2("One of your faux pas seems to have wounded me deeply...", - "in fact, I'm barely conscious. Please fix it and try again."); - } - succumb(); -} - -@ Users occasionally want to interrupt \TeX\ while it's running. -If the runtime system allows this, one can implement -a routine that sets the global variable |interrupt| to some nonzero value -when such an interrupt is signalled. Otherwise there is probably at least -a way to make |interrupt| nonzero using the debugger. -@^system dependencies@> -@^debugging@> - -@c -void check_interrupt(void) -{ - if (interrupt != 0) - pause_for_instructions(); -} - -@ When an interrupt has been detected, the program goes into its -highest interaction level and lets the user have nearly the full flexibility of -the |error| routine. \TeX\ checks for interrupts only at times when it is -safe to do this. - -@c -void pause_for_instructions(void) -{ - if (OK_to_interrupt) { - interaction = error_stop_mode; - if ((selector == log_only) || (selector == no_print)) - incr(selector); - print_err("Interruption"); - help3("You rang?", - "Try to insert some instructions for me (e.g.,`I\\showlists'),", - "unless you just want to quit by typing `X'."); - deletions_allowed = false; - error(); - deletions_allowed = true; - interrupt = 0; - } -} - -@ @c -void tex_error(const char *msg, const char **hlp) -{ - print_err(msg); - if (hlp != NULL) { - int i; - for (i = 0; (hlp[i] != NULL && i <= 5); i++) { - help_line[i] = hlp[i]; - } - help_line[i] = NULL; - } else { - help_line[0] = NULL; - } - error(); -} - -@ The |back_error| routine is used when we want to replace an offending token -just before issuing an error message. This routine, like |back_input|, -requires that |cur_tok| has been set. We disable interrupts during the -call of |back_input| so that the help message won't be lost. - -@c -void back_error(void) -{ /* back up one token and call |error| */ - OK_to_interrupt = false; - back_input(); - OK_to_interrupt = true; - error(); -} - -@ @c -void ins_error(void) -{ /* back up one inserted token and call |error| */ - OK_to_interrupt = false; - back_input(); - token_type = inserted; - OK_to_interrupt = true; - error(); -} - -@ When \TeX\ wants to typeset a character that doesn't exist, the -character node is not created; thus the output routine can assume -that characters exist when it sees them. The following procedure -prints a warning message unless the user has suppressed it. - -@c -void char_warning(internal_font_number f, int c) -{ - int old_setting; /* saved value of |tracing_online| */ - int k; /* index to current digit; we assume that $0\L n<16^{22}$ */ - if (tracing_lost_chars_par > 0) { - old_setting = tracing_online_par; - if (tracing_lost_chars_par > 1) - tracing_online_par = 1; - begin_diagnostic(); - tprint_nl("Missing character: There is no "); - print(c); - tprint(" (U+"); - k = 0; - if (c < 16) - print_char('0'); - if (c < 256) - print_char('0'); - if (c < 4096) - print_char('0'); - do { - dig[k] = c % 16; - c = c / 16; - incr(k); - } while (c != 0); - print_the_digs((eight_bits) k); - tprint(") in font "); - print_font_name(f); - print_char('!'); - end_diagnostic(false); - tracing_online_par = old_setting; - } -} - -@ @c - -void wrapup_backend(void) { - ensure_output_state(static_pdf, ST_OMODE_FIX); - switch (output_mode_used) { - case OMODE_NONE: - print_err(" ==> Fatal error occurred, no FMT file produced!"); - break; - case OMODE_PDF: - if (history == fatal_error_stop) { - remove_pdffile(static_pdf); /* will become remove_output_file */ - print_err(" ==> Fatal error occurred, no output PDF file produced!"); - } else { - finish_pdf_file(static_pdf, luatex_version, get_luatexrevision()); - } - break; - case OMODE_DVI: - if (history == fatal_error_stop) { - print_err(" ==> Fatal error occurred, bad output DVI file produced!"); - finish_dvi_file(static_pdf, luatex_version, get_luatexrevision()); - } else { - finish_dvi_file(static_pdf, luatex_version, get_luatexrevision()); - } - break; - } -} - -void normal_error(const char *t, const char *p) -{ - normalize_selector(); - if (interaction == error_stop_mode) { - wake_up_terminal(); - } - if (filelineerrorstylep) { - print_file_line(); - } else { - tprint_nl("! "); - } - tprint("error: "); - if (cur_file_name) { - tprint(" (file "); - tprint(cur_file_name); - tprint(")"); - } - if (t != NULL) { - tprint(" ("); - tprint(t); - tprint(")"); - } - tprint(": "); - if (p != NULL) - tprint(p); - history = fatal_error_stop; - wrapup_backend(); - exit(EXIT_FAILURE); -} - -/* -void normal_error(const char *t, const char *p) -{ - normalize_selector(); - if (interaction == error_stop_mode) { - wake_up_terminal(); - } - tprint("error : "); - if (p != NULL) - tprint(p); - history = fatal_error_stop; - wrapup_backend(); - exit(EXIT_FAILURE); -} -*/ - -@ @c -void normal_warning(const char *t, const char *p) -{ - int report_id ; - if (strcmp(t,"lua") == 0) { - int saved_new_line_char; - saved_new_line_char = new_line_char_par; - new_line_char_par = 10; - report_id = callback_defined(show_lua_error_hook_callback); - if (report_id == 0) { - tprint(p); - help2("The lua interpreter ran into a problem, so the", - "remainder of this lua chunk will be ignored."); - } else { - (void) run_callback(report_id, "->"); - } - error(); - new_line_char_par = saved_new_line_char; - } else { - report_id = callback_defined(show_warning_message_callback); - if (report_id > 0) { - /* free last ones */ - xfree(last_warning_str); - xfree(last_warning_tag); - last_warning_str = (string) xmalloc(strlen(p) + 1); - last_warning_tag = (string) xmalloc(strlen(t) + 1); - strcpy(last_warning_str,p); - strcpy(last_warning_tag,t); - run_callback(report_id, "->"); - } else { - print_ln(); - tprint("warning "); - if (cur_file_name) { - tprint(" (file "); - tprint(cur_file_name); - tprint(")"); - } - if (t != NULL) { - tprint(" ("); - tprint(t); - tprint(")"); - } - tprint(": "); - if (p != NULL) - tprint(p); - print_ln(); - } - if (history == spotless) - history = warning_issued; - } -} - -@ @c -static char print_buf[PRINTF_BUF_SIZE]; -__attribute__ ((format(printf, 2,3))) -void formatted_error(const char *t, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); - normal_error(t,print_buf); - va_end(args); -} - -__attribute__ ((format(printf, 2,3))) -void formatted_warning(const char *t, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - vsnprintf(print_buf, PRINTF_BUF_SIZE, fmt, args); - normal_warning(t,print_buf); - va_end(args); -} diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.c b/Build/source/texk/web2c/luatexdir/tex/expand.c new file mode 100644 index 00000000000..a0a317db238 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/expand.c @@ -0,0 +1,967 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + + Only a dozen or so command codes |>max_command| can possibly be returned by + |get_next|; in increasing order, they are |undefined_cs|, |expand_after|, + |no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|, + |top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and + |end_template|. + + Sometimes, recursive calls to the following |expand| routine may cause + exhaustion of the run-time calling stack, resulting in forced execution stops + by the operating system. To diminish the chance of this happening, a counter + is used to keep track of the recursion depth, in conjunction with a constant + called |expand_depth|. + + Note that this does not catch all possible infinite recursion loops, just the + ones that exhaust the application calling stack. The actual maximum value of + |expand_depth| is outside of our control, but the initial setting of |100| + should be enough to prevent problems. + +*/ + +static int expand_depth_count = 0; + +/*tex + + The |expand| subroutine is used when |cur_cmd>max_command|. It removes a + ``call'' or a conditional or one of the other special operations just listed. + It follows that |expand| might invoke itself recursively. In all cases, + |expand| destroys the current token, but it sets things up so that the next + |get_next| will deliver the appropriate next token. The value of |cur_tok| + need not be known when |expand| is called. + + Since several of the basic scanning routines communicate via global + variables, their values are saved as local variables of |expand| so that + recursive calls don't invalidate them. + +*/ + +int is_in_csname = 0; + +void expand(void) +{ + /*tex token that is being ``expanded after'' */ + halfword t; + /*tex for list manipulation */ + halfword p; + /*tex for a local token list pointer */ + halfword cur_ptr; + /*tex to save the global quantity |cur_val| */ + int cv_backup; + /*tex to save |cur_val_level|, etc. */ + int cvl_backup, radix_backup, co_backup; + /*tex to save |link(backup_head)| */ + halfword backup_backup; + /*tex temporary storage of |scanner_status| */ + int save_scanner_status; + incr(expand_depth_count); + if (expand_depth_count >= expand_depth) + overflow("expansion depth", (unsigned) expand_depth); + cv_backup = cur_val; + cvl_backup = cur_val_level; + radix_backup = radix; + co_backup = cur_order; + backup_backup = token_link(backup_head); + RESWITCH: + if (cur_cmd < call_cmd) { + /*tex Expand a nonmacro. */ + if (tracing_commands_par > 1) + show_cur_cmd_chr(); + switch (cur_cmd) { + case top_bot_mark_cmd: + /*tex Insert the appropriate mark text into the scanner. */ + t = cur_chr % marks_code; + if (cur_chr >= marks_code) + scan_mark_num(); + else + cur_val = 0; + switch (t) { + case first_mark_code: + cur_ptr = first_mark(cur_val); + break; + case bot_mark_code: + cur_ptr = bot_mark(cur_val); + break; + case split_first_mark_code: + cur_ptr = split_first_mark(cur_val); + break; + case split_bot_mark_code: + cur_ptr = split_bot_mark(cur_val); + break; + default: + cur_ptr = top_mark(cur_val); + break; + } + if (cur_ptr != null) + begin_token_list(cur_ptr, mark_text); + break; + case expand_after_cmd: + if (cur_chr == 0) { + /*tex + + Expand the token after the next token. It takes only a + little shuffling to do what \TeX\ calls + \.{\\expandafter}. + + */ + get_token(); + t = cur_tok; + get_token(); + if (cur_cmd > max_command_cmd) + expand(); + else + back_input(); + cur_tok = t; + back_input(); + } else { + /*tex + + Negate a boolean conditional and |goto reswitch|. The + result of a boolean condition is reversed when the + conditional is preceded by \.{\\unless}. + + */ + get_token(); + if ((cur_cmd == if_test_cmd) && (cur_chr != if_case_code)) { + cur_chr = cur_chr + unless_code; + goto RESWITCH; + } + print_err("You can't use `\\unless' before `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help1("Continue, and I'll forget that it ever happened."); + back_error(); + } + break; + case no_expand_cmd: + if (cur_chr == 0) { + /*tex + + Suppress expansion of the next token. The implementation + of \.{\\noexpand} is a bit trickier, because it is + necessary to insert a special `|dont_expand|' marker into + \TeX's reading mechanism. This special marker is + processed by |get_next|, but it does not slow down the + inner loop. + + Since \.{\\outer} macros might arise here, we must also + clear the |scanner_status| temporarily. + + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + t = cur_tok; + back_input(); + /*tex Now |start| and |loc| point to the backed-up token |t|. */ + if (t >= cs_token_flag) { + p = get_avail(); + set_token_info(p, cs_token_flag + frozen_dont_expand); + set_token_link(p, iloc); + istart = p; + iloc = p; + } + + } else { + /*tex + + The \.{\\primitive} handling. If the primitive meaning of + the next token is an expandable command, it suffices to + replace the current token with the primitive one and + restart |expand|. + + Otherwise, the token we just read has to be pushed back, + as well as a token matching the internal form of + \.{\\primitive}, that is sneaked in as an alternate form + of |ignore_spaces|. + + An implementation problem surfaces: There really is no + |cur_cs| attached to the inserted primitive command, so + it is safer to set |cur_cs| to zero. |cur_tok| has a + similar problem. And for the non-expanded branch, simply + pushing back a token that matches the correct internal + command does not work, because that approach would not + survive roundtripping to a temporary file or even a token + list. + + It would be smart to create |frozen_| versions of all the + primitives. Then, this problem would not happen, at the + expense of a few hundred extra control sequences. + + */ + save_scanner_status = scanner_status; + scanner_status = normal; + get_token(); + scanner_status = save_scanner_status; + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + t = get_prim_eq_type(cur_cs); + if (t > max_command_cmd) { + cur_cmd = t; + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + cur_cs = 0; + goto RESWITCH; + } else { + back_input(); + /*tex Now |loc| and |start| point to a one-item list. */ + p = get_avail(); + set_token_info(p, cs_token_flag + frozen_primitive); + set_token_link(p, iloc); + iloc = p; + istart = p; + } + } else if (suppress_primitive_error_par == 0) { + print_err("Missing primitive name"); + help2( + "The control sequence marked <to be read again> does not", + "represent any known primitive." + ); + back_error(); + } + } + break; + case cs_name_cmd: + /*tex Manufacture a control sequence name. */ + if (cur_chr == 0) { + manufacture_csname(0); + } else if (cur_chr == 1) { + inject_last_tested_cs(); + } else { + manufacture_csname(1); + } + break; + case convert_cmd: + conv_toks(); + break; + case the_cmd: + ins_the_toks(); + break; + case combine_toks_cmd: + combine_the_toks(cur_chr); + break; + case if_test_cmd: + /*tex An experiment. */ + if (cur_chr == if_condition_code) { + break; + } + /*tex End of experiment. */ + conditional(); + break; + case fi_or_else_cmd: + /*tex + + Terminate the current conditional and skip to \.{\\fi} The + processing of conditionals is complete except for the + following code, which is actually part of |expand|. It comes + into play when \.{\\or}, \.{\\else}, or \.{\\fi} is scanned. + + */ + if (tracing_ifs_par > 0) + if (tracing_commands_par <= 1) + show_cur_cmd_chr(); + if (cur_chr > if_limit) { + if (if_limit == if_code) { + /*tex Condition is not yet evaluated. */ + insert_relax(); + } else { + print_err("Extra "); + print_cmd_chr(fi_or_else_cmd, cur_chr); + help1("I'm ignoring this; it doesn't match any \\if."); + error(); + } + } else { + while (cur_chr != fi_code) { + /*tex Skip to \.{\\fi}. */ + pass_text(); + } + pop_condition_stack(); + } + break; + case input_cmd: + /*tex Initiate or terminate input from a file */ + if (cur_chr == 1) + force_eof = true; + else if (cur_chr == 2) + pseudo_start(); + else if (cur_chr == 3) { + pseudo_start(); + iname = 19; + } else if (name_in_progress) + insert_relax(); + else + start_input(); + break; + case lua_expandable_call_cmd: + if (cur_chr <= 0) { + normal_error("luacall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luafunctioncall(cur_chr); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } + break; + case lua_local_call_cmd: + if (cur_chr <= 0) { + normal_error("luacall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + lua_rawgeti(Luas, LUA_REGISTRYINDEX, cur_chr); + lua_call(Luas,0,0); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } + break; + case variable_cmd: + do_variable(); + break; + case feedback_cmd: + do_feedback(); + break; + default: + /*tex Complain about an undefined macro */ + print_err("Undefined control sequence"); + help5( + "The control sequence at the end of the top line", + "of your error message was never \\def'ed. If you have", + "misspelled it (e.g., `\\hobx'), type `I' and the correct", + "spelling (e.g., `I\\hbox'). Otherwise just continue,", + "and I'll forget about whatever was undefined." + ); + error(); + break; + } + } else if (cur_cmd < end_template_cmd) { + macro_call(); + } else { + /*tex + + Insert a token containing |frozen_endv|. An |end_template| command is + effectively changed to an |endv| command by the following code. (The + reason for this is discussed below; the |frozen_end_template| at the + end of the template has passed the |check_outer_validity| test, so + its mission of error detection has been accomplished.) + + */ + cur_tok = cs_token_flag + frozen_endv; + back_input(); + } + cur_val = cv_backup; + cur_val_level = cvl_backup; + radix = radix_backup; + cur_order = co_backup; + set_token_link(backup_head, backup_backup); + decr(expand_depth_count); +} + +void complain_missing_csname(void) +{ + print_err("Missing \\endcsname inserted"); + help2( + "The control sequence marked <to be read again> should", + "not appear between \\csname and \\endcsname." + ); + back_error(); +} + +void manufacture_csname(boolean use) +{ + halfword p, q, r; + lstring *ss; + r = get_avail(); + p = r; + is_in_csname += 1; + do { + get_x_token(); + if (cur_cs == 0) + store_new_token(cur_tok); + } while (cur_cs == 0); + if (cur_cmd != end_cs_name_cmd) { + /*tex Complain about missing \.{\\endcsname}. */ + complain_missing_csname(); + } + /*tex Look up the characters of list |r| in the hash table, and set |cur_cs|. */ + ss = tokenlist_to_lstring(r, true); + is_in_csname -= 1; + if (use) { + if (ss->l > 0) { + cur_cs = string_lookup((char *) ss->s, ss->l); + } else { + cur_cs = null_cs; + } + last_cs_name = cur_cs ; + free_lstring(ss); + flush_list(r); + if (cur_cs == null_cs) { + /*tex skip */ + } else if (eq_type(cur_cs) == undefined_cs_cmd) { + /*tex skip */ + } else { + cur_tok = cur_cs + cs_token_flag; + back_input(); + } + } else { + if (ss->l > 0) { + no_new_control_sequence = false; + cur_cs = string_lookup((char *) ss->s, ss->l); + no_new_control_sequence = true; + } else { + /*tex the list is empty */ + cur_cs = null_cs; + } + last_cs_name = cur_cs ; + free_lstring(ss); + flush_list(r); + if (eq_type(cur_cs) == undefined_cs_cmd) { + /*tex The |save_stack| might change! */ + eq_define(cur_cs, relax_cmd, too_big_char); + }; + /*tex The control sequence will now match \.{\\relax} */ + cur_tok = cur_cs + cs_token_flag; + back_input(); + } +} + +void inject_last_tested_cs(void) +{ + if (last_cs_name != null_cs) { + cur_cs = last_cs_name; + cur_tok = last_cs_name + cs_token_flag; + back_input(); + } +} + +/*tex + + Sometimes the expansion looks too far ahead, so we want to insert a harmless + \.{\\relax} into the user's input. + +*/ + +void insert_relax(void) +{ + cur_tok = cs_token_flag + cur_cs; + back_input(); + cur_tok = cs_token_flag + frozen_relax; + back_input(); + token_type = inserted; +} + + +/*tex + + Here is a recursive procedure that is \TeX's usual way to get the next token + of input. It has been slightly optimized to take account of common cases. + +*/ + +void get_x_token(void) +{ + /*tex This code sets |cur_cmd|, |cur_chr|, |cur_tok|, and expands macros. */ + RESTART: + get_next(); + if (cur_cmd <= max_command_cmd) + goto DONE; + if (cur_cmd >= call_cmd) { + if (cur_cmd < end_template_cmd) { + macro_call(); + } else { + cur_cs = frozen_endv; + cur_cmd = endv_cmd; + /*tex Now |cur_chr=null_list|. */ + goto DONE; + } + } else { + expand(); + } + goto RESTART; + DONE: + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + +/*tex + + The |get_x_token| procedure is equivalent to two consecutive procedure calls: + |get_next; x_token|. It's |get_x_token| without the initial |get_next|. + +*/ + +void x_token(void) +{ + while (cur_cmd > max_command_cmd) { + expand(); + get_next(); + } + if (cur_cs == 0) + cur_tok = token_val(cur_cmd, cur_chr); + else + cur_tok = cs_token_flag + cur_cs; +} + +/*tex + + A control sequence that has been \.{\\def}'ed by the user is expanded by + \TeX's |macro_call| procedure. + + Before we get into the details of |macro_call|, however, let's consider the + treatment of primitives like \.{\\topmark}, since they are essentially macros + without parameters. The token lists for such marks are kept in five global + arrays of pointers; we refer to the individual entries of these arrays by + symbolic macros |top_mark|, etc. The value of |top_mark(x)|, etc. is either + |null| or a pointer to the reference count of a token list. + + The variable |biggest_used_mark| is an aid to try and keep the code somehwat + efficient without too much extra work: it registers the highest mark class + ever instantiated by the user, so the loops in |fire_up| and |vsplit| do not + have to traverse the full range |0..biggest_mark|. + +*/ + +halfword top_marks_array[(biggest_mark + 1)]; +halfword first_marks_array[(biggest_mark + 1)]; +halfword bot_marks_array[(biggest_mark + 1)]; +halfword split_first_marks_array[(biggest_mark + 1)]; +halfword split_bot_marks_array[(biggest_mark + 1)]; +halfword biggest_used_mark; + +void initialize_marks(void) +{ + int i; + biggest_used_mark = 0; + for (i = 0; i <= biggest_mark; i++) { + top_mark(i) = null; + first_mark(i) = null; + bot_mark(i) = null; + split_first_mark(i) = null; + split_bot_mark(i) = null; + } +} + +/*tex + + Now let's consider |macro_call| itself, which is invoked when \TeX\ is + scanning a control sequence whose |cur_cmd| is either |call|, |long_call|, + |outer_call|, or |long_outer_call|. The control sequence definition appears + in the token list whose reference count is in location |cur_chr| of |mem|. + + The global variable |long_state| will be set to |call| or to |long_call|, + depending on whether or not the control sequence disallows \.{\\par} in its + parameters. The |get_next| routine will set |long_state| to |outer_call| and + emit \.{\\par}, if a file ends or if an \.{\\outer} control sequence occurs + in the midst of an argument. + +*/ + +/*tex Governs the acceptance of \.{\\par}: */ + +int long_state; + +/*tex + + The parameters, if any, must be scanned before the macro is expanded. + Parameters are token lists without reference counts. They are placed on an + auxiliary stack called |pstack| while they are being scanned, since the + |param_stack| may be losing entries during the matching process. (Note that + |param_stack| can't be gaining entries, since |macro_call| is the only + routine that puts anything onto |param_stack|, and it is not recursive.) + +*/ + +/*tex The arguments supplied to a macro: */ + +halfword pstack[9]; + +/*tex + + After parameter scanning is complete, the parameters are moved to the + |param_stack|. Then the macro body is fed to the scanner; in other words, + |macro_call| places the defined text of the control sequence at the top of\/ + \TeX's input stack, so that |get_next| will proceed to read it next. + + The global variable |cur_cs| contains the |eqtb| address of the control + sequence being expanded, when |macro_call| begins. If this control sequence + has not been declared \.{\\long}, i.e., if its command code in the |eq_type| + field is not |long_call| or |long_outer_call|, its parameters are not allowed + to contain the control sequence \.{\\par}. If an illegal \.{\\par} appears, + the macro call is aborted, and the \.{\\par} will be rescanned. + +*/ + +/*tex This invokes a user-defined control sequence. */ + +void macro_call(void) +{ + /*tex current node in the macro's token list */ + halfword r; + /*tex current node in parameter token list being built */ + halfword p = null; + /*tex new node being put into the token list */ + halfword q; + /*tex backup pointer for parameter matching */ + halfword s; + /*tex cycle pointer for backup recovery */ + halfword t; + /*tex auxiliary pointers for backup recovery */ + halfword u, v; + /*tex one step before the last |right_brace| token */ + halfword rbrace_ptr = null; + /*tex the number of parameters scanned */ + int n = 0; + /*tex unmatched left braces in current parameter */ + halfword unbalance; + /*tex the number of tokens or groups (usually) */ + halfword m = 0; + /*tex start of the token list */ + halfword ref_count; + /*tex |scanner_status| upon entry */ + int save_scanner_status = scanner_status; + /*tex |warning_index| upon entry */ + halfword save_warning_index = warning_index; + /*tex character used in parameter */ + int match_chr = 0; + warning_index = cur_cs; + ref_count = cur_chr; + r = token_link(ref_count); + if (tracing_macros_par > 0) { + /*tex Show the text of the macro being expanded. */ + begin_diagnostic(); + print_ln(); + print_cs(warning_index); + token_show(ref_count); + end_diagnostic(false); + } + if (token_info(r) == protected_token) + r = token_link(r); + if (token_info(r) != end_match_token) { + /*tex + + Scan the parameters and make |link(r)| point to the macro body; but + |return| if an illegal \.{\\par} is detected. + + At this point, the reader will find it advisable to review the + explanation of token list format that was presented earlier, since + many aspects of that format are of importance chiefly in the + |macro_call| routine. + + The token list might begin with a string of compulsory tokens before + the first |match| or |end_match|. In that case the macro name is + supposed to be followed by those tokens; the following program will + set |s=null| to represent this restriction. Otherwise |s| will be set + to the first token of a string that will delimit the next parameter. + + */ + scanner_status = matching; + unbalance = 0; + long_state = eq_type(cur_cs); + if (long_state >= outer_call_cmd) + long_state = long_state - 2; + do { + set_token_link(temp_token_head, null); + if ((token_info(r) >= end_match_token) + || (token_info(r) < match_token)) { + s = null; + } else { + match_chr = token_info(r) - match_token; + s = token_link(r); + r = s; + p = temp_token_head; + m = 0; + } + /*tex + + Scan a parameter until its delimiter string has been found; or, + if |s=null|, simply scan the delimiter string. + + If |info(r)| is a |match| or |end_match| command, it cannot be + equal to any token found by |get_token|. Therefore an undelimited + parameter---i.e., a |match| that is immediately followed by + |match| or |end_match|---will always fail the test + `|cur_tok=info(r)|' in the following algorithm. + + */ + CONTINUE: + /*tex Set |cur_tok| to the next token of input: */ + get_token(); + if (cur_tok == token_info(r)) { + /*tex + + Advance |r|; |goto found| if the parameter delimiter has been + fully matched, otherwise |goto continue|. + + A slightly subtle point arises here: When the parameter + delimiter ends with `\.{\#\{}', the token list will have a + left brace both before and after the |end_match|\kern-.4pt. + Only one of these should affect the |align_state|, but both + will be scanned, so we must make a correction. + + */ + r = token_link(r); + if ((token_info(r) >= match_token) + && (token_info(r) <= end_match_token)) { + if (cur_tok < left_brace_limit) + decr(align_state); + goto FOUND; + } else { + goto CONTINUE; + } + + } + /*tex + + Contribute the recently matched tokens to the current parameter, + and |goto continue| if a partial match is still in effect; but + abort if |s=null|. + + When the following code becomes active, we have matched tokens + from |s| to the predecessor of |r|, and we have found that + |cur_tok<>info(r)|. An interesting situation now presents itself: + If the parameter is to be delimited by a string such as `\.{ab}', + and if we have scanned `\.{aa}', we want to contribute one `\.a' + to the current parameter and resume looking for a `\.b'. The + program must account for such partial matches and for others that + can be quite complex. But most of the time we have |s=r| and + nothing needs to be done. + + Incidentally, it is possible for \.{\\par} tokens to sneak in to + certain parameters of non-\.{\\long} macros. For example, + consider a case like `\.{\\def\\a\#1\\par!\{...\}}' where the + first \.{\\par} is not followed by an exclamation point. In such + situations it does not seem appropriate to prohibit the + \.{\\par}, so \TeX\ keeps quiet about this bending of the rules. + + */ + if (s != r) { + if (s == null) { + /*tex Report an improper use of the macro and abort. */ + print_err("Use of "); + sprint_cs(warning_index); + tprint(" doesn't match its definition"); + help4( + "If you say, e.g., `\\def\\a1{...}', then you must always", + "put `1' after `\\a', since control sequence names are", + "made up of letters only. The macro here has not been", + "followed by the required stuff, so I'm ignoring it." + ); + error(); + goto EXIT; + + } else { + t = s; + do { + store_new_token(token_info(t)); + incr(m); + u = token_link(t); + v = s; + while (1) { + if (u == r) { + if (cur_tok != token_info(v)) { + goto DONE; + } else { + r = token_link(v); + goto CONTINUE; + } + } + if (token_info(u) != token_info(v)) + goto DONE; + u = token_link(u); + v = token_link(v); + } + DONE: + t = token_link(t); + } while (t != r); + r = s; + /*tex At this point, no tokens are recently matched. */ + } + } + if (cur_tok == par_token) + if (long_state != long_call_cmd) + if (!suppress_long_error_par) { + goto RUNAWAY; + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + /*tex Contribute an entire group to the current parameter. */ + unbalance = 1; + while (1) { + fast_store_new_token(cur_tok); + get_token(); + if (cur_tok == par_token) { + if (long_state != long_call_cmd) { + if (!suppress_long_error_par) { + goto RUNAWAY; + + } + } + } + if (cur_tok < right_brace_limit) { + if (cur_tok < left_brace_limit) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + break; + } + } + } + rbrace_ptr = p; + store_new_token(cur_tok); + } else { + /*tex Report an extra right brace and |goto continue|. */ + back_input(); + print_err("Argument of "); + sprint_cs(warning_index); + tprint(" has an extra }"); + help6( + "I've run across a `}' that doesn't seem to match anything.", + "For example, `\\def\\a#1{...}' and `\\a}' would produce", + "this error. If you simply proceed now, the `\\par' that", + "I've just inserted will cause me to report a runaway", + "argument that might be the root of the problem. But if", + "your `}' was spurious, just type `2' and it will go away." + ); + incr(align_state); + long_state = call_cmd; + cur_tok = par_token; + ins_error(); + goto CONTINUE; + /*tex A white lie; the \.{\\par} won't always trigger a runaway. */ + } + } else { + /*tex + + Store the current token, but |goto continue| if it is a blank + space that would become an undelimited parameter. + + */ + if (cur_tok == space_token && token_info(r) <= end_match_token && token_info(r) >= match_token) + goto CONTINUE; + store_new_token(cur_tok); + } + incr(m); + if (token_info(r) > end_match_token) + goto CONTINUE; + if (token_info(r) < match_token) + goto CONTINUE; + FOUND: + if (s != null) { + /* + + Tidy up the parameter just scanned, and tuck it away. If the + parameter consists of a single group enclosed in braces, we + must strip off the enclosing braces. That's why |rbrace_ptr| + was introduced. + + */ + if ((m == 1) && (token_info(p) < right_brace_limit) + && (p != temp_token_head)) { + set_token_link(rbrace_ptr, null); + free_avail(p); + p = token_link(temp_token_head); + pstack[n] = token_link(p); + free_avail(p); + } else { + pstack[n] = token_link(temp_token_head); + } + incr(n); + if (tracing_macros_par > 0) { + begin_diagnostic(); + print_nl(match_chr); + print_int(n); + tprint("<-"); + show_token_list(pstack[n - 1], null, 1000); + end_diagnostic(false); + } + } + /*tex + + Now |info(r)| is a token whose command code is either |match| or + |end_match|. + + */ + } while (token_info(r) != end_match_token); + } + /*tex + + Feed the macro body and its parameters to the scanner Before we put a new + token list on the input stack, it is wise to clean off all token lists + that have recently been depleted. Then a user macro that ends with a call + to itself will not require unbounded stack space. + + */ + while ((istate == token_list) && (iloc == null) && (token_type != v_template)) { + /*tex Conserve stack space. */ + end_token_list(); + } + begin_token_list(ref_count, macro); + iname = warning_index; + iloc = token_link(r); + if (n > 0) { + if (param_ptr + n > max_param_stack) { + max_param_stack = param_ptr + n; + if (max_param_stack > param_size) + overflow("parameter stack size", (unsigned) param_size); + } + for (m = 0; m <= n - 1; m++) + param_stack[param_ptr + m] = pstack[m]; + param_ptr = param_ptr + n; + } + goto EXIT; + RUNAWAY: + /*tex + + Report a runaway argument and abort. If |long_state=outer_call|, a + runaway argument has already been reported. + + */ + if (long_state == call_cmd) { + runaway(); + print_err("Paragraph ended before "); + sprint_cs(warning_index); + tprint(" was complete"); + help3( + "I suspect you've forgotten a `}', causing me to apply this", + "control sequence to too much text. How can we recover?", + "My plan is to forget the whole thing and hope for the best." + ); + back_error(); + } + pstack[n] = token_link(temp_token_head); + align_state = align_state - unbalance; + for (m = 0; m <= n; m++) + flush_list(pstack[m]); + EXIT: + scanner_status = save_scanner_status; + warning_index = save_warning_index; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/expand.w b/Build/source/texk/web2c/luatexdir/tex/expand.w deleted file mode 100644 index c912863b2a1..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/expand.w +++ /dev/null @@ -1,829 +0,0 @@ -% expand.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - - -#include "ptexlib.h" - -@ Only a dozen or so command codes |>max_command| can possibly be returned by -|get_next|; in increasing order, they are |undefined_cs|, |expand_after|, -|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|, -|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and -|end_template|.{\emergencystretch=40pt\par} - -Sometimes, recursive calls to the following |expand| routine may -cause exhaustion of the run-time calling stack, resulting in -forced execution stops by the operating system. To diminish the chance -of this happening, a counter is used to keep track of the recursion -depth, in conjunction with a constant called |expand_depth|. - -Note that this does not catch all possible infinite recursion loops, -just the ones that exhaust the application calling stack. The -actual maximum value of |expand_depth| is outside of our control, but -the initial setting of |100| should be enough to prevent problems. -@^system dependencies@> - -@c -static int expand_depth_count = 0; - - -@ The |expand| subroutine is used when |cur_cmd>max_command|. It removes a -``call'' or a conditional or one of the other special operations just -listed. It follows that |expand| might invoke itself recursively. In all -cases, |expand| destroys the current token, but it sets things up so that -the next |get_next| will deliver the appropriate next token. The value of -|cur_tok| need not be known when |expand| is called. - -Since several of the basic scanning routines communicate via global variables, -their values are saved as local variables of |expand| so that -recursive calls don't invalidate them. -@^recursion@> - -@c -int is_in_csname = 0; - -@ @c -void expand(void) -{ - halfword t; /* token that is being ``expanded after'' */ - halfword p; /* for list manipulation */ - halfword cur_ptr; /* for a local token list pointer */ - int cv_backup; /* to save the global quantity |cur_val| */ - int cvl_backup, radix_backup, co_backup; /* to save |cur_val_level|, etc. */ - halfword backup_backup; /* to save |link(backup_head)| */ - int save_scanner_status; /* temporary storage of |scanner_status| */ - incr(expand_depth_count); - if (expand_depth_count >= expand_depth) - overflow("expansion depth", (unsigned) expand_depth); - cv_backup = cur_val; - cvl_backup = cur_val_level; - radix_backup = radix; - co_backup = cur_order; - backup_backup = token_link(backup_head); - RESWITCH: - if (cur_cmd < call_cmd) { - /* Expand a nonmacro */ - if (tracing_commands_par > 1) - show_cur_cmd_chr(); - switch (cur_cmd) { - case top_bot_mark_cmd: - /* Insert the appropriate mark text into the scanner */ - t = cur_chr % marks_code; - if (cur_chr >= marks_code) - scan_mark_num(); - else - cur_val = 0; - switch (t) { - case first_mark_code: - cur_ptr = first_mark(cur_val); - break; - case bot_mark_code: - cur_ptr = bot_mark(cur_val); - break; - case split_first_mark_code: - cur_ptr = split_first_mark(cur_val); - break; - case split_bot_mark_code: - cur_ptr = split_bot_mark(cur_val); - break; - default: - cur_ptr = top_mark(cur_val); - break; - } - if (cur_ptr != null) - begin_token_list(cur_ptr, mark_text); - break; - case expand_after_cmd: - if (cur_chr == 0) { - /* Expand the token after the next token */ - /* It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}. */ - get_token(); - t = cur_tok; - get_token(); - if (cur_cmd > max_command_cmd) - expand(); - else - back_input(); - cur_tok = t; - back_input(); - - } else { /* \\unless */ - /* Negate a boolean conditional and |goto reswitch| */ - /* The result of a boolean condition is reversed when the conditional is - preceded by \.{\\unless}. */ - get_token(); - if ((cur_cmd == if_test_cmd) && (cur_chr != if_case_code)) { - cur_chr = cur_chr + unless_code; - goto RESWITCH; - } - print_err("You can't use `\\unless' before `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - print_char('\''); - help1("Continue, and I'll forget that it ever happened."); - back_error(); - } - break; - case no_expand_cmd: - if (cur_chr == 0) { - /* Suppress expansion of the next token */ - /* The implementation of \.{\\noexpand} is a bit trickier, because it is - necessary to insert a special `|dont_expand|' marker into \TeX's reading - mechanism. This special marker is processed by |get_next|, but it does - not slow down the inner loop. - - Since \.{\\outer} macros might arise here, we must also - clear the |scanner_status| temporarily. - */ - - save_scanner_status = scanner_status; - scanner_status = normal; - get_token(); - scanner_status = save_scanner_status; - t = cur_tok; - back_input(); /* now |start| and |loc| point to the backed-up token |t| */ - if (t >= cs_token_flag) { - p = get_avail(); - set_token_info(p, cs_token_flag + frozen_dont_expand); - set_token_link(p, iloc); - istart = p; - iloc = p; - } - - } else { - /* Implement \.{\\primitive} */ - /* - The \.{\\primitive} handling. If the primitive meaning of the next - token is an expandable command, it suffices to replace the current - token with the primitive one and restart |expand|. - - Otherwise, the token we just read has to be pushed back, as well - as a token matching the internal form of \.{\\primitive}, that is - sneaked in as an alternate form of |ignore_spaces|. - - An implementation problem surfaces: There really is no |cur_cs| - attached to the inserted primitive command, so it is safer to set - |cur_cs| to zero. |cur_tok| has a similar problem. And for the - non-expanded branch, simply pushing back a token that matches the - correct internal command does not work, because that approach would - not survive roundtripping to a temporary file or even a token list. - - In a next version, it would be smart to create |frozen_| versions of - all the primitives. Then, this problem would not happen, at the - expense of a few hundred extra control sequences. - */ - save_scanner_status = scanner_status; - scanner_status = normal; - get_token(); - scanner_status = save_scanner_status; - cur_cs = prim_lookup(cs_text(cur_cs)); - if (cur_cs != undefined_primitive) { - t = get_prim_eq_type(cur_cs); - if (t > max_command_cmd) { - cur_cmd = t; - cur_chr = get_prim_equiv(cur_cs); - cur_tok = token_val(cur_cmd, cur_chr); - cur_cs = 0; - goto RESWITCH; - } else { - back_input(); /* now |loc| and |start| point to a one-item list */ - p = get_avail(); - set_token_info(p, cs_token_flag + frozen_primitive); - set_token_link(p, iloc); - iloc = p; - istart = p; - } - } else if (suppress_primitive_error_par == 0) { - print_err("Missing primitive name"); - help2 - ("The control sequence marked <to be read again> does not", - "represent any known primitive."); - back_error(); - } - - } - break; - case cs_name_cmd: - /* Manufacture a control sequence name; */ - if (cur_chr == 0) { - manufacture_csname(0); - } else if (cur_chr == 1) { - inject_last_tested_cs(); - } else { - manufacture_csname(1); - } - break; - case convert_cmd: - conv_toks(); /* this procedure is discussed in Part 27 below */ - break; - case the_cmd: - ins_the_toks(); /* this procedure is discussed in Part 27 below */ - break; - case combine_toks_cmd: - combine_the_toks(cur_chr); - break; - case if_test_cmd: - conditional(); /* this procedure is discussed in Part 28 below */ - break; - case fi_or_else_cmd: - /* Terminate the current conditional and skip to \.{\\fi} */ - /* The processing of conditionals is complete except for the following - code, which is actually part of |expand|. It comes into play when - \.{\\or}, \.{\\else}, or \.{\\fi} is scanned. */ - - if (tracing_ifs_par > 0) - if (tracing_commands_par <= 1) - show_cur_cmd_chr(); - if (cur_chr > if_limit) { - if (if_limit == if_code) { - insert_relax(); /* condition not yet evaluated */ - } else { - print_err("Extra "); - print_cmd_chr(fi_or_else_cmd, cur_chr); - help1("I'm ignoring this; it doesn't match any \\if."); - error(); - } - } else { - while (cur_chr != fi_code) - pass_text(); /* skip to \.{\\fi} */ - pop_condition_stack(); - } - - break; - case input_cmd: - /* Initiate or terminate input from a file */ - if (cur_chr == 1) - force_eof = true; - else if (cur_chr == 2) - pseudo_start(); - else if (cur_chr == 3) { - pseudo_start(); - iname = 19; - } else if (name_in_progress) - insert_relax(); - else - start_input(); - break; - case variable_cmd: - do_variable(); - break; - case feedback_cmd: - do_feedback(); - break; - default: - /* Complain about an undefined macro */ - print_err("Undefined control sequence"); - help5("The control sequence at the end of the top line", - "of your error message was never \\def'ed. If you have", - "misspelled it (e.g., `\\hobx'), type `I' and the correct", - "spelling (e.g., `I\\hbox'). Otherwise just continue,", - "and I'll forget about whatever was undefined."); - error(); - break; - } - } else if (cur_cmd < end_template_cmd) { - macro_call(); - } else { - /* Insert a token containing |frozen_endv| */ - /* An |end_template| command is effectively changed to an |endv| command - by the following code. (The reason for this is discussed below; the - |frozen_end_template| at the end of the template has passed the - |check_outer_validity| test, so its mission of error detection has been - accomplished.) - */ - cur_tok = cs_token_flag + frozen_endv; - back_input(); - - } - cur_val = cv_backup; - cur_val_level = cvl_backup; - radix = radix_backup; - cur_order = co_backup; - set_token_link(backup_head, backup_backup); - decr(expand_depth_count); -} - -@ @c -void complain_missing_csname(void) -{ - print_err("Missing \\endcsname inserted"); - help2("The control sequence marked <to be read again> should", - "not appear between \\csname and \\endcsname."); - back_error(); -} - -@ @c -void manufacture_csname(boolean use) -{ - halfword p, q, r; - lstring *ss; - r = get_avail(); - p = r; /* head of the list of characters */ - is_in_csname += 1; - do { - get_x_token(); - if (cur_cs == 0) - store_new_token(cur_tok); - } while (cur_cs == 0); - if (cur_cmd != end_cs_name_cmd) { - /* Complain about missing \.{\\endcsname} */ - complain_missing_csname(); - } - /* Look up the characters of list |r| in the hash table, and set |cur_cs| */ - ss = tokenlist_to_lstring(r, true); - is_in_csname -= 1; - if (use) { - if (ss->l > 0) { - cur_cs = string_lookup((char *) ss->s, ss->l); - } else { - cur_cs = null_cs; - } - last_cs_name = cur_cs ; - free_lstring(ss); - flush_list(r); - if (cur_cs == null_cs) { - /* skip */ - } else if (eq_type(cur_cs) == undefined_cs_cmd) { - /* skip */ - } else { - cur_tok = cur_cs + cs_token_flag; - back_input(); - } - } else { - if (ss->l > 0) { - no_new_control_sequence = false; - cur_cs = string_lookup((char *) ss->s, ss->l); - no_new_control_sequence = true; - } else { - cur_cs = null_cs; /* the list is empty */ - } - last_cs_name = cur_cs ; - free_lstring(ss); - flush_list(r); - if (eq_type(cur_cs) == undefined_cs_cmd) { - eq_define(cur_cs, relax_cmd, too_big_char); /* N.B.: The |save_stack| might change */ - }; /* the control sequence will now match `\.{\\relax}' */ - cur_tok = cur_cs + cs_token_flag; - back_input(); - } -} - -void inject_last_tested_cs(void) -{ - if (last_cs_name != null_cs) { - cur_cs = last_cs_name; - cur_tok = last_cs_name + cs_token_flag; - back_input(); - } -} - -@ Sometimes the expansion looks too far ahead, so we want to insert -a harmless \.{\\relax} into the user's input. - -@c -void insert_relax(void) -{ - cur_tok = cs_token_flag + cur_cs; - back_input(); - cur_tok = cs_token_flag + frozen_relax; - back_input(); - token_type = inserted; -} - - -@ Here is a recursive procedure that is \TeX's usual way to get the -next token of input. It has been slightly optimized to take account of -common cases. - -@c -void get_x_token(void) -{ /* sets |cur_cmd|, |cur_chr|, |cur_tok|, and expands macros */ - RESTART: - get_next(); - if (cur_cmd <= max_command_cmd) - goto DONE; - if (cur_cmd >= call_cmd) { - if (cur_cmd < end_template_cmd) { - macro_call(); - } else { - cur_cs = frozen_endv; - cur_cmd = endv_cmd; - goto DONE; /* |cur_chr=null_list| */ - } - } else { - expand(); - } - goto RESTART; - DONE: - if (cur_cs == 0) - cur_tok = token_val(cur_cmd, cur_chr); - else - cur_tok = cs_token_flag + cur_cs; -} - - -@ The |get_x_token| procedure is equivalent to two consecutive -procedure calls: |get_next; x_token|. - -@c -void x_token(void) -{ /* |get_x_token| without the initial |get_next| */ - while (cur_cmd > max_command_cmd) { - expand(); - get_next(); - } - if (cur_cs == 0) - cur_tok = token_val(cur_cmd, cur_chr); - else - cur_tok = cs_token_flag + cur_cs; -} - - -@ A control sequence that has been \.{\\def}'ed by the user is expanded by -\TeX's |macro_call| procedure. - -Before we get into the details of |macro_call|, however, let's consider the -treatment of primitives like \.{\\topmark}, since they are essentially -macros without parameters. The token lists for such marks are kept in five -global arrays of pointers; we refer to the individual entries of these -arrays by symbolic macros |top_mark|, etc. The value of |top_mark(x)|, etc. -is either |null| or a pointer to the reference count of a token list. - -The variable |biggest_used_mark| is an aid to try and keep the code -somehwat efficient without too much extra work: it registers the -highest mark class ever instantiated by the user, so the loops -in |fire_up| and |vsplit| do not have to traverse the full range -|0..biggest_mark|. - -@c -halfword top_marks_array[(biggest_mark + 1)]; -halfword first_marks_array[(biggest_mark + 1)]; -halfword bot_marks_array[(biggest_mark + 1)]; -halfword split_first_marks_array[(biggest_mark + 1)]; -halfword split_bot_marks_array[(biggest_mark + 1)]; -halfword biggest_used_mark; - -@ @c -void initialize_marks(void) -{ - int i; - biggest_used_mark = 0; - for (i = 0; i <= biggest_mark; i++) { - top_mark(i) = null; - first_mark(i) = null; - bot_mark(i) = null; - split_first_mark(i) = null; - split_bot_mark(i) = null; - } -} - - -@ Now let's consider |macro_call| itself, which is invoked when \TeX\ is -scanning a control sequence whose |cur_cmd| is either |call|, |long_call|, -|outer_call|, or |long_outer_call|. The control sequence definition -appears in the token list whose reference count is in location |cur_chr| -of |mem|. - -The global variable |long_state| will be set to |call| or to |long_call|, -depending on whether or not the control sequence disallows \.{\\par} -in its parameters. The |get_next| routine will set |long_state| to -|outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer} -control sequence occurs in the midst of an argument. - -@c -int long_state; /* governs the acceptance of \.{\\par} */ - -@ The parameters, if any, must be scanned before the macro is expanded. -Parameters are token lists without reference counts. They are placed on -an auxiliary stack called |pstack| while they are being scanned, since -the |param_stack| may be losing entries during the matching process. -(Note that |param_stack| can't be gaining entries, since |macro_call| is -the only routine that puts anything onto |param_stack|, and it -is not recursive.) - -@c -halfword pstack[9]; /* arguments supplied to a macro */ - - -@ After parameter scanning is complete, the parameters are moved to the -|param_stack|. Then the macro body is fed to the scanner; in other words, -|macro_call| places the defined text of the control sequence at the -top of\/ \TeX's input stack, so that |get_next| will proceed to read it -next. - -The global variable |cur_cs| contains the |eqtb| address of the control sequence -being expanded, when |macro_call| begins. If this control sequence has not been -declared \.{\\long}, i.e., if its command code in the |eq_type| field is -not |long_call| or |long_outer_call|, its parameters are not allowed to contain -the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro -call is aborted, and the \.{\\par} will be rescanned. - -@c -void macro_call(void) -{ /* invokes a user-defined control sequence */ - halfword r; /* current node in the macro's token list */ - halfword p = null; /* current node in parameter token list being built */ - halfword q; /* new node being put into the token list */ - halfword s; /* backup pointer for parameter matching */ - halfword t; /* cycle pointer for backup recovery */ - halfword u, v; /* auxiliary pointers for backup recovery */ - halfword rbrace_ptr = null; /* one step before the last |right_brace| token */ - int n = 0; /* the number of parameters scanned */ - halfword unbalance; /* unmatched left braces in current parameter */ - halfword m = 0; /* the number of tokens or groups (usually) */ - halfword ref_count; /* start of the token list */ - int save_scanner_status = scanner_status; /* |scanner_status| upon entry */ - halfword save_warning_index = warning_index; /* |warning_index| upon entry */ - int match_chr = 0; /* character used in parameter */ - warning_index = cur_cs; - ref_count = cur_chr; - r = token_link(ref_count); - if (tracing_macros_par > 0) { - /* Show the text of the macro being expanded */ - begin_diagnostic(); - print_ln(); - print_cs(warning_index); - token_show(ref_count); - end_diagnostic(false); - } - if (token_info(r) == protected_token) - r = token_link(r); - if (token_info(r) != end_match_token) { - /* Scan the parameters and make |link(r)| point to the macro body; but - |return| if an illegal \.{\\par} is detected */ - /* At this point, the reader will find it advisable to review the explanation - of token list format that was presented earlier, since many aspects of that - format are of importance chiefly in the |macro_call| routine. - - The token list might begin with a string of compulsory tokens before the - first |match| or |end_match|. In that case the macro name is supposed to be - followed by those tokens; the following program will set |s=null| to - represent this restriction. Otherwise |s| will be set to the first token of - a string that will delimit the next parameter. - */ - - scanner_status = matching; - unbalance = 0; - long_state = eq_type(cur_cs); - if (long_state >= outer_call_cmd) - long_state = long_state - 2; - do { - set_token_link(temp_token_head, null); - if ((token_info(r) >= end_match_token) - || (token_info(r) < match_token)) { - s = null; - } else { - match_chr = token_info(r) - match_token; - s = token_link(r); - r = s; - p = temp_token_head; - m = 0; - } - /* Scan a parameter until its delimiter string has been found; or, if |s=null|, - simply scan the delimiter string; */ - - /* If |info(r)| is a |match| or |end_match| command, it cannot be equal to - any token found by |get_token|. Therefore an undelimited parameter---i.e., - a |match| that is immediately followed by |match| or |end_match|---will - always fail the test `|cur_tok=info(r)|' in the following algorithm. */ - CONTINUE: - get_token(); /* set |cur_tok| to the next token of input */ - if (cur_tok == token_info(r)) { - /* Advance |r|; |goto found| if the parameter delimiter has been - fully matched, otherwise |goto continue| */ - /* A slightly subtle point arises here: When the parameter delimiter ends - with `\.{\#\{}', the token list will have a left brace both before and - after the |end_match|\kern-.4pt. Only one of these should affect the - |align_state|, but both will be scanned, so we must make a correction. - */ - r = token_link(r); - if ((token_info(r) >= match_token) - && (token_info(r) <= end_match_token)) { - if (cur_tok < left_brace_limit) - decr(align_state); - goto FOUND; - } else { - goto CONTINUE; - } - - } - /* Contribute the recently matched tokens to the current parameter, and - |goto continue| if a partial match is still in effect; but abort if |s=null| */ - - /* When the following code becomes active, we have matched tokens from |s| to - the predecessor of |r|, and we have found that |cur_tok<>info(r)|. An - interesting situation now presents itself: If the parameter is to be - delimited by a string such as `\.{ab}', and if we have scanned `\.{aa}', - we want to contribute one `\.a' to the current parameter and resume - looking for a `\.b'. The program must account for such partial matches and - for others that can be quite complex. But most of the time we have |s=r| - and nothing needs to be done. - - Incidentally, it is possible for \.{\\par} tokens to sneak in to certain - parameters of non-\.{\\long} macros. For example, consider a case like - `\.{\\def\\a\#1\\par!\{...\}}' where the first \.{\\par} is not followed - by an exclamation point. In such situations it does not seem appropriate - to prohibit the \.{\\par}, so \TeX\ keeps quiet about this bending of - the rules. */ - - if (s != r) { - if (s == null) { - /* Report an improper use of the macro and abort */ - print_err("Use of "); - sprint_cs(warning_index); - tprint(" doesn't match its definition"); - help4 - ("If you say, e.g., `\\def\\a1{...}', then you must always", - "put `1' after `\\a', since control sequence names are", - "made up of letters only. The macro here has not been", - "followed by the required stuff, so I'm ignoring it."); - error(); - goto EXIT; - - } else { - t = s; - do { - store_new_token(token_info(t)); - incr(m); - u = token_link(t); - v = s; - while (1) { - if (u == r) { - if (cur_tok != token_info(v)) { - goto DONE; - } else { - r = token_link(v); - goto CONTINUE; - } - } - if (token_info(u) != token_info(v)) - goto DONE; - u = token_link(u); - v = token_link(v); - } - DONE: - t = token_link(t); - } while (t != r); - r = s; /* at this point, no tokens are recently matched */ - } - } - - if (cur_tok == par_token) - if (long_state != long_call_cmd) - if (!suppress_long_error_par) { - goto RUNAWAY; - } - if (cur_tok < right_brace_limit) { - if (cur_tok < left_brace_limit) { - /* Contribute an entire group to the current parameter */ - unbalance = 1; - while (1) { - fast_store_new_token(cur_tok); - get_token(); - if (cur_tok == par_token) { - if (long_state != long_call_cmd) { - if (!suppress_long_error_par) { - goto RUNAWAY; - - } - } - } - if (cur_tok < right_brace_limit) { - if (cur_tok < left_brace_limit) { - incr(unbalance); - } else { - decr(unbalance); - if (unbalance == 0) - break; - } - } - } - rbrace_ptr = p; - store_new_token(cur_tok); - - } else { - /* Report an extra right brace and |goto continue| */ - back_input(); - print_err("Argument of "); - sprint_cs(warning_index); - tprint(" has an extra }"); - help6 - ("I've run across a `}' that doesn't seem to match anything.", - "For example, `\\def\\a#1{...}' and `\\a}' would produce", - "this error. If you simply proceed now, the `\\par' that", - "I've just inserted will cause me to report a runaway", - "argument that might be the root of the problem. But if", - "your `}' was spurious, just type `2' and it will go away."); - incr(align_state); - long_state = call_cmd; - cur_tok = par_token; - ins_error(); - goto CONTINUE; - /* a white lie; the \.{\\par} won't always trigger a runaway */ - } - } else { - /* Store the current token, but |goto continue| if it is - a blank space that would become an undelimited parameter */ - if (cur_tok == space_token) - if (token_info(r) <= end_match_token) - if (token_info(r) >= match_token) - goto CONTINUE; - store_new_token(cur_tok); - - } - incr(m); - if (token_info(r) > end_match_token) - goto CONTINUE; - if (token_info(r) < match_token) - goto CONTINUE; - FOUND: - if (s != null) { - /* Tidy up the parameter just scanned, and tuck it away */ - /* If the parameter consists of a single group enclosed in braces, we must - strip off the enclosing braces. That's why |rbrace_ptr| was introduced. */ - if ((m == 1) && (token_info(p) < right_brace_limit) - && (p != temp_token_head)) { - set_token_link(rbrace_ptr, null); - free_avail(p); - p = token_link(temp_token_head); - pstack[n] = token_link(p); - free_avail(p); - } else { - pstack[n] = token_link(temp_token_head); - } - incr(n); - if (tracing_macros_par > 0) { - begin_diagnostic(); - print_nl(match_chr); - print_int(n); - tprint("<-"); - show_token_list(pstack[n - 1], null, 1000); - end_diagnostic(false); - } - - } - - /* now |info(r)| is a token whose command code is either |match| or |end_match| */ - } while (token_info(r) != end_match_token); - - } - /* Feed the macro body and its parameters to the scanner */ - /* Before we put a new token list on the input stack, it is wise to clean off - all token lists that have recently been depleted. Then a user macro that ends - with a call to itself will not require unbounded stack space. */ - while ((istate == token_list) && (iloc == null) && (token_type != v_template)) { - /* conserve stack space */ - end_token_list(); - } - begin_token_list(ref_count, macro); - iname = warning_index; - iloc = token_link(r); - if (n > 0) { - if (param_ptr + n > max_param_stack) { - max_param_stack = param_ptr + n; - if (max_param_stack > param_size) - overflow("parameter stack size", (unsigned) param_size); - } - for (m = 0; m <= n - 1; m++) - param_stack[param_ptr + m] = pstack[m]; - param_ptr = param_ptr + n; - } - goto EXIT; - RUNAWAY: - /* Report a runaway argument and abort */ - /* If |long_state=outer_call|, a runaway argument has already been reported. */ - if (long_state == call_cmd) { - runaway(); - print_err("Paragraph ended before "); - sprint_cs(warning_index); - tprint(" was complete"); - help3("I suspect you've forgotten a `}', causing me to apply this", - "control sequence to too much text. How can we recover?", - "My plan is to forget the whole thing and hope for the best."); - back_error(); - } - pstack[n] = token_link(temp_token_head); - align_state = align_state - unbalance; - for (m = 0; m <= n; m++) - flush_list(pstack[m]); - - EXIT: - scanner_status = save_scanner_status; - warning_index = save_warning_index; -} diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.c b/Build/source/texk/web2c/luatexdir/tex/extensions.c new file mode 100644 index 00000000000..d5914db8a67 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/extensions.c @@ -0,0 +1,1329 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +#define mode mode_par +#define tail tail_par +#define head head_par +#define dir_save dirs_par + +/*tex + + The program above includes a bunch of ``hooks'' that allow further + capabilities to be added without upsetting \TeX's basic structure. Most of + these hooks are concerned with ``whatsit'' nodes, which are intended to be + used for special purposes; whenever a new extension to \TeX\ involves a new + kind of whatsit node, a corresponding change needs to be made to the routines + below that deal with such nodes, but it will usually be unnecessary to make + many changes to the other parts of this program. + + In order to demonstrate how extensions can be made, we shall treat + `\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}', and + `\.{\\special}' as if they were extensions. These commands are actually + primitives of \TeX, and they should appear in all implementations of the + system; but let's try to imagine that they aren't. Then the program below + illustrates how a person could add them. + + Sometimes, of course, an extension will require changes to \TeX\ itself; no + system of hooks could be complete enough for all conceivable extensions. The + features associated with `\.{\\write}' are almost all confined to the + following paragraphs, but there are small parts of the |print_ln| and + |print_char| procedures that were introduced specifically to \.{\\write} + characters. Furthermore one of the token lists recognized by the scanner is a + |write_text|; and there are a few other miscellaneous places where we have + already provided for some aspect of \.{\\write}. The goal of a \TeX\ extender + should be to minimize alterations to the standard parts of the program, and + to avoid them completely if possible. He or she should also be quite sure + that there's no easy way to accomplish the desired goals with the standard + features that \TeX\ already has. ``Think thrice before extending,'' because + that may save a lot of work, and it will also keep incompatible extensions of + \TeX\ from proliferating. + + First let's consider the format of whatsit nodes that are used to represent + the data associated with \.{\\write} and its relatives. Recall that a whatsit + has |type=whatsit_node|, and the |subtype| is supposed to distinguish + different kinds of whatsits. Each node occupies two or more words; the exact + number is immaterial, as long as it is readily determined from the |subtype| + or other data. + + We shall introduce five |subtype| values here, corresponding to the control + sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, and \.{\\special}. The + second word of I/O whatsits has a |write_stream| field that identifies the + write-stream number (0 to 15, or 16 for out-of-range and positive, or 17 for + out-of-range and negative). In the case of \.{\\write} and \.{\\special}, + there is also a field that points to the reference count of a token list that + should be sent. In the case of \.{\\openout}, we need three words and three + auxiliary subfields to hold the string numbers for name, area, and extension. + + Extensions might introduce new command codes; but it's best to use + |extension| with a modifier, whenever possible, so that |main_control| stays + the same. + + The sixteen possible \.{\\write} streams are represented by the |write_file| + array. The |j|th file is open if and only if |write_open[j]=true|. The last + two streams are special; |write_open[16]| represents a stream number greater + than 15, while |write_open[17]| represents a negative stream number, and both + of these variables are always |false|. + +*/ + +alpha_file write_file[last_file_selector+1]; +halfword write_file_mode[last_file_selector+1]; +halfword write_file_translation[last_file_selector+1]; +boolean write_open[last_file_selector+1]; + +scaled neg_wd; +scaled pos_wd; +scaled neg_ht; + +/*tex + + The variable |write_loc| just introduced is used to provide an appropriate + error message in case of ``runaway'' write texts. + +*/ + +/*tex The |eqtb| address of \.{\\write}: */ + +halfword write_loc; + +/*tex + + When an |extension| command occurs in |main_control|, in any mode, the + |do_extension| routine is called. + +*/ + +int last_saved_image_index ; +int last_saved_image_pages ; +int last_saved_box_index ; +scaledpos last_position = { 0, 0 }; + +static void do_extension_dvi(int immediate) +{ + if (scan_keyword("literal")) { + new_whatsit(special_node); + write_stream(tail) = null; + scan_toks(false, true); + write_tokens(tail) = def_ref; + } else { + tex_error("unexpected use of \\dviextension",null); + } +} + +static void do_extension_pdf(int immediate) +{ + int i; + if (scan_keyword("literal")) { + new_whatsit(pdf_literal_node); + if (scan_keyword("direct")) + set_pdf_literal_mode(tail, direct_always); + else if (scan_keyword("page")) + set_pdf_literal_mode(tail, direct_page); + else if (scan_keyword("text")) + set_pdf_literal_mode(tail, direct_text); + else if (scan_keyword("raw")) + set_pdf_literal_mode(tail, direct_raw); + else if (scan_keyword("origin")) + set_pdf_literal_mode(tail, set_origin); + else + set_pdf_literal_mode(tail, set_origin); + scan_toks(false, true); + set_pdf_literal_type(tail, normal); + set_pdf_literal_data(tail, def_ref); + } else if (scan_keyword("dest")) { + scan_pdfdest(static_pdf); + } else if (scan_keyword("annot")) { + scan_annot(static_pdf); + } else if (scan_keyword("save")) { + new_whatsit(pdf_save_node); + } else if (scan_keyword("restore")) { + new_whatsit(pdf_restore_node); + } else if (scan_keyword("setmatrix")) { + new_whatsit(pdf_setmatrix_node); + scan_toks(false, true); + set_pdf_setmatrix_data(tail, def_ref); + } else if (scan_keyword("obj")) { + scan_obj(static_pdf); + if (immediate) { + if (obj_data_ptr(static_pdf, pdf_last_obj) == 0) { + /*tex This object has not been initialized yet. */ + normal_error("pdf backend","\\pdfextension obj 'reserveobjnum' cannot be used with \\immediate"); + } + pdf_write_obj(static_pdf, pdf_last_obj); + } + } else if (scan_keyword("refobj")) { + scan_refobj(static_pdf); + } else if (scan_keyword("colorstack")) { + scan_int(); + if (cur_val >= colorstackused()) { + print_err("Unknown color stack number "); + print_int(cur_val); + help3( + "Allocate and initialize a color stack with \\pdfextension colorstackinit.", + "I'll use default color stack 0 here.", + "Proceed, with fingers crossed." + ); + error(); + cur_val = 0; + } + if (cur_val < 0) { + print_err("Invalid negative color stack number"); + help2( + "I'll use default color stack 0 here.", + "Proceed, with fingers crossed." + ); + error(); + cur_val = 0; + } + if (scan_keyword("set")) + i = colorstack_set; + else if (scan_keyword("push")) + i = colorstack_push; + else if (scan_keyword("pop")) + i = colorstack_pop; + else if (scan_keyword("current")) + i = colorstack_current; + else + i = -1; + if (i >= 0) { + new_whatsit(pdf_colorstack_node); + set_pdf_colorstack_stack(tail, cur_val); + set_pdf_colorstack_cmd(tail, i); + set_pdf_colorstack_data(tail, null); + if (i <= colorstack_data) { + scan_toks(false, true); + set_pdf_colorstack_data(tail, def_ref); + } + } else { + print_err("Color stack action is missing"); + help3( + "The expected actions for \\pdfextension colorstack:", + " set, push, pop, current", + "I'll ignore the color stack command." + ); + error(); + } + } else if (scan_keyword("startlink")) { + scan_startlink(static_pdf); + } else if (scan_keyword("endlink")) { + if (abs(mode) == vmode) + normal_error("pdf backend", "\\pdfextension endlink cannot be used in vertical mode"); + new_whatsit(pdf_end_link_node); + } else if (scan_keyword("startthread")) { + new_annot_whatsit(pdf_start_thread_node); + scan_thread_id(); + } else if (scan_keyword("endthread")) { + new_whatsit(pdf_end_thread_node); + } else if (scan_keyword("thread")) { + new_annot_whatsit(pdf_thread_node); + scan_thread_id(); + } else if (scan_keyword("outline")) { + scan_pdfoutline(static_pdf); + } else if (scan_keyword("glyphtounicode")) { + glyph_to_unicode(); + } else if (scan_keyword("catalog")) { + scan_pdfcatalog(static_pdf); + } else if (scan_keyword("fontattr")) { + /*tex + + The font attributes are simply initialized to zero now, this is + easier to deal with from C than an empty \TeX{} string, and surely + nobody will want to set font attr to a string containing a single + zero, as that would be nonsensical in the PDF output. + + */ + scan_font_ident(); + i = cur_val; + if (i == null_font) + normal_error("pdf backend", "invalid font identifier"); + scan_toks(false, true); + set_pdf_font_attr(i, tokens_to_string(def_ref)); + if (str_length(pdf_font_attr(i)) == 0) { + /*tex From |tokens_to_string|. */ + flush_str((str_ptr - 1)); + set_pdf_font_attr(i, 0); + } + } else if (scan_keyword("mapfile")) { + scan_toks(false, true); + pdfmapfile(def_ref); + delete_token_ref(def_ref); + } else if (scan_keyword("mapline")) { + scan_toks(false, true); + pdfmapline(def_ref); + delete_token_ref(def_ref); + } else if (scan_keyword("includechars")) { + pdf_include_chars(static_pdf); + } else if (scan_keyword("info")) { + scan_toks(false, true); + pdf_info_toks = concat_tokens(pdf_info_toks, def_ref); + } else if (scan_keyword("names")) { + scan_toks(false, true); + pdf_names_toks = concat_tokens(pdf_names_toks, def_ref); + } else if (scan_keyword("trailer")) { + scan_toks(false, true); + pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref); + } else { + tex_error("unexpected use of \\pdfextension",null); + } +} + +static void do_resource_dvi(int immediate, int code) +{ + /*tex Nothing is done here. */ +} + +static void do_resource_pdf(int immediate, int code) +{ + switch (code) { + case use_box_resource_code: + scan_pdfrefxform(static_pdf); + break; + case use_image_resource_code: + scan_pdfrefximage(static_pdf); + break; + case save_box_resource_code: + scan_pdfxform(static_pdf); + if (immediate) { + pdf_cur_form = last_saved_box_index; + ship_out(static_pdf, obj_xform_box(static_pdf, last_saved_box_index), SHIPPING_FORM); + } + break; + case save_image_resource_code: + scan_pdfximage(static_pdf); + if (immediate) { + pdf_write_image(static_pdf, last_saved_image_index); + } + break; + } +} + +/*tex + + Ad immediate: + + To write a token list, we must run it through \TeX's scanner, expanding + macros and \.{\\the} and \.{\\number}, etc. This might cause runaways, if a + delimited macro parameter isn't matched, and runaways would be extremely + confusing since we are calling on \TeX's scanner in the middle of a + \.{\\shipout} command. Therefore we will put a dummy control sequence as a + ``stopper,'' right after the token list. This control sequence is + artificially defined to be \.{\\outer}. + + The presence of `\.{\\immediate}' causes the |do_extension| procedure to + descend to one level of recursion. Nothing happens unless \.{\\immediate} is + followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'. + +*/ + +/*tex + + The extensions are backend related. The next subroutine uses |cur_chr| to + decide what sort of whatsit is involved, and also inserts a |write_stream| + number. + +*/ + +static void new_write_whatsit(int w, int check) +{ + new_whatsit(cur_chr); + if (check) { + /*tex So we check with open and close. */ + scan_limited_int(last_file_selector,NULL); + } else { + /*tex But we're tolerant with the rest. */ + scan_int(); + if (cur_val < 0) + cur_val = term_only; + else if (cur_val > last_file_selector) { + cur_val = term_and_log; + } + } + write_stream(tail) = cur_val; +} + +void do_extension(int immediate) +{ + /*tex An all-purpose pointer. */ + halfword p; + if (cur_cmd == extension_cmd) { + /*tex These have their own range starting at 0. */ + switch (cur_chr) { + case open_code: + p = tail; + new_write_whatsit(open_node_size,1); + scan_optional_equals(); + scan_file_name(); + open_name(tail) = cur_name; + open_area(tail) = cur_area; + open_ext(tail) = cur_ext; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case write_code: + /*tex + + When `\.{\\write 12\{...\}}' appears, we scan the token list + `\.{\{...\}}' without expanding its macros; the macros will + be expanded later when this token list is rescanned. + + */ + p = tail; + new_write_whatsit(write_node_size,0); + cur_cs = write_stream(tail); + scan_toks(false, false); + write_tokens(tail) = def_ref; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case close_code: + p = tail; + new_write_whatsit(close_node_size,1); + write_tokens(tail) = null; + if (immediate) { + wrapup_leader(tail); + flush_node_list(tail); + tail = p; + vlink(p) = null; + } + break; + case special_code: + /*tex + + When `\.{\\special\{...\}}' appears, we expand the macros in + the token list as in \.{\\xdef} and \.{\\mark}. + + */ + new_whatsit(special_node); + write_stream(tail) = null; + p = scan_toks(false, true); + write_tokens(tail) = def_ref; + break; + case immediate_code: + get_x_token(); + do_extension(1); + break; + case end_local_code: + if (tracing_nesting_par > 2) { + local_control_message("leaving token scanner"); + } + end_local_control(); + break; + case use_box_resource_code: + case use_image_resource_code: + case save_box_resource_code: + case save_image_resource_code: + switch (get_o_mode()) { + case OMODE_DVI: + do_resource_dvi(immediate,cur_chr); + break; + case OMODE_PDF: + do_resource_pdf(immediate,cur_chr); + break; + default: + break; + } + break; + /*tex Backend extensions have their own range starting at 32. */ + case dvi_extension_code: + if (get_o_mode() == OMODE_DVI) + do_extension_dvi(immediate); + break; + case pdf_extension_code: + if (get_o_mode() == OMODE_PDF) + do_extension_pdf(immediate); + break; + /*tex Done. */ + default: + if (immediate) { + back_input(); + } else { + confusion("invalid extension"); + } + break; + } + } else { + /*tex No extension command, quite certainly following |\immediate|. */ + back_input(); + } +} + +/*tex + + Here is a subroutine that creates a whatsit node having a given |subtype| and + a given number of words. It initializes only the first word of the whatsit, + and appends it to the current list. + +*/ + +void new_whatsit(int s) +{ + halfword p = new_node(whatsit_node, s); + couple_nodes(tail, p); + tail = p; +} + +/*tex + + The final line of this routine is slightly subtle; at least, the author + didn't think about it until getting burnt! There is a used-up token list on + the stack, namely the one that contained |end_write_token|. (We insert this + artificial `\.{\\endwrite}' to prevent runaways, as explained above.) If it + were not removed, and if there were numerous writes on a single page, the + stack would overflow. + +*/ + +void expand_macros_in_tokenlist(halfword p) +{ + int old_mode; + pointer q = get_avail(); + pointer r = get_avail(); + token_info(q) = right_brace_token + '}'; + token_link(q) = r; + token_info(r) = end_write_token; + begin_token_list(q, inserted); + begin_token_list(write_tokens(p), write_text); + q = get_avail(); + token_info(q) = left_brace_token + '{'; + begin_token_list(q, inserted); + /*tex + + Now we're ready to scan `\.\{$\langle\,$token list$\,\rangle$\.{\} + \\endwrite}'. + + */ + old_mode = mode; + mode = 0; + /*tex + + Disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, + \.{\\prevgraf}. + + */ + cur_cs = write_loc; + /*tex Expand macros, etc. */ + q = scan_toks(false, true); + get_token(); + if (cur_tok != end_write_token) { + /*tex Recover from an unbalanced write command */ + const char *hlp[] = { + "On this page there's a \\write with fewer real {'s than }'s.", + "I can't handle that very well; good luck.", + NULL + }; + tex_error("Unbalanced write command", hlp); + do { + get_token(); + } while (cur_tok != end_write_token); + } + mode = old_mode; + /*tex Conserve stack space. */ + end_token_list(); +} + +void write_out(halfword p) +{ + /*tex holds print |selector| */ + int old_setting; + /*tex write stream number */ + int j; + /*tex line to be written, as a C string */ + char *s, *ss; + int callback_id; + int lua_retval; + expand_macros_in_tokenlist(p); + old_setting = selector; + j = write_stream(p); + if (file_can_be_written(j)) { + selector = j; + } else if ((j == term_only) && (selector == term_and_log)) { + /*tex write to the terminal if file isn't open */ + selector = log_only; + tprint_nl(""); + } else { + tprint_nl(""); + } + s = tokenlist_to_cstring(def_ref, false, NULL); + if (selector < no_print) { + /*tex selector is a file */ + callback_id = callback_defined(process_output_buffer_callback); + if (callback_id > 0) { + /*tex fix up the output buffer using callbacks */ + lua_retval = run_callback(callback_id, "S->S", s, &ss); + if ((lua_retval == true) && (ss != NULL)) { + xfree(s); + s = ss; + } + } + } + tprint(s); + xfree(s); + print_ln(); + flush_list(def_ref); + selector = old_setting; +} + +void finalize_write_files(void) { + int k; + for (k = 0; k <= last_file_selector; k++) { + if (write_open[k]) { + lua_a_close_out(write_file[k]); + } + } +} + +void initialize_write_files(void) { + int k; + for (k = 0; k <= last_file_selector; k++) { + write_open[k] = false; + } +} + +void close_write_file(int id) { + if (write_open[id]) { + lua_a_close_out(write_file[id]); + write_open[id] = false; + } +} + +boolean open_write_file(int id, char *fn) { + if (lua_a_open_out(&(write_file[id]), fn, (id + 1))) { + write_open[id] = true; + return true; + } else { + return false; + } +} + +/*tex + + To implement primitives as \.{\\pdfextension info}, \.{\\pdfextension + catalog} or \.{\\pdfextension names} we need to concatenate tokens lists. + +*/ + +halfword concat_tokens(halfword q, halfword r) +{ + /*tex concat |q| and |r| and returns the result tokens list */ + halfword p; + if (q == null) + return r; + p = q; + while (token_link(p) != null) + p = token_link(p); + set_token_link(p, token_link(r)); + free_avail(r); + return q; +} + +/*tex + + The \eTeX\ features available in extended mode are grouped into two + categories: (1)~Some of them are permanently enabled and have no semantic + effect as long as none of the additional primitives are executed. (2)~The + remaining \eTeX\ features are optional and can be individually enabled and + disabled. For each optional feature there is an \eTeX\ state variable named + \.{\\...state}; the feature is enabled, resp.\ disabled by assigning a + positive, resp.\ non-positive value to that integer. + + In order to handle \.{\\everyeof} we need an array |eof_seen| of boolean + variables. + +*/ + +boolean *eof_seen; + +/*tex + + The |print_group| procedure prints the current level of grouping and the name + corresponding to |cur_group|. + +*/ + +void print_group(boolean e) +{ + switch (cur_group) { + case bottom_level: + tprint("bottom level"); + return; + break; + case simple_group: + case semi_simple_group: + if (cur_group == semi_simple_group) + tprint("semi "); + tprint("simple"); + break;; + case hbox_group: + case adjusted_hbox_group: + if (cur_group == adjusted_hbox_group) + tprint("adjusted "); + tprint("hbox"); + break; + case vbox_group: + tprint("vbox"); + break; + case vtop_group: + tprint("vtop"); + break; + case align_group: + case no_align_group: + if (cur_group == no_align_group) + tprint("no "); + tprint("align"); + break; + case output_group: + tprint("output"); + break; + case disc_group: + tprint("disc"); + break; + case insert_group: + tprint("insert"); + break; + case vcenter_group: + tprint("vcenter"); + break; + case math_group: + case math_choice_group: + case math_shift_group: + case math_left_group: + tprint("math"); + if (cur_group == math_choice_group) + tprint(" choice"); + else if (cur_group == math_shift_group) + tprint(" shift"); + else if (cur_group == math_left_group) + tprint(" left"); + break; + } + tprint(" group (level "); + print_int(cur_level); + print_char(')'); + if (saved_value(-1) != 0) { + /*tex |saved_line| */ + if (e) + tprint(" entered at line "); + else + tprint(" at line "); + print_int(saved_value(-1)); + } +} + +/*tex + + The |group_trace| procedure is called when a new level of grouping begins + (|e=false|) or ends (|e=true|) with |saved_value(-1)| containing the line + number. + +*/ + +void group_trace(boolean e) +{ + begin_diagnostic(); + print_char('{'); + if (e) + tprint("leaving "); + else + tprint("entering "); + print_group(e); + print_char('}'); + end_diagnostic(false); +} + +/*tex + + A group entered (or a conditional started) in one file may end in a different + file. Such slight anomalies, although perfectly legitimate, may cause errors + that are difficult to locate. In order to be able to give a warning message + when such anomalies occur, \eTeX\ uses the |grp_stack| and |if_stack| arrays + to record the initial |cur_boundary| and |cond_ptr| values for each input + file. + +*/ + +/*tex initial |cur_boundary| */ + +save_pointer *grp_stack; + +/*tex initial |cond_ptr| */ + +halfword *if_stack; + +/*tex + + When a group ends that was apparently entered in a different input file, the + |group_warning| procedure is invoked in order to update the |grp_stack|. If + moreover \.{\\tracingnesting} is positive we want to give a warning message. + The situation is, however, somewhat complicated by two facts: (1)~There may + be |grp_stack| elements without a corresponding \.{\\input} file or + \.{\\scantokens} pseudo file (e.g., error insertions from the terminal); and + (2)~the relevant information is recorded in the |name_field| of the + |input_stack| only loosely synchronized with the |in_open| variable indexing + |grp_stack|. + +*/ + +void group_warning(void) +{ + /*tex do we need a warning? */ + boolean w = false; + /*tex index into |grp_stack| */ + int i = in_open; + base_ptr = input_ptr; + /*tex store current state */ + input_stack[base_ptr] = cur_input; + while ((grp_stack[i] == cur_boundary) && (i > 0)) { + /*tex + + Set variable |w| to indicate if this case should be reported. This + code scans the input stack in order to determine the type of the + current input file. + + */ + if (tracing_nesting_par > 0) { + while ((input_stack[base_ptr].state_field == token_list) || (input_stack[base_ptr].index_field > i)) + decr(base_ptr); + if (input_stack[base_ptr].name_field > 17) + w = true; + } + grp_stack[i] = save_value(save_ptr); + decr(i); + } + if (w) { + tprint_nl("Warning: end of "); + print_group(true); + tprint(" of a different file"); + print_ln(); + if (tracing_nesting_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; + } +} + +/*tex + + When a conditional ends that was apparently started in a different input + file, the |if_warning| procedure is invoked in order to update the + |if_stack|. If moreover \.{\\tracingnesting} is positive we want to give a + warning message (with the same complications as above). + +*/ + +void if_warning(void) +{ + /*tex Do we need a warning? */ + boolean w = false; + int i = in_open; + base_ptr = input_ptr; + /*tex Store current state. */ + input_stack[base_ptr] = cur_input; + while (if_stack[i] == cond_ptr) { + /*tex Set variable |w| to. */ + if (tracing_nesting_par > 0) { + while ((input_stack[base_ptr].state_field == token_list) || (input_stack[base_ptr].index_field > i)) + decr(base_ptr); + if (input_stack[base_ptr].name_field > 17) + w = true; + } + if_stack[i] = vlink(cond_ptr); + decr(i); + } + if (w) { + tprint_nl("Warning: end of "); + print_cmd_chr(if_test_cmd, cur_if); + print_if_line(if_line); + tprint(" of a different file"); + print_ln(); + if (tracing_nesting_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; + } +} + +/*tex + + Conversely, the |file_warning| procedure is invoked when a file ends and some + groups entered or conditionals started while reading from that file are still + incomplete. + +*/ + +void file_warning(void) +{ + /*tex saved value of |save_ptr| or |cond_ptr| */ + halfword p = save_ptr; + /*tex saved value of |cur_level| or |if_limit| */ + int l = cur_level; + /*tex saved value of |cur_group| or |cur_if| */ + int c = cur_group; + /*tex saved value of |if_line| */ + int i; + save_ptr = cur_boundary; + while (grp_stack[in_open] != save_ptr) { + decr(cur_level); + tprint_nl("Warning: end of file when "); + print_group(true); + tprint(" is incomplete"); + cur_group = save_level(save_ptr); + save_ptr = save_value(save_ptr); + } + save_ptr = p; + cur_level = (quarterword) l; + /*tex Restore old values. */ + cur_group = (group_code) c; + p = cond_ptr; + l = if_limit; + c = cur_if; + i = if_line; + while (if_stack[in_open] != cond_ptr) { + tprint_nl("Warning: end of file when "); + print_cmd_chr(if_test_cmd, cur_if); + if (if_limit == fi_code) + tprint_esc("else"); + print_if_line(if_line); + tprint(" is incomplete"); + if_line = if_line_field(cond_ptr); + cur_if = if_limit_subtype(cond_ptr); + if_limit = if_limit_type(cond_ptr); + cond_ptr = vlink(cond_ptr); + } + /*tex restore old values */ + cond_ptr = p; + if_limit = l; + cur_if = c; + if_line = i; + print_ln(); + if (tracing_nesting_par > 1) + show_context(); + if (history == spotless) + history = warning_issued; +} + +/*tex The |par_fill_skip| glue node of the new paragraph. */ + +halfword last_line_fill; + +/*tex + + The lua interface needs some extra functions. The functions themselves are + quite boring, but they are handy because otherwise this internal stuff has to + be accessed from C directly, where lots of the defines are not available. + +*/ + +#define get_tex_dimen_register(j) dimen(j) +#define get_tex_skip_register(j) skip(j) +#define get_tex_mu_skip_register(j) mu_skip(j) +#define get_tex_count_register(j) count(j) +#define get_tex_attribute_register(j) attribute(j) +#define get_tex_box_register(j) box(j) + +/*tex These can now be macros (todo). */ + +int get_tex_extension_count_register(int i) +{ + return (int) int_par(backend_int_base-int_base+i); +} + +void set_tex_extension_count_register(int i, int d) +{ + int_par(backend_int_base-int_base+i) = d; +} + +int get_tex_extension_dimen_register(int i) +{ + return (int) dimen_par(backend_dimen_base-dimen_base+i); +} + +void set_tex_extension_dimen_register(int i, int d) +{ + dimen_par(backend_dimen_base-dimen_base+i) = d; +} + +int get_tex_extension_toks_register(int i) +{ + return equiv(backend_toks_base+i); +} + +int set_tex_dimen_register(int j, scaled v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + word_define(j + scaled_base, v); + return 0; +} + +int set_tex_skip_register(int j, halfword v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + if (type(v) != glue_spec_node) + return 1; + word_define(j + skip_base, v); + return 0; +} + +int set_tex_mu_skip_register(int j, halfword v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + if (type(v) != glue_spec_node) + return 1; + word_define(j + mu_skip_base, v); + return 0; +} + +int set_tex_count_register(int j, scaled v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + word_define(j + count_base, v); + return 0; +} + +int set_tex_box_register(int j, scaled v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + define(j + box_base, box_ref_cmd, v); + return 0; +} + +int set_tex_attribute_register(int j, scaled v) +{ + int a; + if (global_defs_par > 0) + a = 4; + else + a = 0; + if (j > max_used_attr) + max_used_attr = j; + attr_list_cache = cache_disabled; + word_define(j + attribute_base, v); + return 0; +} + +int get_tex_toks_register(int j) +{ + str_number s = get_nullstr(); + if (toks(j) != null) { + s = tokens_to_string(toks(j)); + } + return s; +} + +int set_tex_toks_register(int j, lstring s) +{ + int a; + halfword ref = get_avail(); + (void) str_toks(s); + set_token_ref_count(ref, 0); + set_token_link(ref, token_link(temp_token_head)); + if (global_defs_par > 0) + a = 4; + else + a = 0; + define(j + toks_base, call_cmd, ref); + return 0; +} + +int scan_tex_toks_register(int j, int c, lstring s) +{ + int a; + halfword ref = get_avail(); + (void) str_scan_toks(c,s); + set_token_ref_count(ref, 0); + set_token_link(ref, token_link(temp_token_head)); + if (global_defs_par > 0) + a = 4; + else + a = 0; + define(j + toks_base, call_cmd, ref); + return 0; +} + +scaled get_tex_box_width(int j) +{ + halfword q = box(j); + if (q != null) + return width(q); + return 0; +} + +int set_tex_box_width(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + width(q) = v; + return 0; +} + +scaled get_tex_box_height(int j) +{ + halfword q = box(j); + if (q != null) + return height(q); + return 0; +} + +int set_tex_box_height(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + height(q) = v; + return 0; +} + +scaled get_tex_box_depth(int j) +{ + halfword q = box(j); + if (q != null) + return depth(q); + return 0; +} + +int set_tex_box_depth(int j, scaled v) +{ + halfword q = box(j); + if (q == null) + return 1; + depth(q) = v; + return 0; +} + +/*tex + + This section is devoted to the {\sl Synchronize \TeX nology} - or simply {\sl + Sync\TeX} - used to synchronize between input and output. This section + explains how synchronization basics are implemented. Before we enter into + more technical details, let us recall in a few words what is synchronization. + + \TeX\ typesetting system clearly separates the input and the output material, + and synchronization will provide a new link between both that can help text + editors and viewers to work together. More precisely, forwards + synchronization is the ability, given a location in the input source file, to + find what is the corresponding place in the output. Backwards synchronization + just performs the opposite: given a location in the output, retrieve the + corresponding material in the input source file. + + For better code management and maintainance, we adopt a naming convention. + Throughout this program, code related to the {\sl Synchronize \TeX nology} is + tagged with the ``{\sl synctex}'' key word. Any code extract where {\sl + Sync\TeX} plays its part, either explicitly or implicitly, (should) contain + the string ``{\sl synctex}''. This naming convention also holds for external + files. Moreover, all the code related to {\sl Sync\TeX} is gathered in this + section, except the definitions. + + Enabling synchronization should be performed from the command line, + |synctexoption| is used for that purpose. This global integer variable is + declared here but it is not used here. This is just a placeholder where the + command line controller will put the {\sl Sync\TeX} related options, and the + {\sl Sync\TeX} controller will read them. + +*/ + +int synctexoption; + +/*tex + + A convenient primitive is provided: \.{\\synctex=1} in the input source file + enables synchronization whereas \.{\\synctex=0} disables it. Its memory + address is |synctex_code|. It is initialized by the {\sl Sync\TeX} controller + to the command-line option if given. The controller may filter some reserved + bits. + + In order to give the {\sl Sync\TeX} controller read and write access to the + contents of the \.{\\synctex} primitive, we declare |synctexoffset|, such + that |mem[synctexoffset]| and \.{\\synctex} correspond to the same memory + storage. |synctexoffset| is initialized to the correct value when quite + everything is initialized. + +*/ + +/*tex Holds the true value of |synctex_code|: */ + +int synctexoffset; + +/*tex + + Synchronization is achieved with the help of an auxiliary file named `\.{{\sl + jobname}.synctex}' ({\sl jobname} is the contents of the \.{\\jobname} + macro), where a {\sl Sync\TeX} controller implemented in the external + |synctex.c| file will store geometrical information. This {\sl Sync\TeX} + controller will take care of every technical details concerning the {\sl + Sync\TeX} file, we will only focus on the messages the controller will + receive from the \TeX\ program. + + The most accurate synchronization information should allow to map any + character of the input source file to the corresponding location in the + output, if relevant. Ideally, the synchronization information of the input + material consists of the file name, the line and column numbers of every + character. The synchronization information in the output is simply the page + number and either point coordinates, or box dimensions and position. The + problem is that the mapping between these informations is only known at ship + out time, which means that we must keep track of the input synchronization + information until the pages ship out. + + As \TeX\ only knows about file names and line numbers, but forgets the column + numbers, we only consider a restricted input synchronization information + called {\sl Sync\TeX\ information}. It consists of a unique file name + identifier, the {\sl Sync\TeX\ file tag}, and the line number. + + Keeping track of such information, should be different whether characters or + nodes are involved. Actually, only certain nodes are involved in {\sl + Sync\TeX}, we call them {\sl synchronized nodes}. Synchronized nodes store + the {\sl Sync\TeX} information in their last two words: the first one + contains a {\sl Sync\TeX\ file tag} uniquely identifying the input file, and + the second one contains the current line number, as returned by the + \.{\\inputlineno} primitive. The |synctex_field_size| macro contains the + necessary size to store the {\sl Sync\TeX} information in a node. + + When declaring the size of a new node, it is recommanded to use the following + convention: if the node is synchronized, use a definition similar to + |my_synchronized_node_size|={\sl xxx}+|synctex_field_size|. Moreover, one + should expect that the {\sl Sync\TeX} information is always stored in the + last two words of a synchronized node. + + By default, every node with a sufficiently big size is initialized at + creation time in the |get_node| routine with the current {\sl Sync\TeX} + information, whether or not the node is synchronized. One purpose is to set + this information very early in order to minimize code dependencies, including + forthcoming extensions. Another purpose is to avoid the assumption that every + node type has a dedicated getter, where initialization should take place. + Actually, it appears that some nodes are created using directly the + |get_node| routine and not the dedicated constructor. And finally, + initializing the node at only one place is less error prone. + + Instead of storing the input file name, it is better to store just an + identifier. Each time \TeX\ opens a new file, it notifies the {\sl Sync\TeX} + controller with a |synctex_start_input| message. This controller will create + a new {\sl Sync\TeX} file tag and will update the current input state record + accordingly. If the input comes from the terminal or a pseudo file, the + |synctex_tag| is set to 0. It results in automatically disabling + synchronization for material input from the terminal or pseudo files. + + Synchronized nodes are boxes, math, kern and glue nodes. Other nodes should + be synchronized too, in particular math noads. \TeX\ assumes that math, kern + and glue nodes have the same size, this is why both are synchronized. {\sl In + fine}, only horizontal lists are really used in {\sl Sync\TeX}, but all box + nodes are considered the same with respect to synchronization, because a box + node type is allowed to change at execution time. + + {\em Nota Bene:} The {\sl Sync\TeX} code is very close to the memory model. + It is not connected to any other part of the code, except for memory + management. It is possible to neutralize the {\sl Sync\TeX} code rather + simply. The first step is to define a null |synctex_field_size|. The second + step is to comment out the code in ``Initialize bigger nodes...'' and every + ``Copy ... {\sl Sync\TeX} information''. The last step will be to comment out + the |synctex_tag_field| related code in the definition of |synctex_tag| and + the various ``Prepare ... {\sl Sync\TeX} information''. Then all the + remaining code should be just harmless. The resulting program would behave + exactly the same as if absolutely no {\sl Sync\TeX} related code was there, + including memory management. Of course, all this assumes that {\sl Sync\TeX} + is turned off from the command line. + + Here are extra variables for Web2c. (This numbering of the system-dependent + section allows easy integration of Web2c and e-\TeX, etc.) + +*/ + +/*tex where the filename to switch to starts */ + +pool_pointer edit_name_start; + +/*tex what line to start editing at */ + +int edit_name_length, edit_line; + +/*tex whether |more_name| returns false for space */ + +boolean stop_at_space; + +/*tex + + The |edit_name_start| will be set to point into |str_pool| somewhere after + its beginning if \TeX\ is supposed to switch to an editor on exit. + +*/ + +int shellenabledp; +int restrictedshell; +char *output_comment; + +/*tex + + Are we printing extra info as we read the format file? + +*/ + +boolean debug_format_file; + +void wrapup_leader(halfword p) +{ + /*tex Do some work that has been queued up for \.{\\write}. */ + if (!doing_leaders) { + int j = write_stream(p); + if (subtype(p) == write_node) { + write_out(p); + } else if (subtype(p) == close_node) { + close_write_file(j); + } else if (valid_write_file(j)) { + char *fn; + close_write_file(j); + cur_name = open_name(p); + cur_area = open_area(p); + cur_ext = open_ext(p); + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(".tex"); + fn = pack_file_name(cur_name, cur_area, cur_ext); + while (! open_write_file(j,fn)) { + fn = prompt_file_name("output file name", ".tex"); + } + } + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/extensions.w b/Build/source/texk/web2c/luatexdir/tex/extensions.w deleted file mode 100644 index 73dc3ba4514..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/extensions.w +++ /dev/null @@ -1,1211 +0,0 @@ -% extensions.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -\def\eTeX{e-\TeX} -\def\pdfTeX{pdf\TeX} - -@ @c -#include "ptexlib.h" - -@ @c -#define mode mode_par -#define tail tail_par -#define head head_par -#define dir_save dirs_par - -@ The program above includes a bunch of ``hooks'' that allow further -capabilities to be added without upsetting \TeX's basic structure. -Most of these hooks are concerned with ``whatsit'' nodes, which are -intended to be used for special purposes; whenever a new extension to -\TeX\ involves a new kind of whatsit node, a corresponding change needs -to be made to the routines below that deal with such nodes, -but it will usually be unnecessary to make many changes to the -other parts of this program. - -In order to demonstrate how extensions can be made, we shall treat -`\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}', -and `\.{\\special}' as if they were extensions. -These commands are actually primitives of \TeX, and they should -appear in all implementations of the system; but let's try to imagine -that they aren't. Then the program below illustrates how a person -could add them. - -Sometimes, of course, an extension will require changes to \TeX\ itself; -no system of hooks could be complete enough for all conceivable extensions. -The features associated with `\.{\\write}' are almost all confined to the -following paragraphs, but there are small parts of the |print_ln| and -|print_char| procedures that were introduced specifically to \.{\\write} -characters. Furthermore one of the token lists recognized by the scanner -is a |write_text|; and there are a few other miscellaneous places where we -have already provided for some aspect of \.{\\write}. The goal of a \TeX\ -extender should be to minimize alterations to the standard parts of the -program, and to avoid them completely if possible. He or she should also -be quite sure that there's no easy way to accomplish the desired goals -with the standard features that \TeX\ already has. ``Think thrice before -extending,'' because that may save a lot of work, and it will also keep -incompatible extensions of \TeX\ from proliferating. -@^system dependencies@> -@^extensions to \TeX@> - -First let's consider the format of whatsit nodes that are used to represent -the data associated with \.{\\write} and its relatives. Recall that a whatsit -has |type=whatsit_node|, and the |subtype| is supposed to distinguish -different kinds of whatsits. Each node occupies two or more words; the -exact number is immaterial, as long as it is readily determined from the -|subtype| or other data. - -We shall introduce five |subtype| values here, corresponding to the -control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, and \.{\\special}. -The second word of I/O whatsits has a |write_stream| field -that identifies the write-stream number (0 to 15, or 16 for out-of-range and -positive, or 17 for out-of-range and negative). -In the case of \.{\\write} and \.{\\special}, there is also a field that -points to the reference count of a token list that should be sent. In the -case of \.{\\openout}, we need three words and three auxiliary subfields -to hold the string numbers for name, area, and extension. - -@ Extensions might introduce new command codes; but it's best to use -|extension| with a modifier, whenever possible, so that |main_control| -stays the same. - -@ The sixteen possible \.{\\write} streams are represented by the |write_file| -array. The |j|th file is open if and only if |write_open[j]=true|. The last -two streams are special; |write_open[16]| represents a stream number -greater than 15, while |write_open[17]| represents a negative stream number, -and both of these variables are always |false|. - -@c -alpha_file write_file[last_file_selector+1]; -halfword write_file_mode[last_file_selector+1]; -halfword write_file_translation[last_file_selector+1]; -boolean write_open[last_file_selector+1]; - -scaled neg_wd; -scaled pos_wd; -scaled neg_ht; - -@ The variable |write_loc| just introduced is used to provide an -appropriate error message in case of ``runaway'' write texts. - -@c -halfword write_loc; /* |eqtb| address of \.{\\write} */ - -/* - hh: eventually i'll make \pdfextension a lua token parsed function; - a complication is that sometimes token lists are delayed -*/ - -@ When an |extension| command occurs in |main_control|, in any mode, -the |do_extension| routine is called. - -@c -int last_saved_image_index ; -int last_saved_image_pages ; -int last_saved_box_index ; -scaledpos last_position = { 0, 0 }; - -static void do_extension_dvi(int immediate) -{ - if (scan_keyword("literal")) { - new_whatsit(special_node); - write_stream(tail) = null; - scan_toks(false, true); - write_tokens(tail) = def_ref; - } else { - tex_error("unexpected use of \\dviextension",null); - } -} - -static void do_extension_pdf(int immediate) -{ - int i; - - if (scan_keyword("literal")) { - new_whatsit(pdf_literal_node); - if (scan_keyword("direct")) - set_pdf_literal_mode(tail, direct_always); - else if (scan_keyword("page")) - set_pdf_literal_mode(tail, direct_page); - else if (scan_keyword("text")) - set_pdf_literal_mode(tail, direct_text); - else if (scan_keyword("raw")) - set_pdf_literal_mode(tail, direct_raw); - else if (scan_keyword("origin")) - set_pdf_literal_mode(tail, set_origin); - else - set_pdf_literal_mode(tail, set_origin); - scan_toks(false, true); - set_pdf_literal_type(tail, normal); - set_pdf_literal_data(tail, def_ref); - } else if (scan_keyword("dest")) { - scan_pdfdest(static_pdf); - } else if (scan_keyword("annot")) { - scan_annot(static_pdf); - } else if (scan_keyword("save")) { - new_whatsit(pdf_save_node); - } else if (scan_keyword("restore")) { - new_whatsit(pdf_restore_node); - } else if (scan_keyword("setmatrix")) { - new_whatsit(pdf_setmatrix_node); - scan_toks(false, true); - set_pdf_setmatrix_data(tail, def_ref); - } else if (scan_keyword("obj")) { - scan_obj(static_pdf); - if (immediate) { - if (obj_data_ptr(static_pdf, pdf_last_obj) == 0) /* this object has not been initialized yet */ - normal_error("pdf backend","\\pdfextension obj 'reserveobjnum' cannot be used with \\immediate"); - pdf_write_obj(static_pdf, pdf_last_obj); - } - } else if (scan_keyword("refobj")) { - scan_refobj(static_pdf); - } else if (scan_keyword("colorstack")) { - scan_int(); - if (cur_val >= colorstackused()) { - print_err("Unknown color stack number "); - print_int(cur_val); - help3 - ("Allocate and initialize a color stack with \\pdfextension colorstackinit.", - "I'll use default color stack 0 here.", - "Proceed, with fingers crossed."); - error(); - cur_val = 0; - } - if (cur_val < 0) { - print_err("Invalid negative color stack number"); - help2("I'll use default color stack 0 here.", - "Proceed, with fingers crossed."); - error(); - cur_val = 0; - } - if (scan_keyword("set")) - i = colorstack_set; - else if (scan_keyword("push")) - i = colorstack_push; - else if (scan_keyword("pop")) - i = colorstack_pop; - else if (scan_keyword("current")) - i = colorstack_current; - else - i = -1; /* error */ - if (i >= 0) { - new_whatsit(pdf_colorstack_node); - set_pdf_colorstack_stack(tail, cur_val); - set_pdf_colorstack_cmd(tail, i); - set_pdf_colorstack_data(tail, null); - if (i <= colorstack_data) { - scan_toks(false, true); - set_pdf_colorstack_data(tail, def_ref); - } - } else { - print_err("Color stack action is missing"); - help3("The expected actions for \\pdfextension colorstack:", - " set, push, pop, current", - "I'll ignore the color stack command."); - error(); - } - } else if (scan_keyword("startlink")) { - scan_startlink(static_pdf); - } else if (scan_keyword("endlink")) { - if (abs(mode) == vmode) - normal_error("pdf backend", "\\pdfextension endlink cannot be used in vertical mode"); - new_whatsit(pdf_end_link_node); - } else if (scan_keyword("startthread")) { - new_annot_whatsit(pdf_start_thread_node); - scan_thread_id(); - } else if (scan_keyword("endthread")) { - new_whatsit(pdf_end_thread_node); - } else if (scan_keyword("thread")) { - new_annot_whatsit(pdf_thread_node); - scan_thread_id(); - } else if (scan_keyword("outline")) { - scan_pdfoutline(static_pdf); - } else if (scan_keyword("glyphtounicode")) { - glyph_to_unicode(); - } else if (scan_keyword("catalog")) { - scan_pdfcatalog(static_pdf); - } else if (scan_keyword("fontattr")) { - /* - The font attributes are simply initialized to zero now, this is easier to deal with from C than an - empty \TeX{} string, and surely nobody will want to set font attr to a string containing a single zero, - as that would be nonsensical in the PDF output. - */ - scan_font_ident(); - i = cur_val; - if (i == null_font) - normal_error("pdf backend", "invalid font identifier"); - scan_toks(false, true); - set_pdf_font_attr(i, tokens_to_string(def_ref)); - if (str_length(pdf_font_attr(i)) == 0) { - flush_str((str_ptr - 1)); /* from |tokens_to_string| */ - set_pdf_font_attr(i, 0); - } - } else if (scan_keyword("mapfile")) { - scan_toks(false, true); - pdfmapfile(def_ref); - delete_token_ref(def_ref); - } else if (scan_keyword("mapline")) { - scan_toks(false, true); - pdfmapline(def_ref); - delete_token_ref(def_ref); - } else if (scan_keyword("includechars")) { - pdf_include_chars(static_pdf); - } else if (scan_keyword("info")) { - scan_toks(false, true); - pdf_info_toks = concat_tokens(pdf_info_toks, def_ref); - } else if (scan_keyword("names")) { - scan_toks(false, true); - pdf_names_toks = concat_tokens(pdf_names_toks, def_ref); - } else if (scan_keyword("trailer")) { - scan_toks(false, true); - pdf_trailer_toks = concat_tokens(pdf_trailer_toks, def_ref); - } else { - tex_error("unexpected use of \\pdfextension",null); - } -} - -static void do_resource_dvi(int immediate, int code) -{ -} - -static void do_resource_pdf(int immediate, int code) -{ - switch (code) { - case use_box_resource_code: - scan_pdfrefxform(static_pdf); - break; - case use_image_resource_code: - scan_pdfrefximage(static_pdf); - break; - case save_box_resource_code: - scan_pdfxform(static_pdf); - if (immediate) { - pdf_cur_form = last_saved_box_index; - ship_out(static_pdf, obj_xform_box(static_pdf, last_saved_box_index), SHIPPING_FORM); - } - break; - case save_image_resource_code: - scan_pdfximage(static_pdf); - if (immediate) { - pdf_write_image(static_pdf, last_saved_image_index); - } - break; - } -} - -/* - - Ad immediate: - - To write a token list, we must run it through \TeX's scanner, expanding - macros and \.{\\the} and \.{\\number}, etc. This might cause runaways, - if a delimited macro parameter isn't matched, and runaways would be - extremely confusing since we are calling on \TeX's scanner in the middle - of a \.{\\shipout} command. Therefore we will put a dummy control sequence as - a ``stopper,'' right after the token list. This control sequence is - artificially defined to be \.{\\outer}. - - The presence of `\.{\\immediate}' causes the |do_extension| procedure - to descend to one level of recursion. Nothing happens unless \.{\\immediate} - is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'. - -*/ - -/* extensions are backend related */ - -@ The next subroutine uses |cur_chr| to decide what sort of whatsit is -involved, and also inserts a |write_stream| number. - -@c -static void new_write_whatsit(int w, int check) -{ - new_whatsit(cur_chr); - if (check) { - /* so we check with open and close */ - scan_limited_int(last_file_selector,NULL); - } else { - /* but we're tolerant with the rest */ - scan_int(); - if (cur_val < 0) - cur_val = term_only; - else if (cur_val > last_file_selector) { - cur_val = term_and_log; - } - } - write_stream(tail) = cur_val; -} - -void do_extension(int immediate) -{ - halfword p; /* all-purpose pointer */ - if (cur_cmd == extension_cmd) { - /* these have their own range starting at 0 */ - switch (cur_chr) { - case open_code: - p = tail; - new_write_whatsit(open_node_size,1); - scan_optional_equals(); - scan_file_name(); - open_name(tail) = cur_name; - open_area(tail) = cur_area; - open_ext(tail) = cur_ext; - if (immediate) { - out_what(static_pdf, tail); - flush_node_list(tail); - tail = p; - vlink(p) = null; - } - break; - case write_code: - /* - When `\.{\\write 12\{...\}}' appears, we scan the token list `\.{\{...\}}' - without expanding its macros; the macros will be expanded later when this - token list is rescanned. - */ - p = tail; - new_write_whatsit(write_node_size,0); - cur_cs = write_stream(tail); - scan_toks(false, false); - write_tokens(tail) = def_ref; - if (immediate) { - out_what(static_pdf, tail); - flush_node_list(tail); - tail = p; - vlink(p) = null; - } - break; - case close_code: - p = tail; - new_write_whatsit(close_node_size,1); - write_tokens(tail) = null; - if (immediate) { - out_what(static_pdf, tail); - flush_node_list(tail); - tail = p; - vlink(p) = null; - } - break; - case special_code: - /* - When `\.{\\special\{...\}}' appears, we expand the macros in the token - list as in \.{\\xdef} and \.{\\mark}. - */ - new_whatsit(special_node); - write_stream(tail) = null; - p = scan_toks(false, true); - write_tokens(tail) = def_ref; - break; - case immediate_code: - get_x_token(); - do_extension(1); - break; - case use_box_resource_code: - case use_image_resource_code: - case save_box_resource_code: - case save_image_resource_code: - switch (get_o_mode()) { - case OMODE_DVI: - do_resource_dvi(immediate,cur_chr); - break; - case OMODE_PDF: - do_resource_pdf(immediate,cur_chr); - break; - default: - break; - } - break; - /* backend extensions have their own range starting at 32 */ - case dvi_extension_code: - if (get_o_mode() == OMODE_DVI) - do_extension_dvi(immediate); - break; - case pdf_extension_code: - if (get_o_mode() == OMODE_PDF) - do_extension_pdf(immediate); - break; - /* done */ - default: - if (immediate) { - back_input(); - } else { - confusion("invalid extension"); - } - break; - } - } else { - /* no extension command, quite certainly following \immediate */ - back_input(); - } -} - -@ Here is a subroutine that creates a whatsit node having a given |subtype| -and a given number of words. It initializes only the first word of the whatsit, -and appends it to the current list. - -@c -void new_whatsit(int s) -{ - halfword p = new_node(whatsit_node, s); - couple_nodes(tail, p); - tail = p; -} - -@ The final line of this routine is slightly subtle; at least, the author -didn't think about it until getting burnt! There is a used-up token list -@^Knuth, Donald Ervin@> -on the stack, namely the one that contained |end_write_token|. (We -insert this artificial `\.{\\endwrite}' to prevent runaways, as explained -above.) If it were not removed, and if there were numerous writes on a -single page, the stack would overflow. - -@c -void expand_macros_in_tokenlist(halfword p) -{ - int old_mode; - pointer q = get_avail(); - pointer r = get_avail(); - token_info(q) = right_brace_token + '}'; - token_link(q) = r; - token_info(r) = end_write_token; - begin_token_list(q, inserted); - begin_token_list(write_tokens(p), write_text); - q = get_avail(); - token_info(q) = left_brace_token + '{'; - begin_token_list(q, inserted); - /* now we're ready to scan - `\.\{$\langle\,$token list$\,\rangle$\.{\} \\endwrite}' */ - old_mode = mode; - mode = 0; - /* disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, \.{\\prevgraf} */ - cur_cs = write_loc; - q = scan_toks(false, true); /* expand macros, etc. */ - get_token(); - if (cur_tok != end_write_token) { - /* Recover from an unbalanced write command */ - const char *hlp[] = { - "On this page there's a \\write with fewer real {'s than }'s.", - "I can't handle that very well; good luck.", NULL - }; - tex_error("Unbalanced write command", hlp); - do { - get_token(); - } while (cur_tok != end_write_token); - } - mode = old_mode; - end_token_list(); /* conserve stack space */ -} - -void write_out(halfword p) -{ - int old_setting; /* holds print |selector| */ - int j; /* write stream number */ - char *s, *ss; /* line to be written, as a C string */ - int callback_id; - int lua_retval; - expand_macros_in_tokenlist(p); - old_setting = selector; - j = write_stream(p); - if (file_can_be_written(j)) { - selector = j; - } else if ((j == term_only) && (selector == term_and_log)) { - /* write to the terminal if file isn't open */ - selector = log_only; - tprint_nl(""); - } else { - tprint_nl(""); - } - s = tokenlist_to_cstring(def_ref, false, NULL); - if (selector < no_print) { - /* selector is a file */ - callback_id = callback_defined(process_output_buffer_callback); - if (callback_id > 0) { - /* fix up the output buffer using callbacks */ - lua_retval = run_callback(callback_id, "S->S", s, &ss); - if ((lua_retval == true) && (ss != NULL)) { - xfree(s); - s = ss; - } - } - } - tprint(s); - xfree(s); - print_ln(); - flush_list(def_ref); - selector = old_setting; -} - -void finalize_write_files(void) { - int k; - for (k = 0; k <= last_file_selector; k++) { - if (write_open[k]) { - lua_a_close_out(write_file[k]); - } - } -} - -void initialize_write_files(void) { - int k; - for (k = 0; k <= last_file_selector; k++) { - write_open[k] = false; - } -} - -void close_write_file(int id) { - if (write_open[id]) { - lua_a_close_out(write_file[id]); - write_open[id] = false; - } -} - -boolean open_write_file(int id, char *fn) { - if (lua_a_open_out(&(write_file[id]), fn, (id + 1))) { - write_open[id] = true; - return true; - } else { - return false; - } -} - - -@ To implement primitives as \.{\\pdfextension info}, \.{\\pdfextension catalog} or -\.{\\pdfextension names} we need to concatenate tokens lists. - -@c -halfword concat_tokens(halfword q, halfword r) -{ /* concat |q| and |r| and returns the result tokens list */ - halfword p; - if (q == null) - return r; - p = q; - while (token_link(p) != null) - p = token_link(p); - set_token_link(p, token_link(r)); - free_avail(r); - return q; -} - -@ The \eTeX\ features available in extended mode are grouped into two -categories: (1)~Some of them are permanently enabled and have no -semantic effect as long as none of the additional primitives are -executed. (2)~The remaining \eTeX\ features are optional and can be -individually enabled and disabled. For each optional feature there is -an \eTeX\ state variable named \.{\\...state}; the feature is enabled, -resp.\ disabled by assigning a positive, resp.\ non-positive value to -that integer. - -@ In order to handle \.{\\everyeof} we need an array |eof_seen| of -boolean variables. - -@c -boolean *eof_seen; /* has eof been seen? */ - -@ The |print_group| procedure prints the current level of grouping and -the name corresponding to |cur_group|. - -@c -void print_group(boolean e) -{ - switch (cur_group) { - case bottom_level: - tprint("bottom level"); - return; - break; - case simple_group: - case semi_simple_group: - if (cur_group == semi_simple_group) - tprint("semi "); - tprint("simple"); - break;; - case hbox_group: - case adjusted_hbox_group: - if (cur_group == adjusted_hbox_group) - tprint("adjusted "); - tprint("hbox"); - break; - case vbox_group: - tprint("vbox"); - break; - case vtop_group: - tprint("vtop"); - break; - case align_group: - case no_align_group: - if (cur_group == no_align_group) - tprint("no "); - tprint("align"); - break; - case output_group: - tprint("output"); - break; - case disc_group: - tprint("disc"); - break; - case insert_group: - tprint("insert"); - break; - case vcenter_group: - tprint("vcenter"); - break; - case math_group: - case math_choice_group: - case math_shift_group: - case math_left_group: - tprint("math"); - if (cur_group == math_choice_group) - tprint(" choice"); - else if (cur_group == math_shift_group) - tprint(" shift"); - else if (cur_group == math_left_group) - tprint(" left"); - break; - } /* there are no other cases */ - tprint(" group (level "); - print_int(cur_level); - print_char(')'); - if (saved_value(-1) != 0) { /* |saved_line| */ - if (e) - tprint(" entered at line "); - else - tprint(" at line "); - print_int(saved_value(-1)); /* |saved_line| */ - } -} - -@ The |group_trace| procedure is called when a new level of grouping -begins (|e=false|) or ends (|e=true|) with |saved_value(-1)| containing the -line number. - -@c -void group_trace(boolean e) -{ - begin_diagnostic(); - print_char('{'); - if (e) - tprint("leaving "); - else - tprint("entering "); - print_group(e); - print_char('}'); - end_diagnostic(false); -} - -@ A group entered (or a conditional started) in one file may end in a -different file. Such slight anomalies, although perfectly legitimate, -may cause errors that are difficult to locate. In order to be able to -give a warning message when such anomalies occur, \eTeX\ uses the -|grp_stack| and |if_stack| arrays to record the initial |cur_boundary| -and |cond_ptr| values for each input file. - -@c -save_pointer *grp_stack; /* initial |cur_boundary| */ -halfword *if_stack; /* initial |cond_ptr| */ - -@ When a group ends that was apparently entered in a different input -file, the |group_warning| procedure is invoked in order to update the -|grp_stack|. If moreover \.{\\tracingnesting} is positive we want to -give a warning message. The situation is, however, somewhat complicated -by two facts: (1)~There may be |grp_stack| elements without a -corresponding \.{\\input} file or \.{\\scantokens} pseudo file (e.g., -error insertions from the terminal); and (2)~the relevant information is -recorded in the |name_field| of the |input_stack| only loosely -synchronized with the |in_open| variable indexing |grp_stack|. - -@c -void group_warning(void) -{ - boolean w = false; /* do we need a warning? */ - int i = in_open; /* index into |grp_stack| */ - base_ptr = input_ptr; - input_stack[base_ptr] = cur_input; /* store current state */ - while ((grp_stack[i] == cur_boundary) && (i > 0)) { - /* Set variable |w| to indicate if this case should be reported */ - /* This code scans the input stack in order to determine the type of the - current input file. */ - if (tracing_nesting_par > 0) { - while ((input_stack[base_ptr].state_field == token_list) || - (input_stack[base_ptr].index_field > i)) - decr(base_ptr); - if (input_stack[base_ptr].name_field > 17) - w = true; - } - - grp_stack[i] = save_value(save_ptr); - decr(i); - } - if (w) { - tprint_nl("Warning: end of "); - print_group(true); - tprint(" of a different file"); - print_ln(); - if (tracing_nesting_par > 1) - show_context(); - if (history == spotless) - history = warning_issued; - } -} - -@ When a conditional ends that was apparently started in a different -input file, the |if_warning| procedure is invoked in order to update the -|if_stack|. If moreover \.{\\tracingnesting} is positive we want to -give a warning message (with the same complications as above). - -@c -void if_warning(void) -{ - boolean w = false; /* do we need a warning? */ - int i = in_open; - base_ptr = input_ptr; - input_stack[base_ptr] = cur_input; /* store current state */ - while (if_stack[i] == cond_ptr) { - /* Set variable |w| to... */ - if (tracing_nesting_par > 0) { - while ((input_stack[base_ptr].state_field == token_list) || - (input_stack[base_ptr].index_field > i)) - decr(base_ptr); - if (input_stack[base_ptr].name_field > 17) - w = true; - } - - if_stack[i] = vlink(cond_ptr); - decr(i); - } - if (w) { - tprint_nl("Warning: end of "); - print_cmd_chr(if_test_cmd, cur_if); - print_if_line(if_line); - tprint(" of a different file"); - print_ln(); - if (tracing_nesting_par > 1) - show_context(); - if (history == spotless) - history = warning_issued; - } -} - - -@ Conversely, the |file_warning| procedure is invoked when a file ends -and some groups entered or conditionals started while reading from that -file are still incomplete. - -@c -void file_warning(void) -{ - halfword p = save_ptr; /* saved value of |save_ptr| or |cond_ptr| */ - int l = cur_level; /* saved value of |cur_level| or |if_limit| */ - int c = cur_group; /* saved value of |cur_group| or |cur_if| */ - int i; /* saved value of |if_line| */ - save_ptr = cur_boundary; - while (grp_stack[in_open] != save_ptr) { - decr(cur_level); - tprint_nl("Warning: end of file when "); - print_group(true); - tprint(" is incomplete"); - cur_group = save_level(save_ptr); - save_ptr = save_value(save_ptr); - } - save_ptr = p; - cur_level = (quarterword) l; - cur_group = (group_code) c; /* restore old values */ - p = cond_ptr; - l = if_limit; - c = cur_if; - i = if_line; - while (if_stack[in_open] != cond_ptr) { - tprint_nl("Warning: end of file when "); - print_cmd_chr(if_test_cmd, cur_if); - if (if_limit == fi_code) - tprint_esc("else"); - print_if_line(if_line); - tprint(" is incomplete"); - if_line = if_line_field(cond_ptr); - cur_if = if_limit_subtype(cond_ptr); - if_limit = if_limit_type(cond_ptr); - cond_ptr = vlink(cond_ptr); - } - cond_ptr = p; - if_limit = l; - cur_if = c; - if_line = i; /* restore old values */ - print_ln(); - if (tracing_nesting_par > 1) - show_context(); - if (history == spotless) - history = warning_issued; -} - -@ @c -halfword last_line_fill; /* the |par_fill_skip| glue node of the new paragraph */ - -@ The lua interface needs some extra functions. The functions -themselves are quite boring, but they are handy because otherwise this -internal stuff has to be accessed from C directly, where lots of the -defines are not available. - -@c -#define get_tex_dimen_register(j) dimen(j) -#define get_tex_skip_register(j) skip(j) -#define get_tex_mu_skip_register(j) mu_skip(j) -#define get_tex_count_register(j) count(j) -#define get_tex_attribute_register(j) attribute(j) -#define get_tex_box_register(j) box(j) - -/* these can now be macros (todo) */ - -int get_tex_extension_count_register(int i) -{ - return (int) int_par(backend_int_base-int_base+i); -} - -void set_tex_extension_count_register(int i, int d) -{ - int_par(backend_int_base-int_base+i) = d; -} - -int get_tex_extension_dimen_register(int i) -{ - return (int) dimen_par(backend_dimen_base-dimen_base+i); -} - -void set_tex_extension_dimen_register(int i, int d) -{ - dimen_par(backend_dimen_base-dimen_base+i) = d; -} - -int get_tex_extension_toks_register(int i) -{ - return equiv(backend_toks_base+i); -} - -int set_tex_dimen_register(int j, scaled v) -{ - int a; /* return non-nil for error */ - if (global_defs_par > 0) - a = 4; - else - a = 0; - word_define(j + scaled_base, v); - return 0; -} - -int set_tex_skip_register(int j, halfword v) -{ - int a; /* return non-nil for error */ - if (global_defs_par > 0) - a = 4; - else - a = 0; - if (type(v) != glue_spec_node) - return 1; - word_define(j + skip_base, v); - return 0; -} - -int set_tex_mu_skip_register(int j, halfword v) -{ - int a; /* return non-nil for error */ - if (global_defs_par > 0) - a = 4; - else - a = 0; - if (type(v) != glue_spec_node) - return 1; - word_define(j + mu_skip_base, v); - return 0; -} - -int set_tex_count_register(int j, scaled v) -{ - int a; /* return non-nil for error */ - if (global_defs_par > 0) - a = 4; - else - a = 0; - word_define(j + count_base, v); - return 0; -} - -int set_tex_box_register(int j, scaled v) -{ - int a; /* return non-nil for error */ - if (global_defs_par > 0) - a = 4; - else - a = 0; - define(j + box_base, box_ref_cmd, v); - return 0; -} - -int set_tex_attribute_register(int j, scaled v) -{ - int a; /* return non-nil for error */ - if (global_defs_par > 0) - a = 4; - else - a = 0; - if (j > max_used_attr) - max_used_attr = j; - attr_list_cache = cache_disabled; - word_define(j + attribute_base, v); - return 0; -} - -int get_tex_toks_register(int j) -{ - str_number s = get_nullstr(); - if (toks(j) != null) { - s = tokens_to_string(toks(j)); - } - return s; -} - -int set_tex_toks_register(int j, lstring s) -{ - int a; - halfword ref = get_avail(); - (void) str_toks(s); - set_token_ref_count(ref, 0); - set_token_link(ref, token_link(temp_token_head)); - if (global_defs_par > 0) - a = 4; - else - a = 0; - define(j + toks_base, call_cmd, ref); - return 0; -} - -int scan_tex_toks_register(int j, int c, lstring s) -{ - int a; - halfword ref = get_avail(); - (void) str_scan_toks(c,s); - set_token_ref_count(ref, 0); - set_token_link(ref, token_link(temp_token_head)); - if (global_defs_par > 0) - a = 4; - else - a = 0; - define(j + toks_base, call_cmd, ref); - return 0; -} - -scaled get_tex_box_width(int j) -{ - halfword q = box(j); - if (q != null) - return width(q); - return 0; -} - -int set_tex_box_width(int j, scaled v) -{ - halfword q = box(j); - if (q == null) - return 1; - width(q) = v; - return 0; -} - -scaled get_tex_box_height(int j) -{ - halfword q = box(j); - if (q != null) - return height(q); - return 0; -} - -int set_tex_box_height(int j, scaled v) -{ - halfword q = box(j); - if (q == null) - return 1; - height(q) = v; - return 0; -} - -scaled get_tex_box_depth(int j) -{ - halfword q = box(j); - if (q != null) - return depth(q); - return 0; -} - -int set_tex_box_depth(int j, scaled v) -{ - halfword q = box(j); - if (q == null) - return 1; - depth(q) = v; - return 0; -} - -@ This section is devoted to the {\sl Synchronize \TeX nology} -- or simply {\sl Sync\TeX} - used to synchronize between input and output. -This section explains how synchronization basics are implemented. -Before we enter into more technical details, -let us recall in a few words what is synchronization. - -\TeX\ typesetting system clearly separates the input and the output material, -and synchronization will provide a new link between both that can help -text editors and viewers to work together. -More precisely, forwards synchronization is the ability, -given a location in the input source file, -to find what is the corresponding place in the output. -Backwards synchronization just performs the opposite: -given a location in the output, -retrieve the corresponding material in the input source file. - -For better code management and maintainance, we adopt a naming convention. -Throughout this program, code related to the {\sl Synchronize \TeX nology} is tagged -with the ``{\sl synctex}'' key word. Any code extract where {\sl Sync\TeX} plays -its part, either explicitly or implicitly, (should) contain the string ``{\sl synctex}''. -This naming convention also holds for external files. -Moreover, all the code related to {\sl Sync\TeX} is gathered in this section, -except the definitions. - -Enabling synchronization should be performed from the command line, -|synctexoption| is used for that purpose. -This global integer variable is declared here but it is not used here. -This is just a placeholder where the command line controller will put -the {\sl Sync\TeX} related options, and the {\sl Sync\TeX} controller will read them. - -@c -int synctexoption; - -@ A convenient primitive is provided: -\.{\\synctex=1} in the input source file enables synchronization whereas -\.{\\synctex=0} disables it. -Its memory address is |synctex_code|. -It is initialized by the {\sl Sync\TeX} controller to the command-line option if given. -The controller may filter some reserved bits. - -In order to give the {\sl Sync\TeX} controller read and write access to -the contents of the \.{\\synctex} primitive, we declare |synctexoffset|, -such that |mem[synctexoffset]| and \.{\\synctex} correspond to -the same memory storage. |synctexoffset| is initialized to -the correct value when quite everything is initialized. - -@c -int synctexoffset; /* holds the true value of |synctex_code| */ - -@ Synchronization is achieved with the help of an auxiliary file named -`\.{{\sl jobname}.synctex}' ({\sl jobname} is the contents of the -\.{\\jobname} macro), where a {\sl Sync\TeX} controller implemented -in the external |synctex.c| file will store geometrical information. -This {\sl Sync\TeX} controller will take care of every technical details -concerning the {\sl Sync\TeX} file, we will only focus on the messages -the controller will receive from the \TeX\ program. - -The most accurate synchronization information should allow to map -any character of the input source file to the corresponding location -in the output, if relevant. -Ideally, the synchronization information of the input material consists of -the file name, the line and column numbers of every character. -The synchronization information in the output is simply the page number and -either point coordinates, or box dimensions and position. -The problem is that the mapping between these informations is only known at -ship out time, which means that we must keep track of the input -synchronization information until the pages ship out. - -As \TeX\ only knows about file names and line numbers, -but forgets the column numbers, we only consider a -restricted input synchronization information called {\sl Sync\TeX\ information}. -It consists of a unique file name identifier, the {\sl Sync\TeX\ file tag}, -and the line number. - -Keeping track of such information, -should be different whether characters or nodes are involved. -Actually, only certain nodes are involved in {\sl Sync\TeX}, -we call them {\sl synchronized nodes}. -Synchronized nodes store the {\sl Sync\TeX} information in their last two words: -the first one contains a {\sl Sync\TeX\ file tag} uniquely identifying -the input file, and the second one contains the current line number, -as returned by the \.{\\inputlineno} primitive. -The |synctex_field_size| macro contains the necessary size to store -the {\sl Sync\TeX} information in a node. - -When declaring the size of a new node, it is recommanded to use the following -convention: if the node is synchronized, use a definition similar to -|my_synchronized_node_size|={\sl xxx}+|synctex_field_size|. -Moreover, one should expect that the {\sl Sync\TeX} information is always stored -in the last two words of a synchronized node. - -By default, every node with a sufficiently big size is initialized -at creation time in the |get_node| routine with the current -{\sl Sync\TeX} information, whether or not the node is synchronized. -One purpose is to set this information very early in order to minimize code -dependencies, including forthcoming extensions. -Another purpose is to avoid the assumption that every node type has a dedicated getter, -where initialization should take place. Actually, it appears that some nodes are created -using directly the |get_node| routine and not the dedicated constructor. -And finally, initializing the node at only one place is less error prone. - -Instead of storing the input file name, it is better to store just an identifier. -Each time \TeX\ opens a new file, it notifies the {\sl Sync\TeX} controller with -a |synctex_start_input| message. -This controller will create a new {\sl Sync\TeX} file tag and -will update the current input state record accordingly. -If the input comes from the terminal or a pseudo file, the |synctex_tag| is set to 0. -It results in automatically disabling synchronization for material -input from the terminal or pseudo files. - -Synchronized nodes are boxes, math, kern and glue nodes. -Other nodes should be synchronized too, in particular math noads. -\TeX\ assumes that math, kern and glue nodes have the same size, -this is why both are synchronized. -{\sl In fine}, only horizontal lists are really used in {\sl Sync\TeX}, -but all box nodes are considered the same with respect to synchronization, -because a box node type is allowed to change at execution time. - -{\sl Nota Bene:} -The {\sl Sync\TeX} code is very close to the memory model. -It is not connected to any other part of the code, -except for memory management. It is possible to neutralize the {\sl Sync\TeX} code -rather simply. The first step is to define a null |synctex_field_size|. -The second step is to comment out the code in ``Initialize bigger nodes...'' and every -``Copy ... {\sl Sync\TeX} information''. -The last step will be to comment out the |synctex_tag_field| related code in the -definition of |synctex_tag| and the various ``Prepare ... {\sl Sync\TeX} information''. -Then all the remaining code should be just harmless. -The resulting program would behave exactly the same as if absolutely no {\sl Sync\TeX} -related code was there, including memory management. -Of course, all this assumes that {\sl Sync\TeX} is turned off from the command line. -@^synctex@> -@^synchronization@> - -@ Here are extra variables for Web2c. (This numbering of the -system-dependent section allows easy integration of Web2c and e-\TeX, etc.) -@^<system dependencies@> - -@c -pool_pointer edit_name_start; /* where the filename to switch to starts */ -int edit_name_length, edit_line; /* what line to start editing at */ -boolean stop_at_space; /* whether |more_name| returns false for space */ - -@ The |edit_name_start| will be set to point into |str_pool| somewhere after -its beginning if \TeX\ is supposed to switch to an editor on exit. - -@c -int shellenabledp; -int restrictedshell; -char *output_comment; - -@ Are we printing extra info as we read the format file? - -@c -boolean debug_format_file; diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.w b/Build/source/texk/web2c/luatexdir/tex/filename.c index 9e5d9c5cc1c..fdc6641efca 100644 --- a/Build/source/texk/web2c/luatexdir/tex/filename.w +++ b/Build/source/texk/web2c/luatexdir/tex/filename.c @@ -1,59 +1,62 @@ -% filename.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* +filename.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ In order to isolate the system-dependent aspects of file names, the - @^system dependencies@> - system-independent parts of \TeX\ are expressed in terms - of three system-dependent - procedures called |begin_name|, |more_name|, and |end_name|. In - essence, if the user-specified characters of the file name are $c_1\ldots c_n$, - the system-independent driver program does the operations - $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); - \,|end_name|.$$ - These three procedures communicate with each other via global variables. - Afterwards the file name will appear in the string pool as three strings - called |cur_name|\penalty10000\hskip-.05em, - |cur_area|, and |cur_ext|; the latter two are null (i.e., - |""|), unless they were explicitly specified by the user. - - Actually the situation is slightly more complicated, because \TeX\ needs - to know when the file name ends. The |more_name| routine is a function - (with side effects) that returns |true| on the calls |more_name|$(c_1)$, - \dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ - returns |false|; or, it returns |true| and the token following $c_n$ is - something like `\.{\\hbox}' (i.e., not a character). In other words, - |more_name| is supposed to return |true| unless it is sure that the - file name has been completely scanned; and |end_name| is supposed to be able - to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of - whether $|more_name|(c_n)$ returned |true| or |false|. - - -@ Here now is the first of the system-dependent routines for file name scanning. -@^system dependencies@> - -@c +/*tex + + In order to isolate the system-dependent aspects of file names, the @^system + dependencies@> system-independent parts of \TeX\ are expressed in terms of + three system-dependent procedures called |begin_name|, |more_name|, and + |end_name|. In essence, if the user-specified characters of the file name are + $c_1\ldots c_n$, the system-independent driver program does the operations + $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); + \,|end_name|.$$ + + These three procedures communicate with each other via global variables. + Afterwards the file name will appear in the string pool as three strings + called |cur_name|\penalty10000\hskip-.05em, |cur_area|, and |cur_ext|; the + latter two are null (i.e., |""|), unless they were explicitly specified by + the user. + + Actually the situation is slightly more complicated, because \TeX\ needs to + know when the file name ends. The |more_name| routine is a function (with + side effects) that returns |true| on the calls |more_name|$(c_1)$, \dots, + |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ returns |false|; + or, it returns |true| and the token following $c_n$ is something like + `\.{\\hbox}' (i.e., not a character). In other words, |more_name| is supposed + to return |true| unless it is sure that the file name has been completely + scanned; and |end_name| is supposed to be able to finish the assembly of + |cur_name|, |cur_area|, and |cur_ext| regardless of whether + $|more_name|(c_n)$ returned |true| or |false|. + + + Here now is the first of the system-dependent routines for file name + scanning. @^system dependencies@> + +*/ + static void begin_name(void) { area_delimiter = 0; @@ -61,13 +64,16 @@ static void begin_name(void) quoted_filename = false; } -@ And here's the second. The string pool might change as the file name is - being scanned, since a new \.{\\csname} might be entered; therefore we keep - |area_delimiter| and |ext_delimiter| relative to the beginning of the current - string, instead of assigning an absolute address like |pool_ptr| to them. - @^system dependencies@> +/*tex + + And here's the second. The string pool might change as the file name is being + scanned, since a new \.{\\csname} might be entered; therefore we keep + |area_delimiter| and |ext_delimiter| relative to the beginning of the current + string, instead of assigning an absolute address like |pool_ptr| to them. + @^system dependencies@> + +*/ -@c static boolean more_name(ASCII_code c) { if (c == ' ' && stop_at_space && (!quoted_filename)) { @@ -77,7 +83,7 @@ static boolean more_name(ASCII_code c) return true; } else { str_room(1); - append_char(c); /* contribute |c| to the current string */ + append_char(c); if (IS_DIR_SEP(c)) { area_delimiter = (pool_pointer) cur_length; ext_delimiter = 0; @@ -87,17 +93,21 @@ static boolean more_name(ASCII_code c) } } -@ The third. -@^system dependencies@> +/*tex + + The third. @^system dependencies@> + +*/ -@c static void end_name(void) { unsigned char *s; if (str_ptr + 3 > (max_strings + STRING_OFFSET)) - overflow("number of strings", - (unsigned) (max_strings - init_str_ptr + STRING_OFFSET)); - /* at this point, the full string lives in |cur_string| */ + overflow( + "number of strings", + (unsigned) (max_strings - init_str_ptr + STRING_OFFSET) + ); + /*tex At this point, the full string lives in |cur_string|. */ if (area_delimiter == 0) { cur_area = get_nullstr(); } else { @@ -105,7 +115,7 @@ static void end_name(void) cur_string[area_delimiter] = '\0'; cur_length = (unsigned) strlen((char *) cur_string); cur_area = make_string(); - xfree(cur_string); + xfree(cur_string); cur_length = (unsigned) strlen((char *) s); cur_string = s; } @@ -118,24 +128,27 @@ static void end_name(void) cur_string[l] = '\0'; cur_length = (unsigned) strlen((char *) cur_string); cur_name = make_string(); - xfree(cur_string); + xfree(cur_string); cur_length = (unsigned) strlen((char *) s); cur_string = s; cur_ext = make_string(); } } -@ Now let's consider the ``driver'' routines by which \TeX\ deals with file names - in a system-independent manner. First comes a procedure that looks for a +/*tex + + Now let's consider the ``driver'' routines by which \TeX\ deals with file + names in a system-independent manner. First comes a procedure that looks for a file name in the input by calling |get_x_token| for the information. -@c +*/ + void scan_file_name(void) { str_number u = 0; name_in_progress = true; begin_name(); - /* Get the next non-blank non-call token; */ + /*tex Get the next non-blank non-call token: */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); @@ -145,9 +158,11 @@ void scan_file_name(void) back_input(); break; } - /* If |cur_chr| is a space and we're not scanning a token list, check - whether we're at the end of the buffer. Otherwise we end up adding - spurious spaces to file names in some cases. */ + /*tex + If |cur_chr| is a space and we're not scanning a token list, check + whether we're at the end of the buffer. Otherwise we end up adding + spurious spaces to file names in some cases. + */ if ((cur_chr == ' ') && (istate != token_list) && (iloc > ilimit) && !quoted_filename) break; @@ -174,8 +189,10 @@ void scan_file_name(void) name_in_progress = false; } -@ This function constructs a the three file name strings from a token list -@c +/* + This function constructs a the three file name strings from a token list. +*/ + void scan_file_name_toks(void) { char *a, *n, *e, *s = NULL; @@ -192,12 +209,14 @@ void scan_file_name_toks(void) e = s + i; } } - if (n != s) { /* explicit area */ + if (n != s) { + /*tex explicit area */ cur_area = maketexlstring(a, (size_t) (n - a)); } else { cur_area = get_nullstr(); } - if (e != NULL) { /* explicit extension */ + if (e != NULL) { + /*tex explicit extension */ cur_name = maketexlstring(n, (size_t) (e - n)); cur_ext = maketexstring(e); } else { @@ -205,33 +224,34 @@ void scan_file_name_toks(void) cur_ext = get_nullstr(); } xfree(s); - -} - +} +/*tex -@ Here is a routine that manufactures the output file names, assuming that - |job_name<>0|. It ignores and changes the current settings of |cur_area| - and |cur_ext|. + Here is a routine that manufactures the output file names, assuming that + |job_name<>0|. It ignores and changes the current settings of |cur_area| and + |cur_ext|; |s = ".log"|, |".dvi"|, or |format_extension| +*/ -@c char *pack_job_name(const char *s) -{ /* |s = ".log"|, |".dvi"|, or |format_extension| */ +{ cur_area = get_nullstr(); cur_ext = maketexstring(s); cur_name = job_name; return pack_file_name(cur_name, cur_area, cur_ext); } -@ If some trouble arises when \TeX\ tries to open a file, the following - routine calls upon the user to supply another file name. Parameter~|s| - is used in the error message to identify the type of file; parameter~|e| - is the default extension if none is given. Upon exit from the routine, - variables |cur_name|, |cur_area|, and |cur_ext| are - ready for another attempt at file opening. +/*tex + + If some trouble arises when \TeX\ tries to open a file, the following routine + calls upon the user to supply another file name. Parameter~|s| is used in the + error message to identify the type of file; parameter~|e| is the default + extension if none is given. Upon exit from the routine, variables |cur_name|, + |cur_area|, and |cur_ext| are ready for another attempt at file opening. + +*/ -@c char *prompt_file_name(const char *s, const char *e) { int k; /* index into |buffer| */ @@ -290,12 +310,10 @@ char *prompt_file_name(const char *s, const char *e) return pack_file_name(cur_name, cur_area, cur_ext); } - -@ @c void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e) { - boolean must_quote; /* whether to quote the filename */ - unsigned char *j; /* index into string */ + boolean must_quote; /* whether to quote the filename */ + unsigned char *j; /* index into string */ must_quote = false; if (a != NULL) { j = a; @@ -318,13 +336,11 @@ void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e) j++; } } - /* FIXME: Alternative is to assume that any filename that has to be quoted has - at least one quoted component...if we pick this, a number of insertions - of |print_file_name| should go away. - |must_quote|:=((|a|<>0)and(|str_pool|[|str_start|[|a|]]=""""))or - ((|n|<>0)and(|str_pool|[|str_start|[|n|]]=""""))or - ((|e|<>0)and(|str_pool|[|str_start|[|e|]]="""")); */ - + /*tex + Alternative is to assume that any filename that has to be quoted has at + least one quoted component...if we pick this, a number of insertions of + |print_file_name| should go away. + */ if (must_quote) print_char('"'); if (a != NULL) { @@ -346,15 +362,17 @@ void tprint_file_name(unsigned char *n, unsigned char *a, unsigned char *e) print_char('"'); } -@ @c void print_file_name(str_number n, str_number a, str_number e) { char *nam, *are, *ext; nam = makecstring(n); are = makecstring(a); ext = makecstring(e); - tprint_file_name((unsigned char *) nam, (unsigned char *) are, - (unsigned char *) ext); + tprint_file_name( + (unsigned char *) nam, + (unsigned char *) are, + (unsigned char *) ext + ); free(nam); free(are); free(ext); diff --git a/Build/source/texk/web2c/luatexdir/tex/inputstack.w b/Build/source/texk/web2c/luatexdir/tex/inputstack.c index 68b22e1a334..c64111edc89 100644 --- a/Build/source/texk/web2c/luatexdir/tex/inputstack.w +++ b/Build/source/texk/web2c/luatexdir/tex/inputstack.c @@ -1,51 +1,85 @@ -% inputstack.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +inputstack.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ @c in_state_record *input_stack = NULL; -int input_ptr = 0; /* first unused location of |input_stack| */ -int max_in_stack = 0; /* largest value of |input_ptr| when pushing */ -in_state_record cur_input; /* the ``top'' input state */ -int in_open = 0; /* the number of lines in the buffer, less one */ -int open_parens = 0; /* the number of open text files */ +/*tex First unused location of |input_stack|: */ + +int input_ptr = 0; + +/*tex Largest value of |input_ptr| when pushing: */ + +int max_in_stack = 0; + +/*tex The `top' input state: */ + +in_state_record cur_input; + +/*tex The number of lines in the buffer, less one: */ + +int in_open = 0; + +/*tex The number of open text files: */ + +int open_parens = 0; + alpha_file *input_file = NULL; -int line = 0; /* current line number in the current source file */ + +/*tex The current line number in the current source file: */ + +int line = 0; + int *line_stack = NULL; + str_number *source_filename_stack = NULL; + char **full_source_filename_stack = NULL; -int scanner_status = 0; /* can a subfile end now? */ -pointer warning_index = null; /* identifier relevant to non-|normal| scanner status */ -pointer def_ref = null; /* reference count of token list being defined */ +/*tex Can a subfile end now? */ -@ Here is a procedure that uses |scanner_status| to print a warning message -when a subfile has ended, and at certain other crucial times: +int scanner_status = 0; + +/*tex Identifier relevant to non-|normal| scanner status: */ + +pointer warning_index = null; + +/*tex Reference count of token list being defined: */ + +pointer def_ref = null; + +/*tex + +Here is a procedure that uses |scanner_status| to print a warning message when a +subfile has ended, and at certain other crucial times: + +*/ -@c void runaway(void) { - pointer p = null; /* head of runaway list */ + /*tex The head of the runaway list: */ + pointer p = null; if (scanner_status > skipping) { switch (scanner_status) { case defining: @@ -65,7 +99,7 @@ void runaway(void) p = def_ref; break; default: - /* there are no other cases */ + /*tex There are no other cases. */ break; } print_char('?'); @@ -74,48 +108,69 @@ void runaway(void) } } -@ The |param_stack| is an auxiliary array used to hold pointers to the token -lists for parameters at the current level and subsidiary levels of input. -This stack is maintained with convention (2), and it grows at a different -rate from the others. - -@c -pointer *param_stack = NULL; /* token list pointers for parameters */ -int param_ptr = 0; /* first unused entry in |param_stack| */ -int max_param_stack = 0; /* largest value of |param_ptr|, will be |<=param_size+9| */ - -@ The input routines must also interact with the processing of -\.{\\halign} and \.{\\valign}, since the appearance of tab marks and -\.{\\cr} in certain places is supposed to trigger the beginning of special -$v_j$ template text in the scanner. This magic is accomplished by an -|align_state| variable that is increased by~1 when a `\.{\char'173}' is -scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state| -is nonzero during the $u_j$ template, after which it is set to zero; the -$v_j$ template begins when a tab mark or \.{\\cr} occurs at a time that -|align_state=0|. - -@c -int align_state = 0; /* group level with respect to current alignment */ - -@ Thus, the ``current input state'' can be very complicated indeed; there -can be many levels and each level can arise in a variety of ways. The -|show_context| procedure, which is used by \TeX's error-reporting routine to -print out the current input state on all levels down to the most recent -line of characters from an input file, illustrates most of these conventions. -The global variable |base_ptr| contains the lowest level that was -displayed by this procedure. - -@c -int base_ptr = 0; /* shallowest level shown by |show_context| */ - -@ The status at each level is indicated by printing two lines, where the first -line indicates what was read so far and the second line shows what remains -to be read. The context is cropped, if necessary, so that the first line -contains at most |half_error_line| characters, and the second contains -at most |error_line|. Non-current input levels whose |token_type| is -`|backed_up|' are shown only if they have not been fully read. - -@c +/*tex + +The |param_stack| is an auxiliary array used to hold pointers to the token lists +for parameters at the current level and subsidiary levels of input. This stack is +maintained with convention (2), and it grows at a different rate from the others. + +*/ + +/*tex Token list pointers for parameters: */ + +pointer *param_stack = NULL; + +/*tex First unused entry in |param_stack|: */ + +int param_ptr = 0; + +/*tex Largest value of |param_ptr|, will be |<=param_size+9|: */ + +int max_param_stack = 0; + +/*tex + +The input routines must also interact with the processing of \.{\\halign} and +\.{\\valign}, since the appearance of tab marks and \.{\\cr} in certain places is +supposed to trigger the beginning of special $v_j$ template text in the scanner. +This magic is accomplished by an |align_state| variable that is increased by~1 +when a `\.{\char'173}' is scanned and decreased by~1 when a `\.{\char'175}' is +scanned. The |align_state| is nonzero during the $u_j$ template, after which it +is set to zero; the $v_j$ template begins when a tab mark or \.{\\cr} occurs at a +time that |align_state=0|. + +*/ + +/*tex The group level with respect to current alignment: */ + +int align_state = 0; + +/*tex + +Thus, the ``current input state'' can be very complicated indeed; there can be +many levels and each level can arise in a variety of ways. The |show_context| +procedure, which is used by \TeX's error-reporting routine to print out the +current input state on all levels down to the most recent line of characters from +an input file, illustrates most of these conventions. The global variable +|base_ptr| contains the lowest level that was displayed by this procedure. + +*/ + +/*tex The shallowest level shown by |show_context|: */ + +int base_ptr = 0; + +/*tex + +The status at each level is indicated by printing two lines, where the first line +indicates what was read so far and the second line shows what remains to be read. +The context is cropped, if necessary, so that the first line contains at most +|half_error_line| characters, and the second contains at most |error_line|. +Non-current input levels whose |token_type| is `|backed_up|' are shown only if +they have not been fully read. + +*/ + static void print_token_list_type(int t) { switch (t) { @@ -172,47 +227,49 @@ static void print_token_list_type(int t) case write_text: tprint_nl("<write> "); break; + case local_text: + tprint_nl("<local> "); + break; default: tprint_nl("?"); - /* this should never happen */ + /*tex This should never happen. */ break; } } -@ Here it is necessary to explain a little trick. We don't want to store a long -string that corresponds to a token list, because that string might take up -lots of memory; and we are printing during a time when an error message is -being given, so we dare not do anything that might overflow one of \TeX's -tables. So `pseudoprinting' is the answer: We enter a mode of printing -that stores characters into a buffer of length |error_line|, where character -$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if -|k<trick_count|, otherwise character |k| is dropped. Initially we set -|tally:=0| and |trick_count:=1000000|; then when we reach the -point where transition from line 1 to line 2 should occur, we -set |first_count:=tally| and |trick_count:=@tmax@>(error_line, -tally+1+error_line-half_error_line)|. At the end of the -pseudoprinting, the values of |first_count|, |tally|, and -|trick_count| give us all the information we need to print the two lines, -and all of the necessary text is in |trick_buf|. - -Namely, let |l| be the length of the descriptive information that appears -on the first line. The length of the context information gathered for that -line is |k=first_count|, and the length of the context information -gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|, -where |h=half_error_line|, we print |trick_buf[0..k-1]| after the -descriptive information on line~1, and set |n:=l+k|; here |n| is the -length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h| -and print `\.{...}' followed by -$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$ -where subscripts of |trick_buf| are circular modulo |error_line|. The -second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|, -unless |n+m>error_line|; in the latter case, further cropping is done. -This is easier to program than to explain. - -@ The following code sets up the print routines so that they will gather -the desired information. - -@c +/*tex + +Here it is necessary to explain a little trick. We don't want to store a long +string that corresponds to a token list, because that string might take up lots +of memory; and we are printing during a time when an error message is being +given, so we dare not do anything that might overflow one of \TeX's tables. So +`pseudoprinting' is the answer: We enter a mode of printing that stores +characters into a buffer of length |error_line|, where character $k+1$ is placed +into \hbox{|trick_buf[k mod error_line]|} if |k<trick_count|, otherwise character +|k| is dropped. Initially we set |tally:=0| and |trick_count:=1000000|; then when +we reach the point where transition from line 1 to line 2 should occur, we set +|first_count:=tally| and |trick_count:=@tmax@>(error_line, +tally+1+error_line-half_error_line)|. At the end of the pseudoprinting, the +values of |first_count|, |tally|, and |trick_count| give us all the information +we need to print the two lines, and all of the necessary text is in |trick_buf|. + +Namely, let |l| be the length of the descriptive information that appears on the +first line. The length of the context information gathered for that line is +|k=first_count|, and the length of the context information gathered for line~2 is +$m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|, where |h=half_error_line|, we +print |trick_buf[0..k-1]| after the descriptive information on line~1, and set +|n:=l+k|; here |n| is the length of line~1. If $l+k>h$, some cropping is +necessary, so we set |n:=h| and print `\.{...}' followed by +$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$ where subscripts of |trick_buf| are +circular modulo |error_line|. The second line consists of |n|~spaces followed by +|trick_buf[k..(k+m-1)]|, unless |n+m>error_line|; in the latter case, further +cropping is done. This is easier to program than to explain. + +The following code sets up the print routines so that they will gather the +desired information. + +*/ + void set_trick_count(void) { first_count = tally; @@ -230,10 +287,12 @@ void set_trick_count(void) #define PSEUDO_PRINT_THE_LINE() do { \ begin_pseudoprint(); \ - if (buffer[ilimit]==end_line_char_par) \ + if (buffer[ilimit]==end_line_char_par) { \ j=ilimit; \ - else \ - j=ilimit+1; /* determine the effective end of the line */ \ + } else { \ + /*tex Determine the effective end of the line. */ \ + j=ilimit+1; \ + } \ if (j>0) { \ for (i=istart;i<=j-1;i++) { \ if (i==iloc) \ @@ -243,10 +302,12 @@ void set_trick_count(void) } \ } while (0) -/* - We don't care too much if we stay a bit too much below the max error_line - even if we have more room on the line. If length is really an issue then - any length is. After all one can set the length larger. +/*tex + +We don't care too much if we stay a bit too much below the max error_line even if +we have more room on the line. If length is really an issue then any length is. +After all one can set the length larger. + */ #define print_valid_utf8(q) do { \ @@ -268,46 +329,66 @@ void set_trick_count(void) print_char(trick_buf[(q+2) % error_line]); \ print_char(trick_buf[(q+3) % error_line]); \ } else { \ - /* invalid */ \ + /*tex Invalid character! */ \ } \ } while (0) -@ @c +/*tex + +This one prints where the scanner is. + +*/ + void show_context(void) -{ /* prints where the scanner is */ - int old_setting; /* saved |selector| setting */ - int nn = -1; /* number of contexts shown so far, less one */ - boolean bottom_line = false; /* have we reached the final context to be shown? */ - int i; /* index into |buffer| */ - int j; /* end of current line in |buffer| */ - int l; /* length of descriptive information on line 1 */ - int m; /* context information gathered for line 2 */ - int n; /* length of line 1 */ - int p; /* starting or ending place in |trick_buf| */ - int q; /* temporary index */ - int c; /* used in sanitizer */ +{ + /*tex Saved |selector| setting: */ + int old_setting; + /*tex Number of contexts shown so far, less one: */ + int nn = -1; + /*tex Have we reached the final context to be shown? */ + boolean bottom_line = false; + /*tex Index into |buffer|: */ + int i; + /*tex End of current line in |buffer|: */ + int j; + /*tex Length of descriptive information on line 1: */ + int l; + /*tex Context information gathered for line 2: */ + int m; + /*tex Length of line 1: */ + int n; + /*tex Starting or ending place in |trick_buf|: */ + int p; + /*tex Temporary index: */ + int q; + /*tex Used in sanitizer: */ + int c; base_ptr = input_ptr; input_stack[base_ptr] = cur_input; - /* store current state */ + /*tex Store the current state. */ while (true) { - cur_input = input_stack[base_ptr]; /* enter into the context */ + /*tex Enter into the context. */ + cur_input = input_stack[base_ptr]; if (istate != token_list) { if ((iname > 21) || (base_ptr == 0)) bottom_line = true; } if ((base_ptr == input_ptr) || bottom_line || (nn < error_context_lines_par)) { - /* Display the current context */ + /*tex Display the current context. */ if ((base_ptr == input_ptr) || (istate != token_list) || (token_type != backed_up) || (iloc != null)) { - /* we omit backed-up token lists that have already been read */ - tally = 0; /* get ready to count characters */ + /*tex + We omit backed-up token lists that have already been read. + Get ready to count characters. + */ + tally = 0; old_setting = selector; if (istate != token_list) { - /* - Print location of current line - - This routine should be changed, if necessary, to give the best possible - indication of where the current line resides in the input file. For example, - on some systems it is best to print both a page and line number. + /*tex + Print the location of the current line. This routine + should be changed, if necessary, to give the best + possible indication of where the current line resides in + the input file. For example, on some systems it is best + to print both a page and line number. */ if (iname <= 17) { if (terminal_input) { @@ -327,7 +408,8 @@ void show_context(void) tprint_nl("l."); if (iindex == in_open) { print_int(line); - } else { /* input from a pseudo file */ + } else { + /*tex Input from a pseudo file. */ print_int(line_stack[iindex + 1]); } } @@ -337,21 +419,26 @@ void show_context(void) print_token_list_type(token_type); begin_pseudoprint(); - if (token_type < macro) + if (token_type < macro) { show_token_list(istart, iloc, 100000); - else - show_token_list(token_link(istart), iloc, 100000); /* avoid reference count */ + } else { + /*tex Avoid reference count. */ + show_token_list(token_link(istart), iloc, 100000); + } } - /* stop pseudoprinting */ + /*tex Stop pseudoprinting. */ selector = old_setting; - /* Print two lines using the tricky pseudoprinted information */ - if (trick_count == 1000000) + /*tex Print two lines using the tricky pseudoprinted information. */ + if (trick_count == 1000000) { set_trick_count(); - /* |set_trick_count| must be performed */ - if (tally < trick_count) + } + /*tex The |set_trick_count| must be performed. */ + if (tally < trick_count) { m = tally - first_count; - else - m = trick_count - first_count; /* context on line 2 */ + } else { + /* The context on line 2: */ + m = trick_count - first_count; + } if (l + first_count <= half_error_line) { p = 0; n = l + first_count; @@ -360,12 +447,14 @@ void show_context(void) p = l + first_count - half_error_line + 3; n = half_error_line; } - for (q = p; q <= first_count - 1; q++) + for (q = p; q <= first_count - 1; q++) { print_valid_utf8(q); - print_ln(); - /* print |n| spaces to begin line~2 */ - for (q = 1; q <= n; q++) + } + print_ln(); + /*tex Print |n| spaces to begin line 2. */ + for (q = 1; q <= n; q++) { print_char(' '); + } if (m + n <= error_line) p = first_count + m; else @@ -379,24 +468,26 @@ void show_context(void) } else if (nn == error_context_lines_par) { tprint_nl("..."); incr(nn); - /* omitted if |error_context_lines_par<0| */ + /*tex Omitted if |error_context_lines_par<0|. */ } if (bottom_line) break; decr(base_ptr); } - /* restore original state */ + /*tex Restore the original state. */ cur_input = input_stack[input_ptr]; } -@ The following subroutines change the input status in commonly needed ways. +/*tex + +The following subroutines change the input status in commonly needed ways. First comes |push_input|, which stores the current state and creates a new level (having, initially, the same properties as the old). -@c +Enter a new input level, save the old: -/* enter a new input level, save the old */ +*/ # define pop_input() \ cur_input=input_stack[--input_ptr] @@ -411,12 +502,14 @@ new level (having, initially, the same properties as the old). nofilter = false; \ incr(input_ptr); -@ -Here is a procedure that starts a new level of token-list input, given -a token list |p| and its type |t|. If |t=macro|, the calling routine should -set |name| and |loc|. +/*tex + +Here is a procedure that starts a new level of token-list input, given a token +list |p| and its type |t|. If |t=macro|, the calling routine should set |name| +and |loc|. + +*/ -@c void begin_token_list(halfword p, quarterword t) { push_input(); @@ -424,7 +517,7 @@ void begin_token_list(halfword p, quarterword t) istart = p; token_type = (unsigned char) t; if (t >= macro) { - /* the token list starts with a reference count */ + /*tex The token list starts with a reference count. */ add_token_ref(p); if (t == macro) { param_start = param_ptr; @@ -438,8 +531,7 @@ void begin_token_list(halfword p, quarterword t) else if (t == write_text) tprint_esc("write"); else - print_cmd_chr(assign_toks_cmd, - t - output_text + output_routine_loc); + print_cmd_chr(assign_toks_cmd, t - output_text + output_routine_loc); tprint("->"); token_show(p); end_diagnostic(false); @@ -450,24 +542,26 @@ void begin_token_list(halfword p, quarterword t) } } -@ When a token list has been fully scanned, the following computations -should be done as we leave that level of input. The |token_type| tends -to be equal to either |backed_up| or |inserted| about 2/3 of the time. -@^inner loop@> +/*tex + +When a token list has been fully scanned, the following computations should be +done as we leave that level of input. The |token_type| tends to be equal to +either |backed_up| or |inserted| about 2/3 of the time. @^inner loop@> + +*/ -@c void end_token_list(void) { - /* leave a token-list input level */ + /*tex Leave a token-list input level: */ if (token_type >= backed_up) { - /* token list to be deleted */ + /*tex The token list to be deleted: */ if (token_type <= inserted) { flush_list(istart); } else { - /* update reference count */ + /*tex Update the reference count: */ delete_token_ref(istart); if (token_type == macro) { - /* parameters must be flushed */ + /*tex Parameters must be flushed: */ while (param_ptr > param_start) { decr(param_ptr); flush_list(param_stack[param_ptr]); @@ -484,23 +578,24 @@ void end_token_list(void) check_interrupt(); } -@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has -seen. The |back_input| procedure takes care of this by putting the token -just scanned back into the input stream, ready to be read again. This -procedure can be used only if |cur_tok| represents the token to be -replaced. Some applications of \TeX\ use this procedure a lot, -so it has been slightly optimized for speed. -@^inner loop@> +/*tex + +Sometimes \TeX\ has read too far and wants to ``unscan'' what it has seen. The +|back_input| procedure takes care of this by putting the token just scanned back +into the input stream, ready to be read again. This procedure can be used only if +|cur_tok| represents the token to be replaced. Some applications of \TeX\ use +this procedure a lot, so it has been slightly optimized for speed. @^inner loop@> -@c +*/ /* undoes one token of input */ void back_input(void) { - halfword p; /* a token list of length one */ + /*tex A token list of length one: */ + halfword p; while ((istate == token_list) && (iloc == null) && (token_type != v_template)) { - /* conserve stack space */ + /*tex Conserve stack space. */ end_token_list(); } p = get_avail(); @@ -512,14 +607,18 @@ void back_input(void) incr(align_state); } push_input(); + /*tex This is |back_list(p)|, without procedure overhead: */ istate = token_list; istart = p; token_type = backed_up; - iloc = p; /* that was |back_list(p)|, without procedure overhead */ + iloc = p; } -@ Insert token |p| into \TeX's input -@c +/*tex + +Insert token |p| into \TeX's input + +*/ void reinsert_token(boolean a, halfword pp) { @@ -545,13 +644,15 @@ void reinsert_token(boolean a, halfword pp) cur_tok = t; } -@ The |begin_file_reading| procedure starts a new level of input for lines -of characters to be read from a file, or as an insertion from the -terminal. It does not take care of opening the file, nor does it set |loc| -or |limit| or |line|. +/*tex + +The |begin_file_reading| procedure starts a new level of input for lines of +characters to be read from a file, or as an insertion from the terminal. It does +not take care of opening the file, nor does it set |loc| or |limit| or |line|. @^system dependencies@> -@c +*/ + void begin_file_reading(void) { if (in_open == max_in_open) @@ -569,48 +670,59 @@ void begin_file_reading(void) line_stack[iindex] = line; istart = first; istate = mid_line; - iname = 0; /* |terminal_input| is now |true| */ + iname = 0; + /*tex Variable |terminal_input| is now |true|. */ line_catcode_table = DEFAULT_CAT_TABLE; line_partial = false; - /* Prepare terminal input {\sl Sync\TeX} information */ + /*tex Prepare terminal input \SYNCTEX\ information. */ synctex_tag = 0; } -@ Conversely, the variables must be downdated when such a level of input -is finished: +/*tex + +Conversely, the variables must be downdated when such a level of input is +finished: + +*/ -@c void end_file_reading(void) { first = istart; line = line_stack[iindex]; - if ((iname >= 18) && (iname <= 20)) + if ((iname >= 18) && (iname <= 20)) { pseudo_close(); - else if (iname == 21) + } else if (iname == 21) { luacstring_close(iindex); - else if (iname > 17) - lua_a_close_in(cur_file, 0); /* forget it */ + } else if (iname > 17) { + /*tex Forget it. */ + lua_a_close_in(cur_file, 0); + } pop_input(); decr(in_open); } -@ In order to keep the stack from overflowing during a long sequence of +/*tex + +In order to keep the stack from overflowing during a long sequence of inserted `\.{\\show}' commands, the following routine removes completed error-inserted lines from memory. -@c +*/ void clear_for_error_prompt(void) { - while ((istate != token_list) && terminal_input - && (input_ptr > 0) && (iloc > ilimit)) + while ((istate != token_list) && terminal_input && (input_ptr > 0) && (iloc > ilimit)) { end_file_reading(); + } print_ln(); clear_terminal(); } -@ To get \TeX's whole input mechanism going, we perform the following actions. +/*tex + +To get \TeX's whole input mechanism going, we perform the following actions. + +*/ -@c void initialize_inputstack(void) { input_ptr = 0; @@ -645,29 +757,29 @@ void initialize_inputstack(void) line_catcode_table = DEFAULT_CAT_TABLE; line_partial = false; align_state = 1000000; - if (!init_terminal()) - exit(EXIT_FAILURE); /* |goto final_end|; */ + if (!init_terminal()) { + /*tex |goto final_end|; */ + exit(EXIT_FAILURE); + } + /* |init_terminal| has set |loc| and |last| */ ilimit = last; - first = last + 1; /* |init_terminal| has set |loc| and |last| */ + first = last + 1; } -@ The global variable |pseudo_files| is used to maintain a stack of -pseudo files. The |pseudo_lines| field of each pseudo file points to -a linked list of variable size nodes representing lines not yet -processed: the |subtype| field contains the size of this node, -all the following words contain ASCII codes. +/*tex -/* +The global variable |pseudo_files| is used to maintain a stack of pseudo files. +The |pseudo_lines| field of each pseudo file points to a linked list of variable +size nodes representing lines not yet processed: the |subtype| field contains the +size of this node, all the following words contain ASCII codes. - hh: todo: if this is really critical code (which it isn't) then we can - consider a c stack and store a pointer to a line in the line node instead - which saves splitting here and reconstructing later. +If this is really critical code (which it isn't) then we can consider a c stack +and store a pointer to a line in the line node instead which saves splitting here +and reconstructing later. */ - -@c -halfword pseudo_files; /* stack of pseudo files */ +halfword pseudo_files; static halfword string_to_pseudo(str_number str, int nl) { @@ -680,7 +792,8 @@ static halfword string_to_pseudo(str_number str, int nl) len = (unsigned) str_length(str); l = 0; while (l < len) { - unsigned m = l; /* start of current line */ + /*tex start of current line */ + unsigned m = l; while ((l < len) && (s[l] != nl)) l++; sz = (int) (l - m + 7) / 4; @@ -703,7 +816,8 @@ static halfword string_to_pseudo(str_number str, int nl) if (q == null) { pseudo_lines(h) = r; } else { - vlink(q) = r ; /* no prev node here so no couple_nodes !*/ + /*tex There is no |prev| node here so no need to couple_nodes! */ + vlink(q) = r ; } q = r ; if (s[l] == nl) @@ -712,24 +826,33 @@ static halfword string_to_pseudo(str_number str, int nl) return h; } -@ The |pseudo_start| procedure initiates reading from a pseudo file. +/*tex + +The |pseudo_start| procedure initiates reading from a pseudo file. + +*/ -@c void pseudo_from_string(void) { - str_number s; /* string to be converted into a pseudo file */ - halfword p; /* for list construction */ + /*tex The string to be converted into a pseudo file: */ + str_number s; + /*tex A helper for list construction: */ + halfword p; s = make_string(); - /* Convert string |s| into a new pseudo file */ + /*tex Convert string |s| into a new pseudo file */ p = string_to_pseudo(s, new_line_char_par); vlink(p) = pseudo_files; pseudo_files = p; flush_str(s); - /* Initiate input from new pseudo file */ - begin_file_reading(); /* set up |cur_file| and new level of input */ + /*tex + Initiate input from new pseudo file. It sets up |cur_file| and a new level + of input + */ + begin_file_reading(); line = 0; ilimit = istart; - iloc = ilimit + 1; /* force line read */ + /*tex force line read */ + iloc = ilimit + 1; if (tracing_scan_tokens_par > 0) { if (term_offset > max_print_line - 3) print_ln(); @@ -742,7 +865,7 @@ void pseudo_from_string(void) } else { iname = 18; } - /* Prepare pseudo file {\sl Sync\TeX} information */ + /*tex Prepare pseudo file \SYNCTEX\ information. */ synctex_tag = 0; } @@ -759,29 +882,37 @@ void pseudo_start(void) pseudo_from_string(); } -@ @c void lua_string_start(void) { - begin_file_reading(); /* set up |cur_file| and new level of input */ + /*tex Set up |cur_file| and a new level of input: */ + begin_file_reading(); line = 0; ilimit = istart; - iloc = ilimit + 1; /* force line read */ + /*tex Force line read: */ + iloc = ilimit + 1; iname = 21; luacstring_start(iindex); } -@ Here we read a line from the current pseudo file into |buffer|. +/*tex -@c -/* inputs the next line or returns |false| */ +Here we read a line from the current pseudo file into |buffer|. +It inputs the next line or returns |false|. + +*/ boolean pseudo_input(void) { - halfword p; /* current line from pseudo file */ - int sz; /* size of node |p| */ - four_quarters w; /* four ASCII codes */ - halfword r; /* loop index */ - last = first; /* cf.\ Matthew 19\thinspace:\thinspace30 */ + /*tex The current line from pseudo file: */ + halfword p; + /*tex The size of node |p|: */ + int sz; + /*tex Four ASCII codes: */ + four_quarters w; + /*tex The loop index: */ + halfword r; + /*tex cf.\ Matthew 19\thinspace:\thinspace30 */ + last = first; p = pseudo_lines(pseudo_files); if (p == null) { return false; @@ -808,10 +939,11 @@ boolean pseudo_input(void) return true; } -@ When we are done with a pseudo file we `close' it. +/*tex + +When we are done with a pseudo file we `close' it. -@c -/* close the top level pseudo file */ +*/ void pseudo_close(void) { diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.c b/Build/source/texk/web2c/luatexdir/tex/linebreak.c new file mode 100644 index 00000000000..df13931e14c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.c @@ -0,0 +1,2520 @@ +/* + +Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + + We come now to what is probably the most interesting algorithm of \TeX: the + mechanism for choosing the ``best possible'' breakpoints that yield the + individual lines of a paragraph. \TeX's line-breaking algorithm takes a given + horizontal list and converts it to a sequence of boxes that are appended to + the current vertical list. In the course of doing this, it creates a special + data structure containing three kinds of records that are not used elsewhere + in \TeX. Such nodes are created while a paragraph is being processed, and + they are destroyed afterwards; thus, the other parts of \TeX\ do not need to + know anything about how line-breaking is done. + + The method used here is based on an approach devised by Michael F. Plass and + the author in 1977, subsequently generalized and improved by the same two + people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice + \AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the + line-breaking problem can be regarded as a special case of the problem of + computing the shortest path in an acyclic network. The cited paper includes + numerous examples and describes the history of line breaking as it has been + practiced by printers through the ages. The present implementation adds two + new ideas to the algorithm of 1980: Memory space requirements are + considerably reduced by using smaller records for inactive nodes than for + active ones, and arithmetic overflow is avoided by using ``delta distances'' + instead of keeping track of the total distance from the beginning of the + paragraph to the current point. + + The |line_break| procedure should be invoked only in horizontal mode; it + leaves that mode and places its output into the current vlist of the + enclosing vertical mode (or internal vertical mode). There is one explicit + parameter: |d| is true for partial paragraphs preceding display math mode; in + this case the amount of additional penalty inserted before the final line is + |display_widow_penalty| instead of |widow_penalty|. + + There are also a number of implicit parameters: The hlist to be broken starts + at |vlink(head)|, and it is nonempty. The value of |prev_graf| in the + enclosing semantic level tells where the paragraph should begin in the + sequence of line numbers, in case hanging indentation or \.{\\parshape} are + in use; |prev_graf| is zero unless this paragraph is being continued after a + displayed formula. Other implicit parameters, such as the |par_shape_ptr| and + various penalties to use for hyphenation, etc., appear in |eqtb|. + + After |line_break| has acted, it will have updated the current vlist and the + value of |prev_graf|. Furthermore, the global variable |just_box| will point + to the final box created by |line_break|, so that the width of this line can + be ascertained when it is necessary to decide whether to use + |above_display_skip| or |above_display_short_skip| before a displayed + formula. + +*/ + +/*tex The |hlist_node| for the last line of the new paragraph: */ + +halfword just_box; + +/*tex + + In it's complete form, |line_break| is a rather lengthy procedure---sort of a + small world unto itself---we must build it up little by little. Below you see + only the general outline. + + The main task performed here is to move the list from |head| to |temp_head| + and go into the enclosing semantic level. We also append the + \.{\\parfillskip} glue to the end of the paragraph, removing a space (or + other glue node) if it was there, since spaces usually precede blank lines + and instances of `\.{\$\$}'. The |par_fill_skip| is preceded by an infinite + penalty, so it will never be considered as a potential breakpoint. + + That code assumes that a |glue_node| and a |penalty_node| occupy the same + number of |mem|~words. + + Most other processing is delegated to external functions. + +*/ + +void line_break(boolean d, int line_break_context) +{ + /*tex Main direction of paragraph: */ + int paragraph_dir = 0; + halfword final_par_glue; + halfword start_of_par; + int callback_id; + /*tex this is for over/underfull box messages */ + pack_begin_line = cur_list.ml_field; + alink(temp_head) = null; + vlink(temp_head) = vlink(cur_list.head_field); + new_hyphenation(temp_head, cur_list.tail_field); + cur_list.tail_field = new_ligkern(temp_head, cur_list.tail_field); + if (is_char_node(cur_list.tail_field)) { + tail_append(new_penalty(inf_penalty,line_penalty)); + } else if (type(cur_list.tail_field) != glue_node) { + tail_append(new_penalty(inf_penalty,line_penalty)); + } else { + halfword t = alink(cur_list.tail_field); + flush_node(cur_list.tail_field); + cur_list.tail_field = t; + tail_append(new_penalty(inf_penalty,line_penalty)); + } + final_par_glue = new_param_glue(par_fill_skip_code); + couple_nodes(cur_list.tail_field, final_par_glue); + cur_list.tail_field = vlink(cur_list.tail_field); + lua_node_filter(pre_linebreak_filter_callback, line_break_context, temp_head, addressof(cur_list.tail_field)); + last_line_fill = cur_list.tail_field; + pop_nest(); + start_of_par = cur_list.tail_field; + callback_id = callback_defined(linebreak_filter_callback); + if (callback_id > 0) { + callback_id = lua_linebreak_callback(d, temp_head, addressof(cur_list.tail_field)); + if (callback_id > 0) { + /*tex find the correct value for the |just_box| */ + halfword box_search = cur_list.tail_field; + just_box = null; + if (box_search != null) { + do { + if (type(box_search) == hlist_node) { + just_box = box_search; + } + box_search = vlink(box_search); + } while (box_search != null); + } + if (just_box == null) { + help3( + "A linebreaking routine should return a non-empty list of nodes", + "and at least one of those has to be a \\hbox.", + "Sorry, I cannot recover from this." + ); + print_err("Invalid linebreak_filter"); + succumb(); + } + } else { + if (tracing_paragraphs_par > 0) { + begin_diagnostic(); + print_int(line); + end_diagnostic(true); + } + } + } + if (callback_id == 0) { + if ((!is_char_node(vlink(temp_head))) && ((type(vlink(temp_head)) == local_par_node))) { + paragraph_dir = local_par_dir(vlink(temp_head)); + } else { + confusion("weird par dir"); + } + ext_do_line_break( + paragraph_dir, + pretolerance_par, + tracing_paragraphs_par, + tolerance_par, + emergency_stretch_par, + looseness_par, + adjust_spacing_par, + par_shape_par_ptr, + adj_demerits_par, + protrude_chars_par, + line_penalty_par, + last_line_fit_par, + double_hyphen_demerits_par, + final_hyphen_demerits_par, + hang_indent_par, + hsize_par, + hang_after_par, + left_skip_par, + right_skip_par, + inter_line_penalties_par_ptr, + inter_line_penalty_par, + club_penalty_par, + club_penalties_par_ptr, + (d ? display_widow_penalties_par_ptr : widow_penalties_par_ptr), + (d ? display_widow_penalty_par : widow_penalty_par), + broken_penalty_par, + final_par_glue + ); + } + lua_node_filter(post_linebreak_filter_callback, line_break_context, start_of_par, addressof(cur_list.tail_field)); + pack_begin_line = 0; +} + +/*tex + + Glue nodes in a horizontal list that is being paragraphed are not supposed to + include ``infinite'' shrinkability; that is why the algorithm maintains four + registers for stretching but only one for shrinking. If the user tries to + introduce infinite shrinkability, the shrinkability will be reset to finite + and an error message will be issued. A boolean variable |no_shrink_error_yet| + prevents this error message from appearing more than once per paragraph. + +*/ + +#define check_shrinkage(a) \ + if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \ + a=finite_shrink((a)) + +/*tex Have we complained about infinite shrinkage? */ + +static boolean no_shrink_error_yet; + +/*tex Recovers from infinite shrinkage. */ + +static halfword finite_shrink(halfword p) +{ + const char *hlp[] = { + "The paragraph just ended includes some glue that has", + "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.", + "Such glue doesn't belong there---it allows a paragraph", + "of any length to fit on one line. But it's safe to proceed,", + "since the offensive shrinkability has been made finite.", + NULL + }; + if (no_shrink_error_yet) { + no_shrink_error_yet = false; + tex_error("Infinite glue shrinkage found in a paragraph", hlp); + } + shrink_order(p) = normal; + return p; +} + +/*tex + + A pointer variable |cur_p| runs through the given horizontal list as we look + for breakpoints. This variable is global, since it is used both by + |line_break| and by its subprocedure |try_break|. + + Another global variable called |threshold| is used to determine the + feasibility of individual lines: breakpoints are feasible if there is a way + to reach them without creating lines whose badness exceeds |threshold|. (The + badness is compared to |threshold| before penalties are added, so that + penalty values do not affect the feasibility of breakpoints, except that no + break is allowed when the penalty is 10000 or more.) If |threshold| is 10000 + or more, all legal breaks are considered feasible, since the |badness| + function specified above never returns a value greater than~10000. + + Up to three passes might be made through the paragraph in an attempt to find + at least one set of feasible breakpoints. On the first pass, we have + |threshold=pretolerance| and |second_pass=final_pass=false|. If this pass + fails to find a feasible solution, |threshold| is set to |tolerance|, + |second_pass| is set |true|, and an attempt is made to hyphenate as many + words as possible. If that fails too, we add |emergency_stretch| to the + background stretchability and set |final_pass=true|. + +*/ + +/*tex is this our second attempt to break this paragraph? */ + +static boolean second_pass; + +/*tex is this our final attempt to break this paragraph? */ + +static boolean final_pass; + +/*tex maximum badness on feasible lines */ + +static int threshold; + +/*tex + + The maximum fill level for |hlist_stack|. Maybe good if larger than |2 * + max_quarterword|, so that box nesting level would overflow first. + +*/ + +#define max_hlist_stack 512 + +/*tex stack for |find_protchar_left()| and |find_protchar_right()| */ + +static halfword hlist_stack[max_hlist_stack]; + +/*tex fill level for |hlist_stack| */ + +static short hlist_stack_level = 0; + +static void push_node(halfword p) +{ + if (hlist_stack_level >= max_hlist_stack) + normal_error("push_node","stack overflow"); + hlist_stack[hlist_stack_level++] = p; +} + +static halfword pop_node(void) +{ + if (hlist_stack_level <= 0) { + /*tex This can point to some bug. */ + normal_error("pop_node","stack underflow (internal error)"); + } + return hlist_stack[--hlist_stack_level]; +} + +/*tex maximal stretch ratio of expanded fonts */ + +static int max_stretch_ratio = 0; + +/*tex maximal shrink ratio of expanded fonts */ + +static int max_shrink_ratio = 0; + +/*tex the current step of expanded fonts */ + +static int cur_font_step = 0; + +static boolean check_expand_pars(internal_font_number f) +{ + int m; + if ((font_step(f) == 0) || ((font_max_stretch(f) == 0) && (font_max_shrink(f) == 0))) + return false; + if (cur_font_step < 0) + cur_font_step = font_step(f); + else if (cur_font_step != font_step(f)) + normal_error("font expansion","using fonts with different step of expansion in one paragraph is not allowed"); + m = font_max_stretch(f); + if (m != 0) { + if (max_stretch_ratio < 0) + max_stretch_ratio = m; + else if (max_stretch_ratio != m) + normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed"); + } + m = font_max_shrink(f); + if (m != 0) { + if (max_shrink_ratio < 0) + max_shrink_ratio = -m; + else if (max_shrink_ratio != -m) + normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed"); + } + return true; +} + +/*tex Search left to right from list head |l|, returns 1st non-skipable item */ + +halfword find_protchar_left(halfword l, boolean d) +{ + halfword t; + boolean run; + boolean done = false ; + while ((vlink(l) != null) && (type(l) == hlist_node) && zero_dimensions(l) && (list_ptr(l) == null)) { + /*tex For paragraph start with \.{\\parindent} = 0pt or any empty hbox. */ + l = vlink(l); + done = true ; + } + if ((!done) && (type(l) == local_par_node)) { + l = vlink(l); + done = true ; + } + if ((!done) && d) { + while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) { + /*tex standard discardables at line break, \TeX book, p 95 */ + l = vlink(l); + } + } + if (type(l) != glyph_node) { + hlist_stack_level = 0; + run = true; + do { + t = l; + while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) { + push_node(l); + l = list_ptr(l); + } + while (run && cp_skipable(l)) { + while ((vlink(l) == null) && (hlist_stack_level > 0)) { + /*tex Don't visit this node again. */ + l = pop_node(); + run = false; + } + if ((vlink(l) != null) && (type(l) == boundary_node) && (subtype(l) == protrusion_boundary) && + ((boundary_value(l) == 1) || (boundary_value(l) == 3))) { + /*tex Skip next node. */ + l = vlink(l); + } + if (vlink(l) != null) { + l = vlink(l); + } else if (hlist_stack_level == 0) { + run = false; + } + } + } while (t != l); + } + return l; +} + +/*tex + + Search right to left from list tail |r| to head |l|, returns 1st non-skipable + item. + +*/ + +halfword find_protchar_right(halfword l, halfword r) +{ + halfword t; + boolean run = true; + if (r == null) + return null; + hlist_stack_level = 0; + do { + t = r; + while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) { + push_node(l); + push_node(r); + l = list_ptr(r); + r = l; + while (vlink(r) != null) { + halfword s = r; + r = vlink(r); + alink(r) = s; + } + } + while (run && cp_skipable(r)) { + while ((r == l) && (hlist_stack_level > 0)) { + /*tex Don't visit this node again. */ + r = pop_node(); + l = pop_node(); + } + if ((r != l) && (r != null)) { + if ((alink(r) != null) && (type(r) == boundary_node) && (subtype(r) == protrusion_boundary) && + ((boundary_value(r) == 2) || (boundary_value(r) == 3))) { + /*tex Skip next node. */ + r = alink(r); + } + if (alink(r) != null) { + r = alink(r); + } else { + /*tex This is the input: \.{\\leavevmode\\penalty-10000\\penalty-10000} (bug \#268). */ + run = false; + } + } else if ((r == l) && (hlist_stack_level == 0)) + run = false; + } + } while (t != r); + return r; +} + +#define left_pw(a) char_pw((a), left_side) +#define right_pw(a) char_pw((a), right_side) + +/*tex + + When looking for optimal line breaks, \TeX\ creates a ``break node'' for each + break that is {\sl feasible}, in the sense that there is a way to end a line + at the given place without requiring any line to stretch more than a given + tolerance. A break node is characterized by three things: the position of the + break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|, or + |disc_node|); the ordinal number of the line that will follow this + breakpoint; and the fitness classification of the line that has just ended, + i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|. + +*/ + +typedef enum { + /*tex fitness classification for lines stretching more than their stretchability */ + very_loose_fit = 0, + /*tex fitness classification for lines stretching 0.5 to 1.0 of their stretchability */ + loose_fit, + /*tex fitness classification for all other lines */ + decent_fit, + /*tex fitness classification for lines shrinking 0.5 to 1.0 of their shrinkability */ + tight_fit +} fitness_value; + +/*tex + + The algorithm essentially determines the best possible way to achieve each + feasible combination of position, line, and fitness. Thus, it answers + questions like, ``What is the best way to break the opening part of the + paragraph so that the fourth line is a tight line ending at such-and-such a + place?'' However, the fact that all lines are to be the same length after a + certain point makes it possible to regard all sufficiently large line numbers + as equivalent, when the looseness parameter is zero, and this makes it + possible for the algorithm to save space and time. + + An ``active node'' and a ``passive node'' are created in |mem| for each + feasible breakpoint that needs to be considered. Active nodes are three words + long and passive nodes are two words long. We need active nodes only for + breakpoints near the place in the paragraph that is currently being examined, + so they are recycled within a comparatively short time after they are + created. + + An active node for a given breakpoint contains six fields: + + \startitemize[n] + + \startitem + |vlink| points to the next node in the list of active nodes; the last + active node has |vlink=active|. + \stopitem + + \startitem + |break_node| points to the passive node associated with this + breakpoint. + \stopitem + + \startitem + |line_number| is the number of the line that follows this breakpoint. + \stopitem + + \startitem + |fitness| is the fitness classification of the line ending at this + breakpoint. + \stopitem + + \startitem + |type| is either |hyphenated_node| or |unhyphenated_node|, depending + on whether this breakpoint is a |disc_node|. + \stopitem + + \startitem + |total_demerits| is the minimum possible sum of demerits over all + lines leading from the beginning of the paragraph to this breakpoint. + \stopitem + + \stopitemize + + The value of |vlink(active)| points to the first active node on a vlinked + list of all currently active nodes. This list is in order by |line_number|, + except that nodes with |line_number>easy_line| may be in any order relative + to each other. + +*/ + +void initialize_active(void) +{ + type(active) = hyphenated_node; + line_number(active) = max_halfword; + /*tex The |subtype| is never examined. */ + subtype(active) = 0; +} + +/*tex + + The passive node for a given breakpoint contains eight fields: + + \startitemize + + \startitem + |vlink| points to the passive node created just before this one, if + any, otherwise it is |null|. + \stopitem + + \startitem + |cur_break| points to the position of this breakpoint in the + horizontal list for the paragraph being broken. + \stopitem + + \startitem + |prev_break| points to the passive node that should precede this one + in an optimal path to this breakpoint. + \stopitem + + \startitem + |serial| is equal to |n| if this passive node is the |n|th one + created during the current pass. (This field is used only when + printing out detailed statistics about the line-breaking + calculations.) + \stopitem + + \startitem + |passive_pen_inter| holds the current \.{\\localinterlinepenalty} + \stopitem + + \startitem + |passive_pen_broken| holds the current \.{\\localbrokenpenalty} + \stopitem + + \stopitemize + + There is a global variable called |passive| that points to the most recently + created passive node. Another global variable, |printed_node|, is used to + help print out the paragraph when detailed information about the + line-breaking computation is being displayed. + +*/ + +/*tex most recent node on passive list */ + +static halfword passive; + +/*tex most recent node that has been printed */ + +static halfword printed_node; + +/*tex the number of passive nodes allocated on this pass */ + +static halfword pass_number; + +/*tex + + The active list also contains ``delta'' nodes that help the algorithm compute + the badness of individual lines. Such nodes appear only between two active + nodes, and they have |type=delta_node|. If |p| and |r| are active nodes and + if |q| is a delta node between them, so that |vlink(p)=q| and |vlink(q)=r|, + then |q| tells the space difference between lines in the horizontal list that + start after breakpoint |p| and lines that start after breakpoint |r|. In + other words, if we know the length of the line that starts after |p| and ends + at our current position, then the corresponding length of the line that + starts after |r| is obtained by adding the amounts in node~|q|. A delta node + contains seven scaled numbers, since it must record the net change in glue + stretchability with respect to all orders of infinity. The natural width + difference appears in |mem[q+1].sc|; the stretch differences in units of pt, + sfi, fil, fill, and filll appear in |mem[q+2..q+6].sc|; and the shrink + difference appears in |mem[q+7].sc|. The |subtype| field of a delta node is + not used. + + Actually, we have two more fields that are used by |pdftex|. + + As the algorithm runs, it maintains a set of seven delta-like registers for + the length of the line following the first active breakpoint to the current + position in the given hlist. When it makes a pass through the active list, it + also maintains a similar set of seven registers for the length following the + active breakpoint of current interest. A third set holds the length of an + empty line (namely, the sum of \.{\\leftskip} and \.{\\rightskip}); and a + fourth set is used to create new delta nodes. + + When we pass a delta node we want to do operations like: + + \starttyping + for k := 1 to 7 do + cur_active_width[k] := cur_active_width[k] + mem[q+k].sc|}; + \stoptyping + + and we want to do this without the overhead of |for| loops. The |do_all_six| + macro makes such six-tuples convenient. + +*/ + +/*tex distance from first active node to~|cur_p| */ + +static scaled active_width[10] = { 0 }; + +/*tex length of an ``empty'' line */ + +static scaled background[10] = { 0 }; + +/*tex length being computed after current break */ + +static scaled break_width[10] = { 0 }; + +/*tex Make |auto_breaking| accessible out of |line_break|: */ + +static boolean auto_breaking; + +/*tex + + Let's state the principles of the delta nodes more precisely and concisely, + so that the following programs will be less obscure. For each legal + breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and + $\beta(p)$ such that the length of material in a line from breakpoint~|p| to + breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$. + Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from + the beginning of the paragraph to a point ``after'' a break at |p| and to a + point ``before'' a break at |q|; and $\gamma$ is the width of an empty line, + namely the length contributed by \.{\\leftskip} and \.{\\rightskip}. + + Suppose, for example, that the paragraph consists entirely of alternating + boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and let the + skips have widths $y_1\ldots y_n$, so that the paragraph can be represented + by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint at $y_i$; then + $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)= x_1+y_1+\cdots+x_i$. + To check this, note that the length of material from $p_2$ to $p_5$, say, is + $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5) -\alpha(p_2)$. + + The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and + shrinkability as well as a natural width. If we were to compute $\alpha(p)$ + and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and + the multiprecise numbers would have to be kept in the active nodes. \TeX\ + avoids this problem by working entirely with relative differences or + ``deltas.'' Suppose, for example, that the active list contains + $a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints + and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$ + and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is + currently positioned at some other breakpoint |p|, the |active_width| array + contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through + the list of active nodes and considering a tentative line that runs from + $a_2$ to~|p|, say, the |cur_active_width| array will contain the value + $\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$, we + want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this is just + $\delta_2$, which appears in the active list between $a_2$ and $a_3$. The + |background| array contains $\gamma$. The |break_width| array will be used to + calculate values of new delta nodes when the active list is being updated. + + The heart of the line-breaking procedure is `|try_break|', a subroutine that + tests if the current breakpoint |cur_p| is feasible, by running through the + active list to see what lines of text can be made from active nodes + to~|cur_p|. If feasible breaks are possible, new break nodes are created. If + |cur_p| is too far from an active node, that node is deactivated. + + The parameter |pi| to |try_break| is the penalty associated with a break at + |cur_p|; we have |pi=eject_penalty| if the break is forced, and + |pi=inf_penalty| if the break is illegal. + + The other parameter, |break_type|, is set to |hyphenated_node| or + |unhyphenated_node|, depending on whether or not the current break is at a + |disc_node|. The end of a paragraph is also regarded as `|hyphenated_node|'; + this case is distinguishable by the condition |cur_p=null|. + +*/ + +/*tex running \.{\\localinterlinepenalty} */ + +static int internal_pen_inter; + +/*tex running \.{\\localbrokenpenalty} */ + +static int internal_pen_broken; + +/*tex running \.{\\localleftbox} */ + +static halfword internal_left_box; + +/*tex running \.{\\localleftbox} width */ + +static int internal_left_box_width; + +/*tex running \.{\\localleftbox} */ + +static halfword init_internal_left_box; + +/*tex running \.{\\localleftbox} width */ + +static int init_internal_left_box_width; + +/*tex running \.{\\localrightbox} */ + +static halfword internal_right_box; + +/*tex running \.{\\localrightbox} width */ + +static int internal_right_box_width; + +/*tex the length of discretionary material preceding a break */ + +static scaled disc_width[10] = { 0 }; + +/*tex + + As we consider various ways to end a line at |cur_p|, in a given line number + class, we keep track of the best total demerits known, in an array with one + entry for each of the fitness classifications. For example, + |minimal_demerits[tight_fit]| contains the fewest total demerits of feasible + line breaks ending at |cur_p| with a |tight_fit| line; + |best_place[tight_fit]| points to the passive node for the break + before~|cur_p| that achieves such an optimum; and |best_pl_line[tight_fit]| + is the |line_number| field in the active node corresponding to + |best_place[tight_fit]|. When no feasible break sequence is known, the + |minimal_demerits| entries will be equal to |awful_bad|, which is $2^{30}-1$. + Another variable, |minimum_demerits|, keeps track of the smallest value in + the |minimal_demerits| array. + +*/ + +/*tex best total demerits known for current line class and position, given the fitness */ + +static int minimal_demerits[4]; + +/*tex best total demerits known for current line class and position */ + +static int minimum_demerits; + +/*tex how to achieve |minimal_demerits| */ + +static halfword best_place[4]; + +/*tex corresponding line number */ + +static halfword best_pl_line[4]; + +/*tex + + The length of lines depends on whether the user has specified \.{\\parshape} + or \.{\\hangindent}. If |par_shape_ptr| is not null, it points to a + $(2n+1)$-word record in |mem|, where the |vinfo| in the first word contains + the value of |n|, and the other $2n$ words contain the left margins and line + lengths for the first |n| lines of the paragraph; the specifications for line + |n| apply to all subsequent lines. If |par_shape_ptr=null|, the shape of the + paragraph depends on the value of |n=hang_after|; if |n>=0|, hanging + indentation takes place on lines |n+1|, |n+2|, \dots, otherwise it takes + place on lines 1, \dots, $\vert n\vert$. When hanging indentation is active, + the left margin is |hang_indent|, if |hang_indent>=0|, else it is 0; the line + length is $|hsize|-\vert|hang_indent|\vert$. The normal setting is + |par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|. Note that if + |hang_indent=0|, the value of |hang_after| is irrelevant. + +*/ + +/*tex line numbers |>easy_line| are equivalent in break nodes */ + +static halfword easy_line; + +/*tex line numbers |>last_special_line| all have the same width */ + +static halfword last_special_line; + +/*tex the width of all lines |<=last_special_line|, if no \.{\\parshape} has been specified */ + +static scaled first_width; + +/*tex the width of all lines |>last_special_line| */ + +static scaled second_width; + +/*tex left margin to go with |first_width| */ + +static scaled first_indent; + +/*tex left margin to go with |second_width| */ + +static scaled second_indent; + +/*tex use this passive node and its predecessors */ + +static halfword best_bet; + +/*tex the demerits associated with |best_bet| */ + +static int fewest_demerits; + +/*tex line number following the last line of the new paragraph */ + +static halfword best_line; + +/*tex the difference between |line_number(best_bet)| and the optimum |best_line| */ + +static int actual_looseness; + +/*tex the difference between the current line number and the optimum |best_line| */ + +static int line_diff; + +/*tex + + \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, |rule_node|, + |ins_node|, |mark_node|, |adjust_node|, |disc_node|, |whatsit_node|, and + |math_node| are at the low end of the type codes, by permitting a break at + glue in a list if and only if the |type| of the previous node is less than + |math_node|. Furthermore, a node is discarded after a break if its type is + |math_node| or~more. + +*/ + +#define do_all_six(a) a(1);a(2);a(3);a(4);a(5);a(6);a(7) +#define do_seven_eight(a) if (adjust_spacing > 1) { a(8);a(9); } +#define do_all_eight(a) do_all_six(a); do_seven_eight(a) +#define do_one_seven_eight(a) a(1); do_seven_eight(a) + +#define store_background(a) {active_width[a]=background[a];} + +#define kern_break() { \ + if ((!is_char_node(vlink(cur_p))) && auto_breaking) \ + if (type(vlink(cur_p))==glue_node) \ + ext_try_break(\ + 0, \ + unhyphenated_node, \ + line_break_dir, \ + adjust_spacing, \ + par_shape_ptr, \ + adj_demerits, \ + tracing_paragraphs, \ + protrude_chars, \ + line_penalty, \ + last_line_fit, \ + double_hyphen_demerits, \ + final_hyphen_demerits, \ + first_p, \ + cur_p \ + ); \ + if (type(cur_p)!=math_node) \ + active_width[1] += width(cur_p); \ + else \ + active_width[1] += surround(cur_p); \ +} + +#define clean_up_the_memory() { \ + q=vlink(active); \ + while (q!=active) { \ + cur_p = vlink(q); \ + if (type(q)==delta_node) \ + flush_node(q); \ + else \ + flush_node(q); \ + q = cur_p; \ + } \ + q = passive; \ + while (q!=null) { \ + cur_p = vlink(q); \ + flush_node(q); \ + q = cur_p; \ + } \ +} + +/*tex special algorithm for last line of paragraph? */ + +static boolean do_last_line_fit; + +/*tex infinite stretch components of |par_fill_skip| */ + +static scaled fill_width[4]; + +/*tex |shortfall| corresponding to |minimal_demerits| */ + +static scaled best_pl_short[4]; + +/*tex corresponding glue stretch or shrink */ + +static scaled best_pl_glue[4]; + +#define reset_disc_width(a) disc_width[(a)] = 0 + +#define add_disc_width_to_break_width(a) break_width[(a)] += disc_width[(a)] +#define sub_disc_width_from_active_width(a) active_width[(a)] -= disc_width[(a)] + +#define add_char_shrink(a,b) a += char_shrink((b)) +#define add_char_stretch(a,b) a += char_stretch((b)) +#define sub_char_shrink(a,b) a -= char_shrink((b)) +#define sub_char_stretch(a,b) a -= char_stretch((b)) + +#define add_kern_shrink(a,b) a += kern_shrink((b)) +#define add_kern_stretch(a,b) a += kern_stretch((b)) +#define sub_kern_shrink(a,b) a -= kern_shrink((b)) +#define sub_kern_stretch(a,b) a -= kern_stretch((b)) + +/*tex + + This function is used to add the width of a list of nodes (from a + discretionary) to one of the width arrays. + + Replacement texts and discretionary texts are supposed to contain only + character nodes, kern nodes, and box or rule nodes. + +*/ + +#define bad_node_in_disc_error(p) { \ + if (type(p) == whatsit_node) { \ + formatted_error("linebreak","invalid node with type %s and subtype %i found in discretionary",node_data[type(p)].name,subtype(p)); \ + } else { \ + formatted_error("linebreak","invalid node with type %s found in discretionary",node_data[type(p)].name); \ + } \ +} + +static void add_to_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths) +{ + while (s != null) { + if (is_char_node(s)) { + widths[1] += pack_width(line_break_dir, dir_TRT, s, true); + if ((adjust_spacing > 1) && check_expand_pars(font(s))) { + set_prev_char_p(s); + add_char_stretch(widths[8], s); + add_char_shrink(widths[9], s); + }; + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + widths[1] += pack_width(line_break_dir, box_dir(s), s, false); + break; + case kern_node: + if ((adjust_spacing == 2) && (subtype(s) == normal)) { + add_kern_stretch(widths[8], s); + add_kern_shrink(widths[9], s); + } + /*tex fall through */ + case rule_node: + widths[1] += width(s); + break; + case disc_node: + break; + default: + bad_node_in_disc_error(s); + break; + } + } + s = vlink(s); + } +} + +/*tex + + This function is used to substract the width of a list of nodes (from a + discretionary) from one of the width arrays. It is used only once, but + deserves it own function because of orthogonality with the |add_to_widths| + function. + +*/ + +static void sub_from_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths) +{ + while (s != null) { + /*tex Subtract the width of node |s| from |break_width|; */ + if (is_char_node(s)) { + widths[1] -= pack_width(line_break_dir, dir_TRT, s, true); + if ((adjust_spacing > 1) && check_expand_pars(font(s))) { + set_prev_char_p(s); + sub_char_stretch(widths[8], s); + sub_char_shrink(widths[9], s); + } + } else { + switch (type(s)) { + case hlist_node: + case vlist_node: + widths[1] -= pack_width(line_break_dir, box_dir(s), s, false); + break; + case kern_node: + if ((adjust_spacing == 2) && (subtype(s) == normal)) { + sub_kern_stretch(widths[8], s); + sub_kern_shrink(widths[9], s); + } + /*tex fall through */ + case rule_node: + widths[1] -= width(s); + break; + case disc_node: + break; + default: + bad_node_in_disc_error(s); + break; + } + } + s = vlink(s); + } +} + +/*tex + + When we insert a new active node for a break at |cur_p|, suppose this new + node is to be placed just before active node |a|; then we essentially want to + insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where + $\delta=\alpha(a)-\alpha(|cur_p|)$ and + $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$ in the notation explained above. + The |cur_active_width| array now holds $\gamma+\beta(|cur_p|)-\alpha(a)$; so + $\delta$ can be obtained by subtracting |cur_active_width| from the quantity + $\gamma+\beta(|cur_p|)- \alpha(|cur_p|)$. The latter quantity can be regarded + as the length of a line ``from |cur_p| to |cur_p|''; we call it the + |break_width| at |cur_p|. + + The |break_width| is usually negative, since it consists of the background + (which is normally zero) minus the width of nodes following~|cur_p| that are + eliminated after a break. If, for example, node |cur_p| is a glue node, the + width of this glue is subtracted from the background; and we also look ahead + to eliminate all subsequent glue and penalty and kern and math nodes, + subtracting their widths as well. + + Kern nodes do not disappear at a line break unless they are |explicit|. + +*/ + +static void compute_break_width(int break_type, int line_break_dir, int adjust_spacing, halfword p) +{ + /*tex + + Glue and other 'whitespace' to be skipped after a break; used if + unhyphenated, or |post_break==empty|. + + */ + halfword s = p; + if (break_type > unhyphenated_node && p != null) { + /*tex + + Compute the discretionary |break_width| values. + + When |p| is a discretionary break, the length of a line ``from |p| to + |p|'' has to be defined properly so that the other calculations work + out. Suppose that the pre-break text at |p| has length $l_0$, the + post-break text has length $l_1$, and the replacement text has length + |l|. Suppose also that |q| is the node following the replacement + text. Then length of a line from |p| to |q| will be computed as + $\gamma+\beta(q)-\alpha(|p|)$, where $\beta(q)=\beta(|p|)-l_0+l$. The + actual length will be the background plus $l_1$, so the length from + |p| to |p| should be $\gamma+l_0+l_1-l$. If the post-break text of + the discretionary is empty, a break may also discard~|q|; in that + unusual case we subtract the length of~|q| and any other nodes that + will be discarded after the discretionary break. + + The value of $l_0$ need not be computed, since |line_break| will put + it into the global variable |disc_width| before calling |try_break|. + + In case of nested discretionaries, we always follow the no-break + path, as we are talking about the breaking on {\it this} position. + + */ + sub_from_widths(vlink_no_break(p), line_break_dir, adjust_spacing, break_width); + add_to_widths(vlink_post_break(p), line_break_dir, adjust_spacing, break_width); + do_one_seven_eight(add_disc_width_to_break_width); + if (vlink_post_break(p) == null) { + /*tex no |post_break|: 'skip' any 'whitespace' following */ + s = vlink(p); + } else { + s = null; + } + } + while (s != null) { + switch (type(s)) { + case math_node: + /*tex begin mathskip code */ + if (glue_is_zero(s)) { + break_width[1] -= surround(s); + break; + } else { + /*tex fall through */ + } + /*tex end mathskip code */ + case glue_node: + /*tex Subtract glue from |break_width|; */ + break_width[1] -= width(s); + break_width[2 + stretch_order(s)] -= stretch(s); + break_width[7] -= shrink(s); + break; + case penalty_node: + break; + case kern_node: + if (subtype(s) != explicit_kern && subtype(s) != italic_kern) + return; + else + break_width[1] -= width(s); + break; + default: + return; + }; + s = vlink(s); + } +} + +static void print_break_node(halfword q, fitness_value fit_class, quarterword break_type, halfword cur_p) +{ + /*tex Print a symbolic description of the new break node. */ + tprint_nl("@@"); + print_int(serial(passive)); + tprint(": line "); + print_int(line_number(q) - 1); + print_char('.'); + print_int(fit_class); + if (break_type == hyphenated_node) + print_char('-'); + tprint(" t="); + print_int(total_demerits(q)); + if (do_last_line_fit) { + /*tex Print additional data in the new active node. */ + tprint(" s="); + print_scaled(active_short(q)); + if (cur_p == null) + tprint(" a="); + else + tprint(" g="); + print_scaled(active_glue(q)); + } + tprint(" -> @"); + if (prev_break(passive) == null) + print_char('0'); + else + print_int(serial(prev_break(passive))); +} + +static void print_feasible_break(halfword cur_p, pointer r, halfword b, int pi, int d, boolean artificial_demerits) +{ + /*tex + + Print a symbolic description of this feasible break. + + */ + if (printed_node != cur_p) { + /*tex + + Print the list between |printed_node| and |cur_p|, then set + |printed_node:=cur_p|. + + */ + tprint_nl(""); + if (cur_p == null) { + short_display(vlink(printed_node)); + } else { + halfword save_link = vlink(cur_p); + vlink(cur_p) = null; + tprint_nl(""); + short_display(vlink(printed_node)); + vlink(cur_p) = save_link; + } + printed_node = cur_p; + } + tprint_nl("@"); + if (cur_p == null) { + tprint_esc("par"); + } else if (type(cur_p) != glue_node) { + if (type(cur_p) == penalty_node) + tprint_esc("penalty"); + else if (type(cur_p) == disc_node) + tprint_esc("discretionary"); + else if (type(cur_p) == kern_node) + tprint_esc("kern"); + else + tprint_esc("math"); + } + tprint(" via @"); + if (break_node(r) == null) + print_char('0'); + else + print_int(serial(break_node(r))); + tprint(" b="); + if (b > inf_bad) + print_char('*'); + else + print_int(b); + tprint(" p="); + print_int(pi); + tprint(" d="); + if (artificial_demerits) + print_char('*'); + else + print_int(d); +} + +#define add_disc_width_to_active_width(a) active_width[a] += disc_width[a] +#define update_width(a) cur_active_width[a] += varmem[(r+(a))].cint + +#define set_break_width_to_background(a) break_width[a]=background[(a)] + +#define convert_to_break_width(a) \ + varmem[(prev_r+(a))].cint = varmem[(prev_r+(a))].cint-cur_active_width[(a)]+break_width[(a)] + +#define store_break_width(a) active_width[(a)]=break_width[(a)] + +#define new_delta_to_break_width(a) \ + varmem[(q+(a))].cint=break_width[(a)]-cur_active_width[(a)] + +#define new_delta_from_break_width(a) \ + varmem[(q+(a))].cint=cur_active_width[(a)]-break_width[(a)] + +#define copy_to_cur_active(a) cur_active_width[(a)]=active_width[(a)] + +#define combine_two_deltas(a) varmem[(prev_r+(a))].cint += varmem[(r+(a))].cint +#define downdate_width(a) cur_active_width[(a)] -= varmem[(prev_r+(a))].cint +#define update_active(a) active_width[(a)]+=varmem[(r+(a))].cint + +#define total_font_stretch cur_active_width[8] +#define total_font_shrink cur_active_width[9] + +#define cal_margin_kern_var(a) { \ + character(cp) = character((a)); \ + font(cp) = font((a)); \ + do_subst_font(cp, 1000); \ + if (font(cp) != font((a))) \ + margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \ + font(cp) = font((a)); \ + do_subst_font(cp, -1000); \ + if (font(cp) != font((a))) \ + margin_kern_shrink += (left_pw(cp) - left_pw((a))); \ +} + +static void ext_try_break( + int pi, + quarterword break_type, + int line_break_dir, + int adjust_spacing, + int par_shape_ptr, + int adj_demerits, + int tracing_paragraphs, + int protrude_chars, + int line_penalty, + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, halfword first_p, halfword cur_p +) +{ + /*tex runs through the active list */ + pointer r; + scaled margin_kern_stretch; + scaled margin_kern_shrink; + halfword lp, rp, cp; + /*tex stays a step behind |r| */ + halfword prev_r = active; + /*tex a step behind |prev_r|, if |type(prev_r)=delta_node| */ + halfword prev_prev_r = null; + /*tex maximum line number in current equivalence class of lines */ + halfword old_l = 0; + /*tex have we found a feasible break at |cur_p|? */ + boolean no_break_yet = true; + /*tex points to a new node being created */ + halfword q; + /*tex line number of current active node */ + halfword l; + /*tex should node |r| remain in the active list? */ + boolean node_r_stays_active; + /*tex the current line will be justified to this width */ + scaled line_width = 0; + /*tex possible fitness class of test line */ + fitness_value fit_class; + /*tex badness of test line */ + halfword b; + /*tex demerits of test line */ + int d; + /*tex has |d| been forced to zero? */ + boolean artificial_demerits; + /*tex used in badness calculations */ + scaled shortfall; + /*tex glue stretch or shrink of test line, adjustment for last line */ + scaled g = 0; + /*tex distance from current active node */ + scaled cur_active_width[10] = { 0 }; + /*tex Make sure that |pi| is in the proper range; */ + if (pi >= inf_penalty) { + /*tex this breakpoint is inhibited by infinite penalty */ + return; + } else if (pi <= -inf_penalty) { + /*tex this breakpoint will be forced */ + pi = eject_penalty; + } + + do_all_eight(copy_to_cur_active); + + while (1) { + r = vlink(prev_r); + /*tex + + If node |r| is of type |delta_node|, update |cur_active_width|, set + |prev_r| and |prev_prev_r|, then |goto continue|. The following code + uses the fact that |type(active)<>delta_node|. + + */ + if (type(r) == delta_node) { + /*tex implicit */ + do_all_eight(update_width); + prev_prev_r = prev_r; + prev_r = r; + continue; + } + /*tex + + If a line number class has ended, create new active nodes for the + best feasible breaks in that class; then |return| if |r=active|, + otherwise compute the new |line_width|. + + The first part of the following code is part of \TeX's inner loop, so + we don't want to waste any time. The current active node, namely node + |r|, contains the line number that will be considered next. At the + end of the list we have arranged the data structure so that + |r=active| and |line_number(active)>old_l|. + + */ + l = line_number(r); + if (l > old_l) { + /*tex now we are no longer in the inner loop */ + if ((minimum_demerits < awful_bad) + && ((old_l != easy_line) || (r == active))) { + /*tex + + Create new active nodes for the best feasible breaks just + found. It is not necessary to create new active nodes having + |minimal_demerits| greater than + |minimum_demerits+abs(adj_demerits)|, since such active nodes + will never be chosen in the final paragraph breaks. This + observation allows us to omit a substantial number of + feasible breakpoints from further consideration. + + */ + if (no_break_yet) { + no_break_yet = false; + do_all_eight(set_break_width_to_background); + compute_break_width(break_type, line_break_dir, adjust_spacing, cur_p); + } + /*tex + + Insert a delta node to prepare for breaks at |cur_p|. We use + the fact that |type(active)<>delta_node|. + + */ + if (type(prev_r) == delta_node) { + /*tex modify an existing delta node */ + do_all_eight(convert_to_break_width); + } else if (prev_r == active) { + /*tex no delta node needed at the beginning */ + do_all_eight(store_break_width); + } else { + q = new_node(delta_node, 0); + vlink(q) = r; + do_all_eight(new_delta_to_break_width); + vlink(prev_r) = q; + prev_prev_r = prev_r; + prev_r = q; + } + if (abs(adj_demerits) >= awful_bad - minimum_demerits) + minimum_demerits = awful_bad - 1; + else + minimum_demerits += abs(adj_demerits); + for (fit_class = very_loose_fit; fit_class <= tight_fit; + fit_class++) { + if (minimal_demerits[fit_class] <= minimum_demerits) { + /*tex + + Insert a new active node from |best_place[fit_class]| + to |cur_p|. When we create an active node, we also + create the corresponding passive node. + + */ + q = new_node(passive_node, 0); + vlink(q) = passive; + passive = q; + cur_break(q) = cur_p; + incr(pass_number); + serial(q) = pass_number; + prev_break(q) = best_place[fit_class]; + /*tex + + Here we keep track of the subparagraph penalties in + the break nodes. + + */ + passive_pen_inter(q) = internal_pen_inter; + passive_pen_broken(q) = internal_pen_broken; + passive_last_left_box(q) = internal_left_box; + passive_last_left_box_width(q) = + internal_left_box_width; + if (prev_break(q) != null) { + passive_left_box(q) = passive_last_left_box(prev_break(q)); + passive_left_box_width(q) = passive_last_left_box_width(prev_break(q)); + } else { + passive_left_box(q) = init_internal_left_box; + passive_left_box_width(q) = init_internal_left_box_width; + } + passive_right_box(q) = internal_right_box; + passive_right_box_width(q) = internal_right_box_width; + q = new_node(break_type, fit_class); + break_node(q) = passive; + line_number(q) = best_pl_line[fit_class] + 1; + total_demerits(q) = minimal_demerits[fit_class]; + if (do_last_line_fit) { + /*tex + + Store additional data in the new active node. + Here we save these data in the active node + representing a potential line break. + + */ + active_short(q) = best_pl_short[fit_class]; + active_glue(q) = best_pl_glue[fit_class]; + } + vlink(q) = r; + vlink(prev_r) = q; + prev_r = q; + if (tracing_paragraphs > 0) + print_break_node(q, fit_class, break_type, cur_p); + } + minimal_demerits[fit_class] = awful_bad; + } + minimum_demerits = awful_bad; + /*tex + + Insert a delta node to prepare for the next active node. When + the following code is performed, we will have just inserted + at least one active node before |r|, so + |type(prev_r)<>delta_node|. + + */ + if (r != active) { + q = new_node(delta_node, 0); + vlink(q) = r; + do_all_eight(new_delta_from_break_width); + vlink(prev_r) = q; + prev_prev_r = prev_r; + prev_r = q; + } + } + if (r == active) + return; + /*tex + + Compute the new line width. When we come to the following code, + we have just encountered the first active node~|r| whose + |line_number| field contains |l|. Thus we want to compute the + length of the $l\mskip1mu$th line of the current paragraph. + Furthermore, we want to set |old_l| to the last number in the + class of line numbers equivalent to~|l|. + + */ + if (l > easy_line) { + old_l = max_halfword - 1; + line_width = second_width; + } else { + old_l = l; + if (l > last_special_line) { + line_width = second_width; + } else if (par_shape_ptr == null) { + line_width = first_width; + } else { + line_width = varmem[(par_shape_ptr + 2 * l + 1)].cint; + } + } + } + /*tex + + If a line number class has ended, create new active nodes for the + best feasible breaks in that class; then |return| if |r=active|, + otherwise compute the new |line_width|. + + Consider the demerits for a line from |r| to |cur_p|; deactivate node + |r| if it should no longer be active; then |goto continue| if a line + from |r| to |cur_p| is infeasible, otherwise record a new feasible + break. + + */ + artificial_demerits = false; + shortfall = line_width - cur_active_width[1]; + if (break_node(r) == null) + shortfall -= init_internal_left_box_width; + else + shortfall -= passive_last_left_box_width(break_node(r)); + shortfall -= internal_right_box_width; + if (protrude_chars > 1) { + halfword l1, o; + l1 = (break_node(r) == null) ? first_p : cur_break(break_node(r)); + if (cur_p == null) { + o = null; + } else { + o = alink(cur_p); + assert(vlink(o) == cur_p); + } + /*tex + + The disc could be a SELECT subtype, to we might need to get the + last character as |pre_break| from either the |pre_break| list + (if the previous INIT disc was taken), or the |no_break| (sic) + list (if the previous INIT disc was not taken). + + The last characters (hyphenation character) if these two list + should always be the same anyway, so we just look at |pre_break|. + + Let's look at the right margin first. + + */ + if ((cur_p != null) && (type(cur_p) == disc_node) && (vlink_pre_break(cur_p) != null)) { + /*tex a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ + o = tlink_pre_break(cur_p); + } else { + o = find_protchar_right(l1, o); + } + /*tex now the left margin */ + if ((l1 != null) && (type(l1) == disc_node) && (vlink_post_break(l1) != null)) { + /*tex The first char could be a disc! Protrude the first char. */ + l1 = vlink_post_break(l1); + } else { + l1 = find_protchar_left(l1, true); + } + shortfall += (left_pw(l1) + right_pw(o)); + } + if (shortfall != 0) { + margin_kern_stretch = 0; + margin_kern_shrink = 0; + if (protrude_chars > 1) { + /*tex Calculate variations of marginal kerns. */ + lp = last_leftmost_char; + rp = last_rightmost_char; + cp = raw_glyph_node(); + if (lp != null) { + cal_margin_kern_var(lp); + } + if (rp != null) { + cal_margin_kern_var(rp); + } + flush_node(cp); + } + if ((shortfall > 0) && ((total_font_stretch + margin_kern_stretch) > 0)) { + if ((total_font_stretch + margin_kern_stretch) > shortfall) + shortfall = ((total_font_stretch + margin_kern_stretch) / (max_stretch_ratio / cur_font_step)) / 2; + else + shortfall -= (total_font_stretch + margin_kern_stretch); + } else if ((shortfall < 0) && ((total_font_shrink + margin_kern_shrink) > 0)) { + if ((total_font_shrink + margin_kern_shrink) > -shortfall) + shortfall -= ((total_font_shrink + margin_kern_shrink) / (max_shrink_ratio / cur_font_step)) / 2; + else + shortfall += (total_font_shrink + margin_kern_shrink); + } + } + if (shortfall > 0) { + /*tex + + Set the value of |b| to the badness for stretching the line, and + compute the corresponding |fit_class|. + + When a line must stretch, the available stretchability can be + found in the subarray |cur_active_width[2..6]|, in units of + points, sfi, fil, fill and filll. + + The present section is part of \TeX's inner loop, and it is most + often performed when the badness is infinite; therefore it is + worth while to make a quick test for large width excess and small + stretchability, before calling the |badness| subroutine. + + */ + if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) || + (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) { + if (do_last_line_fit) { + if (cur_p == null) { + /*tex + + The last line of a paragraph. Perform computations + for last line and |goto found|. + + Here we compute the adjustment |g| and badness |b| + for a line from |r| to the end of the paragraph. When + any of the criteria for adjustment is violated we + fall through to the normal algorithm. + + The last line must be too short, and have infinite + stretch entirely due to |par_fill_skip|. + + */ + if ((active_short(r) == 0) || (active_glue(r) <= 0)) + /*tex + + Previous line was neither stretched nor shrunk, + or was infinitely bad. + + */ + goto NOT_FOUND; + if ((cur_active_width[3] != fill_width[0]) || (cur_active_width[4] != fill_width[1]) || + (cur_active_width[5] != fill_width[2]) || (cur_active_width[6] != fill_width[3])) + /*tex + + Infinite stretch of this line not entirely due to |par_fill_skip|. + + */ + goto NOT_FOUND; + if (active_short(r) > 0) + g = cur_active_width[2]; + else + g = cur_active_width[7]; + if (g <= 0) + /*tex No finite stretch resp.\ no shrink. */ + goto NOT_FOUND; + arith_error = false; + g = fract(g, active_short(r), active_glue(r), + max_dimen); + if (last_line_fit < 1000) + g = fract(g, last_line_fit, 1000, max_dimen); + if (arith_error) { + if (active_short(r) > 0) + g = max_dimen; + else + g = -max_dimen; + } + if (g > 0) { + /*tex + + Set the value of |b| to the badness of the last + line for stretching, compute the corresponding + |fit_class, and |goto found|. These badness + computations are rather similar to those of the + standard algorithm, with the adjustment amount + |g| replacing the |shortfall|. + + */ + if (g > shortfall) + g = shortfall; + if (g > 7230584) { + if (cur_active_width[2] < 1663497) { + b = inf_bad; + fit_class = very_loose_fit; + goto FOUND; + } + } + b = badness(g, cur_active_width[2]); + if (b > 99) { + fit_class = very_loose_fit; + } else if (b > 12) { + fit_class = loose_fit; + } else { + fit_class = decent_fit; + } + goto FOUND; + } else if (g < 0) { + /*tex + + Set the value of |b| to the badness of the last + line for shrinking, compute the corresponding + |fit_class, and |goto found||. + + */ + if (-g > cur_active_width[7]) + g = -cur_active_width[7]; + b = badness(-g, cur_active_width[7]); + if (b > 12) + fit_class = tight_fit; + else + fit_class = decent_fit; + goto FOUND; + } + } + NOT_FOUND: + shortfall = 0; + } + b = 0; + /*tex Infinite stretch. */ + fit_class = decent_fit; + } else if (shortfall > 7230584 && cur_active_width[2] < 1663497) { + b = inf_bad; + fit_class = very_loose_fit; + } else { + b = badness(shortfall, cur_active_width[2]); + if (b > 99) { + fit_class = very_loose_fit; + } else if (b > 12) { + fit_class = loose_fit; + } else { + fit_class = decent_fit; + } + } + } else { + /*tex + + Set the value of |b| to the badness for shrinking the line, and + compute the corresponding |fit_class|. Shrinkability is never + infinite in a paragraph; we can shrink the line from |r| to + |cur_p| by at most |cur_active_width[7]|. + + */ + if (-shortfall > cur_active_width[7]) + b = inf_bad + 1; + else + b = badness(-shortfall, cur_active_width[7]); + if (b > 12) + fit_class = tight_fit; + else + fit_class = decent_fit; + } + if (do_last_line_fit) { + /*tex Adjust the additional data for last line; */ + if (cur_p == null) + shortfall = 0; + if (shortfall > 0) { + g = cur_active_width[2]; + } else if (shortfall < 0) { + g = cur_active_width[7]; + } else { + g = 0; + } + } + FOUND: + if ((b > inf_bad) || (pi == eject_penalty)) { + /*tex + + Prepare to deactivate node~|r|, and |goto deactivate| unless + there is a reason to consider lines of text from |r| to |cur_p|. + During the final pass, we dare not lose all active nodes, lest we + lose touch with the line breaks already found. The code shown + here makes sure that such a catastrophe does not happen, by + permitting overfull boxes as a last resort. This particular part + of \TeX\ was a source of several subtle bugs before the correct + program logic was finally discovered; readers who seek to + ``improve'' \TeX\ should therefore think thrice before daring to + make any changes here. + + */ + if (final_pass && (minimum_demerits == awful_bad) && + (vlink(r) == active) && (prev_r == active)) { + /*tex Set demerits zero, this break is forced. */ + artificial_demerits = true; + } else if (b > threshold) { + goto DEACTIVATE; + } + node_r_stays_active = false; + } else { + prev_r = r; + if (b > threshold) + continue; + node_r_stays_active = true; + } + /*tex + + Record a new feasible break. When we get to this part of the code, + the line from |r| to |cur_p| is feasible, its badness is~|b|, and its + fitness classification is |fit_class|. We don't want to make an + active node for this break yet, but we will compute the total + demerits and record them in the |minimal_demerits| array, if such a + break is the current champion among all ways to get to |cur_p| in a + given line-number class and fitness class. + + */ + if (artificial_demerits) { + d = 0; + } else { + /*tex Compute the demerits, |d|, from |r| to |cur_p|. */ + d = line_penalty + b; + if (abs(d) >= 10000) + d = 100000000; + else + d = d * d; + if (pi != 0) { + if (pi > 0) { + d += (pi * pi); + } else if (pi > eject_penalty) { + d -= (pi * pi); + } + } + if ((break_type == hyphenated_node) && (type(r) == hyphenated_node)) { + if (cur_p != null) + d += double_hyphen_demerits; + else + d += final_hyphen_demerits; + } + if (abs(fit_class - fitness(r)) > 1) + d = d + adj_demerits; + } + if (tracing_paragraphs > 0) { + print_feasible_break(cur_p, r, b, pi, d, artificial_demerits); + } + /*tex This is the minimum total demerits from the beginning to |cur_p| via |r|. */ + d += total_demerits(r); + if (d <= minimal_demerits[fit_class]) { + minimal_demerits[fit_class] = d; + best_place[fit_class] = break_node(r); + best_pl_line[fit_class] = l; + if (do_last_line_fit) { + /*tex + + Store additional data for this feasible break. For each + feasible break we record the shortfall and glue stretch or + shrink (or adjustment). + + */ + best_pl_short[fit_class] = shortfall; + best_pl_glue[fit_class] = g; + } + if (d < minimum_demerits) + minimum_demerits = d; + } + /*tex Record a new feasible break. */ + if (node_r_stays_active) { + /*tex |prev_r| has been set to |r|. */ + continue; + } + DEACTIVATE: + /*tex + + Deactivate node |r|. When an active node disappears, we must delete + an adjacent delta node if the active node was at the beginning or the + end of the active list, or if it was surrounded by delta nodes. We + also must preserve the property that |cur_active_width| represents + the length of material from |vlink(prev_r)| to~|cur_p|. + + */ + vlink(prev_r) = vlink(r); + flush_node(r); + if (prev_r == active) { + /*tex + + Update the active widths, since the first active node has been + deleted. The following code uses the fact that + |type(active)<>delta_node|. If the active list has just become + empty, we do not need to update the |active_width| array, since + it will be initialized when an active node is next inserted. + + */ + r = vlink(active); + if (type(r) == delta_node) { + do_all_eight(update_active); + do_all_eight(copy_to_cur_active); + vlink(active) = vlink(r); + flush_node(r); + } + } else if (type(prev_r) == delta_node) { + r = vlink(prev_r); + if (r == active) { + do_all_eight(downdate_width); + vlink(prev_prev_r) = active; + flush_node(prev_r); + prev_r = prev_prev_r; + } else if (type(r) == delta_node) { + do_all_eight(update_width); + do_all_eight(combine_two_deltas); + vlink(prev_r) = vlink(r); + flush_node(r); + } + } + } +} + +void ext_do_line_break( + int paragraph_dir, + int pretolerance, + int tracing_paragraphs, + int tolerance, + scaled emergency_stretch, + int looseness, + int adjust_spacing, + halfword par_shape_ptr, + int adj_demerits, + int protrude_chars, + int line_penalty, + int last_line_fit, + int double_hyphen_demerits, + int final_hyphen_demerits, + int hang_indent, + int hsize, + int hang_after, + halfword left_skip, + halfword right_skip, + halfword inter_line_penalties_ptr, + int inter_line_penalty, + int club_penalty, + halfword club_penalties_ptr, + halfword widow_penalties_ptr, + int widow_penalty, + int broken_penalty, + halfword final_par_glue +) +{ + /*tex Miscellaneous nodes of temporary interest. */ + halfword cur_p, q, r, s; + int line_break_dir = paragraph_dir; + /*tex Get ready to start */ + minimum_demerits = awful_bad; + minimal_demerits[tight_fit] = awful_bad; + minimal_demerits[decent_fit] = awful_bad; + minimal_demerits[loose_fit] = awful_bad; + minimal_demerits[very_loose_fit] = awful_bad; + fewest_demerits = 0; + actual_looseness = 0; + /*tex + + We compute the values of |easy_line| and the other local variables + relating to line length when the |line_break| procedure is initializing + itself. + + */ + if (par_shape_ptr == null) { + if (hang_indent == 0) { + last_special_line = 0; + second_width = hsize; + second_indent = 0; + } else { + halfword used_hang_indent = swap_hang_indent(hang_indent); + /*tex + + Set line length parameters in preparation for hanging + indentation. We compute the values of |easy_line| and the other + local variables relating to line length when the |line_break| + procedure is initializing itself. + + */ + last_special_line = abs(hang_after); + if (hang_after < 0) { + first_width = hsize - abs(used_hang_indent); + if (used_hang_indent >= 0) + first_indent = used_hang_indent; + else + first_indent = 0; + second_width = hsize; + second_indent = 0; + } else { + first_width = hsize; + first_indent = 0; + second_width = hsize - abs(used_hang_indent); + if (used_hang_indent >= 0) + second_indent = used_hang_indent; + else + second_indent = 0; + } + } + } else { + last_special_line = vinfo(par_shape_ptr + 1) - 1; + second_indent = varmem[(par_shape_ptr + 2 * (last_special_line + 1))].cint; + second_width = varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint; + second_indent = swap_parshape_indent(second_indent,second_width); + } + if (looseness == 0) + easy_line = last_special_line; + else + easy_line = max_halfword; + no_shrink_error_yet = true; + check_shrinkage(left_skip); + check_shrinkage(right_skip); + q = left_skip; + r = right_skip; + background[1] = width(q) + width(r); + background[2] = 0; + background[3] = 0; + background[4] = 0; + background[5] = 0; + background[6] = 0; + background[2 + stretch_order(q)] = stretch(q); + background[2 + stretch_order(r)] += stretch(r); + background[7] = shrink(q) + shrink(r); + if (adjust_spacing > 1) { + background[8] = 0; + background[9] = 0; + max_stretch_ratio = -1; + max_shrink_ratio = -1; + cur_font_step = -1; + set_prev_char_p(null); + } + /*tex + + Check for special treatment of last line of paragraph. The new algorithm + for the last line requires that the stretchability |par_fill_skip| is + infinite and the stretchability of |left_skip| plus |right_skip| is + finite. + + */ + do_last_line_fit = false; + if (last_line_fit > 0) { + q = last_line_fill; + if ((stretch(q) > 0) && (stretch_order(q) > normal)) { + if ((background[3] == 0) && (background[4] == 0) && (background[5] == 0) && (background[6] == 0)) { + do_last_line_fit = true; + fill_width[0] = 0; + fill_width[1] = 0; + fill_width[2] = 0; + fill_width[3] = 0; + fill_width[stretch_order(q) - 1] = stretch(q); + } + } + } + /*tex Initialize |dir_ptr| for |line_break|. */ + if (dir_ptr != null) { + flush_node_list(dir_ptr); + dir_ptr = null; + } + /*tex Find optimal breakpoints. */ + threshold = pretolerance; + if (threshold >= 0) { + if (tracing_paragraphs > 0) { + begin_diagnostic(); + tprint_nl("@firstpass"); + } + second_pass = false; + final_pass = false; + } else { + threshold = tolerance; + second_pass = true; + final_pass = (emergency_stretch <= 0); + if (tracing_paragraphs > 0) + begin_diagnostic(); + } + while (1) { + halfword first_p; + halfword nest_stack[10]; + int nest_index = 0; + if (threshold > inf_bad) + threshold = inf_bad; + /*tex Create an active breakpoint representing the beginning of the paragraph. */ + q = new_node(unhyphenated_node, decent_fit); + vlink(q) = active; + break_node(q) = null; + line_number(q) = cur_list.pg_field + 1; + total_demerits(q) = 0; + active_short(q) = 0; + active_glue(q) = 0; + vlink(active) = q; + do_all_eight(store_background); + passive = null; + printed_node = temp_head; + pass_number = 0; + font_in_short_display = null_font; + /*tex Create an active breakpoint representing the beginning of the paragraph. */ + auto_breaking = true; + cur_p = vlink(temp_head); + /*tex Initialize with first |local_paragraph| node. */ + if ((cur_p != null) && (type(cur_p) == local_par_node)) { + /*tex This used to be an assert, but may as well force it. */ + alink(cur_p) = temp_head; + internal_pen_inter = local_pen_inter(cur_p); + internal_pen_broken = local_pen_broken(cur_p); + init_internal_left_box = local_box_left(cur_p); + init_internal_left_box_width = local_box_left_width(cur_p); + internal_left_box = init_internal_left_box; + internal_left_box_width = init_internal_left_box_width; + internal_right_box = local_box_right(cur_p); + internal_right_box_width = local_box_right_width(cur_p); + } else { + internal_pen_inter = 0; + internal_pen_broken = 0; + init_internal_left_box = null; + init_internal_left_box_width = 0; + internal_left_box = init_internal_left_box; + internal_left_box_width = init_internal_left_box_width; + internal_right_box = null; + internal_right_box_width = 0; + } + /*tex Initialize with first |local_paragraph| node. */ + set_prev_char_p(null); + first_p = cur_p; + /*tex + + To access the first node of paragraph as the first active node has + |break_node=null|. + + */ + while ((cur_p != null) && (vlink(active) != active)) { + /*tex + + |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, + also look at |disc_node|s. + + */ + while (is_char_node(cur_p)) { + /*tex + + Advance |cur_p| to the node following the present string of + characters. The code that passes over the characters of words + in a paragraph is part of \TeX's inner loop, so it has been + streamlined for speed. We use the fact that + `\.{\\parfillskip}' glue appears at the end of each + paragraph; it is therefore unnecessary to check if + |vlink(cur_p)=null| when |cur_p| is a character node. + + */ + active_width[1] += pack_width(line_break_dir, dir_TRT, cur_p, true); + if ((adjust_spacing > 1) && check_expand_pars(font(cur_p))) { + set_prev_char_p(cur_p); + add_char_stretch(active_width[8], cur_p); + add_char_shrink(active_width[9], cur_p); + } + cur_p = vlink(cur_p); + while (cur_p == null && nest_index > 0) { + cur_p = nest_stack[--nest_index]; + } + } + if (cur_p == null) { + normal_error("linebreak","invalid list tail, probably missing glue"); + } + /*tex + + Determine legal breaks: As we move through the hlist, we need to + keep the |active_width| array up to date, so that the badness of + individual lines is readily calculated by |try_break|. It is + convenient to use the short name |active_width[1]| for the + component of active width that represents real width as opposed + to glue. + + */ + switch (type(cur_p)) { + case hlist_node: + case vlist_node: + active_width[1] += pack_width(line_break_dir, box_dir(cur_p), cur_p, false); + break; + case rule_node: + active_width[1] += width(cur_p); + break; + case dir_node: + /*tex Adjust the dir stack for the |line_break| routine. */ + if (subtype(cur_p) == normal_dir) { + line_break_dir = dir_dir(cur_p); + /* Adds to |dir_ptr|. */ + push_dir_node(dir_ptr,cur_p); + } else { + pop_dir_node(dir_ptr); + if (dir_ptr != null) { + line_break_dir = dir_dir(dir_ptr); + } + } + break; + case local_par_node: + /*tex Advance past a |local_paragraph| node. */ + internal_pen_inter = local_pen_inter(cur_p); + internal_pen_broken = local_pen_broken(cur_p); + internal_left_box = local_box_left(cur_p); + internal_left_box_width = local_box_left_width(cur_p); + internal_right_box = local_box_right(cur_p); + internal_right_box_width = local_box_right_width(cur_p); + break; + case math_node: + auto_breaking = (subtype(cur_p) == after); + /*tex begin mathskip code */ + if (glue_is_zero(cur_p) || ignore_math_skip(cur_p)) { + kern_break(); + break; + } else { + /*tex fall through */ + } + /*tex end mathskip code */ + case glue_node: + /*tex + + If node |cur_p| is a legal breakpoint, call |try_break|; + then update the active widths by including the glue in + |glue_ptr(cur_p)|. + + When node |cur_p| is a glue node, we look at the previous + to see whether or not a breakpoint is legal at |cur_p|, + as explained above. + + We only break after certain nodes (see texnodes.h), a + font related kern and a dir node when + |\breakafterdirmode=1|. + + */ + if (auto_breaking) { + halfword prev_p = alink(cur_p); + if (prev_p != temp_head && (is_char_node(prev_p) + || precedes_break(prev_p) || precedes_kern(prev_p) || precedes_dir(prev_p))) { + ext_try_break( + 0, + unhyphenated_node, + line_break_dir, + adjust_spacing, + par_shape_ptr, + adj_demerits, + tracing_paragraphs, + protrude_chars, + line_penalty, + last_line_fit, + double_hyphen_demerits, + final_hyphen_demerits, + first_p, + cur_p + ); + } + } + check_shrinkage(cur_p); + active_width[1] += width(cur_p); + active_width[2 + stretch_order(cur_p)] += stretch(cur_p); + active_width[7] += shrink(cur_p); + break; + case kern_node: + if (subtype(cur_p) == explicit_kern || subtype(cur_p) == italic_kern) { + kern_break(); + } else { + active_width[1] += width(cur_p); + if ((adjust_spacing == 2) && (subtype(cur_p) == normal)) { + add_kern_stretch(active_width[8], cur_p); + add_kern_shrink(active_width[9], cur_p); + } + } + break; + case disc_node: + /*tex + + |select_disc|s are handled by the leading |init_disc|. + + */ + if (subtype(cur_p) == select_disc) + break; + /*tex + + Try to break after a discretionary fragment, then |goto + done5|. The following code knows that discretionary texts + contain only character nodes, kern nodes, box nodes, and + rule nodes. This branch differs a bit from older engines + because in \LUATEX\ we already have hyphenated the list. + This means that we need to skip automatic disc nodes. Of + better, we need to treat discretionaries and explicit + hyphens always, even in the first pass. + + */ + if (second_pass || subtype(cur_p) <= automatic_disc) { + int actual_penalty = (int) disc_penalty(cur_p); + s = vlink_pre_break(cur_p); + do_one_seven_eight(reset_disc_width); + if (s == null) { + /*tex trivial pre-break */ + ext_try_break(actual_penalty, hyphenated_node, + line_break_dir, adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, protrude_chars, + line_penalty, last_line_fit, + double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + } else { + add_to_widths(s, line_break_dir, adjust_spacing, disc_width); + do_one_seven_eight(add_disc_width_to_active_width); + ext_try_break(actual_penalty, hyphenated_node, + line_break_dir, adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, protrude_chars, + line_penalty, last_line_fit, + double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + if (subtype(cur_p) == init_disc) { + /*tex + + We should at two break points after the one + we added above: + + \startitemize[n] + \startitem + which does a possible break in INIT's + |post_break| + \stopitem + \startitem + which means the |no_break| actually + was broken just a character later + \stopitem + \stopitemize + + Do the select-0 case |f-f-i|: + + */ + s = vlink_pre_break(vlink(cur_p)); + add_to_widths(s, line_break_dir, adjust_spacing, disc_width); + ext_try_break(actual_penalty, hyphenated_node, + line_break_dir, adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, + protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, + vlink(cur_p)); + /*tex This does not work. */ +#if 0 + /*tex Go back to the starting situation. */ + do_one_seven_eight(sub_disc_width_from_active_width); + do_one_seven_eight(reset_disc_width); + /*tex Add select |no_break| to |active_width|. */ + s = vlink_no_break(vlink(cur_p)); + add_to_widths(s, line_break_dir, adjust_spacing, disc_width); + ext_try_break(actual_penalty, hyphenated_node, + line_break_dir, adjust_spacing, + par_shape_ptr, adj_demerits, + tracing_paragraphs, + protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, + vlink(cur_p)); +#endif + } + do_one_seven_eight(sub_disc_width_from_active_width); + } + } + s = vlink_no_break(cur_p); + add_to_widths(s, line_break_dir, adjust_spacing, active_width); + break; + case penalty_node: + ext_try_break(penalty(cur_p), unhyphenated_node, line_break_dir, + adjust_spacing, par_shape_ptr, adj_demerits, + tracing_paragraphs, protrude_chars, + line_penalty, last_line_fit, + double_hyphen_demerits, final_hyphen_demerits, + first_p, cur_p); + break; + case boundary_node: + case whatsit_node: + /*tex Advance past a whatsit node in the |line_break| loop. */ + case mark_node: + case ins_node: + case adjust_node: + break; + case glue_spec_node: + normal_warning("parbuilder","found a glue_spec in a paragraph"); + break; + default: + formatted_error("parbuilder","weird node %d in paragraph",type(cur_p)); + } + cur_p = vlink(cur_p); + while (cur_p == null && nest_index > 0) { + cur_p = nest_stack[--nest_index]; + } + } + if (cur_p == null) { + /*tex + + Try the final line break at the end of the paragraph, and |goto + done| if the desired breakpoints have been found. + + The forced line break at the paragraph's end will reduce the list + of breakpoints so that all active nodes represent breaks at + |cur_p=null|. On the first pass, we insist on finding an active + node that has the correct ``looseness.'' On the final pass, there + will be at least one active node, and we will match the desired + looseness as well as we can. + + The global variable |best_bet| will be set to the active node for + the best way to break the paragraph, and a few other variables + are used to help determine what is best. + + */ + ext_try_break(eject_penalty, hyphenated_node, line_break_dir, + adjust_spacing, par_shape_ptr, adj_demerits, + tracing_paragraphs, protrude_chars, line_penalty, + last_line_fit, double_hyphen_demerits, + final_hyphen_demerits, first_p, cur_p); + if (vlink(active) != active) { + /*tex Find an active node with fewest demerits; */ + r = vlink(active); + fewest_demerits = awful_bad; + do { + if (type(r) != delta_node) { + if (total_demerits(r) < fewest_demerits) { + fewest_demerits = total_demerits(r); + best_bet = r; + } + } + r = vlink(r); + } while (r != active); + best_line = line_number(best_bet); + /*tex + Find an active node with fewest demerits; + */ + if (looseness == 0) + goto DONE; + /*tex + + Find the best active node for the desired looseness; + + The adjustment for a desired looseness is a slightly more + complicated version of the loop just considered. Note that if + a paragraph is broken into segments by displayed equations, + each segment will be subject to the looseness calculation, + independently of the other segments. + + */ + r = vlink(active); + actual_looseness = 0; + do { + if (type(r) != delta_node) { + line_diff = line_number(r) - best_line; + if (((line_diff < actual_looseness) + && (looseness <= line_diff)) + || ((line_diff > actual_looseness) + && (looseness >= line_diff))) { + best_bet = r; + actual_looseness = line_diff; + fewest_demerits = total_demerits(r); + } else if ((line_diff == actual_looseness) && + (total_demerits(r) < fewest_demerits)) { + best_bet = r; + fewest_demerits = total_demerits(r); + } + } + r = vlink(r); + } while (r != active); + best_line = line_number(best_bet); + /*tex + Find the best active node for the desired looseness. + */ + if ((actual_looseness == looseness) || final_pass) + goto DONE; + } + } + /*tex Clean up the memory by removing the break nodes. */ + clean_up_the_memory(); + /*tex Clean up the memory by removing the break nodes. */ + if (!second_pass) { + if (tracing_paragraphs > 0) + tprint_nl("@secondpass"); + threshold = tolerance; + second_pass = true; + final_pass = (emergency_stretch <= 0); + } else { + /*tex If at first you do not succeed, then: */ + if (tracing_paragraphs > 0) + tprint_nl("@emergencypass"); + background[2] += emergency_stretch; + final_pass = true; + } + } + + DONE: + if (tracing_paragraphs > 0) { + end_diagnostic(true); + normalize_selector(); + } + if (do_last_line_fit) { + /*tex + Adjust the final line of the paragraph; here we either reset + |do_last_line_fit| or adjust the |par_fill_skip| glue. + */ + if (active_short(best_bet) == 0) { + do_last_line_fit = false; + } else { + width(last_line_fill) += (active_short(best_bet) - active_glue(best_bet)); + stretch(last_line_fill) = 0; + } + } + /*tex + Break the paragraph at the chosen. Once the best sequence of + breakpoints has been found (hurray), we call on the procedure + |post_line_break| to finish the remainder of the work. By introducing + this subprocedure, we are able to keep |line_break| from getting + extremely long. + + the first thing |ext_post_line_break| does is reset |dir_ptr|. + + */ + flush_node_list(dir_ptr); + dir_ptr = null; + ext_post_line_break(paragraph_dir, + right_skip, + left_skip, + protrude_chars, + par_shape_ptr, + adjust_spacing, + inter_line_penalties_par_ptr, + inter_line_penalty, + club_penalty, + club_penalties_ptr, + widow_penalties_ptr, + widow_penalty, + broken_penalty, + final_par_glue, + best_bet, + last_special_line, + second_width, + second_indent, first_width, first_indent, best_line); + /*tex + + Clean up the memory by removing the break nodes. + + */ + clean_up_the_memory(); +} + +void get_linebreak_info (int *f, int *a) +{ + *f = fewest_demerits; + *a = actual_looseness; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.w b/Build/source/texk/web2c/luatexdir/tex/linebreak.w deleted file mode 100644 index 345a1a42456..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/linebreak.w +++ /dev/null @@ -1,2164 +0,0 @@ -% linebreak.w -% -% Copyright 2006-2008 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - -#include "ptexlib.h" - -@ We come now to what is probably the most interesting algorithm of \TeX: -the mechanism for choosing the ``best possible'' breakpoints that yield -the individual lines of a paragraph. \TeX's line-breaking algorithm takes -a given horizontal list and converts it to a sequence of boxes that are -appended to the current vertical list. In the course of doing this, it -creates a special data structure containing three kinds of records that are -not used elsewhere in \TeX. Such nodes are created while a paragraph is -being processed, and they are destroyed afterwards; thus, the other parts -of \TeX\ do not need to know anything about how line-breaking is done. - -The method used here is based on an approach devised by Michael F. Plass and -@^Plass, Michael Frederick@> -@^Knuth, Donald Ervin@> -the author in 1977, subsequently generalized and improved by the same two -people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice -\AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the -line-breaking problem can be regarded as a special case of the problem of -computing the shortest path in an acyclic network. The cited paper includes -numerous examples and describes the history of line breaking as it has been -practiced by printers through the ages. The present implementation adds two -new ideas to the algorithm of 1980: Memory space requirements are considerably -reduced by using smaller records for inactive nodes than for active ones, -and arithmetic overflow is avoided by using ``delta distances'' instead of -keeping track of the total distance from the beginning of the paragraph to the -current point. - -The |line_break| procedure should be invoked only in horizontal mode; it -leaves that mode and places its output into the current vlist of the -enclosing vertical mode (or internal vertical mode). -There is one explicit parameter: |d| is true for partial paragraphs -preceding display math mode; in this case the amount of additional -penalty inserted before the final line is |display_widow_penalty| -instead of |widow_penalty|. - -There are also a number of implicit parameters: The hlist to be broken -starts at |vlink(head)|, and it is nonempty. The value of |prev_graf| in the -enclosing semantic level tells where the paragraph should begin in the -sequence of line numbers, in case hanging indentation or \.{\\parshape} -are in use; |prev_graf| is zero unless this paragraph is being continued -after a displayed formula. Other implicit parameters, such as the -|par_shape_ptr| and various penalties to use for hyphenation, etc., appear -in |eqtb|. - -After |line_break| has acted, it will have updated the current vlist and the -value of |prev_graf|. Furthermore, the global variable |just_box| will -point to the final box created by |line_break|, so that the width of this -line can be ascertained when it is necessary to decide whether to use -|above_display_skip| or |above_display_short_skip| before a displayed formula. - -@c -halfword just_box; /* the |hlist_node| for the last line of the new paragraph */ - -@ In it's complete form, |line_break| is a rather lengthy -procedure---sort of a small world unto itself---we must build it up -little by little. Below you see only the general outline. - -The main task performed here is to move the list from |head| to -|temp_head| and go into the enclosing semantic level. We also append -the \.{\\parfillskip} glue to the end of the paragraph, removing a -space (or other glue node) if it was there, since spaces usually -precede blank lines and instances of `\.{\$\$}'. The |par_fill_skip| -is preceded by an infinite penalty, so it will never be considered as -a potential breakpoint. - -That code assumes that a |glue_node| and a |penalty_node| occupy the -same number of |mem|~words. -@^data structure assumptions@> - -Most other processing is delegated to external functions. - -@c -void line_break(boolean d, int line_break_context) -{ - int paragraph_dir = 0; /* main direction of paragraph */ - halfword final_par_glue; - halfword start_of_par; - int callback_id; - pack_begin_line = cur_list.ml_field; /* this is for over/underfull box messages */ - alink(temp_head) = null; /* hh-ls */ - vlink(temp_head) = vlink(cur_list.head_field); - new_hyphenation(temp_head, cur_list.tail_field); - cur_list.tail_field = new_ligkern(temp_head, cur_list.tail_field); - if (is_char_node(cur_list.tail_field)) { - tail_append(new_penalty(inf_penalty,line_penalty)); - } else if (type(cur_list.tail_field) != glue_node) { - tail_append(new_penalty(inf_penalty,line_penalty)); - } else { - halfword t = alink(cur_list.tail_field); - flush_node(cur_list.tail_field); - cur_list.tail_field = t; - tail_append(new_penalty(inf_penalty,line_penalty)); - } - final_par_glue = new_param_glue(par_fill_skip_code); - couple_nodes(cur_list.tail_field, final_par_glue); - cur_list.tail_field = vlink(cur_list.tail_field); - lua_node_filter(pre_linebreak_filter_callback, - line_break_context, temp_head, - addressof(cur_list.tail_field)); - last_line_fill = cur_list.tail_field; - pop_nest(); - start_of_par = cur_list.tail_field; - callback_id = callback_defined(linebreak_filter_callback); - if (callback_id > 0) { - callback_id = lua_linebreak_callback(d, temp_head, addressof(cur_list.tail_field)); - if (callback_id > 0) { - /* find the correct value for the |just_box| */ - halfword box_search = cur_list.tail_field; - just_box = null; - if (box_search != null) { - do { - if (type(box_search) == hlist_node) { - just_box = box_search; - } - box_search = vlink(box_search); - } while (box_search != null); - } - if (just_box == null) { - help3 - ("A linebreaking routine should return a non-empty list of nodes", - "and at least one of those has to be a \\hbox.", - "Sorry, I cannot recover from this."); - print_err("Invalid linebreak_filter"); - succumb(); - } - } else { - if (tracing_paragraphs_par > 0) { - begin_diagnostic(); - print_int(line); - end_diagnostic(true); - } - } - } - if (callback_id == 0) { - if ((!is_char_node(vlink(temp_head))) && ((type(vlink(temp_head)) == local_par_node))) { - paragraph_dir = local_par_dir(vlink(temp_head)); - } else { - confusion("weird par dir"); /* assert(0); */ /* |paragraph_dir = 0|; */ - } - ext_do_line_break(paragraph_dir, - pretolerance_par, - tracing_paragraphs_par, - tolerance_par, - emergency_stretch_par, - looseness_par, - adjust_spacing_par, - par_shape_par_ptr, - adj_demerits_par, - protrude_chars_par, - line_penalty_par, - last_line_fit_par, - double_hyphen_demerits_par, - final_hyphen_demerits_par, - hang_indent_par, - hsize_par, - hang_after_par, - left_skip_par, - right_skip_par, - inter_line_penalties_par_ptr, - inter_line_penalty_par, - club_penalty_par, - club_penalties_par_ptr, - (d ? display_widow_penalties_par_ptr : widow_penalties_par_ptr), - (d ? display_widow_penalty_par : widow_penalty_par), - broken_penalty_par, - final_par_glue); - } - lua_node_filter(post_linebreak_filter_callback, - line_break_context, start_of_par, - addressof(cur_list.tail_field)); - pack_begin_line = 0; -} - -@ Glue nodes in a horizontal list that is being paragraphed are not supposed to - include ``infinite'' shrinkability; that is why the algorithm maintains - four registers for stretching but only one for shrinking. If the user tries to - introduce infinite shrinkability, the shrinkability will be reset to finite - and an error message will be issued. A boolean variable |no_shrink_error_yet| - prevents this error message from appearing more than once per paragraph. - -@c -#define check_shrinkage(a) \ - if ((shrink_order((a))!=normal)&&(shrink((a))!=0)) \ - a=finite_shrink((a)) - -static boolean no_shrink_error_yet; /*have we complained about infinite shrinkage? */ - -static halfword finite_shrink(halfword p) -{ /* recovers from infinite shrinkage */ - const char *hlp[] = { - "The paragraph just ended includes some glue that has", - "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.", - "Such glue doesn't belong there---it allows a paragraph", - "of any length to fit on one line. But it's safe to proceed,", - "since the offensive shrinkability has been made finite.", - NULL - }; - if (no_shrink_error_yet) { - no_shrink_error_yet = false; - tex_error("Infinite glue shrinkage found in a paragraph", hlp); - } - shrink_order(p) = normal; - return p; -} - -@ A pointer variable |cur_p| runs through the given horizontal list as we look - for breakpoints. This variable is global, since it is used both by |line_break| - and by its subprocedure |try_break|. - - Another global variable called |threshold| is used to determine the feasibility - of individual lines: breakpoints are feasible if there is a way to reach - them without creating lines whose badness exceeds |threshold|. (The - badness is compared to |threshold| before penalties are added, so that - penalty values do not affect the feasibility of breakpoints, except that - no break is allowed when the penalty is 10000 or more.) If |threshold| - is 10000 or more, all legal breaks are considered feasible, since the - |badness| function specified above never returns a value greater than~10000. - - Up to three passes might be made through the paragraph in an attempt to find at - least one set of feasible breakpoints. On the first pass, we have - |threshold=pretolerance| and |second_pass=final_pass=false|. - If this pass fails to find a - feasible solution, |threshold| is set to |tolerance|, |second_pass| is set - |true|, and an attempt is made to hyphenate as many words as possible. - If that fails too, we add |emergency_stretch| to the background - stretchability and set |final_pass=true|. - -@c -static boolean second_pass; /* is this our second attempt to break this paragraph? */ -static boolean final_pass; /*is this our final attempt to break this paragraph? */ -static int threshold; /* maximum badness on feasible lines */ - -/* maximum fill level for |hlist_stack|*/ -#define max_hlist_stack 512 /* maybe good if larger than |2 * - max_quarterword|, so that box nesting - level would overflow first */ - -/* stack for |find_protchar_left()| and |find_protchar_right()| */ -static halfword hlist_stack[max_hlist_stack]; - -/* fill level for |hlist_stack| */ -static short hlist_stack_level = 0; - -@ @c -static void push_node(halfword p) -{ - if (hlist_stack_level >= max_hlist_stack) - normal_error("push_node","stack overflow"); - hlist_stack[hlist_stack_level++] = p; -} - -static halfword pop_node(void) -{ - if (hlist_stack_level <= 0) /* would point to some bug */ - normal_error("pop_node","stack underflow (internal error)"); - return hlist_stack[--hlist_stack_level]; -} - -@ @c -static int max_stretch_ratio = 0; /*maximal stretch ratio of expanded fonts */ -static int max_shrink_ratio = 0; /*maximal shrink ratio of expanded fonts */ -static int cur_font_step = 0; /*the current step of expanded fonts */ - -static boolean check_expand_pars(internal_font_number f) -{ - int m; - - if ((font_step(f) == 0) - || ((font_max_stretch(f) == 0) && (font_max_shrink(f) == 0))) - return false; - if (cur_font_step < 0) - cur_font_step = font_step(f); - else if (cur_font_step != font_step(f)) - normal_error("font expansion","using fonts with different step of expansion in one paragraph is not allowed"); - m = font_max_stretch(f); - if (m != 0) { - if (max_stretch_ratio < 0) - max_stretch_ratio = m; - else if (max_stretch_ratio != m) - normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed"); - } - m = font_max_shrink(f); - if (m != 0) { - if (max_shrink_ratio < 0) - max_shrink_ratio = -m; - else if (max_shrink_ratio != -m) - normal_error("font expansion","using fonts with different limit of expansion in one paragraph is not allowed"); - } - return true; -} - -@ searches left to right from list head |l|, returns 1st non-skipable item - -@c -/*public*/ halfword find_protchar_left(halfword l, boolean d) -{ - halfword t; - boolean run; - boolean done = false ; - while ((vlink(l) != null) && (type(l) == hlist_node) && zero_dimensions(l) && (list_ptr(l) == null)) { - /*for paragraph start with \.{\\parindent} = 0pt or any empty hbox */ - l = vlink(l); - done = true ; - } - if ((!done) && (type(l) == local_par_node)) { - l = vlink(l); - done = true ; - } - if ((!done) && d) { - while ((vlink(l) != null) && (!(is_char_node(l) || non_discardable(l)))) { - /* std.\ discardables at line break, \TeX book, p 95 */ - l = vlink(l); - } - } - if (type(l) != glyph_node) { - hlist_stack_level = 0; - run = true; - do { - t = l; - while (run && (type(l) == hlist_node) && (list_ptr(l) != null)) { - push_node(l); - l = list_ptr(l); - } - while (run && cp_skipable(l)) { - while ((vlink(l) == null) && (hlist_stack_level > 0)) { - l = pop_node(); /* don't visit this node again */ - run = false; - } - if ((vlink(l) != null) && (type(l) == boundary_node) && (subtype(l) == protrusion_boundary) && - ((boundary_value(l) == 1) || (boundary_value(l) == 3))) { - /* skip next node */ - l = vlink(l); - } - if (vlink(l) != null) { - l = vlink(l); - } else if (hlist_stack_level == 0) { - run = false; - } - } - } while (t != l); - } - return l; -} - -@ searches right to left from list tail |r| to head |l|, returns 1st non-skipable item - -@c -/*public*/ halfword find_protchar_right(halfword l, halfword r) -{ - halfword t; - boolean run = true; - if (r == null) - return null; - hlist_stack_level = 0; - do { - t = r; - while (run && (type(r) == hlist_node) && (list_ptr(r) != null)) { - push_node(l); - push_node(r); - l = list_ptr(r); - r = l; - while (vlink(r) != null) { - halfword s = r; - r = vlink(r); - alink(r) = s; - } - } - while (run && cp_skipable(r)) { - while ((r == l) && (hlist_stack_level > 0)) { - r = pop_node(); /* don't visit this node again */ - l = pop_node(); - } - if ((r != l) && (r != null)) { - if ((alink(r) != null) && (type(r) == boundary_node) && (subtype(r) == protrusion_boundary) && - ((boundary_value(r) == 2) || (boundary_value(r) == 3))) { - /* skip next node */ - r = alink(r); - } - if (alink(r) != null) { - r = alink(r); - } else { /* this is the input: \.{\\leavevmode\\penalty-10000\\penalty-10000} (bug \#268) */ - run = false; - } - } else if ((r == l) && (hlist_stack_level == 0)) - run = false; - } - } while (t != r); - return r; -} - -@ @c -#define left_pw(a) char_pw((a), left_side) -#define right_pw(a) char_pw((a), right_side) - -@ When looking for optimal line breaks, \TeX\ creates a ``break node'' for - each break that is {\sl feasible}, in the sense that there is a way to end - a line at the given place without requiring any line to stretch more than - a given tolerance. A break node is characterized by three things: the position - of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|, - or |disc_node|); the ordinal number of the line that will follow this - breakpoint; and the fitness classification of the line that has just - ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|. - -@c -typedef enum { - very_loose_fit = 0, /* fitness classification for lines stretching more than - their stretchability */ - loose_fit, /* fitness classification for lines stretching 0.5 to 1.0 of their - stretchability */ - decent_fit, /* fitness classification for all other lines */ - tight_fit /* fitness classification for lines shrinking 0.5 to 1.0 of their - shrinkability */ -} fitness_value; - - -@ The algorithm essentially determines the best possible way to achieve - each feasible combination of position, line, and fitness. Thus, it answers - questions like, ``What is the best way to break the opening part of the - paragraph so that the fourth line is a tight line ending at such-and-such - a place?'' However, the fact that all lines are to be the same length - after a certain point makes it possible to regard all sufficiently large - line numbers as equivalent, when the looseness parameter is zero, and this - makes it possible for the algorithm to save space and time. - - An ``active node'' and a ``passive node'' are created in |mem| for each - feasible breakpoint that needs to be considered. Active nodes are three - words long and passive nodes are two words long. We need active nodes only - for breakpoints near the place in the paragraph that is currently being - examined, so they are recycled within a comparatively short time after - they are created. - -@ An active node for a given breakpoint contains six fields: - -|vlink| points to the next node in the list of active nodes; the -last active node has |vlink=active|. - -|break_node| points to the passive node associated with this -breakpoint. - -|line_number| is the number of the line that follows this -breakpoint. - -|fitness| is the fitness classification of the line ending at this -breakpoint. - -|type| is either |hyphenated_node| or |unhyphenated_node|, depending on -whether this breakpoint is a |disc_node|. - -|total_demerits| is the minimum possible sum of demerits over all -lines leading from the beginning of the paragraph to this breakpoint. - -The value of |vlink(active)| points to the first active node on a vlinked list -of all currently active nodes. This list is in order by |line_number|, -except that nodes with |line_number>easy_line| may be in any order relative -to each other. - -@c -void initialize_active(void) -{ - type(active) = hyphenated_node; - line_number(active) = max_halfword; - subtype(active) = 0; /* the |subtype| is never examined */ -} - -@ The passive node for a given breakpoint contains EIGHT fields: - -|vlink| points to the passive node created just before this one, -if any, otherwise it is |null|. - -|cur_break| points to the position of this breakpoint in the -horizontal list for the paragraph being broken. - -|prev_break| points to the passive node that should precede this -one in an optimal path to this breakpoint. - -|serial| is equal to |n| if this passive node is the |n|th -one created during the current pass. (This field is used only when -printing out detailed statistics about the line-breaking calculations.) - -|passive_pen_inter| holds the current \.{\\localinterlinepenalty} - -|passive_pen_broken| holds the current \.{\\localbrokenpenalty} - -There is a global variable called |passive| that points to the most -recently created passive node. Another global variable, |printed_node|, -is used to help print out the paragraph when detailed information about -the line-breaking computation is being displayed. - -@c -static halfword passive; /* most recent node on passive list */ -static halfword printed_node; /*most recent node that has been printed */ -static halfword pass_number; /*the number of passive nodes allocated on this pass */ - -@ The active list also contains ``delta'' nodes that help the algorithm -compute the badness of individual lines. Such nodes appear only between two -active nodes, and they have |type=delta_node|. If |p| and |r| are active nodes -and if |q| is a delta node between them, so that |vlink(p)=q| and |vlink(q)=r|, -then |q| tells the space difference between lines in the horizontal list that -start after breakpoint |p| and lines that start after breakpoint |r|. In -other words, if we know the length of the line that starts after |p| and -ends at our current position, then the corresponding length of the line that -starts after |r| is obtained by adding the amounts in node~|q|. A delta node -contains seven scaled numbers, since it must record the net change in glue -stretchability with respect to all orders of infinity. The natural width -difference appears in |mem[q+1].sc|; the stretch differences in units of -pt, sfi, fil, fill, and filll appear in |mem[q+2..q+6].sc|; and the shrink -difference appears in |mem[q+7].sc|. The |subtype| field of a delta node -is not used. - -Actually, we have two more fields that are used by |pdftex|. - -As the algorithm runs, it maintains a set of seven delta-like registers -for the length of the line following the first active breakpoint to the -current position in the given hlist. When it makes a pass through the -active list, it also maintains a similar set of seven registers for the -length following the active breakpoint of current interest. A third set -holds the length of an empty line (namely, the sum of \.{\\leftskip} and -\.{\\rightskip}); and a fourth set is used to create new delta nodes. - -When we pass a delta node we want to do operations like -$$\hbox{\ignorespaces|for -k:=1 to 7 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we -want to do this without the overhead of |for| loops. The |do_all_six| -macro makes such six-tuples convenient. - -@c -static scaled active_width[10] = { 0 }; /*distance from first active node to~|cur_p| */ -static scaled background[10] = { 0 }; /*length of an ``empty'' line */ -static scaled break_width[10] = { 0 }; /*length being computed after current break */ - -static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_break| */ - -@ Let's state the principles of the delta nodes more precisely and concisely, - so that the following programs will be less obscure. For each legal - breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and - $\beta(p)$ such that the length of material in a line from breakpoint~|p| - to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$. - Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from - the beginning of the paragraph to a point ``after'' a break at |p| and to a - point ``before'' a break at |q|; and $\gamma$ is the width of an empty line, - namely the length contributed by \.{\\leftskip} and \.{\\rightskip}. - - Suppose, for example, that the paragraph consists entirely of alternating - boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and - let the skips have widths $y_1\ldots y_n$, so that the paragraph can be - represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint - at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)= - x_1+y_1+\cdots+x_i$. To check this, note that the length of material from - $p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5) - -\alpha(p_2)$. - - The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and - shrinkability as well as a natural width. If we were to compute $\alpha(p)$ - and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and - the multiprecise numbers would have to be kept in the active nodes. - \TeX\ avoids this problem by working entirely with relative differences - or ``deltas.'' Suppose, for example, that the active list contains - $a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints - and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$ - and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is - currently positioned at some other breakpoint |p|, the |active_width| array - contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through - the list of active nodes and considering a tentative line that runs from - $a_2$ to~|p|, say, the |cur_active_width| array will contain the value - $\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$, - we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this - is just $\delta_2$, which appears in the active list between $a_2$ and - $a_3$. The |background| array contains $\gamma$. The |break_width| array - will be used to calculate values of new delta nodes when the active - list is being updated. - -@ The heart of the line-breaking procedure is `|try_break|', a subroutine - that tests if the current breakpoint |cur_p| is feasible, by running - through the active list to see what lines of text can be made from active - nodes to~|cur_p|. If feasible breaks are possible, new break nodes are - created. If |cur_p| is too far from an active node, that node is - deactivated. - - The parameter |pi| to |try_break| is the penalty associated - with a break at |cur_p|; we have |pi=eject_penalty| if the break is forced, - and |pi=inf_penalty| if the break is illegal. - - The other parameter, |break_type|, is set to |hyphenated_node| or |unhyphenated_node|, - depending on whether or not the current break is at a |disc_node|. The - end of a paragraph is also regarded as `|hyphenated_node|'; this case is - distinguishable by the condition |cur_p=null|. - -@c -static int internal_pen_inter; /* running \.{\\localinterlinepenalty} */ -static int internal_pen_broken; /* running \.{\\localbrokenpenalty} */ -static halfword internal_left_box; /* running \.{\\localleftbox} */ -static int internal_left_box_width; /* running \.{\\localleftbox} width */ -static halfword init_internal_left_box; /* running \.{\\localleftbox} */ -static int init_internal_left_box_width; /* running \.{\\localleftbox} width */ -static halfword internal_right_box; /* running \.{\\localrightbox} */ -static int internal_right_box_width; /* running \.{\\localrightbox} width */ - -static scaled disc_width[10] = { 0 }; /* the length of discretionary material preceding a break */ - -@ As we consider various ways to end a line at |cur_p|, in a given line number - class, we keep track of the best total demerits known, in an array with - one entry for each of the fitness classifications. For example, - |minimal_demerits[tight_fit]| contains the fewest total demerits of feasible - line breaks ending at |cur_p| with a |tight_fit| line; |best_place[tight_fit]| - points to the passive node for the break before~|cur_p| that achieves such - an optimum; and |best_pl_line[tight_fit]| is the |line_number| field in the - active node corresponding to |best_place[tight_fit]|. When no feasible break - sequence is known, the |minimal_demerits| entries will be equal to - |awful_bad|, which is $2^{30}-1$. Another variable, |minimum_demerits|, - keeps track of the smallest value in the |minimal_demerits| array. - -@c -static int minimal_demerits[4]; /* best total demerits known for current - line class and position, given the fitness */ -static int minimum_demerits; /* best total demerits known for current line class - and position */ -static halfword best_place[4]; /* how to achieve |minimal_demerits| */ -static halfword best_pl_line[4]; /*corresponding line number */ - -@ The length of lines depends on whether the user has specified -\.{\\parshape} or \.{\\hangindent}. If |par_shape_ptr| is not null, it -points to a $(2n+1)$-word record in |mem|, where the |vinfo| in the first -word contains the value of |n|, and the other $2n$ words contain the left -margins and line lengths for the first |n| lines of the paragraph; the -specifications for line |n| apply to all subsequent lines. If -|par_shape_ptr=null|, the shape of the paragraph depends on the value of -|n=hang_after|; if |n>=0|, hanging indentation takes place on lines |n+1|, -|n+2|, \dots, otherwise it takes place on lines 1, \dots, $\vert -n\vert$. When hanging indentation is active, the left margin is -|hang_indent|, if |hang_indent>=0|, else it is 0; the line length is -$|hsize|-\vert|hang_indent|\vert$. The normal setting is -|par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|. -Note that if |hang_indent=0|, the value of |hang_after| is irrelevant. -@^length of lines@> @^hanging indentation@> - -@c -static halfword easy_line; /*line numbers |>easy_line| are equivalent in break nodes */ -static halfword last_special_line; /*line numbers |>last_special_line| all have the same width */ -static scaled first_width; /*the width of all lines |<=last_special_line|, if - no \.{\\parshape} has been specified */ -static scaled second_width; /*the width of all lines |>last_special_line| */ -static scaled first_indent; /*left margin to go with |first_width| */ -static scaled second_indent; /*left margin to go with |second_width| */ - -static halfword best_bet; /*use this passive node and its predecessors */ -static int fewest_demerits; /*the demerits associated with |best_bet| */ -static halfword best_line; /*line number following the last line of the new paragraph */ -static int actual_looseness; /*the difference between |line_number(best_bet)| - and the optimum |best_line| */ -static int line_diff; /*the difference between the current line number and - the optimum |best_line| */ - -@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, - |rule_node|, |ins_node|, |mark_node|, |adjust_node|, - |disc_node|, |whatsit_node|, and |math_node| are at the low end of the - type codes, by permitting a break at glue in a list if and only if the - |type| of the previous node is less than |math_node|. Furthermore, a - node is discarded after a break if its type is |math_node| or~more. - -@c -#define do_all_six(a) a(1);a(2);a(3);a(4);a(5);a(6);a(7) -#define do_seven_eight(a) if (adjust_spacing > 1) { a(8);a(9); } -#define do_all_eight(a) do_all_six(a); do_seven_eight(a) -#define do_one_seven_eight(a) a(1); do_seven_eight(a) - -#define store_background(a) {active_width[a]=background[a];} - -#define kern_break() { \ - if ((!is_char_node(vlink(cur_p))) && auto_breaking) \ - if (type(vlink(cur_p))==glue_node) \ - ext_try_break(0, \ - unhyphenated_node, \ - line_break_dir, \ - adjust_spacing, \ - par_shape_ptr, \ - adj_demerits, \ - tracing_paragraphs, \ - protrude_chars, \ - line_penalty, \ - last_line_fit, \ - double_hyphen_demerits, \ - final_hyphen_demerits, \ - first_p, \ - cur_p); \ - if (type(cur_p)!=math_node) \ - active_width[1] += width(cur_p); \ - else \ - active_width[1] += surround(cur_p); \ -} - -#define clean_up_the_memory() { \ - q=vlink(active); \ - while (q!=active) { \ - cur_p = vlink(q); \ - if (type(q)==delta_node) \ - flush_node(q); \ - else \ - flush_node(q); \ - q = cur_p; \ - } \ - q = passive; \ - while (q!=null) { \ - cur_p = vlink(q); \ - flush_node(q); \ - q = cur_p; \ - } \ -} - -static boolean do_last_line_fit; /* special algorithm for last line of paragraph? */ -static scaled fill_width[4]; /* infinite stretch components of |par_fill_skip| */ -static scaled best_pl_short[4]; /* |shortfall| corresponding to |minimal_demerits| */ -static scaled best_pl_glue[4]; /*corresponding glue stretch or shrink */ - -#define reset_disc_width(a) disc_width[(a)] = 0 - -#define add_disc_width_to_break_width(a) break_width[(a)] += disc_width[(a)] -#define sub_disc_width_from_active_width(a) active_width[(a)] -= disc_width[(a)] - -#define add_char_shrink(a,b) a += char_shrink((b)) -#define add_char_stretch(a,b) a += char_stretch((b)) -#define sub_char_shrink(a,b) a -= char_shrink((b)) -#define sub_char_stretch(a,b) a -= char_stretch((b)) - -#define add_kern_shrink(a,b) a += kern_shrink((b)) -#define add_kern_stretch(a,b) a += kern_stretch((b)) -#define sub_kern_shrink(a,b) a -= kern_shrink((b)) -#define sub_kern_stretch(a,b) a -= kern_stretch((b)) - -@ This function is used to add the width of a list of nodes -(from a discretionary) to one of the width arrays. - -Replacement texts and discretionary texts are supposed to contain -only character nodes, kern nodes, and box or rule nodes. - -@c -#define bad_node_in_disc_error(p) { \ - if (type(p) == whatsit_node) { \ - formatted_error("linebreak","invalid node with type %s and subtype %i found in discretionary",node_data[type(p)].name,subtype(p)); \ - } else { \ - formatted_error("linebreak","invalid node with type %s found in discretionary",node_data[type(p)].name); \ - } \ -} - -static void add_to_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths) -{ - while (s != null) { - if (is_char_node(s)) { - widths[1] += pack_width(line_break_dir, dir_TRT, s, true); - if ((adjust_spacing > 1) && check_expand_pars(font(s))) { - set_prev_char_p(s); - add_char_stretch(widths[8], s); - add_char_shrink(widths[9], s); - }; - } else { - switch (type(s)) { - case hlist_node: - case vlist_node: - widths[1] += pack_width(line_break_dir, box_dir(s), s, false); - break; - case kern_node: - if ((adjust_spacing == 2) && (subtype(s) == normal)) { - add_kern_stretch(widths[8], s); - add_kern_shrink(widths[9], s); - } - /* fall through */ - case rule_node: - widths[1] += width(s); - break; - case disc_node: /* TH temp */ - break; - default: - bad_node_in_disc_error(s); - break; - } - } - s = vlink(s); - } -} - -@ This function is used to substract the width of a list of nodes -(from a discretionary) from one of the width arrays. -It is used only once, but deserves it own function because of orthogonality -with the |add_to_widths| function. - -@c -static void sub_from_widths(halfword s, int line_break_dir, int adjust_spacing, scaled * widths) -{ - while (s != null) { - /* Subtract the width of node |s| from |break_width|; */ - if (is_char_node(s)) { - widths[1] -= pack_width(line_break_dir, dir_TRT, s, true); - if ((adjust_spacing > 1) && check_expand_pars(font(s))) { - set_prev_char_p(s); - sub_char_stretch(widths[8], s); - sub_char_shrink(widths[9], s); - } - } else { - switch (type(s)) { - case hlist_node: - case vlist_node: - widths[1] -= pack_width(line_break_dir, box_dir(s), s, false); - break; - case kern_node: - if ((adjust_spacing == 2) && (subtype(s) == normal)) { - sub_kern_stretch(widths[8], s); - sub_kern_shrink(widths[9], s); - } - /* fall through */ - case rule_node: - widths[1] -= width(s); - break; - case disc_node: /* TH temp */ - break; - default: - bad_node_in_disc_error(s); - break; - } - } - s = vlink(s); - } -} - -@ When we insert a new active node for a break at |cur_p|, suppose this - new node is to be placed just before active node |a|; then we essentially - want to insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where - $\delta=\alpha(a)-\alpha(|cur_p|)$ and $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$ - in the notation explained above. The |cur_active_width| array now holds - $\gamma+\beta(|cur_p|)-\alpha(a)$; so $\delta$ can be obtained by - subtracting |cur_active_width| from the quantity $\gamma+\beta(|cur_p|)- - \alpha(|cur_p|)$. The latter quantity can be regarded as the length of a - line ``from |cur_p| to |cur_p|''; we call it the |break_width| at |cur_p|. - - The |break_width| is usually negative, since it consists of the background - (which is normally zero) minus the width of nodes following~|cur_p| that are - eliminated after a break. If, for example, node |cur_p| is a glue node, the - width of this glue is subtracted from the background; and we also look - ahead to eliminate all subsequent glue and penalty and kern and math - nodes, subtracting their widths as well. - - Kern nodes do not disappear at a line break unless they are |explicit|. - -@c -static void compute_break_width(int break_type, int line_break_dir, int adjust_spacing, halfword p) -{ - halfword s = p; /* glue and other 'whitespace' to be skipped after a break; - used if unhyphenated, or |post_break==empty| */ - if (break_type > unhyphenated_node && p != null) { - /*Compute the discretionary |break_width| values; */ - /* When |p| is a discretionary break, the length of a line - ``from |p| to |p|'' has to be defined properly so - that the other calculations work out. Suppose that the - pre-break text at |p| has length $l_0$, the post-break - text has length $l_1$, and the replacement text has length - |l|. Suppose also that |q| is the node following the - replacement text. Then length of a line from |p| to |q| - will be computed as $\gamma+\beta(q)-\alpha(|p|)$, where - $\beta(q)=\beta(|p|)-l_0+l$. The actual length will be - the background plus $l_1$, so the length from |p| to - |p| should be $\gamma+l_0+l_1-l$. If the post-break text - of the discretionary is empty, a break may also discard~|q|; - in that unusual case we subtract the length of~|q| and any - other nodes that will be discarded after the discretionary - break. - - TH: I don't quite understand the above remarks. - - The value of $l_0$ need not be computed, since |line_break| - will put it into the global variable |disc_width| before - calling |try_break|. - */ - /* In case of nested discretionaries, we always follow the no-break - path, as we are talking about the breaking on {\it this} position. - */ - - sub_from_widths(vlink_no_break(p), line_break_dir, adjust_spacing, break_width); - add_to_widths(vlink_post_break(p), line_break_dir, adjust_spacing, break_width); - do_one_seven_eight(add_disc_width_to_break_width); - if (vlink_post_break(p) == null) { - s = vlink(p); /* no |post_break|: 'skip' any 'whitespace' following */ - } else { - s = null; - } - } - while (s != null) { - switch (type(s)) { - case math_node: - /* begin mathskip code */ - if (glue_is_zero(s)) { - break_width[1] -= surround(s); - break; - } else { - /* fall through */ - } - /* end mathskip code */ - case glue_node: - /*Subtract glue from |break_width|; */ - break_width[1] -= width(s); - break_width[2 + stretch_order(s)] -= stretch(s); - break_width[7] -= shrink(s); - break; - case penalty_node: - break; - case kern_node: - if (subtype(s) != explicit_kern && subtype(s) != italic_kern) - return; - else - break_width[1] -= width(s); - break; - default: - return; - }; - s = vlink(s); - } -} - -@ @c -static void print_break_node(halfword q, fitness_value fit_class, - quarterword break_type, halfword cur_p) -{ - /* Print a symbolic description of the new break node */ - tprint_nl("@@@@"); - print_int(serial(passive)); - tprint(": line "); - print_int(line_number(q) - 1); - print_char('.'); - print_int(fit_class); - if (break_type == hyphenated_node) - print_char('-'); - tprint(" t="); - print_int(total_demerits(q)); - if (do_last_line_fit) { - /*Print additional data in the new active node; */ - tprint(" s="); - print_scaled(active_short(q)); - if (cur_p == null) - tprint(" a="); - else - tprint(" g="); - print_scaled(active_glue(q)); - } - tprint(" -> @@"); - if (prev_break(passive) == null) - print_char('0'); - else - print_int(serial(prev_break(passive))); -} - -@ @c -static void print_feasible_break(halfword cur_p, pointer r, halfword b, int pi, - int d, boolean artificial_demerits) -{ - /* Print a symbolic description of this feasible break; */ - if (printed_node != cur_p) { - /* Print the list between |printed_node| and |cur_p|, then - set |printed_node:=cur_p|; */ - tprint_nl(""); - if (cur_p == null) { - short_display(vlink(printed_node)); - } else { - halfword save_link = vlink(cur_p); - vlink(cur_p) = null; - tprint_nl(""); - short_display(vlink(printed_node)); - vlink(cur_p) = save_link; - } - printed_node = cur_p; - } - tprint_nl("@@"); - if (cur_p == null) { - tprint_esc("par"); - } else if (type(cur_p) != glue_node) { - if (type(cur_p) == penalty_node) - tprint_esc("penalty"); - else if (type(cur_p) == disc_node) - tprint_esc("discretionary"); - else if (type(cur_p) == kern_node) - tprint_esc("kern"); - else - tprint_esc("math"); - } - tprint(" via @@"); - if (break_node(r) == null) - print_char('0'); - else - print_int(serial(break_node(r))); - tprint(" b="); - if (b > inf_bad) - print_char('*'); - else - print_int(b); - tprint(" p="); - print_int(pi); - tprint(" d="); - if (artificial_demerits) - print_char('*'); - else - print_int(d); -} - -@ @c -#define add_disc_width_to_active_width(a) active_width[a] += disc_width[a] -#define update_width(a) cur_active_width[a] += varmem[(r+(a))].cint - -#define set_break_width_to_background(a) break_width[a]=background[(a)] - -#define convert_to_break_width(a) \ - varmem[(prev_r+(a))].cint = varmem[(prev_r+(a))].cint-cur_active_width[(a)]+break_width[(a)] - -#define store_break_width(a) active_width[(a)]=break_width[(a)] - -#define new_delta_to_break_width(a) \ - varmem[(q+(a))].cint=break_width[(a)]-cur_active_width[(a)] - -#define new_delta_from_break_width(a) \ - varmem[(q+(a))].cint=cur_active_width[(a)]-break_width[(a)] - -#define copy_to_cur_active(a) cur_active_width[(a)]=active_width[(a)] - -#define combine_two_deltas(a) varmem[(prev_r+(a))].cint += varmem[(r+(a))].cint -#define downdate_width(a) cur_active_width[(a)] -= varmem[(prev_r+(a))].cint -#define update_active(a) active_width[(a)]+=varmem[(r+(a))].cint - -#define total_font_stretch cur_active_width[8] -#define total_font_shrink cur_active_width[9] - -#define cal_margin_kern_var(a) { \ - character(cp) = character((a)); \ - font(cp) = font((a)); \ - do_subst_font(cp, 1000); \ - if (font(cp) != font((a))) \ - margin_kern_stretch += (left_pw((a)) - left_pw(cp)); \ - font(cp) = font((a)); \ - do_subst_font(cp, -1000); \ - if (font(cp) != font((a))) \ - margin_kern_shrink += (left_pw(cp) - left_pw((a))); \ -} - -static void ext_try_break(int pi, - quarterword break_type, - int line_break_dir, - int adjust_spacing, - int par_shape_ptr, - int adj_demerits, - int tracing_paragraphs, - int protrude_chars, - int line_penalty, - int last_line_fit, - int double_hyphen_demerits, - int final_hyphen_demerits, halfword first_p, halfword cur_p) -{ - /* labels: |CONTINUE,DEACTIVATE,FOUND,NOT_FOUND|; */ - pointer r; /* runs through the active list */ - scaled margin_kern_stretch; - scaled margin_kern_shrink; - halfword lp, rp, cp; - halfword prev_r = active; /* stays a step behind |r| */ - halfword prev_prev_r = null; /*a step behind |prev_r|, if |type(prev_r)=delta_node| */ - halfword old_l = 0; /* maximum line number in current equivalence class of lines */ - boolean no_break_yet = true; /* have we found a feasible break at |cur_p|? */ - halfword q; /*points to a new node being created */ - halfword l; /*line number of current active node */ - boolean node_r_stays_active; /*should node |r| remain in the active list? */ - scaled line_width = 0; /*the current line will be justified to this width */ - fitness_value fit_class; /*possible fitness class of test line */ - halfword b; /*badness of test line */ - int d; /*demerits of test line */ - boolean artificial_demerits; /*has |d| been forced to zero? */ - - scaled shortfall; /*used in badness calculations */ - scaled g = 0; /*glue stretch or shrink of test line, adjustment for last line */ - scaled cur_active_width[10] = { 0 }; /*distance from current active node */ - - /*Make sure that |pi| is in the proper range; */ - if (pi >= inf_penalty) { - return; /* this breakpoint is inhibited by infinite penalty */ - } else if (pi <= -inf_penalty) { - pi = eject_penalty; /*this breakpoint will be forced */ - } - - do_all_eight(copy_to_cur_active); - - while (1) { - r = vlink(prev_r); - /* If node |r| is of type |delta_node|, update |cur_active_width|, - set |prev_r| and |prev_prev_r|, then |goto continue|; */ - /* The following code uses the fact that |type(active)<>delta_node| */ - if (type(r) == delta_node) { - do_all_eight(update_width); /* IMPLICIT ,r */ - prev_prev_r = prev_r; - prev_r = r; - continue; - } - /* If a line number class has ended, create new active nodes for - the best feasible breaks in that class; then |return| - if |r=active|, otherwise compute the new |line_width|; */ - /* The first part of the following code is part of \TeX's inner loop, so - we don't want to waste any time. The current active node, namely node |r|, - contains the line number that will be considered next. At the end of the - list we have arranged the data structure so that |r=active| and - |line_number(active)>old_l|. - */ - l = line_number(r); - if (l > old_l) { /* now we are no longer in the inner loop */ - if ((minimum_demerits < awful_bad) - && ((old_l != easy_line) || (r == active))) { - /*Create new active nodes for the best feasible breaks just found */ - /* It is not necessary to create new active nodes having |minimal_demerits| - greater than - |minimum_demerits+abs(adj_demerits)|, since such active nodes will never - be chosen in the final paragraph breaks. This observation allows us to - omit a substantial number of feasible breakpoints from further consideration. - */ - if (no_break_yet) { - no_break_yet = false; - do_all_eight(set_break_width_to_background); - compute_break_width(break_type, line_break_dir, adjust_spacing, cur_p); - } - /* Insert a delta node to prepare for breaks at |cur_p|; */ - /* We use the fact that |type(active)<>delta_node|. */ - if (type(prev_r) == delta_node) { /* modify an existing delta node */ - do_all_eight(convert_to_break_width); /* IMPLICIT |prev_r| */ - } else if (prev_r == active) { /* no delta node needed at the beginning */ - do_all_eight(store_break_width); - } else { - q = new_node(delta_node, 0); - vlink(q) = r; - do_all_eight(new_delta_to_break_width); /* IMPLICIT q */ - vlink(prev_r) = q; - prev_prev_r = prev_r; - prev_r = q; - } - - if (abs(adj_demerits) >= awful_bad - minimum_demerits) - minimum_demerits = awful_bad - 1; - else - minimum_demerits += abs(adj_demerits); - for (fit_class = very_loose_fit; fit_class <= tight_fit; - fit_class++) { - if (minimal_demerits[fit_class] <= minimum_demerits) { - /* Insert a new active node from |best_place[fit_class]| - to |cur_p|; */ - /* When we create an active node, we also create the corresponding - passive node. - */ - q = new_node(passive_node, 0); - vlink(q) = passive; - passive = q; - cur_break(q) = cur_p; - incr(pass_number); - serial(q) = pass_number; - prev_break(q) = best_place[fit_class]; - /*Here we keep track of the subparagraph penalties in the break nodes */ - passive_pen_inter(q) = internal_pen_inter; - passive_pen_broken(q) = internal_pen_broken; - passive_last_left_box(q) = internal_left_box; - passive_last_left_box_width(q) = - internal_left_box_width; - if (prev_break(q) != null) { - passive_left_box(q) = - passive_last_left_box(prev_break(q)); - passive_left_box_width(q) = - passive_last_left_box_width(prev_break(q)); - } else { - passive_left_box(q) = init_internal_left_box; - passive_left_box_width(q) = - init_internal_left_box_width; - } - passive_right_box(q) = internal_right_box; - passive_right_box_width(q) = internal_right_box_width; - q = new_node(break_type, fit_class); - break_node(q) = passive; - line_number(q) = best_pl_line[fit_class] + 1; - total_demerits(q) = minimal_demerits[fit_class]; - if (do_last_line_fit) { - /*Store additional data in the new active node */ - /* Here we save these data in the active node - representing a potential line break. */ - active_short(q) = best_pl_short[fit_class]; - active_glue(q) = best_pl_glue[fit_class]; - } - vlink(q) = r; - vlink(prev_r) = q; - prev_r = q; - if (tracing_paragraphs > 0) - print_break_node(q, fit_class, break_type, cur_p); - } - minimal_demerits[fit_class] = awful_bad; - } - minimum_demerits = awful_bad; - /* Insert a delta node to prepare for the next active node; */ - /* When the following code is performed, we will have just inserted at - least one active node before |r|, so |type(prev_r)<>delta_node|. - */ - if (r != active) { - q = new_node(delta_node, 0); - vlink(q) = r; - do_all_eight(new_delta_from_break_width); /* IMPLICIT q */ - vlink(prev_r) = q; - prev_prev_r = prev_r; - prev_r = q; - } - } - if (r == active) - return; - /*Compute the new line width; */ - /* When we come to the following code, we have just encountered - the first active node~|r| whose |line_number| field contains - |l|. Thus we want to compute the length of the $l\mskip1mu$th - line of the current paragraph. Furthermore, we want to set - |old_l| to the last number in the class of line numbers - equivalent to~|l|. - */ - if (l > easy_line) { - old_l = max_halfword - 1; - line_width = second_width; - } else { - old_l = l; - if (l > last_special_line) { - line_width = second_width; - } else if (par_shape_ptr == null) { - line_width = first_width; - } else { - line_width = varmem[(par_shape_ptr + 2 * l + 1)].cint; - } - } - } - /* /If a line number class has ended, create new active nodes for - the best feasible breaks in that class; then |return| - if |r=active|, otherwise compute the new |line_width|; */ - - /* Consider the demerits for a line from |r| to |cur_p|; - deactivate node |r| if it should no longer be active; - then |goto continue| if a line from |r| to |cur_p| is infeasible, - otherwise record a new feasible break; */ - artificial_demerits = false; - shortfall = line_width - cur_active_width[1]; - if (break_node(r) == null) - shortfall -= init_internal_left_box_width; - else - shortfall -= passive_last_left_box_width(break_node(r)); - shortfall -= internal_right_box_width; - if (protrude_chars > 1) { - halfword l1, o; - l1 = (break_node(r) == null) ? first_p : cur_break(break_node(r)); - if (cur_p == null) { - o = null; - } else { /* TODO |if (is_character_node(alink(cur_p)))| */ - o = alink(cur_p); - assert(vlink(o) == cur_p); - } - /* MAGIC: the disc could be a SELECT subtype, to we might need - to get the last character as |pre_break| from either the - |pre_break| list (if the previous INIT disc was taken), or the - |no_break| (sic) list (if the previous INIT disc was not taken) - - BUT: - the last characters (hyphenation character) if these two list - should always be the same anyway, so we just look at |pre_break|. - */ - /* let's look at the right margin first */ - if ((cur_p != null) && (type(cur_p) == disc_node) - && (vlink_pre_break(cur_p) != null)) { - /* a |disc_node| with non-empty |pre_break|, protrude the last char of |pre_break| */ - o = tlink_pre_break(cur_p); - } else { - o = find_protchar_right(l1, o); - } - /* now the left margin */ - if ((l1 != null) && (type(l1) == disc_node) && (vlink_post_break(l1) != null)) { - /* FIXME: first 'char' could be a disc! */ - l1 = vlink_post_break(l1); /* protrude the first char */ - } else { - l1 = find_protchar_left(l1, true); - } - shortfall += (left_pw(l1) + right_pw(o)); - } - if (shortfall != 0) { - margin_kern_stretch = 0; - margin_kern_shrink = 0; - if (protrude_chars > 1) { - /* Calculate variations of marginal kerns; */ - lp = last_leftmost_char; - rp = last_rightmost_char; - cp = raw_glyph_node(); - if (lp != null) { - cal_margin_kern_var(lp); - } - if (rp != null) { - cal_margin_kern_var(rp); - } - flush_node(cp); - } - if ((shortfall > 0) && ((total_font_stretch + margin_kern_stretch) > 0)) { - if ((total_font_stretch + margin_kern_stretch) > shortfall) - shortfall = ((total_font_stretch + margin_kern_stretch) / (max_stretch_ratio / cur_font_step)) / 2; - else - shortfall -= (total_font_stretch + margin_kern_stretch); - } else if ((shortfall < 0) && ((total_font_shrink + margin_kern_shrink) > 0)) { - if ((total_font_shrink + margin_kern_shrink) > -shortfall) - shortfall = -((total_font_shrink + margin_kern_shrink) / (max_shrink_ratio / cur_font_step)) / 2; - else - shortfall += (total_font_shrink + margin_kern_shrink); - } - } - if (shortfall > 0) { - /* Set the value of |b| to the badness for stretching the line, - and compute the corresponding |fit_class| */ - - /* When a line must stretch, the available stretchability can be - found in the subarray |cur_active_width[2..6]|, in units of - points, sfi, fil, fill and filll. - - The present section is part of \TeX's inner loop, and it is - most often performed when the badness is infinite; therefore - it is worth while to make a quick test for large width excess - and small stretchability, before calling the |badness| - subroutine. */ - - if ((cur_active_width[3] != 0) || (cur_active_width[4] != 0) || - (cur_active_width[5] != 0) || (cur_active_width[6] != 0)) { - if (do_last_line_fit) { - if (cur_p == null) { /* the last line of a paragraph */ - /* Perform computations for last line and |goto found|; */ - - /* Here we compute the adjustment |g| and badness |b| for - a line from |r| to the end of the paragraph. When any - of the criteria for adjustment is violated we fall - through to the normal algorithm. - - The last line must be too short, and have infinite - stretch entirely due to |par_fill_skip|. */ - if ((active_short(r) == 0) || (active_glue(r) <= 0)) - /* previous line was neither stretched nor shrunk, or - was infinitely bad */ - goto NOT_FOUND; - if ((cur_active_width[3] != fill_width[0]) || - (cur_active_width[4] != fill_width[1]) || - (cur_active_width[5] != fill_width[2]) || - (cur_active_width[6] != fill_width[3])) - /* infinite stretch of this line not entirely due to - |par_fill_skip| */ - goto NOT_FOUND; - if (active_short(r) > 0) - g = cur_active_width[2]; - else - g = cur_active_width[7]; - if (g <= 0) - /*no finite stretch resp.\ no shrink */ - goto NOT_FOUND; - arith_error = false; - g = fract(g, active_short(r), active_glue(r), - max_dimen); - if (last_line_fit < 1000) - g = fract(g, last_line_fit, 1000, max_dimen); - if (arith_error) { - if (active_short(r) > 0) - g = max_dimen; - else - g = -max_dimen; - } - if (g > 0) { - /*Set the value of |b| to the badness of the last line - for stretching, compute the corresponding |fit_class, - and |goto found|| */ - /* These badness computations are rather similar to - those of the standard algorithm, with the adjustment - amount |g| replacing the |shortfall|. */ - if (g > shortfall) - g = shortfall; - if (g > 7230584) { - if (cur_active_width[2] < 1663497) { - b = inf_bad; - fit_class = very_loose_fit; - goto FOUND; - } - } - b = badness(g, cur_active_width[2]); - if (b > 99) { - fit_class = very_loose_fit; - } else if (b > 12) { - fit_class = loose_fit; - } else { - fit_class = decent_fit; - } - goto FOUND; - } else if (g < 0) { - /*Set the value of |b| to the badness of the last line - for shrinking, compute the corresponding |fit_class, - and |goto found||; */ - if (-g > cur_active_width[7]) - g = -cur_active_width[7]; - b = badness(-g, cur_active_width[7]); - if (b > 12) - fit_class = tight_fit; - else - fit_class = decent_fit; - goto FOUND; - } - } - NOT_FOUND: - shortfall = 0; - } - b = 0; - fit_class = decent_fit; /* infinite stretch */ - } else { - if (shortfall > 7230584 && cur_active_width[2] < 1663497) { - b = inf_bad; - fit_class = very_loose_fit; - } else { - b = badness(shortfall, cur_active_width[2]); - if (b > 99) { - fit_class = very_loose_fit; - } else if (b > 12) { - fit_class = loose_fit; - } else { - fit_class = decent_fit; - } - } - } - } else { - /* Set the value of |b| to the badness for shrinking the line, - and compute the corresponding |fit_class|; */ - /* Shrinkability is never infinite in a paragraph; we can shrink - the line from |r| to |cur_p| by at most - |cur_active_width[7]|. */ - if (-shortfall > cur_active_width[7]) - b = inf_bad + 1; - else - b = badness(-shortfall, cur_active_width[7]); - if (b > 12) - fit_class = tight_fit; - else - fit_class = decent_fit; - } - if (do_last_line_fit) { - /* Adjust the additional data for last line; */ - if (cur_p == null) - shortfall = 0; - if (shortfall > 0) { - g = cur_active_width[2]; - } else if (shortfall < 0) { - g = cur_active_width[7]; - } else { - g = 0; - } - } - FOUND: - if ((b > inf_bad) || (pi == eject_penalty)) { - /* Prepare to deactivate node~|r|, and |goto deactivate| unless - there is a reason to consider lines of text from |r| to |cur_p| */ - /* During the final pass, we dare not lose all active nodes, lest we lose - touch with the line breaks already found. The code shown here makes - sure that such a catastrophe does not happen, by permitting overfull - boxes as a last resort. This particular part of \TeX\ was a source of - several subtle bugs before the correct program logic was finally - discovered; readers who seek to ``improve'' \TeX\ should therefore - think thrice before daring to make any changes here. - */ - if (final_pass && (minimum_demerits == awful_bad) && - (vlink(r) == active) && (prev_r == active)) { - artificial_demerits = true; /* set demerits zero, this break is forced */ - } else if (b > threshold) { - goto DEACTIVATE; - } - node_r_stays_active = false; - } else { - prev_r = r; - if (b > threshold) - continue; - node_r_stays_active = true; - } - /* Record a new feasible break; */ - /* When we get to this part of the code, the line from |r| to |cur_p| is - feasible, its badness is~|b|, and its fitness classification is - |fit_class|. We don't want to make an active node for this break yet, - but we will compute the total demerits and record them in the - |minimal_demerits| array, if such a break is the current champion among - all ways to get to |cur_p| in a given line-number class and fitness - class. - */ - if (artificial_demerits) { - d = 0; - } else { - /* Compute the demerits, |d|, from |r| to |cur_p|; */ - d = line_penalty + b; - if (abs(d) >= 10000) - d = 100000000; - else - d = d * d; - if (pi != 0) { - if (pi > 0) { - d += (pi * pi); - } else if (pi > eject_penalty) { - d -= (pi * pi); - } - } - if ((break_type == hyphenated_node) && (type(r) == hyphenated_node)) { - if (cur_p != null) - d += double_hyphen_demerits; - else - d += final_hyphen_demerits; - } - if (abs(fit_class - fitness(r)) > 1) - d = d + adj_demerits; - } - if (tracing_paragraphs > 0) - print_feasible_break(cur_p, r, b, pi, d, artificial_demerits); - d += total_demerits(r); /*this is the minimum total demerits - from the beginning to |cur_p| via |r| */ - if (d <= minimal_demerits[fit_class]) { - minimal_demerits[fit_class] = d; - best_place[fit_class] = break_node(r); - best_pl_line[fit_class] = l; - if (do_last_line_fit) { - /* Store additional data for this feasible break; */ - /* For each feasible break we record the shortfall and glue stretch or - shrink (or adjustment). */ - best_pl_short[fit_class] = shortfall; - best_pl_glue[fit_class] = g; - } - if (d < minimum_demerits) - minimum_demerits = d; - } - /* /Record a new feasible break */ - if (node_r_stays_active) - continue; /*|prev_r| has been set to |r| */ - DEACTIVATE: - /* Deactivate node |r|; */ - /* When an active node disappears, we must delete an adjacent delta node if - the active node was at the beginning or the end of the active list, or - if it was surrounded by delta nodes. We also must preserve the property - that |cur_active_width| represents the length of material from - |vlink(prev_r)| to~|cur_p|. */ - - vlink(prev_r) = vlink(r); - flush_node(r); - if (prev_r == active) { - /*Update the active widths, since the first active node has been - deleted */ - /* The following code uses the fact that |type(active)<>delta_node|. - If the active list has just become empty, we do not need to update the - |active_width| array, since it will be initialized when an active - node is next inserted. - */ - r = vlink(active); - if (type(r) == delta_node) { - do_all_eight(update_active); /* IMPLICIT r */ - do_all_eight(copy_to_cur_active); - vlink(active) = vlink(r); - flush_node(r); - } - } else if (type(prev_r) == delta_node) { - r = vlink(prev_r); - if (r == active) { - do_all_eight(downdate_width); /* IMPLICIT |prev_r| */ - vlink(prev_prev_r) = active; - flush_node(prev_r); - prev_r = prev_prev_r; - } else if (type(r) == delta_node) { - do_all_eight(update_width); /* IMPLICIT ,r */ - do_all_eight(combine_two_deltas); /* IMPLICIT r |prev_r| */ - vlink(prev_r) = vlink(r); - flush_node(r); - } - } - } -} - -@ @c -void ext_do_line_break(int paragraph_dir, - int pretolerance, - int tracing_paragraphs, - int tolerance, - scaled emergency_stretch, - int looseness, - int adjust_spacing, - halfword par_shape_ptr, - int adj_demerits, - int protrude_chars, - int line_penalty, - int last_line_fit, - int double_hyphen_demerits, - int final_hyphen_demerits, - int hang_indent, - int hsize, - int hang_after, - halfword left_skip, - halfword right_skip, - halfword inter_line_penalties_ptr, - int inter_line_penalty, - int club_penalty, - halfword club_penalties_ptr, - halfword widow_penalties_ptr, - int widow_penalty, - int broken_penalty, - halfword final_par_glue) -{ - /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE; */ - halfword cur_p, q, r, s; /* miscellaneous nodes of temporary interest */ - int line_break_dir = paragraph_dir; - - /* Get ready to start ... */ - minimum_demerits = awful_bad; - minimal_demerits[tight_fit] = awful_bad; - minimal_demerits[decent_fit] = awful_bad; - minimal_demerits[loose_fit] = awful_bad; - minimal_demerits[very_loose_fit] = awful_bad; - - fewest_demerits = 0; - actual_looseness = 0; - - /* We compute the values of |easy_line| and the other local variables relating - to line length when the |line_break| procedure is initializing itself. */ - if (par_shape_ptr == null) { - if (hang_indent == 0) { - last_special_line = 0; - second_width = hsize; - second_indent = 0; - } else { - halfword used_hang_indent = swap_hang_indent(hang_indent); - /* Set line length parameters in preparation for hanging indentation */ - /* We compute the values of |easy_line| and the other local variables relating - to line length when the |line_break| procedure is initializing itself. */ - last_special_line = abs(hang_after); - if (hang_after < 0) { - first_width = hsize - abs(used_hang_indent); - if (used_hang_indent >= 0) - first_indent = used_hang_indent; - else - first_indent = 0; - second_width = hsize; - second_indent = 0; - } else { - first_width = hsize; - first_indent = 0; - second_width = hsize - abs(used_hang_indent); - if (used_hang_indent >= 0) - second_indent = used_hang_indent; - else - second_indent = 0; - } - } - } else { - last_special_line = vinfo(par_shape_ptr + 1) - 1; - second_indent = varmem[(par_shape_ptr + 2 * (last_special_line + 1))].cint; - second_width = varmem[(par_shape_ptr + 2 * (last_special_line + 1) + 1)].cint; - second_indent = swap_parshape_indent(second_indent,second_width); - } - if (looseness == 0) - easy_line = last_special_line; - else - easy_line = max_halfword; - - no_shrink_error_yet = true; - check_shrinkage(left_skip); - check_shrinkage(right_skip); - q = left_skip; - r = right_skip; - background[1] = width(q) + width(r); - background[2] = 0; - background[3] = 0; - background[4] = 0; - background[5] = 0; - background[6] = 0; - background[2 + stretch_order(q)] = stretch(q); - background[2 + stretch_order(r)] += stretch(r); - background[7] = shrink(q) + shrink(r); - if (adjust_spacing > 1) { - background[8] = 0; - background[9] = 0; - max_stretch_ratio = -1; - max_shrink_ratio = -1; - cur_font_step = -1; - set_prev_char_p(null); - } - /* Check for special treatment of last line of paragraph; */ - /* The new algorithm for the last line requires that the stretchability - |par_fill_skip| is infinite and the stretchability of |left_skip| plus - |right_skip| is finite. - */ - do_last_line_fit = false; - if (last_line_fit > 0) { - q = last_line_fill; - if ((stretch(q) > 0) && (stretch_order(q) > normal)) { - if ((background[3] == 0) && (background[4] == 0) && - (background[5] == 0) && (background[6] == 0)) { - do_last_line_fit = true; - fill_width[0] = 0; - fill_width[1] = 0; - fill_width[2] = 0; - fill_width[3] = 0; - fill_width[stretch_order(q) - 1] = stretch(q); - } - } - } - /* DIR: Initialize |dir_ptr| for |line_break| */ - if (dir_ptr != null) { - flush_node_list(dir_ptr); - dir_ptr = null; - } -#if 0 - push_dir(dir_ptr,paragraph_dir); /* TODO what was the point of this? */ -#endif - - /* Find optimal breakpoints; */ - threshold = pretolerance; - if (threshold >= 0) { - if (tracing_paragraphs > 0) { - begin_diagnostic(); - tprint_nl("@@firstpass"); - } - second_pass = false; - final_pass = false; - } else { - threshold = tolerance; - second_pass = true; - final_pass = (emergency_stretch <= 0); - if (tracing_paragraphs > 0) - begin_diagnostic(); - } - while (1) { - halfword first_p; - halfword nest_stack[10]; - int nest_index = 0; - if (threshold > inf_bad) - threshold = inf_bad; - /* Create an active breakpoint representing the beginning of the paragraph */ - q = new_node(unhyphenated_node, decent_fit); - vlink(q) = active; - break_node(q) = null; - line_number(q) = cur_list.pg_field + 1; - total_demerits(q) = 0; - active_short(q) = 0; - active_glue(q) = 0; - vlink(active) = q; - do_all_eight(store_background); - passive = null; - printed_node = temp_head; - pass_number = 0; - font_in_short_display = null_font; - /* /Create an active breakpoint representing the beginning of the paragraph */ - auto_breaking = true; - cur_p = vlink(temp_head); - /* LOCAL: Initialize with first |local_paragraph| node */ - if ((cur_p != null) && (type(cur_p) == local_par_node)) { - alink(cur_p) = temp_head; /* this used to be an assert, but may as well force it */ - internal_pen_inter = local_pen_inter(cur_p); - internal_pen_broken = local_pen_broken(cur_p); - init_internal_left_box = local_box_left(cur_p); - init_internal_left_box_width = local_box_left_width(cur_p); - internal_left_box = init_internal_left_box; - internal_left_box_width = init_internal_left_box_width; - internal_right_box = local_box_right(cur_p); - internal_right_box_width = local_box_right_width(cur_p); - } else { - internal_pen_inter = 0; - internal_pen_broken = 0; - init_internal_left_box = null; - init_internal_left_box_width = 0; - internal_left_box = init_internal_left_box; - internal_left_box_width = init_internal_left_box_width; - internal_right_box = null; - internal_right_box_width = 0; - } - /* /LOCAL: Initialize with first |local_paragraph| node */ - set_prev_char_p(null); - first_p = cur_p; - /* to access the first node of paragraph as the first active node - has |break_node=null| */ - while ((cur_p != null) && (vlink(active) != active)) { - /* |try_break| if |cur_p| is a legal breakpoint; on the 2nd pass, also look at |disc_node|s. */ - - while (is_char_node(cur_p)) { - /* Advance |cur_p| to the node following the present string of characters ; */ - /* The code that passes over the characters of words in a paragraph is part of - \TeX's inner loop, so it has been streamlined for speed. We use the fact that - `\.{\\parfillskip}' glue appears at the end of each paragraph; it is therefore - unnecessary to check if |vlink(cur_p)=null| when |cur_p| is a character node. - */ - active_width[1] += pack_width(line_break_dir, dir_TRT, cur_p, true); - if ((adjust_spacing > 1) && check_expand_pars(font(cur_p))) { - set_prev_char_p(cur_p); - add_char_stretch(active_width[8], cur_p); - add_char_shrink(active_width[9], cur_p); - } - cur_p = vlink(cur_p); - while (cur_p == null && nest_index > 0) { - cur_p = nest_stack[--nest_index]; - } - } - if (cur_p == null) { - normal_error("linebreak","invalid list tail, probably missing glue"); - } - /* Determine legal breaks: As we move through the hlist, we need to keep - the |active_width| array up to date, so that the badness of individual - lines is readily calculated by |try_break|. It is convenient to use the - short name |active_width[1]| for the component of active width that represents - real width as opposed to glue. */ - - switch (type(cur_p)) { - case hlist_node: - case vlist_node: - active_width[1] += pack_width(line_break_dir, box_dir(cur_p), cur_p, false); - break; - case rule_node: - active_width[1] += width(cur_p); - break; - case dir_node: /* DIR: Adjust the dir stack for the |line_break| routine; */ - if (dir_dir(cur_p) >= 0) { - line_break_dir = dir_dir(cur_p); - push_dir_node(dir_ptr,cur_p); /* adds to |dir_ptr| */ - } else { - pop_dir_node(dir_ptr); - if (dir_ptr != null) { - line_break_dir = dir_dir(dir_ptr); - } - } - break; - case local_par_node: /* LOCAL: Advance past a |local_paragraph| node; */ - internal_pen_inter = local_pen_inter(cur_p); - internal_pen_broken = local_pen_broken(cur_p); - internal_left_box = local_box_left(cur_p); - internal_left_box_width = local_box_left_width(cur_p); - internal_right_box = local_box_right(cur_p); - internal_right_box_width = local_box_right_width(cur_p); - break; - case math_node: - auto_breaking = (subtype(cur_p) == after); - /* begin mathskip code */ - if (glue_is_zero(cur_p) || ignore_math_skip(cur_p)) { - kern_break(); - break; - } else { - /* fall through */ - } - /* end mathskip code */ - case glue_node: - /* - If node |cur_p| is a legal breakpoint, call |try_break|; - then update the active widths by including the glue in - |glue_ptr(cur_p)|; - - When node |cur_p| is a glue node, we look at the previous - to see whether or not a breakpoint is legal at |cur_p|, - as explained above. - - We only break after certain nodes (see texnodes.h), a font related - kern and a dir node when |\breakafterdirmode=1|. - */ - if (auto_breaking) { - halfword prev_p = alink(cur_p); - if (prev_p != temp_head && ( - is_char_node(prev_p) - || precedes_break(prev_p) - || precedes_kern(prev_p) - || precedes_dir(prev_p) - )) { - ext_try_break(0, unhyphenated_node, line_break_dir, adjust_spacing, - par_shape_ptr, adj_demerits, - tracing_paragraphs, protrude_chars, - line_penalty, last_line_fit, - double_hyphen_demerits, - final_hyphen_demerits, first_p, cur_p); - } - } - /* *INDENT-ON* */ - check_shrinkage(cur_p); - active_width[1] += width(cur_p); - active_width[2 + stretch_order(cur_p)] += stretch(cur_p); - active_width[7] += shrink(cur_p); - break; - case kern_node: - if (subtype(cur_p) == explicit_kern || subtype(cur_p) == italic_kern) { - kern_break(); - } else { - active_width[1] += width(cur_p); - if ((adjust_spacing == 2) && (subtype(cur_p) == normal)) { - add_kern_stretch(active_width[8], cur_p); - add_kern_shrink(active_width[9], cur_p); - } - } - break; - case disc_node: - /* |select_disc|s are handled by the leading |init_disc| */ - if (subtype(cur_p) == select_disc) - break; - /* Try to break after a discretionary fragment, then |goto done5|; */ - /* The following code knows that discretionary texts contain - only character nodes, kern nodes, box nodes, and rule - nodes. This branch differs a bit from older engines because in LuaTeX we - already have hyphenated the list. This means that we need to skip - automatic disc nodes. Of better, we need to treat discretionaries - and explicit hyphens always, even in the first pass (HH). */ - if (second_pass || subtype(cur_p) <= automatic_disc) { - /* - int actual_penalty = hyphen_penalty; - if (disc_penalty(cur_p) != 0) { - actual_penalty = (int) disc_penalty(cur_p); - } else if (subtype(cur_p) == automatic_disc) { - actual_penalty = ex_hyphen_penalty; - } - */ - int actual_penalty = (int) disc_penalty(cur_p); - s = vlink_pre_break(cur_p); - do_one_seven_eight(reset_disc_width); - if (s == null) { /* trivial pre-break */ - ext_try_break(actual_penalty, hyphenated_node, - line_break_dir, adjust_spacing, - par_shape_ptr, adj_demerits, - tracing_paragraphs, protrude_chars, - line_penalty, last_line_fit, - double_hyphen_demerits, - final_hyphen_demerits, first_p, cur_p); - } else { - add_to_widths(s, line_break_dir, adjust_spacing, disc_width); - do_one_seven_eight(add_disc_width_to_active_width); - ext_try_break(actual_penalty, hyphenated_node, - line_break_dir, adjust_spacing, - par_shape_ptr, adj_demerits, - tracing_paragraphs, protrude_chars, - line_penalty, last_line_fit, - double_hyphen_demerits, - final_hyphen_demerits, first_p, cur_p); - if (subtype(cur_p) == init_disc) { - /* we should at two break points after the one we - added above: - \item1 which does a possible break in INIT's |post_break| - \item2 which means the |no_break| actually was broken - just a character later */ - /* do the select-0 case 'f-f-i' */ - s = vlink_pre_break(vlink(cur_p)); - add_to_widths(s, line_break_dir, adjust_spacing, disc_width); - ext_try_break(actual_penalty, hyphenated_node, - line_break_dir, adjust_spacing, - par_shape_ptr, adj_demerits, - tracing_paragraphs, - protrude_chars, line_penalty, - last_line_fit, double_hyphen_demerits, - final_hyphen_demerits, first_p, - vlink(cur_p)); -#if 0 - /* TODO this does not work */ - /* go back to the starting situation */ - do_one_seven_eight(sub_disc_width_from_active_width); - do_one_seven_eight(reset_disc_width); - /* add select |no_break| to |active_width| */ - s = vlink_no_break(vlink(cur_p)); - add_to_widths(s, line_break_dir, adjust_spacing, disc_width); - ext_try_break(actual_penalty, hyphenated_node, - line_break_dir, adjust_spacing, - par_shape_ptr, adj_demerits, - tracing_paragraphs, - protrude_chars, line_penalty, - last_line_fit, double_hyphen_demerits, - final_hyphen_demerits, first_p, - vlink(cur_p)); -#endif - } - do_one_seven_eight(sub_disc_width_from_active_width); - } - } - s = vlink_no_break(cur_p); - add_to_widths(s, line_break_dir, adjust_spacing, active_width); - break; - case penalty_node: - ext_try_break(penalty(cur_p), unhyphenated_node, line_break_dir, - adjust_spacing, par_shape_ptr, adj_demerits, - tracing_paragraphs, protrude_chars, - line_penalty, last_line_fit, - double_hyphen_demerits, final_hyphen_demerits, - first_p, cur_p); - break; - case boundary_node: - case whatsit_node: - /* / Advance past a whatsit node in the |line_break| loop/; */ - case mark_node: - case ins_node: - case adjust_node: - break; - case glue_spec_node: - normal_warning("parbuilder","found a glue_spec in a paragraph"); - break; - default: - formatted_error("parbuilder","weird node %d in paragraph",type(cur_p)); - } - cur_p = vlink(cur_p); - while (cur_p == null && nest_index > 0) { - cur_p = nest_stack[--nest_index]; - } - } - if (cur_p == null) { - /* - Try the final line break at the end of the paragraph, - and |goto done| if the desired breakpoints have been found - - The forced line break at the paragraph's end will reduce the list of - breakpoints so that all active nodes represent breaks at |cur_p=null|. - On the first pass, we insist on finding an active node that has the - correct ``looseness.'' On the final pass, there will be at least one active - node, and we will match the desired looseness as well as we can. - - The global variable |best_bet| will be set to the active node for the best - way to break the paragraph, and a few other variables are used to - help determine what is best. - */ - ext_try_break(eject_penalty, hyphenated_node, line_break_dir, - adjust_spacing, par_shape_ptr, adj_demerits, - tracing_paragraphs, protrude_chars, line_penalty, - last_line_fit, double_hyphen_demerits, - final_hyphen_demerits, first_p, cur_p); - if (vlink(active) != active) { - /* Find an active node with fewest demerits; */ - r = vlink(active); - fewest_demerits = awful_bad; - do { - if (type(r) != delta_node) { - if (total_demerits(r) < fewest_demerits) { - fewest_demerits = total_demerits(r); - best_bet = r; - } - } - r = vlink(r); - } while (r != active); - best_line = line_number(best_bet); - /* - Find an active node with fewest demerits; - */ - if (looseness == 0) - goto DONE; - /* - Find the best active node for the desired looseness; - - The adjustment for a desired looseness is a slightly more complicated - version of the loop just considered. Note that if a paragraph is broken - into segments by displayed equations, each segment will be subject to the - looseness calculation, independently of the other segments. - */ - r = vlink(active); - actual_looseness = 0; - do { - if (type(r) != delta_node) { - line_diff = line_number(r) - best_line; - if (((line_diff < actual_looseness) - && (looseness <= line_diff)) - || ((line_diff > actual_looseness) - && (looseness >= line_diff))) { - best_bet = r; - actual_looseness = line_diff; - fewest_demerits = total_demerits(r); - } else if ((line_diff == actual_looseness) && - (total_demerits(r) < fewest_demerits)) { - best_bet = r; - fewest_demerits = total_demerits(r); - } - } - r = vlink(r); - } while (r != active); - best_line = line_number(best_bet); - /* - Find the best active node for the desired looseness; - */ - if ((actual_looseness == looseness) || final_pass) - goto DONE; - } - } - /* Clean up the memory by removing the break nodes; */ - clean_up_the_memory(); - /* /Clean up the memory by removing the break nodes; */ - if (!second_pass) { - if (tracing_paragraphs > 0) - tprint_nl("@@secondpass"); - threshold = tolerance; - second_pass = true; - final_pass = (emergency_stretch <= 0); - } else { - /* if at first you do not succeed, \dots */ - if (tracing_paragraphs > 0) - tprint_nl("@@emergencypass"); - background[2] += emergency_stretch; - final_pass = true; - } - } - - DONE: - if (tracing_paragraphs > 0) { - end_diagnostic(true); - normalize_selector(); - } - if (do_last_line_fit) { - /* - Adjust the final line of the paragraph; here we either reset - |do_last_line_fit| or adjust the |par_fill_skip| glue. - */ - if (active_short(best_bet) == 0) { - do_last_line_fit = false; - } else { - width(last_line_fill) += (active_short(best_bet) - active_glue(best_bet)); - stretch(last_line_fill) = 0; - } - } - - /* - Break the paragraph at the chosen...; Once the best sequence of breakpoints - has been found (hurray), we call on the procedure |post_line_break| to finish - the remainder of the work. By introducing this subprocedure, we are able to - keep |line_break| from getting extremely long. - */ - - /* first thing |ext_post_line_break| does is reset |dir_ptr| */ - flush_node_list(dir_ptr); - dir_ptr = null; - ext_post_line_break(paragraph_dir, - right_skip, - left_skip, - protrude_chars, - par_shape_ptr, - adjust_spacing, - inter_line_penalties_par_ptr, - inter_line_penalty, - club_penalty, - club_penalties_ptr, - widow_penalties_ptr, - widow_penalty, - broken_penalty, - final_par_glue, - best_bet, - last_special_line, - second_width, - second_indent, first_width, first_indent, best_line); - /* - Break the paragraph at the chosen ...Clean up the memory by removing - the break nodes. - */ - clean_up_the_memory(); -} - -@ @c -void get_linebreak_info (int *f, int *a) -{ - *f = fewest_demerits; - *a = actual_looseness; -} diff --git a/Build/source/texk/web2c/luatexdir/tex/mainbody.c b/Build/source/texk/web2c/luatexdir/tex/mainbody.c new file mode 100644 index 00000000000..ca9a79dbb02 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mainbody.c @@ -0,0 +1,769 @@ +/* + +mainbody.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +This is where the action starts. We're speaking of \LUATEX, a continuation of +\PDFTEX\ (which included \ETEX) and \ALEPH. As \TEX, \LUATEX\ is a document +compiler intended to simplify high quality typesetting for many of the world's +languages. It is an extension of D. E. Knuth's \TEX, which was designed +essentially for the typesetting of languages using the Latin alphabet. Although +it is a direct decendant of \TEX, and therefore mostly compatible, there are +some subtle differences that relate to \UNICODE\ support and \OPENTYPE\ math. + +The \ALEPH\ subsystem loosens many of the restrictions imposed by~\TeX: register +numbers are no longer limited to 8~bits. Fonts may have more than 256~characters, +more than 256~fonts may be used, etc. We use a similar model. We also borrowed +the directional model but have upgraded it a bit as well as integrated it more +tightly. + +This program is directly derived from Donald E. Knuth's \TEX; the change history +which follows and the reward offered for finders of bugs refer specifically to +\TEX; they should not be taken as referring to \LUATEX, \PDFTEX, nor \ETEX, +although the change history is relevant in that it demonstrates the evolutionary +path followed. This program is not \TEX; that name is reserved strictly for the +program which is the creation and sole responsibility of Professor Knuth. + +\starttyping +% Version 0 was released in September 1982 after it passed a variety of tests. +% Version 1 was released in November 1983 after thorough testing. +% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984). +% Version 1.2 allowed `0' in response to an error, et alia (October 1984). +% Version 1.3 made memory allocation more flexible and local (November 1984). +% Version 1.4 fixed accents right after line breaks, et alia (April 1985). +% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985). +% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986). +% Version 2.1 corrected anomalies in discretionary breaks (January 1987). +% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987). +% Version 2.3 avoided incomplete page in premature termination (August 1987). +% Version 2.4 fixed \noaligned rules in indented displays (August 1987). +% Version 2.5 saved cur_order when expanding tokens (September 1987). +% Version 2.6 added 10sp slop when shipping leaders (November 1987). +% Version 2.7 improved rounding of negative-width characters (November 1987). +% Version 2.8 fixed weird bug if no \patterns are used (December 1987). +% Version 2.9 made \csname\endcsname's "relax" local (December 1987). +% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988). +% Version 2.92 fixed \patterns, also file names with complex macros (May 1988). +% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988). +% Version 2.94 kept open_log_file from calling fatal_error (November 1988). +% Version 2.95 solved that problem a better way (December 1988). +% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989). +% Version 2.97 corrected blunder in creating 2.95 (February 1989). +% Version 2.98 omitted save_for_after at outer level (March 1989). +% Version 2.99 caught $$\begingroup\halign..$$ (June 1989). +% Version 2.991 caught .5\ifdim.6... (June 1989). +% Version 2.992 introduced major changes for 8-bit extensions (September 1989). +% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989). +% Version 3.0 fixed unusual displays; was more \output robust (March 1990). +% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990). +% Version 3.14 fixed unprintable font names and corrected typos (March 1991). +% Version 3.141 more of same; reconstituted ligatures better (March 1992). +% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993). +% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995). +% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002). +% Version 3.1415926 was a general cleanup with minor fixes (February 2008). +\stoptyping + +Although considerable effort has been expended to make the LuaTeX program correct +and reliable, no warranty is implied; the authors disclaim any obligation or +liability for damages, including but not limited to special, indirect, or +consequential damages arising out of or in connection with the use or performance +of this software. This work has been a ``labor of love'' and the authors hope +that users enjoy it. + +{\em You will find a lot of comments that originate in original \TEX. We kept them +as a side effect of the conversion from \WEB\ to \CWEB. Because there is not much +webbing going on here eventually the files became regular \CCODE\ files with still +potentially typeset comments. As we add our own comments, and also comments are +there from \PDFTEX, \ALEPH\ and \ETEX, we get a curious mix. The best comments are +of course from Don Knuth. All bad comments are ours. All errors are ours too! + +Not all comments make sense, because some things are implemented differently, for +instance some memory management. But the principles of tokens and nodes stayed. +It anyway means that you sometimes need to keep in mind that the explanation is +more geared to traditional \TEX. But that's not a bad thing. Sorry Don for any +confusion we introduced. The readers should have a copy of the \TEX\ books at hand +anyway.} + +A large piece of software like \TeX\ has inherent complexity that cannot be +reduced below a certain level of difficulty, although each individual part is +fairly simple by itself. The \.{WEB} language is intended to make the algorithms +as readable as possible, by reflecting the way the individual program pieces fit +together and by providing the cross-references that connect different parts. +Detailed comments about what is going on, and about why things were done in +certain ways, have been liberally sprinkled throughout the program. These +comments explain features of the implementation, but they rarely attempt to +explain the \TeX\ language itself, since the reader is supposed to be familiar +with {\sl The \TeX book}. + +The present implementation has a long ancestry, beginning in the summer of~1977, +when Michael~F. Plass and Frank~M. Liang designed and coded a prototype @^Plass, +Michael Frederick@> @^Liang, Franklin Mark@> @^Knuth, Donald Ervin@> based on +some specifications that the author had made in May of that year. This original +proto\TeX\ included macro definitions and elementary manipulations on boxes and +glue, but it did not have line-breaking, page-breaking, mathematical formulas, +alignment routines, error recovery, or the present semantic nest; furthermore, it +used character lists instead of token lists, so that a control sequence like +\.{\\halign} was represented by a list of seven characters. A complete version of +\TeX\ was designed and coded by the author in late 1977 and early 1978; that +program, like its prototype, was written in the {\mc SAIL} language, for which an +excellent debugging system was available. Preliminary plans to convert the {\mc +SAIL} code into a form somewhat like the present ``web'' were developed by Luis +Trabb~Pardo and @^Trabb Pardo, Luis Isidoro@> the author at the beginning of +1979, and a complete implementation was created by Ignacio~A. Zabala in 1979 and +1980. The \TeX82 program, which @^Zabala Salelles, Ignacio Andr\'es@> was written +by the author during the latter part of 1981 and the early part of 1982, also +incorporates ideas from the 1979 implementation of @^Guibas, Leonidas Ioannis@> +@^Sedgewick, Robert@> @^Wyatt, Douglas Kirk@> \TeX\ in {\mc MESA} that was +written by Leonidas Guibas, Robert Sedgewick, and Douglas Wyatt at the Xerox Palo +Alto Research Center. Several hundred refinements were introduced into \TeX82 +based on the experiences gained with the original implementations, so that +essentially every part of the system has been substantially improved. After the +appearance of ``Version 0'' in September 1982, this program benefited greatly +from the comments of many other people, notably David~R. Fuchs and Howard~W. +Trickey. A final revision in September 1989 extended the input character set to +eight-bit codes and introduced the ability to hyphenate words from different +languages, based on some ideas of Michael~J. Ferguson. @^Fuchs, David Raymond@> +@^Trickey, Howard Wellington@> @^Ferguson, Michael John@> + +No doubt there still is plenty of room for improvement, but the author is firmly +committed to keeping \TeX82 ``frozen'' from now on; stability and reliability are +to be its main virtues. +On the other hand, the \.{WEB} description can be extended without changing the +core of \TeX82 itself, and the program has been designed so that such extensions +are not extremely difficult to make. The |banner| string defined here should be +changed whenever \TeX\ undergoes any modifications, so that it will be clear +which version of \TeX\ might be the guilty party when a problem arises. +@^extensions to \TeX@> @^system dependencies@> + +This program contains code for various features extending \TeX, therefore this +program is called `\eTeX' and not `\TeX'; the official name `\TeX' by itself is +reserved for software systems that are fully compatible with each other. A +special test suite called the ``\.{TRIP} test'' is available for helping to +determine whether a particular implementation deserves to be known as `\TeX' +[cf.~Stanford Computer Science report CS1027, November 1984]. + +A similar test suite called the ``\.{e-TRIP} test'' is available for helping to +determine whether a particular implementation deserves to be known as `\eTeX'. + +This is the first of many sections of \TeX\ where global variables are defined. + +*/ + +/*tex Are we using lua for initializations? */ + +boolean luainit; + +/*tex Print file open and close info? */ + +boolean tracefilenames; + +/*tex + +This program has two important variations: (1) There is a long and slow version +called \.{INITEX}, which does the extra calculations needed to @.INITEX@> +initialize \TeX's internal tables; and (2)~there is a shorter and faster +production version, which cuts the initialization to a bare minimum. + +*/ + +/*tex are we \.{INITEX}? */ + +boolean ini_version; + +/*tex was the dump name option used? */ + +boolean dump_option; + +/*tex was a \.{\%\AM format} line seen? */ + +boolean dump_line; + +/*tex temporary for setup */ + +int bound_default; + +/*tex temporary for setup */ + +char *bound_name; + +/*tex width of context lines on terminal error messages */ + +int error_line; + +/*tex + width of first lines of contexts in terminal error messages; should be + between 30 and |error_line-15| +*/ + +int half_error_line; + +/*tex width of longest text lines output; should be at least 60 */ + +int max_print_line; + +/*tex maximum number of strings; must not exceed |max_halfword| */ + +int max_strings; + +/*tex strings available after format loaded */ + +int strings_free; + +/*tex loop variable for initialization */ + +int font_k; + +/*tex + maximum number of characters simultaneously present in current lines of open + files and in control sequences between \.{\\csname} and \.{\\endcsname}; must + not exceed |max_halfword| +*/ + +int buf_size; + +/*tex maximum number of simultaneous input sources */ + +int stack_size; + +/*tex + maximum number of input files and error insertions that can be going on + simultaneously +*/ + +int max_in_open; + +/*tex maximum number of simultaneous macro parameters */ + +int param_size; + +/*tex maximum number of semantic levels simultaneously active */ + +int nest_size; + +/*tex + space for saving values outside of current group; must be at most + |max_halfword| +*/ + +int save_size; + +/*tex limits recursive calls of the |expand| procedure */ + +int expand_depth; + +/*tex parse the first line for options */ + +int parsefirstlinep; + +/*tex format messages as file:line:error */ + +int filelineerrorstylep; + +/*tex stop at first error */ + +int haltonerrorp; + +/*tex current filename is quoted */ + +boolean quoted_filename; + +int get_luatexversion(void) +{ + return luatex_version; +} + +/*tex the number of pages that have been shipped out */ + +int total_pages = 0; + +/*tex recent outputs that didn't ship anything out */ + +int dead_cycles = 0; + +str_number get_luatexrevision(void) +{ + return luatex_revision; +} + +/*tex + +This is it: the part of \TeX\ that executes all those procedures we have written. + +We have noted that there are two versions of \TeX82. One, called \.{INITEX}, +@.INITEX@> has to be run first; it initializes everything from scratch, without +reading a format file, and it has the capability of dumping a format file. The +other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs +@.VIRTEX@> to input a format file in order to get started. + +For \LUATEX\ it is important to know that we still dump a format. But, in order +to gain speed and a smaller footprint, we gzip the format (level 3). We also +store some information that makes an abort possible in case of an incompatible +engine version, which is important as \LUATEX\ develops. It is possible to store +\LUA\ code in the format but not the current upvalues so you still need to +initialize. Also, traditional fonts are stored, as are extended fonts but any +additional information needed for instance to deal with \OPENTYPE\ fonts is to be +handled by \LUA\ code and therefore not present in the format. + +*/ + +#define const_chk(A) do { \ + if (A < inf_##A) \ + A = inf_##A; \ + if (A > sup_##A) \ + A = sup_##A; \ +} while (0) + +#define setup_bound_var(A,B,C) do { \ + if (luainit>0) { \ + get_lua_number("texconfig",B,&C); \ + if (C==0) \ + C=A; \ + } else { \ + integer x; \ + setupboundvariable(&x, B, A); \ + C = (int)x; \ + } \ +} while (0) + +int ready_already = 0; + +int main_initialize(void) +{ + /* + In case somebody has inadvertently made bad settings of the + ``constants,'' \LUATEX\ checks them using a variable called |bad|. + */ + int bad = 0; + /*tex + Bounds that may be set from the configuration file. We want the user to + be able to specify the names with underscores, but \.{TANGLE} removes + underscores, so we're stuck giving the names twice, once as a string, + once as the identifier. How ugly. (We can change that now.) + */ + setup_bound_var(15000, "max_strings", max_strings); + setup_bound_var(100, "strings_free", strings_free); + setup_bound_var(3000, "buf_size", buf_size); + setup_bound_var(50, "nest_size", nest_size); + setup_bound_var(15, "max_in_open", max_in_open); + setup_bound_var(60, "param_size", param_size); + setup_bound_var(4000, "save_size", save_size); + setup_bound_var(300, "stack_size", stack_size); + setup_bound_var(16384, "dvi_buf_size", dvi_buf_size); + setup_bound_var(79, "error_line", error_line); + setup_bound_var(50, "half_error_line", half_error_line); + setup_bound_var(79, "max_print_line", max_print_line); + setup_bound_var(0, "hash_extra", hash_extra); + setup_bound_var(72, "pk_dpi", pk_dpi); + setup_bound_var(10000, "expand_depth", expand_depth); + /*tex + Check other constants against their sup and inf. + */ + const_chk(buf_size); + const_chk(nest_size); + const_chk(max_in_open); + const_chk(param_size); + const_chk(save_size); + const_chk(stack_size); + const_chk(dvi_buf_size); + const_chk(max_strings); + const_chk(strings_free); + const_chk(hash_extra); + const_chk(pk_dpi); + if (error_line > ssup_error_line) { + error_line = ssup_error_line; + } + /*tex + Array memory allocation + */ + buffer = xmallocarray(packed_ASCII_code, (unsigned) buf_size); + nest = xmallocarray(list_state_record, (unsigned) nest_size); + save_stack = xmallocarray(save_record, (unsigned) save_size); + input_stack = xmallocarray(in_state_record, (unsigned) stack_size); + input_file = xmallocarray(alpha_file, (unsigned) max_in_open); + input_file_callback_id = xmallocarray(int, (unsigned) max_in_open); + line_stack = xmallocarray(int, (unsigned) max_in_open); + eof_seen = xmallocarray(boolean, (unsigned) max_in_open); + grp_stack = xmallocarray(save_pointer, (unsigned) max_in_open); + if_stack = xmallocarray(pointer, (unsigned) max_in_open); + source_filename_stack = xmallocarray(str_number, (unsigned) max_in_open); + full_source_filename_stack = xmallocarray(char *, (unsigned) max_in_open); + param_stack = xmallocarray(halfword, (unsigned) param_size); + dvi_buf = xmallocarray(eight_bits, (unsigned) dvi_buf_size); + /*tex + Only in ini mode: + */ + if (ini_version) { + fixmem = xmallocarray(smemory_word, fix_mem_init + 1); + memset(voidcast(fixmem), 0, (fix_mem_init + 1) * sizeof(smemory_word)); + fix_mem_min = 0; + fix_mem_max = fix_mem_init; + eqtb_top = eqtb_size + hash_extra; + if (hash_extra == 0) + hash_top = undefined_control_sequence; + else + hash_top = eqtb_top; + hash = xmallocarray(two_halves, (unsigned) (hash_top + 1)); + memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); + eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); + memset(eqtb, 0, sizeof(memory_word) * (unsigned) (eqtb_top + 1)); + init_string_pool_array((unsigned) max_strings); + reset_cur_string(); + } + /*tex + Check the ``constant'' values... + */ + if ((half_error_line < 30) || (half_error_line > error_line - 15)) + bad = 1; + if (max_print_line < 60) + bad = 2; + if (dvi_buf_size % 8 != 0) + bad = 3; + if (hash_prime > hash_size) + bad = 5; + if (max_in_open >= (sup_max_in_open+1)) /* 128 */ + bad = 6; + /*tex + Here are the inequalities that the quarterword and halfword values + must satisfy (or rather, the inequalities that they mustn't satisfy): + */ + if ((min_quarterword > 0) || (max_quarterword < 0x7FFF)) + bad = 11; + if ((min_halfword > 0) || (max_halfword < 0x3FFFFFFF)) + bad = 12; + if ((min_quarterword < min_halfword) || (max_quarterword > max_halfword)) + bad = 13; + if (font_base < min_quarterword) + bad = 15; + if ((save_size > max_halfword) || (max_strings > max_halfword)) + bad = 17; + if (buf_size > max_halfword) + bad = 18; + if (max_quarterword - min_quarterword < 0xFFFF) + bad = 19; + if (cs_token_flag + eqtb_size + hash_extra > max_halfword) + bad = 21; + if (bad > 0) { + wterm_cr(); + fprintf(term_out, + "Ouch---my internal constants have been clobbered! ---case %d", + (int) bad + ); + } else { + /*tex Set global variables to their starting values. */ + initialize(); + if (ini_version) { + /*tex Initialize all the primitives. */ + no_new_control_sequence = false; + first = 0; + initialize_commands(); + initialize_etex_commands(); + init_str_ptr = str_ptr; + no_new_control_sequence = true; + fix_date_and_time(); + } + ready_already = 314159; + } + return bad; +} + + +void main_body(void) +{ + static char pdftex_map[] = "pdftex.map"; + int bad = main_initialize(); + /*tex in case we quit during initialization */ + history = fatal_error_stop; + /*tex open the terminal for output */ + t_open_out(); + if (!luainit) + tracefilenames = true; + if (bad > 0) { + goto FINAL_END; + } + print_banner(luatex_version_string); + /*tex + Get the first line of input and prepare to start When we begin the + following code, \TeX's tables may still contain garbage; the strings + might not even be present. Thus we must proceed cautiously to get + bootstrapped in. + + But when we finish this part of the program, \TeX\ is ready to call on + the |main_control| routine to do its work. + */ + /*tex + This copies the command line, + */ + initialize_inputstack(); + if (buffer[iloc] == '*') + incr(iloc); + if ((format_ident == 0) || (buffer[iloc] == '&') || dump_line) { + char *fname = NULL; + if (format_ident != 0 && !ini_version) { + /*tex Erase preloaded format. */ + initialize(); + } + if ((fname = open_fmt_file()) == NULL) + goto FINAL_END; + if (!load_fmt_file(fname)) { + zwclose(fmt_file); + goto FINAL_END; + } + zwclose(fmt_file); + while ((iloc < ilimit) && (buffer[iloc] == ' ')) + incr(iloc); + } + if (output_mode_option != 0) + output_mode_par = output_mode_value; + if (draft_mode_option != 0) { + draft_mode_par = draft_mode_value; + } + /*tex can this be moved? */ + pdf_init_map_file((char *) pdftex_map); + /* */ + if (end_line_char_inactive) + decr(ilimit); + else + buffer[ilimit] = (packed_ASCII_code) end_line_char_par; + fix_date_and_time(); + random_seed = (microseconds * 1000) + (epochseconds % 1000000); + init_randoms(random_seed); + initialize_math(); + fixup_selector(log_opened_global); + check_texconfig_init(); + if ((iloc < ilimit) && (get_cat_code(cat_code_table_par, buffer[iloc]) != escape_cmd)) { + /*tex \.{\\input} assumed */ + start_input(); + } + /*tex Initialize |text_dir_ptr| */ + text_dir_ptr = new_dir(0); + /*tex Ready to go! */ + history = spotless; + /*tex Initialize synctex primitive */ + synctexinitcommand(); + /*tex Come to life. */ + main_control(); + flush_node(text_dir_ptr); + /*tex Prepare for death. */ + final_cleanup(); + close_files_and_terminate(); + FINAL_END: + do_final_end(); +} + +/*tex + +Here we do whatever is needed to complete \TeX's job gracefully on the local +operating system. The code here might come into play after a fatal error; it must +therefore consist entirely of ``safe'' operations that cannot produce error +messages. For example, it would be a mistake to call |str_room| or |make_string| +at this time, because a call on |overflow| might lead to an infinite loop. +@^system dependencies@> + +Actually there's one way to get error messages, via |prepare_mag|; but that can't +cause infinite recursion. @^recursion@> + +This program doesn't bother to close the input files that may still be open. + +*/ + +void close_files_and_terminate(void) +{ + int callback_id; + callback_id = callback_defined(stop_run_callback); + finalize_write_files(); + if (tracing_stats_par > 0) { + if (callback_id == 0) { + /*tex + Output statistics about this job. The present section goes + directly to the log file instead of using |print| commands, + because there's no need for these strings to take up + |string_pool| memory when a non-{\bf stat} version of \TeX\ is + being used. + */ + if (log_opened_global) { + fprintf(log_file, + "\n\nHere is how much of LuaTeX's memory you used:\n" + ); + fprintf(log_file, " %d string%s out of %d\n", + (int) (str_ptr - init_str_ptr), + (str_ptr == (init_str_ptr + 1) ? "" : "s"), + (int) (max_strings - init_str_ptr + STRING_OFFSET) + ); + fprintf(log_file, " %d,%d words of node,token memory allocated", + (int) var_mem_max, (int) fix_mem_max + ); + print_node_mem_stats(); + fprintf(log_file, + " %d multiletter control sequences out of %ld+%d\n", + (int) cs_count, (long) hash_size, (int) hash_extra + ); + fprintf(log_file, " %d font%s using %d bytes\n", + (int) max_font_id(), (max_font_id() == 1 ? "" : "s"), + (int) font_bytes + ); + fprintf(log_file, + " %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds\n", + (int) max_in_stack, (int) max_nest_stack, + (int) max_param_stack, (int) max_buf_stack, + (int) max_save_stack + 6, (int) stack_size, + (int) nest_size, (int) param_size, (int) buf_size, + (int) save_size + ); + } + } + } + wake_up_terminal(); + /*tex + Rubish, these \PDF arguments, passed, needs to be fixed, e.g. with a + dummy in \DVI. + */ + wrapup_backend(); + /*tex + Close {\sl Sync\TeX} file and write status. + */ + synctexterminate(log_opened_global); + /*tex + The following is needed because synctex removes files and we want to keep + them which means renaming a temp file .. we can't bypass the terminate + because it might do mem cleanup. + */ + if (synctex_get_mode() > 0) { + callback_id = callback_defined(finish_synctex_callback); + if (callback_id > 0) { + run_callback(callback_id, "->"); + } + } + /* free_text_codes(); */ + /* free_math_codes(); */ + if (log_opened_global) { + wlog_cr(); + selector = selector - 2; + if ((selector == term_only) && (callback_id == 0)) { + tprint_nl("Transcript written on "); + tprint_file_name(NULL, texmf_log_name, NULL); + print_char('.'); + print_ln(); + } + lua_a_close_out(log_file); + } + callback_id = callback_defined(wrapup_run_callback); + if (callback_id > 0) { + run_callback(callback_id, "->"); + } + free_text_codes(); + free_math_codes(); +} + +/*tex + +We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has been +scanned and |its_all_over|. + +*/ + +void final_cleanup(void) +{ + /*tex This one gets the value 0 for \.{\\end}, 1 for \.{\\dump}. */ + int c; + /*tex Here's one for looping marks: */ + halfword i; + /*tex This was a global temp_ptr: */ + halfword t; + c = cur_chr; + if (job_name == 0) + open_log_file(); + while (input_ptr > 0) + if (istate == token_list) + end_token_list(); + else + end_file_reading(); + while (open_parens > 0) { + report_stop_file(filetype_tex); + decr(open_parens); + } + if (cur_level > level_one) { + tprint_nl("(\\end occurred inside a group at level "); + print_int(cur_level - level_one); + print_char(')'); + show_save_groups(); + } + while (cond_ptr != null) { + tprint_nl("(\\end occurred when "); + print_cmd_chr(if_test_cmd, cur_if); + if (if_line != 0) { + tprint(" on line "); + print_int(if_line); + } + tprint(" was incomplete)"); + if_line = if_line_field(cond_ptr); + cur_if = subtype(cond_ptr); + t = cond_ptr; + cond_ptr = vlink(cond_ptr); + flush_node(t); + } + if (callback_defined(stop_run_callback) == 0) + if (history != spotless) + if ((history == warning_issued) || (interaction < error_stop_mode)) + if (selector == term_and_log) { + selector = term_only; + tprint_nl("(see the transcript file for additional information)"); + selector = term_and_log; + } + if (c == 1) { + if (ini_version) { + for (i = 0; i <= biggest_used_mark; i++) { + delete_top_mark(i); + delete_first_mark(i); + delete_bot_mark(i); + delete_split_first_mark(i); + delete_split_bot_mark(i); + } + for (c = last_box_code; c <= vsplit_code; c++) + flush_node_list(disc_ptr[c]); + if (last_glue != max_halfword) { + flush_node(last_glue); + } + /*tex Flush the pseudo files. */ + while (pseudo_files != null) { + pseudo_close(); + } + store_fmt_file(); + return; + } + tprint_nl("(\\dump is performed only by INITEX)"); + return; + } +} + +/*tex + +Once \TeX\ is working, you should be able to diagnose most errors with the +\.{\\show} commands and other diagnostic features. + +Because we have made some internal changes the optional debug interface +has been removed. + +*/ diff --git a/Build/source/texk/web2c/luatexdir/tex/mainbody.w b/Build/source/texk/web2c/luatexdir/tex/mainbody.w deleted file mode 100644 index 4d38b9d4a47..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/mainbody.w +++ /dev/null @@ -1,708 +0,0 @@ -% mainbody.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -\def\eTeX{e-\TeX} -\def\Aleph{Aleph} -\def\pdfTeX{pdf\TeX} - -@ @c - - -#include "ptexlib.h" - -@ -pdfTeX is copyright (C) 1996-2006 Han The Thanh, <thanh@@pdftex.org>. - -e-TeX is copyright (C) 1994,98 by Peter Breitenlohner. - -This is LuaTeX, a continuation of $\pdfTeX$ and $\Aleph$. LuaTeX is a -document compiler intended to simplify high-quality typesetting for -many of the world's languages. It is an extension of D. E. Knuth's -\TeX, which was designed essentially for the typesetting of languages -using the Latin alphabet. - -The $\Aleph$ subsystem loosens many of the restrictions imposed by~\TeX: -register numbers are no longer limited to 8~bits; fonts may have more -than 256~characters; more than 256~fonts may be used; etc. - -% This program is directly derived from Donald E. Knuth's TeX; -% the change history which follows and the reward offered for finders of -% bugs refer specifically to TeX; they should not be taken as referring -% to LuaTeX, pdfTeX, nor e-TeX, although the change history is relevant in that it -% demonstrates the evolutionary path followed. This program is not TeX; -% that name is reserved strictly for the program which is the creation -% and sole responsibility of Professor Knuth. - -% Version 0 was released in September 1982 after it passed a variety of tests. -% Version 1 was released in November 1983 after thorough testing. -% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984). -% Version 1.2 allowed `0' in response to an error, et alia (October 1984). -% Version 1.3 made memory allocation more flexible and local (November 1984). -% Version 1.4 fixed accents right after line breaks, et alia (April 1985). -% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985). -% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986). -% Version 2.1 corrected anomalies in discretionary breaks (January 1987). -% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987). -% Version 2.3 avoided incomplete page in premature termination (August 1987). -% Version 2.4 fixed \noaligned rules in indented displays (August 1987). -% Version 2.5 saved cur_order when expanding tokens (September 1987). -% Version 2.6 added 10sp slop when shipping leaders (November 1987). -% Version 2.7 improved rounding of negative-width characters (November 1987). -% Version 2.8 fixed weird bug if no \patterns are used (December 1987). -% Version 2.9 made \csname\endcsname's "relax" local (December 1987). -% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988). -% Version 2.92 fixed \patterns, also file names with complex macros (May 1988). -% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988). -% Version 2.94 kept open_log_file from calling fatal_error (November 1988). -% Version 2.95 solved that problem a better way (December 1988). -% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989). -% Version 2.97 corrected blunder in creating 2.95 (February 1989). -% Version 2.98 omitted save_for_after at outer level (March 1989). -% Version 2.99 caught $$\begingroup\halign..$$ (June 1989). -% Version 2.991 caught .5\ifdim.6... (June 1989). -% Version 2.992 introduced major changes for 8-bit extensions (September 1989). -% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989). -% Version 3.0 fixed unusual displays; was more \output robust (March 1990). -% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990). -% Version 3.14 fixed unprintable font names and corrected typos (March 1991). -% Version 3.141 more of same; reconstituted ligatures better (March 1992). -% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993). -% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995). -% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002). -% Version 3.1415926 was a general cleanup with minor fixes (February 2008). - - -% Although considerable effort has been expended to make the LuaTeX program -% correct and reliable, no warranty is implied; the authors disclaim any -% obligation or liability for damages, including but not limited to -% special, indirect, or consequential damages arising out of or in -% connection with the use or performance of this software. This work has -% been a ``labor of love'' and the authors hope that users enjoy it. - -A large piece of software like \TeX\ has inherent complexity that cannot -be reduced below a certain level of difficulty, although each individual -part is fairly simple by itself. The \.{WEB} language is intended to make -the algorithms as readable as possible, by reflecting the way the -individual program pieces fit together and by providing the -cross-references that connect different parts. Detailed comments about -what is going on, and about why things were done in certain ways, have -been liberally sprinkled throughout the program. These comments explain -features of the implementation, but they rarely attempt to explain the -\TeX\ language itself, since the reader is supposed to be familiar with -{\sl The \TeX book}. -@.WEB@> -@:TeXbook}{\sl The \TeX book@> - -The present implementation has a long ancestry, beginning in the summer -of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded -a prototype -@^Plass, Michael Frederick@> -@^Liang, Franklin Mark@> -@^Knuth, Donald Ervin@> -based on some specifications that the author had made in May of that year. -This original proto\TeX\ included macro definitions and elementary -manipulations on boxes and glue, but it did not have line-breaking, -page-breaking, mathematical formulas, alignment routines, error recovery, -or the present semantic nest; furthermore, -it used character lists instead of token lists, so that a control sequence -like \.{\\halign} was represented by a list of seven characters. A -complete version of \TeX\ was designed and coded by the author in late -1977 and early 1978; that program, like its prototype, was written in the -{\mc SAIL} language, for which an excellent debugging system was -available. Preliminary plans to convert the {\mc SAIL} code into a form -somewhat like the present ``web'' were developed by Luis Trabb~Pardo and -@^Trabb Pardo, Luis Isidoro@> -the author at the beginning of 1979, and a complete implementation was -created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which -@^Zabala Salelles, Ignacio Andr\'es@> -was written by the author during the latter part of 1981 and the early -part of 1982, also incorporates ideas from the 1979 implementation of -@^Guibas, Leonidas Ioannis@> -@^Sedgewick, Robert@> -@^Wyatt, Douglas Kirk@> -\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick, -and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred -refinements were introduced into \TeX82 based on the experiences gained with -the original implementations, so that essentially every part of the system -has been substantially improved. After the appearance of ``Version 0'' in -September 1982, this program benefited greatly from the comments of -many other people, notably David~R. Fuchs and Howard~W. Trickey. -A final revision in September 1989 extended the input character set to -eight-bit codes and introduced the ability to hyphenate words from -different languages, based on some ideas of Michael~J. Ferguson. -@^Fuchs, David Raymond@> -@^Trickey, Howard Wellington@> -@^Ferguson, Michael John@> - -No doubt there still is plenty of room for improvement, but the author -is firmly committed to keeping \TeX82 ``frozen'' from now on; stability -and reliability are to be its main virtues. - -On the other hand, the \.{WEB} description can be extended without changing -the core of \TeX82 itself, and the program has been designed so that such -extensions are not extremely difficult to make. -The |banner| string defined here should be changed whenever \TeX\ -undergoes any modifications, so that it will be clear which version of -\TeX\ might be the guilty party when a problem arises. -@^extensions to \TeX@> -@^system dependencies@> - -This program contains code for various features extending \TeX, -therefore this program is called `\eTeX' and not -`\TeX'; the official name `\TeX' by itself is reserved -for software systems that are fully compatible with each other. -A special test suite called the ``\.{TRIP} test'' is available for -helping to determine whether a particular implementation deserves to be -known as `\TeX' [cf.~Stanford Computer Science report CS1027, -November 1984]. - -A similar test suite called the ``\.{e-TRIP} test'' is available for -helping to determine whether a particular implementation deserves to be -known as `\eTeX'. - -@ This is the first of many sections of \TeX\ where global variables are -defined. - -@c -boolean luainit; /* are we using lua for initializations */ -boolean tracefilenames; /* print file open-close info? */ - - -@ This program has two important variations: (1) There is a long and slow -version called \.{INITEX}, which does the extra calculations needed to -@.INITEX@> -initialize \TeX's internal tables; and (2)~there is a shorter and faster -production version, which cuts the initialization to a bare minimum. - -@c -boolean ini_version; /* are we \.{INITEX}? */ -boolean dump_option; /* was the dump name option used? */ -boolean dump_line; /* was a \.{\%\AM format} line seen? */ -int bound_default; /* temporary for setup */ -char *bound_name; /* temporary for setup */ -int error_line; /* width of context lines on terminal error messages */ -int half_error_line; /* width of first lines of contexts in terminal - error messages; should be between 30 and |error_line-15| */ -int max_print_line; /* width of longest text lines output; should be at least 60 */ -int max_strings; /* maximum number of strings; must not exceed |max_halfword| */ -int strings_free; /* strings available after format loaded */ -int font_k; /* loop variable for initialization */ -int buf_size; /* maximum number of characters simultaneously present in - current lines of open files and in control sequences between - \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword| */ -int stack_size; /* maximum number of simultaneous input sources */ -int max_in_open; /* maximum number of input files and error insertions that - can be going on simultaneously */ -int param_size; /* maximum number of simultaneous macro parameters */ -int nest_size; /* maximum number of semantic levels simultaneously active */ -int save_size; /* space for saving values outside of current group; must be - at most |max_halfword| */ -int expand_depth; /* limits recursive calls of the |expand| procedure */ -int parsefirstlinep; /* parse the first line for options */ -int filelineerrorstylep; /* format messages as file:line:error */ -int haltonerrorp; /* stop at first error */ -boolean quoted_filename; /* current filename is quoted */ - -@ @c -int get_luatexversion(void) -{ - return luatex_version; -} - -str_number get_luatexrevision(void) -{ - return luatex_revision; -} - -@ This is it: the part of \TeX\ that executes all those procedures we have -written. - -We have noted that there are two versions of \TeX82. One, called \.{INITEX}, -@.INITEX@> -has to be run first; it initializes everything from scratch, without -reading a format file, and it has the capability of dumping a format file. -The other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs -@.VIRTEX@> -to input a format file in order to get started. - -@c -#define const_chk(A) do { \ - if (A < inf_##A) A = inf_##A; \ - if (A > sup_##A) A = sup_##A; \ - } while (0) - -#define setup_bound_var(A,B,C) do { \ - if (luainit>0) { \ - get_lua_number("texconfig",B,&C); \ - if (C==0) C=A; \ - } else { \ - integer x; \ - setupboundvariable(&x, B, A); \ - C = (int)x; \ - } \ - } while (0) - - -int ready_already = 0; - -int main_initialize(void) -{ - /* In case somebody has inadvertently made bad settings of the ``constants,'' - \LuaTeX\ checks them using a variable called |bad|. */ - int bad = 0; - /* Bounds that may be set from the configuration file. We want the user to - be able to specify the names with underscores, but \.{TANGLE} removes - underscores, so we're stuck giving the names twice, once as a string, - once as the identifier. How ugly. */ - - setup_bound_var(15000, "max_strings", max_strings); - setup_bound_var(100, "strings_free", strings_free); - setup_bound_var(3000, "buf_size", buf_size); - setup_bound_var(50, "nest_size", nest_size); - setup_bound_var(15, "max_in_open", max_in_open); - setup_bound_var(60, "param_size", param_size); - setup_bound_var(4000, "save_size", save_size); - setup_bound_var(300, "stack_size", stack_size); - setup_bound_var(16384, "dvi_buf_size", dvi_buf_size); - setup_bound_var(79, "error_line", error_line); - setup_bound_var(50, "half_error_line", half_error_line); - setup_bound_var(79, "max_print_line", max_print_line); - setup_bound_var(0, "hash_extra", hash_extra); - setup_bound_var(72, "pk_dpi", pk_dpi); - setup_bound_var(10000, "expand_depth", expand_depth); - - /* Check other constants against their sup and inf. */ - const_chk(buf_size); - const_chk(nest_size); - const_chk(max_in_open); - const_chk(param_size); - const_chk(save_size); - const_chk(stack_size); - const_chk(dvi_buf_size); - const_chk(max_strings); - const_chk(strings_free); - const_chk(hash_extra); - const_chk(pk_dpi); - if (error_line > ssup_error_line) - error_line = ssup_error_line; - - /* array memory allocation */ - buffer = xmallocarray(packed_ASCII_code, (unsigned) buf_size); - nest = xmallocarray(list_state_record, (unsigned) nest_size); - save_stack = xmallocarray(save_record, (unsigned) save_size); - input_stack = xmallocarray(in_state_record, (unsigned) stack_size); - input_file = xmallocarray(alpha_file, (unsigned) max_in_open); - input_file_callback_id = xmallocarray(int, (unsigned) max_in_open); - line_stack = xmallocarray(int, (unsigned) max_in_open); - eof_seen = xmallocarray(boolean, (unsigned) max_in_open); - grp_stack = xmallocarray(save_pointer, (unsigned) max_in_open); - if_stack = xmallocarray(pointer, (unsigned) max_in_open); - source_filename_stack = xmallocarray(str_number, (unsigned) max_in_open); - full_source_filename_stack = xmallocarray(char *, (unsigned) max_in_open); - param_stack = xmallocarray(halfword, (unsigned) param_size); - dvi_buf = xmallocarray(eight_bits, (unsigned) dvi_buf_size); - - if (ini_version) { - fixmem = xmallocarray(smemory_word, fix_mem_init + 1); - memset(voidcast(fixmem), 0, (fix_mem_init + 1) * sizeof(smemory_word)); - fix_mem_min = 0; - fix_mem_max = fix_mem_init; - eqtb_top = eqtb_size + hash_extra; - if (hash_extra == 0) - hash_top = undefined_control_sequence; - else - hash_top = eqtb_top; - hash = xmallocarray(two_halves, (unsigned) (hash_top + 1)); - memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); - eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); - memset(eqtb, 0, sizeof(memory_word) * (unsigned) (eqtb_top + 1)); - init_string_pool_array((unsigned) max_strings); - reset_cur_string(); - } - /* Check the ``constant'' values... */ - if ((half_error_line < 30) || (half_error_line > error_line - 15)) - bad = 1; - if (max_print_line < 60) - bad = 2; - if (dvi_buf_size % 8 != 0) - bad = 3; - if (hash_prime > hash_size) - bad = 5; - if (max_in_open >= (sup_max_in_open+1)) /* 128 */ - bad = 6; - /* Here are the inequalities that the quarterword and halfword values - must satisfy (or rather, the inequalities that they mustn't satisfy): */ - if ((min_quarterword > 0) || (max_quarterword < 0x7FFF)) - bad = 11; - if ((min_halfword > 0) || (max_halfword < 0x3FFFFFFF)) - bad = 12; - if ((min_quarterword < min_halfword) || (max_quarterword > max_halfword)) - bad = 13; - if (font_base < min_quarterword) - bad = 15; - if ((save_size > max_halfword) || (max_strings > max_halfword)) - bad = 17; - if (buf_size > max_halfword) - bad = 18; - if (max_quarterword - min_quarterword < 0xFFFF) - bad = 19; - if (cs_token_flag + eqtb_size + hash_extra > max_halfword) - bad = 21; - if (bad > 0) { - wterm_cr(); - fprintf(term_out, - "Ouch---my internal constants have been clobbered! ---case %d", - (int) bad); - } else { - initialize(); /* set global variables to their starting values */ - if (ini_version) { - /* initialize all the primitives */ - no_new_control_sequence = false; - first = 0; - initialize_commands(); - initialize_etex_commands(); - init_str_ptr = str_ptr; - no_new_control_sequence = true; - fix_date_and_time(); - } - ready_already = 314159; - } - return bad; -} - -@ @c -void main_body(void) -{ - static char pdftex_map[] = "pdftex.map"; - int bad = main_initialize(); - history = fatal_error_stop; /* in case we quit during initialization */ - t_open_out(); /* open the terminal for output */ - if (!luainit) - tracefilenames = true; - if (bad > 0) { - goto FINAL_END; - } - print_banner(luatex_version_string); - - /* Get the first line of input and prepare to start */ - /* When we begin the following code, \TeX's tables may still contain garbage; - the strings might not even be present. Thus we must proceed cautiously to get - bootstrapped in. - - But when we finish this part of the program, \TeX\ is ready to call on the - |main_control| routine to do its work. - */ - initialize_inputstack(); /* this copies the command-line */ - if (buffer[iloc] == '*') - incr(iloc); - if ((format_ident == 0) || (buffer[iloc] == '&') || dump_line) { - char *fname = NULL; - if (format_ident != 0 && !ini_version) - initialize(); /* erase preloaded format */ - if ((fname = open_fmt_file()) == NULL) - goto FINAL_END; - if (!load_fmt_file(fname)) { - zwclose(fmt_file); - goto FINAL_END; - } - zwclose(fmt_file); - while ((iloc < ilimit) && (buffer[iloc] == ' ')) - incr(iloc); - } - if (output_mode_option != 0) - output_mode_par = output_mode_value; - if (draft_mode_option != 0) { - draft_mode_par = draft_mode_value; - } - /* can this be moved? */ - pdf_init_map_file((char *) pdftex_map); - /* */ - if (end_line_char_inactive) - decr(ilimit); - else - buffer[ilimit] = (packed_ASCII_code) end_line_char_par; - fix_date_and_time(); - random_seed = (microseconds * 1000) + (epochseconds % 1000000); - init_randoms(random_seed); - initialize_math(); - fixup_selector(log_opened_global); - check_texconfig_init(); - if ((iloc < ilimit) && (get_cat_code(cat_code_table_par, buffer[iloc]) != escape_cmd)) - start_input(); /* \.{\\input} assumed */ - /* DIR: Initialize |text_dir_ptr| */ - text_dir_ptr = new_dir(0); - - history = spotless; /* ready to go! */ - /* Initialize synctex primitive */ - synctexinitcommand(); - main_control(); /* come to life */ - flush_node(text_dir_ptr); - final_cleanup(); /* prepare for death */ - close_files_and_terminate(); - FINAL_END: - do_final_end(); -} - - -@ Here we do whatever is needed to complete \TeX's job gracefully on the -local operating system. The code here might come into play after a fatal -error; it must therefore consist entirely of ``safe'' operations that -cannot produce error messages. For example, it would be a mistake to call -|str_room| or |make_string| at this time, because a call on |overflow| -might lead to an infinite loop. -@^system dependencies@> - -Actually there's one way to get error messages, via |prepare_mag|; -but that can't cause infinite recursion. -@^recursion@> - -This program doesn't bother to close the input files that may still be open. - -@c -void close_files_and_terminate(void) -{ - int callback_id; - callback_id = callback_defined(stop_run_callback); - finalize_write_files(); - if (tracing_stats_par > 0) { - if (callback_id == 0) { - /* Output statistics about this job */ - /* The present section goes directly to the log file instead of using - |print| commands, because there's no need for these strings to take - up |string_pool| memory when a non-{\bf stat} version of \TeX\ is being used. - */ - - if (log_opened_global) { - fprintf(log_file, - "\n\nHere is how much of LuaTeX's memory you used:\n"); - fprintf(log_file, " %d string%s out of %d\n", - (int) (str_ptr - init_str_ptr), - (str_ptr == (init_str_ptr + 1) ? "" : "s"), - (int) (max_strings - init_str_ptr + STRING_OFFSET)); - fprintf(log_file, " %d,%d words of node,token memory allocated", - (int) var_mem_max, (int) fix_mem_max); - print_node_mem_stats(); - fprintf(log_file, - " %d multiletter control sequences out of %ld+%d\n", - (int) cs_count, (long) hash_size, (int) hash_extra); - fprintf(log_file, " %d font%s using %d bytes\n", - (int) max_font_id(), (max_font_id() == 1 ? "" : "s"), - (int) font_bytes); - fprintf(log_file, - " %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds\n", - (int) max_in_stack, (int) max_nest_stack, - (int) max_param_stack, (int) max_buf_stack, - (int) max_save_stack + 6, (int) stack_size, - (int) nest_size, (int) param_size, (int) buf_size, - (int) save_size); - } - } - } - wake_up_terminal(); - /* rubish, these pdf arguments, passed, needs to be fixed, e.g. with a dummy in dvi */ - wrapup_backend(); - /* Close {\sl Sync\TeX} file and write status */ - synctexterminate(log_opened_global); - /* - The following is needed because synctex removes files and we want to keep them which - means renaming a temp file .. we can't bypass the terminate because it might do mem - cleanup. - */ - if (synctex_get_mode() > 0) { - callback_id = callback_defined(finish_synctex_callback); - if (callback_id > 0) { - run_callback(callback_id, "->"); - } - } - free_text_codes(); - free_math_codes(); - if (log_opened_global) { - wlog_cr(); - selector = selector - 2; - if ((selector == term_only) && (callback_id == 0)) { - tprint_nl("Transcript written on "); - tprint_file_name(NULL, texmf_log_name, NULL); - print_char('.'); - print_ln(); - } - lua_a_close_out(log_file); - } -} - - -@ We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has -been scanned and |its_all_over|\kern-2pt. - -@c -void final_cleanup(void) -{ - int c; /* 0 for \.{\\end}, 1 for \.{\\dump} */ - halfword i; /* for looping marks */ - halfword t; /* was a global temp_ptr */ - c = cur_chr; - if (job_name == 0) - open_log_file(); - while (input_ptr > 0) - if (istate == token_list) - end_token_list(); - else - end_file_reading(); - while (open_parens > 0) { - report_stop_file(filetype_tex); - decr(open_parens); - } - if (cur_level > level_one) { - tprint_nl("(\\end occurred inside a group at level "); - print_int(cur_level - level_one); - print_char(')'); - show_save_groups(); - } - while (cond_ptr != null) { - tprint_nl("(\\end occurred when "); - print_cmd_chr(if_test_cmd, cur_if); - if (if_line != 0) { - tprint(" on line "); - print_int(if_line); - } - tprint(" was incomplete)"); - if_line = if_line_field(cond_ptr); - cur_if = subtype(cond_ptr); - t = cond_ptr; - cond_ptr = vlink(cond_ptr); - flush_node(t); - } - if (callback_defined(stop_run_callback) == 0) - if (history != spotless) - if ((history == warning_issued) || (interaction < error_stop_mode)) - if (selector == term_and_log) { - selector = term_only; - tprint_nl("(see the transcript file for additional information)"); - selector = term_and_log; - } - if (c == 1) { - if (ini_version) { - for (i = 0; i <= biggest_used_mark; i++) { - delete_top_mark(i); - delete_first_mark(i); - delete_bot_mark(i); - delete_split_first_mark(i); - delete_split_bot_mark(i); - } - for (c = last_box_code; c <= vsplit_code; c++) - flush_node_list(disc_ptr[c]); - if (last_glue != max_halfword) { - flush_node(last_glue); - } - while (pseudo_files != null) - pseudo_close(); /* flush pseudo files */ - store_fmt_file(); - return; - } - tprint_nl("(\\dump is performed only by INITEX)"); - return; - } -} - -@ Once \TeX\ is working, you should be able to diagnose most errors with -the \.{\\show} commands and other diagnostic features. -An additional routine called |debug_help| -will come into play when you type `\.D' after an error message; -|debug_help| also occurs just before a fatal error causes \TeX\ to succumb. -@^debugging@> -@^system dependencies@> - -The interface to |debug_help| is primitive, but it is good enough when used -with a debugger that allows you to set breakpoints and to read -variables and change their values. After getting the prompt `\.{debug \#}', you -type either a negative number (this exits |debug_help|), or zero (this -goes to a location where you can set a breakpoint, thereby entering into -dialog with the debugger), or a positive number |m| followed by -an argument |n|. The meaning of |m| and |n| will be clear from the -program below. (If |m=13|, there is an additional argument, |l|.) -@.debug \#@> - -@c -#ifdef DEBUG -void debug_help(void) -{ /* routine to display various things */ - int k; - int m = 0, n = 0, l = 0; - while (1) { - wake_up_terminal(); - tprint_nl("debug # (-1 to exit):"); - update_terminal(); - (void) fscanf(term_in, "%d", &m); - if (m < 0) - return; - else if (m == 0) - abort(); /* go to every label at least once */ - else { - (void) fscanf(term_in, "%d", &n); - switch (m) { - case 1: - print_word(varmem[n]); /* display |varmem[n]| in all forms */ - break; - case 2: - print_int(info(n)); - break; - case 3: - print_int(link(n)); - break; - case 4: - print_word(eqtb[n]); - break; - case 6: - print_int(save_type(n)); - print_int(save_level(n)); - print_word(save_word(n)); - break; - case 7: - show_box(n); /* show a box, abbreviated by |show_box_depth| and |show_box_breadth| */ - break; - case 8: - breadth_max = 10000; - depth_threshold = 0x7FFFFFFF; - show_node_list(n); /* show a box in its entirety */ - break; - case 9: - show_token_list(n, null, 1000); - break; - case 10: - print(n); - break; - case 13: - (void) fscanf(term_in, "%d", &l); - print_cmd_chr(n, l); - break; - case 14: - for (k = 0; k <= n; k++) - print(buffer[k]); - break; - case 15: - font_in_short_display = null_font; - short_display(n); - break; - default: - tprint("?"); - break; - } - } - } -} -#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w b/Build/source/texk/web2c/luatexdir/tex/maincontrol.c index 7b4cf9065ca..295e2c6ccae 100644 --- a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w +++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.c @@ -1,80 +1,79 @@ -% maincontrol.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +maincontrol.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" #include "lua/luatex-api.h" -@ @c #define mode mode_par #define tail tail_par #define head head_par #define dir_save dirs_par -@ We come now to the |main_control| routine, which contains the master -switch that causes all the various pieces of \TeX\ to do their things, -in the right order. - -In a sense, this is the grand climax of the program: It applies all the -tools that we have worked so hard to construct. In another sense, this is -the messiest part of the program: It necessarily refers to other pieces -of code all over the place, so that a person can't fully understand what is -going on without paging back and forth to be reminded of conventions that -are defined elsewhere. We are now at the hub of the web, the central nervous -system that touches most of the other parts and ties them together. -@^brain@> - -The structure of |main_control| itself is quite simple. There's a label -called |big_switch|, at which point the next token of input is fetched -using |get_x_token|. Then the program branches at high speed into one of -about 100 possible directions, based on the value of the current -mode and the newly fetched command code; the sum |abs(mode)+cur_cmd| -indicates what to do next. For example, the case `|vmode+letter|' arises -when a letter occurs in vertical mode (or internal vertical mode); this -case leads to instructions that initialize a new paragraph and enter -horizontal mode. +/*tex + +We come now to the |main_control| routine, which contains the master switch that +causes all the various pieces of \TeX\ to do their things, in the right order. + +In a sense, this is the grand climax of the program: It applies all the tools +that we have worked so hard to construct. In another sense, this is the messiest +part of the program: It necessarily refers to other pieces of code all over the +place, so that a person can't fully understand what is going on without paging +back and forth to be reminded of conventions that are defined elsewhere. We are +now at the hub of the web, the central nervous system that touches most of the +other parts and ties them together. @^brain@> + +The structure of |main_control| itself is quite simple. There's a label called +|big_switch|, at which point the next token of input is fetched using +|get_x_token|. Then the program branches at high speed into one of about 100 +possible directions, based on the value of the current mode and the newly fetched +command code; the sum |abs(mode)+cur_cmd| indicates what to do next. For example, +the case `|vmode+letter|' arises when a letter occurs in vertical mode (or +internal vertical mode); this case leads to instructions that initialize a new +paragraph and enter horizontal mode. The big |case| statement that contains this multiway switch has been labeled -|reswitch|, so that the program can |goto reswitch| when the next token -has already been fetched. Most of the cases are quite short; they call -an ``action procedure'' that does the work for that case, and then they -either |goto reswitch| or they ``fall through'' to the end of the |case| -statement, which returns control back to |big_switch|. Thus, |main_control| -is not an extremely large procedure, in spite of the multiplicity of things -it must do; it is small enough to be handled by PASCAL compilers that put -severe restrictions on procedure size. -@!@^action procedure@> - -One case is singled out for special treatment, because it accounts for most -of \TeX's activities in typical applications. The process of reading simple -text and converting it into |char_node| records, while looking for ligatures -and kerns, is part of \TeX's ``inner loop''; the whole program runs -efficiently when its inner loop is fast, so this part has been written -with particular care. - -We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we -set it equal to |sf_code(cur_chr)|, except that it should never change -from a value less than 1000 to a value exceeding 1000. The most common -case is |sf_code(cur_chr)=1000|, so we want that case to be fast. - -@c +|reswitch|, so that the program can |goto reswitch| when the next token has +already been fetched. Most of the cases are quite short; they call an ``action +procedure'' that does the work for that case, and then they either |goto +reswitch| or they ``fall through'' to the end of the |case| statement, which +returns control back to |big_switch|. Thus, |main_control| is not an extremely +large procedure, in spite of the multiplicity of things it must do; it is small +enough to be handled by PASCAL compilers that put severe restrictions on +procedure size. @!@^action procedure@> + +One case is singled out for special treatment, because it accounts for most of +\TeX's activities in typical applications. The process of reading simple text and +converting it into |char_node| records, while looking for ligatures and kerns, is +part of \TeX's ``inner loop''; the whole program runs efficiently when its inner +loop is fast, so this part has been written with particular care. + +We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we set +it equal to |sf_code(cur_chr)|, except that it should never change from a value +less than 1000 to a value exceeding 1000. The most common case is +|sf_code(cur_chr)=1000|, so we want that case to be fast. + +*/ + void adjust_space_factor(void) { halfword s = get_sf_code(cur_chr); @@ -90,21 +89,21 @@ void adjust_space_factor(void) } } -@ From Knuth: ``Having |font_glue| allocated for each text font saves -both time and memory.'' That may be true, but it also punches through -the API wall for fonts, so I removed that -- Taco. But a bit of caching -is very welcome, which is why I need to have the next two globals: +/*tex -@ To handle the execution state of |main_control|'s eternal loop, -an extra global variable is used, along with a macro to define -its values. +To handle the execution state of |main_control|'s eternal loop, an extra global +variable is used, along with a macro to define its values. + +*/ -@c #define goto_next 0 #define goto_skip_token 1 #define goto_return 2 static int main_control_state; +static int local_level = 0; + +/*tex @* Main control helpers. @@ -112,8 +111,8 @@ Here are all the functions that are called from |main_control| that are not already defined elsewhere. For the moment, this list simply in the order that the appear in |init_main_control|, below. -@ -@c +*/ + static void run_char_num (void) { scan_char_num(); cur_chr = cur_val; @@ -126,23 +125,49 @@ static void run_char (void) { tail_append(new_char(cur_font_par, cur_chr)); } -@ -The occurrence of blank spaces is almost part of \TeX's inner loop, -since we usually encounter about one space for every five non-blank characters. -Therefore |main_control| gives second-highest priority to ordinary spaces. +static void run_node (void) { + halfword n = cur_chr; + if (copy_lua_input_nodes_par) { + n = copy_node_list(n); + } + tail_append(n); + while (vlink(n) != null) { + n = vlink(n); + tail_append(n); + } +} -When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will -see to it later that the corresponding glue specification is precisely -|zero_glue|, not merely a pointer to some specification that happens -to be full of zeroes. Therefore it is simple to test whether a glue parameter -is zero or~not. +static void run_lua_call(void) { + if (cur_chr <= 0) { + normal_error("luacall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luafunctioncall(cur_chr); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } +} + +/*tex + +The occurrence of blank spaces is almost part of \TeX's inner loop, since we +usually encounter about one space for every five non-blank characters. Therefore +|main_control| gives second-highest priority to ordinary spaces. + +When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will see to it +later that the corresponding glue specification is precisely |zero_glue|, not +merely a pointer to some specification that happens to be full of zeroes. +Therefore it is simple to test whether a glue parameter is zero or~not. + +*/ -@c static void run_app_space (void) { halfword p; /* was a global temp_ptr */ int method = disable_space_par ; if (method == 1) { - /* don't inject anything, not even zero skip */ + /*tex Don't inject anything, not even zero skip. */ } else if (method == 2) { p = new_glue(zero_glue); couple_nodes(tail,p); @@ -150,9 +175,9 @@ static void run_app_space (void) { } else if ((abs(mode) + cur_cmd == hmode + spacer_cmd) && (!(space_factor_par == 1000))) { app_space(); } else { - /* Append a normal inter-word space to the current list */ + /*tex Append a normal inter-word space to the current list. */ if (glue_is_zero(space_skip_par)) { - /* Find the glue specification for text spaces in the current font */ + /*tex Find the glue specification for text spaces in the current font. */ p = new_glue(zero_glue); width(p) = space(cur_font_par); stretch(p) = space_stretch(cur_font_par); @@ -161,20 +186,24 @@ static void run_app_space (void) { } else { p = new_param_glue(space_skip_code); } - /* so from now we have a subtype with spaces: */ + /*tex So from now we have a subtype with spaces: */ subtype(p) = space_skip_code + 1 ; couple_nodes(tail,p); tail = p; } } -@ Append a |boundary_node| -@c +/*tex + +Append a |boundary_node| + +*/ + static void run_boundary (void) { halfword n ; n = new_node(boundary_node,cur_chr); if ((cur_chr == 1) || (cur_chr == 2) ) { - /* user boundary or protrusion boundary */ + /*tex We expect a user boundary or protrusion boundary. */ scan_int(); boundary_value(n) = cur_val; } @@ -182,7 +211,6 @@ static void run_boundary (void) { tail = n; } -@ @c static void run_char_ghost (void) { int t; t = cur_chr; @@ -199,19 +227,21 @@ static void run_char_ghost (void) { } } -@ @c static void run_relax (void) { return; } -@ |ignore_spaces| is a special case: after it has acted, |get_x_token| has already -fetched the next token from the input, so that operation in |main_control| -should be skipped. +/*tex + +|ignore_spaces| is a special case: after it has acted, |get_x_token| has already +fetched the next token from the input, so that operation in |main_control| should +be skipped. + +*/ -@c static void run_ignore_spaces (void) { if (cur_chr == 0) { - /* Get the next non-blank non-call... */ + /*tex Get the next non-blank non-call... */ do { get_x_token(); } while (cur_cmd == spacer_cmd); @@ -231,24 +261,33 @@ static void run_ignore_spaces (void) { } } -@ |stop| is the second special case. We want |main_control| to return to its caller +/*tex + +|stop| is the second special case. We want |main_control| to return to its caller if there is nothing left to do. -@c +*/ + static void run_stop (void) { - if (its_all_over()) - main_control_state= goto_return; /* this is the only way out */ + if (its_all_over()) { + /*tex this is the only way out */ + main_control_state= goto_return; + } } -@ @c static void run_non_math_math (void) { back_input(); new_graf(true); } -@ @c +/*tex + + We build up an argument to |set_math_char|: + +*/ + static void run_math_char_num (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ + mathcodeval mval; if (cur_chr == 0) mval = scan_mathchar(tex_mathcode); else if (cur_chr == 1) @@ -258,35 +297,47 @@ static void run_math_char_num (void) { math_char_in_text(mval); } -@ @c +/*tex + + We build up an argument to |set_math_char|: +*/ + static void run_math_given (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ + mathcodeval mval; mval = mathchar_from_integer(cur_chr, tex_mathcode); math_char_in_text(mval); } +/*tex + + We build up an argument to |set_math_char| the \LUATEX\ way: +*/ + static void run_xmath_given (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ + mathcodeval mval; mval = mathchar_from_integer(cur_chr, umath_mathcode); math_char_in_text(mval); } -@ The most important parts of |main_control| are concerned with \TeX's -chief mission of box-making. We need to control the activities that put -entries on vlists and hlists, as well as the activities that convert -those lists into boxes. All of the necessary machinery has already been -developed; it remains for us to ``push the buttons'' at the right times. +/*tex -As an introduction to these routines, let's consider one of the simplest -cases: What happens when `\.{\\hrule}' occurs in vertical mode, or -`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control| -is short, since the |scan_rule_spec| routine already does most of what is -required; thus, there is no need for a special action procedure. +The most important parts of |main_control| are concerned with \TeX's chief +mission of box-making. We need to control the activities that put entries on +vlists and hlists, as well as the activities that convert those lists into boxes. +All of the necessary machinery has already been developed; it remains for us to +``push the buttons'' at the right times. -Note that baselineskip calculations are disabled after a rule in vertical -mode, by setting |prev_depth:=ignore_depth|. +As an introduction to these routines, let's consider one of the simplest cases: +What happens when `\.{\\hrule}' occurs in vertical mode, or `\.{\\vrule}' in +horizontal mode or math mode? The code in |main_control| is short, since the +|scan_rule_spec| routine already does most of what is required; thus, there is no +need for a special action procedure. + +Note that baselineskip calculations are disabled after a rule in vertical mode, +by setting |prev_depth:=ignore_depth|. + +*/ -@c static void run_rule (void) { tail_append(scan_rule_spec()); if (abs(mode) == vmode) @@ -295,29 +346,30 @@ static void run_rule (void) { space_factor_par = 1000; } -@ -Many of the actions related to box-making are triggered by the appearance -of braces in the input. For example, when the user says `\.{\\hbox} -\.{to} \.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode, -the information about the box size (100pt, |exactly|) is put onto |save_stack| -with a level boundary word just above it, and |cur_group:=adjusted_hbox_group|; -\TeX\ enters restricted horizontal mode to process the hlist. The right -brace eventually causes |save_stack| to be restored to its former state, -at which time the information about the box size (100pt, |exactly|) is -available once again; a box is packaged and we leave restricted horizontal -mode, appending the new box to the current list of the enclosing mode -(in this case to the current list of vertical mode), followed by any -vertical adjustments that were removed from the box by |hpack|. - -The next few sections of the program are therefore concerned with the -treatment of left and right curly braces. - -If a left brace occurs in the middle of a page or paragraph, it simply -introduces a new level of grouping, and the matching right brace will not have -such a drastic effect. Such grouping affects neither the mode nor the -current list. - -@c +/*tex + +Many of the actions related to box-making are triggered by the appearance of +braces in the input. For example, when the user says `\.{\\hbox} \.{to} +\.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode, the information +about the box size (100pt, |exactly|) is put onto |save_stack| with a level +boundary word just above it, and |cur_group:=adjusted_hbox_group|; \TeX\ enters +restricted horizontal mode to process the hlist. The right brace eventually +causes |save_stack| to be restored to its former state, at which time the +information about the box size (100pt, |exactly|) is available once again; a box +is packaged and we leave restricted horizontal mode, appending the new box to the +current list of the enclosing mode (in this case to the current list of vertical +mode), followed by any vertical adjustments that were removed from the box by +|hpack|. + +The next few sections of the program are therefore concerned with the treatment +of left and right curly braces. + +If a left brace occurs in the middle of a page or paragraph, it simply introduces +a new level of grouping, and the matching right brace will not have such a +drastic effect. Such grouping affects neither the mode nor the current list. + +*/ + static void run_left_brace (void) { new_save_level(simple_group); eq_word_define(int_base + no_local_whatsits_code, 0); @@ -338,11 +390,14 @@ static void run_end_group (void) { } } -@ Constructions that require a box are started by calling |scan_box| with -a specified context code. The |scan_box| routine verifies -that a |make_box| command comes next and then it calls |begin_box|. +/*tex + +Constructions that require a box are started by calling |scan_box| with a +specified context code. The |scan_box| routine verifies that a |make_box| command +comes next and then it calls |begin_box|. + +*/ -@c static void run_move (void) { int t = cur_chr; scan_normal_dimen(); @@ -352,17 +407,14 @@ static void run_move (void) { scan_box(-cur_val); } -@ @c static void run_leader_ship (void) { scan_box(leader_flag - a_leaders + cur_chr); } -@ @c static void run_make_box (void) { begin_box(0); } -@ @c static void run_box_dir (void) { scan_register_num(); cur_box = box(cur_val); @@ -372,37 +424,51 @@ static void run_box_dir (void) { box_dir(cur_box) = cur_val; } -@ There is a really small patch to add a new primitive called -\.{\\quitvmode}. In vertical modes, it is identical to \.{\\indent}, -but in horizontal and math modes it is really a no-op (as opposed to -\.{\\indent}, which executes the |indent_in_hmode| procedure). +static void run_box_direction (void) { + scan_register_num(); + cur_box = box(cur_val); + scan_optional_equals(); + scan_int(); + check_dir_value(cur_val); + if (cur_box != null) + box_dir(cur_box) = cur_val; +} + +/*tex + +There is a really small patch to add a new primitive called \.{\\quitvmode}. In +vertical modes, it is identical to \.{\\indent}, but in horizontal and math modes +it is really a no-op (as opposed to \.{\\indent}, which executes the +|indent_in_hmode| procedure). -A paragraph begins when horizontal-mode material occurs in vertical mode, -or when the paragraph is explicitly started by `\.{\\quitvmode}', -`\.{\\indent}' or `\.{\\noindent}'. +A paragraph begins when horizontal-mode material occurs in vertical mode, or when +the paragraph is explicitly started by `\.{\\quitvmode}', `\.{\\indent}' or +`\.{\\noindent}'. + +*/ -@c static void run_start_par_vmode (void) { new_graf((cur_chr > 0)); } -@ @c static void run_start_par (void) { if (cur_chr != 2) indent_in_hmode(); } -@ @c static void run_new_graf (void) { back_input(); new_graf(true); } -@ A paragraph ends when a |par_end| command is sensed, or when we are in -horizontal mode when reaching the right brace of vertical-mode routines -like \.{\\vbox}, \.{\\insert}, or \.{\\output}. +/*tex + +A paragraph ends when a |par_end| command is sensed, or when we are in horizontal +mode when reaching the right brace of vertical-mode routines like \.{\\vbox}, +\.{\\insert}, or \.{\\output}. + +*/ -@c static void run_par_end_vmode (void) { normal_paragraph(); if (mode > 0) { @@ -411,28 +477,27 @@ static void run_par_end_vmode (void) { } } -@ @c static void run_par_end_hmode (void) { - if (align_state < 0) - off_save(); /* this tries to recover from an alignment that didn't end properly */ - end_graf(bottom_level); /* this takes us to the enclosing mode, if |mode>0| */ + if (align_state < 0) { + /*tex This tries to recover from an alignment that didn't end properly. */ + off_save(); + } + /* This takes us to the enclosing mode, if |mode>0|. */ + end_graf(bottom_level); if (mode == vmode) { checked_page_filter(hmode_par); build_page(); } } -@ @c static void append_italic_correction_mmode (void) { - tail_append(new_kern(0)); /* what subtype to use */ + tail_append(new_kern(0)); } -@ @c static void run_local_box (void) { append_local_box(cur_chr); } -@ @c static void run_halign_mmode (void) { if (privileged()) { if (cur_group == math_shift_group) @@ -442,7 +507,6 @@ static void run_halign_mmode (void) { } } -@ @c static void run_eq_no (void) { if (privileged()) { if (cur_group == math_shift_group) @@ -452,21 +516,18 @@ static void run_eq_no (void) { } } -@ @c static void run_letter_mmode (void) { set_math_char(get_math_code(cur_chr)); } -@ @c static void run_char_num_mmode (void) { scan_char_num(); cur_chr = cur_val; set_math_char(get_math_code(cur_chr)); } -@ @c static void run_math_char_num_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ + mathcodeval mval; if (cur_chr == 0) mval = scan_mathchar(tex_mathcode); else if (cur_chr == 1) @@ -476,31 +537,27 @@ static void run_math_char_num_mmode (void) { set_math_char(mval); } -@ @c static void run_math_given_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ + mathcodeval mval; mval = mathchar_from_integer(cur_chr, tex_mathcode); set_math_char(mval); } static void run_xmath_given_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ + mathcodeval mval; mval = mathchar_from_integer(cur_chr, umath_mathcode); set_math_char(mval); } -@ @c static void run_delim_num (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ + mathcodeval mval; if (cur_chr == 0) mval = scan_delimiter_as_mathchar(tex_mathcode); else mval = scan_delimiter_as_mathchar(umath_mathcode); set_math_char(mval); - } -@ @c static void run_vcenter (void) { scan_spec(vcenter_group); normal_paragraph(); @@ -511,18 +568,15 @@ static void run_vcenter (void) { begin_token_list(every_vbox_par, every_vbox_text); } -@ @c static void run_math_style (void) { tail_append(new_style((small_number) cur_chr)); } -@ @c static void run_non_script (void) { tail_append(new_glue(zero_glue)); subtype(tail) = cond_math_glue; } -@ @c static void run_math_choice (void) { if (cur_chr == 0) append_choices(); @@ -530,7 +584,6 @@ static void run_math_choice (void) { setup_math_style(); } -@ @c static void run_math_shift (void) { if (cur_group == math_shift_group) after_math(); @@ -538,19 +591,16 @@ static void run_math_shift (void) { off_save(); } -@ @c static void run_after_assignment (void) { get_token(); after_token = cur_tok; } -@ @c static void run_after_group (void) { get_token(); save_for_after(cur_tok); } -@ @c static void run_extension (void) { do_extension(0); } @@ -565,12 +615,16 @@ static void run_normal (void) { scan_int(); if ((cur_val < 0) || (cur_val > 0x7FFF)) { print_err("Invalid \\catcode table"); - help1("All \\catcode table ids must be between 0 and 0x7FFF"); + help1( + "All \\catcode table ids must be between 0 and 0x7FFF" + ); error(); } else { if (cur_val == cat_code_table_par) { print_err("Invalid \\catcode table"); - help1("You cannot overwrite the current \\catcode table"); + help1( + "You cannot overwrite the current \\catcode table" + ); error(); } else { copy_cat_codes(cat_code_table_par, cur_val); @@ -581,12 +635,16 @@ static void run_normal (void) { scan_int(); if ((cur_val < 0) || (cur_val > 0x7FFF)) { print_err("Invalid \\catcode table"); - help1("All \\catcode table ids must be between 0 and 0x7FFF"); + help1( + "All \\catcode table ids must be between 0 and 0x7FFF" + ); error(); } else { if (cur_val == cat_code_table_par) { print_err("Invalid \\catcode table"); - help1("You cannot overwrite the current \\catcode table"); + help1( + "You cannot overwrite the current \\catcode table" + ); error(); } else { initex_cat_codes(cur_val); @@ -607,6 +665,12 @@ static void run_normal (void) { (void) scan_toks(false, false); late_lua_data(tail) = def_ref; break; + case late_lua_call_code: + new_whatsit(late_lua_node); + late_lua_type(tail) = lua_refid_call; + scan_int(); + late_lua_data(tail) = cur_val; + break; case expand_font_code: read_expand_font(); break; @@ -617,9 +681,10 @@ static void run_normal (void) { } } -/* - this is experimental and not used for production, only for testing and writing - macros (some options stay) +/*tex + +This is experimental and not used for production, only for testing and writing +macros (some options stay). */ @@ -633,14 +698,10 @@ static void run_option(void) { case math_option_code: if (scan_keyword("old")) { mathoption_set_int(c_mathoption_old_code); - } else if (scan_keyword("noitaliccompensation")) { - mathoption_set_int(c_mathoption_no_italic_compensation_code); - } else if (scan_keyword("nocharitalic")) { - mathoption_set_int(c_mathoption_no_char_italic_code); - } else if (scan_keyword("useoldfractionscaling")) { - mathoption_set_int(c_mathoption_use_old_fraction_scaling_code); + /* } else if (scan_keyword("umathcodemeaning")) { mathoption_set_int(c_mathoption_umathcode_meaning_code); + */ } else { normal_warning("mathoption","unknown key"); } @@ -651,20 +712,55 @@ static void run_option(void) { } } -@ For mode-independent commands, the following macro is useful. +static void lua_function_call(void) { + scan_int(); + if (cur_val <= 0) { + normal_error("luafunctioncall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luafunctioncall(cur_val); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } +} + +static void lua_bytecode_call(void) { + scan_int(); + if (cur_val < 0 || cur_val > 65535) { + normal_error("luabytecodecall", "invalid number"); + } else { + str_number u = save_cur_string(); + luacstrings = 0; + luabytecodecall(cur_val); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } +} + +/*tex + +For mode-independent commands, the following macro is useful. -Also, there is a list of cases where the user has probably gotten into or out of math -mode by mistake. \TeX\ will insert a dollar sign and rescan the current token, and -it makes sense ot have a macro for that as well. +Also, there is a list of cases where the user has probably gotten into or out of +math mode by mistake. \TeX\ will insert a dollar sign and rescan the current +token, and it makes sense ot have a macro for that as well. + +*/ -@c #define any_mode(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; jump_table[mmode+(A)]=B #define non_math(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; +/*tex + +The |main_control| uses a jump table, and |init_main_control| sets that table up. + +*/ -@ The |main_control| uses a jump table, and |init_main_control| sets that table up. -@c typedef void (*main_control_function) (void); + main_control_function *jump_table; static void init_main_control (void) { @@ -749,6 +845,7 @@ static void init_main_control (void) { any_mode(leader_ship_cmd, run_leader_ship); any_mode(make_box_cmd, run_make_box); any_mode(assign_box_dir_cmd, run_box_dir); + any_mode(assign_box_direction_cmd, run_box_direction); jump_table[vmode + start_par_cmd] = run_start_par_vmode; jump_table[hmode + start_par_cmd] = run_start_par; jump_table[mmode + start_par_cmd] = run_start_par; @@ -835,6 +932,7 @@ static void init_main_control (void) { any_mode(assign_int_cmd, prefixed_command); any_mode(assign_attr_cmd, prefixed_command); any_mode(assign_dir_cmd, prefixed_command); + any_mode(assign_direction_cmd, prefixed_command); any_mode(assign_dimen_cmd, prefixed_command); any_mode(assign_glue_cmd, prefixed_command); any_mode(assign_mu_glue_cmd, prefixed_command); @@ -879,46 +977,181 @@ static void init_main_control (void) { any_mode(normal_cmd, run_normal); any_mode(extension_cmd, run_extension); any_mode(option_cmd, run_option); + + any_mode(lua_function_call_cmd, lua_function_call); + any_mode(lua_bytecode_call_cmd, lua_bytecode_call); + any_mode(def_lua_call_cmd, prefixed_command); + any_mode(lua_call_cmd, run_lua_call); + /* any_mode(lua_expandable_call_cmd, run_lua_call); */ /* no! outside jump table anyway, handled in expand() */ + any_mode(node_cmd, run_node); + } -@ And here is |main_control| itself. It is quite short nowadays. +/*tex + + And here is |main_control| itself. It is quite short nowadays. + +*/ -@c void main_control(void) { main_control_state = goto_next; - init_main_control () ; - - if (equiv(every_job_loc) != null) + init_main_control() ; + if (equiv(every_job_loc) != null) { begin_token_list(equiv(every_job_loc), every_job_text); - + } while (1) { - if (main_control_state == goto_skip_token) - main_control_state = goto_next; /* reset */ - else + if (main_control_state == goto_skip_token) { + main_control_state = goto_next; + } else { get_x_token(); - - /* Give diagnostic information, if requested */ - /* When a new token has just been fetched at |big_switch|, we have an - ideal place to monitor \TeX's activity. */ + } + /*tex + Give diagnostic information, if requested When a new token has just + been fetched at |big_switch|, we have an ideal place to monitor + \TeX's activity. + */ if (interrupt != 0 && OK_to_interrupt) { back_input(); check_interrupt(); continue; } - if (tracing_commands_par > 0) + if (tracing_commands_par > 0) { show_cur_cmd_chr(); + } + /*tex run the command */ + (jump_table[(abs(mode) + cur_cmd)])(); + if (main_control_state == goto_return) { + return; + } + } + /*tex not reached */ + return; +} - (jump_table[(abs(mode) + cur_cmd)])(); /* run the command */ +/*tex - if (main_control_state == goto_return) { +We assume a trailing relax: |{...}\relax|, so we don't need a |back_input()| here. + +*/ + +/*int local_level = 0; */ + +extern void local_control_message(const char *s) +{ + tprint("local control level "); + print_int(local_level); + tprint(": "); + tprint(s); + tprint_nl(""); +} + +void local_control(void) +{ + int ll = local_level; + main_control_state = goto_next; + local_level += 1; + while (1) { + if (main_control_state == goto_skip_token) { + main_control_state = goto_next; + } else { + get_x_token(); + } + if (interrupt != 0 && OK_to_interrupt) { + back_input(); + check_interrupt(); + continue; + } + if (tracing_commands_par > 0) { + show_cur_cmd_chr(); + } + (jump_table[(abs(mode) + cur_cmd)])(); + if (local_level <= ll) { + main_control_state = goto_next; + if (tracing_nesting_par > 2) { + local_control_message("leaving due to level change"); + } + return ; + } else if (main_control_state == goto_return) { + if (tracing_nesting_par > 2) { + local_control_message("leaving due to triggering"); + } return; } } - return; /* not reached */ + return; +} + +void end_local_control(void ) +{ + local_level -= 1; +} + +/*tex + We need to go back to the main loop. This is rather nasty and dirty + and counterintuive code and there might be a cleaner way. Basically + we trigger the main control state from here. + + \starttyping + 0 0 \directlua{token.scan_list()}\hbox{!} + -1 0 \setbox0\hbox{x}\directlua{token.scan_list()}\box0 + 1 1 \toks0={\directlua{token.scan_list()}\hbox{x}}\directlua{tex.runtoks(0)} + 0 0 1 1 \directlua{tex.box[0]=token.scan_list()}\hbox{x\directlua{node.write(token.scan_list())}\hbox{x}} + 0 0 0 1 \setbox0\hbox{x}\directlua{tex.box[0]=token.scan_list()}\hbox{x\directlua{node.write(token.scan_list())}\box0} + \stoptyping + + It's rather fragile code so we added some tracing options. + +*/ + +halfword local_scan_box(void) +{ + int old_mode = mode; + int ll = local_level; + mode = -hmode; + scan_box(lua_scan_flag); + if (local_level == ll) { + /*tex |\directlua{print(token.scan_list())}\hbox{!}| (n n) */ + if (tracing_nesting_par > 2) { + local_control_message("entering at end of box scanning"); + } + local_control(); + } else { + /*tex |\directlua{print(token.scan_list())}\box0| (n-1 n) */ + /* + if (tracing_nesting_par > 2) { + local_control_message("setting level after box scanning"); + } + */ + local_level = ll; + } + mode = old_mode; + return cur_box; +} + +/*tex + + We have an issue with modes when we quit here because we're coming + from and still staying at the \LUA\ end. So, unless we're already + nested, we trigger an end_local_level token (an extension code). + +*/ + +static void wrapup_local_scan_box(void) +{ + /* + if (tracing_nesting_par > 2) { + local_control_message("leaving box scanner"); + } + */ + local_level -= 1; +} + +int current_local_level(void) +{ + return local_level; } -@ @c void app_space(void) { /* handle spaces when |space_factor<>1000| */ halfword q; /* glue node */ @@ -948,20 +1181,24 @@ void app_space(void) tail = q; } -@ @c void insert_dollar_sign(void) { back_input(); cur_tok = math_shift_token + '$'; print_err("Missing $ inserted"); - help2("I've inserted a begin-math/end-math symbol since I think", - "you left one out. Proceed, with fingers crossed."); + help2( + "I've inserted a begin-math/end-math symbol since I think", + "you left one out. Proceed, with fingers crossed." + ); ins_error(); } -@ We can silently ignore \.{\\par}s in a math formula. +/*tex + + We can silently ignore \.{\\par}s in a math formula. + +*/ -@c void insert_dollar_sign_par_end(void) { if (!suppress_mathpar_error_par) { @@ -969,10 +1206,13 @@ void insert_dollar_sign_par_end(void) } } -@ The `|you_cant|' procedure prints a line saying that the current command -is illegal in the current mode; it identifies these things symbolically. +/*tex + +The `|you_cant|' procedure prints a line saying that the current command is +illegal in the current mode; it identifies these things symbolically. + +*/ -@c void you_cant(void) { print_err("You can't use `"); @@ -980,34 +1220,40 @@ void you_cant(void) print_in_mode(mode); } -@ -When erroneous situations arise, \TeX\ usually issues an error message -specific to the particular error. For example, `\.{\\noalign}' should -not appear in any mode, since it is recognized by the |align_peek| routine -in all of its legitimate appearances; a special error message is given -when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate -error message is simply that the user is not allowed to do what he or she -has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode, -and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated -here and in the other sections referred to under `See also \dots.' +/*tex + +When erroneous situations arise, \TeX\ usually issues an error message specific +to the particular error. For example, `\.{\\noalign}' should not appear in any +mode, since it is recognized by the |align_peek| routine in all of its legitimate +appearances; a special error message is given when `\.{\\noalign}' occurs +elsewhere. But sometimes the most appropriate error message is simply that the +user is not allowed to do what he or she has attempted. For example, +`\.{\\moveleft}' is allowed only in vertical mode, and `\.{\\lower}' only in +non-vertical modes. Such cases are enumerated here and in the other sections +referred to under `See also \dots.' + +*/ -@c void report_illegal_case(void) { you_cant(); - help4("Sorry, but I'm not programmed to handle this case;", - "I'll just pretend that you didn''t ask for it.", - "If you're in the wrong mode, you might be able to", - "return to the right one by typing `I}' or `I$' or `I\\par'."); + help4( + "Sorry, but I'm not programmed to handle this case;", + "I'll just pretend that you didn''t ask for it.", + "If you're in the wrong mode, you might be able to", + "return to the right one by typing `I}' or `I$' or `I\\par'." + ); error(); } -@ Some operations are allowed only in privileged modes, i.e., in cases -that |mode>0|. The |privileged| function is used to detect violations -of this rule; it issues an error message and returns |false| if the -current |mode| is negative. +/*tex + +Some operations are allowed only in privileged modes, i.e., in cases that +|mode>0|. The |privileged| function is used to detect violations of this rule; it +issues an error message and returns |false| if the current |mode| is negative. + +*/ -@c boolean privileged(void) { if (mode > 0) { @@ -1018,15 +1264,18 @@ boolean privileged(void) } } -@ We don't want to leave |main_control| immediately when a |stop| command -is sensed, because it may be necessary to invoke an \.{\\output} routine -several times before things really grind to a halt. (The output routine -might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.) -Therefore |its_all_over| is |true| only when the current page -and contribution list are empty, and when the last output was not a -``dead cycle.'' +/*tex + +We don't want to leave |main_control| immediately when a |stop| command is +sensed, because it may be necessary to invoke an \.{\\output} routine several +times before things really grind to a halt. (The output routine might even say +`\.{\\gdef\\end\{...\}}', to prolong the life of the job.) Therefore +|its_all_over| is |true| only when the current page and contribution list are +empty, and when the last output was not a ``dead cycle.'' + +*/ + -@c boolean its_all_over(void) { /* do this when \.{\\end} or \.{\\dump} occurs */ if (privileged()) { @@ -1044,16 +1293,18 @@ boolean its_all_over(void) return false; } +/*tex + +The |hskip| and |vskip| command codes are used for control sequences like +\.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}. The +difference is in the value of |cur_chr|. -@ The |hskip| and |vskip| command codes are used for control sequences -like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}. -The difference is in the value of |cur_chr|. +All the work relating to glue creation has been relegated to the following +subroutine. It does not call |build_page|, because it is used in at least one +place where that would be a mistake. -All the work relating to glue creation has been relegated to the -following subroutine. It does not call |build_page|, because it is -used in at least one place where that would be a mistake. +*/ -@c void append_glue(void) { int s = cur_chr; @@ -1085,35 +1336,37 @@ void append_glue(void) } } -@ @c void append_kern(void) { - int s; /* |subtype| of the kern node */ - s = cur_chr; + int s = cur_chr; /* |subtype| of the kern node */ scan_dimen((s == mu_glue), false, false); tail_append(new_kern(cur_val)); subtype(tail) = (quarterword) s; } -@ We have to deal with errors in which braces and such things are not -properly nested. Sometimes the user makes an error of commission by -inserting an extra symbol, but sometimes the user makes an error of omission. -\TeX\ can't always tell one from the other, so it makes a guess and tries -to avoid getting into a loop. +/*tex + +We have to deal with errors in which braces and such things are not properly +nested. Sometimes the user makes an error of commission by inserting an extra +symbol, but sometimes the user makes an error of omission. \TeX\ can't always +tell one from the other, so it makes a guess and tries to avoid getting into a +loop. The |off_save| routine is called when the current group code is wrong. It tries -to insert something into the user's input that will help clean off -the top level. +to insert something into the user's input that will help clean off the top level. + +*/ -@c void off_save(void) { - halfword p, q; /* inserted token */ + halfword p, q; if (cur_group == bottom_level) { - /* Drop current token and complain that it was unmatched */ + /*tex Drop current token and complain that it was unmatched */ print_err("Extra "); print_cmd_chr((quarterword) cur_cmd, cur_chr); - help1("Things are pretty mixed up, but I think the worst is over."); + help1( + "Things are pretty mixed up, but I think the worst is over." + ); error(); } else { @@ -1121,8 +1374,11 @@ void off_save(void) p = get_avail(); set_token_link(temp_token_head, p); print_err("Missing "); - /* Prepare to insert a token that matches |cur_group|, and print what it is */ - /* At this point, |link(temp_token_head)=p|, a pointer to an empty one-word node. */ + /*tex + Prepare to insert a token that matches |cur_group|, and print what it + is. At this point, |link(temp_token_head)=p|, a pointer to an empty + one-word node. + */ switch (cur_group) { case semi_simple_group: set_token_info(p, cs_token_flag + frozen_end_group); @@ -1147,23 +1403,27 @@ void off_save(void) } tprint(" inserted"); ins_list(token_link(temp_token_head)); - help5("I've inserted something that you may have forgotten.", - "(See the <inserted text> above.)", - "With luck, this will get me unwedged. But if you", - "really didn't forget anything, try typing `2' now; then", - "my insertion and my current dilemma will both disappear."); + help5( + "I've inserted something that you may have forgotten.", + "(See the <inserted text> above.)", + "With luck, this will get me unwedged. But if you", + "really didn't forget anything, try typing `2' now; then", + "my insertion and my current dilemma will both disappear." + ); error(); } } +/*tex + +The routine for a |right_brace| character branches into many subcases, since a +variety of things may happen, depending on |cur_group|. Some types of groups are +not supposed to be ended by a right brace; error messages are given in hopes of +pinpointing the problem. Most branches of this routine will be filled in later, +when we are ready to understand them; meanwhile, we must prepare ourselves to +deal with such errors. -@ The routine for a |right_brace| character branches into many subcases, -since a variety of things may happen, depending on |cur_group|. Some -types of groups are not supposed to be ended by a right brace; error -messages are given in hopes of pinpointing the problem. Most branches -of this routine will be filled in later, when we are ready to understand -them; meanwhile, we must prepare ourselves to deal with such errors. +*/ -@c void handle_right_brace(void) { halfword p, q; /* for short-term use */ @@ -1176,8 +1436,10 @@ void handle_right_brace(void) break; case bottom_level: print_err("Too many }'s"); - help2("You've closed more groups than you opened.", - "Such booboos are generally harmless, so keep going."); + help2( + "You've closed more groups than you opened.", + "Such booboos are generally harmless, so keep going." + ); error(); break; case semi_simple_group: @@ -1185,10 +1447,20 @@ void handle_right_brace(void) case math_left_group: extra_right_brace(); break; + /*tex + When the right brace occurs at the end of an \.{\\hbox} or + \.{\\vbox} or \.{\\vtop} construction, the |package| routine + comes into action. We might also have to finish a paragraph that + hasn't ended. + */ case hbox_group: - /* When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or - \.{\\vtop} construction, the |package| routine comes into action. We might - also have to finish a paragraph that hasn't ended. */ + if (fixup_boxes_par) { + /*tex + This is unofficial! Fixing up (also elsewhere) might become default + some day but for a while I will test this in ConTeXt. + */ + fixup_directions_only(); + } package(0); break; case adjusted_hbox_group: @@ -1211,7 +1483,9 @@ void handle_right_brace(void) f = floating_penalty_par; unsave(); save_ptr--; - /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */ + /*tex + Now |saved_value(0)| is the insertion number, or the |vadjust| subtype. + */ p = vpack(vlink(head), 0, additional, -1); pop_nest(); if (saved_type(0) == saved_insert) { @@ -1236,9 +1510,11 @@ void handle_right_brace(void) } break; case output_group: - /* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */ + /*tex + this is needed in case the \.{\\output} executes a \.{\\textdir} command. + */ if (dir_level(text_dir_ptr) == cur_level) { - /* DIR: Remove from |text_dir_ptr| */ + /*tex Remove from |text_dir_ptr| */ halfword text_dir_tmp = vlink(text_dir_ptr); flush_node(text_dir_ptr); text_dir_ptr = text_dir_tmp; @@ -1255,7 +1531,9 @@ void handle_right_brace(void) back_input(); cur_tok = cs_token_flag + frozen_cr; print_err("Missing \\cr inserted"); - help1("I'm guessing that you meant to end an alignment here."); + help1( + "I'm guessing that you meant to end an alignment here." + ); ins_error(); break; case no_align_group: @@ -1279,7 +1557,6 @@ void handle_right_brace(void) } } -@ @c void extra_right_brace(void) { print_err("Extra }, or forgotten "); @@ -1294,19 +1571,24 @@ void extra_right_brace(void) tprint_esc("right"); break; } - help5("I've deleted a group-closing symbol because it seems to be", - "spurious, as in `$x}$'. But perhaps the } is legitimate and", - "you forgot something else, as in `\\hbox{$x}'. In such cases", - "the way to recover is to insert both the forgotten and the", - "deleted material, e.g., by typing `I$}'."); + help5( + "I've deleted a group-closing symbol because it seems to be", + "spurious, as in `$x}$'. But perhaps the } is legitimate and", + "you forgot something else, as in `\\hbox{$x}'. In such cases", + "the way to recover is to insert both the forgotten and the", + "deleted material, e.g., by typing `I$}'." + ); error(); incr(align_state); } -@ Here is where we clear the parameters that are supposed to revert to their +/*tex + +Here is where we clear the parameters that are supposed to revert to their default values after every paragraph and when internal vertical mode is entered. -@c +*/ + void normal_paragraph(void) { if (looseness_par != 0) @@ -1323,27 +1605,35 @@ void normal_paragraph(void) eq_word_define(dimen_base + shape_mode_code, 0); } -@ The global variable |cur_box| will point to a newly-made box. If the box -is void, we will have |cur_box=null|. Otherwise we will have -|type(cur_box)=hlist_node| or |vlist_node| or |rule_node|; the |rule_node| -case can occur only with leaders. +/*tex + +The global variable |cur_box| will point to a newly-made box. If the box is void, +we will have |cur_box=null|. Otherwise we will have |type(cur_box)=hlist_node| or +|vlist_node| or |rule_node|; the |rule_node| case can occur only with leaders. + +*/ -@c halfword cur_box; /* box to be placed into its context */ -@ The |box_end| procedure does the right thing with |cur_box|, if -|box_context| represents the context as explained above. +/*tex + +The |box_end| procedure does the right thing with |cur_box|, if |box_context| +represents the context as explained above. + +*/ -@c void box_end(int box_context) { if (box_context < box_flag) { - /* Append box |cur_box| to the current list, shifted by |box_context| */ - /* - The global variable |adjust_tail| will be non-null if and only if the - current box might include adjustments that should be appended to the - current vertical list. - */ + /*tex + + Append box |cur_box| to the current list, shifted by |box_context|. + The global variable |adjust_tail| will be non-null if and only if the + current box might include adjustments that should be appended to the + current vertical list. + + */ + if (cur_box != null) { shift_amount(cur_box) = box_context; if (abs(mode) == vmode) { @@ -1372,15 +1662,28 @@ void box_end(int box_context) } } } else if (box_context < ship_out_flag) { - /* Store |cur_box| in a box register */ + /*tex + Store |cur_box| in a box register + */ if (box_context < global_box_flag) eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box); else geq_define(box_base + box_context - global_box_flag, box_ref_cmd, cur_box); + } else if (box_context == lua_scan_flag) { + /*tex + We are done with scanning so let's return to the caller. + */ + wrapup_local_scan_box(); } else if (cur_box != null) { - if (box_context > ship_out_flag) { - /* Append a new leader node that uses |cur_box| */ - /* Get the next non-blank non-relax... */ + /*tex + The leaders contexts come after shipout and luascan contexts. + */ + /* if (box_context > lua_scan_flag) { */ + if (box_context >= leader_flag) { + /*tex + Append a new leader node that uses |cur_box| and get the next + non-blank non-relax... + */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); @@ -1391,25 +1694,32 @@ void box_end(int box_context) leader_ptr(tail) = cur_box; } else { print_err("Leaders not followed by proper glue"); - help3 - ("You should say `\\leaders <box or rule><hskip or vskip>'.", - "I found the <box or rule>, but there's no suitable", - "<hskip or vskip>, so I'm ignoring these leaders."); + help3( + "You should say `\\leaders <box or rule><hskip or vskip>'.", + "I found the <box or rule>, but there's no suitable", + "<hskip or vskip>, so I'm ignoring these leaders." + ); back_error(); flush_node_list(cur_box); } } else { + if (box_context != ship_out_flag) { + normal_error("scanner","shipout expected"); + } ship_out(static_pdf, cur_box, SHIPPING_PAGE); } } } -@ the next input should specify a box or perhaps a rule +/*tex + +the next input should specify a box or perhaps a rule + +*/ -@c void scan_box(int box_context) { - /* Get the next non-blank non-relax... */ + /*tex Get the next non-blank non-relax... */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); @@ -1422,26 +1732,36 @@ void scan_box(int box_context) box_end(box_context); } else { print_err("A <box> was supposed to be here"); - help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or", - "something like that. So you might find something missing in", - "your output. But keep trying; you can fix this later."); + help3( + "I was expecting to see \\hbox or \\vbox or \\copy or \\box or", + "something like that. So you might find something missing in", + "your output. But keep trying; you can fix this later." + ); back_error(); + if (box_context == lua_scan_flag) { + cur_box = null; + box_end(box_context); + } } } -@ @c void new_graf(boolean indented) { halfword p, q, dir_graf_tmp; halfword dir_rover; - prev_graf_par = 0; + int callback_id; if ((mode == vmode) || (head != tail)) { tail_append(new_param_glue(par_skip_code)); } + callback_id = callback_defined(new_graf_callback); + if (callback_id > 0) { + run_callback(callback_id, "db->b", cur_list.mode_field,indented,&indented); + } + prev_graf_par = 0; push_nest(); mode = hmode; space_factor_par = 1000; - /* LOCAL: Add local paragraph node */ + /*tex Add local paragraph node */ tail_append(make_local_par_node(new_graf_par_code)); if (indented) { p = new_null_box(); @@ -1470,15 +1790,16 @@ void new_graf(boolean indented) begin_token_list(every_par_par, every_par_text); if (nest_ptr == 1) { checked_page_filter(new_graf); - build_page(); /* put |par_skip| glue on current page */ + /*tex put |par_skip| glue on current page */ + build_page(); } } -@ @c void indent_in_hmode(void) { halfword p; - if (cur_chr > 0) { /* \.{\\indent} */ + if (cur_chr > 0) { + /*tex \.{\\indent} */ p = new_null_box(); width(p) = par_indent_par; if (abs(mode) == hmode) @@ -1489,7 +1810,6 @@ void indent_in_hmode(void) } } -@ @c void head_for_vmode(void) { if (mode < 0) { @@ -1497,8 +1817,10 @@ void head_for_vmode(void) off_save(); } else { print_err("You can't use `\\hrule' here except with leaders"); - help2("To put a horizontal rule in an hbox or an alignment,", - "you should use \\leaders or \\hrulefill (see The TeXbook)."); + help2( + "To put a horizontal rule in an hbox or an alignment,", + "you should use \\leaders or \\hrulefill (see The TeXbook)." + ); error(); } } else { @@ -1509,19 +1831,23 @@ void head_for_vmode(void) } } -@ TODO (BUG?): |dir_save| would have been set by |line_break| by means -of |post_line_break|, but this is not done right now, as it introduces -pretty heavy memory leaks. This means the current code is probably -wrong in some way that relates to in-paragraph displays. +/*tex + +|dir_save| would have been set by |line_break| by means of |post_line_break|, but +this is not done right now, as it introduces pretty heavy memory leaks. This +means the current code might be wrong in some way that relates to in-paragraph +displays. + +*/ -@c void end_graf(int line_break_context) { if (mode == hmode) { if ((head == tail) || (vlink(head) == tail)) { if (vlink(head) == tail) flush_node(vlink(head)); - pop_nest(); /* null paragraphs are ignored, all contain a |local_paragraph| node */ + /*tex |null| paragraphs are ignored, all contain a |local_paragraph| node */ + pop_nest(); } else { line_break(false, line_break_context); } @@ -1534,7 +1860,6 @@ void end_graf(int line_break_context) } } -@ @c void begin_insert_or_adjust(void) { if (cur_cmd != vadjust_cmd) { @@ -1542,7 +1867,9 @@ void begin_insert_or_adjust(void) if (cur_val == output_box_par) { print_err("You can't \\insert"); print_int(output_box_par); - help1("I'm changing to \\insert0; box \\outputbox is special."); + help1( + "I'm changing to \\insert0; box \\outputbox is special." + ); error(); cur_val = 0; } @@ -1561,12 +1888,14 @@ void begin_insert_or_adjust(void) prev_depth_par = ignore_depth; } -@ I (TH)'ve renamed the |make_mark| procedure to this, because if the -current chr code is 1, then the actual command was \.{\\clearmarks}, -which does not generate a mark node but instead destroys the current -mark tokenlists. +/*tex + +I (TH)'ve renamed the |make_mark| procedure to this, because if the current chr +code is 1, then the actual command was \.{\\clearmarks}, which does not generate +a mark node but instead destroys the current mark tokenlists. + +*/ -@c void handle_mark(void) { halfword p; /* new node */ @@ -1597,7 +1926,6 @@ void handle_mark(void) } } -@ @c void append_penalty(void) { scan_int(); @@ -1608,29 +1936,34 @@ void append_penalty(void) } } -@ When |delete_last| is called, |cur_chr| is the |type| of node that -will be deleted, if present. +/*tex + +When |delete_last| is called, |cur_chr| is the |type| of node that will be +deleted, if present. -The |remove_item| command removes a penalty, kern, or glue node if it -appears at the tail of the current list, using a brute-force linear scan. -Like \.{\\lastbox}, this command is not allowed in vertical mode (except -internal vertical mode), since the current list in vertical mode is sent -to the page builder. But if we happen to be able to implement it in -vertical mode, we do. +The |remove_item| command removes a penalty, kern, or glue node if it appears at +the tail of the current list, using a brute-force linear scan. Like +\.{\\lastbox}, this command is not allowed in vertical mode (except internal +vertical mode), since the current list in vertical mode is sent to the page +builder. But if we happen to be able to implement it in vertical mode, we do. + +*/ -@c void delete_last(void) { halfword p, q; /* run through the current list */ if ((mode == vmode) && (tail == head)) { - /* Apologize for inability to do the operation now, - unless \.{\\unskip} follows non-glue */ + /*tex + Apologize for inability to do the operation now, unless \.{\\unskip} + follows non-glue + */ if ((cur_chr != glue_node) || (last_glue != max_halfword)) { you_cant(); if (cur_chr == kern_node) { - help2 - ("Sorry...I usually can't take things from the current page.", - "Try `I\\kern-\\lastkern' instead."); + help2( + "Sorry...I usually can't take things from the current page.", + "Try `I\\kern-\\lastkern' instead." + ); } else if (cur_chr != glue_node) { help2 ("Sorry...I usually can't take things from the current page.", @@ -1643,7 +1976,7 @@ void delete_last(void) error(); } } else { - /* todo: clean this up */ + /*tex Todo: clean this up! */ if (!is_char_node(tail)) { if (type(tail) == cur_chr) { q = head; @@ -1665,7 +1998,6 @@ void delete_last(void) } } -@ @c void unpackage(void) { halfword p; /* the box */ @@ -1673,7 +2005,7 @@ void unpackage(void) int c; /* should we copy? */ halfword s; /* for varmem assignment */ if (cur_chr > copy_code) { - /* Handle saved items and |goto done| */ + /*tex Handle saved items and |goto done| */ try_couple_nodes(tail, disc_ptr[cur_chr]); disc_ptr[cur_chr] = null; goto DONE; @@ -1687,9 +2019,11 @@ void unpackage(void) || ((abs(mode) == vmode) && (type(p) != vlist_node)) || ((abs(mode) == hmode) && (type(p) != hlist_node))) { print_err("Incompatible list can't be unboxed"); - help3("Sorry, Pandora. (You sneaky devil.)", - "I refuse to unbox an \\hbox in vertical mode or vice versa.", - "And I can't open any boxes in math mode."); + help3( + "Sorry, Pandora. (You sneaky devil.)", + "I refuse to unbox an \\hbox in vertical mode or vice versa.", + "And I can't open any boxes in math mode." + ); error(); return; } @@ -1713,12 +2047,14 @@ void unpackage(void) } } -@ +/*tex + Italic corrections are converted to kern nodes when the |ital_corr| command -follows a character. In math mode the same effect is achieved by appending -a kern of zero here, since italic corrections are supplied later. +follows a character. In math mode the same effect is achieved by appending a kern +of zero here, since italic corrections are supplied later. + +*/ -@c void append_italic_correction(void) { halfword p; /* |char_node| at the tail of the current list */ @@ -1734,7 +2070,6 @@ void append_italic_correction(void) } } -@ @c void append_local_box(int kind) { incr(save_ptr); @@ -1746,20 +2081,23 @@ void append_local_box(int kind) space_factor_par = 1000; } -@ Discretionary nodes are easy in the common case `\.{\\-}', but in the -general case we must process three braces full of items. +/*tex + +Discretionary nodes are easy in the common case `\.{\\-}', but in the general +case we must process three braces full of items. + +The space factor does not change when we append a discretionary node, but it +starts out as 1000 in the subsidiary lists. -The space factor does not change when we append a discretionary node, -but it starts out as 1000 in the subsidiary lists. +*/ -@c void append_discretionary(void) { int c; tail_append(new_disc()); subtype(tail) = (quarterword) cur_chr; if (cur_chr == explicit_disc) { - /* \- */ + /* |\-| */ c = get_pre_hyphen_char(cur_lang_par); if (c > 0) { vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c); @@ -1774,7 +2112,7 @@ void append_discretionary(void) } set_explicit_disc_penalty(tail); } else if (cur_chr == automatic_disc) { - /* - as done in hyphenator */ + /*tex As done in hyphenator: */ c = get_pre_exhyphen_char(cur_lang_par); if (c <= 0) { c = ex_hyphen_char_par; @@ -1798,7 +2136,7 @@ void append_discretionary(void) } set_automatic_disc_penalty(tail); } else { - /* \discretionary */ + /*tex |\discretionary| */ if (scan_keyword("penalty")) { scan_int(); disc_penalty(tail) = cur_val; @@ -1810,14 +2148,17 @@ void append_discretionary(void) push_nest(); mode = -hmode; space_factor_par = 1000; - /* already preset: disc_penalty(tail) = hyphen_penalty_par; */ + /*tex Already preset: |disc_penalty(tail) = hyphen_penalty_par;| */ } } -@ The test for |p != null| ensures that empty \.{\\localleftbox} and - \.{\\localrightbox} commands are not applied. +/*tex + +The test for |p != null| ensures that empty \.{\\localleftbox} and +\.{\\localrightbox} commands are not applied. + +*/ -@c void build_local_box(void) { halfword p; @@ -1828,41 +2169,51 @@ void build_local_box(void) decr(save_ptr); p = vlink(head); pop_nest(); - if (p != null) - p = hpack(p, 0, additional, -1); + if (p != null) { + /*tex Somehow |filtered_hpack| goes beyond the first node so we loose it. */ + new_hyphenation(p, null); + (void) new_ligkern(p, null); + p = lua_hpack_filter(p, 0, additional, local_box_group, -1, null); + } if (kind == 0) eq_define(local_left_box_base, box_ref_cmd, p); else eq_define(local_right_box_base, box_ref_cmd, p); if (abs(mode) == hmode) { - /* LOCAL: Add local paragraph node */ + /*tex Add local paragraph node */ tail_append(make_local_par_node(local_box_par_code)); } eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); } -@ The three discretionary lists are constructed somewhat as if they were -hboxes. A~subroutine called |build_discretionary| handles the transitions. -(This is sort of fun.) +/*tex + +The three discretionary lists are constructed somewhat as if they were hboxes. +A~subroutine called |build_discretionary| handles the transitions. (This is sort +of fun.) + +*/ -@c void build_discretionary(void) { halfword p, q; /* for link manipulation */ int n; /* length of discretionary list */ unsave(); - /* Prune the current list, if necessary, until it contains only - |char_node|, |kern_node|, |hlist_node|, |vlist_node| and - |rule_node| items; set |n| to the length of the list, - and set |q| to the lists tail */ - /* During this loop, |p=vlink(q)| and there are |n| items preceding |p|. */ + /*tex + Prune the current list, if necessary, until it contains only |char_node|, + |kern_node|, |hlist_node|, |vlist_node| and |rule_node| items; set |n| to + the length of the list, and set |q| to the lists tail. During this loop, + |p=vlink(q)| and there are |n| items preceding |p|. + */ q = head; p = vlink(q); n = 0; while (p != null) { if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) { print_err("Improper discretionary list"); - help1("Discretionary lists must contain only boxes and kerns."); + help1( + "Discretionary lists must contain only boxes and kerns." + ); error(); begin_diagnostic(); tprint_nl("The following discretionary sublist has been deleted:"); @@ -1897,12 +2248,16 @@ void build_discretionary(void) } break; case 2: - /* Attach list |p| to the current list, and record its length; - then finish up and |return| */ + /*tex + Attach list |p| to the current list, and record its length; then + finish up and |return| + */ if ((n > 0) && (abs(mode) == mmode)) { print_err("Illegal math \\discretionary"); - help2("Sorry: The third part of a discretionary break must be", - "empty, in math formulas. I had to delete your third part."); + help2( + "Sorry: The third part of a discretionary break must be", + "empty, in math formulas. I had to delete your third part." + ); flush_node_list(p); error(); } else { @@ -1913,9 +2268,10 @@ void build_discretionary(void) } } decr(save_ptr); + /*tex There are no other cases. */ return; break; - } /* there are no other cases */ + } set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1)); new_save_level(disc_group); scan_left_brace(); @@ -1924,15 +2280,18 @@ void build_discretionary(void) space_factor_par = 1000; } -@ The positioning of accents is straightforward but tedious. Given an accent -of width |a|, designed for characters of height |x| and slant |s|; -and given a character of width |w|, height |h|, and slant |t|: We will shift -the accent down by |x-h|, and we will insert kern nodes that have the effect of -centering the accent over the character and shifting the accent to the -right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is -absent from the font, we will simply use the other, without shifting. +/*tex + +The positioning of accents is straightforward but tedious. Given an accent of +width |a|, designed for characters of height |x| and slant |s|; and given a +character of width |w|, height |h|, and slant |t|: We will shift the accent down +by |x-h|, and we will insert kern nodes that have the effect of centering the +accent over the character and shifting the accent to the right by +$\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is absent from the +font, we will simply use the other, without shifting. + +*/ -@c void make_accent(void) { double s, t; /* amount of slant */ @@ -1944,11 +2303,14 @@ void make_accent(void) p = new_glyph(f, cur_val); if (p != null) { x = x_height(f); - s = float_cast(slant(f)) / float_constant(65536); /* real division */ + /*tex real division */ + s = float_cast(slant(f)) / float_constant(65536); a = glyph_width(p); do_assignments(); - /* Create a character node |q| for the next character, - but set |q:=null| if problems arise */ + /*tex + Create a character node |q| for the next character, but set |q:=null| + if problems arise + */ q = null; f = equiv(cur_font_loc); if ((cur_cmd == letter_cmd) || @@ -1962,22 +2324,26 @@ void make_accent(void) } if (q != null) { - /* Append the accent with appropriate kerns, then set |p:=q| */ - /* The kern nodes appended here must be distinguished from other kerns, lest - they be wiped away by the hyphenation algorithm or by a previous line break. - - The two kerns are computed with (machine-dependent) |real| arithmetic, but - their sum is machine-independent; the net effect is machine-independent, - because the user cannot remove these nodes nor access them via \.{\\lastkern}. + /*tex + Append the accent with appropriate kerns, then set |p:=q|. The + kern nodes appended here must be distinguished from other kerns, + lest they be wiped away by the hyphenation algorithm or by a + previous line break. The two kerns are computed with + (machine-dependent) |real| arithmetic, but their sum is + machine-independent; the net effect is machine-independent, + because the user cannot remove these nodes nor access them via + \.{\\lastkern}. */ t = float_cast(slant(f)) / float_constant(65536); /* real division */ w = glyph_width(q); h = glyph_height(q); - if (h != x) { /* the accent must be shifted up or down */ + if (h != x) { + /*tex the accent must be shifted up or down */ p = hpack(p, 0, additional, -1); shift_amount(p) = x - h; } - delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); /* real multiplication */ + /*tex real multiplication */ + delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); r = new_kern(delta); subtype(r) = accent_kern; couple_nodes(tail, r); @@ -1994,34 +2360,43 @@ void make_accent(void) } } -@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner -into |main_control|, it might be that the user has foolishly inserted -one of them into something that has nothing to do with alignment. But it is -far more likely that a left brace or right brace has been omitted, since -|get_next| takes actions appropriate to alignment only when `\.{\\cr}' -or `\.{\\span}' or tab marks occur with |align_state=0|. The following -program attempts to make an appropriate recovery. +/*tex + +When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner into +|main_control|, it might be that the user has foolishly inserted one of them into +something that has nothing to do with alignment. But it is far more likely that a +left brace or right brace has been omitted, since |get_next| takes actions +appropriate to alignment only when `\.{\\cr}' or `\.{\\span}' or tab marks occur +with |align_state=0|. The following program attempts to make an appropriate +recovery. + +*/ -@c void align_error(void) { if (abs(align_state) > 2) { - /* Express consternation over the fact that no alignment is in progress */ + /*tex + Express consternation over the fact that no alignment is in progress. + */ print_err("Misplaced "); print_cmd_chr((quarterword) cur_cmd, cur_chr); if (cur_tok == tab_token + '&') { - help6("I can't figure out why you would want to use a tab mark", - "here. If you just want an ampersand, the remedy is", - "simple: Just type `I\\&' now. But if some right brace", - "up above has ended a previous alignment prematurely,", - "you're probably due for more error messages, and you", - "might try typing `S' now just to see what is salvageable."); + help6( + "I can't figure out why you would want to use a tab mark", + "here. If you just want an ampersand, the remedy is", + "simple: Just type `I\\&' now. But if some right brace", + "up above has ended a previous alignment prematurely,", + "you're probably due for more error messages, and you", + "might try typing `S' now just to see what is salvageable." + ); } else { - help5("I can't figure out why you would want to use a tab mark", - "or \\cr or \\span just now. If something like a right brace", - "up above has ended a previous alignment prematurely,", - "you're probably due for more error messages, and you", - "might try typing `S' now just to see what is salvageable."); + help5( + "I can't figure out why you would want to use a tab mark", + "or \\cr or \\span just now. If something like a right brace", + "up above has ended a previous alignment prematurely,", + "you're probably due for more error messages, and you", + "might try typing `S' now just to see what is salvageable." + ); } error(); @@ -2036,43 +2411,55 @@ void align_error(void) decr(align_state); cur_tok = right_brace_token + '}'; } - help3("I've put in what seems to be necessary to fix", - "the current column of the current alignment.", - "Try to go on, since this might almost work."); + help3( + "I've put in what seems to be necessary to fix", + "the current column of the current alignment.", + "Try to go on, since this might almost work." + ); ins_error(); } } -@ The help messages here contain a little white lie, since \.{\\noalign} -and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'. +/*tex + +The help messages here contain a little white lie, since \.{\\noalign} and +\.{\\omit} are allowed also after `\.{\\noalign\{...\}}'. + +*/ -@c void no_align_error(void) { print_err("Misplaced \\noalign"); - help2("I expect to see \\noalign only after the \\cr of", - "an alignment. Proceed, and I'll ignore this case."); + help2( + "I expect to see \\noalign only after the \\cr of", + "an alignment. Proceed, and I'll ignore this case." + ); error(); } void omit_error(void) { print_err("Misplaced \\omit"); - help2("I expect to see \\omit only after tab marks or the \\cr of", - "an alignment. Proceed, and I'll ignore this case."); + help2( + "I expect to see \\omit only after tab marks or the \\cr of", + "an alignment. Proceed, and I'll ignore this case." + ); error(); } -@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}. -Let's take a look at what happens when they are used correctly. +/*tex + +We've now covered most of the abuses of \.{\\halign} and \.{\\valign}. Let's take +a look at what happens when they are used correctly. -An |align_group| code is supposed to remain on the |save_stack| -during an entire alignment, until |fin_align| removes it. +An |align_group| code is supposed to remain on the |save_stack| during an entire +alignment, until |fin_align| removes it. -A devious user might force an |endv| command to occur just about anywhere; -we must defeat such hacks. +A devious user might force an |endv| command to occur just about anywhere; we +must defeat such hacks. + +*/ -@c void do_endv(void) { base_ptr = input_ptr; @@ -2085,7 +2472,7 @@ void do_endv(void) (input_stack[base_ptr].loc_field != null) || (input_stack[base_ptr].state_field != token_list)) fatal_error("(interwoven alignment preambles are not allowed)"); - /*.interwoven alignment preambles... */ + /*tex interwoven alignment preambles... */ if (cur_group == align_group) { end_graf(align_group); if (fin_col()) @@ -2095,55 +2482,62 @@ void do_endv(void) } } -@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|. +/*tex + +Finally, \.{\\endcsname} is not supposed to get through to |main_control|. + +*/ -@c void cs_error(void) { print_err("Extra \\endcsname"); - help1("I'm ignoring this, since I wasn't doing a \\csname."); + help1( + "I'm ignoring this, since I wasn't doing a \\csname." + ); error(); } -@ - Assignments to values in |eqtb| can be global or local. Furthermore, a - control sequence can be defined to be `\.{\\long}', `\.{\\protected}', - or `\.{\\outer}', and it might or might not be expanded. The prefixes - `\.{\\global}', `\.{\\long}', `\.{\\protected}', - and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric - codes, making it possible to accumulate the union of all specified prefixes - by adding the corresponding codes. (PASCAL's |set| operations could also - have been used.) +/*tex + +Assignments to values in |eqtb| can be global or local. Furthermore, a control +sequence can be defined to be `\.{\\long}', `\.{\\protected}', or `\.{\\outer}', +and it might or might not be expanded. The prefixes `\.{\\global}', `\.{\\long}', +`\.{\\protected}', and `\.{\\outer}' can occur in any order. Therefore we assign +binary numeric codes, making it possible to accumulate the union of all specified +prefixes by adding the corresponding codes. (PASCAL's |set| operations could also +have been used.) - Every prefix, and every command code that might or might not be prefixed, - calls the action procedure |prefixed_command|. This routine accumulates - a sequence of prefixes until coming to a non-prefix, then it carries out - the command. +Every prefix, and every command code that might or might not be prefixed, calls +the action procedure |prefixed_command|. This routine accumulates a sequence of +prefixes until coming to a non-prefix, then it carries out the command. -@ If the user says, e.g., `\.{\\global\\global}', the redundancy is -silently accepted. +*/ +/*tex -@ The different types of code values have different legal ranges; the +If the user says, e.g., `\.{\\global\\global}', the redundancy is silently +accepted. The different types of code values have different legal ranges; the following program is careful to check each case properly. -@c -#define check_def_code(A) do { \ - if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \ - print_err("Invalid code ("); \ - print_int(cur_val); \ - if (p<(A)) \ - tprint("), should be in the range 0.."); \ - else \ - tprint("), should be at most "); \ - print_int(n); \ - help1("I'm going to use 0 instead of that illegal code value."); \ - error(); \ - cur_val=0; \ - } \ +*/ + +#define check_def_code(A) do { \ + if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \ + print_err("Invalid code ("); \ + print_int(cur_val); \ + if (p<(A)) \ + tprint("), should be in the range 0.."); \ + else \ + tprint("), should be at most "); \ + print_int(n); \ + help1( \ + "I'm going to use 0 instead of that illegal code value." \ + ); \ + error(); \ + cur_val=0; \ + } \ } while (0) -@ @c /* halfword swap_hang_indent(halfword indentation, halfword shape_mode) { if (shape_mode == 1 || shape_mode == 3 || shape_mode == -1 || shape_mode == -3) { @@ -2176,40 +2570,51 @@ void prefixed_command(void) while (cur_cmd == prefix_cmd) { if (!odd(a / cur_chr)) a = a + cur_chr; - /* Get the next non-blank non-relax... */ + /*tex + Get the next non-blank non-relax... + */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); if (cur_cmd <= max_non_prefixed_command) { - /* Discard erroneous prefixes and |return| */ + /*tex + Discard erroneous prefixes and |return| + */ print_err("You can't use a prefix with `"); print_cmd_chr((quarterword) cur_cmd, cur_chr); print_char('\''); - help2 - ("I'll pretend you didn't say \\long or \\outer or \\global or", - "\\protected."); + help2( + "I'll pretend you didn't say \\long or \\outer or \\global or", + "\\protected." + ); back_error(); return; } if (tracing_commands_par > 2) show_cur_cmd_chr(); } - /* Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant */ + /*tex + Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant + */ if (a >= 8) { j = protected_token; a = a - 8; } else { j = 0; } - if ((cur_cmd != def_cmd) && ((a % 4 != 0) || (j != 0))) { + if ((cur_cmd != def_cmd) && (cur_cmd != def_lua_call_cmd) && ((a % 4 != 0) || (j != 0))) { print_err("You can't use `\\long' or `\\outer' or `\\protected' with `"); print_cmd_chr((quarterword) cur_cmd, cur_chr); print_char('\''); - help1("I'll pretend you didn't say \\long or \\outer or \\protected here."); + help1( + "I'll pretend you didn't say \\long or \\outer or \\protected here." + ); error(); } - /* Adjust for the setting of \.{\\globaldefs} */ + /*tex + Adjust for the setting of \.{\\globaldefs} + */ if (global_defs_par != 0) { if (global_defs_par < 0) { if (is_global(a)) @@ -2221,15 +2626,18 @@ void prefixed_command(void) } switch (cur_cmd) { case set_font_cmd: - /* Here's an example of the way many of the following routines operate. - (Unfortunately, they aren't all as simple as this.) */ + /*tex + Here's an example of the way many of the following routines operate. + (Unfortunately, they aren't all as simple as this.) + */ define(cur_font_loc, data_cmd, cur_chr); break; case def_cmd: - /* When a |def| command has been scanned, - |cur_chr| is odd if the definition is supposed to be global, and - |cur_chr>=2| if the definition is supposed to be expanded. */ - + /*tex + When a |def| command has been scanned, |cur_chr| is odd if the + definition is supposed to be global, and |cur_chr>=2| if the + definition is supposed to be expanded. + */ if (odd(cur_chr) && !is_global(a) && (global_defs_par >= 0)) a = a + 4; e = (cur_chr >= 2); @@ -2246,35 +2654,15 @@ void prefixed_command(void) break; case let_cmd: n = cur_chr; - if (n == normal) { - get_r_token(); - p = cur_cs; - do { - get_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '=') { - get_token(); - if (cur_cmd == spacer_cmd) - get_token(); - } - } else if (n == normal + 1) { - /* futurelet */ - get_r_token(); - p = cur_cs; - get_token(); - q = cur_tok; - get_token(); - back_input(); - cur_tok = q; - /* look ahead, then back up */ - /* note that |back_input| doesn't affect |cur_cmd|, |cur_chr| */ - back_input(); - } else { - /* letcharcode */ - scan_int(); - if (cur_val > 0) { - cur_cs = active_to_cs(cur_val, true); - set_token_info(cur_cs, cur_cs + cs_token_flag); + switch (n) { + case 0: + /*tex |glet| */ + if (!is_global(a) && (global_defs_par >= 0)) { + a = a + 4; + } + case 1: + /*tex |let| */ + get_r_token(); p = cur_cs; do { get_token(); @@ -2284,21 +2672,60 @@ void prefixed_command(void) if (cur_cmd == spacer_cmd) get_token(); } - } else { + break; + case 2: + /*tex |futurelet| */ + get_r_token(); + p = cur_cs; + get_token(); + q = cur_tok; + get_token(); + back_input(); + cur_tok = q; + /*tex + We look ahead and then back up. Note that |back_input| doesn't + affect |cur_cmd|, |cur_chr| + */ + back_input(); + break; + case 3: + /*tex |letcharcode| */ + scan_int(); + if (cur_val > 0) { + cur_cs = active_to_cs(cur_val, true); + set_token_info(cur_cs, cur_cs + cs_token_flag); + p = cur_cs; + do { + get_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '=') { + get_token(); + if (cur_cmd == spacer_cmd) + get_token(); + } + } else { + p = null; + tex_error("invalid number for \\letcharcode",NULL); + } + break; + default: + /*tex We please the compiler. */ p = null; - tex_error("invalid number for \\letcharcode",NULL); - } + confusion("let"); + break; } if (cur_cmd >= call_cmd) add_token_ref(cur_chr); define(p, cur_cmd, cur_chr); break; case shorthand_def_cmd: - /* We temporarily define |p| to be |relax|, so that an occurrence of |p| - while scanning the definition will simply stop the scanning instead of - producing an ``undefined control sequence'' error or expanding the - previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'. - */ + /*tex + We temporarily define |p| to be |relax|, so that an occurrence of + |p| while scanning the definition will simply stop the scanning + instead of producing an ``undefined control sequence'' error or + expanding the previous meaning. This allows, for instance, + `\.{\\chardef\\foo=123\\foo}'. + */ n = cur_chr; get_r_token(); p = cur_cs; @@ -2311,13 +2738,13 @@ void prefixed_command(void) break; case math_char_def_code: mval = scan_mathchar(tex_mathcode); - if (math_umathcode_meaning_par == 1) { - cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; - define(p, xmath_given_cmd, cur_val); - } else { + /* if (math_umathcode_meaning_par == 1) { */ + /* cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; */ + /* define(p, xmath_given_cmd, cur_val); */ + /* } else { */ cur_val = (mval.class_value * 16 + mval.family_value) * 256 + mval.character_value; define(p, math_given_cmd, cur_val); - } + /* } */ break; case xmath_char_def_code: mval = scan_mathchar(umath_mathcode); @@ -2363,8 +2790,10 @@ void prefixed_command(void) n = cur_val; if (!scan_keyword("to")) { print_err("Missing `to' inserted"); - help2("You should have said `\\read<number> to \\cs'.", - "I'm going to look for the \\cs now."); + help2( + "You should have said `\\read<number> to \\cs'.", + "I'm going to look for the \\cs now." + ); error(); } get_r_token(); @@ -2374,25 +2803,30 @@ void prefixed_command(void) break; case toks_register_cmd: case assign_toks_cmd: - /* The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive - their values in the following way. (For safety's sake, we place an - enclosing pair of braces around an \.{\\output} list.) */ + /*tex + The token-list parameters, \.{\\output} and \.{\\everypar}, etc., + receive their values in the following way. (For safety's sake, we + place an enclosing pair of braces around an \.{\\output} list.) + */ q = cur_cs; if (cur_cmd == toks_register_cmd) { scan_register_num(); p = toks_base + cur_val; } else { - p = cur_chr; /* |p=every_par_loc| or |output_routine_loc| or \dots */ + /*tex |p=every_par_loc| or |output_routine_loc| or \dots */ + p = cur_chr; } scan_optional_equals(); - /* Get the next non-blank non-relax non-call token */ + /*tex Get the next non-blank non-relax non-call token */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); if (cur_cmd != left_brace_cmd) { - /* If the right-hand side is a token parameter - or token register, finish the assignment and |goto done| */ + /*tex + If the right-hand side is a token parameter or token + register, finish the assignment and |goto done| + */ if (cur_cmd == toks_register_cmd) { scan_register_num(); cur_cmd = assign_toks_cmd; @@ -2431,7 +2865,7 @@ void prefixed_command(void) } break; case assign_int_cmd: - /* Similar routines are used to assign values to the numeric parameters. */ + /*tex Similar routines are used to assign values to the numeric parameters. */ p = cur_chr; scan_optional_equals(); scan_int(); @@ -2446,9 +2880,18 @@ void prefixed_command(void) attr_list_cache = cache_disabled; word_define(p, cur_val); break; + case assign_direction_cmd: case assign_dir_cmd: - /* DIR: Assign direction codes */ - scan_direction(); + /*tex Assign direction codes. */ + if (cur_cmd == assign_direction_cmd) { + p = cur_chr; + scan_optional_equals(); + scan_int(); + check_dir_value(cur_val); + cur_chr = p; + } else { + scan_direction(); + } switch (cur_chr) { case int_base + page_direction_code: eq_word_define(int_base + page_direction_code, cur_val); @@ -2487,17 +2930,19 @@ void prefixed_command(void) } if (abs(mode) == hmode) { if (no_local_dirs_par > 0) { - /* tail is non zero but we test anyway */ + /*tex |tail| is non zero but we test anyway. */ if (check_glue && (tail != null && type(tail) == glue_node)) { halfword prev = alink(tail); - halfword dirn = new_dir(text_direction_par - dir_swap); + halfword dirn = new_dir(text_direction_par); + subtype(dirn) = cancel_dir; couple_nodes(prev,dirn); couple_nodes(dirn,tail); } else { - tail_append(new_dir(text_direction_par - dir_swap)); + tail_append(new_dir(text_direction_par)); + subtype(tail) = cancel_dir; } } else { - /* what is the use of nolocaldirs .. maybe we should get rid of it */ + /*tex What is the use of nolocaldirs? Maybe we should get rid of it. */ } update_text_dir_ptr(cur_val); tail_append(new_dir(cur_val)); @@ -2505,19 +2950,6 @@ void prefixed_command(void) } else { update_text_dir_ptr(cur_val); } - /* original: - - // if ((no_local_dirs_par > 0) && (abs(mode) == hmode)) { - // // tail_append(new_dir(text_direction_par) // kind of wrong - // tail_append(new_dir(text_direction_par - dir_swap)); // better - // } - - update_text_dir_ptr(cur_val); - if (abs(mode) == hmode) { - tail_append(new_dir(cur_val)); - dir_level(tail) = cur_level; - } - */ eq_word_define(int_base + text_direction_code, cur_val); eq_word_define(int_base + no_local_dirs_code, no_local_dirs_par + 1); break; @@ -2542,14 +2974,13 @@ void prefixed_command(void) break; case def_char_code_cmd: case def_del_code_cmd: - /* Let |n| be the largest legal code value, based on |cur_chr| */ + /*tex Let |n| be the largest legal code value, based on |cur_chr| */ if (cur_chr == cat_code_base) n = max_char_code; else if (cur_chr == sf_code_base) n = 077777; else n = biggest_char; - p = cur_chr; if (cur_chr == math_code_base) { if (is_global(a)) @@ -2621,8 +3052,9 @@ void prefixed_command(void) get_token(); if (cur_cmd != math_style_cmd) { print_err("Missing math style, treated as \\displaystyle"); - help1 - ("A style should have been here; I inserted `\\displaystyle'."); + help1( + "A style should have been here; I inserted `\\displaystyle'." + ); cur_val1 = display_style; back_error(); } else { @@ -2652,9 +3084,11 @@ void prefixed_command(void) do_register_command(a); break; case set_box_cmd: - /* The processing of boxes is somewhat different, because we may need - to scan and create an entire box before we actually change the value - of the old one. */ + /*tex + The processing of boxes is somewhat different, because we may + need to scan and create an entire box before we actually change + the value of the old one. + */ scan_register_num(); if (is_global(a)) n = global_box_flag + cur_val; @@ -2665,16 +3099,22 @@ void prefixed_command(void) scan_box(n); } else { print_err("Improper \\setbox"); - help2("Sorry, \\setbox is not allowed after \\halign in a display,", - "or between \\accent and an accented character."); + help3( + "Sorry, \\setbox is not allowed after \\halign in a display,", + "between \\accent and an accented character, or in immediate", + "assignments." + ); error(); } break; case set_aux_cmd: - /* The |space_factor| or |prev_depth| settings are changed when a |set_aux| - command is sensed. Similarly, |prev_graf| is changed in the presence of - |set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of - |set_page_int|. These definitions are always global. */ + /*tex + The |space_factor| or |prev_depth| settings are changed when a + |set_aux| command is sensed. Similarly, |prev_graf| is changed in + the presence of |set_prev_graf|, and |dead_cycles| or + |insert_penalties| in the presence of |set_page_int|. These + definitions are always global. + */ alter_aux(); break; case set_prev_graf_cmd: @@ -2687,8 +3127,11 @@ void prefixed_command(void) alter_integer(); break; case set_box_dimen_cmd: - /* When some dimension of a box register is changed, the change isn't exactly - global; but \TeX\ does not look at the \.{\\global} switch. */ + /*tex + When some dimension of a box register is changed, the change + isn't exactly global; but \TeX\ does not look at the \.{\\global} + switch. + */ alter_box_dimen(); break; case set_tex_shape_cmd: @@ -2722,7 +3165,7 @@ void prefixed_command(void) p = new_node(shape_node, 2 * n + 1 + 1); vinfo(p + 1) = n; n = cur_val; - varmem[p + 2].cint = n; /* number of penalties */ + varmem[p + 2].cint = n; /* number of penalties */ for (j = p + 3; j <= p + n + 2; j++) { scan_int(); varmem[j].cint = cur_val; /* penalty values */ @@ -2733,8 +3176,10 @@ void prefixed_command(void) define(q, shape_ref_cmd, p); break; case hyph_data_cmd: - /* All of \TeX's parameters are kept in |eqtb| except the font information, - the interaction mode, and the hyphenation tables; these are strictly global. + /*tex + All of \TeX's parameters are kept in |eqtb| except the font + information, the interaction mode, and the hyphenation tables; + these are strictly global. */ switch (cur_chr) { case 0: @@ -2801,9 +3246,20 @@ void prefixed_command(void) } break; case def_font_cmd: - /* Here is where the information for a new font gets loaded. */ + /*tex Here is where the information for a new font gets loaded. */ tex_def_font((small_number) a); break; + case def_lua_call_cmd: + get_r_token(); + p = cur_cs; + scan_optional_equals(); + scan_int(); + if (j != 0) { + define(p, lua_call_cmd, cur_val); + } else { + define(p, lua_expandable_call_cmd, cur_val); + } + break; case letterspace_font_cmd: new_letterspaced_font((small_number) a); break; @@ -2821,9 +3277,10 @@ void prefixed_command(void) default: confusion("prefix"); break; - } /* end of Assignments cases */ + } + /*tex End of assignments cases. */ DONE: - /* Insert a token saved by \.{\\afterassignment}, if any */ + /*tex Insert a token saved by \.{\\afterassignment}, if any. */ if (after_token != 0) { cur_tok = after_token; back_input(); @@ -2831,14 +3288,13 @@ void prefixed_command(void) } } -@ @c void fixup_directions(void) { int temp_no_whatsits = no_local_whatsits_par; int temp_no_dirs = no_local_dirs_par; int temporary_dir = text_direction_par; if (dir_level(text_dir_ptr) == cur_level) { - /* DIR: Remove from |text_dir_ptr| */ + /* Remove from |text_dir_ptr|. */ halfword text_dir_tmp = vlink(text_dir_ptr); flush_node(text_dir_ptr); text_dir_ptr = text_dir_tmp; @@ -2846,22 +3302,50 @@ void fixup_directions(void) unsave(); if (abs(mode) == hmode) { if (temp_no_dirs != 0) { - /* DIR: Add local dir node */ + /* Add local dir node. */ tail_append(new_dir(text_direction_par)); - dir_dir(tail) = temporary_dir - dir_swap; + dir_dir(tail) = temporary_dir; + subtype(tail) = cancel_dir; } if (temp_no_whatsits != 0) { - /* LOCAL: Add local paragraph node */ + /*tex Add local paragraph node. */ tail_append(make_local_par_node(hmode_par_par_code)); } } } -@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or -something similar, the |get_r_token| routine will substitute a special -control sequence for a token that is not redefinable. +/*tex + + This is experimental and needs more checking! + +*/ + +void fixup_directions_only(void) +{ + int temp_no_dirs = no_local_dirs_par; + int temporary_dir = text_direction_par; + if (dir_level(text_dir_ptr) == cur_level) { + /* Remove from |text_dir_ptr|. */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; + } + if (temp_no_dirs != 0) { + /* Add local dir node. */ + tail_append(new_dir(text_direction_par)); + dir_dir(tail) = temporary_dir; + subtype(tail) = cancel_dir; + } +} + +/*tex + +When a control sequence is to be defined, by \.{\\def} or \.{\\let} or something +similar, the |get_r_token| routine will substitute a special control sequence for +a token that is not redefinable. + +*/ -@c void get_r_token(void) { RESTART: @@ -2871,11 +3355,13 @@ void get_r_token(void) if ((cur_cs == 0) || (cur_cs > eqtb_top) || ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) { print_err("Missing control sequence inserted"); - help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.", - "I've inserted an inaccessible control sequence so that your", - "definition will be completed without mixing me up too badly.", - "You can recover graciously from this error, if you're", - "careful; see exercise 27.2 in The TeXbook."); + help5( + "Please don't say `\\def cs{...}', say `\\def\\cs{...}'.", + "I've inserted an inaccessible control sequence so that your", + "definition will be completed without mixing me up too badly.", + "You can recover graciously from this error, if you're", + "careful; see exercise 27.2 in The TeXbook." + ); if (cur_cs == 0) back_input(); cur_tok = cs_token_flag + frozen_protection; @@ -2884,7 +3370,6 @@ void get_r_token(void) } } -@ @c void assign_internal_value(int a, halfword p, int val) { halfword n; @@ -2896,17 +3381,19 @@ void assign_internal_value(int a, halfword p, int val) word_define(p, val); } else { print_err("Invalid \\catcode table"); - help2 - ("You can only switch to a \\catcode table that is initialized", - "using \\savecatcodetable or \\initcatcodetable, or to table 0"); + help2( + "You can only switch to a \\catcode table that is initialized", + "using \\savecatcodetable or \\initcatcodetable, or to table 0" + ); error(); } break; case output_box_code: if ((val > 65535) | (val < 0)) { print_err("Invalid \\outputbox"); - help1 - ("The value for \\outputbox has to be between 0 and 65535."); + help1( + "The value for \\outputbox has to be between 0 and 65535." + ); error(); } else { word_define(p, val); @@ -2915,9 +3402,10 @@ void assign_internal_value(int a, halfword p, int val) case new_line_char_code: if (val > 127) { print_err("Invalid \\newlinechar"); - help2 - ("The value for \\newlinechar has to be no higher than 127.", - "Your invalid assignment will be ignored."); + help2( + "The value for \\newlinechar has to be no higher than 127.", + "Your invalid assignment will be ignored." + ); error(); } else { word_define(p, val); @@ -2926,9 +3414,10 @@ void assign_internal_value(int a, halfword p, int val) case end_line_char_code: if (val > 127) { print_err("Invalid \\endlinechar"); - help2 - ("The value for \\endlinechar has to be no higher than 127.", - "Your invalid assignment will be ignored."); + help2( + "The value for \\endlinechar has to be no higher than 127.", + "Your invalid assignment will be ignored." + ); error(); } else { word_define(p, val); @@ -2940,9 +3429,10 @@ void assign_internal_value(int a, halfword p, int val) word_define(p, -1); } else if (val > 16383) { print_err("Invalid \\language"); - help2 - ("The absolute value for \\language has to be no higher than 16383.", - "Your invalid assignment will be ignored."); + help2( + "The absolute value for \\language has to be no higher than 16383.", + "Your invalid assignment will be ignored." + ); error(); } else { word_define(int_base + cur_lang_code, val); @@ -2953,15 +3443,17 @@ void assign_internal_value(int a, halfword p, int val) word_define(p, val); break; } - /* If we are defining subparagraph penalty levels while we are - in hmode, then we put out a whatsit immediately, otherwise - we leave it alone. This mechanism might not be sufficiently - powerful, and some other algorithm, searching down the stack, - might be necessary. Good first step. */ + /*tex + If we are defining subparagraph penalty levels while we are in hmode, + then we put out a whatsit immediately, otherwise we leave it alone. + This mechanism might not be sufficiently powerful, and some other + algorithm, searching down the stack, might be necessary. Good first + step. + */ if ((abs(mode) == hmode) && ((p == (int_base + local_inter_line_penalty_code)) || (p == (int_base + local_broken_penalty_code)))) { - /* LOCAL: Add local paragraph node */ + /*tex Add local paragraph node */ tail_append(make_local_par_node(penalty_par_code)); eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); } @@ -2980,20 +3472,23 @@ void assign_internal_value(int a, halfword p, int val) word_define(dimen_base + page_top_offset_code, n); } word_define(p, val); - } else if ((p >= local_base) && (p < toks_base)) { /* internal locals */ + } else if ((p >= local_base) && (p < toks_base)) { + /*tex internal locals */ define(p, call_cmd, val); } else { confusion("assign internal value"); } } -@ We use the fact that |register<advance<multiply<divide| +/*tex -Compute the register location |l| and its type |p|; but |return| if invalid -Here we use the fact that the consecutive codes |int_val..mu_val| and +We use the fact that |register| $<$ |advance| $<$ |multiply| $<$ |divide|/ We +compute the register location |l| and its type |p| but |return| if invalid. Here +we use the fact that the consecutive codes |int_val..mu_val| and |assign_int..assign_mu_glue| correspond to each other nicely. -@c +*/ + void do_register_command(int a) { int p; @@ -3011,7 +3506,9 @@ void do_register_command(int a) print_cmd_chr((quarterword) cur_cmd, cur_chr); tprint("' after "); print_cmd_chr((quarterword) q, 0); - help1("I'm forgetting what you said and not changing anything."); + help1( + "I'm forgetting what you said and not changing anything." + ); error(); return; } @@ -3032,11 +3529,11 @@ void do_register_command(int a) if (q == register_cmd) { scan_optional_equals(); } else if (scan_keyword("by")) { - /* optional `\.{by}' */ + /*tex optional `\.{by}' */ } arith_error = false; if (q < multiply_cmd) { - /* Compute result of |register| or |advance|, put it in |cur_val| */ + /*tex Compute result of |register| or |advance|, put it in |cur_val|. */ if (p < glue_val_level) { if ((p == int_val_level) || (p == attr_val_level)) scan_int(); @@ -3045,7 +3542,6 @@ void do_register_command(int a) if (q == advance_cmd) cur_val = cur_val + eqtb[l].cint; } else { - /* we can probably save a copy */ scan_glue(p); if (q == advance_cmd) { /* Compute the sum of two glue specs */ @@ -3075,7 +3571,7 @@ void do_register_command(int a) } } } else { - /* Compute result of |multiply| or |divide|, put it in |cur_val| */ + /*tex Compute result of |multiply| or |divide|, put it in |cur_val| */ scan_int(); if (p < glue_val_level) { if (q == multiply_cmd) { @@ -3104,8 +3600,10 @@ void do_register_command(int a) } if (arith_error) { print_err("Arithmetic overflow"); - help2("I can't carry out that multiplication or division,", - "since the result is out of range."); + help2( + "I can't carry out that multiplication or division,", + "since the result is out of range." + ); if (p >= glue_val_level) flush_node(cur_val); error(); @@ -3126,10 +3624,9 @@ void do_register_command(int a) } } -@ @c void alter_aux(void) { - halfword c; /* |hmode| or |vmode| */ + halfword c; /* |hmode| or |vmode| */ if (cur_chr != abs(mode)) { report_illegal_case(); } else { @@ -3142,7 +3639,9 @@ void alter_aux(void) scan_int(); if ((cur_val <= 0) || (cur_val > 32767)) { print_err("Bad space factor"); - help1("I allow only values in the range 1..32767 here."); + help1( + "I allow only values in the range 1..32767 here." + ); int_error(cur_val); } else { space_factor_par = cur_val; @@ -3151,39 +3650,39 @@ void alter_aux(void) } } -@ @c void alter_prev_graf(void) { - int p; /* index into |nest| */ - p = nest_ptr; + int p = nest_ptr; /* index into |nest| */ while (abs(nest[p].mode_field) != vmode) decr(p); scan_optional_equals(); scan_int(); if (cur_val < 0) { print_err("Bad \\prevgraf"); - help1("I allow only nonnegative values here."); + help1( + "I allow only nonnegative values here." + ); int_error(cur_val); } else { nest[p].pg_field = cur_val; } } -@ @c void alter_page_so_far(void) { - int c; /* index into |page_so_far| */ - c = cur_chr; + int c = cur_chr; /* index into |page_so_far| */ scan_optional_equals(); scan_normal_dimen(); page_so_far[c] = cur_val; } -@ @c +/*tex + The value of |c| is 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. +*/ + void alter_integer(void) { - int c; /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */ - c = cur_chr; + int c = cur_chr; scan_optional_equals(); scan_int(); if (c == 0) { @@ -3191,8 +3690,10 @@ void alter_integer(void) } else if (c == 2) { if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) { print_err("Bad interaction mode"); - help2("Modes are 0=batch, 1=nonstop, 2=scroll, and", - "3=errorstop. Proceed, and I'll ignore this case."); + help2( + "Modes are 0=batch, 1=nonstop, 2=scroll, and", + "3=errorstop. Proceed, and I'll ignore this case." + ); int_error(cur_val); } else { cur_chr = cur_val; @@ -3203,11 +3704,10 @@ void alter_integer(void) } } -@ @c void alter_box_dimen(void) { - int c; /* |width_offset| or |height_offset| or |depth_offset| */ - int b; /* box number */ + int c; /* |width_offset| or |height_offset| or |depth_offset| */ + int b; /* box number */ c = cur_chr; scan_register_num(); b = cur_val; @@ -3217,7 +3717,6 @@ void alter_box_dimen(void) varmem[box(b) + c].cint = cur_val; } -@ @c void new_interaction(void) { print_ln(); @@ -3229,21 +3728,27 @@ void new_interaction(void) fixup_selector(log_opened_global); } -@ The \.{\\afterassignment} command puts a token into the global -variable |after_token|. This global variable is examined just after -every assignment has been performed. +/*tex + +The \.{\\afterassignment} command puts a token into the global variable +|after_token|. This global variable is examined just after every assignment has +been performed. It's value is zero, or a saved token. + +*/ + +halfword after_token; + +/*tex -@c -halfword after_token; /* zero, or a saved token */ + Here is a procedure that might be called `Get the next non-blank non-relax + non-call non-assignment token'. -@ Here is a procedure that might be called `Get the next non-blank non-relax -non-call non-assignment token'. +*/ -@c void do_assignments(void) { while (true) { - /* Get the next non-blank non-relax... */ + /*tex Get the next non-blank non-relax... */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); @@ -3255,13 +3760,13 @@ void do_assignments(void) } } -@ @c +/*tex |cur_chr| is 1 for \.{\\openin}, 0 for \.{\\closein}: */ + void open_or_close_in(void) { - int c; /* 1 for \.{\\openin}, 0 for \.{\\closein} */ - int n; /* stream number */ + int c = cur_chr; + int n; char *fn; - c = cur_chr; scan_four_bit_int(); n = cur_val; if (read_open[n] != closed) { @@ -3275,7 +3780,8 @@ void open_or_close_in(void) } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); back_input(); if (cur_cmd != left_brace_cmd) { - scan_file_name(); /* set |cur_name| to desired file name */ + /*tex Set |cur_name| to desired file name. */ + scan_file_name(); if (cur_ext == get_nullstr()) cur_ext = maketexstring(".tex"); } else { @@ -3288,14 +3794,17 @@ void open_or_close_in(void) } } -@ @c -boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */ +/*tex + Has the long \.{\\errmessage} help been used? +*/ + +boolean long_help_seen; void issue_message(void) { - int old_setting; /* holds |selector| setting */ - int c; /* identifies \.{\\message} and \.{\\errmessage} */ - str_number s; /* the message */ + int old_setting; /* holds |selector| setting */ + int c; /* identifies \.{\\message} and \.{\\errmessage} */ + str_number s; /* the message */ c = cur_chr; (void) scan_toks(false, true); old_setting = selector; @@ -3306,7 +3815,7 @@ void issue_message(void) str_room(1); s = make_string(); if (c == 0) { - /* Print string |s| on the terminal */ + /*tex Print string |s| on the terminal */ if (term_offset + (int) str_length(s) > max_print_line - 2) print_ln(); else if ((term_offset > 0) || (file_offset > 0)) @@ -3315,23 +3824,29 @@ void issue_message(void) update_terminal(); } else { - /* Print string |s| as an error message */ - /* If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined - \.{\\errhelp}, we don't want to give a long help message each time. So we - give a verbose explanation only once. */ + /*tex + Print string |s| as an error message. If \.{\\errmessage} occurs + often in |scroll_mode|, without user-defined \.{\\errhelp}, we don't + want to give a long help message each time. So we give a verbose + explanation only once. + */ print_err(""); print(s); if (err_help_par != null) { use_err_help = true; } else if (long_help_seen) { - help1("(That was another \\errmessage.)"); + help1( + "(That was another \\errmessage.)" + ); } else { if (interaction < error_stop_mode) long_help_seen = true; - help4("This error message was generated by an \\errmessage", - "command, so I can't give any explicit help.", - "Pretend that you're Hercule Poirot: Examine all clues,", - "and deduce the truth by order and method."); + help4( + "This error message was generated by an \\errmessage", + "command, so I can't give any explicit help.", + "Pretend that you're Hercule Poirot: Examine all clues,", + "and deduce the truth by order and method." + ); } error(); use_err_help = false; @@ -3340,34 +3855,40 @@ void issue_message(void) flush_str(s); } -@ The |error| routine calls on |give_err_help| if help is requested from -the |err_help| parameter. +/*tex + +The |error| routine calls on |give_err_help| if help is requested from the +|err_help| parameter. + +*/ -@c void give_err_help(void) { token_show(err_help_par); } -@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by -building a token list and then changing the cases of the letters in it. +/*tex + +The \.{\\uppercase} and \.{\\lowercase} commands are implemented by building a +token list and then changing the cases of the letters in it. + +*/ -@c void shift_case(void) { - halfword b; /* |lc_code_base| or |uc_code_base| */ - halfword p; /* runs through the token list */ - halfword t; /* token */ - halfword c; /* character code */ - halfword i; /* inbetween */ + halfword b; /* |lc_code_base| or |uc_code_base| */ + halfword p; /* runs through the token list */ + halfword t; /* token */ + halfword c; /* character code */ + halfword i; /* inbetween */ b = cur_chr; p = scan_toks(false, false); p = token_link(def_ref); while (p != null) { - /* Change the case of the token in |p|, if a change is appropriate */ - /* - When the case of a |chr_code| changes, we don't change the |cmd|. - We also change active characters. + /*tex + Change the case of the token in |p|, if a change is appropriate. When + the case of a |chr_code| changes, we don't change the |cmd|. We also + change active characters. */ t = token_info(p); if (t < cs_token_flag) { @@ -3390,27 +3911,30 @@ void shift_case(void) p = token_link(p); } back_list(token_link(def_ref)); - free_avail(def_ref); /* omit reference count */ + free_avail(def_ref); } -@ We come finally to the last pieces missing from |main_control|, namely the +/*tex + +We come finally to the last pieces missing from |main_control|, namely the `\.{\\show}' commands that are useful when debugging. -@c +*/ + void show_whatever(void) { - halfword p; /* tail of a token list to show */ - int t; /* type of conditional being shown */ - int m; /* upper bound on |fi_or_else| codes */ - int l; /* line where that conditional began */ - int n; /* level of \.{\\if...\\fi} nesting */ + halfword p; /* tail of a token list to show */ + int t; /* type of conditional being shown */ + int m; /* upper bound on |fi_or_else| codes */ + int l; /* line where that conditional began */ + int n; /* level of \.{\\if...\\fi} nesting */ switch (cur_chr) { case show_lists: begin_diagnostic(); show_activities(); break; case show_box_code: - /* Show the current contents of a box */ + /*tex Show the current contents of a box. */ scan_register_num(); begin_diagnostic(); tprint_nl("> \\box"); @@ -3422,7 +3946,7 @@ void show_whatever(void) show_box(box(cur_val)); break; case show_code: - /* Show the current meaning of a token, then |goto common_ending| */ + /*tex Show the current meaning of a token, then |goto common_ending|. */ get_token(); if (interaction == error_stop_mode) wake_up_terminal(); @@ -3434,7 +3958,7 @@ void show_whatever(void) print_meaning(); goto COMMON_ENDING; break; - /* Cases for |show_whatever| */ + /*tex Cases for |show_whatever| */ case show_groups: begin_diagnostic(); show_save_groups(); @@ -3474,8 +3998,10 @@ void show_whatever(void) } break; default: - /* Show the current value of some parameter or register, - then |goto common_ending| */ + /*tex + Show the current value of some parameter or register, then |goto + common_ending|. + */ p = the_toks(); if (interaction == error_stop_mode) wake_up_terminal(); @@ -3485,7 +4011,7 @@ void show_whatever(void) goto COMMON_ENDING; break; } - /* Complete a potentially long \.{\\show} command */ + /*tex Complete a potentially long \.{\\show} command: */ end_diagnostic(true); print_err("OK"); if (selector == term_and_log) { @@ -3500,32 +4026,40 @@ void show_whatever(void) help0(); decr(error_count); } else if (tracing_online_par > 0) { - help3("This isn't an error message; I'm just \\showing something.", - "Type `I\\show...' to show more (e.g., \\show\\cs,", - "\\showthe\\count10, \\showbox255, \\showlists)."); + help3( + "This isn't an error message; I'm just \\showing something.", + "Type `I\\show...' to show more (e.g., \\show\\cs,", + "\\showthe\\count10, \\showbox255, \\showlists)." + ); } else { - help5("This isn't an error message; I'm just \\showing something.", - "Type `I\\show...' to show more (e.g., \\show\\cs,", - "\\showthe\\count10, \\showbox255, \\showlists).", - "And type `I\\tracingonline=1\\show...' to show boxes and", - "lists on your terminal as well as in the transcript file."); + help5( + "This isn't an error message; I'm just \\showing something.", + "Type `I\\show...' to show more (e.g., \\show\\cs,", + "\\showthe\\count10, \\showbox255, \\showlists).", + "And type `I\\tracingonline=1\\show...' to show boxes and", + "lists on your terminal as well as in the transcript file." + ); } error(); } -@ @c +/*tex + This procedure gets things started properly: +*/ + void initialize(void) -{ /* this procedure gets things started properly */ - int k; /* index into |mem|, |eqtb|, etc. */ - /* Initialize whatever \TeX\ might access */ - /* Set initial values of key variables */ +{ + int k; /* index into |mem|, |eqtb|, etc. */ + /*tex + Initialize whatever \TeX\ might access and set initial values of key + variables + */ initialize_errors(); initialize_arithmetic(); max_used_attr = -1; attr_list_cache = cache_disabled; initialize_nesting(); - - /* Start a new current page */ + /*tex Start a new current page: */ page_contents = empty; page_tail = page_head; #if 0 @@ -3539,7 +4073,7 @@ void initialize(void) page_max_depth = 0; initialize_equivalents(); - no_new_control_sequence = true; /* new identifiers are usually forbidden */ + no_new_control_sequence = true; /* new identifiers are usually forbidden */ init_primitives(); mag_set = 0; @@ -3559,11 +4093,10 @@ void initialize(void) stop_at_space = true; if (ini_version) { - /* Initialize table entries (done by \.{INITEX} only) */ - + /*tex Initialize table entries (done by \.{INITEX} only). */ init_node_mem(500); initialize_tokens(); - /* Initialize the special list heads and constant nodes */ + /*tex Initialize the special list heads and constant nodes. */ initialize_alignments(); initialize_buildpage(); @@ -3629,7 +4162,9 @@ void initialize(void) math_pre_display_gap_factor_par = 2000; pre_bin_op_penalty_par = inf_penalty; math_script_box_mode_par = 1; + math_script_char_mode_par = 1; pre_rel_penalty_par = inf_penalty; + compound_hyphen_mode_par = 1; escape_char_par = '\\'; end_line_char_par = carriage_return; set_del_code('.', 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */ @@ -3642,7 +4177,7 @@ void initialize(void) page_right_offset_par = one_inch; page_bottom_offset_par = one_inch; ini_init_primitives(); - hash_used = frozen_control_sequence; /* nothing is used */ + hash_used = frozen_control_sequence; hash_high = 0; cs_count = 0; set_eq_type(frozen_dont_expand, dont_expand_cmd); @@ -3655,16 +4190,16 @@ void initialize(void) font_bytes = 0; px_dimen_par = one_bp; math_eqno_gap_step_par = 1000 ; + math_flatten_mode_par = 1; /* ord */ cs_text(frozen_protection) = maketexstring("inaccessible"); format_ident = maketexstring(" (INITEX)"); cs_text(end_write) = maketexstring("endwrite"); set_eq_level(end_write, level_one); set_eq_type(end_write, outer_call_cmd); set_equiv(end_write, null); - /* bah */ + /*tex Bah, this is a bad place do do this. */ set_pdf_major_version(1); set_pdf_minor_version(0); } synctexoffset = int_base + synctex_code; - } diff --git a/Build/source/texk/web2c/luatexdir/tex/mathcodes.w b/Build/source/texk/web2c/luatexdir/tex/mathcodes.c index a0a357e5319..42e8b0b15b9 100644 --- a/Build/source/texk/web2c/luatexdir/tex/mathcodes.w +++ b/Build/source/texk/web2c/luatexdir/tex/mathcodes.c @@ -1,81 +1,88 @@ -% mathnodes.w -% -% Copyright 2006-2012 Taco Hoekwater <taco@@luatex.org> -% Copyright 2012 Khaled Hosny <khaledhosny@@eglug.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +mathnodes.w + +Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ math codes -@c +/*tex + +We support tre traditional math codes as well as larger ones suitable for +\UNICODE\ input and fonts. + +*/ + static sa_tree mathcode_head = NULL; -/* the 0xFFFFFFFF is a flag value */ +/*tex the |0xFFFFFFFF| is a flag value. */ #define MATHCODESTACK 8 #define MATHCODEDEFAULT 0xFFFFFFFF #define MATHCODEACTIVE 0xFFFFFFFE -@ delcodes -@c +/*tex + +Delcodes are also went larger. + +*/ + static sa_tree delcode_head = NULL; #define DELCODESTACK 4 #define DELCODEDEFAULT 0xFFFFFFFF -@ some helpers for mathcode printing +/*tex + +We now get lots of helpers for definitions and printing. The storage model +that we use is different because we can hav emany more so we need to be +sparse. Therefore we use trees. + +*/ + -@c -#define print_hex_digit(A) do { \ - if ((A)>=10) print_char('A'+(A)-10); \ - else print_char('0'+(A)); \ +#define print_hex_digit(A) do { \ + if ((A)>=10) print_char('A'+(A)-10); \ + else print_char('0'+(A)); \ } while (0) -#define two_hex(A) do { \ - print_hex_digit((A)/16); \ - print_hex_digit((A)%16); \ +#define two_hex(A) do { \ + print_hex_digit((A)/16); \ + print_hex_digit((A)%16); \ } while (0) -#define four_hex(A) do { \ - two_hex((A)/256); \ - two_hex((A)%256); \ +#define four_hex(A) do { \ + two_hex((A)/256); \ + two_hex((A)%256); \ } while (0) -#define six_hex(A) do { \ - two_hex((A)/65536); \ +#define six_hex(A) do { \ + two_hex((A)/65536); \ two_hex(((A)%65536)/256); \ - two_hex((A)%256); \ + two_hex((A)%256); \ } while (0) -/* - At some point we will drop the mathchardef 8 bit storage (c_mathoption_umathcode_meaning_code => 1) - and then some of the conversion can go away. Like mathchar_from_integer: only wide characters are - possible then. -*/ - - -@ @c mathcodeval mathchar_from_integer(int value, int extcode) { mathcodeval mval; if (extcode == tex_mathcode) { - /* printf("can't happen: tex_mathcode\n"); */ mval.class_value = (value / 0x1000); mval.family_value = ((value % 0x1000) / 0x100); mval.character_value = (value % 0x100); @@ -88,12 +95,12 @@ mathcodeval mathchar_from_integer(int value, int extcode) return mval; } -@ @c void show_mathcode_value_old(int value) { print_char('"'); four_hex(value); } + void show_mathcode_value(mathcodeval c) { print_char('"'); @@ -104,7 +111,6 @@ void show_mathcode_value(mathcodeval c) six_hex(c.character_value); } -@ @c static void show_mathcode(int n) { mathcodeval c = get_math_code(n); @@ -114,7 +120,6 @@ static void show_mathcode(int n) show_mathcode_value(c); } -@ @c static void unsavemathcode(quarterword gl) { sa_stack_item st; @@ -138,7 +143,6 @@ static void unsavemathcode(quarterword gl) } } -@ @c void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quarterword level) { sa_tree_item v; @@ -161,9 +165,6 @@ void set_math_code(int n, int mathclass, int mathfamily, int mathcharacter, quar } } -@ @c -/* we could use two structs ... tex and umath */ - mathcodeval get_math_code(int n) { mathcodeval d; @@ -189,14 +190,12 @@ mathcodeval get_math_code(int n) return d; } -@ @c int get_math_code_num(int n) { mathcodeval d = get_math_code(n); return (d.class_value + (d.family_value * 8)) * (65536 * 32) + d.character_value; } -@ @c static void initializemathcode(void) { sa_tree_item sa_value = { 0 }; @@ -214,7 +213,6 @@ static void undumpmathcode(void) mathcode_head = undump_sa_tree("mathcodes"); } -@ @c static void show_delcode(int n) { delcodeval c; @@ -232,7 +230,6 @@ static void show_delcode(int n) } } -@ @c static void unsavedelcode(quarterword gl) { sa_stack_item st; @@ -256,7 +253,6 @@ static void unsavedelcode(quarterword gl) } } -@ @c void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, int lmathcharacter, quarterword gl) { sa_tree_item v; @@ -266,7 +262,8 @@ void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, i v.del_code_value.dummy_value = 0; v.del_code_value.large_family_value = lmathfamily; v.del_code_value.large_character_value = lmathcharacter; - set_sa_item(delcode_head, n, v, gl); /* always global */ + /*tex Always global! */ + set_sa_item(delcode_head, n, v, gl); if (tracing_assigns_par > 1) { begin_diagnostic(); print_char('{'); @@ -278,7 +275,6 @@ void set_del_code(int n, int smathfamily, int smathcharacter, int lmathfamily, i } } -@ @c delcodeval get_del_code(int n) { delcodeval d; @@ -299,9 +295,12 @@ delcodeval get_del_code(int n) return d; } -@ this really only works for old-style delcodes! +/*tex + +This really only works for old-style delcodes! + +*/ -@c int get_del_code_num(int n) { delcodeval d = get_del_code(n); @@ -313,7 +312,6 @@ int get_del_code_num(int n) } } -@ @c static void initializedelcode(void) { sa_tree_item sa_value = { 0 }; @@ -321,7 +319,6 @@ static void initializedelcode(void) delcode_head = new_sa_tree(DELCODESTACK, 2, sa_value); } -@ @c static void dumpdelcode(void) { dump_sa_tree(delcode_head,"delcodes"); @@ -332,28 +329,24 @@ static void undumpdelcode(void) delcode_head = undump_sa_tree("delcodes"); } -@ @c void unsave_math_codes(quarterword grouplevel) { unsavemathcode(grouplevel); unsavedelcode(grouplevel); } -@ @c void initialize_math_codes(void) { initializemathcode(); initializedelcode(); } -@ @c void free_math_codes(void) { destroy_sa_tree(mathcode_head); destroy_sa_tree(delcode_head); } -@ @c void dump_math_codes(void) { dumpmathcode(); diff --git a/Build/source/texk/web2c/luatexdir/tex/memoryword.c b/Build/source/texk/web2c/luatexdir/tex/memoryword.c new file mode 100644 index 00000000000..7bf758dd0d4 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/memoryword.c @@ -0,0 +1,29 @@ +/* +memoryword.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +The debug code is no longer present! + +*/ diff --git a/Build/source/texk/web2c/luatexdir/tex/memoryword.w b/Build/source/texk/web2c/luatexdir/tex/memoryword.w deleted file mode 100644 index b596abb37ec..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/memoryword.w +++ /dev/null @@ -1,55 +0,0 @@ -% memoryword.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - - -#include "ptexlib.h" - -@ When debugging, we may want to print a |memory_word| without knowing -what type it is; so we print it in all modes. - -@c -#ifdef DEBUG -void print_word(memory_word w) -{ - /* prints |w| in all ways */ - print_int(w.cint); - print_char(' '); - print_scaled(w.cint); - print_char(' '); - print_scaled(round(unity * float_cast(w.gr))); - print_ln(); - print_int(w.hh.lhfield); - print_char('='); - print_int(w.hh.b0); - print_char(':'); - print_int(w.hh.b1); - print_char(';'); - print_int(w.hh.rh); - print_char(' '); - print_int(w.qqqq.b0); - print_char(':'); - print_int(w.qqqq.b1); - print_char(':'); - print_int(w.qqqq.b2); - print_char(':'); - print_int(w.qqqq.b3); -} -#endif diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.w b/Build/source/texk/web2c/luatexdir/tex/mlist.c index 291ce8c4868..8f6d71eb294 100644 --- a/Build/source/texk/web2c/luatexdir/tex/mlist.w +++ b/Build/source/texk/web2c/luatexdir/tex/mlist.c @@ -1,71 +1,72 @@ -% mlist.w -% -% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -% (HH / 0.82+): - -@ In traditional \TeX\ the italic correction is added to the width of the glyph. This -is part of the engine design and related font design. In opentype math this is -different. There the italic correction had more explicit usage. The 1.7 spec -says: - -italic correction: - - When a run of slanted characters is followed by a straight character (such as - an operator or a delimiter), the italics correction of the last glyph is added - to its advance width. - - When positioning limits on an N-ary operator (e.g., integral sign), the horizontal - position of the upper limit is moved to the right by ½ of the italics correction, - while the position of the lower limit is moved to the left by the same distance. - - When positioning superscripts and subscripts, their default horizontal positions are - also different by the amount of the italics correction of the preceding glyph. - -math kerning: - - Set the default horizontal position for the superscript as shifted relative to the - position of the subscript by the italics correction of the base glyph. - -Before this was specified we had to gamble a bit and assume that cambria was the font -benchmark and trust our eyes (and msword) for the logic. I must admit that I have been -fighting these italics in fonts (and the heuristics that Lua\TeX\ provided) right from -the start (e.g. using Lua based postprocessing) but by now we know more and have more -fonts to test with. More fonts are handy because not all fonts are alike when it comes -to italics. Axis are another area of concern, as it looks like opentype math fonts often -already apply that shift. - -@ @c +/* + +Copyright 2006-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +/*tex + + In traditional \TeX\ the italic correction is added to the width of the + glyph. This is part of the engine design and related font design. In opentype + math this is different. There the italic correction had more explicit usage. + The 1.7 spec says: + + \startitemize + + \startitem + {\em italic correction:} When a run of slanted characters is followed by + a straight character (such as an operator or a delimiter), the italics + correction of the last glyph is added to its advance width. + + When positioning limits on an N-ary operator (e.g., integral sign), the + horizontal position of the upper limit is moved to the right by ½ of the + italics correction, while the position of the lower limit is moved to the + left by the same distance. + + When positioning superscripts and subscripts, their default horizontal + positions are also different by the amount of the italics correction of + the preceding glyph. + \stopitem + + \startitem + {\em math kerning:} Set the default horizontal position for the + superscript as shifted relative to the position of the subscript by the + italics correction of the base glyph. + \stopitem + + \stopitemize + + Before this was specified we had to gamble a bit and assume that cambria was + the font benchmark and trust our eyes (and msword) for the logic. I must + admit that I have been fighting these italics in fonts (and the heuristics + that Lua\TeX\ provided) right from the start (e.g. using Lua based + postprocessing) but by now we know more and have more fonts to test with. + More fonts are handy because not all fonts are alike when it comes to + italics. Axis are another area of concern, as it looks like opentype math + fonts often already apply that shift. + +*/ + #define is_new_mathfont(A) ((font_math_params(A) >0) && (math_old_par == 0)) #define is_old_mathfont(A,B) ((font_math_params(A)==0) && (font_params(A)>=(B))) #define do_new_math(A) ((font_math_params(A) >0) && (font_oldmath(A) == 0) && (math_old_par == 0)) -@ -\def\LuaTeX{Lua\TeX} - -@ @c - #include "ptexlib.h" #include "lua/luatex-api.h" -@ @c -#define nDEBUG - #define reset_attributes(p,newatt) do { \ delete_attribute_ref(node_attr(p)); \ node_attr(p) = newatt; \ @@ -97,83 +98,160 @@ already apply that shift. #define font_MATH_par(a,b) \ (font_math_params(a)>=b ? font_math_param(a,b) : undefined_math_parameter) -@ here are the math parameters that are font-dependant +/*tex + + Here are the math parameters that are font-dependant. -@ Before an mlist is converted to an hlist, \TeX\ makes sure that -the fonts in family~2 have enough parameters to be math-symbol -fonts, and that the fonts in family~3 have enough parameters to be -math-extension fonts. The math-symbol parameters are referred to by using the -following macros, which take a size code as their parameter; for example, -|num1(cur_size)| gives the value of the |num1| parameter for the current size. -@^parameters for symbols@> -@^font parameters@> + Before an mlist is converted to an hlist, \TeX\ makes sure that the fonts in + family~2 have enough parameters to be math-symbol fonts, and that the fonts + in family~3 have enough parameters to be math-extension fonts. The + math-symbol parameters are referred to by using the following macros, which + take a size code as their parameter; for example, |num1(cur_size)| gives the + value of the |num1| parameter for the current size. + +*/ -@c #define total_mathsy_params 22 #define total_mathex_params 13 #define mathsy(A,B) font_param(fam_fnt(2,A),B) -#define math_x_height(A) mathsy(A,5) /* height of `\.x' */ -#define math_quad(A) mathsy(A,6) /* \.{18mu} */ -#define num1(A) mathsy(A,8) /* numerator shift-up in display styles */ -#define num2(A) mathsy(A,9) /* numerator shift-up in non-display, non-\.{\\atop} */ -#define num3(A) mathsy(A,10) /* numerator shift-up in non-display \.{\\atop} */ -#define denom1(A) mathsy(A,11) /* denominator shift-down in display styles */ -#define denom2(A) mathsy(A,12) /* denominator shift-down in non-display styles */ -#define sup1(A) mathsy(A,13) /* superscript shift-up in uncramped display style */ -#define sup2(A) mathsy(A,14) /* superscript shift-up in uncramped non-display */ -#define sup3(A) mathsy(A,15) /* superscript shift-up in cramped styles */ -#define sub1(A) mathsy(A,16) /* subscript shift-down if superscript is absent */ -#define sub2(A) mathsy(A,17) /* subscript shift-down if superscript is present */ -#define sup_drop(A) mathsy(A,18) /* superscript baseline below top of large box */ -#define sub_drop(A) mathsy(A,19) /* subscript baseline below bottom of large box */ -#define delim1(A) mathsy(A,20) /* size of \.{\\atopwithdelims} delimiters in display styles */ -#define delim2(A) mathsy(A,21) /* size of \.{\\atopwithdelims} delimiters in non-displays */ -#define axis_height(A) mathsy(A,22) /* height of fraction lines above the baseline */ - -@ The math-extension parameters have similar macros, but the size code is -omitted (since it is always |cur_size| when we refer to such parameters). -@^parameters for symbols@> -@^font parameters@> - -@c +/*tex height of `\.x' */ + +#define math_x_height(A) mathsy(A,5) + +/*tex \.{18mu} */ + +#define math_quad(A) mathsy(A,6) + +/*tex numerator shift-up in display styles */ + +#define num1(A) mathsy(A,8) + +/*tex numerator shift-up in non-display, non-\.{\\atop} */ + +#define num2(A) mathsy(A,9) + +/*tex numerator shift-up in non-display \.{\\atop} */ + +#define num3(A) mathsy(A,10) + +/*tex denominator shift-down in display styles */ + +#define denom1(A) mathsy(A,11) + +/*tex denominator shift-down in non-display styles */ + +#define denom2(A) mathsy(A,12) + +/*tex superscript shift-up in uncramped display style */ + +#define sup1(A) mathsy(A,13) + +/*tex superscript shift-up in uncramped non-display */ + +#define sup2(A) mathsy(A,14) + +/*tex superscript shift-up in cramped styles */ + +#define sup3(A) mathsy(A,15) + +/*tex subscript shift-down if superscript is absent */ + +#define sub1(A) mathsy(A,16) + +/*tex subscript shift-down if superscript is present */ + +#define sub2(A) mathsy(A,17) + +/*tex superscript baseline below top of large box */ + +#define sup_drop(A) mathsy(A,18) + +/*tex subscript baseline below bottom of large box */ + +#define sub_drop(A) mathsy(A,19) + +/*tex size of \.{\\atopwithdelims} delimiters in display styles */ + +#define delim1(A) mathsy(A,20) + +/*tex size of \.{\\atopwithdelims} delimiters in non-displays */ + +#define delim2(A) mathsy(A,21) + +/*tex height of fraction lines above the baseline */ + +#define axis_height(A) mathsy(A,22) + +/*tex + + The math-extension parameters have similar macros, but the size code is + omitted (since it is always |cur_size| when we refer to such parameters). + +*/ + #define mathex(A,B) font_param(fam_fnt(3,A),B) -#define default_rule_thickness(A) mathex(A,8) /* thickness of \.{\\over} bars */ -#define big_op_spacing1(A) mathex(A,9) /* minimum clearance above a displayed op */ -#define big_op_spacing2(A) mathex(A,10) /* minimum clearance below a displayed op */ -#define big_op_spacing3(A) mathex(A,11) /* minimum baselineskip above displayed op */ -#define big_op_spacing4(A) mathex(A,12) /* minimum baselineskip below displayed op */ -#define big_op_spacing5(A) mathex(A,13) /* padding above and below displayed limits */ -@ I (TH) made a bunch of extensions cf. the MATH table in OpenType, but some of -the MathConstants values have no matching usage in \LuaTeX\ right now. +/*tex thickness of \.{\\over} bars */ + +#define default_rule_thickness(A) mathex(A,8) + +/*tex minimum clearance above a displayed op */ + +#define big_op_spacing1(A) mathex(A,9) + +/*tex minimum clearance below a displayed op */ + +#define big_op_spacing2(A) mathex(A,10) + +/*tex minimum baselineskip above displayed op */ + +#define big_op_spacing3(A) mathex(A,11) + +/*tex minimum baselineskip below displayed op */ -ScriptPercentScaleDown, -ScriptScriptPercentScaleDown: - These should be handled by the macro package, on the engine - side there are three separate fonts +#define big_op_spacing4(A) mathex(A,12) -DelimitedSubFormulaMinHeight: - This is perhaps related to word's natural math input? I have - no idea what to do about it +/*tex padding above and below displayed limits */ -MathLeading: - LuaTeX does not currently handle multi-line displays, and - the parameter does not seem to make much sense elsewhere +#define big_op_spacing5(A) mathex(A,13) -FlattenedAccentBaseHeight: - This is based on the 'flac' GSUB feature. It would not be hard - to support that, but proper math accent placements cf. MATH - needs support for MathTopAccentAttachment table to be - implemented first +/*tex -Also still TODO for OpenType Math: - * prescripts + \LUATEX makes a bunch of extensions cf. the |MATH| table in \OPENTYPE, but + some of the |MathConstants| values have no matching usage in \LUATEX\ right + now. -@ this is not really a math parameter at all + \startitemize + + \startitem + |ScriptPercentScaleDown| |ScriptScriptPercentScaleDown|: These should + be handled by the macro package, on the engine side there are three + separate fonts. + \stopitem + + \startitem + |DelimitedSubFormulaMinHeight|: This is perhaps related to word's + natural math input? We have no idea what to do about it. + \stopitem + + \startitem + |MathLeading|: \LUATEX does not currently handle multi-line displays, + and the parameter does not seem to make much sense elsewhere. + \stopitem + + \startitem + |FlattenedAccentBaseHeight|: This is based on the |flac| |GSUB| + feature. It would not be hard to support that, but proper math accent + placements cf.\ |MATH| needs support for |MathTopAccentAttachment| + table to be implemented first. + \stopitem + + \stopitemize + +*/ -@c static void math_param_error(const char *param, int style) { char s[256]; @@ -184,16 +262,11 @@ static void math_param_error(const char *param, int style) "the parameter mentioned earlier.", NULL }; - snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", - param, math_style_names[style]); + snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", param, math_style_names[style]); tex_error(s, hlp); -#if 0 - flush_math(); -#endif return; } -@ @c static scaled accent_base_height(int f) { scaled a; @@ -207,11 +280,14 @@ static scaled accent_base_height(int f) return a; } -@ The non-staticness of this function is for the benefit of |texmath.w|. Watch out, -this one uses the style! The style and size numbers don't match because we have -cramped styles. +/*tex + + The non-staticness of this function is for the benefit of |texmath.w|. + Watch out, this one uses the style! The style and size numbers don't + match because we have cramped styles. + +*/ -@c scaled get_math_quad_style(int var) { scaled a = get_math_param(math_param_quad, var); @@ -223,10 +299,13 @@ scaled get_math_quad_style(int var) } } -@ For this reason the next one is different because it is called with a size -specifier instead of a style specifier. +/*tex + + For this reason the next one is different because it is called with a size + specifier instead of a style specifier. + +*/ -@c static scaled math_axis_size(int b) { scaled a; @@ -246,7 +325,6 @@ static scaled math_axis_size(int b) } } -@ @c scaled get_math_quad_size(int b) { int var; @@ -259,26 +337,32 @@ scaled get_math_quad_size(int b) return get_math_param(math_param_quad, var); } -@ @c static scaled minimum_operator_size(int var) { scaled a = get_math_param(math_param_operator_size, var); return a; } -@ Old-style fonts do not define the |radical_rule|. This allows |make_radical| to select -the backward compatibility code, and it means that we can't raise an error here. +/*tex + + Old-style fonts do not define the |radical_rule|. This allows |make_radical| + to select the backward compatibility code, and it means that we can't raise + an error here. + +*/ -@c static scaled radical_rule_par(int var) { scaled a = get_math_param(math_param_radical_rule, var); return a; } -@ now follow all the trivial math parameters +/*tex + + Now follow all the trivial math parameters. + +*/ -@c #define get_math_param_or_error(a,b) do_get_math_param_or_error(a, math_param_##b, #b) #define get_math_param_or_zero(a,b) do_get_math_param_or_zero(a, math_param_##b, #b) @@ -301,9 +385,12 @@ static scaled do_get_math_param_or_zero(int var, int param, const char *name) return a; } -@ A variant on a suggestion on the list based on analysis by UV. +/*tex + + A variant on a suggestion on the list based on analysis by UV. + +*/ -@c static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { scaled delta, delta1, delta2; if (axis) { @@ -313,7 +400,7 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { } delta1 = max_h + max_d - delta2; if (delta2 > delta1) { - /* |delta1| is max distance from axis */ + /*tex |delta1| is max distance from axis */ delta1 = delta2; } delta = (delta1 / 500) * delimiter_factor_par; @@ -325,7 +412,6 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { } } -@ @c #define radical_degree_before(a) get_math_param_or_error(a, radical_degree_before) #define radical_degree_after(a) get_math_param_or_error(a, radical_degree_after) #define radical_degree_raise(a) get_math_param_or_error(a, radical_degree_raise) @@ -359,9 +445,7 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { #define fraction_num_up(a) get_math_param_or_error(a, fraction_num_up) #define fraction_denom_down(a) get_math_param_or_error(a, fraction_denom_down) #define fraction_del_size_new(a) get_math_param_or_error(a, fraction_del_size) -/* -#define fraction_del_size_old(a) get_math_param(a, math_param_fraction_del_size) -*/ +/* fraction_del_size_old(a) get_math_param (a, math_param_fraction_del_size) */ #define fraction_del_size_old(a) get_math_param_or_error(a, fraction_del_size) #define skewed_fraction_hgap(a) get_math_param_or_error(a, skewed_fraction_hgap) @@ -390,11 +474,11 @@ static scaled get_delimiter_height(scaled max_d, scaled max_h, boolean axis) { #define space_after_script(a) get_math_param_or_error(a, space_after_script) -@ @c void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) { + if (is_new_mathfont(f)) { - if (is_new_mathfont(f)) { /* fix all known parameters */ + /*tex Fix all known parameters. */ DEFINE_MATH_PARAMETERS(math_param_quad, size_id, font_size(f), lvl); @@ -632,7 +716,7 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) { - /* fix old-style |sy| parameters */ + /*tex Fix old-style |sy| parameters. */ DEFINE_MATH_PARAMETERS(math_param_quad, size_id, math_quad(size_id), lvl); @@ -746,9 +830,11 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, (abs(math_x_height(size_id) * 4) / 5), lvl); - /* - The display-size |radical_vgap| is done twice because it needs - values from both the sy and the ex font. + /*tex + + The display-size |radical_vgap| is done twice because it needs values + from both the sy and the ex font. + */ DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id, @@ -768,7 +854,7 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) { - /* fix old-style |ex| parameters */ + /*tex Fix old-style |ex| parameters. */ DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id, default_rule_thickness(size_id), lvl); @@ -853,9 +939,12 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id, 4 * default_rule_thickness(size_id), lvl); - /* - All of the |space_after_script|s are done in |finalize_math_parameters| - because the \.{\\scriptspace} may have been altered by the user + /*tex + + All of the |space_after_script|s are done in + |finalize_math_parameters| because the \.{\\scriptspace} may have + been altered by the user. + */ DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id, @@ -880,9 +969,11 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, big_op_spacing3(size_id), lvl); - /* - The display-size |radical_vgap| is done twice because it needs - values from both the sy and the ex font. + /*tex + + The display-size |radical_vgap| is done twice because it needs values + from both the sy and the ex font. + */ DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id, @@ -891,10 +982,13 @@ void fixup_math_parameters(int fam_id, int size_id, int f, int lvl) } } -@ This needs to be called just at the start of |mlist_to_hlist|, for -backward compatibility with \.{\\scriptspace}. +/*tex + + This needs to be called just at the start of |mlist_to_hlist|, for backward + compatibility with \.{\\scriptspace}. + +*/ -@c static void finalize_math_parameters(void) { int saved_trace = tracing_assigns_par; @@ -920,16 +1014,19 @@ static void finalize_math_parameters(void) tracing_assigns_par = saved_trace; } -@ In order to convert mlists to hlists, i.e., noads to nodes, we need several -subroutines that are conveniently dealt with now. +/*tex + + In order to convert mlists to hlists, i.e., noads to nodes, we need several + subroutines that are conveniently dealt with now. + + Let us first introduce the macros that make it easy to get at the parameters + and other font information. A size code, which is a multiple of 256, is added + to a family number to get an index into the table of internal font numbers + for each combination of family and size. (Be alert: Size codes get larger as + the type gets smaller.) -Let us first introduce the macros that make it easy to get at the parameters and -other font information. A size code, which is a multiple of 256, is added to a -family number to get an index into the table of internal font numbers -for each combination of family and size. (Be alert: Size codes get -larger as the type gets smaller.) +*/ -@c static const char *math_size_string(int s) { if (s == text_size) @@ -940,10 +1037,13 @@ static const char *math_size_string(int s) return "scriptscriptfont"; } -@ When the style changes, the following piece of program computes associated -information: +/*tex + + When the style changes, the following piece of program computes associated + information: + +*/ -@c #define setup_cur_size(a) do { \ if (a==script_style || a==cramped_script_style) \ cur_size = script_size; \ @@ -954,8 +1054,12 @@ information: } while (0) -@ a simple routine that creates a flat copy of a nucleus -@c +/*tex + + A simple routine that creates a flat copy of a nucleus. + +*/ + static pointer math_clone(pointer q) { pointer x; @@ -972,14 +1076,17 @@ static pointer math_clone(pointer q) return x; } -@ Here is a function that returns a pointer to a rule node having a given - thickness |t|. The rule will extend horizontally to the boundary of the vlist - that eventually contains it. +/*tex + + Here is a function that returns a pointer to a rule node having a given + thickness |t|. The rule will extend horizontally to the boundary of the vlist + that eventually contains it. + +*/ -@c static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfword cur_size, halfword cur_fam) { - pointer p; /* the new node */ + pointer p; if (math_rules_mode_par) { p = new_rule(some_rule); rule_math_size(p) = cur_size; @@ -994,14 +1101,17 @@ static pointer do_fraction_rule(scaled t, pointer att, halfword some_rule, halfw return p; } -@ The |overbar| function returns a pointer to a vlist box that consists of - a given box |b|, above which has been placed a kern of height |k| under a - fraction rule of thickness |t| under additional space of height |ht|. +/*tex + + The |overbar| function returns a pointer to a vlist box that consists of a + given box |b|, above which has been placed a kern of height |k| under a + fraction rule of thickness |t| under additional space of height |ht|. + +*/ -@c static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, halfword index, halfword cur_size, halfword cur_fam) { - pointer p, q; /* nodes being constructed */ + pointer p, q; p = new_kern(k); reset_attributes(p, att); couple_nodes(p,b); @@ -1015,16 +1125,20 @@ static pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att, ha return q; } -@ Here is a subroutine that creates a new box, whose list contains a -single character, and whose width includes the italic correction for -that character. The height or depth of the box will be negative, if -the height or depth of the character is negative; thus, this routine -may deliver a slightly different result than |hpack| would produce. +/*tex + + Here is a subroutine that creates a new box, whose list contains a single + character, and whose width includes the italic correction for that character. + The height or depth of the box will be negative, if the height or depth of + the character is negative; thus, this routine may deliver a slightly + different result than |hpack| would produce. + +*/ -@c static pointer char_box(internal_font_number f, int c, pointer bb) { - pointer b, p; /* the new box and its character node */ + /*tex The new box and its character node. */ + pointer b, p; b = new_null_box(); if (do_new_math(f)) width(b) = char_width(f, c); @@ -1039,24 +1153,31 @@ static pointer char_box(internal_font_number f, int c, pointer bb) return b; } -@ Another handy subroutine computes the height plus depth of - a given character: +/*tex + + Another handy subroutine computes the height plus depth of a given character: + +*/ -@c static scaled height_plus_depth(internal_font_number f, int c) { return (char_height(f, c) + char_depth(f, c)); } -@ When we build an extensible character, it's handy to have the - following subroutine, which puts a given character on top - of the characters already in box |b|: +/*tex + + When we build an extensible character, it's handy to have the following + subroutine, which puts a given character on top of the characters already in + box |b|: + +*/ -@c static scaled stack_into_box(pointer b, internal_font_number f, int c) { - pointer p, q; /* new node placed into |b| */ - p = char_box(f, c, node_attr(b)); /* italic gets added to width */ + /*tex New node placed into |b|: */ + pointer p, q; + /*tex Italic gets added to width. */ + p = char_box(f, c, node_attr(b)); if (type(b) == vlist_node) { try_couple_nodes(p,list_ptr(b)); list_ptr(b) = p; @@ -1085,7 +1206,9 @@ static void stack_glue_into_box(pointer b, scaled min, scaled max) { halfword p = new_glue(zero_glue); width(p) = min; stretch(p) = max - min; - reset_attributes(p, node_attr(b)); + if (node_attr(b) != null) { + reset_attributes(p, node_attr(b)); + } if (type(b) == vlist_node) { try_couple_nodes(p,list_ptr(b)); list_ptr(b) = p; @@ -1094,59 +1217,96 @@ static void stack_glue_into_box(pointer b, scaled min, scaled max) { if (q == null) { list_ptr(b) = p; } else { - while (vlink(q) != null) + while (vlink(q) != null) { q = vlink(q); + } couple_nodes(q,p); } } } -@ \TeX's most important routine for dealing with formulas is called - |mlist_to_hlist|. After a formula has been scanned and represented - as an mlist, this routine converts it to an hlist that can be placed - into a box or incorporated into the text of a paragraph. The - explicit parameter |cur_mlist| points to the first node or noad in - the given mlist (and it might be |null|); the parameter |penalties| - is |true| if penalty nodes for potential line breaks are to be - inserted into the resulting hlist, the parameter |cur_style| is a - style code. After |mlist_to_hlist| has acted, |vlink(temp_head)| - points to the translated hlist. - - Since mlists can be inside mlists, the procedure is recursive. And since this - is not part of \TeX's inner loop, the program has been written in a manner - that stresses compactness over efficiency. -@^recursion@> - -@c -int cur_size; /* size code corresponding to |cur_style| */ - -@ @c -static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, scaled v, - pointer att, int cur_style, int boxtype) +/*tex + + \TeX's most important routine for dealing with formulas is called + |mlist_to_hlist|. After a formula has been scanned and represented as an + mlist, this routine converts it to an hlist that can be placed into a box or + incorporated into the text of a paragraph. The explicit parameter |cur_mlist| + points to the first node or noad in the given mlist (and it might be |null|); + the parameter |penalties| is |true| if penalty nodes for potential line + breaks are to be inserted into the resulting hlist, the parameter |cur_style| + is a style code. After |mlist_to_hlist| has acted, |vlink(temp_head)| points + to the translated hlist. + + Since mlists can be inside mlists, the procedure is recursive. And since this + is not part of \TeX's inner loop, the program has been written in a manner + that stresses compactness over efficiency. + +*/ + +/*tex Size code corresponding to |cur_style|: */ + +int cur_size; + +static pointer get_delim_box(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att) { - pointer b; /* new box */ - scaled b_max; /* natural (maximum) size of the stack */ - scaled s_max; /* amount of possible shrink in the stack */ + int callback_id = callback_defined(make_extensible_callback); + if (callback_id > 0) { + /*tex + This call is not optimized as it hardly makes sense to use it ... special + and a it of feature creep too. + */ + halfword b = null; + run_callback(callback_id, "ddddbN->N",fnt,chr,v,min_overlap,horizontal,att,&b); + if (b == null) { + /*tex + We see this as a signal to do it the \TEX\ way. + */ + } else if (type(b) == hlist_node || type(b) == vlist_node) { + return b; + } else { + formatted_error("fonts","invalid extensible character %i created for font %i, [h|v]list expected",chr,fnt); + } + } + return make_extensible(fnt, chr, v, min_overlap, horizontal, att); +} + +pointer make_extensible(internal_font_number fnt, halfword chr, scaled v, scaled min_overlap, int horizontal, halfword att) +{ + /*tex new box */ + pointer b; + /*tex natural (maximum) size of the stack */ + scaled b_max; + /*tex amount of possible shrink in the stack */ + scaled s_max; extinfo *cur; - scaled min_overlap, prev_overlap; - int i; /* a temporary counter number of extensible pieces */ - int with_extenders; /* number of times to repeat each repeatable item in |ext| */ + extinfo *ext; + scaled prev_overlap; + /*tex a temporary counter number of extensible pieces */ + int i; + /*tex number of times to repeat each repeatable item in |ext| */ + int with_extenders; int num_extenders, num_normal; scaled a, c, d; - - assert(ext != NULL); b = new_null_box(); - type(b) = (quarterword) boxtype; - reset_attributes(b, att); - min_overlap = connector_overlap_min(cur_style); - assert(min_overlap >= 0); with_extenders = -1; num_extenders = 0; num_normal = 0; - + if (min_overlap < 0) { + min_overlap = 0; + } + if (horizontal) { + type(b) = (quarterword) hlist_node; + ext = get_charinfo_hor_variants(char_info(fnt,chr)); + } else { + type(b) = (quarterword) vlist_node; + ext = get_charinfo_vert_variants(char_info(fnt,chr)); + } + if (att != null) { + reset_attributes(b,att); + } cur = ext; while (cur != NULL) { - if (!char_exists(f, cur->glyph)) { + if (!char_exists(fnt, cur->glyph)) { const char *hlp[] = { "Each glyph part in an extensible item should exist in the font.", "I will give up trying to find a suitable size for now. Fix your font!", @@ -1160,7 +1320,7 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s num_extenders++; else num_normal++; - /* no negative overlaps or advances are allowed */ + /*tex No negative overlaps or advances are allowed. */ if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) { const char *hlp[] = { "All measurements in extensible items should be positive.", @@ -1190,16 +1350,16 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s num_normal = 1; num_extenders--; } - /* + /*tex + |ext| holds a linked list of numerous items that may or may not be repeatable. For the total height, we have to figure out how many items are needed to create a stack of at least |v|. - The next |while| loop does that. It has two goals: it finds out - the natural height |b_max| of the all the parts needed to reach - at least |v|, and it sets |with_extenders| to the number of times - each of the repeatable items in |ext| has to be repeated to reach - that height. + The next |while| loop does that. It has two goals: it finds out the + natural height |b_max| of the all the parts needed to reach at least |v|, + and it sets |with_extenders| to the number of times each of the + repeatable items in |ext| has to be repeated to reach that height. */ cur = ext; @@ -1217,12 +1377,15 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s c = prev_overlap; a = cur->advance; if (a == 0) { - /* for tfm fonts */ - if (boxtype == vlist_node) - a = height_plus_depth(f, cur->glyph); - else - a = char_width(f, cur->glyph); - assert(a >= 0); + /*tex for tfm fonts */ + if (horizontal) { + a = char_width(fnt, cur->glyph); + } else { + a = height_plus_depth(fnt, cur->glyph); + } + if (a < 0) { + formatted_error("fonts","bad extensible character %i in font %i",chr,fnt); + } } b_max += a - c; prev_overlap = cur->end_overlap; @@ -1236,12 +1399,15 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s c = prev_overlap; a = cur->advance; if (a == 0) { - /* for tfm fonts */ - if (boxtype == vlist_node) - a = height_plus_depth(f, cur->glyph); - else - a = char_width(f, cur->glyph); - assert(a >= 0); + /*tex for tfm fonts */ + if (horizontal) { + a = char_width(fnt, cur->glyph); + } else { + a = height_plus_depth(fnt, cur->glyph); + } + if (a < 0) { + formatted_error("fonts","bad extensible character %i in font %i",chr,fnt); + } } b_max += a - c; prev_overlap = cur->end_overlap; @@ -1250,10 +1416,11 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s } } } + /*tex + + Assemble box using |with_extenders| copies of each extender, with + appropriate glue wherever an overlap occurs. - /* - assemble box using |with_extenders| copies of each extender, with - appropriate glue wherever an overlap occurs */ prev_overlap = 0; b_max = 0; @@ -1271,7 +1438,7 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s s_max += (-c) - (-d); b_max -= d; } - b_max += stack_into_box(b, f, cur->glyph); + b_max += stack_into_box(b, fnt, cur->glyph); prev_overlap = cur->end_overlap; i--; } else { @@ -1288,19 +1455,17 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s s_max += (-c) - (-d); b_max -= d; } - b_max += stack_into_box(b, f, cur->glyph); + b_max += stack_into_box(b, fnt, cur->glyph); prev_overlap = cur->end_overlap; i--; } } } - - /* set glue so as to stretch the connections if needed */ - + /*tex Set glue so as to stretch the connections if needed. */ d = 0; if (v > b_max && s_max > 0) { d = v-b_max; - /* don't stretch more than |s_max| */ + /*tex Don't stretch more than |s_max|. */ if (d > s_max) d = s_max; glue_order(b) = normal; @@ -1308,32 +1473,33 @@ static pointer get_delim_box(pointer q, extinfo * ext, internal_font_number f, s glue_set(b) = unfloat(d/(float) s_max); b_max += d; } - - if (boxtype == vlist_node) { - height(b) = b_max; - } else { + if (horizontal) { width(b) = b_max; + } else { + height(b) = b_max; } - return b; } -@ The |var_delimiter| function, which finds or constructs a sufficiently - large delimiter, is the most interesting of the auxiliary functions that - currently concern us. Given a pointer |d| to a delimiter field in some noad, - together with a size code |s| and a vertical distance |v|, this function - returns a pointer to a box that contains the smallest variant of |d| whose - height plus depth is |v| or more. (And if no variant is large enough, it - returns the largest available variant.) In particular, this routine will - construct arbitrarily large delimiters from extensible components, if - |d| leads to such characters. - - The value returned is a box whose |shift_amount| has been set so that - the box is vertically centered with respect to the axis in the given size. - If a built-up symbol is returned, the height of the box before shifting - will be the height of its topmost component. - -@c +/*tex + + The |var_delimiter| function, which finds or constructs a sufficiently large + delimiter, is the most interesting of the auxiliary functions that currently + concern us. Given a pointer |d| to a delimiter field in some noad, together + with a size code |s| and a vertical distance |v|, this function returns a + pointer to a box that contains the smallest variant of |d| whose height plus + depth is |v| or more. (And if no variant is large enough, it returns the + largest available variant.) In particular, this routine will construct + arbitrarily large delimiters from extensible components, if |d| leads to such + characters. + + The value returned is a box whose |shift_amount| has been set so that the box + is vertically centered with respect to the axis in the given size. If a + built-up symbol is returned, the height of the box before shifting will be + the height of its topmost component. + +*/ + static void endless_loop_error(internal_font_number g, int y) { char s[256]; @@ -1343,23 +1509,31 @@ static void endless_loop_error(internal_font_number g, int y) "I will jump out of the loop all by myself now. Fix your font!", NULL }; - snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)", - (int) y, font_name(g)); + snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)", (int) y, font_name(g)); tex_error(s, hlp); } static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, int cur_style, boolean shift, boolean *stack, scaled *delta, int *same) { - pointer b; /* the box that will be constructed */ - internal_font_number f, g; /* best-so-far and tentative font codes */ - int c, i, x, y; /* best-so-far and tentative character codes */ - scaled u; /* height-plus-depth of a tentative character */ - scaled w = 0; /* largest height-plus-depth so far */ - int z; /* runs through font family members */ - boolean large_attempt = false; /* are we trying the ``large'' variant? */ - pointer att = null; /* to save the current attribute list */ + /*tex the box that will be constructed */ + pointer b; + /*tex best-so-far and tentative font codes */ + internal_font_number f, g; + /*tex best-so-far and tentative character codes */ + int c, i, x, y; + /*tex height-plus-depth of a tentative character */ + scaled u; + /*tex largest height-plus-depth so far */ + scaled w = 0; + /*tex runs through font family members */ + int z; + /*tex are we trying the ``large'' variant? */ + boolean large_attempt = false; + /*tex to save the current attribute list */ + pointer att = null; int emas = 0 ; boolean do_parts = false; + boolean parts_done = false; extinfo *ext; f = null_font; c = 0; @@ -1373,10 +1547,12 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, same = 0; } while (true) { - /* - The search process is complicated slightly by the facts that some of the - characters might not be present in some of the fonts, and they might not - be probed in increasing order of height. + /*tex + + The search process is complicated slightly by the facts that some of + the characters might not be present in some of the fonts, and they + might not be probed in increasing order of height. + */ if ((z != 0) || (x != 0)) { g = fam_fnt(z, s); @@ -1403,7 +1579,6 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, goto FOUND; } if (i > 10000) { - /* endless loop */ endless_loop_error(g, y); goto FOUND; } @@ -1414,8 +1589,10 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, } } } - if (large_attempt) - goto FOUND; /* there were none large enough */ + if (large_attempt) { + /*tex There were none large enough. */ + goto FOUND; + } large_attempt = true; z = large_fam(d); x = large_char(d); @@ -1427,17 +1604,20 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, flush_node(d); } if (f != null_font) { - /* - When the following code is executed, |do_parts| will be true - if a built-up symbol is supposed to be returned. + /*tex + + When the following code is executed, |do_parts| will be true if a + built-up symbol is supposed to be returned. + */ ext = NULL; if ((do_parts) && ((!flat && (ext = get_charinfo_vert_variants(char_info(f,c))) != NULL) || ( flat && (ext = get_charinfo_hor_variants (char_info(f,c))) != NULL))) { + parts_done = true; if (flat) { - b = get_delim_box(d, ext, f, v, att, cur_style, hlist_node); + b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 1, att); } else { - b = get_delim_box(d, ext, f, v, att, cur_style, vlist_node); + b = get_delim_box(f, c, v, connector_overlap_min(cur_style), 0, att); } if (delta != NULL) { if (do_new_math(f)) { @@ -1449,16 +1629,18 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, if (stack != NULL) *stack = true ; } else { + parts_done = false; if (same != NULL && x == c) { *same = emas; } b = char_box(f, c, att); if (!do_new_math(f)) { - /* italic gets added to width */ + /*tex Italic gets added to width. */ width(b) += char_italic(f, c); } if (delta != NULL) { - *delta = char_italic(f, c); /* was historically (f, x) */ + /*tex This used to be (f, x). */ + *delta = char_italic(f, c); } if (stack != NULL) *stack = false ; @@ -1469,7 +1651,7 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, if (flat) { width(b) = 0; } else { - /* use this width if no delimiter was found */ + /*tex Use this width if no delimiter was found. */ width(b) = null_delimiter_space_par; } if (delta != NULL) { @@ -1479,38 +1661,54 @@ static pointer do_delimiter(pointer q, pointer d, int s, scaled v, boolean flat, *stack = false ; } if (!flat) { - if (emas == 0 || ! delimitermodenoshift) { - /* vertical variant */ - shift_amount(b) = half(height(b) - depth(b)); - if (shift) { - shift_amount(b) -= math_axis_size(s); - } + /*tex when emas ~= 0 then we have a non scaled character */ + if (emas != 0 && delimitermodesamenos) { + /*tex same character and no shift when same forced */ + goto DONE; + } + if (! parts_done && delimitermodecharnos) { + /*tex same character and no shift when same forced */ + goto DONE; + } + if (delimitermodenoshift) { + /*tex no shift forced */ + goto DONE; + } + /*tex vertical variant */ + shift_amount(b) = half(height(b) - depth(b)); + if (shift) { + shift_amount(b) -= math_axis_size(s); } } + DONE: delete_attribute_ref(att); return b; } -@ The next subroutine is much simpler; it is used for numerators and -denominators of fractions as well as for displayed operators and -their limits above and below. It takes a given box~|b| and -changes it so that the new box is centered in a box of width~|w|. -The centering is done by putting \.{\\hss} glue at the left and right -of the list inside |b|, then packaging the new box; thus, the -actual box might not really be centered, if it already contains -infinite glue. +/*tex + + The next subroutine is much simpler; it is used for numerators and + denominators of fractions as well as for displayed operators and their limits + above and below. It takes a given box~|b| and changes it so that the new box + is centered in a box of width~|w|. The centering is done by putting \.{\\hss} + glue at the left and right of the list inside |b|, then packaging the new + box; thus, the actual box might not really be centered, if it already + contains infinite glue. + + The given box might contain a single character whose italic correction has + been added to the width of the box; in this case a compensating kern is + inserted. -The given box might contain a single character whose italic correction -has been added to the width of the box; in this case a compensating -kern is inserted. +*/ -@c static pointer rebox(pointer b, scaled w) { - pointer p, q, r, att; /* temporary registers for list manipulation */ - internal_font_number f; /* font in a one-character box */ - scaled v; /* width of a character without italic correction */ - + /*tex temporary registers for list manipulation */ + pointer p, q, r, att; + /*tex font in a one-character box */ + internal_font_number f; + /*tex width of a character without italic correction */ + scaled v; if ((width(b) != w) && (list_ptr(b) != null)) { if (type(b) == vlist_node) { p = hpack(b, 0, additional, -1); @@ -1549,23 +1747,30 @@ static pointer rebox(pointer b, scaled w) } } -@ Here is a subroutine that creates a new glue specification from another -one that is expressed in `\.{mu}', given the value of the math unit. +/*tex + + Here is a subroutine that creates a new glue specification from another one + that is expressed in `\.{mu}', given the value of the math unit. + +*/ -@c #define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen) static pointer math_glue(pointer g, scaled m) { - int n = x_over_n(m, unity); /* integer part of |m| */ - scaled f = tex_remainder; /* fraction part of |m| */ - pointer p; /* the new glue specification */ + /*tex integer part of |m| */ + int n = x_over_n(m, unity); + /*tex fraction part of |m| */ + scaled f = tex_remainder; + /*tex the new glue specification */ + pointer p; if (f < 0) { decr(n); f = f + unity; } p = new_node(glue_node, 0); - width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */ + /* convert \.{mu} to \.{pt} */ + width(p) = mu_mult(width(g)); stretch_order(p) = stretch_order(g); if (stretch_order(p) == normal) stretch(p) = mu_mult(stretch(g)); @@ -1581,13 +1786,16 @@ static pointer math_glue(pointer g, scaled m) static void math_glue_to_glue(pointer p, scaled m) { - int n = x_over_n(m, unity); /* integer part of |m| */ - scaled f = tex_remainder; /* fraction part of |m| */ + /*tex integer part of |m| */ + int n = x_over_n(m, unity); + /*tex fraction part of |m| */ + scaled f = tex_remainder; if (f < 0) { decr(n); f = f + unity; } - width(p) = mu_mult(width(p)); /* convert \.{mu} to \.{pt} */ + /* convert \.{mu} to \.{pt} */ + width(p) = mu_mult(width(p)); if (stretch_order(p) == normal) stretch(p) = mu_mult(stretch(p)); if (shrink_order(p) == normal) @@ -1595,14 +1803,18 @@ static void math_glue_to_glue(pointer p, scaled m) subtype(p) = normal; } -@ The |math_kern| subroutine removes |mu_glue| from a kern node, given -the value of the math unit. +/*tex + + The |math_kern| subroutine removes |mu_glue| from a kern node, given the + value of the math unit. -@c +*/ static void math_kern(pointer p, scaled m) { - int n; /* integer part of |m| */ - scaled f; /* fraction part of |m| */ + /*tex integer part of |m| */ + int n; + /*tex fraction part of |m| */ + scaled f; if (subtype(p) == mu_glue) { n = x_over_n(m, unity); f = tex_remainder; @@ -1611,15 +1823,15 @@ static void math_kern(pointer p, scaled m) f = f + unity; } width(p) = mu_mult(width(p)); - subtype(p) = italic_kern; /* this is weird, it's not a italic but explicit_kern */ + /* this is weird, it's not a italic but explicit_kern */ + subtype(p) = italic_kern; } } -@ @c void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle) { int callback_id; - int a, sfix; + int a, sfix, i; if (p == null) { vlink(temp_head) = null; return; @@ -1636,18 +1848,16 @@ void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle) nodelist_to_lua(Luas, p); lua_push_math_style_name(Luas, mstyle); lua_pushboolean(Luas, penalties); - if (lua_pcall(Luas, 3, 1, 0) != 0) { /* 3 args, 1 result */ - char errmsg[256]; /* temp hack ... we will have a formatted error */ - snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1)); - errmsg[255]='\0'; + if ((i=lua_pcall(Luas, 3, 1, 0)) != 0) { + formatted_warning("mlist to hlist","error: %s",lua_tostring(Luas, -1)); lua_settop(Luas, sfix); - normal_error("mlist to hlist",errmsg); /* to be done */ + luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); return; } - a = nodelist_from_lua(Luas); + a = nodelist_from_lua(Luas,-1); /* alink(vlink(a)) = null; */ - lua_settop(Luas, sfix); vlink(temp_head) = a; + lua_settop(Luas, sfix); } else if (callback_id == 0) { mlist_to_hlist(p, penalties, mstyle); } else { @@ -1655,22 +1865,27 @@ void run_mlist_to_hlist(halfword p, boolean penalties, int mstyle) } } -@ The recursion in |mlist_to_hlist| is due primarily to a subroutine -called |clean_box| that puts a given noad field into a box using a given -math style; |mlist_to_hlist| can call |clean_box|, which can call -|mlist_to_hlist|. -@^recursion@> +/*tex + + The recursion in |mlist_to_hlist| is due primarily to a subroutine called + |clean_box| that puts a given noad field into a box using a given math style; + |mlist_to_hlist| can call |clean_box|, which can call |mlist_to_hlist|. -The box returned by |clean_box| is ``clean'' in the -sense that its |shift_amount| is zero. + The box returned by |clean_box| is ``clean'' in the sense that its + |shift_amount| is zero. + +*/ -@c static pointer clean_box(pointer p, int s, int cur_style) { - pointer q; /* beginning of a list to be boxed */ - pointer x; /* box to be returned */ - pointer r; /* temporary pointer */ - pointer mlist = null; /* beginning of mlist to be translated */ + /*tex beginning of a list to be boxed */ + pointer q; + /*tex box to be returned */ + pointer x; + /*tex temporary pointer */ + pointer r; + /*tex beginning of mlist to be translated */ + pointer mlist = null; switch (type(p)) { case math_char_node: mlist = new_noad(); @@ -1689,18 +1904,20 @@ static pointer clean_box(pointer p, int s, int cur_style) goto FOUND; } mlist_to_hlist(mlist, false, s); - q = vlink(temp_head); /* recursive call */ + /*tex recursive call */ + q = vlink(temp_head); setup_cur_size(cur_style); FOUND: if (is_char_node(q) || (q == null)) x = hpack(q, 0, additional, -1); else if ((vlink(q) == null) && (type(q) <= vlist_node) && (shift_amount(q) == 0)) - x = q; /* it's already clean */ + /*tex It's already clean. */ + x = q; else x = hpack(q, 0, additional, -1); if (x != q && q != null) reset_attributes(x, node_attr(q)); - /* Here we save memory space in a common case. */ + /*tex Here we save memory space in a common case. */ q = list_ptr(x); if (is_char_node(q)) { r = vlink(q); @@ -1708,7 +1925,7 @@ static pointer clean_box(pointer p, int s, int cur_style) if (vlink(r) == null) { if (!is_char_node(r)) { if (type(r) == kern_node) { - /* unneeded italic correction */ + /*tex Unneeded italic correction. */ flush_node(r); vlink(q) = null; } @@ -1719,22 +1936,30 @@ static pointer clean_box(pointer p, int s, int cur_style) return x; } -@ It is convenient to have a procedure that converts a |math_char| -field to an ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c| -to the font code and character code of a given noad field. -It also takes care of issuing error messages for -nonexistent characters; in such cases, |char_exists(cur_f,cur_c)| will be |false| -after |fetch| has acted, and the field will also have been reset to |null|. +/*tex -The outputs of |fetch| are placed in global variables. + It is convenient to have a procedure that converts a |math_char| field to an + ``unpacked'' form. The |fetch| routine sets |cur_f| and |cur_c| to the font + code and character code of a given noad field. It also takes care of issuing + error messages for nonexistent characters; in such cases, + |char_exists(cur_f,cur_c)| will be |false| after |fetch| has acted, and the + field will also have been reset to |null|. -@c -internal_font_number cur_f; /* the |font| field of a |math_char| */ -int cur_c; /* the |character| field of a |math_char| */ + The outputs of |fetch| are placed in global variables. -@ Here we unpack the |math_char| field |a|. +*/ + +/*tex the |font| field of a |math_char| */ + +internal_font_number cur_f; + +/*tex the |character| field of a |math_char| */ -@c static void fetch(pointer a) +int cur_c; + +/*tex Here we unpack the |math_char| field |a|. */ + +static void fetch(pointer a) { cur_c = math_character(a); cur_f = fam_fnt(math_fam(a), cur_size); @@ -1757,75 +1982,125 @@ int cur_c; /* the |character| field of a |math_char| */ } } -@ We need to do a lot of different things, so |mlist_to_hlist| makes two -passes over the given mlist. +/*tex + + We need to do a lot of different things, so |mlist_to_hlist| makes two passes + over the given mlist. -The first pass does most of the processing: It removes ``mu'' spacing from -glue, it recursively evaluates all subsidiary mlists so that only the -top-level mlist remains to be handled, it puts fractions and square roots -and such things into boxes, it attaches subscripts and superscripts, and -it computes the overall height and depth of the top-level mlist so that -the size of delimiters for a |fence_noad| will be known. -The hlist resulting from each noad is recorded in that noad's |new_hlist| -field, an integer field that replaces the |nucleus| or |thickness|. -@^recursion@> + The first pass does most of the processing: It removes ``mu'' spacing from + glue, it recursively evaluates all subsidiary mlists so that only the + top-level mlist remains to be handled, it puts fractions and square roots and + such things into boxes, it attaches subscripts and superscripts, and it + computes the overall height and depth of the top-level mlist so that the size + of delimiters for a |fence_noad| will be known. The hlist resulting from each + noad is recorded in that noad's |new_hlist| field, an integer field that + replaces the |nucleus| or |thickness|. -The second pass eliminates all noads and inserts the correct glue and -penalties between nodes. + The second pass eliminates all noads and inserts the correct glue and + penalties between nodes. + +*/ -@c static void assign_new_hlist(pointer q, pointer r) { switch (type(q)) { - case fraction_noad: - math_list(numerator(q)) = null; - flush_node(numerator(q)); - numerator(q) = null; - math_list(denominator(q)) = null; - flush_node(denominator(q)); - denominator(q) = null; - break; - case radical_noad: - case simple_noad: - case accent_noad: - if (nucleus(q) != null) { - math_list(nucleus(q)) = null; - flush_node(nucleus(q)); - nucleus(q) = null; - } - break; + case fraction_noad: + math_list(numerator(q)) = null; + flush_node(numerator(q)); + numerator(q) = null; + math_list(denominator(q)) = null; + flush_node(denominator(q)); + denominator(q) = null; + break; + case radical_noad: + case simple_noad: + case accent_noad: + if (nucleus(q) != null) { + math_list(nucleus(q)) = null; + flush_node(nucleus(q)); + nucleus(q) = null; + } + break; } new_hlist(q) = r; } -@ @c #define choose_mlist(A) do { p=A(q); A(q)=null; } while (0) -@ Most of the actual construction work of |mlist_to_hlist| is done -by procedures with names like |make_fraction|, |make_radical|, etc. To -illustrate the general setup of such procedures, let's begin with a -couple of simple ones. +/*tex + + Most of the actual construction work of |mlist_to_hlist| is done by + procedures with names like |make_fraction|, |make_radical|, etc. To + illustrate the general setup of such procedures, let's begin with a couple of + simple ones. + +*/ -@c static void make_over(pointer q, int cur_style, int cur_size, int cur_fam) { + /*tex + + No rule adaption yet, maybe never as overbars should be proper + extensibles. + + */ pointer p; + scaled f, t; + scaled used_thickness = overbar_rule(cur_style); + scaled used_fam = cur_fam; + if (math_rule_thickness_mode_par > 0) { + f = noad_fam(q); + if (f >= 0) { + t = fam_fnt(f,cur_size); + if (do_new_math(t)) { + t = font_MATH_par(t, OverbarRuleThickness); + if (t != undefined_math_parameter) { + used_thickness = t; + used_fam = f; + } + } + } + } p = overbar(clean_box(nucleus(q), cramped_style(cur_style), cur_style), - overbar_vgap(cur_style), overbar_rule(cur_style), - overbar_kern(cur_style), node_attr(nucleus(q)), math_over_rule, cur_size, cur_fam); + overbar_vgap(cur_style), used_thickness, overbar_kern(cur_style), + node_attr(nucleus(q)), math_over_rule, cur_size, used_fam); math_list(nucleus(q)) = p; type(nucleus(q)) = sub_box_node; } static void make_under(pointer q, int cur_style, int cur_size, int cur_fam) { - pointer p, x, y, r; /* temporary registers for box construction */ - scaled delta; /* overall height plus depth */ + /*tex + + No rule adaption yet, maybe never as underbars should be proper + extensibles. + + */ + /*tex temporary registers for box construction */ + pointer p, x, y, r; + /*tex overall height plus depth */ + scaled delta; + scaled f, t; + scaled used_thickness = underbar_rule(cur_style); + scaled used_fam = cur_fam; x = clean_box(nucleus(q), cur_style, cur_style); p = new_kern(underbar_vgap(cur_style)); reset_attributes(p, node_attr(q)); couple_nodes(x,p); - r = do_fraction_rule(underbar_rule(cur_style), node_attr(q), math_under_rule, cur_size, cur_fam); + if (math_rule_thickness_mode_par > 0) { + f = noad_fam(q); + if (f >= 0) { + t = fam_fnt(f,cur_size); + if (do_new_math(t)) { + t = font_MATH_par(t, UnderbarRuleThickness); + if (t != undefined_math_parameter) { + used_thickness = t; + used_fam = f; + } + } + } + } + r = do_fraction_rule(used_thickness, node_attr(q), math_under_rule, cur_size, used_fam); couple_nodes(p,r); y = vpackage(x, 0, additional, max_dimen, math_direction_par); reset_attributes(y, node_attr(q)); @@ -1838,8 +2113,10 @@ static void make_under(pointer q, int cur_style, int cur_size, int cur_fam) static void make_vcenter(pointer q) { - pointer v; /* the box that should be centered vertically */ - scaled delta; /* its height plus depth */ + /*tex the box that should be centered vertically */ + pointer v; + /*tex its height plus depth */ + scaled delta; v = math_list(nucleus(q)); if (type(v) != vlist_node) confusion("vcenter"); @@ -1848,16 +2125,19 @@ static void make_vcenter(pointer q) depth(v) = delta - height(v); } -@ According to the rules in the \.{DVI} file specifications, we ensure alignment -@^square roots@> -between a square root sign and the rule above its nucleus by assuming that the -baseline of the square-root symbol is the same as the bottom of the rule. The -height of the square-root symbol will be the thickness of the rule, and the -depth of the square-root symbol should exceed or equal the height-plus-depth -of the nucleus plus a certain minimum clearance~|psi|. The symbol will be -placed so that the actual clearance is |psi| plus half the excess. +/*tex + + According to the rules in the \.{DVI} file specifications, we ensure + alignment between a square root sign and the rule above its nucleus by + assuming that the baseline of the square-root symbol is the same as the + bottom of the rule. The height of the square-root symbol will be the + thickness of the rule, and the depth of the square-root symbol should exceed + or equal the height-plus-depth of the nucleus plus a certain minimum + clearance~|psi|. The symbol will be placed so that the actual clearance is + |psi| plus half the excess. + +*/ -@c static void make_hextension(pointer q, int cur_style) { pointer e, p; @@ -1885,25 +2165,50 @@ static void make_hextension(pointer q, int cur_style) static void make_radical(pointer q, int cur_style) { - pointer x, y, p, l1, l2; /* temporary registers for box construction */ - scaled delta, clr, theta, h; /* dimensions involved in the calculation */ + /*tex temporary registers for box construction */ + pointer x, y, p, l1, l2; + /*tex dimensions involved in the calculation */ + scaled delta, clr, theta, h, f; + scaled t, used_fam ; x = clean_box(nucleus(q), cramped_style(cur_style), cur_style); clr = radical_vgap(cur_style); theta = radical_rule_par(cur_style); + used_fam = small_fam(left_delimiter(q)); + /*tex + + We can take the rule width from the fam/style of the delimiter or use the + most recent math parameters value. + + */ + if (math_rule_thickness_mode_par > 0) { + f = small_fam(left_delimiter(q)); + if (f >= 0) { + t = fam_fnt(f,cur_size); + if (do_new_math(t)) { + t = font_MATH_par(t, RadicalRuleThickness); + if (t != undefined_math_parameter) { + theta = t; + used_fam = f; + } + } + } + } if (theta == undefined_math_parameter) { - /* a real radical */ + /*tex a real radical */ theta = fraction_rule(cur_style); y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL); - /* + /*tex + If |y| is a composite then set |theta| to the height of its top character, else set it to the height of |y|. + */ l1 = list_ptr(y); if ((l1 != null) && (type(l1) == hlist_node)) { - /* possible composite */ + /*tex possible composite */ l2 = list_ptr(l1); if ((l2 != null) && (type(l2) == glyph_node)) { - /* top character */ + /*tex top character */ theta = char_height(font(l2), character(l2)); } else { theta = height(y); @@ -1912,18 +2217,29 @@ static void make_radical(pointer q, int cur_style) theta = height(y); } } else { - /* not really a radical but we use its node, historical sharing (like in mathml) */ + /*tex + + Not really a radical but we use its node, historical sharing (like in + mathml). + + */ y = do_delimiter(q, left_delimiter(q), cur_size, height(x) + depth(x) + clr + theta, false, cur_style, true, NULL, NULL, NULL); } + /*tex + + Weird hack, in overbar we use small_fam(left_delimiter(q)) so actually + small_fam(0). + + */ left_delimiter(q) = null; delta = (depth(y) + height(y) - theta) - (height(x) + depth(x) + clr); if (delta > 0) { - /* increase the actual clearance */ + /*tex increase the actual clearance */ clr = clr + half(delta); } shift_amount(y) = (height(y) - theta) - (height(x) + clr); h = depth(y) + height(y); - p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y), math_radical_rule, cur_size, small_fam(left_delimiter(q))); + p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y), math_radical_rule, cur_size, used_fam); couple_nodes(y,p); if (degree(q) != null) { scaled wr, br, ar; @@ -1949,7 +2265,7 @@ static void make_radical(pointer q, int cur_style) couple_nodes(x,r); y = x; } - /* for \.{\\Uroot ..{<list>}{}} : */ + /*tex for \.{\\Uroot ..{<list>}{}} : */ math_list(degree(q)) = null; flush_node(degree(q)); } @@ -1959,12 +2275,11 @@ static void make_radical(pointer q, int cur_style) type(nucleus(q)) = sub_box_node; } -@ Construct a vlist box +/*tex Construct a vlist box: */ -@c static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scaled shift_up, scaled shift_down) { - pointer p; /* temporary register for box construction */ + pointer p; pointer v = new_null_box(); type(v) = vlist_node; height(v) = shift_up + height(x); @@ -1978,9 +2293,7 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal return v; } -/* when exact use radicalwidth (y is delimiter) */ - -@ @c +/*tex When |exact| use radicalwidth (|y| is delimiter). */ #define fixup_widths(q,x,y) do { \ if (width(y) >= width(x)) { \ @@ -2014,7 +2327,7 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal width(r) = radicalwidth(q); \ reset_attributes(r, node_attr(q)); \ } else if (radicalright(q)) { \ - /* also kind of exact compared to vertical */ \ + /*tex also kind of exact compared to vertical */ \ r = hpack(r, 0, additional, -1); \ width(r) = radicalwidth(q); \ reset_attributes(r, node_attr(q)); \ @@ -2030,12 +2343,15 @@ static pointer wrapup_over_under_delimiter(pointer x, pointer y, pointer q, scal } \ } while (0) -@ this has the |nucleus| box |x| as a limit above an extensible delimiter |y| +/*tex + + This has the |nucleus| box |x| as a limit above an extensible delimiter |y|. + +*/ -@c static void make_over_delimiter(pointer q, int cur_style) { - pointer x, y, v; /* temporary registers for box construction */ + pointer x, y, v; scaled shift_up, shift_down, clr, delta, wd; boolean stack; x = clean_box(nucleus(q), sub_style(cur_style), cur_style); @@ -2052,17 +2368,21 @@ static void make_over_delimiter(pointer q, int cur_style) shift_up = shift_up + delta; } v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down); - width(v) = width(x); /* this also equals |width(y)| */ + /*tex This also equals |width(y)|: */ + width(v) = width(x); math_list(nucleus(q)) = v; type(nucleus(q)) = sub_box_node; } -@ this has the extensible delimiter |x| as a limit below |nucleus| box |y| +/*tex + + This has the extensible delimiter |x| as a limit below |nucleus| box |y|. + +*/ -@c static void make_under_delimiter(pointer q, int cur_style) { - pointer x, y, v; /* temporary registers for box construction */ + pointer x, y, v; scaled shift_up, shift_down, clr, delta, wd; boolean stack; y = clean_box(nucleus(q), sup_style(cur_style), cur_style); @@ -2079,17 +2399,21 @@ static void make_under_delimiter(pointer q, int cur_style) shift_down = shift_down + delta; } v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down); - width(v) = width(y); /* this also equals |width(y)| */ + /*tex This also equals |width(y)|: */ + width(v) = width(y); math_list(nucleus(q)) = v; type(nucleus(q)) = sub_box_node; } -@ this has the extensible delimiter |x| as a limit above |nucleus| box |y| +/*tex + + This has the extensible delimiter |x| as a limit above |nucleus| box |y|. + +*/ -@c static void make_delimiter_over(pointer q, int cur_style) { - pointer x, y, v; /* temporary registers for box construction */ + pointer x, y, v; scaled shift_up, shift_down, clr, actual, wd; boolean stack; y = clean_box(nucleus(q), cur_style, cur_style); @@ -2106,17 +2430,21 @@ static void make_delimiter_over(pointer q, int cur_style) shift_up = shift_up + (clr-actual); } v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down); - width(v) = width(x); /* this also equals |width(y)| */ + /*tex This also equals |width(y)|: */ + width(v) = width(x); math_list(nucleus(q)) = v; type(nucleus(q)) = sub_box_node; } -@ this has the extensible delimiter |y| as a limit below a |nucleus| box |x| +/*tex + + This has the extensible delimiter |y| as a limit below a |nucleus| box |x|. + +*/ -@c static void make_delimiter_under(pointer q, int cur_style) { - pointer x, y, v; /* temporary registers for box construction */ + pointer x, y, v; scaled shift_up, shift_down, clr, actual, wd; boolean stack; x = clean_box(nucleus(q), cur_style, cur_style); @@ -2133,16 +2461,20 @@ static void make_delimiter_under(pointer q, int cur_style) shift_down += (clr-actual); } v = wrapup_over_under_delimiter(x, y, q, shift_up, shift_down); - width(v) = width(y); /* this also equals |width(y)| */ + /*tex This also equals |width(y)|: */ + width(v) = width(y); math_list(nucleus(q)) = v; type(nucleus(q)) = sub_box_node; } -@ Slants are not considered when placing accents in math mode. The accenter is -centered over the accentee, and the accent width is treated as zero with -respect to the size of the final box. +/*tex + + Slants are not considered when placing accents in math mode. The accenter is + centered over the accentee, and the accent width is treated as zero with + respect to the size of the final box. + +*/ -@c #define TOP_CODE 1 #define BOT_CODE 2 #define OVERLAY_CODE 4 @@ -2150,14 +2482,17 @@ respect to the size of the final box. static boolean compute_accent_skew(pointer q, int flags, scaled *s) { - pointer p; /* temporary register for box construction */ - boolean s_is_absolute = false; /* will be true if a top-accent is placed in |s| */ + /*tex temporary register for box construction */ + pointer p; + /*tex will be true if a top-accent is placed in |s| */ + boolean s_is_absolute = false; if (type(nucleus(q)) == math_char_node) { fetch(nucleus(q)); if (do_new_math(cur_f)) { - /* - there is no bot_accent so let's assume similarity + /*tex + There is no bot_accent so let's assume similarity + \starttyping if (flags & (TOP_CODE | OVERLAY_CODE)) { *s = char_top_accent(cur_f, cur_c); if (*s != INT_MIN) { @@ -2169,6 +2504,7 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s) s_is_absolute = true; } } + \stoptyping */ *s = char_top_accent(cur_f, cur_c); if (*s != INT_MIN) { @@ -2182,15 +2518,24 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s) } } } else if (type(nucleus(q)) == sub_mlist_node) { - /* - if |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we - - * use the positioning of the nucleus of that noad, recursing until - * the inner most |accent_noad|. This way multiple stacked accents are - * aligned to the inner most one. - - the vlink test was added in version 1.06, so that we only consider a lone - noad: + /*tex + If |nucleus(q)| is a |sub_mlist_node| composed of an |accent_noad| we: + + \startitemize + \startitem + use the positioning of the nucleus of that noad, recursing until + \stopitem + \startitem + the inner most |accent_noad|. This way multiple stacked accents + are + \stopitem + \startitem + aligned to the inner most one. + \stopitem + \stoptitemize + + The vlink test was added in version 1.06, so that we only consider a + lone noad: $ \Umathaccent bottom 0 0 "023DF { \Umathaccent fixed 0 0 "00302 { m } r } \quad @@ -2213,12 +2558,18 @@ static boolean compute_accent_skew(pointer q, int flags, scaled *s) static void do_make_math_accent(pointer q, internal_font_number f, int c, int flags, int cur_style) { - pointer p, r, x, y; /* temporary registers for box construction */ - scaled s; /* amount to skew the accent to the right */ - scaled h; /* height of character being accented */ - scaled delta; /* space to remove between accent and accentee */ - scaled w; /* width of the accentee, not including sub/superscripts */ - boolean s_is_absolute; /* will be true if a top-accent is placed in |s| */ + /*tex temporary registers for box construction */ + pointer p, r, x, y; + /*tex amount to skew the accent to the right */ + scaled s; + /*tex height of character being accented */ + scaled h; + /*tex space to remove between accent and accentee */ + scaled delta; + /*tex width of the accentee, not including sub/superscripts */ + scaled w; + /*tex will be true if a top-accent is placed in |s| */ + boolean s_is_absolute; scaled fraction ; scaled ic = 0; scaled target ; @@ -2232,7 +2583,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl if (fraction == 0) { fraction = 1000; } - /* Compute the amount of skew, or set |s| to an alignment point */ + /*tex Compute the amount of skew, or set |s| to an alignment point */ s_is_absolute = compute_accent_skew(q, flags, &s); x = clean_box(nucleus(q), cramped_style(cur_style), cur_style); w = width(x); @@ -2241,7 +2592,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl s = half(w); s_is_absolute = true; } - /* Switch to a larger accent if available and appropriate */ + /*tex Switch to a larger accent if available and appropriate */ y = null; ext = NULL; if (flags & OVERLAY_CODE) { @@ -2258,31 +2609,32 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl } } if ((flags & STRETCH_ACCENT_CODE) && (char_width(f, c) < w)) { - while (1) { - if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) { - /* a bit weird for an overlay but anyway, here we don't need a factor as we don't step */ - y = get_delim_box(q, ext, f, w, node_attr(attr_p), cur_style, hlist_node); - break; - } else if (char_tag(f, c) != list_tag) { - break; - } else { - int yy = char_remainder(f, c); - if (!char_exists(f, yy)) { + while (1) { + if ((char_tag(f, c) == ext_tag) && ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) { + /*tex a bit weird for an overlay but anyway, here we don't need a factor as we don't step */ + y = get_delim_box(f, c, w, connector_overlap_min(cur_style), 1, node_attr(attr_p)); + break; + } else if (char_tag(f, c) != list_tag) { break; - } else if (flags & OVERLAY_CODE) { - if (char_height(f, yy) > target) { - break; - } } else { - if (char_width(f, yy) > target) - break; + int yy = char_remainder(f, c); + if (!char_exists(f, yy)) { + break; + } else if (flags & OVERLAY_CODE) { + if (char_height(f, yy) > target) { + break; + } + } else { + if (char_width(f, yy) > target) + break; + } + c = yy; } - c = yy; } - } } if (y == null) { - y = char_box(f, c, node_attr(attr_p)); /* italic gets added to width */ + /*tex italic gets added to width */ + y = char_box(f, c, node_attr(attr_p)); } if (flags & TOP_CODE) { if (h < accent_base_height(f)) { @@ -2291,13 +2643,14 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl delta = accent_base_height(f); } } else if (flags & OVERLAY_CODE) { - delta = half(height(y) + depth(y) + height(x) + depth(x)); /* center the accent vertically around the accentee */ + /*tex center the accent vertically around the accentee */ + delta = half(height(y) + depth(y) + height(x) + depth(x)); } else { delta = 0; /* hm */ } if ((supscr(q) != null) || (subscr(q) != null)) { if (type(nucleus(q)) == math_char_node) { - /* swap the subscript and superscript into box |x| */ + /*tex swap the subscript and superscript into box |x| */ flush_node_list(x); x = new_noad(); r = math_clone(nucleus(q)); @@ -2313,32 +2666,34 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl h = height(x); } } else if ((vlink(q) != null) && (type(nucleus(q)) == math_char_node)) { - /* only pure math char nodes */ + /*tex only pure math char nodes */ internal_font_number f = fam_fnt(math_fam(nucleus(q)),cur_size); if (do_new_math(f)) { ic = char_italic(f,math_character(nucleus(q))); } } - /* the top accents of both characters are aligned */ + /*tex the top accents of both characters are aligned */ if (s_is_absolute) { scaled sa; if (ext != NULL) { - /* if the accent is extensible just take the center */ + /*tex if the accent is extensible just take the center */ sa = half(width(y)); } else { - /* - there is no bot_accent so let's assume similarity + /*tex + There is no bot_accent so let's assume similarity + \starttyping if (flags & BOT_CODE) { sa = char_bot_accent(f, c); } else { sa = char_top_accent(f, c); } + \stoptyping */ sa = char_top_accent(f, c); } if (sa == INT_MIN) { - /* just take the center */ + /*tex just take the center */ sa = half(width(y)); } if (math_direction_par == dir_TRT) { @@ -2371,7 +2726,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl y = r; if (flags & (TOP_CODE | OVERLAY_CODE)) { if (height(y) < h) { - /* make the height of box |y| equal to |h| */ + /*tex make the height of box |y| equal to |h| */ p = new_kern(h - height(y)); reset_attributes(p, node_attr(q)); try_couple_nodes(p,list_ptr(y)); @@ -2382,7 +2737,7 @@ static void do_make_math_accent(pointer q, internal_font_number f, int c, int fl shift_amount(y) = -(h - height(y)); } if (ic != 0) { - /* old font codepath has ic built in, new font code doesn't */ + /*tex old font codepath has ic built in, new font code doesn't */ width(r) += ic ; } math_list(nucleus(q)) = y; @@ -2420,26 +2775,48 @@ static void make_math_accent(pointer q, int cur_style) } } -@ The |make_fraction| procedure is a bit different because it sets -|new_hlist(q)| directly rather than making a sub-box. +/*tex + + The |make_fraction| procedure is a bit different because it sets + |new_hlist(q)| directly rather than making a sub-box. + +*/ -@c static void make_fraction(pointer q, int cur_style) { - pointer p, p1, p2, v, x, y, z, l, r, m; /* temporary registers for box construction */ - scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2; - /* dimensions for box calculations */ + pointer p, p1, p2, v, x, y, z, l, r, m; + scaled delta, delta1, delta2, shift_up, shift_down, clr1, clr2, f, t;\ + /*tex + + We can take the rule width from an explicitly set fam, even if a fraction + itself has no character, otherwise we just use the math parameter. + + */ + scaled used_fam = math_rules_fam_par; + if (math_rule_thickness_mode_par > 0 && thickness(q) != 0) { + f = fraction_fam(q); + if (f >= 0) { + t = fam_fnt(f,cur_size); + if (do_new_math(t)) { + t = font_MATH_par(t, FractionRuleThickness); + if (t != undefined_math_parameter) { + thickness(q) = t; + used_fam = f; + } + } + } + } if (thickness(q) == default_code) thickness(q) = fraction_rule(cur_style); - /* + /*tex + Create equal-width boxes |x| and |z| for the numerator and denominator, and compute the default amounts |shift_up| and |shift_down| by which they - are displaced from the baseline - */ + are displaced from the baseline. + */ x = clean_box(numerator(q), num_style(cur_style), cur_style); z = clean_box(denominator(q), denom_style(cur_style), cur_style); - if (middle_delimiter(q) != null) { delta = 0; m = do_delimiter(q, middle_delimiter(q), cur_size, delta, false, cur_style, true, NULL, NULL, NULL); @@ -2452,17 +2829,18 @@ static void make_fraction(pointer q, int cur_style) z = rebox(z, width(x)); } } - if (m != null) { shift_up = 0; shift_down = 0; } else if (thickness(q) == 0) { shift_up = stack_num_up(cur_style); shift_down = stack_denom_down(cur_style); - /* - the numerator and denominator must be separated by a certain minimum - clearance, called |clr| in the following program. The difference between - |clr| and the actual clearance is |2delta|. + /*tex + + The numerator and denominator must be separated by a certain minimum + clearance, called |clr| in the following program. The difference + between |clr| and the actual clearance is |2delta|. + */ clr1 = stack_vgap(cur_style); delta = half(clr1 - ((shift_up - depth(x)) - (height(z) - shift_down))); @@ -2473,9 +2851,11 @@ static void make_fraction(pointer q, int cur_style) } else { shift_up = fraction_num_up(cur_style); shift_down = fraction_denom_down(cur_style); - /* - in the case of a fraction line, the minimum clearance depends on the actual - thickness of the line. + /*tex + + In the case of a fraction line, the minimum clearance depends on the + actual thickness of the line. + */ clr1 = fraction_num_vgap(cur_style); clr2 = fraction_denom_vgap(cur_style); @@ -2497,15 +2877,17 @@ static void make_fraction(pointer q, int cur_style) } } if (m != null) { - /* - construct a hlist box for the fraction, according to |hgap| and |vgap| + /*tex + + Construct a hlist box for the fraction, according to |hgap| and + |vgap|. + */ shift_up = skewed_fraction_vgap(cur_style); if (!fractionnoaxis(q)) { shift_up += half(math_axis_size(cur_size)); } - shift_down = shift_up; v = new_null_box(); reset_attributes(v, node_attr(q)); @@ -2516,7 +2898,6 @@ static void make_fraction(pointer q, int cur_style) depth(v) = depth(x); shift_amount(v) = - shift_up; x = v; - v = new_null_box(); reset_attributes(v, node_attr(q)); type(v) = hlist_node; @@ -2526,7 +2907,6 @@ static void make_fraction(pointer q, int cur_style) depth(v) = depth(z) + shift_down; shift_amount(v) = shift_down; z = v; - v = new_null_box(); reset_attributes(v, node_attr(q)); type(v) = hlist_node; @@ -2546,7 +2926,6 @@ static void make_fraction(pointer q, int cur_style) if (depth(m) > depth(v)) { depth(v) = depth(m); } - if (fractionexact(q)) { delta1 = -half(skewed_fraction_hgap(cur_style)); delta2 = delta1; @@ -2557,33 +2936,34 @@ static void make_fraction(pointer q, int cur_style) width(v) = width(x) + width(z) + skewed_fraction_hgap(cur_style); width(m) = 0; } - p1 = new_kern(delta1); reset_attributes(p1, node_attr(q)); p2 = new_kern(delta2); reset_attributes(p2, node_attr(q)); - couple_nodes(x,p1); couple_nodes(p1,m); couple_nodes(m,p2); couple_nodes(p2,z); - list_ptr(v) = x; } else { - /* - construct a vlist box for the fraction, according to |shift_up| and |shift_down| + /*tex + + Construct a vlist box for the fraction, according to |shift_up| and + |shift_down|. + */ v = new_null_box(); type(v) = vlist_node; height(v) = shift_up + height(x); depth(v) = depth(z) + shift_down; - width(v) = width(x); /* this also equals |width(z)| */ + /*tex This also equals |width(z)|. */ + width(v) = width(x); reset_attributes(v, node_attr(q)); if (thickness(q) == 0) { p = new_kern((shift_up - depth(x)) - (height(z) - shift_down)); couple_nodes(p,z); } else { - y = do_fraction_rule(thickness(q), node_attr(q), math_fraction_rule, cur_size, math_rules_fam_par); + y = do_fraction_rule(thickness(q), node_attr(q), math_fraction_rule, cur_size, used_fam); p = new_kern((math_axis_size(cur_size) - delta) - (height(z) - shift_down)); reset_attributes(p, node_attr(q)); couple_nodes(y,p); @@ -2595,16 +2975,14 @@ static void make_fraction(pointer q, int cur_style) couple_nodes(x,p); list_ptr(v) = x; } - /* - put the fraction into a box with its delimiters, and make |new_hlist(q)| - point to it + /*tex + + Put the fraction into a box with its delimiters, and make |new_hlist(q)| + point to it. + */ if (do_new_math(cur_f)) { - if (math_use_old_fraction_scaling_par) { - delta = fraction_del_size_old(cur_style); - } else { - delta = fraction_del_size_new(cur_style); - } + delta = fraction_del_size_new(cur_style); if (delta == undefined_math_parameter) { delta = get_delimiter_height(depth(v), height(v), true); } @@ -2622,31 +3000,38 @@ static void make_fraction(pointer q, int cur_style) assign_new_hlist(q, y); } -@ If the nucleus of an |op_noad| is a single character, it is to be -centered vertically with respect to the axis, after first being enlarged -(via a character list in the font) if we are in display style. The normal -convention for placing displayed limits is to put them above and below the -operator in display style. +/*tex + + If the nucleus of an |op_noad| is a single character, it is to be centered + vertically with respect to the axis, after first being enlarged (via a + character list in the font) if we are in display style. The normal convention + for placing displayed limits is to put them above and below the operator in + display style. + + The italic correction is removed from the character if there is a subscript + and the limits are not being displayed. The |make_op| routine returns the + value that should be used as an offset between subscript and superscript. -The italic correction is removed from the character if there is a subscript -and the limits are not being displayed. The |make_op| routine returns the -value that should be used as an offset between subscript and superscript. + After |make_op| has acted, |subtype(q)| will be |limits| if and only if the + limits have been set above and below the operator. In that case, + |new_hlist(q)| will already contain the desired final box. -After |make_op| has acted, |subtype(q)| will be |limits| if and only if -the limits have been set above and below the operator. In that case, -|new_hlist(q)| will already contain the desired final box. +*/ -@c static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift); static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same); static scaled make_op(pointer q, int cur_style) { - scaled delta = 0; /* offset between subscript and superscript */ + /*tex offset between subscript and superscript */ + scaled delta = 0; scaled dummy = 0; - pointer p, v, x, y, z, n; /* temporary registers for box construction */ - int c; /* register for character examination */ - scaled shift_up, shift_down; /* dimensions for box calculation */ + /*tex temporary registers for box construction */ + pointer p, v, x, y, z, n; + /*tex register for character examination */ + int c; + /*tex dimensions for box calculation */ + scaled shift_up, shift_down; boolean axis_shift = false; scaled ok_size; if ((subtype(q) == op_noad_type_normal) && (cur_style < text_style)) { @@ -2655,10 +3040,10 @@ static scaled make_op(pointer q, int cur_style) if (type(nucleus(q)) == math_char_node) { fetch(nucleus(q)); if (cur_style < text_style) { - /* try to make it larger */ + /*tex try to make it larger */ ok_size = minimum_operator_size(cur_style); if (ok_size != undefined_math_parameter) { - /* creating a temporary delimiter is the cleanest way */ + /*tex creating a temporary delimiter is the cleanest way */ y = new_node(delim_node, 0); reset_attributes(y, node_attr(q)); small_fam(y) = math_fam(nucleus(q)); @@ -2666,9 +3051,9 @@ static scaled make_op(pointer q, int cur_style) x = do_delimiter(q, y, text_size, ok_size, false, cur_style, true, NULL, &delta, NULL); if (delta != 0) { if (do_new_math(cur_f)) { - /* we never added italic correction */ + /*tex we never added italic correction */ } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { - /* remove italic correction */ + /*tex remove italic correction */ width(x) -= delta; } } @@ -2685,82 +3070,91 @@ static scaled make_op(pointer q, int cur_style) x = clean_box(nucleus(q), cur_style, cur_style); if (delta != 0) { if (do_new_math(cur_f)) { - /* we never added italic correction */ + /*tex we never added italic correction */ } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { - /* remove italic correction */ + /*tex remove italic correction */ width(x) -= delta; } } axis_shift = true; } } else { - /* normal size */ + /*tex normal size */ delta = char_italic(cur_f, cur_c); x = clean_box(nucleus(q), cur_style, cur_style); if (delta != 0) { if (do_new_math(cur_f)) { - /* we never added italic correction */ + /*tex we never added italic correction */ } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { - /* remove italic correction */ + /*tex remove italic correction */ width(x) -= delta; } } axis_shift = true; } if (axis_shift) { - /* center vertically */ + /*tex center vertically */ shift_amount(x) = half(height(x) - depth(x)) - math_axis_size(cur_size); } type(nucleus(q)) = sub_box_node; math_list(nucleus(q)) = x; } - - /* we now handle op_nod_type_no_limits here too */ - + /*tex we now handle op_nod_type_no_limits here too */ if (subtype(q) == op_noad_type_no_limits) { if (do_new_math(cur_f)) { - /* + /*tex + Not: + + \starttyping if (delta != 0) { delta = half(delta) ; } + \stoptyping */ p = check_nucleus_complexity(q, &dummy, cur_style, NULL); if ((subscr(q) == null) && (supscr(q) == null)) { assign_new_hlist(q, p); } else { - /* + /*tex + Not: + + \starttyping make_scripts(q, p, 0, cur_style, delta, -delta); + \stoptyping */ int mode = math_nolimits_mode_par; /* wins */ - /* - for easy configuration ... fonts are somewhat inconsistent and the - values for italic correction run from 30 to 60% of the width + /*tex + + For easy configuration ... fonts are somewhat inconsistent + and the values for italic correction run from 30 to 60\% of. + the width. + */ switch (mode) { case 0 : - /* full bottom correction */ + /*tex full bottom correction */ make_scripts(q, p, 0, cur_style, 0, -delta); break; case 1 : - /* MathConstants driven */ + /*tex |MathConstants| driven */ make_scripts(q, p, 0, cur_style, round_xn_over_d(delta, nolimit_sup_factor(cur_style), 1000), -round_xn_over_d(delta, nolimit_sub_factor(cur_style), 1000)); case 2 : - /* no correction */ + /*tex no correction */ make_scripts(q, p, 0, cur_style, 0, 0); break ; case 3 : - /* half bottom correction */ + /*tex half bottom correction */ make_scripts(q, p, 0, cur_style, 0, -half(delta)); break; case 4 : - /* half bottom and top correction */ + /*tex half bottom and top correction */ make_scripts(q, p, 0, cur_style, half(delta), -half(delta)); break; default : if (mode > 15) { - /* for quickly testing values */ + /*tex for quickly testing values */ make_scripts(q, p, 0, cur_style, 0, -round_xn_over_d(delta, mode, 1000)); } else { make_scripts(q, p, 0, cur_style, 0, 0); @@ -2770,7 +3164,7 @@ static scaled make_op(pointer q, int cur_style) } delta = 0; } else { - /* similar code then the caller (before CHECK_DIMENSIONS) */ + /*tex similar code then the caller (before CHECK_DIMENSIONS) */ p = check_nucleus_complexity(q, &delta, cur_style, NULL); if ((subscr(q) == null) && (supscr(q) == null)) { assign_new_hlist(q, p); @@ -2779,8 +3173,13 @@ static scaled make_op(pointer q, int cur_style) } } } else if (subtype(q) == op_noad_type_limits) { - /* The following program builds a vlist box |v| for displayed limits. The - width of the box is not affected by the fact that the limits may be skewed. */ + /*tex + + The following program builds a vlist box |v| for displayed limits. + The width of the box is not affected by the fact that the limits may + be skewed. + + */ x = clean_box(supscr(q), sup_style(cur_style), cur_style); y = clean_box(nucleus(q), cur_style, cur_style); z = clean_box(subscr(q), sub_style(cur_style), cur_style); @@ -2788,38 +3187,36 @@ static scaled make_op(pointer q, int cur_style) reset_attributes(v, node_attr(q)); type(v) = vlist_node; if (do_new_math(cur_f)) { - n = null; - if (! math_no_italic_compensation_par) { - n = nucleus(q); - if (n != null) { - if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) { - n = math_list(n); - if (n != null) { - if (type(n) == hlist_node) { - n = list_ptr(n); /* just a not scaled char */ - while (n != null) { - if (type(n) == glyph_node) { - delta = char_italic(font(n),character(n)); - } - n = vlink(n); + n = nucleus(q); + if (n != null) { + if ((type(n) == sub_mlist_node) || (type(n) == sub_box_node)) { + n = math_list(n); + if (n != null) { + if (type(n) == hlist_node) { + /*tex just a not scaled char */ + n = list_ptr(n); + while (n != null) { + if (type(n) == glyph_node) { + delta = char_italic(font(n),character(n)); } - } else { - while (n != null) { - if (type(n) == fence_noad) { - if (delimiteritalic(n) > delta) { - /* we can have dummies, the period ones */ - delta = delimiteritalic(n); - } + n = vlink(n); + } + } else { + while (n != null) { + if (type(n) == fence_noad) { + if (delimiteritalic(n) > delta) { + /*tex we can have dummies, the period ones */ + delta = delimiteritalic(n); } - n = vlink(n); } + n = vlink(n); } } - } else { - n = nucleus(q); - if (type(n) == math_char_node) { - delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n)); - } + } + } else { + n = nucleus(q); + if (type(n) == math_char_node) { + delta = char_italic(fam_fnt(math_fam(n),cur_size),math_character(n)); } } } @@ -2834,23 +3231,23 @@ static scaled make_op(pointer q, int cur_style) z = rebox(z, width(v)); shift_amount(x) = half(delta); shift_amount(z) = -shift_amount(x); - /* v is the still empty target */ + /*tex v is the still empty target */ height(v) = height(y); depth(v) = depth(y); - /* - attach the limits to |y| and adjust |height(v)|, |depth(v)| to - account for their presence + /*tex - we use |shift_up| and |shift_down| in the following program for the - amount of glue between the displayed operator |y| and its limits |x| and - |z| + Attach the limits to |y| and adjust |height(v)|, |depth(v)| to + account for their presence. - the vlist inside box |v| will consist of |x| followed by |y| followed - by |z|, with kern nodes for the spaces between and around them + We use |shift_up| and |shift_down| in the following program for the + amount of glue between the displayed operator |y| and its limits |x| + and |z|. - b: baseline v: minumum gap - */ + The vlist inside box |v| will consist of |x| followed by |y| followed + by |z|, with kern nodes for the spaces between and around them; + |b| is baseline and |v| is the minumum gap. + */ if (supscr(q) == null) { list_ptr(x) = null; flush_node(x); @@ -2905,17 +3302,20 @@ static scaled make_op(pointer q, int cur_style) return delta; } -@ A ligature found in a math formula does not create a ligature, because -there is no question of hyphenation afterwards; the ligature will simply be -stored in an ordinary |glyph_node|, after residing in an |ord_noad|. +/*tex -The |type| is converted to |math_text_char| here if we would not want to -apply an italic correction to the current character unless it belongs -to a math font (i.e., a font with |space=0|). + A ligature found in a math formula does not create a ligature, because there + is no question of hyphenation afterwards; the ligature will simply be stored + in an ordinary |glyph_node|, after residing in an |ord_noad|. -No boundary characters enter into these ligatures. + The |type| is converted to |math_text_char| here if we would not want to + apply an italic correction to the current character unless it belongs to a + math font (i.e., a font with |space=0|). + + No boundary characters enter into these ligatures. + +*/ -@c #define simple_char_noad(p) (\ (p != null) && \ (type(p) == simple_noad) && \ @@ -2928,10 +3328,14 @@ No boundary characters enter into these ligatures. static void make_ord(pointer q) { - int a; /* the left-side character for lig/kern testing */ - pointer p, r, s; /* temporary registers for list manipulation */ - scaled k; /* a kern */ - liginfo lig; /* a ligature */ + /*tex the left-side character for lig/kern testing */ + int a; + /*tex temporary registers for list manipulation */ + pointer p, r, s; + /*tex a kern */ + scaled k; + /*tex a ligature */ + liginfo lig; RESTART: if (subscr(q) == null && supscr(q) == null && type(nucleus(q)) == math_char_node) { p = vlink(q); @@ -2939,7 +3343,7 @@ static void make_ord(pointer q) type(nucleus(q)) = math_text_char_node; fetch(nucleus(q)); a = cur_c; - /* add italic correction */ + /*tex add italic correction */ if (do_new_math(cur_f) && (char_italic(cur_f,math_character(nucleus(q))) != 0)) { p = new_kern(char_italic(cur_f,math_character(nucleus(q)))); subtype(p) = italic_kern; @@ -2948,45 +3352,49 @@ static void make_ord(pointer q) couple_nodes(q,p); return; } - /* construct ligatures, quite unlikely in new math fonts */ + /*tex construct ligatures, quite unlikely in new math fonts */ if ((has_kern(cur_f, a)) || (has_lig(cur_f, a))) { cur_c = math_character(nucleus(p)); - /* - if character |a| has a kern with |cur_c|, attach the kern after~|q|; or if - it has a ligature with |cur_c|, combine noads |q| and~|p| appropriately; - then |return| if the cursor has moved past a noad, or |goto restart| + /*tex - note that a ligature between an |ord_noad| and another kind of noad - is replaced by an |ord_noad|, when the two noads collapse into one + If character |a| has a kern with |cur_c|, attach the kern + after~|q|; or if it has a ligature with |cur_c|, combine + noads |q| and~|p| appropriately; then |return| if the cursor + has moved past a noad, or |goto restart|. - we could make a parenthesis (say) change shape when it follows - certain letters. Presumably a font designer will define such - ligatures only when this convention makes sense - */ + Note that a ligature between an |ord_noad| and another kind + of noad is replaced by an |ord_noad|, when the two noads + collapse into one. + We could make a parenthesis (say) change shape when it + follows certain letters. Presumably a font designer will + define such ligatures only when this convention makes sense. + + */ if (disable_lig_par == 0 && has_lig(cur_f, a)) { lig = get_ligature(cur_f, a, cur_c); if (is_valid_ligature(lig)) { - check_interrupt(); /* allow a way out of infinite ligature loop */ + /*tex allow a way out of infinite ligature loop */ + check_interrupt(); switch (lig_type(lig)) { case 1: - /* \.{=:\char`\|} */ + /*tex \.{=:\char`\|} */ case 5: - /* \.{=:\char`\|>} */ + /*tex \.{=:\char`\|>} */ math_character(nucleus(q)) = lig_replacement(lig); break; case 2: - /* \.{\char`\|=:} */ + /*tex \.{\char`\|=:} */ case 6: - /* \.{\char`\|=:>} */ + /*tex \.{\char`\|=:>} */ math_character(nucleus(p)) = lig_replacement(lig); break; case 3: - /* \.{\char`\|=:\char`\|} */ + /*tex \.{\char`\|=:\char`\|} */ case 7: - /* \.{\char`\|=:\char`\|>} */ + /*tex \.{\char`\|=:\char`\|>} */ case 11: - /* \.{\char`\|=:\char`\|>>} */ + /*tex \.{\char`\|=:\char`\|>>} */ r = new_noad(); reset_attributes(r, node_attr(q)); s = new_node(math_char_node, 0); @@ -2996,21 +3404,20 @@ static void make_ord(pointer q) math_fam(nucleus(r)) = math_fam(nucleus(q)); couple_nodes(q,r); couple_nodes(r,p); - if (lig_type(lig) < 11) + if (lig_type(lig) < 11) { type(nucleus(r)) = math_char_node; - else - /* prevent combination */ + } else { + /*tex prevent combination */ type(nucleus(r)) = math_text_char_node; + } break; default: try_couple_nodes(q,vlink(p)); math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:} */ - s = math_clone(subscr(p)); - subscr(q) = s; - s = math_clone(supscr(p)); - supscr(q) = s; - math_reset(subscr(p)); /* just in case */ - math_reset(supscr(p)); + subscr(q) = subscr(p); + supscr(q) = supscr(p); + subscr(p) = null ; + supscr(p) = null ; flush_node(p); break; } @@ -3021,7 +3428,7 @@ static void make_ord(pointer q) } } if (disable_kern_par == 0 && has_kern(cur_f, a)) { - /* todo: should this use mathkerns? */ + /*tex todo: should this use mathkerns? */ k = get_kern(cur_f, a, cur_c); if (k != 0) { p = new_kern(k); @@ -3036,32 +3443,35 @@ static void make_ord(pointer q) } } -@ If the fonts for the left and right bits of a mathkern are not -both new-style fonts, then return a sentinel value meaning: -please use old-style italic correction placement +/*tex + + If the fonts for the left and right bits of a mathkern are not both new-style + fonts, then return a sentinel value meaning: please use old-style italic + correction placement + +*/ -@c #define MATH_KERN_NOT_FOUND 0x7FFFFFFF -@ This function tries to find the kern needed for proper cut-ins. -The left side doesn't move, but the right side does, so the first -order of business is to create a staggered fence line on the -left side of the right character. +/*tex -The microsoft spec says that there are four quadrants, but the -actual images say + This function tries to find the kern needed for proper cut-ins. The left side + doesn't move, but the right side does, so the first order of business is to + create a staggered fence line on the left side of the right character. + + The microsoft spec says that there are four quadrants, but the actual images + say. + +*/ -@c static scaled math_kern_at(internal_font_number f, int c, int side, int v) { int h, k, numkerns; scaled *kerns_heights; scaled kern = 0; - charinfo *co = char_info(f, c); /* known to exist */ + /*tex Known to exist: */ + charinfo *co = char_info(f, c); numkerns = get_charinfo_math_kerns(co, side); -#ifdef DEBUG - fprintf(stderr, " entries = %d, height = %d\n", numkerns, v); -#endif if (numkerns == 0) return kern; if (side == top_left_kern) { @@ -3073,21 +3483,15 @@ static scaled math_kern_at(internal_font_number f, int c, int side, int v) } else if (side == bottom_right_kern) { kerns_heights = co->bottom_right_math_kern_array; } else { + /*tex Not reached: */ confusion("math_kern_at"); - kerns_heights = NULL; /* not reached */ + kerns_heights = NULL; } -#ifdef DEBUG - fprintf(stderr, " entry 0: %d,%d\n", kerns_heights[0], kerns_heights[1]); -#endif if (v < kerns_heights[0]) return kerns_heights[1]; for (k = 0; k < numkerns; k++) { h = kerns_heights[(k * 2)]; kern = kerns_heights[(k * 2) + 1]; -#ifdef DEBUG - if (k > 0) - fprintf(stderr, " entry %d: %d,%d\n", k, h, kern); -#endif if (h > v) { return kern; } @@ -3095,68 +3499,55 @@ static scaled math_kern_at(internal_font_number f, int c, int side, int v) return kern; } -@ @c -static scaled find_math_kern(internal_font_number l_f, int l_c, - internal_font_number r_f, int r_c, - int cmd, scaled shift) +static scaled find_math_kern(internal_font_number l_f, int l_c, internal_font_number r_f, int r_c, int cmd, scaled shift) { scaled corr_height_top = 0, corr_height_bot = 0; scaled krn_l = 0, krn_r = 0, krn = 0; -// if ((!do_new_math(l_f)) || (!do_new_math(r_f)) || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c))) - if ((!(do_new_math(l_f) || do_new_math(r_f))) || (!char_exists(l_f, l_c)) || (!char_exists(r_f, r_c))) + if ((!do_new_math(l_f)) || (!do_new_math(r_f)) || (!char_exists(l_f,l_c)) || (!char_exists(r_f,r_c))) return MATH_KERN_NOT_FOUND; - if (cmd == sup_mark_cmd) { corr_height_top = char_height(l_f, l_c); - corr_height_bot = -char_depth(r_f, r_c) + shift; /* bottom of superscript */ + /*tex bottom of superscript */ + corr_height_bot = -char_depth(r_f, r_c) + shift; krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_top); krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_top); -#ifdef DEBUG - fprintf(stderr, "SUPER Top LR = %d,%d (shift %d)\n", krn_l, krn_r, shift); -#endif krn = (krn_l + krn_r); krn_l = math_kern_at(l_f, l_c, top_right_kern, corr_height_bot); krn_r = math_kern_at(r_f, r_c, bottom_left_kern, corr_height_bot); -#ifdef DEBUG - fprintf(stderr, "SUPER Bot LR = %d,%d\n", krn_l, krn_r); -#endif if ((krn_l + krn_r) < krn) krn = (krn_l + krn_r); return (krn); - } else if (cmd == sub_mark_cmd) { - corr_height_top = char_height(r_f, r_c) - shift; /* top of subscript */ + /*tex top of subscript */ + corr_height_top = char_height(r_f, r_c) - shift; corr_height_bot = -char_depth(l_f, l_c); krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_top); krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_top); -#ifdef DEBUG - fprintf(stderr, "SUB Top LR = %d,%d\n", krn_l, krn_r); -#endif krn = (krn_l + krn_r); krn_l = math_kern_at(l_f, l_c, bottom_right_kern, corr_height_bot); krn_r = math_kern_at(r_f, r_c, top_left_kern, corr_height_bot); -#ifdef DEBUG - fprintf(stderr, "SUB Bot LR = %d,%d\n", krn_l, krn_r); -#endif if ((krn_l + krn_r) < krn) krn = (krn_l + krn_r); return (krn); - } else { confusion("find_math_kern"); } - return 0; /* not reached */ + /*tex Not reached: */ + return 0; } -@ just a small helper -@c -static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2) +/*tex Just a small helper: */ + +static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2, halfword subtyp) { pointer y; pointer z = new_kern(delta2); + if (subtyp != 0) { + subtype(z) = subtyp; + } reset_attributes(z, node_attr(q)); if (new_hlist(q) == null) { - /* this is somewhat weird */ + /*tex this is somewhat weird */ new_hlist(q) = z; } else { y = new_hlist(q); @@ -3167,67 +3558,36 @@ static pointer attach_hkern_to_new_hlist(pointer q, scaled delta2) return new_hlist(q); } -@ -@c -#ifdef DEBUG -void dump_simple_field(pointer q) -{ - pointer p; - printf(" [%d, type=%d, vlink=%d] ", q, type(q), vlink(q)); - switch (type(q)) { - case math_char_node: - printf("mathchar "); - break; - case math_text_char_node: - printf("texchar "); - break; - case sub_box_node: - printf("box "); - break; - case sub_mlist_node: - printf("mlist "); - p = math_list(q); - while (p != null) { - dump_simple_field(p); - p = vlink(p); - } - break; - } -} +/*tex -void dump_simple_node(pointer q) -{ - printf("node %d, type=%d, vlink=%d\n", q, type(q), vlink(q)); - printf("nucleus: "); - dump_simple_field(nucleus(q)); - printf("\n"); - printf("sub: "); - dump_simple_field(subscr(q)); - printf("\n"); - printf("sup: "); - dump_simple_field(supscr(q)); - printf("\n\n"); -} -#endif + The purpose of |make_scripts(q,it)| is to attach the subscript and/or + superscript of noad |q| to the list that starts at |new_hlist(q)|, given that + subscript and superscript aren't both empty. The superscript will be + horizontally shifted over |delta1|, the subscript over |delta2|. -@ The purpose of |make_scripts(q,it)| is to attach the subscript and/or -superscript of noad |q| to the list that starts at |new_hlist(q)|, -given that subscript and superscript aren't both empty. The superscript -will be horizontally shifted over |delta1|, the subscript over |delta2|. + We set |shift_down| and |shift_up| to the minimum amounts to shift the + baseline of subscripts and superscripts based on the given nucleus. -We set |shift_down| and |shift_up| to the minimum amounts to shift the -baseline of subscripts and superscripts based on the given nucleus. + Note: We need to look at a character but also at the first one in a sub list + and there we ignore leading kerns and glue. Elsewhere is code that removes + kerns assuming that is italic correction. The heuristics are unreliable for + the new fonts so eventualy there will be an option to ignore such + corrections. -Note: We need to look at a character but also at the first one in a sub list -and there we ignore leading kerns and glue. Elsewhere is code that removes -kerns assuming that is italic correction. The heuristics are unreliable for -the new fonts so eventualy there will be an option to ignore such corrections. +*/ -@ @c #define analyze_script(init,su_n,su_f,su_c) do { \ su_n = init; \ if (su_n != null) { \ - if (math_script_box_mode_par > 0 && type(su_n) == sub_mlist_node) { \ + if (math_script_char_mode_par > 0 && type(su_n) == math_char_node) { \ + fetch(su_n); \ + if (char_exists(cur_f, cur_c)) { \ + su_f = cur_f; \ + su_c = cur_c; \ + } else { \ + su_n = null; \ + } \ + } else if (math_script_box_mode_par > 0 && type(su_n) == sub_mlist_node) { \ su_n = math_list(su_n); \ while (su_n != null) { \ if ((type(su_n) == kern_node) || (type(su_n) == glue_node)) { \ @@ -3312,10 +3672,10 @@ the new fonts so eventualy there will be an option to ignore such corrections. static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled supshift, scaled subshift) { - pointer x, y, z; /* temporary registers for box construction */ - scaled shift_up, shift_down, clr; /* dimensions in the calculation */ + pointer x, y, z; + scaled shift_up, shift_down, clr; scaled delta1, delta2; - halfword sub_n, sup_n; + halfword sub_n, sup_n, subtyp; internal_font_number sub_f, sup_f; int sub_c, sup_c; sub_n = null; @@ -3326,18 +3686,13 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled sup_c = 0; delta1 = it; delta2 = 0; - -#ifdef DEBUG - printf("it: %d\n", it); - dump_simple_node(q); - printf("p: node %d, type=%d, subtype=%d\n", p, type(p), subtype(p)); -#endif + subtyp = 0; switch (type(nucleus(q))) { case math_char_node: case math_text_char_node: if ((subscr(q) == null) && (delta1 != 0)) { - /* todo: selective */ - x = new_kern(delta1); /* italic correction */ + /*tex todo: selective italic correction */ + x = new_kern(delta1); subtype(x) = italic_kern; reset_attributes(x, node_attr(nucleus(q))); couple_nodes(p,x); @@ -3355,22 +3710,20 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled list_ptr(z) = null; flush_node(z); } - if (is_char_node(p)) { - /* we look at the subscript character (_i) or first character in a list (_{ij}) */ + /*tex We look at the subscript character (_i) or first character in a list (_{ij}). */ analyze_script(subscr(q),sub_n,sub_f,sub_c); - /* we look at the superscript character (^i) or first character in a list (^{ij}) */ + /*tex We look at the superscript character (^i) or first character in a list (^{ij}). */ analyze_script(supscr(q),sup_n,sup_f,sup_c); } - if (supscr(q) == null) { - /* - construct a subscript box |x| when there is no superscript + /*tex - when there is a subscript without a superscript, the top of the subscript + Construct a subscript box |x| when there is no superscript. When + there is a subscript without a superscript, the top of the subscript should not exceed the baseline plus four-fifths of the x-height. + */ - /* x = clean_box(subscr(q), sub_style(cur_style), cur_style); */ x = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style); width(x) = width(x) + space_after_script(cur_style); switch (math_scripts_mode_par) { @@ -3398,30 +3751,29 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled break; } shift_amount(x) = shift_down; - - /* now find and correct for horizontal shift */ + /*tex Now find and correct for horizontal shift. */ + subtyp = 0; if (sub_n != null) { delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down); if (delta2 == MATH_KERN_NOT_FOUND) { delta2 = subshift ; } else { delta2 = delta2 + subshift ; + subtyp = font_kern; } } else { delta2 = subshift ; } if (delta2 != 0) { - p = attach_hkern_to_new_hlist(q, delta2); + p = attach_hkern_to_new_hlist(q, delta2, subtyp); } - } else { - /* - construct a superscript box |x| + /*tex + + Construct a superscript box |x|. The bottom of a superscript should + never descend below the baseline plus one-fourth of the x-height. - the bottom of a superscript should never descend below the baseline plus - one-fourth of the x-height. */ - /* x = clean_box(supscr(q), sup_style(cur_style), cur_style); */ x = clean_box(supscr(q), (noadoptionnosupscript(q) ? cur_style : sup_style(cur_style)), cur_style); width(x) = width(x) + space_after_script(cur_style); switch (math_scripts_mode_par) { @@ -3451,33 +3803,35 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled } if (subscr(q) == null) { shift_amount(x) = -shift_up; - /* now find and correct for horizontal shift */ + /*tex Now find and correct for horizontal shift. */ + subtyp = 0; if (sup_n != null) { clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up); if (clr == MATH_KERN_NOT_FOUND) { clr = supshift ; } else { clr = clr + supshift ; + subtyp = font_kern; } } else { clr = supshift; } if (clr != 0) { - p = attach_hkern_to_new_hlist(q, clr); + p = attach_hkern_to_new_hlist(q, clr, subtyp); } } else { - /* - construct a sub/superscript combination box |x|, with the superscript offset - by |delta| + /*tex - when both subscript and superscript are present, the subscript must be - separated from the superscript by at least four times |default_rule_thickness| + Construct a sub/superscript combination box |x|, with the + superscript offset by |delta|. When both subscript and + superscript are present, the subscript must be separated from the + superscript by at least four times |default_rule_thickness| If + this condition would be violated, the subscript moves down, after + which both subscript and superscript move up so that the bottom + of the superscript is at least as high as the baseline plus + four-fifths of the x-height. - if this condition would be violated, the subscript moves down, after which - both subscript and superscript move up so that the bottom of the superscript - is at least as high as the baseline plus four-fifths of the x-height */ - /* y = clean_box(subscr(q) sub_style(cur_style), cur_style); */ y = clean_box(subscr(q), (noadoptionnosubscript(q) ? cur_style : sub_style(cur_style)), cur_style); width(y) = width(y) + space_after_script(cur_style); switch (math_scripts_mode_par) { @@ -3510,30 +3864,37 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled } break; } - /* now find and correct for horizontal shift */ + /*tex Now find and correct for horizontal shift. */ + subtyp = 0; if (sub_n != null) { delta2 = find_math_kern(font(p), character(p),sub_f,sub_c,sub_mark_cmd, shift_down); if (delta2 == MATH_KERN_NOT_FOUND) { delta2 = subshift ; } else { delta2 = delta2 + subshift ; + subtyp = font_kern; } } else { delta2 = subshift ; } if (delta2 != 0) { - p = attach_hkern_to_new_hlist(q, delta2); + p = attach_hkern_to_new_hlist(q, delta2, subtyp); } - /* - now the horizontal shift for the superscript; the superscript is also to be shifted - by |delta1| (the italic correction) + /*tex + + Now the horizontal shift for the superscript; the superscript is + also to be shifted by |delta1| (the italic correction). + */ clr = MATH_KERN_NOT_FOUND; if (sup_n != null) { clr = find_math_kern(font(p),character(p),sup_f,sup_c,sup_mark_cmd,shift_up); } + /*tex - /* delta can already have been applied and now be 0 */ + The delta can already have been applied and now be 0. + + */ if (delta2 == MATH_KERN_NOT_FOUND) delta2 = - supshift ; else @@ -3543,18 +3904,17 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled } else { shift_amount(x) = delta1 - delta2; } - /* todo: only if kern != 0 */ + /*tex todo: only if kern != 0 */ p = new_kern((shift_up - depth(x)) - (height(y) - shift_down)); reset_attributes(p, node_attr(q)); couple_nodes(x,p); couple_nodes(p,y); - /* we end up with funny dimensions */ + /*tex We end up with funny dimensions. */ x = vpackage(x, 0, additional, max_dimen, math_direction_par); reset_attributes(x, node_attr(q)); shift_amount(x) = shift_down; } } - if (new_hlist(q) == null) { new_hlist(q) = x; } else { @@ -3575,12 +3935,15 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled } } -@ The |make_left_right| function constructs a left or right delimiter of -the required size and returns the value |open_noad| or |close_noad|. The -|left_noad_side| and |right_noad_side| will both be based on the original |style|, -so they will have consistent sizes. +/*tex + + The |make_left_right| function constructs a left or right delimiter of the + required size and returns the value |open_noad| or |close_noad|. The + |left_noad_side| and |right_noad_side| will both be based on the original + |style|, so they will have consistent sizes. + +*/ -@c static small_number make_left_right(pointer q, int style, scaled max_d, scaled max_h) { scaled delta; @@ -3589,21 +3952,21 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m boolean stack = false; boolean axis = false; int same = subtype(q); - setup_cur_size(style); - if ((delimiterheight(q)!=0) || (delimiterdepth(q)!=0)) { delta = delimiterheight(q) + delimiterdepth(q); tmp = do_delimiter(q, delimiter(q), cur_size, delta, false, style, false, &stack, &ic, &same); delimiteritalic(q) = ic; + /*tex - /* beware, a stacked delimiter has a shift but no corrected height/depth (yet) */ + Beware, a stacked delimiter has a shift but no corrected height/depth + (yet). + */ if (stack) { shift_amount(tmp) = delimiterdepth(q); } - if (delimiterexact(q)) { delimiterheight(q) = height(tmp) - shift_amount(tmp); delimiterdepth(q) = depth(tmp) + shift_amount(tmp); @@ -3629,7 +3992,7 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m } delimiter(q) = null; assign_new_hlist(q, tmp); - delimitersamesize(q) = same; /* new */ + delimitersamesize(q) = same; if (delimiterclass(q) >= ord_noad_type) { if (delimiterclass(q) <= inner_noad_type) { return delimiterclass(q); @@ -3643,35 +4006,32 @@ static small_number make_left_right(pointer q, int style, scaled max_d, scaled m } } -@ @c -#define TEXT_STYLES(A,B) do { \ - def_math_param(A,display_style,(B),level_one); \ - def_math_param(A,cramped_display_style,(B),level_one); \ - def_math_param(A,text_style,(B),level_one); \ - def_math_param(A,cramped_text_style,(B),level_one); \ +#define TEXT_STYLES(A,B) do { \ + def_math_param(A,display_style,(B),level_one); \ + def_math_param(A,cramped_display_style,(B),level_one); \ + def_math_param(A,text_style,(B),level_one); \ + def_math_param(A,cramped_text_style,(B),level_one); \ } while (0) -#define SCRIPT_STYLES(A,B) do { \ - def_math_param(A,script_style,(B),level_one); \ - def_math_param(A,cramped_script_style,(B),level_one); \ - def_math_param(A,script_script_style,(B),level_one); \ - def_math_param(A,cramped_script_script_style,(B),level_one); \ +#define SCRIPT_STYLES(A,B) do { \ + def_math_param(A,script_style,(B),level_one); \ + def_math_param(A,cramped_script_style,(B),level_one); \ + def_math_param(A,script_script_style,(B),level_one); \ + def_math_param(A,cramped_script_script_style,(B),level_one); \ } while (0) -#define ALL_STYLES(A,B) do { \ - TEXT_STYLES(A,(B)); \ - SCRIPT_STYLES(A,(B)); \ +#define ALL_STYLES(A,B) do { \ + TEXT_STYLES(A,(B)); \ + SCRIPT_STYLES(A,(B)); \ } while (0) -#define SPLIT_STYLES(A,B,C) do { \ - TEXT_STYLES(A,(B)); \ - SCRIPT_STYLES(A,(C)); \ +#define SPLIT_STYLES(A,B,C) do { \ + TEXT_STYLES(A,(B)); \ + SCRIPT_STYLES(A,(C)); \ } while (0) - void initialize_math_spacing(void) { - /* *INDENT-OFF* */ ALL_STYLES (math_param_ord_ord_spacing, 0); ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip_code); SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip_code, 0); @@ -3743,10 +4103,8 @@ void initialize_math_spacing(void) ALL_STYLES (math_param_inner_close_spacing, 0); SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip_code, 0); SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip_code, 0); - /* *INDENT-ON* */ } -@ @c #define both_types(A,B) ((A)*16+(B)) static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu) @@ -3758,102 +4116,99 @@ static pointer math_spacing_glue(int l_type, int r_type, int mstyle, scaled mmu) if (r_type == op_noad_type_limits || r_type == op_noad_type_no_limits) r_type = op_noad_type_normal; switch (both_types(l_type, r_type)) { - /* *INDENT-OFF* */ - case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break; - case both_types(ord_noad_type, op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break; - case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break; - case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break; - case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break; - case both_types(ord_noad_type, close_noad_type ): x = get_math_param(math_param_ord_close_spacing,mstyle); break; - case both_types(ord_noad_type, punct_noad_type ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break; - case both_types(ord_noad_type, inner_noad_type ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break; - case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break; - case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break; -#if 0 - case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break; -#endif - case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break; - case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break; - case both_types(op_noad_type_normal, close_noad_type ): x = get_math_param(math_param_op_close_spacing,mstyle); break; - case both_types(op_noad_type_normal, punct_noad_type ): x = get_math_param(math_param_op_punct_spacing,mstyle); break; - case both_types(op_noad_type_normal, inner_noad_type ): x = get_math_param(math_param_op_inner_spacing,mstyle); break; - case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break; - case both_types(bin_noad_type, op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break; -#if 0 - case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break; - case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break; -#endif - case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break; -#if 0 - case both_types(bin_noad_type, close_noad_type ): x = get_math_param(math_param_bin_close_spacing,mstyle); break; - case both_types(bin_noad_type, punct_noad_type ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break; -#endif - case both_types(bin_noad_type, inner_noad_type ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break; - case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break; - case both_types(rel_noad_type, op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break; -#if 0 - case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break; -#endif - case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break; - case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break; - case both_types(rel_noad_type, close_noad_type ): x = get_math_param(math_param_rel_close_spacing,mstyle); break; - case both_types(rel_noad_type, punct_noad_type ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break; - case both_types(rel_noad_type, inner_noad_type ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break; - case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break; - case both_types(open_noad_type, op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break; -#if 0 - case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break; -#endif - case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break; - case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break; - case both_types(open_noad_type, close_noad_type ): x = get_math_param(math_param_open_close_spacing,mstyle); break; - case both_types(open_noad_type, punct_noad_type ): x = get_math_param(math_param_open_punct_spacing,mstyle); break; - case both_types(open_noad_type, inner_noad_type ): x = get_math_param(math_param_open_inner_spacing,mstyle); break; - case both_types(close_noad_type, ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break; - case both_types(close_noad_type, op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break; - case both_types(close_noad_type, bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break; - case both_types(close_noad_type, rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break; - case both_types(close_noad_type, open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break; - case both_types(close_noad_type, close_noad_type ): x = get_math_param(math_param_close_close_spacing,mstyle); break; - case both_types(close_noad_type, punct_noad_type ): x = get_math_param(math_param_close_punct_spacing,mstyle); break; - case both_types(close_noad_type, inner_noad_type ): x = get_math_param(math_param_close_inner_spacing,mstyle); break; - case both_types(punct_noad_type, ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break; - case both_types(punct_noad_type, op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break; -#if 0 - case both_types(punct_noad_type, bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break; -#endif - case both_types(punct_noad_type, rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break; - case both_types(punct_noad_type, open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break; - case both_types(punct_noad_type, close_noad_type ): x = get_math_param(math_param_punct_close_spacing,mstyle); break; - case both_types(punct_noad_type, punct_noad_type ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break; - case both_types(punct_noad_type, inner_noad_type ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break; - case both_types(inner_noad_type, ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break; - case both_types(inner_noad_type, op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break; - case both_types(inner_noad_type, bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break; - case both_types(inner_noad_type, rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break; - case both_types(inner_noad_type, open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break; - case both_types(inner_noad_type, close_noad_type ): x = get_math_param(math_param_inner_close_spacing,mstyle); break; - case both_types(inner_noad_type, punct_noad_type ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break; - case both_types(inner_noad_type, inner_noad_type ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break; - /* *INDENT-ON* */ + case both_types(ord_noad_type, ord_noad_type ): x = get_math_param(math_param_ord_ord_spacing,mstyle); break; + case both_types(ord_noad_type, op_noad_type_normal): x = get_math_param(math_param_ord_op_spacing,mstyle); break; + case both_types(ord_noad_type, bin_noad_type ): x = get_math_param(math_param_ord_bin_spacing,mstyle); break; + case both_types(ord_noad_type, rel_noad_type ): x = get_math_param(math_param_ord_rel_spacing,mstyle); break; + case both_types(ord_noad_type, open_noad_type ): x = get_math_param(math_param_ord_open_spacing,mstyle); break; + case both_types(ord_noad_type, close_noad_type ): x = get_math_param(math_param_ord_close_spacing,mstyle); break; + case both_types(ord_noad_type, punct_noad_type ): x = get_math_param(math_param_ord_punct_spacing,mstyle); break; + case both_types(ord_noad_type, inner_noad_type ): x = get_math_param(math_param_ord_inner_spacing,mstyle); break; + case both_types(op_noad_type_normal, ord_noad_type ): x = get_math_param(math_param_op_ord_spacing,mstyle); break; + case both_types(op_noad_type_normal, op_noad_type_normal): x = get_math_param(math_param_op_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(op_noad_type_normal, bin_noad_type ): x = get_math_param(math_param_op_bin_spacing,mstyle); break; + /* */ + case both_types(op_noad_type_normal, rel_noad_type ): x = get_math_param(math_param_op_rel_spacing,mstyle); break; + case both_types(op_noad_type_normal, open_noad_type ): x = get_math_param(math_param_op_open_spacing,mstyle); break; + case both_types(op_noad_type_normal, close_noad_type ): x = get_math_param(math_param_op_close_spacing,mstyle); break; + case both_types(op_noad_type_normal, punct_noad_type ): x = get_math_param(math_param_op_punct_spacing,mstyle); break; + case both_types(op_noad_type_normal, inner_noad_type ): x = get_math_param(math_param_op_inner_spacing,mstyle); break; + case both_types(bin_noad_type, ord_noad_type ): x = get_math_param(math_param_bin_ord_spacing,mstyle); break; + case both_types(bin_noad_type, op_noad_type_normal): x = get_math_param(math_param_bin_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(bin_noad_type, bin_noad_type ): x = get_math_param(math_param_bin_bin_spacing,mstyle); break; + case both_types(bin_noad_type, rel_noad_type ): x = get_math_param(math_param_bin_rel_spacing,mstyle); break; + /* */ + case both_types(bin_noad_type, open_noad_type ): x = get_math_param(math_param_bin_open_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(bin_noad_type, close_noad_type ): x = get_math_param(math_param_bin_close_spacing,mstyle); break; + case both_types(bin_noad_type, punct_noad_type ): x = get_math_param(math_param_bin_punct_spacing,mstyle); break; + /* */ + case both_types(bin_noad_type, inner_noad_type ): x = get_math_param(math_param_bin_inner_spacing,mstyle); break; + case both_types(rel_noad_type, ord_noad_type ): x = get_math_param(math_param_rel_ord_spacing,mstyle); break; + case both_types(rel_noad_type, op_noad_type_normal): x = get_math_param(math_param_rel_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(rel_noad_type, bin_noad_type ): x = get_math_param(math_param_rel_bin_spacing,mstyle); break; + /* */ + case both_types(rel_noad_type, rel_noad_type ): x = get_math_param(math_param_rel_rel_spacing,mstyle); break; + case both_types(rel_noad_type, open_noad_type ): x = get_math_param(math_param_rel_open_spacing,mstyle); break; + case both_types(rel_noad_type, close_noad_type ): x = get_math_param(math_param_rel_close_spacing,mstyle); break; + case both_types(rel_noad_type, punct_noad_type ): x = get_math_param(math_param_rel_punct_spacing,mstyle); break; + case both_types(rel_noad_type, inner_noad_type ): x = get_math_param(math_param_rel_inner_spacing,mstyle); break; + case both_types(open_noad_type, ord_noad_type ): x = get_math_param(math_param_open_ord_spacing,mstyle); break; + case both_types(open_noad_type, op_noad_type_normal): x = get_math_param(math_param_open_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(open_noad_type, bin_noad_type ): x = get_math_param(math_param_open_bin_spacing,mstyle); break; + /* */ + case both_types(open_noad_type, rel_noad_type ): x = get_math_param(math_param_open_rel_spacing,mstyle); break; + case both_types(open_noad_type, open_noad_type ): x = get_math_param(math_param_open_open_spacing,mstyle); break; + case both_types(open_noad_type, close_noad_type ): x = get_math_param(math_param_open_close_spacing,mstyle); break; + case both_types(open_noad_type, punct_noad_type ): x = get_math_param(math_param_open_punct_spacing,mstyle); break; + case both_types(open_noad_type, inner_noad_type ): x = get_math_param(math_param_open_inner_spacing,mstyle); break; + case both_types(close_noad_type, ord_noad_type ): x = get_math_param(math_param_close_ord_spacing,mstyle); break; + case both_types(close_noad_type, op_noad_type_normal): x = get_math_param(math_param_close_op_spacing,mstyle); break; + case both_types(close_noad_type, bin_noad_type ): x = get_math_param(math_param_close_bin_spacing,mstyle); break; + case both_types(close_noad_type, rel_noad_type ): x = get_math_param(math_param_close_rel_spacing,mstyle); break; + case both_types(close_noad_type, open_noad_type ): x = get_math_param(math_param_close_open_spacing,mstyle); break; + case both_types(close_noad_type, close_noad_type ): x = get_math_param(math_param_close_close_spacing,mstyle); break; + case both_types(close_noad_type, punct_noad_type ): x = get_math_param(math_param_close_punct_spacing,mstyle); break; + case both_types(close_noad_type, inner_noad_type ): x = get_math_param(math_param_close_inner_spacing,mstyle); break; + case both_types(punct_noad_type, ord_noad_type ): x = get_math_param(math_param_punct_ord_spacing,mstyle); break; + case both_types(punct_noad_type, op_noad_type_normal): x = get_math_param(math_param_punct_op_spacing,mstyle); break; + /* shouldn't happen */ + case both_types(punct_noad_type, bin_noad_type ): x = get_math_param(math_param_punct_bin_spacing,mstyle); break; + /* */ + case both_types(punct_noad_type, rel_noad_type ): x = get_math_param(math_param_punct_rel_spacing,mstyle); break; + case both_types(punct_noad_type, open_noad_type ): x = get_math_param(math_param_punct_open_spacing,mstyle); break; + case both_types(punct_noad_type, close_noad_type ): x = get_math_param(math_param_punct_close_spacing,mstyle); break; + case both_types(punct_noad_type, punct_noad_type ): x = get_math_param(math_param_punct_punct_spacing,mstyle); break; + case both_types(punct_noad_type, inner_noad_type ): x = get_math_param(math_param_punct_inner_spacing,mstyle); break; + case both_types(inner_noad_type, ord_noad_type ): x = get_math_param(math_param_inner_ord_spacing,mstyle); break; + case both_types(inner_noad_type, op_noad_type_normal): x = get_math_param(math_param_inner_op_spacing,mstyle); break; + case both_types(inner_noad_type, bin_noad_type ): x = get_math_param(math_param_inner_bin_spacing,mstyle); break; + case both_types(inner_noad_type, rel_noad_type ): x = get_math_param(math_param_inner_rel_spacing,mstyle); break; + case both_types(inner_noad_type, open_noad_type ): x = get_math_param(math_param_inner_open_spacing,mstyle); break; + case both_types(inner_noad_type, close_noad_type ): x = get_math_param(math_param_inner_close_spacing,mstyle); break; + case both_types(inner_noad_type, punct_noad_type ): x = get_math_param(math_param_inner_punct_spacing,mstyle); break; + case both_types(inner_noad_type, inner_noad_type ): x = get_math_param(math_param_inner_inner_spacing,mstyle); break; } if (x < 0) { confusion("mathspacing"); } if (x != 0) { if (x <= thick_mu_skip_code) { - /* trap thin/med/thick settings cf. old TeX */ - z = math_glue(glue_par(x), mmu); /* allocates a glue */ - /* store a symbolic subtype */ + /*tex trap thin/med/thick settings cf.\ old \TeX */ + z = math_glue(glue_par(x), mmu); + /*tex store a symbolic subtype */ subtype(z) = (quarterword) (x + 1); } else { - z = math_glue(x, mmu); /* allocates a glue */ + z = math_glue(x, mmu); } } return z; } -@ @c static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_style, int *same) { pointer p = null; @@ -3866,37 +4221,36 @@ static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_styl case math_text_char_node: fetch(nucleus(q)); if (char_exists(cur_f, cur_c)) { - /* we could look at neighbours */ + /*tex we could look at neighbours */ if (do_new_math(cur_f)) { - *delta = 0 ; /* cf spec only the last one */ + /*tex cf spec only the last one */ + *delta = 0 ; } else { *delta = char_italic(cur_f, cur_c); } p = new_glyph(cur_f, cur_c); reset_attributes(p, node_attr(nucleus(q))); if (do_new_math(cur_f)) { - if (! math_no_char_italic_par) { - /* keep italic, but bad with two successive letters */ - } else if (get_char_cat_code(cur_c) == 11) { - /* no italic correction in mid-word of text font */ + if (get_char_cat_code(cur_c) == 11) { + /*tex no italic correction in mid-word of text font */ *delta = 0; } } else { - /* no italic correction in mid-word of text font */ + /*tex no italic correction in mid-word of text font */ if (((type(nucleus(q))) == math_text_char_node) && (space(cur_f) != 0)) { *delta = 0; } } - /* so we only add italic correction when we have no scripts */ + /*tex so we only add italic correction when we have no scripts */ if ((subscr(q) == null) && (supscr(q) == null) && (*delta != 0)) { pointer x = new_kern(*delta); subtype(x) = italic_kern; reset_attributes(x, node_attr(nucleus(q))); couple_nodes(p,x); *delta = 0; - } else /* needs checking but looks ok */ - if (do_new_math(cur_f)) { - *delta = char_italic(cur_f, cur_c); /* must be more selective */ + } else if (do_new_math(cur_f)) { + /*tex Needs checking but looks ok. It must be more selective. */ + *delta = char_italic(cur_f, cur_c); } } break; @@ -3905,60 +4259,80 @@ static pointer check_nucleus_complexity(halfword q, scaled * delta, int cur_styl break; case sub_mlist_node: t = math_list(nucleus(q)); - mlist_to_hlist(t, false, cur_style); /* recursive call */ -if (same != NULL && type(t) == fence_noad && delimitersamesize(t)) { - *same = delimitersamesize(t) ; -} + /*tex Recursive call: */ + mlist_to_hlist(t, false, cur_style); + if (same != NULL && type(t) == fence_noad && delimitersamesize(t)) { + *same = delimitersamesize(t) ; + } setup_cur_size(cur_style); p = hpack(vlink(temp_head), 0, additional, -1); reset_attributes(p, node_attr(nucleus(q))); break; default: - confusion("mlist2"); /* this can't happen mlist2 */ + confusion("mlist2"); } return p; } -@ Here is the overall plan of |mlist_to_hlist|, and the list of its - local variables. +/*tex + + Here is the overall plan of |mlist_to_hlist|, and the list of its local + variables. + +*/ -@c void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) { - pointer q = mlist; /* runs through the mlist */ - pointer r = null; /* the most recent noad preceding |q| */ - int style = cur_style; /* tuck global parameter away as local variable */ - int r_type = simple_noad; /* the |type| of noad |r|, or |op_noad| if |r=null| */ - int r_subtype = op_noad_type_normal; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */ - int t; /* the effective |type| of noad |q| during the second pass */ - int t_subtype; /* the effective |subtype| of noad |q| during the second pass */ + /*tex runs through the mlist */ + pointer q = mlist; + /*tex the most recent noad preceding |q| */ + pointer r = null; + /*tex tuck global parameter away as local variable */ + int style = cur_style; + /*tex the |type| of noad |r|, or |op_noad| if |r=null| */ + int r_type = simple_noad; + /*tex the |subtype| of noad |r| if |r_type| is |fence_noad| */ + int r_subtype = op_noad_type_normal; + /*tex the effective |type| of noad |q| during the second pass */ + int t; + /*tex the effective |subtype| of noad |q| during the second pass */ + int t_subtype; pointer p = null; pointer pp = null; pointer z = null; halfword nxt ; int same = 0; - int pen; /* a penalty to be inserted */ - int prepen; /* a penalty to be inserted */ - scaled max_hl = 0; /* maximum height of the list translated so far */ - scaled max_d = 0; /* maximum depth of the list translated so far */ - scaled delta; /* italic correction offset for subscript and superscript */ - scaled cur_mu; /* the math unit width corresponding to |cur_size| */ + /*tex a penalty to be inserted */ + int pen; + /*tex a penalty to be inserted */ + int prepen; + /*tex maximum height of the list translated so far */ + scaled max_hl = 0; + /*tex maximum depth of the list translated so far */ + scaled max_d = 0; + /*tex italic correction offset for subscript and superscript */ + scaled delta; + /*tex the math unit width corresponding to |cur_size| */ + scaled cur_mu; r_subtype = op_noad_type_normal; setup_cur_size(cur_style); cur_mu = x_over_n(get_math_quad_size(cur_size), 18); if (math_penalties_mode_par) { - /* we could do this via the callback but it's nice to have it as primitive too */ + /*tex + We could do this via the callback but it's nice to have it as + primitive too. + */ penalties = 1; } while (q != null) { - /* - we use the fact that no character nodes appear in an mlist, hence - the field |type(q)| is always present. + /*tex - one of the things we must do on the first pass is change a |bin_noad| to - an |ord_noad| if the |bin_noad| is not in the context of a binary operator + We use the fact that no character nodes appear in an mlist, hence the + field |type(q)| is always present.One of the things we must do on the + first pass is change a |bin_noad| to an |ord_noad| if the |bin_noad| + is not in the context of a binary operator. The values of |r| and + |r_type| make this fairly easy. - the values of |r| and |r_type| make this fairly easy */ RESWITCH: delta = 0; @@ -3966,66 +4340,69 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) switch (type(q)) { case simple_noad: switch (subtype(q)) { - case bin_noad_type: - switch (r_type) { - case simple_noad: - switch (r_subtype) { - case bin_noad_type: - case op_noad_type_normal: - case op_noad_type_limits: - case op_noad_type_no_limits: - case rel_noad_type: - case open_noad_type: - case punct_noad_type: - subtype(q) = ord_noad_type; - goto RESWITCH; - break; + case bin_noad_type: + switch (r_type) { + case simple_noad: + switch (r_subtype) { + case bin_noad_type: + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + case rel_noad_type: + case open_noad_type: + case punct_noad_type: + subtype(q) = ord_noad_type; + goto RESWITCH; + break; + } + break; + case fence_noad: + if (r_subtype == left_noad_side) { + /*tex So these can best be the same size. */ + subtype(q) = ord_noad_type; + goto RESWITCH; + } + break; } break; - case fence_noad: - if (r_subtype == left_noad_side) { - subtype(q) = ord_noad_type; /* so these can best be the same size */ - goto RESWITCH; + case over_noad_type: + make_over(q, cur_style, cur_size, math_rules_fam_par); + break; + case under_noad_type: + make_under(q, cur_style, cur_size, math_rules_fam_par); + break; + case vcenter_noad_type: + make_vcenter(q); + break; + case rel_noad_type: + case close_noad_type: + case punct_noad_type: + if (r_type == simple_noad && r_subtype == bin_noad_type) { + /*tex Assumes the same size; can't this go. */ + type(r) = simple_noad; + subtype(r) = ord_noad_type; } break; - } - break; - case over_noad_type: - make_over(q, cur_style, cur_size, math_rules_fam_par); - break; - case under_noad_type: - make_under(q, cur_style, cur_size, math_rules_fam_par); - break; - case vcenter_noad_type: - make_vcenter(q); - break; - case rel_noad_type: - case close_noad_type: - case punct_noad_type: - if (r_type == simple_noad && r_subtype == bin_noad_type) { - type(r) = simple_noad; /* assumes the same size .. can't this go */ - subtype(r) = ord_noad_type; - } - break; - case op_noad_type_normal: - case op_noad_type_limits: - case op_noad_type_no_limits: - delta = make_op(q, cur_style); - if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits)) - goto CHECK_DIMENSIONS; - break; - case ord_noad_type: - make_ord(q); - break; - case open_noad_type: - case inner_noad_type: - break; + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + delta = make_op(q, cur_style); + if ((subtype(q) == op_noad_type_limits) || (subtype(q) == op_noad_type_no_limits)) + goto CHECK_DIMENSIONS; + break; + case ord_noad_type: + make_ord(q); + break; + case open_noad_type: + case inner_noad_type: + break; } break; case fence_noad: if (subtype(q) != left_noad_side) { if (r_type == simple_noad && r_subtype == bin_noad_type) { - type(r) = simple_noad; /* assumes the same size */ + /*tex Assumes the same size. */ + type(r) = simple_noad; subtype(r) = ord_noad_type; } } @@ -4060,16 +4437,20 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) break; case choice_node: switch (cur_style / 2) { - case 0: /* |display_style=0| */ + case 0: + /*tex |display_style=0| */ choose_mlist(display_mlist); break; - case 1: /* |text_style=2| */ + case 1: + /*tex |text_style=2| */ choose_mlist(text_mlist); break; - case 2: /* |script_style=4| */ + case 2: + /*tex |script_style=4| */ choose_mlist(script_mlist); break; - case 3: /* |script_script_style=6| */ + case 3: + /*tex |script_script_style=6| */ choose_mlist(script_script_mlist); break; } @@ -4105,14 +4486,16 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) goto DONE_WITH_NODE; break; case glue_node: - /* - conditional math glue (`\.{\\nonscript}') results in a |glue_node| - pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case - the node following will be eliminated if it is a glue or kern node and if the - current size is different from |text_size| + /*tex + + Conditional math glue (`\.{\\nonscript}') results in a + |glue_node| pointing to |zero_glue|, with + |subtype(q)=cond_math_glue|; in such a case the node + following will be eliminated if it is a glue or kern node and + if the current size is different from |text_size|. - unconditional math glue (`\.{\\muskip}') is converted to normal glue by - multiplying the dimensions by |cur_mu| + Unconditional math glue (`\.{\\muskip}') is converted to + normal glue by multiplying the dimensions by |cur_mu|. */ if (subtype(q) == mu_glue) { @@ -4139,29 +4522,31 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) default: confusion("mlist1"); } - /* - When we get to the following part of the program, we have ``fallen through'' - from cases that did not lead to |check_dimensions| or |done_with_noad| or - |done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be - converted to an hlist, and whose subscripts and superscripts need to be - appended if they are present. + /*tex - If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount - by which a superscript should be moved right with respect to a subscript - when both are present. + When we get to the following part of the program, we have ``fallen + through'' from cases that did not lead to |check_dimensions| or + |done_with_noad| or |done_with_node|. Thus, |q|~points to a noad + whose nucleus may need to be converted to an hlist, and whose + subscripts and superscripts need to be appended if they are present. + + If |nucleus(q)| is not a |math_char|, the variable |delta| is the + amount by which a superscript should be moved right with respect to a + subscript when both are present. */ -same = 0 ; + same = 0 ; p = check_nucleus_complexity(q, &delta, cur_style, &same); -if (same) { - noadextra4(q) = same ; -} + if (same) { + noadextra4(q) = same ; + } if ((subscr(q) == null) && (supscr(q) == null)) { - /* + /*tex + Adding italic correction here is kind of fuzzy because some - characters already have that built in. However, we also add - it in the scripts so if it's optional here it also should - be there. + characters already have that built in. However, we also add it in + the scripts so if it's optional here it also should be there. + */ if (nxt && (math_italics_mode_par > 0) && (delta != 0)) { if (type(nxt) == simple_noad) { @@ -4199,7 +4584,7 @@ if (same) { } assign_new_hlist(q, p); } else { - /* top, bottom */ + /*tex top, bottom */ make_scripts(q, p, delta, cur_style, 0, 0); } CHECK_DIMENSIONS: @@ -4209,7 +4594,7 @@ if (same) { if (depth(z) > max_d) max_d = depth(z); list_ptr(z) = null; - /* only drop the \.{\\hbox} */ + /*tex only drop the \.{\\hbox} */ flush_node(z); DONE_WITH_NOAD: r = q; @@ -4219,7 +4604,8 @@ if (same) { r_subtype = left_noad_side; cur_style = style; setup_cur_size(cur_style); - cur_mu = x_over_n(get_math_quad_size(cur_size), 18); /* style */ + /*tex style */ + cur_mu = x_over_n(get_math_quad_size(cur_size), 18); } DONE_WITH_NODE: q = vlink(q); @@ -4228,15 +4614,18 @@ if (same) { type(r) = simple_noad; subtype(r) = ord_noad_type; } - /* + /*tex + Make a second pass over the mlist, removing all noads and inserting the proper spacing and penalties. - We have now tied up all the loose ends of the first pass of |mlist_to_hlist|. - The second pass simply goes through and hooks everything together with the - proper glue and penalties. It also handles the |fence_noad|s that - might be present, since |max_hl| and |max_d| are now known. Variable |p| points - to a node at the current end of the final hlist. + We have now tied up all the loose ends of the first pass of + |mlist_to_hlist|. The second pass simply goes through and hooks + everything together with the proper glue and penalties. It also handles + the |fence_noad|s that might be present, since |max_hl| and |max_d| are + now known. Variable |p| points to a node at the current end of the final + hlist. + */ p = temp_head; vlink(p) = null; @@ -4248,15 +4637,18 @@ if (same) { cur_mu = x_over_n(get_math_quad_size(cur_size), 18); NEXT_NODE: while (q != null) { - /* + /*tex + If node |q| is a style node, change the style and |goto delete_q|; - otherwise if it is not a noad, put it into the hlist, - advance |q|, and |goto done|; otherwise set |s| to the size - of noad |q|, set |t| to the associated type (|ord_noad.. - inner_noad|), and set |pen| to the associated penalty + otherwise if it is not a noad, put it into the hlist, advance |q|, + and |goto done|; otherwise set |s| to the size of noad |q|, set |t| + to the associated type (|ord_noad.. inner_noad|), and set |pen| to + the associated penalty. + + Just before doing the big |case| switch in the second pass, the + program sets up default values so that most of the branches are + short. - Just before doing the big |case| switch in the second pass, the program - sets up default values so that most of the branches are short. */ t = simple_noad; t_subtype = ord_noad_type; @@ -4292,7 +4684,7 @@ if (same) { t_subtype = make_left_right(q, style, max_d, max_hl); break; case style_node: - /* Change the current style and |goto delete_q| */ + /*tex Change the current style and |goto delete_q| */ cur_style = subtype(q); setup_cur_size(cur_style); cur_mu = x_over_n(get_math_quad_style(cur_style), 18); @@ -4316,34 +4708,36 @@ if (same) { default: confusion("mlist3"); } - /* Append inter-element spacing based on |r_type| and |t| */ + /*tex Append inter-element spacing based on |r_type| and |t| */ if (r_type > 0) { - /* not the first noad */ + /*tex not the first noad */ pp = p; -if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1) { - z = math_spacing_glue(r_subtype, ord_noad_type, cur_style, cur_mu); -} else { - z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu); -} + if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1) { + z = math_spacing_glue(r_subtype, ord_noad_type, cur_style, cur_mu); + } else { + z = math_spacing_glue(r_subtype, t_subtype, cur_style, cur_mu); + } if (z != null) { reset_attributes(z, node_attr(p)); couple_nodes(p,z); p = z; } if (penalties && prepen < inf_penalty && type(pp) != penalty_node) { - /* no checking of prev node type */ + /*tex no checking of prev node type */ z = new_penalty(prepen,noad_penalty); reset_attributes(z, node_attr(p)); couple_nodes(p,z); p = z; } } - /* - Append any |new_hlist| entries for |q|, and any appropriate penalties + /*tex + + Append any |new_hlist| entries for |q|, and any appropriate + penalties. We insert a penalty node after the hlist entries of noad + |q| if |pen| is not an ``infinite'' penalty, and if the node + immediately following |q| is not a penalty node or a |rel_noad| or + absent entirely. - We insert a penalty node after the hlist entries of noad |q| if |pen| - is not an ``infinite'' penalty, and if the node immediately following |q| - is not a penalty node or a |rel_noad| or absent entirely. */ if (new_hlist(q) != null) { couple_nodes(p,new_hlist(q)); @@ -4370,12 +4764,13 @@ if (delimitermodeordinal && t_subtype == inner_noad_type && noadextra4(q) == 1) DELETE_Q: r = q; q = vlink(q); - /* - The m-to-hlist conversion takes place in-place, so the various dependant - fields may not be freed (as would happen if |flush_node| was called). + /*tex + + The m-to-hlist conversion takes place in-place, so the various + dependant fields may not be freed (as would happen if |flush_node| + was called). A low-level |free_node| is easier than attempting to + nullify such dependant fields for all possible node and noad types. - A low-level |free_node| is easier than attempting to nullify such dependant - fields for all possible node and noad types. */ if (nodetype_has_attributes(type(r))) { delete_attribute_ref(node_attr(r)); diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.c b/Build/source/texk/web2c/luatexdir/tex/nesting.c new file mode 100644 index 00000000000..0e50c1031dc --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/nesting.c @@ -0,0 +1,429 @@ +/* + +nesting.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex These are for |show_activities|: */ + +#define page_goal page_so_far[0] + +/*tex + +\TeX\ is typically in the midst of building many lists at once. For example, when +a math formula is being processed, \TeX\ is in math mode and working on an mlist; +this formula has temporarily interrupted \TeX\ from being in horizontal mode and +building the hlist of a paragraph; and this paragraph has temporarily interrupted +\TeX\ from being in vertical mode and building the vlist for the next page of a +document. Similarly, when a \.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is +temporarily interrupted from working in restricted horizontal mode, and it enters +internal vertical mode. The ``semantic nest'' is a stack that keeps track of what +lists and modes are currently suspended. + +At each level of processing we are in one of six modes: + +|vmode| stands for vertical mode (the page builder); + +|hmode| stands for horizontal mode (the paragraph builder); + +|mmode| stands for displayed formula mode; + +|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox}); + +|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox}); + +|-mmode| stands for math formula mode (not displayed). + +The mode is temporarily set to zero while processing \.{\\write} texts in the +|ship_out| routine. + +Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that \TeX's ``big +semantic switch'' can select the appropriate thing to do by computing the value +|abs(mode)+cur_cmd|, where |mode| is the current mode and |cur_cmd| is the +current command code. + +*/ + +static const char *string_mode(int m) +{ + if (m > 0) { + switch (m / (max_command_cmd + 1)) { + case 0: + return "vertical mode"; + break; + case 1: + return "horizontal mode"; + break; + case 2: + return "display math mode"; + break; + default: + break; + } + } else if (m == 0) { + return "no mode"; + } else { + switch ((-m) / (max_command_cmd + 1)) { + case 0: + return "internal vertical mode"; + break; + case 1: + return "restricted horizontal mode"; + break; + case 2: + return "math mode"; + break; + default: + break; + } + } + return "unknown mode"; +} + +void print_mode(int m) +{ + tprint(string_mode(m)); +} + +void print_in_mode(int m) +{ + tprint("' in "); + tprint(string_mode(m)); +} + +int get_mode_id(void) +{ + int m = cur_list.mode_field; + if (m > 0) { + switch (m / (max_command_cmd + 1)) { + case 0: + return 'v'; + break; + case 1: + return 'h'; + break; + case 2: + return 'm'; + break; + default: + return '\0'; + break; + } + } else if (m == 0) { + return 'n';; + } else { + switch ((-m) / (max_command_cmd + 1)) { + case 0: + return 'V'; + break; + case 1: + return 'H'; + break; + case 2: + return 'M'; + break; + default: + return '\0'; + break; + } + } +} + +/*tex + +The state of affairs at any semantic level can be represented by five values: + +|mode| is the number representing the semantic mode, as just explained. + +|head| is a |pointer| to a list head for the list being built; |link(head)| +therefore points to the first element of the list, or to |null| if the list is +empty. + +|tail| is a |pointer| to the final node of the list being built; thus, +|tail=head| if and only if the list is empty. + +|prev_graf| is the number of lines of the current paragraph that have already +been put into the present vertical list. + +|aux| is an auxiliary |memory_word| that gives further information that is needed +to characterize the situation. + +In vertical mode, |aux| is also known as |prev_depth|; it is the scaled value +representing the depth of the previous box, for use in baseline calculations, or +it is |<=-1000|pt if the next box on the vertical list is to be exempt from +baseline calculations. In horizontal mode, |aux| is also known as |space_factor|; +it holds the current space factor used in spacing calculations. In math mode, +|aux| is also known as |incompleat_noad|; if not |null|, it points to a record +that represents the numerator of a generalized fraction for which the denominator +is currently being formed in the current list. + +There is also a sixth quantity, |mode_line|, which correlates the semantic nest +with the user's input; |mode_line| contains the source line number at which the +current level of nesting was entered. The negative of this line number is the +|mode_line| at the level of the user's output routine. + +A seventh quantity, |eTeX_aux|, is used by the extended features eTeX. In math +mode it is known as |delim_ptr| and points to the most recent |fence_noad| of a +|math_left_group|. + +In horizontal mode, the |prev_graf| field is used for initial language data. + +The semantic nest is an array called |nest| that holds the |mode|, |head|, +|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels below +the currently active one. Information about the currently active level is kept in +the global quantities |mode|, |head|, |tail|, |prev_graf|, |aux|, and +|mode_line|, which live in a struct that is ready to be pushed onto |nest| if +necessary. + +The math field is used by various bits and pieces in |texmath.w| + +This implementation of \TeX\ uses two different conventions for representing +sequential stacks. @^stack conventions@>@^conventions for representing stacks@> + +1) If there is frequent access to the top entry, and if the stack is essentially +never empty, then the top entry is kept in a global variable (even better would +be a machine register), and the other entries appear in the array +$\\{stack}[0\to(\\{ptr}-1)]$. The semantic stack is handled this way. + +2) If there is infrequent top access, the entire stack contents are in the array +$\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| is treated this way, +as we have seen. + +In |nest_ptr| we have the first unused location of |nest|, and |max_nest_stack| +has the maximum of |nest_ptr| when pushing. In |shown_mode| we store the most +recent mode shown by \.{\\tracingcommands} and with |save_tail| we can examine +whether we have an auto kern before a glue. + +*/ + +list_state_record *nest; +int nest_ptr, max_nest_stack, shown_mode; +halfword save_tail; + +/*tex + +We will see later that the vertical list at the bottom semantic level is split +into two parts; the ``current page'' runs from |page_head| to |page_tail|, and +the ``contribution list'' runs from |contrib_head| to |tail| of semantic level +zero. The idea is that contributions are first formed in vertical mode, then +``contributed'' to the current page (during which time the page-breaking +decisions are made). For now, we don't need to know any more details about the +page-building process. + +*/ + +void initialize_nesting(void) +{ + nest_ptr = 0; + max_nest_stack = 0; + shown_mode = 0; + cur_list.mode_field = vmode; + cur_list.head_field = contrib_head; + cur_list.tail_field = contrib_head; + cur_list.eTeX_aux_field = null; + cur_list.prev_depth_field = ignore_depth; + cur_list.space_factor_field = 1000; + cur_list.incompleat_noad_field = null; + cur_list.ml_field = 0; + cur_list.pg_field = 0; + cur_list.dirs_field = null; + init_math_fields(); +} + +/*tex + +Here is a common way to make the current list grow: + +*/ + +void tail_append(halfword p) +{ + couple_nodes(cur_list.tail_field, p); + cur_list.tail_field = vlink(cur_list.tail_field); +} + +halfword pop_tail(void) +{ + halfword n, r; + if (cur_list.tail_field != cur_list.head_field) { + r = cur_list.tail_field; + if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) { + n = alink(cur_list.tail_field); + } else { + n = cur_list.head_field; + while (vlink(n) != cur_list.tail_field) + n = vlink(n); + } + flush_node(cur_list.tail_field); + cur_list.tail_field = n; + vlink(n) = null; + return r; + } else { + return null; + } +} + +/*tex + +When \TeX's work on one level is interrupted, the state is saved by calling +|push_nest|. This routine changes |head| and |tail| so that a new (empty) list is +begun; it does not change |mode| or |aux|. + +*/ + +void push_nest(void) +{ + if (nest_ptr > max_nest_stack) { + max_nest_stack = nest_ptr; + if (nest_ptr == nest_size) + overflow("semantic nest size", (unsigned) nest_size); + } + incr(nest_ptr); + cur_list.mode_field = nest[nest_ptr - 1].mode_field; + cur_list.head_field = new_node(temp_node, 0); + cur_list.tail_field = cur_list.head_field; + cur_list.eTeX_aux_field = null; + cur_list.ml_field = line; + cur_list.pg_field = 0; + cur_list.dirs_field = null; + cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field; + cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field; + cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field; + init_math_fields(); +} + +/*tex + +Conversely, when \TeX\ is finished on the current level, the former state is +restored by calling |pop_nest|. This routine will never be called at the lowest +semantic level, nor will it be called unless |head| is a node that should be +returned to free memory. + +*/ + +void pop_nest(void) +{ + flush_node(cur_list.head_field); + decr(nest_ptr); +} + +/*tex + +Here is a procedure that displays what \TeX\ is working on, at all levels. + +*/ + +void show_activities(void) +{ + /*tex Index into |nest|: */ + int p; + /*tex The mode: */ + int m; + /*tex For showing the current page: */ + halfword q, r; + /*tex Ditto: */ + int t; + tprint_nl(""); + print_ln(); + for (p = nest_ptr; p >= 0; p--) { + m = nest[p].mode_field; + tprint_nl("### "); + print_mode(m); + tprint(" entered at line "); + print_int(abs(nest[p].ml_field)); + if (nest[p].ml_field < 0) + tprint(" (\\output routine)"); + if (p == 0) { + /*tex Show the status of the current page */ + if (page_head != page_tail) { + tprint_nl("### current page:"); + if (output_active) + tprint(" (held over for next output)"); + show_box(vlink(page_head)); + if (page_contents > empty) { + tprint_nl("total height "); + print_totals(); + tprint_nl(" goal height "); + print_scaled(page_goal); + r = vlink(page_ins_head); + while (r != page_ins_head) { + print_ln(); + tprint_esc("insert"); + t = subtype(r); + print_int(t); + tprint(" adds "); + if (count(t) == 1000) + t = height(r); + else + t = x_over_n(height(r), 1000) * count(t); + print_scaled(t); + if (type(r) == split_up_node) { + q = page_head; + t = 0; + do { + q = vlink(q); + if ((type(q) == ins_node) + && (subtype(q) == subtype(r))) + incr(t); + } while (q != broken_ins(r)); + tprint(", #"); + print_int(t); + tprint(" might split"); + } + r = vlink(r); + } + } + } + if (vlink(contrib_head) != null) + tprint_nl("### recent contributions:"); + } + show_box(vlink(nest[p].head_field)); + /*tex Show the auxiliary field, |a|. */ + switch (abs(m) / (max_command_cmd + 1)) { + case 0: + tprint_nl("prevdepth "); + if (nest[p].prev_depth_field <= ignore_depth) + tprint("ignored"); + else + print_scaled(nest[p].prev_depth_field); + if (nest[p].pg_field != 0) { + tprint(", prevgraf "); + print_int(nest[p].pg_field); + if (nest[p].pg_field != 1) + tprint(" lines"); + else + tprint(" line"); + } + break; + case 1: + tprint_nl("spacefactor "); + print_int(nest[p].space_factor_field); + break; + case 2: + if (nest[p].incompleat_noad_field != null) { + tprint("this will be denominator of:"); + show_box(nest[p].incompleat_noad_field); + } + break; + } + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/nesting.w b/Build/source/texk/web2c/luatexdir/tex/nesting.w deleted file mode 100644 index d9e7cf744ee..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/nesting.w +++ /dev/null @@ -1,436 +0,0 @@ -% nesting.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - -#include "ptexlib.h" - -@ these are for |show_activities| -@c -#define page_goal page_so_far[0] - -@ \TeX\ is typically in the midst of building many lists at once. For example, -when a math formula is being processed, \TeX\ is in math mode and -working on an mlist; this formula has temporarily interrupted \TeX\ from -being in horizontal mode and building the hlist of a paragraph; and this -paragraph has temporarily interrupted \TeX\ from being in vertical mode -and building the vlist for the next page of a document. Similarly, when a -\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily -interrupted from working in restricted horizontal mode, and it enters -internal vertical mode. The ``semantic nest'' is a stack that -keeps track of what lists and modes are currently suspended. - -At each level of processing we are in one of six modes: - -\yskip\hang|vmode| stands for vertical mode (the page builder); - -\hang|hmode| stands for horizontal mode (the paragraph builder); - -\hang|mmode| stands for displayed formula mode; - -\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox}); - -\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox}); - -\hang|-mmode| stands for math formula mode (not displayed). - -\yskip\noindent The mode is temporarily set to zero while processing \.{\\write} -texts in the |ship_out| routine. - -Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that -\TeX's ``big semantic switch'' can select the appropriate thing to -do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current -mode and |cur_cmd| is the current command code. - -@c -static const char *string_mode(int m) -{ /* prints the mode represented by |m| */ - if (m > 0) { - switch (m / (max_command_cmd + 1)) { - case 0: - return "vertical mode"; - break; - case 1: - return "horizontal mode"; - break; - case 2: - return "display math mode"; - break; - default: - break; - } - } else if (m == 0) { - return "no mode"; - } else { - switch ((-m) / (max_command_cmd + 1)) { - case 0: - return "internal vertical mode"; - break; - case 1: - return "restricted horizontal mode"; - break; - case 2: - return "math mode"; - break; - default: - break; - } - } - return "unknown mode"; -} - -@ @c -void print_mode(int m) -{ /* prints the mode represented by |m| */ - tprint(string_mode(m)); -} - -@ @c -void print_in_mode(int m) -{ /* prints the mode represented by |m| */ - tprint("' in "); - tprint(string_mode(m)); -} - -@ @c -int get_mode_id(void) -{ /* returns the mode represented by |m| */ - int m = cur_list.mode_field; - if (m > 0) { - switch (m / (max_command_cmd + 1)) { - case 0: - return 'v'; - break; - case 1: - return 'h'; - break; - case 2: - return 'm'; - break; - default: - return '\0'; - break; - } - } else if (m == 0) { - return 'n';; - } else { - switch ((-m) / (max_command_cmd + 1)) { - case 0: - return 'V'; - break; - case 1: - return 'H'; - break; - case 2: - return 'M'; - break; - default: - return '\0'; - break; - } - } -} - - -@ The state of affairs at any semantic level can be represented by -five values: - -\yskip\hang|mode| is the number representing the semantic mode, as -just explained. - -\yskip\hang|head| is a |pointer| to a list head for the list being built; -|link(head)| therefore points to the first element of the list, or -to |null| if the list is empty. - -\yskip\hang|tail| is a |pointer| to the final node of the list being -built; thus, |tail=head| if and only if the list is empty. - -\yskip\hang|prev_graf| is the number of lines of the current paragraph that -have already been put into the present vertical list. - -\yskip\hang|aux| is an auxiliary |memory_word| that gives further information -that is needed to characterize the situation. - -\yskip\noindent -In vertical mode, |aux| is also known as |prev_depth|; it is the scaled -value representing the depth of the previous box, for use in baseline -calculations, or it is |<=-1000|pt if the next box on the vertical list is to -be exempt from baseline calculations. In horizontal mode, |aux| is also -known as |space_factor|; it holds the current space factor used in -spacing calculations. In math mode, |aux| is also known as |incompleat_noad|; -if not |null|, it points to a record that represents the numerator of a -generalized fraction for which the denominator is currently being formed -in the current list. - -There is also a sixth quantity, |mode_line|, which correlates -the semantic nest with the user's input; |mode_line| contains the source -line number at which the current level of nesting was entered. The negative -of this line number is the |mode_line| at the level of the -user's output routine. - -A seventh quantity, |eTeX_aux|, is used by the extended features eTeX. -In math mode it is known as |delim_ptr| and points to the most -recent |fence_noad| of a |math_left_group|. - -In horizontal mode, the |prev_graf| field is used for initial language data. - -The semantic nest is an array called |nest| that holds the |mode|, |head|, -|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels -below the currently active one. Information about the currently active -level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|, -|aux|, and |mode_line|, which live in a struct that is ready to -be pushed onto |nest| if necessary. - -The math field is used by various bits and pieces in |texmath.w| - -@ This implementation of -\TeX\ uses two different conventions for representing sequential stacks. -@^stack conventions@>@^conventions for representing stacks@> - -\yskip\hang 1) If there is frequent access to the top entry, and if the -stack is essentially never empty, then the top entry is kept in a global -variable (even better would be a machine register), and the other entries -appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. The -semantic stack is handled this way. - -\yskip\hang 2) If there is infrequent top access, the entire stack contents -are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| -is treated this way, as we have seen. - -@c -list_state_record *nest; -int nest_ptr; /* first unused location of |nest| */ -int max_nest_stack; /* maximum of |nest_ptr| when pushing */ -int shown_mode; /* most recent mode shown by \.{\\tracingcommands} */ -halfword save_tail; /* save |tail| so we can examine whether we have an auto - kern before a glue */ - -@ We will see later that the vertical list at the bottom semantic level is split -into two parts; the ``current page'' runs from |page_head| to |page_tail|, -and the ``contribution list'' runs from |contrib_head| to |tail| of -semantic level zero. The idea is that contributions are first formed in -vertical mode, then ``contributed'' to the current page (during which time -the page-breaking decisions are made). For now, we don't need to know -any more details about the page-building process. - -@c -void initialize_nesting(void) -{ - nest_ptr = 0; - max_nest_stack = 0; - shown_mode = 0; - cur_list.mode_field = vmode; - cur_list.head_field = contrib_head; - cur_list.tail_field = contrib_head; - cur_list.eTeX_aux_field = null; - cur_list.prev_depth_field = ignore_depth; - cur_list.space_factor_field = 1000; - cur_list.incompleat_noad_field = null; - cur_list.ml_field = 0; - cur_list.pg_field = 0; - cur_list.dirs_field = null; - init_math_fields(); -} - -@ Here is a common way to make the current list grow: - -@c -void tail_append(halfword p) -{ - couple_nodes(cur_list.tail_field, p); - cur_list.tail_field = vlink(cur_list.tail_field); -} - -@ @c -halfword pop_tail(void) -{ - halfword n, r; - if (cur_list.tail_field != cur_list.head_field) { - r = cur_list.tail_field; - if (vlink(alink(cur_list.tail_field)) == cur_list.tail_field) { - n = alink(cur_list.tail_field); - } else { - n = cur_list.head_field; - while (vlink(n) != cur_list.tail_field) - n = vlink(n); - } - flush_node(cur_list.tail_field); - cur_list.tail_field = n; - vlink(n) = null; - return r; - } else { - return null; - } -} - -@ When \TeX's work on one level is interrupted, the state is saved by -calling |push_nest|. This routine changes |head| and |tail| so that -a new (empty) list is begun; it does not change |mode| or |aux|. - -@c -void push_nest(void) -{ /* enter a new semantic level, save the old */ - if (nest_ptr > max_nest_stack) { - max_nest_stack = nest_ptr; - if (nest_ptr == nest_size) - overflow("semantic nest size", (unsigned) nest_size); - } - incr(nest_ptr); - cur_list.mode_field = nest[nest_ptr - 1].mode_field; - cur_list.head_field = new_node(temp_node, 0); - cur_list.tail_field = cur_list.head_field; - cur_list.eTeX_aux_field = null; - cur_list.ml_field = line; - cur_list.pg_field = 0; - cur_list.dirs_field = null; - cur_list.prev_depth_field = nest[nest_ptr - 1].prev_depth_field; - cur_list.space_factor_field = nest[nest_ptr - 1].space_factor_field; - cur_list.incompleat_noad_field = nest[nest_ptr - 1].incompleat_noad_field; - init_math_fields(); -} - -@ Conversely, when \TeX\ is finished on the current level, the former -state is restored by calling |pop_nest|. This routine will never be -called at the lowest semantic level, nor will it be called unless |head| -is a node that should be returned to free memory. - -@c -void pop_nest(void) -{ /* leave a semantic level, re-enter the old */ - flush_node(cur_list.head_field); - decr(nest_ptr); -} - -@ Here is a procedure that displays what \TeX\ is working on, at all levels. - -@c -void show_activities(void) -{ - int p; /* index into |nest| */ - int m; /* mode */ - halfword q, r; /* for showing the current page */ - int t; /* ditto */ - tprint_nl(""); - print_ln(); - for (p = nest_ptr; p >= 0; p--) { - m = nest[p].mode_field; - tprint_nl("### "); - print_mode(m); - tprint(" entered at line "); - print_int(abs(nest[p].ml_field)); - /* we dont do this any more */ -#if 0 - - if (m == hmode) - if (nest[p].pg_field != 040600000) { - tprint(" (language"); - print_int(nest[p].pg_field % 0200000); - tprint(":hyphenmin"); - print_int(nest[p].pg_field / 020000000); - print_char(','); - print_int((nest[p].pg_field / 0200000) % 0100); - print_char(')'); - } -#endif - if (nest[p].ml_field < 0) - tprint(" (\\output routine)"); - if (p == 0) { - /* Show the status of the current page */ - if (page_head != page_tail) { - tprint_nl("### current page:"); - if (output_active) - tprint(" (held over for next output)"); - show_box(vlink(page_head)); - if (page_contents > empty) { - tprint_nl("total height "); - print_totals(); - tprint_nl(" goal height "); - print_scaled(page_goal); - r = vlink(page_ins_head); - while (r != page_ins_head) { - print_ln(); - tprint_esc("insert"); - t = subtype(r); - print_int(t); - tprint(" adds "); - if (count(t) == 1000) - t = height(r); - else - t = x_over_n(height(r), 1000) * count(t); - print_scaled(t); - if (type(r) == split_up_node) { - q = page_head; - t = 0; - do { - q = vlink(q); - if ((type(q) == ins_node) - && (subtype(q) == subtype(r))) - incr(t); - } while (q != broken_ins(r)); - tprint(", #"); - print_int(t); - tprint(" might split"); - } - r = vlink(r); - } - } - } - if (vlink(contrib_head) != null) - tprint_nl("### recent contributions:"); - } - show_box(vlink(nest[p].head_field)); - /* Show the auxiliary field, |a| */ - switch (abs(m) / (max_command_cmd + 1)) { - case 0: - tprint_nl("prevdepth "); - if (nest[p].prev_depth_field <= ignore_depth) - tprint("ignored"); - else - print_scaled(nest[p].prev_depth_field); - if (nest[p].pg_field != 0) { - tprint(", prevgraf "); - print_int(nest[p].pg_field); - if (nest[p].pg_field != 1) - tprint(" lines"); - else - tprint(" line"); - } - break; - case 1: - tprint_nl("spacefactor "); - print_int(nest[p].space_factor_field); - /* we dont do this any more, this was aux.rh originally */ -#if 0 - if (m > 0) { - if (nest[p].current_language_field > 0) { - tprint(", current language "); - print_int(nest[p].current_language_field); - } - } -#endif - break; - case 2: - if (nest[p].incompleat_noad_field != null) { - tprint("this will be denominator of:"); - show_box(nest[p].incompleat_noad_field); - } - } /* there are no other cases */ - - } -} diff --git a/Build/source/texk/web2c/luatexdir/tex/packaging.w b/Build/source/texk/web2c/luatexdir/tex/packaging.c index f094a0d9665..e2b007f11ef 100644 --- a/Build/source/texk/web2c/luatexdir/tex/packaging.w +++ b/Build/source/texk/web2c/luatexdir/tex/packaging.c @@ -1,84 +1,68 @@ -% packaging.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ We're essentially done with the parts of \TeX\ that are concerned with the -input (|get_next|) and the output (|ship_out|). So it's time to get heavily into -the remaining part, which does the real work of typesetting. - -After lists are constructed, \TeX\ wraps them up and puts them into boxes. Two -major subroutines are given the responsibility for this task: |hpack| applies to -horizontal lists (hlists) and |vpack| applies to vertical lists (vlists). The -main duty of |hpack| and |vpack| is to compute the dimensions of the resulting -boxes, and to adjust the glue if one of those dimensions is pre-specified. The -computed sizes normally enclose all of the material inside the new box; but some -items may stick out if negative glue is used, if the box is overfull, or if a -\.{\\vbox} includes other boxes that have been shifted left. - -The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node| for a box -containing the hlist that starts at |p|. Parameter |w| specifies a width; and -parameter |m| is either `|exactly|' or `|additional|'. Thus, |hpack(p,w,exactly)| -produces a box whose width is exactly |w|, while |hpack(p,w,additional)| yields a -box whose width is the natural width plus |w|. It is convenient to define a macro -called `|natural|' to cover the most common case, so that we can say -|hpack(p,natural)| to get a box that has the natural width of list |p|. - -Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a box -containing the vlist that starts at |p|. In this case |w| represents a height -instead of a width; the parameter |m| is interpreted as in |hpack|. - -@ The parameters to |hpack| and |vpack| correspond to \TeX's primitives like -`\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note that -`\.{\\hbox}' with no dimension following it is equivalent to `\.{\\hbox} -\.{spread} \.{0pt}'. The |scan_spec| subroutine scans such constructions in the -user's input, including the mandatory left brace that follows them, and it puts -the specification onto |save_stack| so that the desired box can later be obtained -by executing the following code: $$\vbox{\halign{#\hfil\cr -|save_ptr:=save_ptr-1;|\cr |hpack(p,saved_value(0),saved_level(0)).|\cr}}$$ - -@c -/* - void scan_spec(group_code c) - { - int spec_code; - if (scan_keyword("to")) { - spec_code = exactly; - scan_normal_dimen(); - } else if (scan_keyword("spread")) { - spec_code = additional; - scan_normal_dimen(); - } else { - spec_code = additional; - cur_val = 0; - } - set_saved_record(0, saved_boxspec, spec_code, cur_val); - save_ptr++; - new_save_level(c); - scan_left_brace(); - } +/*tex + + We're essentially done with the parts of \TeX\ that are concerned with the + input (|get_next|) and the output (|ship_out|). So it's time to get heavily + into the remaining part, which does the real work of typesetting. + + After lists are constructed, \TeX\ wraps them up and puts them into boxes. + Two major subroutines are given the responsibility for this task: |hpack| + applies to horizontal lists (hlists) and |vpack| applies to vertical lists + (vlists). The main duty of |hpack| and |vpack| is to compute the dimensions + of the resulting boxes, and to adjust the glue if one of those dimensions is + pre-specified. The computed sizes normally enclose all of the material inside + the new box; but some items may stick out if negative glue is used, if the + box is overfull, or if a \.{\\vbox} includes other boxes that have been + shifted left. + + The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node| for a + box containing the hlist that starts at |p|. Parameter |w| specifies a width; + and parameter |m| is either `|exactly|' or `|additional|'. Thus, + |hpack(p,w,exactly)| produces a box whose width is exactly |w|, while + |hpack(p,w,additional)| yields a box whose width is the natural width plus + |w|. It is convenient to define a macro called `|natural|' to cover the most + common case, so that we can say |hpack(p,natural)| to get a box that has the + natural width of list |p|. + + Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a box + containing the vlist that starts at |p|. In this case |w| represents a height + instead of a width; the parameter |m| is interpreted as in |hpack|. + + The parameters to |hpack| and |vpack| correspond to \TeX's primitives like + `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note that + `\.{\\hbox}' with no dimension following it is equivalent to `\.{\\hbox} + \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such constructions in + the user's input, including the mandatory left brace that follows them, and + it puts the specification onto |save_stack| so that the desired box can later + be obtained by executing the following code: $$\vbox{\halign{#\hfil\cr + |save_ptr:=save_ptr-1;|\cr |hpack(p,saved_value(0),saved_level(0)).|\cr}}$$ + */ +/*tex Scan a box specification and left brace: */ + void scan_spec(group_code c) -{ /* scans a box specification and left brace */ +{ int spec_code; boolean done = false ; do { @@ -89,7 +73,7 @@ void scan_spec(group_code c) cur_val = 0; done = true; } else { - /* todo: attr */ + /*tex todo: attr */ back_input(); if (scan_keyword("to")) { spec_code = exactly; @@ -110,84 +94,21 @@ void scan_spec(group_code c) } } -@ When scanning, special care is necessary to ensure that the special -|save_stack| codes are placed just below the new group code, because scanning can -change |save_stack| when \.{\\csname} appears. +/*tex -This coincides with the text on |dir| and |attr| keywords, as these are exaclty -the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the input stream (the -others are \.{\\vcenter}, \.{\\valign}, and \.{\\halign}). + When scanning, special care is necessary to ensure that the special + |save_stack| codes are placed just below the new group code, because scanning + can change |save_stack| when \.{\\csname} appears. + + This coincides with the text on |dir| and |attr| keywords, as these are + exaclty the uses of \.{\\hbox}, \.{\\vbox}, and \.{\\vtop} in the input + stream (the others are \.{\\vcenter}, \.{\\valign}, and \.{\\halign}). -@c -/* - void scan_full_spec(group_code c, int spec_direction) - { - int s; - int i; - int v; - int spec_code; - halfword attr_list; - if (attr_list_cache == cache_disabled) - update_attribute_cache(); - attr_list = attr_list_cache; - s = saved_value(0); - CONTINUE: - while (cur_cmd == relax_cmd || cur_cmd == spacer_cmd) { - get_x_token(); - if (cur_cmd != relax_cmd && cur_cmd != spacer_cmd) - back_input(); - } - if (scan_keyword("attr")) { - scan_register_num(); - i = cur_val; - scan_optional_equals(); - scan_int(); - v = cur_val; - if ((attr_list != null) && (attr_list == attr_list_cache)) { - attr_list = copy_attribute_list(attr_list_cache); - add_node_attr_ref(attr_list); - } - attr_list = do_set_attribute(attr_list, i, v); - goto CONTINUE; - } - if (scan_keyword("dir")) { - scan_direction(); - spec_direction = cur_val; - goto CONTINUE; - } - if (attr_list == attr_list_cache) { - add_node_attr_ref(attr_list); - } - if (scan_keyword("to")) { - spec_code = exactly; - } else if (scan_keyword("spread")) { - spec_code = additional; - } else { - spec_code = additional; - cur_val = 0; - goto FOUND; - } - scan_normal_dimen(); - FOUND: - set_saved_record(0, saved_boxcontext, 0, s); - set_saved_record(1, saved_boxspec, spec_code, cur_val); - if (spec_direction != -1) { - set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr); - text_dir_ptr = new_dir(spec_direction); - } else { - set_saved_record(2, saved_boxdir, spec_direction, null); - } - set_saved_record(3, saved_boxattr, 0, attr_list); - save_ptr += 4; - new_save_level(c); - scan_left_brace(); - eq_word_define(int_base + body_direction_code, spec_direction); - eq_word_define(int_base + par_direction_code, spec_direction); - eq_word_define(int_base + text_direction_code, spec_direction); - } */ -/* scans a box specification and left brace */ +/*tex Scan a box specification and left brace: */ + +#define first_char_is(a,A) (cur_cs == 0 && (cur_chr == a || cur_chr == A)) void scan_full_spec(group_code c, int spec_direction, int just_pack) { @@ -195,10 +116,12 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) boolean done = false ; halfword attr_list; boolean attr_done = false ; + boolean dir_done = false ; if (attr_list_cache == cache_disabled) update_attribute_cache(); attr_list = attr_list_cache; - s = saved_value(0); /* the box context */ + /*tex The box context: */ + s = saved_value(0); do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); @@ -219,6 +142,7 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) } } KEYWORDS: + /*tex Multiple |attr| keys possible (before or after dir). */ if (scan_keyword("attr")) { scan_register_num(); i = cur_val; @@ -232,11 +156,23 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) attr_list = do_set_attribute(attr_list, i, v); goto CONTINUE; } - if (scan_keyword("dir")) { - scan_direction(); - spec_direction = cur_val; - goto CONTINUE; + /*tex We only permit one |(b)dir| directive. */ + if (! dir_done) { + if (scan_keyword("bdir")) { + scan_int(); + check_dir_value(cur_val); + spec_direction = cur_val; + dir_done = true; + goto CONTINUE; + } + if (scan_keyword("dir")) { + scan_direction(); + spec_direction = cur_val; + dir_done = true; + goto CONTINUE; + } } + /*tex Only one |to| or |spread| key possible and it comes last. */ if (scan_keyword("to")) { spec_code = exactly; } else if (scan_keyword("spread")) { @@ -256,7 +192,7 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) add_node_attr_ref(attr_list); set_saved_record(0, saved_boxcontext, 0, s); set_saved_record(1, saved_boxspec, spec_code, cur_val); - /* DIR: Adjust |text_dir_ptr| for |scan_spec| */ + /*tex Adjust |text_dir_ptr| for |scan_spec|. */ if (spec_direction != -1) { set_saved_record(2, saved_boxdir, spec_direction, text_dir_ptr); text_dir_ptr = new_dir(spec_direction); @@ -270,73 +206,91 @@ void scan_full_spec(group_code c, int spec_direction, int just_pack) if (! done) { scan_left_brace(); } - /* no gain: if (body_direction_par != spec_direction) etc */ + /*tex No gain in |if (body_direction_par != spec_direction)| etc. */ eq_word_define(int_base + body_direction_code, spec_direction); eq_word_define(int_base + par_direction_code, spec_direction); eq_word_define(int_base + text_direction_code, spec_direction); } +/*tex -@ To figure out the glue setting, |hpack| and |vpack| determine how much -stretchability and shrinkability are present, considering all four orders of -infinity. The highest order of infinity that has a nonzero coefficient is then -used as if no other orders were present. + To figure out the glue setting, |hpack| and |vpack| determine how much + stretchability and shrinkability are present, considering all four orders of + infinity. The highest order of infinity that has a nonzero coefficient is + then used as if no other orders were present. -For example, suppose that the given list contains six glue nodes with the -respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill. Then the -total is essentially 2fil; and if a total additional space of 6pt is to be -achieved by stretching, the actual amounts of stretch will be 0pt, 0pt, 15pt, -0pt, $-9$pt, and 0pt, since only `fil' glue will be considered. (The `fill' glue -is therefore not really stretching infinitely with respect to `fil'; nobody would -actually want that to happen.) + For example, suppose that the given list contains six glue nodes with the + respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill. Then + the total is essentially 2fil; and if a total additional space of 6pt is to + be achieved by stretching, the actual amounts of stretch will be 0pt, 0pt, + 15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be considered. (The + `fill' glue is therefore not really stretching infinitely with respect to + `fil'; nobody would actually want that to happen.) -The arrays |total_stretch| and |total_shrink| are used to determine how much glue -of each kind is present. A global variable |last_badness| is used to implement -\.{\\badness}. + The arrays |total_stretch| and |total_shrink| are used to determine how much + glue of each kind is present. A global variable |last_badness| is used to + implement \.{\\badness}. + +*/ + +/*tex Glue found by |hpack| or |vpack|. */ -@c scaled total_stretch[5]; -scaled total_shrink[5]; /* glue found by |hpack| or |vpack| */ -int last_badness; /* badness of the most recently packaged box */ +scaled total_shrink[5]; + +/*tex Badness of the most recently packaged box. */ + +int last_badness; + +/*tex -@ If the global variable |adjust_tail| is non-null, the |hpack| routine also -removes all occurrences of |ins_node|, |mark_node|, and |adjust_node| items and -appends the resulting material onto the list that ends at location |adjust_tail|. + If the global variable |adjust_tail| is non-null, the |hpack| routine also + removes all occurrences of |ins_node|, |mark_node|, and |adjust_node| items + and appends the resulting material onto the list that ends at location + |adjust_tail|. -@c -halfword adjust_tail; /* tail of adjustment list */ +*/ + +/*tex Tail of adjustment list. */ + +halfword adjust_tail; +/*tex -@ Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to -|pre_adjust_tail| instead of |adjust_tail|. + Materials in \.{\\vadjust} used with \.{pre} keyword will be appended to + |pre_adjust_tail| instead of |adjust_tail|. + +*/ -@c halfword pre_adjust_tail; halfword last_leftmost_char; halfword last_rightmost_char; -halfword next_char_p; /* pointer to the next char of an implicit kern */ -halfword prev_char_p; /* pointer to the previous char of an implicit kern */ +/*tex Pointers to the prev and next char of an implicit kern. */ + +halfword next_char_p; +halfword prev_char_p; + +/*tex This procedure is called repeatedly from inside the line break algorithm. */ -@ This procedure is called repeatedly from inside the line break algorithm. -@c void set_prev_char_p(halfword p) { prev_char_p = p; } -/* - the kern stretch / shrink code was (or had become) rather weird ... the width field - is set, and then used in a second calculation, repeatedly, so why is that ... maybe some - some weird left-over ... anyway, the values are so small that in practice they are not - significant at all when the backend sees them because a few hundred sp positive or - negative are just noise there (so adjustlevel 3 has hardly any consequence for the - result but is more efficient) -*/ +/*tex + + The kern stretch / shrink code was (or had become) rather weird ... the width + field is set, and then used in a second calculation, repeatedly, so why is + that ... maybe some some weird left-over ... anyway, the values are so small + that in practice they are not significant at all when the backend sees them + because a few hundred sp positive or negative are just noise there (so + adjustlevel 3 has hardly any consequence for the result but is more + efficient). +*/ -@ @c scaled char_stretch(halfword p) { internal_font_number f = font(p); @@ -345,7 +299,7 @@ scaled char_stretch(halfword p) int c = character(p); int ef = get_ef_code(f, c); if (ef > 0) { - scaled dw = calc_char_width(f, c, m) - char_width(f, c) - x_advance(p); + scaled dw = calc_char_width(f, c, m) - char_width(f, c); if (dw > 0) { return round_xn_over_d(dw, ef, 1000); } @@ -354,7 +308,6 @@ scaled char_stretch(halfword p) return 0; } -@ @c scaled char_shrink(halfword p) { internal_font_number f = font(p); @@ -363,7 +316,7 @@ scaled char_shrink(halfword p) int c = character(p); int ef = get_ef_code(f, c); if (ef > 0) { - scaled dw = char_width(f, c) + x_advance(p) - calc_char_width(f, c, -m); + scaled dw = char_width(f, c) - calc_char_width(f, c, -m); if (dw > 0) { return round_xn_over_d(dw, ef, 1000); } @@ -372,33 +325,6 @@ scaled char_shrink(halfword p) return 0; } -@ @c -/* -scaled kern_stretch(halfword p) -{ - halfword l, r; - scaled d; - int m; - if ((prev_char_p == null) || (vlink(prev_char_p) != p) || (vlink(p) == null)) - return 0; - l = prev_char_p; - // we need a left char - if (!is_char_node(l)) - return 0; - r = vlink(p); - // and a right char - if (!is_char_node(r)) - return 0; - // and a reason to kern - if ((font(l) != font(r)) || (font_max_stretch(font(l)) == 0)) - return 0; - m = font_max_stretch(font(l)); - d = get_kern(font(l), character(l), character(r)); // real kern, so what is width(p) then; the messed up one - d = round_xn_over_d(d, 1000 + m, 1000); - return round_xn_over_d(d - width(p), get_ef_code(font(l), character(l)), 1000); -} -*/ - scaled kern_stretch(halfword p) { int m; @@ -407,69 +333,39 @@ scaled kern_stretch(halfword p) halfword l; halfword r; if (w == 0) { - /* why bother about zero kerns */ + /*tex Why bother about zero kerns. */ return 0; } l = prev_char_p ; if ((l == null) || (vlink(l) != p)) { - /* we only care about kerns following a char*/ + /*tex We only care about kerns following a char. */ return 0; } r = vlink(p); if (r == null) { - /* we only care about kerns between a char and something else */ + /*tex We only care about kerns between a char and something else. */ } if (!(is_char_node(l) && is_char_node(r))) { - /* we want two chars (but but don't care about the fonts) */ + /*tex We want two chars (but but don't care about the fonts). */ return 0; } - /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */ + /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */ m = (font_max_stretch(font(l)) + font_max_stretch(font(r)))/2; if (m == 0) { - /* nothing to kern */ + /*tex nothing to kern */ return 0; } d = round_xn_over_d(w, 1000 + m, 1000); - /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */ + /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */ e = (get_ef_code(font(l), character(l)) + get_ef_code(font(r), character(r)))/2 ; if (e == 1000) { x = d - w; } else { x = round_xn_over_d(d - w, e, 1000); } - /* - printf("STRETCH w=%i s=%i x=%i\n",w,e+m,x); - */ return x; } -@ @c -/* -scaled kern_shrink(halfword p) -{ - halfword l, r; - scaled d; - int m; - if ((prev_char_p == null) || (vlink(prev_char_p) != p) || (vlink(p) == null)) - return 0; - l = prev_char_p; - // we need a left char - if (!is_char_node(l)) - return 0; - r = vlink(p); - // and a right char - if (!is_char_node(r)) - return 0; - // and a reason to kern - if ((font(l) != font(r)) || (font_max_shrink(font(l)) == 0)) - return 0; - m = font_max_shrink(font(l)); - d = get_kern(font(l), character(l), character(r)); // real kern, so what is width(p) then; the messed up one - d = round_xn_over_d(d, 1000 - m, 1000); - return round_xn_over_d(width(p) - d, get_ef_code(font(l), character(l)), 1000); -} -*/ - scaled kern_shrink(halfword p) { int m; @@ -478,26 +374,26 @@ scaled kern_shrink(halfword p) halfword l; halfword r; if (w == 0) { - /* why bother about zero kerns */ + /*tex Why bother about zero kerns. */ return 0; } l = prev_char_p ; if ((l == null) || (vlink(l) != p)) { - /* we only care about kerns following a char*/ + /*tex We only care about kerns following a char. */ return 0; } r = vlink(p); if (r == null) { - /* we only care about kerns between a char and something else */ + /*tex We only care about kerns between a char and something else. */ } if (!(is_char_node(l) && is_char_node(r))) { - /* we want two chars (but but don't care about the fonts) */ + /*tex We want two chars (but but don't care about the fonts). */ return 0; } - /* we use the old logic, kind of, but average the ef as we might depend on proper overlap */ + /*tex We use the old logic, kind of, but average the ef as we might depend on proper overlap. */ m = (font_max_shrink(font(l)) + font_max_shrink(font(r)))/2; if (m == 0) { - /* nothing to kern */ + /*tex Nothing to kern. */ return 0; } d = round_xn_over_d(w, 1000 - m, 1000); @@ -507,13 +403,9 @@ scaled kern_shrink(halfword p) } else { x = round_xn_over_d(w - d, e, 1000); } - /* - printf("SHRINK w=%i s=%i x=%i\n",w,e+m,x); - */ return x; } -@ @c void do_subst_font(halfword p, int ex_ratio) { if (type(p) == disc_node) { @@ -555,7 +447,6 @@ void do_subst_font(halfword p, int ex_ratio) } } -@ @c scaled char_pw(halfword p, int side) { internal_font_number f; @@ -582,7 +473,6 @@ scaled char_pw(halfword p, int side) return round_xn_over_d(w, c, 1000); } -@ @c halfword new_margin_kern(scaled w, halfword p, int side) { halfword k, q; @@ -595,11 +485,16 @@ halfword new_margin_kern(scaled w, halfword p, int side) return k; } -@ Here is |hpack|, which is place where we do font substituting when font -expansion is being used. +/*tex + + Here we prepare for |hpack|, which is place where we do font substituting + when font expansion is being used. + +*/ + +/*tex The current expansion ratio, needed for recursive call. */ -@c -int font_expand_ratio = 0; /* current expansion ratio, needed for recursive call */ +int font_expand_ratio = 0; int ignore_math_skip(halfword p) { @@ -632,47 +527,60 @@ int ignore_math_skip(halfword p) halfword hpack(halfword p, scaled w, int m, int pack_direction) { - halfword r; /* the box node that will be returned */ - halfword q; /* trails behind |p| */ - scaled h = 0; /* height */ - scaled d = 0; /* depth */ - scaled x = 0; /* natural width */ + /*tex the box node that will be returned */ + halfword r; + /*tex trails behind |p| */ + halfword q; + /*tex height */ + scaled h = 0; + /*tex depth */ + scaled d = 0; + /*tex natural width */ + scaled x = 0; scaled_whd whd; - scaled s; /* shift amount */ - int o; /* order of infinity */ - halfword dir_ptr1 = null; /* for managing the direction stack */ - int hpack_dir; /* the current direction */ + /*tex shift amount */ + scaled s; + /*tex order of infinity */ + int o; + /*tex for managing the direction stack */ + halfword dir_ptr1 = null; + /*tex the current direction */ + int hpack_dir; int disc_level = 0; halfword pack_interrupt[8]; scaled font_stretch = 0; scaled font_shrink = 0; int adjust_spacing = adjust_spacing_par; - -/* - int font_expand_ratio = 0; -*/ last_badness = 0; - r = new_node(hlist_node, min_quarterword); /* the box node that will be returned */ + /*tex the box node that will be returned */ + r = new_node(hlist_node, min_quarterword); if (pack_direction == -1) { hpack_dir = text_direction_par; } else { hpack_dir = pack_direction; } box_dir(r) = hpack_dir; - /* - potential optimimization, save a little but neglectable in practice (not so - many empty boxes are used) + /*tex + A potential optimimization, save a little but neglectable in practice + (not so many empty boxes are used): + + \starttyping if (p == null) { width(r) = w; return r; } + \stoptyping + */ - push_dir(dir_ptr1,hpack_dir); /* push null */ - q = r + list_offset; /* hm, adding something to a node address? */ + /*tex push null */ + push_dir(dir_ptr1,hpack_dir); + /*tex hm, adding something to a node address? */ + q = r + list_offset; vlink(q) = p; if (m == cal_expand_ratio) { - prev_char_p = null; /* why not always */ + /*tex Why not always: */ + prev_char_p = null; } if (adjust_spacing > 2) { adjust_spacing = 0; @@ -687,7 +595,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) total_shrink[fill] = 0; total_stretch[filll] = 0; total_shrink[filll] = 0; - RESWITCH: while ((p != null) || (disc_level > 0)) { if (p == null) { @@ -695,20 +602,24 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) p = pack_interrupt[disc_level]; goto RESWITCH; } - /* - Examine node |p| in the hlist, taking account of its effect - on the dimensions of the new box, or moving it to the adjustment list; - then advance |p| to the next node + /*tex + + Examine node |p| in the hlist, taking account of its effect on the + dimensions of the new box, or moving it to the adjustment list; then + advance |p| to the next node. + */ while (is_char_node(p)) { - /* + /*tex + Incorporate character dimensions into the dimensions of the hbox that will contain~it, then move to the next node. The following code is part of \TeX's inner loop; i.e., adding another character of text to the user's input will cause each of these instructions to be exercised one more time. - */ + + */ if (m >= cal_expand_ratio) { prev_char_p = p; if (m == cal_expand_ratio) { @@ -730,13 +641,16 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) switch (type(p)) { case hlist_node: case vlist_node: - /* - Incorporate box dimensions into the dimensions of the hbox - that will contain~it + /*tex + + Incorporate box dimensions into the dimensions of the + hbox that will contain~it + + The code here implicitly uses the fact that running + dimensions are indicated by |null_flag|, which will be + ignored in the calculations because it is a highly + negative number. - The code here implicitly uses the fact that running dimensions are - indicated by |null_flag|, which will be ignored in the calculations - because it is a highly negative number. */ s = shift_amount(p); whd = pack_width_height_depth(hpack_dir, box_dir(p), p, false); @@ -746,20 +660,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) if (whd.dp + s > d) d = whd.dp + s; break; - /* - case rule_node: - case unset_node: - x += width(p); - if (type(p) >= rule_node) // always - s = 0; - else - s = shift_amount(p); - if (height(p) - s > h) - h = height(p) - s; - if (depth(p) + s > d) - d = depth(p) + s; - break; - */ case rule_node: case unset_node: x += width(p); @@ -768,18 +668,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) if (depth(p) > d) d = depth(p); break; - /* */ case math_node: - /* begin mathskip code */ + /*tex Begin mathskip code. */ if (glue_is_zero(p) || ignore_math_skip(p)) { x += surround(p); break; } else { - /* fall through */ + /*tex Fall through. */ } - /* end mathskip code */ + /*tex End mathskip code. */ case glue_node: - /* Incorporate glue into the horizontal totals */ + /*tex Incorporate glue into the horizontal totals. */ x += width(p); o = stretch_order(p); total_stretch[o] = total_stretch[o] + stretch(p); @@ -796,12 +695,12 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) case kern_node: x += width(p); if (subtype(p) == font_kern && adjust_spacing) { - /* so only when 1 or 2 */ + /*tex So only when 1 or 2. */ if (m == cal_expand_ratio) { font_stretch = font_stretch + kern_stretch(p); font_shrink = font_shrink + kern_shrink(p); } else if (m == subst_ex_font) { - /* this is the finalizer */ + /*tex This is the finalizer. */ int k = 0; if (font_expand_ratio > 0) { k = kern_stretch(p); @@ -810,9 +709,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) } ex_kern(p) = k; x += k; - /* - if (x!=0) printf("SET %i %i %i\n",font_expand_ratio,k,x); - */ } } break; @@ -826,8 +722,8 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) } break; case dir_node: - /* Adjust the dir stack for the |hpack| routine */ - if (dir_dir(p) >= 0) { + /*tex Adjust the dir stack for the |hpack| routine. */ + if (subtype(p) == normal_dir) { hpack_dir = dir_dir(p); push_dir_node(dir_ptr1,p); } else { @@ -856,15 +752,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) case ins_node: case mark_node: case adjust_node: - /* - Transfer node |p| to the adjustment list. - - Although node |q| is not necessarily the immediate predecessor of node |p|, - it always points to some node in the list preceding |p|. Thus, we can delete - nodes by moving |q| when necessary. The algorithm takes linear time, and the - extra computation does not intrude on the inner loop unless it is necessary - to make a deletion. - */ + /*tex + + Transfer node |p| to the adjustment list.\Although node + |q| is not necessarily the immediate predecessor of node + |p|, it always points to some node in the list preceding + |p|. Thus, we can delete nodes by moving |q| when + necessary. The algorithm takes linear time, and the extra + computation does not intrude on the inner loop unless it + is necessary to make a deletion. + + */ if (adjust_tail != null || pre_adjust_tail != null) { while (vlink(q) != p) q = vlink(q); @@ -885,13 +783,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) p = q; } break; - /* */ default: break; } p = vlink(p); } - } if (adjust_tail != null) @@ -900,31 +796,35 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) vlink(pre_adjust_tail) = null; height(r) = h; depth(r) = d; - /* + /*tex + Determine the value of |width(r)| and the appropriate glue setting; then |return| or |goto common_ending|. When we get to the present part of the program, |x| is the natural width of the box being packaged. + */ if (m == additional) w = x + w; width(r) = w; x = w - x; - /* now |x| is the excess to be made up */ + /*tex Now |x| is the excess to be made up. */ if (x == 0) { glue_sign(r) = normal; glue_order(r) = normal; set_glue_ratio_zero(glue_set(r)); goto EXIT; } else if (x > 0) { - /* - Determine horizontal glue stretch setting, then |return| - or \hbox{|goto common_ending|}. + /*tex + + Determine horizontal glue stretch setting, then |return| or + |goto common_ending|. If |hpack| is called with |m=cal_expand_ratio| we calculate |font_expand_ratio| and return without checking for overfull or underfull box. + */ if (total_stretch[filll] != 0) o = filll; @@ -936,7 +836,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) o = sfi; else o = normal; - if ((m == cal_expand_ratio) && (o == normal) && (font_stretch > 0)) { font_expand_ratio = divide_scaled_n(x, font_stretch, 1000.0); goto EXIT; @@ -946,15 +845,17 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) if (total_stretch[o] != 0) { glue_set(r) = unfloat((double) x / total_stretch[o]); } else { - /* there's nothing to stretch */ + /*tex There's nothing to stretch. */ glue_sign(r) = normal; set_glue_ratio_zero(glue_set(r)); } if (o == normal) { if (list_ptr(r) != null) { - /* - Report an underfull hbox and |goto common_ending|, if this box - is sufficiently bad. + /*tex + + Report an underfull hbox and |goto common_ending|, if this + box is sufficiently bad. + */ last_badness = badness(x, total_stretch[normal]); if (last_badness > hbadness_par) { @@ -987,9 +888,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) } goto EXIT; } else { - /* - Determine horizontal glue shrink setting, then |return| - or \hbox{|goto common_ending|}, + /*tex + + Determine horizontal glue shrink setting, then |return| or |goto + common_ending|, + */ if (total_shrink[filll] != 0) o = filll; @@ -1001,7 +904,6 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) o = sfi; else o = normal; - if ((m == cal_expand_ratio) && (o == normal) && (font_shrink > 0)) { font_expand_ratio = divide_scaled_n(x, font_shrink, 1000.0); goto EXIT; @@ -1011,18 +913,20 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) if (total_shrink[o] != 0) { glue_set(r) = unfloat((double) (-x) / (double) total_shrink[o]); } else { - /* there's nothing to shrink */ + /*tex There's nothing to shrink. */ glue_sign(r) = normal; set_glue_ratio_zero(glue_set(r)); } if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) { int overshoot = -x - total_shrink[normal] ; last_badness = 1000000; - /* use the maximum shrinkage */ + /*tex Use the maximum shrinkage */ set_glue_ratio_one(glue_set(r)); - /* - Report an overfull hbox and |goto common_ending|, if this box - is sufficiently bad. + /*tex + + Report an overfull hbox and |goto common_ending|, if this box is + sufficiently bad. + */ if ((overshoot > hfuzz_par) || (hbadness_par < 100)) { int callback_id = callback_defined(hpack_quality_callback); @@ -1050,9 +954,11 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) } } else if (o == normal) { if (list_ptr(r) != null) { - /* + /*tex + Report a tight hbox and |goto common_ending|, if this box is sufficiently bad. + */ last_badness = badness(-x, total_shrink[normal]); if (last_badness > hbadness_par) { @@ -1079,10 +985,7 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) } COMMON_ENDING: - /* - Finish issuing a diagnostic message for an overfull or underfull - hbox. - */ + /*tex Finish issuing a diagnostic message for an overfull or underfull hbox. */ if (output_active) { tprint(") has occurred while \\output is active"); } else { @@ -1113,17 +1016,16 @@ halfword hpack(halfword p, scaled w, int m, int pack_direction) q = list_ptr(r); list_ptr(r) = null; flush_node(r); - /* this nested call uses the more or less global font_expand_ratio */ + /*tex This nested call uses the more or less global font_expand_ratio. */ r = hpack(q, w, subst_ex_font, hpack_dir); } while (dir_ptr1 != null) pop_dir_node(dir_ptr1); - /* here we reset the font_expan_ratio */ + /*tex Here we reset the |font_expand_ratio|. */ font_expand_ratio = 0; return r; } -@ @c halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int pac, int just_pack, halfword attr) { halfword q; @@ -1131,32 +1033,33 @@ halfword filtered_hpack(halfword p, halfword qt, scaled w, int m, int grp, int p q = vlink(p); } else if (type(p) == temp_node && vlink(p) == null) { q = vlink(p); - /* - q = new_node(hlist_node, min_quarterword); - box_dir(q) = (pac == -1) ? text_direction_par : pac; - width(q) = w; - return q; - */ } else { new_hyphenation(p, qt); - (void) new_ligkern(p, qt); /* we don't care about the tail in this case */ + /*tex We don't care about the tail in this case. */ + (void) new_ligkern(p, qt); q = vlink(p); - /* maybe here: alink(p) = null */ - q = lua_hpack_filter(q, w, m, grp, pac, attr); /* ignores empty anyway */ /* maybe also pass tail */ + /*tex Maybe here: |alink(p) = null|. */ + /*tex ignores empty anyway. Maybe also pass tail? */ + q = lua_hpack_filter(q, w, m, grp, pac, attr); } return hpack(q, w, m, pac); } -@ here is a function to calculate the natural whd of a (horizontal) node list +/*tex -@c -scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, - int g_sign, int g_order, int pack_direction) + Here is a function to calculate the natural whd of a (horizontal) node list. + +*/ + +scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, int g_sign, int g_order, int pack_direction) { - scaled s; /* shift amount */ - halfword g; /* points to a glue specification */ + /*tex shift amount */ + scaled s; + /*tex points to a glue specification */ + halfword g; int hpack_dir; - scaled_whd xx; /* for recursion */ + /*tex For recursion */ + scaled_whd xx; scaled_whd whd, siz = { 0, 0, 0 }; scaled gp = 0; scaled gm = 0; @@ -1187,20 +1090,6 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, if (whd.dp + s > siz.dp) siz.dp = whd.dp + s; break; - /* - case rule_node: - case unset_node: - siz.wd += width(p); - if (type(p) >= rule_node) // always true - s = 0; - else - s = shift_amount(p); - if (height(p) - s > siz.ht) - siz.ht = height(p) - s; - if (depth(p) + s > siz.dp) - siz.dp = depth(p) + s; - break; - */ case rule_node: case unset_node: siz.wd += width(p); @@ -1209,29 +1098,28 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, if (depth(p) > siz.dp) siz.dp = depth(p); break; - /* */ case math_node: - /* begin mathskip code */ + /*tex Begin mathskip code. */ if (glue_is_zero(p) || ignore_math_skip(p)) { siz.wd += surround(p); break; } else { - /* fall through */ + /*tex Fall through. */ } - /* end mathskip code */ + /*tex End mathskip code. */ case glue_node: siz.wd += width(p); if (g_sign != normal) { if (g_sign == stretching) { if (stretch_order(p) == g_order) { - /* - siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p))); + /*tex + |siz.wd += float_round(float_cast(g_mult) * float_cast(stretch(p)))| */ gp += stretch(p); } } else if (shrink_order(p) == g_order) { - /* - siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p))); + /*tex + |siz.wd -= float_round(float_cast(g_mult) * float_cast(shrink(p)));| */ gm += shrink(p); } @@ -1275,34 +1163,46 @@ scaled_whd natural_sizes(halfword p, halfword pp, glue_ratio g_mult, return siz; } -@ In order to provide a decent indication of where an overfull or underfull box -originated, we use a global variable |pack_begin_line| that is set nonzero only -when |hpack| is being called by the paragraph builder or the alignment finishing -routine. +/*tex + + In order to provide a decent indication of where an overfull or underfull box + originated, we use a global variable |pack_begin_line| that is set nonzero + only when |hpack| is being called by the paragraph builder or the alignment + finishing routine. -@ The source file line where the current paragraph or alignment began; a negative -value denotes alignment: + The source file line where the current paragraph or alignment began; a + negative value denotes alignment: + +*/ -@c int pack_begin_line; -@ The |vpack| subroutine is actually a special case of a slightly more general -routine called |vpackage|, which has four parameters. The fourth parameter, which -is |max_dimen| in the case of |vpack|, specifies the maximum depth of the page -box that is constructed. The depth is first computed by the normal rules; if it -exceeds this limit, the reference point is simply moved down until the limiting -depth is attained. +/*tex + + The |vpack| subroutine is actually a special case of a slightly more general + routine called |vpackage|, which has four parameters. The fourth parameter, + which is |max_dimen| in the case of |vpack|, specifies the maximum depth of + the page box that is constructed. The depth is first computed by the normal + rules; if it exceeds this limit, the reference point is simply moved down + until the limiting depth is attained. + +*/ -@c halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) { - halfword r; /* the box node that will be returned */ - scaled w = 0; /* width */ - scaled d = 0; /* depth */ - scaled x = 0; /* natural height */ + /*tex the box node that will be returned */ + halfword r; + /*tex width */ + scaled w = 0; + /*tex depth */ + scaled d = 0; + /*tex natural height */ + scaled x = 0; scaled_whd whd; - scaled s; /* shift amount */ - int o; /* order of infinity */ + /*tex shift amount */ + scaled s; + /*tex order of infinity */ + int o; last_badness = 0; r = new_node(vlist_node, 0); if (pack_direction == -1) { @@ -1323,12 +1223,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) total_shrink[fill] = 0; total_stretch[filll] = 0; total_shrink[filll] = 0; - while (p != null) { - /* - Examine node |p| in the vlist, taking account of its effect - on the dimensions of the new box; then advance |p| to the next - node. + /*tex + + Examine node |p| in the vlist, taking account of its effect on the + dimensions of the new box; then advance |p| to the next node. + */ if (is_char_node(p)) { confusion("vpack"); @@ -1336,9 +1236,11 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) switch (type(p)) { case hlist_node: case vlist_node: - /* - Incorporate box dimensions into the dimensions of - the vbox that will contain it. + /*tex + + Incorporate box dimensions into the dimensions of the vbox + that will contain it. + */ s = shift_amount(p); whd = pack_width_height_depth(box_dir(r), box_dir(p), p, false); @@ -1347,19 +1249,6 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) x += d + whd.ht; d = whd.dp; break; - /* - case rule_node: - case unset_node: - x += d + height(p); - d = depth(p); - if (type(p) >= rule_node) // always - s = 0; - else - s = shift_amount(p); - if (width(p) + s > w) - w = width(p) + s; - break; - */ case rule_node: case unset_node: x += d + height(p); @@ -1367,9 +1256,8 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) if (width(p) > w) w = width(p); break; - /* */ case glue_node: - /* Incorporate glue into the vertical totals */ + /*tex Incorporate glue into the vertical totals. */ x += d; d = 0; x += width(p); @@ -1400,27 +1288,30 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) } else { depth(r) = d; } - /* - Determine the value of |height(r)| and the appropriate glue setting; - then |return| or |goto common_ending|. + /*tex + + Determine the value of |height(r)| and the appropriate glue setting; then + |return| or |goto common_ending|. - When we get to the present part of the program, |x| is the natural - height of the box being packaged. + When we get to the present part of the program, |x| is the natural height + of the box being packaged. */ if (m == additional) h = x + h; height(r) = h; x = h - x; - /* now |x| is the excess to be made up */ + /*tex Now |x| is the excess to be made up. */ if (x == 0) { glue_sign(r) = normal; glue_order(r) = normal; set_glue_ratio_zero(glue_set(r)); return r; } else if (x > 0) { - /* - Determine vertical glue stretch setting, then |return| - or \hbox{|goto common_ending|}. + /*tex + + Determine vertical glue stretch setting, then |return| or |goto + common_ending|. + */ if (total_stretch[filll] != 0) o = filll; @@ -1432,20 +1323,22 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) o = sfi; else o = normal; - glue_order(r) = (quarterword) o; glue_sign(r) = stretching; if (total_stretch[o] != 0) { glue_set(r) = unfloat((double) x / total_stretch[o]); } else { + /*tex There's nothing to stretch. */ glue_sign(r) = normal; - set_glue_ratio_zero(glue_set(r)); /* there's nothing to stretch */ + set_glue_ratio_zero(glue_set(r)); } if (o == normal) { if (list_ptr(r) != null) { - /* - Report an underfull vbox and |goto common_ending|, if this box - is sufficiently bad. + /*tex + + Report an underfull vbox and |goto common_ending|, if this + box is sufficiently bad. + */ last_badness = badness(x, total_stretch[normal]); if (last_badness > vbadness_par) { @@ -1471,11 +1364,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) } } return r; - } else { - /* - Determine vertical glue shrink setting, then |return| - or \hbox{|goto common_ending|}. + /*tex + + Determine vertical glue shrink setting, then |return| or |goto + common_ending|. + */ if (total_shrink[filll] != 0) o = filll; @@ -1487,24 +1381,25 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) o = sfi; else o = normal; - glue_order(r) = (quarterword) o; glue_sign(r) = shrinking; if (total_shrink[o] != 0) { glue_set(r) = unfloat((double) (-x) / total_shrink[o]); } else { - /* there's nothing to shrink */ + /*tex There's nothing to shrink. */ glue_sign(r) = normal; set_glue_ratio_zero(glue_set(r)); } if ((total_shrink[o] < -x) && (o == normal) && (list_ptr(r) != null)) { int overshoot = -x - total_shrink[normal]; last_badness = 1000000; - /* use the maximum shrinkage */ + /*tex Use the maximum shrinkage */ set_glue_ratio_one(glue_set(r)); - /* - Report an overfull vbox and |goto common_ending|, if this box - is sufficiently bad. + /*tex + + Report an overfull vbox and |goto common_ending|, if this box is + sufficiently bad. + */ if ((overshoot > vfuzz_par) || (vbadness_par < 100)) { int callback_id = callback_defined(vpack_quality_callback); @@ -1521,9 +1416,11 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) } } else if (o == normal) { if (list_ptr(r) != null) { - /* + /*tex + Report a tight vbox and |goto common_ending|, if this box is sufficiently bad. + */ last_badness = badness(-x, total_shrink[normal]); if (last_badness > vbadness_par) { @@ -1544,12 +1441,12 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) } COMMON_ENDING: - /* Finish issuing a diagnostic message or an overfull or underfull vbox */ + /*tex Finish issuing a diagnostic message or an overfull or underfull vbox. */ if (output_active) { tprint(") has occurred while \\output is active"); } else { if (pack_begin_line != 0) { - /* it's actually negative */ + /*tex It's actually negative. */ tprint(") in alignment at lines "); print_int(abs(pack_begin_line)); tprint("--"); @@ -1566,17 +1463,14 @@ halfword vpackage(halfword p, scaled h, int m, scaled l, int pack_direction) return r; } -@ @c halfword filtered_vpackage(halfword p, scaled h, int m, scaled l, int grp, int pack_direction, int just_pack, halfword attr) { halfword q = p; if (!just_pack) - /* if (q != null) */ q = lua_vpack_filter(q, h, m, l, grp, pack_direction, attr); return vpackage(q, h, m, l, pack_direction); } -@ @c void finish_vcenter(void) { halfword p; @@ -1588,12 +1482,11 @@ void finish_vcenter(void) tail_append(p); } -@ @c void package(int c) { halfword saved0, saved2, saved3, saved4; int grp = cur_group; - scaled d = box_max_depth_par; /* max depth */ + scaled d = box_max_depth_par; unsave(); save_ptr -= 5; saved0 = saved_value(0); @@ -1608,11 +1501,13 @@ void package(int c) cur_box = filtered_vpackage(vlink(cur_list.head_field), saved_value(1), saved_level(1), d, grp, saved_level(2), saved4, saved3); if (c == vtop_code) { - /* + /*tex + Read just the height and depth of |cur_box|, for \.{\\vtop}. The height of a `\.{\\vtop}' box is inherited from the first item on its list, if that item is an |hlist_node|, |vlist_node|, or |rule_node|; otherwise the \.{\\vtop} height is zero. + */ scaled h = 0; halfword p = list_ptr(cur_box); @@ -1625,7 +1520,7 @@ void package(int c) } } if (saved2 != null) { - /* DIR: Adjust back |text_dir_ptr| for |scan_spec| */ + /*tex Adjust back |text_dir_ptr| for |scan_spec| */ flush_node_list(text_dir_ptr); text_dir_ptr = saved2; @@ -1635,14 +1530,19 @@ void package(int c) box_end(saved0); } -@ When a box is being appended to the current vertical list, the baselineskip -calculation is handled by the |append_to_vlist| routine. +/*tex + + When a box is being appended to the current vertical list, the baselineskip + calculation is handled by the |append_to_vlist| routine. + +*/ -@c void append_to_vlist(halfword b, int location) { - scaled d; /* deficiency of space between baselines */ - halfword p; /* a new glue node */ + /*tex The deficiency of space between baselines: */ + scaled d; + /*tex A new glue node. */ + halfword p; boolean mirrored = (type(b) == hlist_node) && is_mirrored(box_dir(b)) ; halfword result = null; halfword next_depth = ignore_depth; @@ -1682,188 +1582,235 @@ void append_to_vlist(halfword b, int location) } } -@ When |saving_vdiscards| is positive then the glue, kern, and penalty nodes -removed by the page builder or by \.{\\vsplit} from the top of a vertical list -are saved in special lists instead of being discarded. +/*tex + + When |saving_vdiscards| is positive then the glue, kern, and penalty nodes + removed by the page builder or by \.{\\vsplit} from the top of a vertical + list are saved in special lists instead of being discarded. + +*/ + +/*tex last item removed by page builder */ + +#define tail_page_disc disc_ptr[copy_code] + +/*tex first item removed by page builder */ + +#define page_disc disc_ptr[last_box_code] + +/*tex first item removed by \.{\\vsplit} */ -@c -#define tail_page_disc disc_ptr[copy_code] /* last item removed by page builder */ -#define page_disc disc_ptr[last_box_code] /* first item removed by page builder */ -#define split_disc disc_ptr[vsplit_code] /* first item removed by \.{\\vsplit} */ +#define split_disc disc_ptr[vsplit_code] -halfword disc_ptr[(vsplit_code + 1)]; /* list pointers */ +/*tex List pointers. */ -@ The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation, is -considerably simpler than |line_break| because it doesn't have to worry about -hyphenation, and because its mission is to discover a single break instead of an -optimum sequence of breakpoints. But before we get into the details of |vsplit|, -we need to consider a few more basic things. +halfword disc_ptr[(vsplit_code + 1)]; -A subroutine called |prune_page_top| takes a pointer to a vlist and returns a -pointer to a modified vlist in which all glue, kern, and penalty nodes have been -deleted before the first box or rule node. However, the first box or rule is -actually preceded by a newly created glue node designed so that the topmost -baseline will be at distance |split_top_skip| from the top, whenever this is -possible without backspacing. +/*tex -When the second argument |s| is |false| the deleted nodes are destroyed, -otherwise they are collected in a list starting at |split_disc|. + The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation, is + considerably simpler than |line_break| because it doesn't have to worry about + hyphenation, and because its mission is to discover a single break instead of + an optimum sequence of breakpoints. But before we get into the details of + |vsplit|, we need to consider a few more basic things. + + A subroutine called |prune_page_top| takes a pointer to a vlist and returns a + pointer to a modified vlist in which all glue, kern, and penalty nodes have + been deleted before the first box or rule node. However, the first box or + rule is actually preceded by a newly created glue node designed so that the + topmost baseline will be at distance |split_top_skip| from the top, whenever + this is possible without backspacing. + + When the second argument |s| is |false| the deleted nodes are destroyed, + otherwise they are collected in a list starting at |split_disc|. + +*/ -@c halfword prune_page_top(halfword p, boolean s) { halfword q; - halfword prev_p = temp_head; /* lags one step behind |p| */ + /*tex Lags one step behind |p|. */ + halfword prev_p = temp_head; halfword r = null; vlink(temp_head) = p; while (p != null) { switch (type(p)) { - case hlist_node: - case vlist_node: - case rule_node: - /* Insert glue for |split_top_skip| and set~|p:=null| */ - q = new_skip_param(split_top_skip_code); - vlink(prev_p) = q; - vlink(q) = p; - if (width(q) > height(p)) - width(q) = width(q) - height(p); - else - width(q) = 0; - p = null; - break; - case boundary_node: - case whatsit_node: - case mark_node: - case ins_node: - prev_p = p; - p = vlink(prev_p); - break; - case glue_node: - case kern_node: - case penalty_node: - q = p; - p = vlink(q); - vlink(q) = null; - vlink(prev_p) = p; - if (s) { - if (split_disc == null) - split_disc = q; + case hlist_node: + case vlist_node: + case rule_node: + /*tex Insert glue for |split_top_skip| and set |p:=null|. */ + q = new_skip_param(split_top_skip_code); + vlink(prev_p) = q; + vlink(q) = p; + if (width(q) > height(p)) + width(q) = width(q) - height(p); else - vlink(r) = q; - r = q; - } else { - flush_node_list(q); - } - break; - default: - confusion("pruning"); - break; + width(q) = 0; + p = null; + break; + case boundary_node: + case whatsit_node: + case mark_node: + case ins_node: + prev_p = p; + p = vlink(prev_p); + break; + case glue_node: + case kern_node: + case penalty_node: + q = p; + p = vlink(q); + vlink(q) = null; + vlink(prev_p) = p; + if (s) { + if (split_disc == null) + split_disc = q; + else + vlink(r) = q; + r = q; + } else { + flush_node_list(q); + } + break; + default: + confusion("pruning"); + break; } } return vlink(temp_head); } -@ The next subroutine finds the best place to break a given vertical list so as -to obtain a box of height~|h|, with maximum depth~|d|. A pointer to the beginning -of the vertical list is given, and a pointer to the optimum breakpoint is -returned. The list is effectively followed by a forced break, i.e., a penalty -node with the |eject_penalty|; if the best break occurs at this artificial node, -the value |null| is returned. +/*tex + + The next subroutine finds the best place to break a given vertical list so as + to obtain a box of height~|h|, with maximum depth~|d|. A pointer to the + beginning of the vertical list is given, and a pointer to the optimum + breakpoint is returned. The list is effectively followed by a forced break, + i.e., a penalty node with the |eject_penalty|; if the best break occurs at + this artificial node, the value |null| is returned. -@c -scaled active_height[10]; /* distance from first active node to~|cur_p| */ +*/ + +/*tex The distance from first active node to |cur_p|: */ + +scaled active_height[10]; -@ An array of six |scaled| distances is used to keep track of the height from the -beginning of the list to the current place, just as in |line_break|. In fact, we -use one of the same arrays, only changing its name to reflect its new -significance. +/*tex + + An array of six |scaled| distances is used to keep track of the height from + the beginning of the list to the current place, just as in |line_break|. In + fact, we use one of the same arrays, only changing its name to reflect its + new significance. + +*/ -@c #define do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6);A(7) #define set_height_zero(A) active_height[A]=0 /* initialize the height to zero */ -@ A global variable |best_height_plus_depth| will be set to the natural size of -the box that corresponds to the optimum breakpoint found by |vert_break|. (This -value is used by the insertion-splitting algorithm of the page builder.) +/*tex -@ height of the best box, without stretching or shrinking + A global variable |best_height_plus_depth| will be set to the natural size of + the box that corresponds to the optimum breakpoint found by |vert_break|. + (This value is used by the insertion-splitting algorithm of the page + builder.) -@c -scaled best_height_plus_depth; +*/ -/* finds optimum page break */ +/*tex The height of the best box, without stretching or shrinking: */ + +scaled best_height_plus_depth; halfword vert_break(halfword p, scaled h, scaled d) { - halfword prev_p = p; /* if |p| is a glue node, |type(prev_p)| determines whether |p| is a - legal breakpoint, an initial glue node is not a legal breakpoint */ - int pi = 0; /* penalty value */ - int b; /* badness at a trial breakpoint */ - int t; /* |type| of the node following a kern */ - int least_cost; /* the smallest badness plus penalties found so far */ - halfword best_place = null; /* the most recent break that leads to |least_cost| */ - scaled prev_dp = 0; /* depth of previous box in the list */ + /*tex + If |p| is a glue node, |type(prev_p)| determines whether |p| is a legal + breakpoint, an initial glue node is not a legal breakpoint. + */ + halfword prev_p = p; + /*tex penalty value */ + int pi = 0; + /*tex badness at a trial breakpoint */ + int b; + /*tex |type| of the node following a kern */ + int t; + /*tex the smallest badness plus penalties found so far */ + int least_cost; + /*tex the most recent break that leads to |least_cost| */ + halfword best_place = null; + /*tex depth of previous box in the list */ + scaled prev_dp = 0; least_cost = awful_bad; do_all_six(set_height_zero); while (1) { - /* If node |p| is a legal breakpoint, check if this break is - the best known, and |goto done| if |p| is null or - if the page-so-far is already too full to accept more stuff */ - /* A subtle point to be noted here is that the maximum depth~|d| might be - negative, so |cur_height| and |prev_dp| might need to be corrected even - after a glue or kern node. */ + /*tex + If node |p| is a legal breakpoint, check if this break is the best + known, and |goto done| if |p| is null or if the page-so-far is + already too full to accept more stuff. + + A subtle point to be noted here is that the maximum depth~|d| might + be negative, so |cur_height| and |prev_dp| might need to be corrected + even after a glue or kern node. + */ if (p == null) { pi = eject_penalty; } else { - /* Use node |p| to update the current height and depth measurements; - if this node is not a legal breakpoint, |goto not_found| - or |update_heights|, - otherwise set |pi| to the associated penalty at the break */ + /*tex + + Use node |p| to update the current height and depth measurements; + if this node is not a legal breakpoint, |goto not_found| or + |update_heights|, otherwise set |pi| to the associated penalty at + the break. + + */ switch (type(p)) { - case hlist_node: - case vlist_node: - case rule_node: - cur_height = cur_height + prev_dp + height(p); - prev_dp = depth(p); - goto NOT_FOUND; - break; - case boundary_node: - case whatsit_node: - goto NOT_FOUND; - break; - case glue_node: - if (precedes_break(prev_p)) - pi = 0; - else - goto UPDATE_HEIGHTS; - break; - case kern_node: - if (vlink(p) == null) - t = penalty_node; - else - t = type(vlink(p)); - if (t == glue_node) - pi = 0; - else - goto UPDATE_HEIGHTS; - break; - case penalty_node: - pi = penalty(p); - break; - case mark_node: - case ins_node: - goto NOT_FOUND; - break; - default: - confusion("vertbreak"); - break; + case hlist_node: + case vlist_node: + case rule_node: + cur_height = cur_height + prev_dp + height(p); + prev_dp = depth(p); + goto NOT_FOUND; + break; + case boundary_node: + case whatsit_node: + goto NOT_FOUND; + break; + case glue_node: + if (precedes_break(prev_p)) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case kern_node: + if (vlink(p) == null) + t = penalty_node; + else + t = type(vlink(p)); + if (t == glue_node) + pi = 0; + else + goto UPDATE_HEIGHTS; + break; + case penalty_node: + pi = penalty(p); + break; + case mark_node: + case ins_node: + goto NOT_FOUND; + break; + default: + confusion("vertbreak"); + break; } } - /* Check if node |p| is a new champion breakpoint; then |goto done| - if |p| is a forced break or if the page-so-far is already too full */ + /*tex + + Check if node |p| is a new champion breakpoint; then |goto done| if + |p| is a forced break or if the page-so-far is already too full. + + */ if (pi < inf_penalty) { - /* Compute the badness, |b|, using |awful_bad| if the box is too full */ + /*tex Compute the badness, |b|, using |awful_bad| if the box is too full. */ if (cur_height < h) { if ((active_height[3] != 0) || (active_height[4] != 0) || (active_height[5] != 0) || (active_height[6] != 0)) @@ -1875,7 +1822,6 @@ halfword vert_break(halfword p, scaled h, scaled d) } else { b = badness(cur_height - h, active_height[7]); } - if (b < awful_bad) { if (pi <= eject_penalty) b = pi; @@ -1892,25 +1838,29 @@ halfword vert_break(halfword p, scaled h, scaled d) if ((b == awful_bad) || (pi <= eject_penalty)) goto DONE; } - if ((type(p) < glue_node) || (type(p) > kern_node)) goto NOT_FOUND; UPDATE_HEIGHTS: - /* Update the current height and depth measurements with - respect to a glue or kern node~|p| */ - /* Vertical lists that are subject to the |vert_break| procedure should not - contain infinite shrinkability, since that would permit any amount of - information to ``fit'' on one page. */ + /*tex + + Update the current height and depth measurements with respect to a + glue or kern node~|p|. Vertical lists that are subject to the + |vert_break| procedure should not contain infinite shrinkability, + since that would permit any amount of information to ``fit'' on one + page. + */ if (type(p) != kern_node) { active_height[2 + stretch_order(p)] += stretch(p); active_height[7] += shrink(p); if ((shrink_order(p) != normal) && (shrink(p) != 0)) { print_err("Infinite glue shrinkage found in box being split"); - help4("The box you are \\vsplitting contains some infinitely", - "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.", - "Such glue doesn't belong there; but you can safely proceed,", - "since the offensive shrinkability has been made finite."); + help4( + "The box you are \\vsplitting contains some infinitely", + "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.", + "Such glue doesn't belong there; but you can safely proceed,", + "since the offensive shrinkability has been made finite." + ); error(); shrink_order(p) = normal; } @@ -1929,31 +1879,39 @@ halfword vert_break(halfword p, scaled h, scaled d) return best_place; } -@ Now we are ready to consider |vsplit| itself. Most of its work is accomplished -by the two subroutines that we have just considered. +/*tex + + Now we are ready to consider |vsplit| itself. Most of its work is + accomplished by the two subroutines that we have just considered. -Given the number of a vlist box |n|, and given a desired page height |h|, the -|vsplit| function finds the best initial segment of the vlist and returns a box -for a page of height~|h|. The remainder of the vlist, if any, replaces the -original box, after removing glue and penalties and adjusting for -|split_top_skip|. Mark nodes in the split-off box are used to set the values of -|split_first_mark| and |split_bot_mark|; we use the fact that -|split_first_mark(x)=null| if and only if |split_bot_mark(x)=null|. + Given the number of a vlist box |n|, and given a desired page height |h|, the + |vsplit| function finds the best initial segment of the vlist and returns a + box for a page of height~|h|. The remainder of the vlist, if any, replaces + the original box, after removing glue and penalties and adjusting for + |split_top_skip|. Mark nodes in the split-off box are used to set the values + of |split_first_mark| and |split_bot_mark|; we use the fact that + |split_first_mark(x)=null| if and only if |split_bot_mark(x)=null|. -The original box becomes ``void'' if and only if it has been entirely extracted. -The extracted box is ``void'' if and only if the original box was void (or if it -was, erroneously, an hlist box). + The original box becomes ``void'' if and only if it has been entirely + extracted. The extracted box is ``void'' if and only if the original box was + void (or if it was, erroneously, an hlist box). -@c -/* extracts a page of height |h| from box |n| */ +*/ + +/*tex Extract a page of height |h| from box |n|: */ halfword vsplit(halfword n, scaled h, int m) { - halfword v; /* the box to be split */ - int vdir; /* the direction of the box to be split */ - halfword p; /* runs through the vlist */ - halfword q; /* points to where the break occurs */ - halfword i; /* for traversing marks lists */ + /*tex the box to be split */ + halfword v; + /*tex the direction of the box to be split */ + int vdir; + /*tex runs through the vlist */ + halfword p; + /*tex points to where the break occurs */ + halfword q; + /*tex for traversing marks lists */ + halfword i; v = box(n); vdir = box_dir(v); flush_node_list(split_disc); @@ -1962,23 +1920,27 @@ halfword vsplit(halfword n, scaled h, int m) delete_split_first_mark(i); delete_split_bot_mark(i); } - /* Dispense with trivial cases of void or bad boxes */ + /*tex Dispense with trivial cases of void or bad boxes. */ if (v == null) { return null; } if (type(v) != vlist_node) { print_err("\\vsplit needs a \\vbox"); - help2("The box you are trying to split is an \\hbox.", - "i can't split such a box, so I''ll leave it alone."); + help2( + "The box you are trying to split is an \\hbox.", + "i can't split such a box, so I''ll leave it alone." + ); error(); return null; } q = vert_break(list_ptr(v), h, split_max_depth_par); - /* - Look at all the marks in nodes before the break, and set the final - link to |null| at the break. It's possible that the box begins with - a penalty node that is the ``best'' break, so we must be careful to - handle this special case correctly. + /*tex + + Look at all the marks in nodes before the break, and set the final link + to |null| at the break. It's possible that the box begins with a penalty + node that is the ``best'' break, so we must be careful to handle this + special case correctly. + */ p = list_ptr(v); if (p == q) { @@ -2009,7 +1971,7 @@ halfword vsplit(halfword n, scaled h, int m) list_ptr(v) = null; flush_node(v); if (q == null) { - /* the |eq_level| of the box stays the same */ + /*tex The |eq_level| of the box stays the same. */ box(n) = null; } else { box(n) = filtered_vpackage(q, 0, additional, max_depth_par, split_keep_group, vdir, 0, 0); @@ -2021,17 +1983,23 @@ halfword vsplit(halfword n, scaled h, int m) } } -@ Now that we can see what eventually happens to boxes, we can consider the first -steps in their creation. The |begin_box| routine is called when |box_context| is -a context specification, |cur_chr| specifies the type of box desired, and -|cur_cmd=make_box|. +/*tex + + Now that we can see what eventually happens to boxes, we can consider the + first steps in their creation. The |begin_box| routine is called when + |box_context| is a context specification, |cur_chr| specifies the type of box + desired, and |cur_cmd=make_box|. + +*/ -@c void begin_box(int box_context) { - halfword q; /* run through the current list */ - halfword k; /* 0 or |vmode| or |hmode| */ - int n; /* a box number */ + /*tex run through the current list */ + halfword q; + /*tex 0 or |vmode| or |hmode| */ + halfword k; + /*tex a box number */ + int n; int spec_direction = -1; int just_pack = 0; int split_mode = exactly ; @@ -2039,7 +2007,7 @@ void begin_box(int box_context) case box_code: scan_register_num(); cur_box = box(cur_val); - /* the box becomes void, at the same level */ + /*tex The box becomes void, at the same level. */ box(cur_val) = null; break; case copy_code: @@ -2047,10 +2015,11 @@ void begin_box(int box_context) cur_box = copy_node_list(box(cur_val)); break; case last_box_code: - /* - If the current list ends with a box node, delete it from - the list and make |cur_box| point to it; otherwise set - |cur_box:=null|. + /*tex + + If the current list ends with a box node, delete it from the list + and make |cur_box| point to it; otherwise set |cur_box:=null|. + */ cur_box = null; if (abs(cur_list.mode_field) == mmode) { @@ -2059,17 +2028,15 @@ void begin_box(int box_context) error(); } else if ((cur_list.mode_field == vmode) && (cur_list.head_field == cur_list.tail_field)) { you_cant(); - help2("Sorry...I usually can't take things from the current page.", - "This \\lastbox will therefore be void."); + help2( + "Sorry...I usually can't take things from the current page.", + "This \\lastbox will therefore be void." + ); error(); } else { if (cur_list.head_field != cur_list.tail_field) { - /* todo: new code, needs testing */ - - /* maybe: ((type(cur_list.tail_field) == hlist_node) < rule_node) */ - if ((type(cur_list.tail_field) == hlist_node) || (type(cur_list.tail_field) == vlist_node)) { - /* Remove the last box ... */ + /*tex Remove the last box */ q = alink(cur_list.tail_field); if (q == null || vlink(q) != cur_list.tail_field) { q = cur_list.head_field; @@ -2086,9 +2053,11 @@ void begin_box(int box_context) } break; case vsplit_code: - /* - Split off part of a vertical box, make |cur_box| point to it. Here we - deal with things like `\.{\\vsplit 13 to 100pt}'. + /*tex + + Split off part of a vertical box, make |cur_box| point to it. + Here we deal with things like `\.{\\vsplit 13 to 100pt}'. + */ scan_register_num(); n = cur_val; @@ -2096,18 +2065,22 @@ void begin_box(int box_context) split_mode = additional ; } else if (!scan_keyword("to")) { print_err("Missing `to' inserted"); - help2("I'm working on `\\vsplit<box number> to <dimen>';", - "will look for the <dimen> next."); + help2( + "I'm working on `\\vsplit<box number> to <dimen>';", + "will look for the <dimen> next." + ); error(); } scan_normal_dimen(); cur_box = vsplit(n, cur_val, split_mode); break; default: - /* - Initiate the construction of an hbox or vbox, then |return|. Here is - where we enter restricted horizontal mode or internal vertical mode, - in order to make a box. + /*tex + + Initiate the construction of an hbox or vbox, then |return|. Here + is where we enter restricted horizontal mode or internal vertical + mode, in order to make a box. + */ switch (cur_chr) { case tpack_code: @@ -2123,7 +2096,6 @@ void begin_box(int box_context) just_pack = 1; break; } - /* */ k = cur_chr - vtop_code; set_saved_record(0, saved_boxcontext, 0, box_context); switch (abs(cur_list.mode_field)) { @@ -2165,6 +2137,6 @@ void begin_box(int box_context) return; break; } - /* in simple cases, we use the box immediately */ + /*tex In simple cases, we use the box immediately. */ box_end(box_context); } diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c index dd85cf01b3e..cbc77e556fa 100644 --- a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w +++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c @@ -1,60 +1,65 @@ -% postlinebreak.w -% -% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. +/* -@ @c +postlinebreak.w +Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ So far we have gotten a little way into the |line_break| routine, having -covered its important |try_break| subroutine. Now let's consider the -rest of the process. +/*tex + +So far we have gotten a little way into the |line_break| routine, having covered +its important |try_break| subroutine. Now let's consider the rest of the process. -The main loop of |line_break| traverses the given hlist, -starting at |vlink(temp_head)|, and calls |try_break| at each legal -breakpoint. A variable called |auto_breaking| is set to true except -within math formulas, since glue nodes are not legal breakpoints when -they appear in formulas. +The main loop of |line_break| traverses the given hlist, starting at +|vlink(temp_head)|, and calls |try_break| at each legal breakpoint. A variable +called |auto_breaking| is set to true except within math formulas, since glue +nodes are not legal breakpoints when they appear in formulas. The current node of interest in the hlist is pointed to by |cur_p|. Another -variable, |prev_p|, is usually one step behind |cur_p|, but the real -meaning of |prev_p| is this: If |type(cur_p)=glue_node| then |cur_p| is a legal -breakpoint if and only if |auto_breaking| is true and |prev_p| does not -point to a glue node, penalty node, explicit kern node, or math node. +variable, |prev_p|, is usually one step behind |cur_p|, but the real meaning of +|prev_p| is this: If |type(cur_p)=glue_node| then |cur_p| is a legal breakpoint +if and only if |auto_breaking| is true and |prev_p| does not point to a glue +node, penalty node, explicit kern node, or math node. + +The total number of lines that will be set by |post_line_break| is +|best_line-prev_graf-1|. The last breakpoint is specified by +|break_node(best_bet)|, and this passive node points to the other breakpoints via +the |prev_break| links. The finishing-up phase starts by linking the relevant +passive nodes in forward order, changing |prev_break| to |next_break|. (The +|next_break| fields actually reside in the same memory space as the |prev_break| +fields did, but we give them a new name because of their new significance.) Then +the lines are justified, one by one. -@ The total number of lines that will be set by |post_line_break| -is |best_line-prev_graf-1|. The last breakpoint is specified by -|break_node(best_bet)|, and this passive node points to the other breakpoints -via the |prev_break| links. The finishing-up phase starts by linking the -relevant passive nodes in forward order, changing |prev_break| to -|next_break|. (The |next_break| fields actually reside in the same memory -space as the |prev_break| fields did, but we give them a new name because -of their new significance.) Then the lines are justified, one by one. +The |post_line_break| must also keep an dir stack, so that it can output end +direction instructions at the ends of lines and begin direction instructions at +the beginnings of lines. -The |post_line_break| must also keep an dir stack, so that it can -output end direction instructions at the ends of lines -and begin direction instructions at the beginnings of lines. +*/ -@c -#define next_break prev_break /*new name for |prev_break| after links are reversed */ +/*tex The new name for |prev_break| after links are reversed: */ + +#define next_break prev_break + +/*tex The |int|s are actually halfwords. */ -/* the ints are actually halfwords */ void ext_post_line_break(int paragraph_dir, int right_skip, int left_skip, @@ -78,32 +83,39 @@ void ext_post_line_break(int paragraph_dir, { boolean have_directional = true; - halfword q, r; /* temporary registers for list manipulation */ + /*tex temporary registers for list manipulation */ + halfword q, r; halfword k; scaled w; - boolean glue_break; /* was a break at glue? */ - boolean disc_break; /*was the current break at a discretionary node? */ - boolean post_disc_break; /*and did it have a nonempty post-break part? */ - scaled cur_width; /*width of line number |cur_line| */ - scaled cur_indent; /*left margin of line number |cur_line| */ - int pen; /*use when calculating penalties between lines */ - halfword cur_p; /* |cur_p|, but localized */ - halfword cur_line; /*the current line number being justified */ - + /*tex was a break at glue? */ + boolean glue_break; + /*tex was the current break at a discretionary node? */ + boolean disc_break; + /*tex and did it have a nonempty post-break part? */ + boolean post_disc_break; + /*tex width of line number |cur_line| */ + scaled cur_width; + /*tex left margin of line number |cur_line| */ + scaled cur_indent; + /*tex use when calculating penalties between lines */ + int pen; + /*tex |cur_p|, but localized */ + halfword cur_p; + /*tex the current line number being justified */ + halfword cur_line; + /*tex the current direction: */ dir_ptr = cur_list.dirs_field; - /* Reverse the links of the relevant passive nodes, setting |cur_p| to - the first breakpoint; */ - /* The job of reversing links in a list is conveniently regarded as the job - of taking items off one stack and putting them on another. In this case we - take them off a stack pointed to by |q| and having |prev_break| fields; - we put them on a stack pointed to by |cur_p| and having |next_break| fields. - Node |r| is the passive node being moved from stack to stack. - */ + /*tex + Reverse the links of the relevant passive nodes, setting |cur_p| to the + first breakpoint. The job of reversing links in a list is conveniently + regarded as the job of taking items off one stack and putting them on + another. In this case we take them off a stack pointed to by |q| and + having |prev_break| fields; we put them on a stack pointed to by |cur_p| + and having |next_break| fields. Node |r| is the passive node being moved + from stack to stack. + */ q = break_node(best_bet); -#if 0 - used_discs = used_disc(best_bet); -#endif - /* |has_direction| */ + /*tex |cur_p| will become the first breakpoint; */ cur_p = null; do { r = q; @@ -111,73 +123,68 @@ void ext_post_line_break(int paragraph_dir, next_break(r) = cur_p; cur_p = r; } while (q != null); - /* |cur_p| is now the first breakpoint; */ - - cur_line = cur_list.pg_field + 1; /* prevgraf+1 */ - + /*tex prevgraf + 1 */ + cur_line = cur_list.pg_field + 1; do { - /* Justify the line ending at breakpoint |cur_p|, and append it to the - current vertical list, together with associated penalties and other - insertions; */ - /* The current line to be justified appears in a horizontal list starting - at |vlink(temp_head)| and ending at |cur_break(cur_p)|. If |cur_break(cur_p)| is - a glue node, we reset the glue to equal the |right_skip| glue; otherwise - we append the |right_skip| glue at the right. If |cur_break(cur_p)| is a - discretionary node, we modify the list so that the discretionary break - is compulsory, and we set |disc_break| to |true|. We also append - the |left_skip| glue at the left of the line, unless it is zero. */ - -#if 0 - tprint("BEGIN OF LINE "); - print_int(cur_break(cur_p)); - breadth_max = 100000; - depth_threshold = 100000; - show_node_list(temp_head); -#endif - - /* DIR: Insert dir nodes at the beginning of the current line; */ - for (q = dir_ptr; q != null; q = vlink(q)) { - halfword tmp = new_dir(dir_dir(q)); - halfword nxt = vlink(temp_head); - delete_attribute_ref(node_attr(tmp)); - node_attr(tmp) = node_attr(temp_head); - add_node_attr_ref(node_attr(tmp)); - couple_nodes(temp_head, tmp); - try_couple_nodes(tmp, nxt); /* \.{\\break}\.{\\par} */ - } + /*tex + Justify the line ending at breakpoint |cur_p|, and append it to the + current vertical list, together with associated penalties and other + insertions. + + The current line to be justified appears in a horizontal list + starting at |vlink(temp_head)| and ending at |cur_break(cur_p)|. If + |cur_break(cur_p)| is a glue node, we reset the glue to equal the + |right_skip| glue; otherwise we append the |right_skip| glue at the + right. If |cur_break(cur_p)| is a discretionary node, we modify the + list so that the discretionary break is compulsory, and we set + |disc_break| to |true|. We also append the |left_skip| glue at the + left of the line, unless it is zero. + */ if (dir_ptr != null) { + /*tex Insert dir nodes at the beginning of the current line. */ + for (q = dir_ptr; q != null; q = vlink(q)) { + halfword tmp = new_dir(dir_dir(q)); + halfword nxt = vlink(temp_head); + delete_attribute_ref(node_attr(tmp)); + node_attr(tmp) = node_attr(temp_head); + add_node_attr_ref(node_attr(tmp)); + couple_nodes(temp_head, tmp); + /*tex \.{\\break}\.{\\par} */ + try_couple_nodes(tmp, nxt); + } flush_node_list(dir_ptr); dir_ptr = null; } + /*tex + Modify the end of the line to reflect the nature of the break and to + include \.{\\rightskip}; also set the proper value of |disc_break|. + At the end of the following code, |q| will point to the final node on + the list about to be justified. In the meanwhile |r| will point to + the node we will use to insert end-of-line stuff after. |q==null| + means we use the final position of |r|. + */ - /* Modify the end of the line to reflect the nature of the break and to - include \.{\\rightskip}; also set the proper value of |disc_break|; */ - /* At the end of the following code, |q| will point to the final node on the - list about to be justified. In the meanwhile |r| will point to the - node we will use to insert end-of-line stuff after. |q==null| means - we use the final position of |r| */ - - /* begin mathskip code */ if (temp_head != null) { - q = temp_head; - while(q != null) { - if (type(q) == math_node) { - surround(q) = 0 ; - reset_glue_to_zero(q); - break; - } else if ((type(q) == hlist_node) && (subtype(q) == indent_list)) { - /* go on */ - } else if (is_char_node(q)) { - break; - } else if (non_discardable(q)) { - break; - } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) { - break; - } - q = vlink(q); + /*tex begin mathskip code */ + q = temp_head; + while(q != null) { + if (type(q) == math_node) { + surround(q) = 0 ; + reset_glue_to_zero(q); + break; + } else if ((type(q) == hlist_node) && (subtype(q) == indent_list)) { + /* go on */ + } else if (is_char_node(q)) { + break; + } else if (non_discardable(q)) { + break; + } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) { + break; } + q = vlink(q); } - /* end mathskip code */ + /*tex end mathskip code */ + } r = cur_break(cur_p); q = null; @@ -189,27 +196,26 @@ void ext_post_line_break(int paragraph_dir, if (r == null) { for (r = temp_head; vlink(r) != null; r = vlink(r)); if (r == final_par_glue) { - /* This should almost always be true... */ - /* TODO assert ? */ + /*tex This should almost always be true... */ q = r; - /* |q| refers to the last node of the line (and paragraph) */ + /*tex |q| refers to the last node of the line (and paragraph) */ r = alink(r); } - /* |r| refers to the node after which the dir nodes should be closed */ + /*tex |r| refers to the node after which the dir nodes should be closed */ } else if (type(r) == math_node) { surround(r) = 0; - /* begin mathskip code */ + /*tex begin mathskip code */ reset_glue_to_zero(r); - /* end mathskip code */ + /*tex end mathskip code */ } else if (type(r) == glue_node) { copy_glue_values(r,right_skip); subtype(r) = right_skip_code + 1; glue_break = true; - /* |q| refers to the last node of the line */ + /*tex |q| refers to the last node of the line */ q = r; r = alink(r); assert(vlink(r) == q); - /* |r| refers to the node after which the dir nodes should be closed */ + /*tex |r| refers to the node after which the dir nodes should be closed */ } else if (type(r) == disc_node) { halfword a = alink(r); halfword v = vlink(r); @@ -228,7 +234,6 @@ void ext_post_line_break(int paragraph_dir, vlink_no_break(r) = null; tlink_no_break(r) = null; } - assert(type(a) == disc_node && subtype(a) == init_disc); flush_node_list(vlink_no_break(a)); vlink_no_break(a) = null; @@ -239,11 +244,10 @@ void ext_post_line_break(int paragraph_dir, flush_node_list(vlink_post_break(a)); vlink_post_break(a) = null; tlink_post_break(a) = null; - break; case init_disc: assert(type(v) == disc_node && subtype(v) == select_disc); - subtype(v) = syllable_disc; /* not special any more */ + subtype(v) = syllable_disc; flush_node_list(vlink_no_break(v)); vlink_no_break(v) = vlink_post_break(r); tlink_no_break(v) = tlink_post_break(r); @@ -273,27 +277,24 @@ void ext_post_line_break(int paragraph_dir, } else if (type(r) == kern_node) { width(r) = 0; } - - /* DIR: Adjust the dir stack based on dir nodes in this line; */ - /* TODO what about the previousparagraph ??? */ + /*tex Adjust the dir stack based on dir nodes in this line. */ if (have_directional) { - halfword e; - halfword p; + halfword e, p; for (e = vlink(temp_head); e != null && e != cur_break(cur_p); e = vlink(e)) { if (type(e) == dir_node) { - if (dir_dir(e) >= 0) { + if (subtype(e) == normal_dir) { dir_ptr = do_push_dir_node(dir_ptr, e); - } else if (dir_ptr != null && dir_dir(dir_ptr) == (dir_dir(e) + dir_swap)) { + } else if (dir_ptr != null && dir_dir(dir_ptr) == dir_dir(e)) { dir_ptr = do_pop_dir_node(dir_ptr); } } } assert(e == cur_break(cur_p)); - - /* DIR: Insert dir nodes at the end of the current line; */ + /*tex Insert dir nodes at the end of the current line. */ e = vlink(r); for (p = dir_ptr; p != null; p = vlink(p)) { - halfword s = new_dir(dir_dir(p) - dir_swap); + halfword s = new_dir(dir_dir(p)); + subtype(s) = cancel_dir; delete_attribute_ref(node_attr(s)); node_attr(s) = node_attr(r); add_node_attr_ref(node_attr(s)); @@ -303,7 +304,6 @@ void ext_post_line_break(int paragraph_dir, } } if (passive_right_box(cur_p) != null) { - /* TODO: CHECK has |s| below a |alink| ? */ halfword s = copy_node_list(passive_right_box(cur_p)); halfword e = vlink(r); couple_nodes(r, s); @@ -313,28 +313,30 @@ void ext_post_line_break(int paragraph_dir, if (q == null) { q = r; } - /* Now [q] refers to the last node on the line */ - - /* FIXME from this point on we no longer keep alink() valid */ - - /* at this point |q| is the rightmost breakpoint; the only exception is - the case of a discretionary break with non-empty |pre_break|, then |q| - has been changed to the last node of the |pre_break| list */ - /* If the par ends with a \break command, the last line is utterly empty. - That is the case of |q==temp_head| */ + /*tex + Now [q] refers to the last node on the line and therefore the + rightmost breakpoint. The only exception is the case of a + discretionary break with non-empty |pre_break|, then |q| has been + changed to the last node of the |pre_break| list. If the par ends + with a \break command, the last line is utterly empty. That is the + case of |q==temp_head|. + */ if (q != temp_head && protrude_chars > 0) { halfword p, ptmp; if (disc_break && (is_char_node(q) || (type(q) != disc_node))) { - p = q; /* |q| has been reset to the last node of |pre_break| */ + /*tex |q| is reset to the last node of |pre_break| */ + p = q; ptmp = p; } else { - p = alink(q); /* get |vlink(p) = q| */ + /*tex get |vlink(p) = q| */ + p = alink(q); assert(vlink(p) == q); ptmp = p; } p = find_protchar_right(vlink(temp_head), p); w = char_pw(p, right_side); - if (w != 0) { /* we have found a marginal kern, append it after |ptmp| */ + if (w != 0) { + /*tex we have found a marginal kern, append it after |ptmp| */ k = new_margin_kern(-w, last_rightmost_char, right_side); delete_attribute_ref(node_attr(k)); node_attr(k) = node_attr(p); @@ -345,10 +347,12 @@ void ext_post_line_break(int paragraph_dir, q = vlink(q); } } - /* if |q| was not a breakpoint at glue and has been reset to |rightskip| - then we append |rightskip| after |q| now */ + /*tex + If |q| was not a breakpoint at glue and has been reset to |rightskip| + then we append |rightskip| after |q| now. + */ if (!glue_break) { - /* Put the \.{\\rightskip} glue after node |q|; */ + /*tex Put the \.{\\rightskip} glue after node |q|. */ halfword r1 = new_glue((right_skip == null ? zero_glue : right_skip)); subtype(r1) = right_skip_code+1; try_couple_nodes(r1,vlink(q)); @@ -358,21 +362,22 @@ void ext_post_line_break(int paragraph_dir, couple_nodes(q,r1); q = r1; } - - /* /Modify the end of the line to reflect the nature of the break and to - include \.{\\rightskip}; also set the proper value of |disc_break|; */ - - /* Put the \.{\\leftskip} glue at the left and detach this line; */ - /* The following code begins with |q| at the end of the list to be - justified. It ends with |q| at the beginning of that list, and with - |vlink(temp_head)| pointing to the remainder of the paragraph, if any. */ + /*tex + Modify the end of the line to reflect the nature of the break and to + include \.{\\rightskip}; also set the proper value of |disc_break|; + Also put the \.{\\leftskip} glue at the left and detach this line. + + The following code begins with |q| at the end of the list to be + justified. It ends with |q| at the beginning of that list, and with + |vlink(temp_head)| pointing to the remainder of the paragraph, if + any. + */ r = vlink(q); vlink(q) = null; q = vlink(temp_head); try_couple_nodes(temp_head, r); if (passive_left_box(cur_p) != null && passive_left_box(cur_p) != 0) { - /* omega bits: */ halfword s; r = copy_node_list(passive_left_box(cur_p)); s = vlink(q); @@ -388,11 +393,14 @@ void ext_post_line_break(int paragraph_dir, } } } - /*at this point |q| is the leftmost node; all discardable nodes have been discarded */ + /*tex + At this point |q| is the leftmost node; all discardable nodes have + been discarded + */ if (protrude_chars > 0) { halfword p; p = q; - p = find_protchar_left(p, false); /* no more discardables */ + p = find_protchar_left(p, false); w = char_pw(p, left_side); if (w != 0) { k = new_margin_kern(-w, last_leftmost_char, left_side); @@ -412,13 +420,14 @@ void ext_post_line_break(int paragraph_dir, couple_nodes(r,q); q = r; } - /* /Put the \.{\\leftskip} glue at the left and detach this line; */ - - /* Call the packaging subroutine, setting |just_box| to the justified box; */ - /* Now |q| points to the hlist that represents the current line of the - paragraph. We need to compute the appropriate line width, pack the - line into a box of this size, and shift the box by the appropriate - amount of indentation. */ + /*tex + Put the \.{\\leftskip} glue at the left and detach this line. Call + the packaging subroutine, setting |just_box| to the justified box. + Now |q| points to the hlist that represents the current line of the + paragraph. We need to compute the appropriate line width, pack the + line into a box of this size, and shift the box by the appropriate + amount of indentation. + */ if (cur_line > last_special_line) { cur_width = second_width; cur_indent = second_indent; @@ -438,8 +447,10 @@ void ext_post_line_break(int paragraph_dir, } shift_amount(just_box) = cur_indent; subtype(just_box) = line_list; - /* /Call the packaging subroutine, setting |just_box| to the justified box; */ - + /*tex + Call the packaging subroutine, setting |just_box| to the justified + box. + */ if ((vlink(contrib_head) != null)) checked_break_filter(pre_box); if (pre_adjust_head != pre_adjust_tail) { @@ -454,17 +465,17 @@ void ext_post_line_break(int paragraph_dir, checked_break_filter(adjust); } adjust_tail = null; - - /* /Append the new box to the current vertical list, followed by the list of - special nodes taken out of the box by the packager; */ - - /* Append a penalty node, if a nonzero penalty is appropriate */ - /* Penalties between the lines of a paragraph come from club and widow lines, - from the |inter_line_penalty| parameter, and from lines that end at - discretionary breaks. Breaking between lines of a two-line paragraph gets - both club-line and widow-line penalties. The local variable |pen| will - be set to the sum of all relevant penalties for the current line, except - that the final line is never penalized. */ + /*tex + Append the new box to the current vertical list, followed by the list + of special nodes taken out of the box by the packager. Append a + penalty node, if a nonzero penalty is appropriate. Penalties between + the lines of a paragraph come from club and widow lines, from the + |inter_line_penalty| parameter, and from lines that end at + discretionary breaks. Breaking between lines of a two-line paragraph + gets both club-line and widow-line penalties. The local variable + |pen| will be set to the sum of all relevant penalties for the + current line, except that the final line is never penalized. + */ if (cur_line + 1 != best_line) { q = inter_line_penalties_ptr; if (q != null) { @@ -481,11 +492,13 @@ void ext_post_line_break(int paragraph_dir, } q = club_penalties_ptr; if (q != null) { - r = cur_line - cur_list.pg_field; /* prevgraf */ + /*tex prevgraf */ + r = cur_line - cur_list.pg_field; if (r > penalty(q)) r = penalty(q); pen += penalty(q + r); - } else if (cur_line == cur_list.pg_field + 1) { /* prevgraf */ + } else if (cur_line == cur_list.pg_field + 1) { + /*tex prevgraf */ pen += club_penalty; } q = widow_penalties_ptr; @@ -510,34 +523,37 @@ void ext_post_line_break(int paragraph_dir, cur_list.tail_field = r; } } - /* /Append a penalty node, if a nonzero penalty is appropriate */ - - /* /Justify the line ending at breakpoint |cur_p|, and append it to the - current vertical list, together with associated penalties and other - insertions; */ + /*tex + Append a penalty node, if a nonzero penalty is appropriate. Justify + the line ending at breakpoint |cur_p|, and append it to the current + vertical list, together with associated penalties and other + insertions. + */ incr(cur_line); cur_p = next_break(cur_p); if (cur_p != null && !post_disc_break) { - /* Prune unwanted nodes at the beginning of the next line; */ - /* Glue and penalty and kern and math nodes are deleted at the - beginning of a line, except in the anomalous case that the - node to be deleted is actually one of the chosen - breakpoints. Otherwise the pruning done here is designed to - match the lookahead computation in |try_break|, where the - |break_width| values are computed for non-discretionary - breakpoints. */ + /*tex + Prune unwanted nodes at the beginning of the next line. Glue and + penalty and kern and math nodes are deleted at the beginning of a + line, except in the anomalous case that the node to be deleted is + actually one of the chosen breakpoints. Otherwise the pruning + done here is designed to match the lookahead computation in + |try_break|, where the |break_width| values are computed for + non-discretionary breakpoints. + */ r = temp_head; - /* - Normally we have a matching math open and math close node but when we cross a line - the open node is removed, including any glue or penalties following it. This is however - not that nice for callbacks that rely on symmetry. Of course this only counts for one - liners, as we can still have only a begin or end node on a line. The end_of_math lua - helper is made robust against this although there you should be aware of the fact that - one can end up in the middle of math in callbacks that don't work on whole paragraphs, - but at least this branch makes sure that some proper analysis is possible. (todo: check - if math glyphs have the subtype marked done). - - Todo: turn math nodes into glues when mathskip otherwise remove them. + /*tex + Normally we have a matching math open and math close node but + when we cross a line the open node is removed, including any glue + or penalties following it. This is however not that nice for + callbacks that rely on symmetry. Of course this only counts for + one liners, as we can still have only a begin or end node on a + line. The end_of_math lua helper is made robust against this + although there you should be aware of the fact that one can end + up in the middle of math in callbacks that don't work on whole + paragraphs, but at least this branch makes sure that some proper + analysis is possible. (todo: check if math glyphs have the + subtype marked done). */ while (1) { q = vlink(r); @@ -550,17 +566,17 @@ void ext_post_line_break(int paragraph_dir, } */ if (type(q) == math_node) { - /* begin mathskip code */ + /*tex begin mathskip code */ surround(q) = 0 ; reset_glue_to_zero(q); - /* end mathskip code */ + /*tex end mathskip code */ } if (q == cur_break(cur_p)) { break; } else if (is_char_node(q)) { break; } else if (type(q) == local_par_node) { - /* weird, in the middle somewhere */ + /*tex weird, in the middle somewhere */ } else if (non_discardable(q)) { break; } else if (type(q) == kern_node && subtype(q) != explicit_kern && subtype(q) != italic_kern) { @@ -577,7 +593,9 @@ void ext_post_line_break(int paragraph_dir, } while (cur_p != null); if ((cur_line != best_line) || (vlink(temp_head) != null)) confusion("line breaking"); - cur_list.pg_field = best_line - 1; /* prevgraf */ - cur_list.dirs_field = dir_ptr; /* |dir_save| */ + /*tex prevgraf */ + cur_list.pg_field = best_line - 1; + /*tex |dir_save| */ + cur_list.dirs_field = dir_ptr; dir_ptr = null; } diff --git a/Build/source/texk/web2c/luatexdir/tex/primitive.c b/Build/source/texk/web2c/luatexdir/tex/primitive.c new file mode 100644 index 00000000000..f16d69a333c --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/primitive.c @@ -0,0 +1,785 @@ +/* + +primitive.w + +Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +/*tex + +Control sequences are stored and retrieved by means of a fairly standard hash +table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C in +{\sl The Art of Computer Programming\/}). Once a control sequence enters the +table, it is never removed, because there are complicated situations involving +\.{\\gdef} where the removal of a control sequence at the end of a group would be +a mistake preventable only by the introduction of a complicated reference-count +mechanism. + +The actual sequence of letters forming a control sequence identifier is stored in +the |str_pool| array together with all the other strings. An auxiliary array +|hash| consists of items with two halfword fields per word. The first of these, +called |next(p)|, points to the next identifier belonging to the same coalesced +list as the identifier corresponding to~|p|; and the other, called |text(p)|, +points to the |str_start| entry for |p|'s identifier. If position~|p| of the hash +table is empty, we have |text(p)=0|; if position |p| is either empty or the end +of a coalesced hash list, we have |next(p)=0|. An auxiliary pointer variable +called |hash_used| is maintained in such a way that all locations |p>=hash_used| +are nonempty. The global variable |cs_count| tells how many multiletter control +sequences have been defined, if statistics are being kept. + +A global boolean variable called |no_new_control_sequence| is set to |true| +during the time that new hash table entries are forbidden. + +*/ + +/*tex The hash table: */ + +two_halves *hash; + +/*tex Allocation pointer for |hash|: */ + +halfword hash_used; + +/*tex |hash_extra=hash| above |eqtb_size|: */ + +int hash_extra; + +/*tex Maximum of the hash array: */ + +halfword hash_top; + +/*tex Pointer to next high hash location: */ + +halfword hash_high; + +/*tex Are new identifiers legal? */ + +boolean no_new_control_sequence; + +/*tex Total number of known identifiers: */ + +int cs_count; + +/*tex Test if all positions are occupied: */ + +#define hash_is_full (hash_used==hash_base) + +/*tex + + \.{\\primitive} support needs a few extra variables and definitions, + like: + +*/ + +#define prim_base 1 + +/*tex + +The arrays |prim| and |prim_eqtb| are used for name -> cmd,chr lookups. The are +modelled after |hash| and |eqtb|, except that primitives do not have an +|eq_level|, that field is replaced by |origin|. + +*/ + +/*tex Link for coalesced lists: */ + +#define prim_next(a) prim[(a)].lhfield + +/*tex String number for control sequence name: */ + +#define prim_text(a) prim[(a)].rh + +/*tex Test if all positions are occupied: */ + +#define prim_is_full (prim_used==prim_base) + +#define prim_origin_field(a) (a).hh.b1 + +#define prim_eq_type_field(a) (a).hh.b0 + +#define prim_equiv_field(a) (a).hh.rh + +/*tex Level of definition: */ + +#define prim_origin(a) prim_origin_field(prim_eqtb[(a)]) + +/*tex Command code for equivalent: */ + +#define prim_eq_type(a) prim_eq_type_field(prim_eqtb[(a)]) + +/*tex Equivalent value: */ + +#define prim_equiv(a) prim_equiv_field(prim_eqtb[(a)]) + +/*tex Allocation pointer for |prim|: */ + +static pointer prim_used; + +/*tex The primitives table: */ + +static two_halves prim[(prim_size + 1)]; + +static memory_word prim_eqtb[(prim_size + 1)]; + +/*tex + +The array |prim_data| works the other way around, it is used for cmd,chr -> name +lookups. + +*/ + +typedef struct prim_info { + /*tex Number of name entries: */ + halfword subids; + /*tex Offset to be used for |chr_code|s: */ + halfword offset; + /*tex Array of names: */ + str_number *names; +} prim_info; + +static prim_info prim_data[(last_cmd + 1)]; + +/*tex + +Initialize the memory arrays: + +*/ + +void init_primitives(void) +{ + int k; + memset(prim_data, 0, (sizeof(prim_info) * (last_cmd + 1))); + memset(prim, 0, (sizeof(two_halves) * (prim_size + 1))); + memset(prim_eqtb, 0, (sizeof(memory_word) * (prim_size + 1))); + for (k = 0; k <= prim_size; k++) { + prim_eq_type(k) = undefined_cs_cmd; + } +} + +/*tex Nothing is used (yet). */ + +void ini_init_primitives(void) +{ + prim_used = prim_size; +} + + +/*tex + +The value of |hash_prime| should be roughly 85\%! of |hash_size|, and it should +be a prime number. The theory of hashing tells us to expect fewer than two table +probes, on the average, when the search is successful. [See J.~S. Vitter, {\sl +Journal of the ACM\/ \bf30} (1983), 231--258.] @^Vitter, Jeffrey Scott@> + +*/ + +static halfword compute_hash(const char *j, unsigned int l, halfword prime_number) +{ + int k; + halfword h = (unsigned char) *j; + for (k = 1; k <= (int)(l - 1); k++) { + h = h + h + (unsigned char) *(j + k); + while (h >= prime_number) { + h = h - prime_number; + } + } + return h; +} + +/*tex + +Here is the subroutine that searches the primitive table for an identifier. + +*/ + +pointer prim_lookup(str_number s) +{ + /*tex The hash code: */ + int h; + /*tex The index in the |hash| array: */ + pointer p; + unsigned char *j; + unsigned l; + if (s < STRING_OFFSET) { + p = s; + if ((p < 0) || (get_prim_eq_type(p) == undefined_cs_cmd)) { + p = undefined_primitive; + } + } else { + j = str_string(s); + l = (unsigned) str_length(s); + h = compute_hash((char *) j, l, prim_prime); + /*tex We start searching here; note that |0<=h<hash_prime|. */ + p = h + prim_base; + while (1) { + if (prim_text(p) > 0) + if (str_length(prim_text(p)) == l) + if (str_eq_str(prim_text(p), s)) + goto FOUND; + if (prim_next(p) == 0) { + if (no_new_control_sequence) { + p = undefined_primitive; + } else { + /*tex Insert a new primitive after |p|, then make |p| point to it. */ + if (prim_text(p) > 0) { + do { + /*tex Search for an empty location in |prim| */ + if (prim_is_full) { + overflow("primitive size", prim_size); + } + decr(prim_used); + } while (prim_text(prim_used) != 0); + prim_next(p) = prim_used; + p = prim_used; + } + prim_text(p) = s; + } + goto FOUND; + } + p = prim_next(p); + } + } + FOUND: + return p; +} + +/*tex + +How to test a csname for primitive-ness? + +*/ + +boolean is_primitive(str_number csname) +{ + int n, m; + char *ss; + m = prim_lookup(csname); + ss = makecstring(csname); + n = string_lookup(ss, str_length(csname)); + free(ss); + return ((n != undefined_cs_cmd) && (m != undefined_primitive) && + (eq_type(n) == prim_eq_type(m)) && (equiv(n) == prim_equiv(m))); +} + + +/*tex + +A few simple accessors. + +*/ + +quarterword get_prim_eq_type(int p) +{ + return prim_eq_type(p); +} + +quarterword get_prim_origin(int p) +{ + return prim_origin(p); +} + +halfword get_prim_equiv(int p) +{ + return prim_equiv(p); +} + +str_number get_prim_text(int p) +{ + return prim_text(p); +} + + +/*tex + +Dumping and undumping. + +*/ + +void dump_primitives(void) +{ + int p, q; + for (p = 0; p <= prim_size; p++) { + dump_hh(prim[p]); + } + for (p = 0; p <= prim_size; p++) { + dump_wd(prim_eqtb[p]); + } + for (p = 0; p <= last_cmd; p++) { + dump_int(prim_data[p].offset); + dump_int(prim_data[p].subids); + for (q = 0; q < prim_data[p].subids; q++) { + dump_int(prim_data[p].names[q]); + } + } +} + +void undump_primitives(void) +{ + int p, q; + for (p = 0; p <= prim_size; p++) { + undump_hh(prim[p]); + } + for (p = 0; p <= prim_size; p++) { + undump_wd(prim_eqtb[p]); + } + for (p = 0; p <= last_cmd; p++) { + undump_int(prim_data[p].offset); + undump_int(prim_data[p].subids); + if (prim_data[p].subids > 0) { + prim_data[p].names = (str_number *) xmalloc((unsigned) ((unsigned) prim_data[p].subids * sizeof(str_number *))); + for (q = 0; q < prim_data[p].subids; q++) { + undump_int(prim_data[p].names[q]); + } + } + } +} + +/*tex + +We need to put \TeX's ``primitive'' control sequences into the hash table, +together with their command code (which will be the |eq_type|) and an operand +(which will be the |equiv|). The |primitive| procedure does this, in a way that +no \TeX\ user can. The global value |cur_val| contains the new |eqtb| pointer +after |primitive| has acted. + +Because the definitions of the actual user-accessible name of a primitive can be +postponed until runtime, the function |primitive_def| is needed that does nothing +except creating the control sequence name. + +*/ + +void primitive_def(const char *s, size_t l, quarterword c, halfword o) +{ + int nncs = no_new_control_sequence; + no_new_control_sequence = false; + /*tex This creates the |text()| string: */ + cur_val = string_lookup(s, l); + no_new_control_sequence = nncs; + eq_level(cur_val) = level_one; + eq_type(cur_val) = c; + equiv(cur_val) = o; +} + +/*tex + +The function |store_primitive_name| sets up the bookkeeping for the reverse +lookup. It is quite paranoid, because it is easy to mess this up accidentally. + +The |offset| is needed because sometimes character codes (in |o|) are indices +into |eqtb| or are offset by a magical value to make sure they do not conflict +with something else. We don't want the |prim_data[c].names| to have too many +entries as it will just be wasted room, so |offset| is substracted from |o| +because creating or accessing the array. The |assert(idx<=0xFFFF)| is not +strictly needed, but it helps catch errors of this kind. + +*/ + +static void store_primitive_name(str_number s, quarterword c, halfword o, halfword offset) +{ + int idx; + /* + if (prim_data[c].offset != 0 && prim_data[c].offset != offset) { + assert(false); + } + */ + prim_data[c].offset = offset; + idx = ((int) o - offset); + /* + assert(idx >= 0); + assert(idx <= 0xFFFF); + */ + if (prim_data[c].subids < (idx + 1)) { + str_number *new = (str_number *) xcalloc((unsigned) (idx + 1), sizeof(str_number *)); + if (prim_data[c].names != NULL) { + /* + assert(prim_data[c].subids); + */ + memcpy(new, (prim_data[c].names), (unsigned) (prim_data[c].subids) * sizeof(str_number)); + free(prim_data[c].names); + } + prim_data[c].names = new; + prim_data[c].subids = idx + 1; + } + prim_data[c].names[idx] = s; +} + +/*tex + +Compared to tex82, |primitive| has two extra parameters. The |off| is an offset +that will be passed on to |store_primitive_name|, the |cmd_origin| is the bit +that is used to group primitives by originator. + +*/ + +void primitive(const char *thes, quarterword c, halfword o, halfword off, int cmd_origin) +{ + /*tex Needed to fill |prim_eqtb|: */ + int prim_val; + str_number ss; + ss = maketexstring(thes); + if (cmd_origin == tex_command || cmd_origin == core_command) { + primitive_def(thes, strlen(thes), c, o); + } + prim_val = prim_lookup(ss); + prim_origin(prim_val) = (quarterword) cmd_origin; + prim_eq_type(prim_val) = c; + prim_equiv(prim_val) = o; + store_primitive_name(ss, c, o, off); +} + +/*tex + +Here is a helper that does the actual hash insertion. This code far from ideal: +the existance of |hash_extra| changes all the potential (short) coalesced lists +into a single (long) one. This will create a slowdown. + +*/ + +static halfword insert_id(halfword p, const unsigned char *j, unsigned int l) +{ + unsigned saved_cur_length; + unsigned saved_cur_string_size; + unsigned char *saved_cur_string; + const unsigned char *k; + if (cs_text(p) > 0) { + if (hash_high < hash_extra) { + incr(hash_high); + /*tex + Can't we use |eqtb_top| here (perhaps because that is not + finalized yet when called from |primitive|? + */ + cs_next(p) = hash_high + eqtb_size; + p = cs_next(p); + } else { + /*tex + Search for an empty location in |hash|. + */ + do { + if (hash_is_full) + overflow("hash size", (unsigned) (hash_size + hash_extra)); + decr(hash_used); + } while (cs_text(hash_used) != 0); + cs_next(p) = hash_used; + p = hash_used; + } + } + saved_cur_length = cur_length; + saved_cur_string = cur_string; + saved_cur_string_size = cur_string_size; + reset_cur_string(); + for (k = j; k <= j + l - 1; k++) { + append_char(*k); + } + cs_text(p) = make_string(); + cur_length = saved_cur_length; + xfree(cur_string); + cur_string = saved_cur_string; + cur_string_size = saved_cur_string_size; + incr(cs_count); + return p; +} + + +/*tex + +Here is the subroutine that searches the hash table for an identifier that +matches a given string of length |l>1| appearing in |buffer[j.. (j+l-1)]|. If the +identifier is found, the corresponding hash table address is returned. Otherwise, +if the global variable |no_new_control_sequence| is |true|, the dummy address +|undefined_control_sequence| is returned. Otherwise the identifier is inserted +into the hash table and its location is returned. + +*/ + +pointer id_lookup(int j, int l) +{ + /*tex The hash code: */ + int h; + /*tex The index in |hash| array: */ + pointer p; + h = compute_hash((char *) (buffer + j), (unsigned) l, hash_prime); + /*tex We start searching here. Note that |0<=h<hash_prime|: */ + p = h + hash_base; + while (1) { + if (cs_text(p) > 0) + if (str_length(cs_text(p)) == (unsigned) l) + if (str_eq_buf(cs_text(p), j)) + goto FOUND; + if (cs_next(p) == 0) { + if (no_new_control_sequence) { + p = undefined_control_sequence; + } else { + p = insert_id(p, (buffer + j), (unsigned) l); + } + goto FOUND; + } + p = cs_next(p); + } + FOUND: + return p; +} + +/*tex + +Here is a similar subroutine for finding a primitive in the hash. +This one is based on a C string. + +*/ + +pointer string_lookup(const char *s, size_t l) +{ + /*tex The hash code: */ + int h; + /*tex The index in |hash| array: */ + pointer p; + h = compute_hash(s, (unsigned) l, hash_prime); + /*tex We start searching here. Note that |0<=h<hash_prime|: */ + p = h + hash_base; + while (1) { + if (cs_text(p) > 0) + if (str_eq_cstr(cs_text(p), s, l)) + goto FOUND; + if (cs_next(p) == 0) { + if (no_new_control_sequence) { + p = undefined_control_sequence; + } else { + p = insert_id(p, (const unsigned char *) s, (unsigned) l); + } + goto FOUND; + } + p = cs_next(p); + } + FOUND: + return p; +} + +/*tex + +The |print_cmd_chr| routine prints a symbolic interpretation of a command code +and its modifier. This is used in certain `\.{You can\'t}' error messages, and in +the implementation of diagnostic routines like \.{\\show}. + +The body of |print_cmd_chr| use to be a rather tedious listing of print commands, +and most of it was essentially an inverse to the |primitive| routine that enters +a \TeX\ primitive into |eqtb|. + +Thanks to |prim_data|, there is no need for all that tediousness. What is left of +|primt_cnd_chr| are just the exceptions to the general rule that the +|cmd,chr_code| pair represents in a single primitive command. + +*/ + +#define chr_cmd(A) do { tprint(A); print(chr_code); } while (0) + +static void prim_cmd_chr(quarterword cmd, halfword chr_code) +{ + int idx = chr_code - prim_data[cmd].offset; + if (cmd <= last_cmd && + idx >= 0 && idx < prim_data[cmd].subids && + prim_data[cmd].names != NULL && prim_data[cmd].names[idx] != 0) { + tprint_esc(""); + print(prim_data[cmd].names[idx]); + } else { + /* \TEX82 didn't print the |cmd,idx| information, but it may be useful. */ + tprint("[unknown command code! ("); + print_int(cmd); + tprint(", "); + print_int(idx); + tprint(")]"); + } +} + +void print_cmd_chr(quarterword cmd, halfword chr_code) +{ + int n; + switch (cmd) { + case left_brace_cmd: + chr_cmd("begin-group character "); + break; + case right_brace_cmd: + chr_cmd("end-group character "); + break; + case math_shift_cmd: + chr_cmd("math shift character "); + break; + case mac_param_cmd: + if (chr_code == tab_mark_cmd_code) + tprint_esc("alignmark"); + else + chr_cmd("macro parameter character "); + break; + case sup_mark_cmd: + chr_cmd("superscript character "); + break; + case sub_mark_cmd: + chr_cmd("subscript character "); + break; + case endv_cmd: + tprint("end of alignment template"); + break; + case spacer_cmd: + chr_cmd("blank space "); + break; + case letter_cmd: + chr_cmd("the letter "); + break; + case other_char_cmd: + chr_cmd("the character "); + break; + case tab_mark_cmd: + if (chr_code == span_code) + tprint_esc("span"); + else if (chr_code == tab_mark_cmd_code) + tprint_esc("aligntab"); + else + chr_cmd("alignment tab character "); + break; + case if_test_cmd: + if (chr_code >= unless_code) + tprint_esc("unless"); + prim_cmd_chr(cmd, (chr_code % unless_code)); + break; + case char_given_cmd: + tprint_esc("char"); + print_qhex(chr_code); + break; + case math_given_cmd: + /*tex + Okay, it's better for old macro packages that mess with meaning + to report a traditional value. A compromise. + */ + tprint_esc("mathchar"); + show_mathcode_value_old(chr_code); + break; + case xmath_given_cmd: + tprint_esc("Umathchar"); + show_mathcode_value(mathchar_from_integer(chr_code, umath_mathcode)); + break; + case lua_expandable_call_cmd: + tprint("expandable luacall "); + print_int(chr_code); + break; + case lua_local_call_cmd: + tprint("local luacall "); + print_int(chr_code); + break; + case lua_call_cmd: + tprint("luacall "); + print_int(chr_code); + break; + case set_font_cmd: + tprint("select font "); + tprint(font_name(chr_code)); + if (font_size(chr_code) != font_dsize(chr_code)) { + tprint(" at "); + print_scaled(font_size(chr_code)); + tprint("pt"); + } + break; + case undefined_cs_cmd: + tprint("undefined"); + break; + case call_cmd: + case long_call_cmd: + case outer_call_cmd: + case long_outer_call_cmd: + n = cmd - call_cmd; + if (token_info(token_link(chr_code)) == protected_token) + n = n + 4; + if (odd(n / 4)) + tprint_esc("protected"); + if (odd(n)) + tprint_esc("long"); + if (odd(n / 2)) + tprint_esc("outer"); + if (n > 0) + tprint(" "); + tprint("macro"); + break; + case assign_glue_cmd: + case assign_mu_glue_cmd: + if (chr_code < skip_base) { + prim_cmd_chr(cmd, chr_code); + } else if (chr_code < mu_skip_base) { + tprint_esc("skip"); + print_int(chr_code - skip_base); + } else { + tprint_esc("muskip"); + print_int(chr_code - mu_skip_base); + } + break; + case assign_toks_cmd: + if (chr_code >= toks_base) { + tprint_esc("toks"); + print_int(chr_code - toks_base); + } else { + prim_cmd_chr(cmd, chr_code); + } + break; + case assign_int_cmd: + if (chr_code < count_base) { + prim_cmd_chr(cmd, chr_code); + } else { + tprint_esc("count"); + print_int(chr_code - count_base); + } + break; + case assign_attr_cmd: + tprint_esc("attribute"); + print_int(chr_code - attribute_base); + break; + case assign_dimen_cmd: + if (chr_code < scaled_base) { + prim_cmd_chr(cmd, chr_code); + } else { + tprint_esc("dimen"); + print_int(chr_code - scaled_base); + } + break; + case normal_cmd: + if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) { + prim_cmd_chr(cmd, chr_code); + } else { + tprint("[unknown command! ("); + print_int(chr_code); + tprint(")]"); + } + break; + case extension_cmd: + if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) { + prim_cmd_chr(cmd, chr_code); + } else { + tprint("[unknown extension! ("); + print_int(chr_code); + tprint(")]"); + + } + break; + case node_cmd: + tprint("node "); + print_int(chr_code); + break; + default: + /*tex These are most commands, actually. */ + prim_cmd_chr(cmd, chr_code); + break; + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/primitive.w b/Build/source/texk/web2c/luatexdir/tex/primitive.w deleted file mode 100644 index 8c6592c9edf..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/primitive.w +++ /dev/null @@ -1,664 +0,0 @@ -% primitive.w -% -% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - - -#include "ptexlib.h" - -@ Control sequences are stored and retrieved by means of a fairly standard hash -table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C -in {\sl The Art of Computer Programming\/}). Once a control sequence enters the -table, it is never removed, because there are complicated situations -involving \.{\\gdef} where the removal of a control sequence at the end of -a group would be a mistake preventable only by the introduction of a -complicated reference-count mechanism. - -The actual sequence of letters forming a control sequence identifier is -stored in the |str_pool| array together with all the other strings. An -auxiliary array |hash| consists of items with two halfword fields per -word. The first of these, called |next(p)|, points to the next identifier -belonging to the same coalesced list as the identifier corresponding to~|p|; -and the other, called |text(p)|, points to the |str_start| entry for -|p|'s identifier. If position~|p| of the hash table is empty, we have -|text(p)=0|; if position |p| is either empty or the end of a coalesced -hash list, we have |next(p)=0|. An auxiliary pointer variable called -|hash_used| is maintained in such a way that all locations |p>=hash_used| -are nonempty. The global variable |cs_count| tells how many multiletter -control sequences have been defined, if statistics are being kept. - -A global boolean variable called |no_new_control_sequence| is set to -|true| during the time that new hash table entries are forbidden. - -@c -two_halves *hash; /* the hash table */ -halfword hash_used; /* allocation pointer for |hash| */ -int hash_extra; /* |hash_extra=hash| above |eqtb_size| */ -halfword hash_top; /* maximum of the hash array */ -halfword hash_high; /* pointer to next high hash location */ -boolean no_new_control_sequence; /* are new identifiers legal? */ -int cs_count; /* total number of known identifiers */ - -#define hash_is_full (hash_used==hash_base) /* test if all positions are occupied */ - -@ \.{\\primitive} support needs a few extra variables and definitions - -@c -#define prim_base 1 - -@ The arrays |prim| and |prim_eqtb| are used for name -> cmd,chr lookups. - - The are modelled after |hash| and |eqtb|, except that primitives do not - have an |eq_level|, that field is replaced by |origin|. - -@c -#define prim_next(a) prim[(a)].lhfield /* link for coalesced lists */ -#define prim_text(a) prim[(a)].rh /* string number for control sequence name */ -#define prim_is_full (prim_used==prim_base) /* test if all positions are occupied */ - -#define prim_origin_field(a) (a).hh.b1 -#define prim_eq_type_field(a) (a).hh.b0 -#define prim_equiv_field(a) (a).hh.rh -#define prim_origin(a) prim_origin_field(prim_eqtb[(a)]) /* level of definition */ -#define prim_eq_type(a) prim_eq_type_field(prim_eqtb[(a)]) /* command code for equivalent */ -#define prim_equiv(a) prim_equiv_field(prim_eqtb[(a)]) /* equivalent value */ - -static pointer prim_used; /* allocation pointer for |prim| */ -static two_halves prim[(prim_size + 1)]; /* the primitives table */ -static memory_word prim_eqtb[(prim_size + 1)]; - -@ The array |prim_data| works the other way around, it is used for - cmd,chr -> name lookups. - -@c -typedef struct prim_info { - halfword subids; /* number of name entries */ - halfword offset; /* offset to be used for |chr_code|s */ - str_number *names; /* array of names */ -} prim_info; - -static prim_info prim_data[(last_cmd + 1)]; - -@ initialize the memory arrays -@c -void init_primitives(void) -{ - int k; - memset(prim_data, 0, (sizeof(prim_info) * (last_cmd + 1))); - memset(prim, 0, (sizeof(two_halves) * (prim_size + 1))); - memset(prim_eqtb, 0, (sizeof(memory_word) * (prim_size + 1))); - for (k = 0; k <= prim_size; k++) - prim_eq_type(k) = undefined_cs_cmd; -} - -void ini_init_primitives(void) -{ - prim_used = prim_size; /* nothing is used */ -} - - -@ The value of |hash_prime| should be roughly 85\%! of |hash_size|, and it - should be a prime number. The theory of hashing tells us to expect fewer - than two table probes, on the average, when the search is successful. - [See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.] - @^Vitter, Jeffrey Scott@> - -@c -static halfword compute_hash(const char *j, unsigned int l, - halfword prime_number) -{ - int k; - halfword h = (unsigned char) *j; - for (k = 1; k <= (int)(l - 1); k++) { - h = h + h + (unsigned char) *(j + k); - while (h >= prime_number) - h = h - prime_number; - } - return h; -} - - -@ Here is the subroutine that searches the primitive table for an identifier -@c -pointer prim_lookup(str_number s) -{ - int h; /* hash code */ - pointer p; /* index in |hash| array */ - unsigned char *j; - unsigned l; - if (s < STRING_OFFSET) { - p = s; - if ((p < 0) || (get_prim_eq_type(p) == undefined_cs_cmd)) { - p = undefined_primitive; - } - } else { - j = str_string(s); - l = (unsigned) str_length(s); - h = compute_hash((char *) j, l, prim_prime); - p = h + prim_base; /* we start searching here; note that |0<=h<hash_prime| */ - while (1) { - if (prim_text(p) > 0) - if (str_length(prim_text(p)) == l) - if (str_eq_str(prim_text(p), s)) - goto FOUND; - if (prim_next(p) == 0) { - if (no_new_control_sequence) { - p = undefined_primitive; - } else { - /* Insert a new primitive after |p|, then make |p| point to it */ - if (prim_text(p) > 0) { - do { /* search for an empty location in |prim| */ - if (prim_is_full) - overflow("primitive size", prim_size); - decr(prim_used); - } while (prim_text(prim_used) != 0); - prim_next(p) = prim_used; - p = prim_used; - } - prim_text(p) = s; - } - goto FOUND; - } - p = prim_next(p); - } - } - FOUND: - return p; -} - -@ how to test a csname for primitive-ness -@c -boolean is_primitive(str_number csname) -{ - int n, m; - char *ss; - m = prim_lookup(csname); - ss = makecstring(csname); - n = string_lookup(ss, str_length(csname)); - free(ss); - return ((n != undefined_cs_cmd) && - (m != undefined_primitive) && - (eq_type(n) == prim_eq_type(m)) && (equiv(n) == prim_equiv(m))); -} - - -@ a few simple accessors -@c -quarterword get_prim_eq_type(int p) -{ - return prim_eq_type(p); -} - -quarterword get_prim_origin(int p) -{ - return prim_origin(p); -} - -halfword get_prim_equiv(int p) -{ - return prim_equiv(p); -} - -str_number get_prim_text(int p) -{ - return prim_text(p); -} - - -@ dumping and undumping -@c -void dump_primitives(void) -{ - int p, q; - for (p = 0; p <= prim_size; p++) - dump_hh(prim[p]); - for (p = 0; p <= prim_size; p++) - dump_wd(prim_eqtb[p]); - for (p = 0; p <= last_cmd; p++) { - dump_int(prim_data[p].offset); - dump_int(prim_data[p].subids); - for (q = 0; q < prim_data[p].subids; q++) { - dump_int(prim_data[p].names[q]); - } - } -} - -void undump_primitives(void) -{ - int p, q; - for (p = 0; p <= prim_size; p++) - undump_hh(prim[p]); - for (p = 0; p <= prim_size; p++) - undump_wd(prim_eqtb[p]); - - for (p = 0; p <= last_cmd; p++) { - undump_int(prim_data[p].offset); - undump_int(prim_data[p].subids); - if (prim_data[p].subids > 0) { - prim_data[p].names = (str_number *) - xmalloc((unsigned) - ((unsigned) prim_data[p].subids * - sizeof(str_number *))); - for (q = 0; q < prim_data[p].subids; q++) - undump_int(prim_data[p].names[q]); - } - } -} - -@ We need to put \TeX's ``primitive'' control sequences into the hash - table, together with their command code (which will be the |eq_type|) - and an operand (which will be the |equiv|). The |primitive| procedure - does this, in a way that no \TeX\ user can. The global value |cur_val| - contains the new |eqtb| pointer after |primitive| has acted. - - -@ Because the definitions of the actual user-accessible name of a - primitive can be postponed until runtime, the function |primitive_def| - is needed that does nothing except creating the control sequence name. - -@c -void primitive_def(const char *s, size_t l, quarterword c, halfword o) -{ - int nncs = no_new_control_sequence; - no_new_control_sequence = false; - cur_val = string_lookup(s, l); /* this creates the |text()| string */ - no_new_control_sequence = nncs; - eq_level(cur_val) = level_one; - eq_type(cur_val) = c; - equiv(cur_val) = o; -} - -@ The function |store_primitive_name| sets up the bookkeeping for the - reverse lookup. It is quite paranoid, because it is easy to mess this up - accidentally. - - The |offset| is needed because sometimes character codes (in |o|) - are indices into |eqtb| or are offset by a magical value to make - sure they do not conflict with something else. We don't want the - |prim_data[c].names| to have too many entries as it will just be - wasted room, so |offset| is substracted from |o| because creating - or accessing the array. The |assert(idx<=0xFFFF)| is not strictly - needed, but it helps catch errors of this kind. - -@c -static void -store_primitive_name(str_number s, quarterword c, halfword o, halfword offset) -{ - int idx; - if (prim_data[c].offset != 0 && prim_data[c].offset != offset) { - assert(false); - } - prim_data[c].offset = offset; - idx = ((int) o - offset); - assert(idx >= 0); - assert(idx <= 0xFFFF); - if (prim_data[c].subids < (idx + 1)) { - str_number *new = - (str_number *) xcalloc((unsigned) (idx + 1), sizeof(str_number *)); - if (prim_data[c].names != NULL) { - assert(prim_data[c].subids); - memcpy(new, (prim_data[c].names), - (unsigned) (prim_data[c].subids) * sizeof(str_number)); - free(prim_data[c].names); - } - prim_data[c].names = new; - prim_data[c].subids = idx + 1; - } - prim_data[c].names[idx] = s; -} - -@ Compared to tex82, |primitive| has two extra parameters. The |off| is an offset - that will be passed on to |store_primitive_name|, the |cmd_origin| is the bit - that is used to group primitives by originator. - -@c -void -primitive(const char *thes, quarterword c, halfword o, halfword off, - int cmd_origin) -{ - int prim_val; /* needed to fill |prim_eqtb| */ - str_number ss; - assert(o >= off); - ss = maketexstring(thes); - if (cmd_origin == tex_command || cmd_origin == core_command) { - primitive_def(thes, strlen(thes), c, o); - } - prim_val = prim_lookup(ss); - prim_origin(prim_val) = (quarterword) cmd_origin; - prim_eq_type(prim_val) = c; - prim_equiv(prim_val) = o; - store_primitive_name(ss, c, o, off); -} - - - -@ Here is a helper that does the actual hash insertion. - -@c -static halfword insert_id(halfword p, const unsigned char *j, unsigned int l) -{ - unsigned saved_cur_length; - unsigned saved_cur_string_size; - unsigned char *saved_cur_string; - const unsigned char *k; - /* This code far from ideal: the existance of |hash_extra| changes - all the potential (short) coalesced lists into a single (long) - one. This will create a slowdown. */ - if (cs_text(p) > 0) { - if (hash_high < hash_extra) { - incr(hash_high); - /* can't use |eqtb_top| here (perhaps because that is not finalized - yet when called from |primitive|?) */ - cs_next(p) = hash_high + eqtb_size; - p = cs_next(p); - } else { - do { - if (hash_is_full) - overflow("hash size", (unsigned) (hash_size + hash_extra)); - decr(hash_used); - } while (cs_text(hash_used) != 0); /* search for an empty location in |hash| */ - cs_next(p) = hash_used; - p = hash_used; - } - } - saved_cur_length = cur_length; - saved_cur_string = cur_string; - saved_cur_string_size = cur_string_size; - reset_cur_string(); - for (k = j; k <= j + l - 1; k++) - append_char(*k); - cs_text(p) = make_string(); - cur_length = saved_cur_length; - xfree(cur_string); - cur_string = saved_cur_string; - cur_string_size = saved_cur_string_size; - incr(cs_count); - return p; -} - - -@ Here is the subroutine that searches the hash table for an identifier - that matches a given string of length |l>1| appearing in |buffer[j.. - (j+l-1)]|. If the identifier is found, the corresponding hash table address - is returned. Otherwise, if the global variable |no_new_control_sequence| - is |true|, the dummy address |undefined_control_sequence| is returned. - Otherwise the identifier is inserted into the hash table and its location - is returned. - -@c -pointer id_lookup(int j, int l) -{ /* search the hash table */ - int h; /* hash code */ - pointer p; /* index in |hash| array */ - - h = compute_hash((char *) (buffer + j), (unsigned) l, hash_prime); -#ifdef VERBOSE - { - unsigned char *todo = xmalloc(l + 2); - strncpy(todo, (buffer + j), l); - todo[l] = '\0'; - todo[l + 1] = '\0'; - fprintf(stdout, "id_lookup(%s)\n", todo); - free(todo); - } -#endif - p = h + hash_base; /* we start searching here; note that |0<=h<hash_prime| */ - while (1) { - if (cs_text(p) > 0) - if (str_length(cs_text(p)) == (unsigned) l) - if (str_eq_buf(cs_text(p), j)) - goto FOUND; - if (cs_next(p) == 0) { - if (no_new_control_sequence) { - p = undefined_control_sequence; - } else { - p = insert_id(p, (buffer + j), (unsigned) l); - } - goto FOUND; - } - p = cs_next(p); - } - FOUND: - return p; -} - -@ Here is a similar subroutine for finding a primitive in the hash. -This one is based on a C string. - -@c -pointer string_lookup(const char *s, size_t l) -{ /* search the hash table */ - int h; /* hash code */ - pointer p; /* index in |hash| array */ - h = compute_hash(s, (unsigned) l, hash_prime); - p = h + hash_base; /* we start searching here; note that |0<=h<hash_prime| */ - while (1) { - if (cs_text(p) > 0) - if (str_eq_cstr(cs_text(p), s, l)) - goto FOUND; - if (cs_next(p) == 0) { - if (no_new_control_sequence) { - p = undefined_control_sequence; - } else { - p = insert_id(p, (const unsigned char *) s, (unsigned) l); - } - goto FOUND; - } - p = cs_next(p); - } - FOUND: - return p; -} - -@ The |print_cmd_chr| routine prints a symbolic interpretation of a - command code and its modifier. This is used in certain `\.{You can\'t}' - error messages, and in the implementation of diagnostic routines like - \.{\\show}. - - The body of |print_cmd_chr| use to be a rather tedious listing of print - commands, and most of it was essentially an inverse to the |primitive| - routine that enters a \TeX\ primitive into |eqtb|. - - Thanks to |prim_data|, there is no need for all that tediousness. What - is left of |primt_cnd_chr| are just the exceptions to the general rule - that the |cmd,chr_code| pair represents in a single primitive command. - -@c -#define chr_cmd(A) do { tprint(A); print(chr_code); } while (0) - -static void prim_cmd_chr(quarterword cmd, halfword chr_code) -{ - int idx = chr_code - prim_data[cmd].offset; - if (cmd <= last_cmd && - idx >= 0 && idx < prim_data[cmd].subids && - prim_data[cmd].names != NULL && prim_data[cmd].names[idx] != 0) { - tprint_esc(""); - print(prim_data[cmd].names[idx]); - } else { - /* TEX82 didn't print the |cmd,idx| information, but it may be useful */ - tprint("[unknown command code! ("); - print_int(cmd); - tprint(", "); - print_int(idx); - tprint(")]"); - } -} - -void print_cmd_chr(quarterword cmd, halfword chr_code) -{ - int n; /* temp variable */ - switch (cmd) { - case left_brace_cmd: - chr_cmd("begin-group character "); - break; - case right_brace_cmd: - chr_cmd("end-group character "); - break; - case math_shift_cmd: - chr_cmd("math shift character "); - break; - case mac_param_cmd: - if (chr_code == tab_mark_cmd_code) - tprint_esc("alignmark"); - else - chr_cmd("macro parameter character "); - break; - case sup_mark_cmd: - chr_cmd("superscript character "); - break; - case sub_mark_cmd: - chr_cmd("subscript character "); - break; - case endv_cmd: - tprint("end of alignment template"); - break; - case spacer_cmd: - chr_cmd("blank space "); - break; - case letter_cmd: - chr_cmd("the letter "); - break; - case other_char_cmd: - chr_cmd("the character "); - break; - case tab_mark_cmd: - if (chr_code == span_code) - tprint_esc("span"); - else if (chr_code == tab_mark_cmd_code) - tprint_esc("aligntab"); - else - chr_cmd("alignment tab character "); - break; - case if_test_cmd: - if (chr_code >= unless_code) - tprint_esc("unless"); - prim_cmd_chr(cmd, (chr_code % unless_code)); - break; - case char_given_cmd: - tprint_esc("char"); - print_hex(chr_code); - break; - case math_given_cmd: - if (math_umathcode_meaning_par == 1) { - tprint_esc("Umathchar"); - show_mathcode_value(mathchar_from_integer(chr_code, tex_mathcode)); - } else { - /* better for old macro packages that mess with meaning */ - tprint_esc("mathchar"); - show_mathcode_value_old(chr_code); - } - break; - case xmath_given_cmd: - tprint_esc("Umathchar"); - show_mathcode_value(mathchar_from_integer(chr_code, umath_mathcode)); - break; - case set_font_cmd: - tprint("select font "); - tprint(font_name(chr_code)); - if (font_size(chr_code) != font_dsize(chr_code)) { - tprint(" at "); - print_scaled(font_size(chr_code)); - tprint("pt"); - } - break; - case undefined_cs_cmd: - tprint("undefined"); - break; - case call_cmd: - case long_call_cmd: - case outer_call_cmd: - case long_outer_call_cmd: - n = cmd - call_cmd; - if (token_info(token_link(chr_code)) == protected_token) - n = n + 4; - if (odd(n / 4)) - tprint_esc("protected"); - if (odd(n)) - tprint_esc("long"); - if (odd(n / 2)) - tprint_esc("outer"); - if (n > 0) - tprint(" "); - tprint("macro"); - break; - case assign_glue_cmd: - case assign_mu_glue_cmd: - if (chr_code < skip_base) { - prim_cmd_chr(cmd, chr_code); - } else if (chr_code < mu_skip_base) { - tprint_esc("skip"); - print_int(chr_code - skip_base); - } else { - tprint_esc("muskip"); - print_int(chr_code - mu_skip_base); - } - break; - case assign_toks_cmd: - if (chr_code >= toks_base) { - tprint_esc("toks"); - print_int(chr_code - toks_base); - } else { - prim_cmd_chr(cmd, chr_code); - } - break; - case assign_int_cmd: - if (chr_code < count_base) { - prim_cmd_chr(cmd, chr_code); - } else { - tprint_esc("count"); - print_int(chr_code - count_base); - } - break; - case assign_attr_cmd: - tprint_esc("attribute"); - print_int(chr_code - attribute_base); - break; - case assign_dimen_cmd: - if (chr_code < scaled_base) { - prim_cmd_chr(cmd, chr_code); - } else { - tprint_esc("dimen"); - print_int(chr_code - scaled_base); - } - break; - case normal_cmd: - if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) { - prim_cmd_chr(cmd, chr_code); - } else { - tprint("[unknown command! ("); - print_int(chr_code); - tprint(")]"); - } - break; - case extension_cmd: - if (chr_code < prim_data[cmd].subids && prim_data[cmd].names[chr_code] != 0) { - prim_cmd_chr(cmd, chr_code); - } else { - tprint("[unknown extension! ("); - print_int(chr_code); - tprint(")]"); - - } - break; - default: - /* these are most commands, actually */ - prim_cmd_chr(cmd, chr_code); - break; - } -} diff --git a/Build/source/texk/web2c/luatexdir/tex/printing.w b/Build/source/texk/web2c/luatexdir/tex/printing.c index 72444156480..0a961071b05 100644 --- a/Build/source/texk/web2c/luatexdir/tex/printing.w +++ b/Build/source/texk/web2c/luatexdir/tex/printing.c @@ -1,87 +1,137 @@ -% printing.w -% -% Copyright 2009-2013 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +printing.w + +Copyright 2009-2013 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + #include "ptexlib.h" -#include "lua/luatex-api.h" /* for luatex_banner */ +#include "lua/luatex-api.h" -@ @c #define wlog(A) fputc(A,log_file) #define wterm(A) fputc(A,term_out) int new_string_line = 0; int escape_controls = 1; -@ Messages that are sent to a user's terminal and to the transcript-log file -are produced by several `|print|' procedures. These procedures will -direct their output to a variety of places, based on the setting of -the global variable |selector|, which has the following possible -values: +/*tex + +Messages that are sent to a user's terminal and to the transcript-log file are +produced by several `|print|' procedures. These procedures will direct their +output to a variety of places, based on the setting of the global variable +|selector|, which has the following possible values: + +\startitemize + +\startitem + |term_and_log|, the normal setting, prints on the terminal and on the + transcript file. +\stopitem + +\startitem + |log_only|, prints only on the transcript file. +\stopitem + +\startitem + |term_only|, prints only on the terminal. +\stopitem + +\startitem + |no_print|, doesn't print at all. This is used only in rare cases before the + transcript file is open. +\stopitem + +\startitem + |pseudo|, puts output into a cyclic buffer that is used by the |show_context| + routine; when we get to that routine we shall discuss the reasoning behind + this curious mode. +\stopitem + +\startitem + |new_string|, appends the output to the current string in the string pool. +\stopitem + +\startitem + 0 to 15, prints on one of the sixteen files for \.{\\write} output. +\stopitem -\yskip -\hang |term_and_log|, the normal setting, prints on the terminal and on the - transcript file. +\stopitemize -\hang |log_only|, prints only on the transcript file. +The symbolic names `|term_and_log|', etc., have been assigned numeric codes that +satisfy the convenient relations |no_print+1=term_only|, |no_print+2=log_only|, +|term_only+2=log_only+1=term_and_log|. -\hang |term_only|, prints only on the terminal. +Three additional global variables, |tally| and |term_offset| and |file_offset|, +record the number of characters that have been printed since they were most +recently cleared to zero. We use |tally| to record the length of (possibly very +long) stretches of printing; |term_offset| and |file_offset|, on the other hand, +keep track of how many characters have appeared so far on the current line that +has been output to the terminal or to the transcript file, respectively. -\hang |no_print|, doesn't print at all. This is used only in rare cases - before the transcript file is open. +*/ + +/*tex transcript of \TeX\ session */ + +alpha_file log_file; + +/*tex where to print a message */ + +int selector = term_only; + +/*tex digits in a number being output */ + +int dig[23]; + +/*tex the number of characters recently printed */ + +int tally = 0; + +/*tex the number of characters on the current terminal line */ -\hang |pseudo|, puts output into a cyclic buffer that is used - by the |show_context| routine; when we get to that routine we shall discuss - the reasoning behind this curious mode. +int term_offset = 0; -\hang |new_string|, appends the output to the current string in the - string pool. +/*tex the number of characters on the current file line */ -\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output. +int file_offset = 0; -\yskip -\noindent The symbolic names `|term_and_log|', etc., have been assigned -numeric codes that satisfy the convenient relations |no_print+1=term_only|, -|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|. +/*tex circular buffer for pseudoprinting */ -Three additional global variables, |tally| and |term_offset| and -|file_offset|, record the number of characters that have been printed -since they were most recently cleared to zero. We use |tally| to record -the length of (possibly very long) stretches of printing; |term_offset| -and |file_offset|, on the other hand, keep track of how many characters -have appeared so far on the current line that has been output to the -terminal or to the transcript file, respectively. +packed_ASCII_code trick_buf[(ssup_error_line + 1)]; -@c -alpha_file log_file; /* transcript of \TeX\ session */ -int selector = term_only; /* where to print a message */ -int dig[23]; /* digits in a number being output */ -int tally = 0; /* the number of characters recently printed */ -int term_offset = 0; /* the number of characters on the current terminal line */ -int file_offset = 0; /* the number of characters on the current file line */ -packed_ASCII_code trick_buf[(ssup_error_line + 1)]; /* circular buffer for pseudoprinting */ -int trick_count; /* threshold for pseudoprinting, explained later */ -int first_count; /* another variable for pseudoprinting */ -boolean inhibit_par_tokens = false; /* for minor adjustments to |show_token_list| */ +/*tex threshold for pseudoprinting, explained later */ -@ To end a line of text output, we call |print_ln| +int trick_count; + +/*tex another variable for pseudoprinting */ + +int first_count; + +/*tex for minor adjustments to |show_token_list| */ + +boolean inhibit_par_tokens = false; + +/*tex + +To end a line of text output, we call |print_ln| + +*/ -@c void print_ln(void) { switch (selector) { @@ -111,19 +161,21 @@ void print_ln(void) fprintf(write_file[selector], "\n"); break; } - /* |tally| is not affected */ + /*tex |tally| is not affected */ } -@ The |print_char| procedure sends one byte to the desired destination. -All printing comes through |print_ln| or |print_char|, except for the -case of |tprint| (see below). +/*tex + +The |print_char| procedure sends one byte to the desired destination. All +printing comes through |print_ln| or |print_char|, except for the case of +|tprint| (see below). -The checking of the line length is an inheritance from previosu engines -and we might drop it after release 1.0. We're not too picky about the exact -match of that length because we have utf output so length is then a bit -fuzzy anyway. +The checking of the line length is an inheritance from previosu engines and we +might drop it after release 1.0. We're not too picky about the exact match of +that length because we have utf output so length is then a bit fuzzy anyway. + +*/ -@c #define needs_escaping(A) \ ((! escape_controls) || (A>=0x20) || (A==0x0A) || (A==0x0D) || (A==0x09)) @@ -144,17 +196,6 @@ fuzzy anyway. term_offset += 2; \ } -/* - -#define needs_wrapping(A,B) \ - (((A>=0xF0)&&(B+4>=max_print_line)) || \ - ((A>=0xE0)&&(B+3>=max_print_line)) || \ - ((A>=0xC0)&&(B+2>=max_print_line))) - -we have mostly ascii in logs, so ... - -*/ - #define needs_wrapping(A,B) \ ( (A>=0xC0) && \ (((A>=0xF0) && (B+4>=max_print_line)) || \ @@ -236,23 +277,25 @@ void print_char(int s) incr(tally); } -@ An entire string is output by calling |print|. Note that if we are outputting -the single standard ASCII character \.c, we could call |print("c")|, since -|"c"=99| is the number of a single-character string, as explained above. But -|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char| -routine when it knows that this is safe. (The present implementation -assumes that it is always safe to print a visible ASCII character.) +/*tex + +An entire string is output by calling |print|. Note that if we are outputting the +single standard ASCII character \.c, we could call |print("c")|, since |"c"=99| +is the number of a single-character string, as explained above. But +|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char| routine +when it knows that this is safe. (The present implementation assumes that it is +always safe to print a visible ASCII character.) -@^system dependencies@> +The first 256 entries above the 17th unicode plane are used for a special trick: +when \TeX\ has to print items in that range, it will instead print the character +that results from substracting 0x110000 from that value. This allows +byte-oriented output to things like \.{\\specials} and \.{\\pdfextension +literals}. Todo: Perhaps it would be useful to do the same substraction while +typesetting. -The first 256 entries above the 17th unicode plane are used for a -special trick: when \TeX\ has to print items in that range, it will -instead print the character that results from substracting 0x110000 -from that value. This allows byte-oriented output to things like -\.{\\specials} and \.{\\pdfextension literals}. Todo: Perhaps it would be useful -to do the same substraction while typesetting. -@c +*/ + void print(int s) { if (s >= str_ptr) { @@ -262,9 +305,9 @@ void print(int s) if (s < 0) { normal_warning("print","bad string offset"); } else { - /* TH not sure about this, disabled for now! */ + /*tex We're not sure about this so it's disabled for now! */ if ((false) && (selector > pseudo)) { - /* internal strings are not expanded */ + /*tex internal strings are not expanded */ print_char(s); return; } @@ -307,12 +350,13 @@ void print(int s) } void lprint (lstring *ss) { - unsigned char *j, *l; /* current character code position */ + /*tex current character code position */ + unsigned char *j, *l; j = ss->s; l = j + ss->l; while (j < l) { - /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80 */ - /* I don't bother checking the last two bytes explicitly */ + /*tex I don't bother checking the last two bytes explicitly */ + /* 0x110000 in utf=8: 0xF4 0x90 0x80 0x80 */ if ((j < l - 4) && (*j == 0xF4) && (*(j + 1) == 0x90)) { int c = (*(j + 2) - 128) * 64 + (*(j + 3) - 128); assert(c >= 0 && c < 256); @@ -325,12 +369,17 @@ void lprint (lstring *ss) { } } -@ The procedure |print_nl| is like |print|, but it makes sure that the -string appears at the beginning of a new line. +/*tex + +The procedure |print_nl| is like |print|, but it makes sure that the string +appears at the beginning of a new line. + +*/ + +/*tex Moev to the beginning of the next line. */ -@c void print_nlp(void) -{ /* move to beginning of a line */ +{ if (new_string_line > 0) { print_char(new_string_line); } else if (((term_offset > 0) && (odd(selector))) || @@ -339,17 +388,22 @@ void print_nlp(void) } } +/*tex Prints string |s| at beginning of the next line. */ + void print_nl(str_number s) -{ /* prints string |s| at beginning of line */ +{ print_nlp(); print(s); } -@ |char *| versions of the same procedures. |tprint| is -different because it uses buffering, which works well because -most of the output actually comes through |tprint|. +/*tex + +|char *| versions of the same procedures. |tprint| is different because it uses +buffering, which works well because most of the output actually comes through +|tprint|. + +*/ -@c #define t_flush_buffer(target,offset) \ buffer[i++] = '\n'; \ buffer[i++] = '\0';\ @@ -361,7 +415,7 @@ most of the output actually comes through |tprint|. void tprint(const char *sss) { char *buffer = NULL; - int i = 0; /* buffer index */ + int i = 0; int newlinechar = new_line_char_par; int dolog = 0; int doterm = 0; @@ -409,7 +463,7 @@ void tprint(const char *sss) } break; } - /* what is left is the 3 term/log settings */ + /*tex What is left is the 3 term/log settings. */ if (dolog || doterm) { buffer = xmalloc(strlen(sss)*3); if (dolog) { @@ -469,13 +523,16 @@ void tprint_nl(const char *s) tprint(s); } -@ Here is the very first thing that \TeX\ prints: a headline that identifies -the version number and format package. The |term_offset| variable is temporarily -incorrect, but the discrepancy is not serious since we assume that the banner -and format identifier together will occupy at most |max_print_line| -character positions. +/*tex + +Here is the very first thing that \TeX\ prints: a headline that identifies the +version number and format package. The |term_offset| variable is temporarily +incorrect, but the discrepancy is not serious since we assume that the banner and +format identifier together will occupy at most |max_print_line| character +positions. + +*/ -@c void print_banner(const char *v) { int callback_id = callback_defined(start_run_callback); @@ -484,15 +541,16 @@ void print_banner(const char *v) if (format_ident > 0) print(format_ident); print_ln(); - if (show_luahashchars){ + if (show_luahashchars) { wterm(' '); fprintf(term_out,"Number of bits used by the hash function (" my_name "): %d",LUAI_HASHLIMIT); - print_ln(); + print_ln(); } if (shellenabledp) { wterm(' '); - if (restrictedshell) + if (restrictedshell) { fprintf(term_out, "restricted "); + } fprintf(term_out, "system commands enabled.\n"); } } else if (callback_id > 0) { @@ -500,7 +558,6 @@ void print_banner(const char *v) } } -@ @c void log_banner(const char *v) { const char *months[] = { " ", @@ -536,41 +593,51 @@ void log_banner(const char *v) } } -@ @c void print_version_banner(void) { fprintf(term_out, "%s", luatex_banner); } -@ The procedure |print_esc| prints a string that is preceded by -the user's escape character (which is usually a backslash). +/*tex + +The procedure |print_esc| prints a string that is preceded by the user's escape +character (which is usually a backslash). + +*/ -@c void print_esc(str_number s) { - int c = escape_char_par; /* Set variable |c| to the current escape character */ - if (c >= 0 && c < STRING_OFFSET) + /*tex Set variable |c| to the current escape character: */ + int c = escape_char_par; + if (c >= 0 && c < 0x110000) print(c); print(s); } -@ This prints escape character, then |s|. +/*tex + +This prints escape character, then |s|. + +*/ -@c void tprint_esc(const char *s) { - int c = escape_char_par; /* Set variable |c| to the current escape character */ - if (c >= 0 && c < STRING_OFFSET) + /*tex Set variable |c| to the current escape character: */ + int c = escape_char_par; + if (c >= 0 && c < 0x110000) print(c); tprint(s); } -@ An array of digits in the range |0..15| is printed by |print_the_digs|. +/*tex + +An array of digits in the range |0..15| is printed by |print_the_digs|. + +*/ -@c void print_the_digs(eight_bits k) { - /* prints |dig[k-1]|$\,\ldots\,$|dig[0]| */ + /*tex prints |dig[k-1]|$\,\ldots\,$|dig[0]| */ while (k-- > 0) { if (dig[k] < 10) print_char('0' + dig[k]); @@ -579,17 +646,22 @@ void print_the_digs(eight_bits k) } } -@ The following procedure, which prints out the decimal representation of a -given integer |n|, has been written carefully so that it works properly -if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div| -to negative arguments, since such operations are not implemented consistently -by all PASCAL compilers. +/*tex + +The following procedure, which prints out the decimal representation of a given +integer |n|, has been written carefully so that it works properly if |n=0| or if +|(-n)| would cause overflow. It does not apply |mod| or |div| to negative +arguments, since such operations are not implemented consistently by all PASCAL +compilers. + +*/ -@c void print_int(longinteger n) { - int k = 0; /* index to current digit; we assume that $|n|<10^{23}$ */ - longinteger m; /* used to negate |n| in possibly dangerous cases */ + /*tex index to current digit; we assume that $|n|<10^{23}$ */ + int k = 0; + /*tex used to negate |n| in possibly dangerous cases */ + longinteger m; if (n < 0) { print_char('-'); if (n > -100000000) { @@ -615,11 +687,13 @@ void print_int(longinteger n) print_the_digs((eight_bits) k); } +/*tex -@ Here is a trivial procedure to print two digits; it is usually called with -a parameter in the range |0<=n<=99|. +Here is a trivial procedure to print two digits; it is usually called with a +parameter in the range |0<=n<=99|. + +*/ -@c void print_two(int n) { n = abs(n) % 100; @@ -627,12 +701,16 @@ void print_two(int n) print_char('0' + (n % 10)); } -@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. +/*tex -@c -void print_hex(int n) +Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. + +*/ + +void print_qhex(int n) { - int k = 0 ; /* index to current digit; we assume that $0\L n<16^{22}$ */ + /*tex index to current digit; we assume that $0\L n<16^{22}$ */ + int k = 0 ; print_char('"'); do { dig[k] = n % 16; @@ -642,16 +720,18 @@ void print_hex(int n) print_the_digs((eight_bits) k); } -@ Roman numerals are produced by the |print_roman_int| routine. Readers -who like puzzles might enjoy trying to figure out how this tricky code -works; therefore no explanation will be given. Notice that 1990 yields -\.{mcmxc}, not \.{mxm}. +/*tex + +Roman numerals are produced by the |print_roman_int| routine. Readers who like +puzzles might enjoy trying to figure out how this tricky code works; therefore no +explanation will be given. Notice that 1990 yields \.{mcmxc}, not \.{mxm}. + +*/ -@c void print_roman_int(int n) { - char *j, *k; /* mysterious indices */ - int u, v; /* mysterious numbers */ + char *j, *k; + int u, v; char mystery[] = "m2d5c2l5x2v5i"; j = (char *) mystery; v = 1000; @@ -661,7 +741,7 @@ void print_roman_int(int n) n = n - v; } if (n <= 0) { - /* nonpositive input produces no output */ + /*tex nonpositive input produces no output */ return; } k = j + 2; @@ -680,25 +760,31 @@ void print_roman_int(int n) } } -@ The |print| subroutine will not print a string that is still being -created. The following procedure will. +/*tex + +The |print| subroutine will not print a string that is still being created. The +following procedure will. + +*/ -@c void print_current_string(void) { - unsigned j = 0; /* points to current character code */ + /*tex points to current character code */ + unsigned j = 0; while (j < cur_length) print_char(cur_string[j++]); } -@ The procedure |print_cs| prints the name of a control sequence, given -a pointer to its address in |eqtb|. A space is printed after the name -unless it is a single nonletter or an active character. This procedure -might be invoked with invalid data, so it is ``extra robust.'' The -individual characters must be printed one at a time using |print|, since -they may be unprintable. +/*tex + +The procedure |print_cs| prints the name of a control sequence, given a pointer +to its address in |eqtb|. A space is printed after the name unless it is a single +nonletter or an active character. This procedure might be invoked with invalid +data, so it is ``extra robust.'' The individual characters must be printed one at +a time using |print|, since they may be unprintable. + +*/ -@c void print_cs(int p) { str_number t = cs_text(p); @@ -731,10 +817,12 @@ void print_cs(int p) } } -@ Here is a similar procedure; it avoids the error checks, and it never -prints a space after the control sequence. +/*tex -@c +Here is a similar procedure; it avoids the error checks, and it never prints a +space after the control sequence. + +*/ void sprint_cs(pointer p) { str_number t; @@ -762,9 +850,12 @@ void sprint_cs_name(pointer p) } } -@ This procedure is never called when |interaction<scroll_mode|. +/*tex + +This procedure is never called when |interaction<scroll_mode|. + +*/ -@c void prompt_input(const char *s) { wake_up_terminal(); @@ -772,10 +863,13 @@ void prompt_input(const char *s) term_input(); } -@ Then there is a subroutine that prints glue stretch and shrink, possibly -followed by the name of finite units: +/*tex + +Then there is a subroutine that prints glue stretch and shrink, possibly followed +by the name of finite units: + +*/ -@c void print_glue(scaled d, int order, const char *s) { print_scaled(d); @@ -792,9 +886,12 @@ void print_glue(scaled d, int order, const char *s) } } -@ The next subroutine prints a whole glue specification +/*tex + +The next subroutine prints a whole glue specification + +*/ -@c void print_spec(int p, const char *s) { if (p < 0) { @@ -814,42 +911,47 @@ void print_spec(int p, const char *s) } } -@ We can reinforce our knowledge of the data structures just introduced -by considering two procedures that display a list in symbolic form. -The first of these, called |short_display|, is used in ``overfull box'' -messages to give the top-level description of a list. The other one, -called |show_node_list|, prints a detailed description of exactly what -is in the data structure. +/*tex -The philosophy of |short_display| is to ignore the fine points about exactly -what is inside boxes, except that ligatures and discretionary breaks are -expanded. As a result, |short_display| is a recursive procedure, but the -recursion is never more than one level deep. -@^recursion@> +We can reinforce our knowledge of the data structures just introduced by +considering two procedures that display a list in symbolic form. The first of +these, called |short_display|, is used in ``overfull box'' messages to give the +top-level description of a list. The other one, called |show_node_list|, prints a +detailed description of exactly what is in the data structure. -A global variable |font_in_short_display| keeps track of the font code that -is assumed to be present when |short_display| begins; deviations from this -font will be printed. +The philosophy of |short_display| is to ignore the fine points about exactly what +is inside boxes, except that ligatures and discretionary breaks are expanded. As +a result, |short_display| is a recursive procedure, but the recursion is never +more than one level deep. @^recursion@> -@c -int font_in_short_display; /* an internal font number */ +A global variable |font_in_short_display| keeps track of the font code that is +assumed to be present when |short_display| begins; deviations from this font will +be printed. -@ Boxes, rules, inserts, whatsits, marks, and things in general that are -sort of ``complicated'' are indicated only by printing `\.{[]}'. +*/ -@c +/*tex An internal font number: */ -/* -So, 0, 1 as well as any large value will behave the same as before. The reason -for this extension is that a \name not always makes sense. +int font_in_short_display; + +/*tex + +Boxes, rules, inserts, whatsits, marks, and things in general that are sort of +``complicated'' are indicated only by printing `\.{[]}'. +We print a bit more than original \TEX. A value of 0 or 1 or any large value will +behave the same as before. The reason for this extension is that a |name| not +always makes sense. + +\starttyping 0 \foo xyz 1 \foo (bar) 2 <bar> xyz -3 <bar @@ ..> xyz +3 <bar @ ..> xyz 4 <id> 5 <id: bar> -6 <id: bar @@ ..> xyz +6 <id: bar @ ..> xyz +\stoptyping */ @@ -858,12 +960,12 @@ void print_font_identifier(internal_font_number f) str_number fonttext; fonttext = font_id_text(f); if (tracing_fonts_par >= 2 && tracing_fonts_par <= 6) { - /* < > is less likely to clash with text parenthesis */ + /*tex < > is less likely to clash with text parenthesis */ tprint("<"); if (tracing_fonts_par >= 2 && tracing_fonts_par <= 3) { print_font_name(f); if (tracing_fonts_par >= 3 || font_size(f) != font_dsize(f)) { - tprint(" @@ "); + tprint(" @ "); print_scaled(font_size(f)); tprint("pt"); } @@ -873,7 +975,7 @@ void print_font_identifier(internal_font_number f) tprint(": "); print_font_name(f); if (tracing_fonts_par >= 6 || font_size(f) != font_dsize(f)) { - tprint(" @@ "); + tprint(" @ "); print_scaled(font_size(f)); tprint("pt"); } @@ -881,7 +983,7 @@ void print_font_identifier(internal_font_number f) } print_char('>'); } else { - /* old method, inherited from pdftex */ + /*tex old method, inherited from pdftex */ if (fonttext > 0) { print_esc(fonttext); } else { @@ -892,7 +994,7 @@ void print_font_identifier(internal_font_number f) tprint(" ("); print_font_name(f); if (font_size(f) != font_dsize(f)) { - tprint("@@"); + tprint("@"); print_scaled(font_size(f)); tprint("pt"); } @@ -901,9 +1003,12 @@ void print_font_identifier(internal_font_number f) } } -@ This prints highlights of list |p|. +/*tex + +This prints highlights of list |p|. + +*/ -@c void short_display(int p) { while (p != null) { @@ -922,20 +1027,27 @@ void short_display(int p) print(character(p)); } } else { - /* Print a short indication of the contents of node |p| */ + /*tex Print a short indication of the contents of node |p| */ print_short_node_contents(p); } p = vlink(p); } } -@ The |show_node_list| routine requires some auxiliary subroutines: one to -print a font-and-character combination, one to print a token list without -its reference count, and one to print a rule dimension. +/*tex -@ This prints |char_node| data. +The |show_node_list| routine requires some auxiliary subroutines: one to print a +font-and-character combination, one to print a token list without its reference +count, and one to print a rule dimension. + +*/ + +/*tex + +This prints |char_node| data. + +*/ -@c void print_font_and_char(int p) { if (!is_valid_font(font(p))) @@ -946,9 +1058,12 @@ void print_font_and_char(int p) print(character(p)); } -@ This prints token list data in braces +/*tex + +This prints token list data in braces + +*/ -@c void print_mark(int p) { print_char('{'); @@ -959,9 +1074,12 @@ void print_mark(int p) print_char('}'); } -@ This prints dimensions of a rule node. +/*tex + +This prints dimensions of a rule node. + +*/ -@c void print_rule_dimen(scaled d) { if (is_running(d)) @@ -970,41 +1088,54 @@ void print_rule_dimen(scaled d) print_scaled(d); } -@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive, -@^recursion@> -up to a given maximum number of levels. The history of nesting is indicated -by the current string, which will be printed at the beginning of each line; -the length of this string, namely |cur_length|, is the depth of nesting. +/*tex -A global variable called |depth_threshold| is used to record the maximum -depth of nesting for which |show_node_list| will show information. If we -have |depth_threshold=0|, for example, only the top level information will -be given and no sublists will be traversed. Another global variable, called -|breadth_max|, tells the maximum number of items to show at each level; -|breadth_max| had better be positive, or you won't see anything. +Since boxes can be inside of boxes, |show_node_list| is inherently recursive, +@^recursion@> up to a given maximum number of levels. The history of nesting is +indicated by the current string, which will be printed at the beginning of each +line; the length of this string, namely |cur_length|, is the depth of nesting. -@c -int depth_threshold; /* maximum nesting depth in box displays */ -int breadth_max; /* maximum number of items shown at the same list level */ +A global variable called |depth_threshold| is used to record the maximum depth of +nesting for which |show_node_list| will show information. If we have +|depth_threshold=0|, for example, only the top level information will be given +and no sublists will be traversed. Another global variable, called |breadth_max|, +tells the maximum number of items to show at each level; |breadth_max| had better +be positive, or you won't see anything. -@ The recursive machinery is started by calling |show_box|. Assign the values +*/ + +/*tex The maximum nesting depth in box displays: */ + +int depth_threshold; + +/*tex The maximum number of items shown at the same list level: */ + +int breadth_max; + +/*tex + +The recursive machinery is started by calling |show_box|. Assign the values |depth_threshold:=show_box_depth| and |breadth_max:=show_box_breadth| -@c +*/ + void show_box(halfword p) { depth_threshold = show_box_depth_par; breadth_max = show_box_breadth_par; if (breadth_max <= 0) breadth_max = 5; - /* the show starts at |p| */ + /*tex the show starts at |p| */ show_node_list(p); print_ln(); } -@ Helper for debugging purposes. It prints highlights of list |p| +/*tex + +Helper for debugging purposes. It prints highlights of list |p| + +*/ -@c void short_display_n(int p, int m) { int i = 0; @@ -1041,7 +1172,7 @@ void short_display_n(int p, int m) short_display(vlink(post_break(p))); print_char('|'); } else { - /* Print a short indication of the contents of node |p| */ + /*tex Print a short indication of the contents of node |p| */ print_short_node_contents(p); } } @@ -1052,13 +1183,16 @@ void short_display_n(int p, int m) update_terminal(); } -@ When debugging a macro package, it can be useful to see the exact -control sequence names in the format file. For example, if ten new -csnames appear, it's nice to know what they are, to help pinpoint where -they came from. (This isn't a truly ``basic'' printing procedure, but -that's a convenient module in which to put it.) +/*tex + +When debugging a macro package, it can be useful to see the exact control +sequence names in the format file. For example, if ten new csnames appear, it's +nice to know what they are, to help pinpoint where they came from. (This isn't a +truly ``basic'' printing procedure, but that's a convenient module in which to +put it.) + +*/ -@c void print_csnames(int hstart, int hfinish) { int h; @@ -1066,11 +1200,11 @@ void print_csnames(int hstart, int hfinish) fprintf(stderr, "fmtdebug:csnames from %d to %d:", (int) hstart, (int) hfinish); for (h = hstart; h <= hfinish; h++) { if (cs_text(h) > 0) { - /* we have anything at this position */ + /*tex We have anything at this position. */ c = str_string(cs_text(h)); l = c + str_length(cs_text(h)); while (c < l) { - /* print the characters */ + /*tex Print the characters. */ fputc(*c++, stderr); } fprintf(stderr, "|"); @@ -1078,11 +1212,14 @@ void print_csnames(int hstart, int hfinish) } } -@ A helper for printing file:line:error style messages. Look for a -filename in |full_source_filename_stack|, and if we fail to find -one fall back on the non-file:line:error style. +/*tex + +A helper for printing file:line:error style messages. Look for a filename in +|full_source_filename_stack|, and if we fail to find one fall back on the +non-file:line:error style. + +*/ -@c void print_file_line(void) { int level = in_open; @@ -1102,11 +1239,14 @@ void print_file_line(void) } } -@ \TeX\ is occasionally supposed to print diagnostic information that -goes only into the transcript file, unless |tracing_online| is positive. -Here are two routines that adjust the destination of print commands: +/*tex + +\TeX\ is occasionally supposed to print diagnostic information that goes only +into the transcript file, unless |tracing_online| is positive. Here are two +routines that adjust the destination of print commands: + +*/ -@c void begin_diagnostic(void) { global_old_setting = selector; @@ -1117,9 +1257,12 @@ void begin_diagnostic(void) } } -@ Restore proper conditions after tracing. +/*tex + +Restore proper conditions after tracing. + +*/ -@c void end_diagnostic(boolean blank_line) { tprint_nl(""); @@ -1128,8 +1271,11 @@ void end_diagnostic(boolean blank_line) selector = global_old_setting; } -@ Of course we had better declare another global variable, if the previous -routines are going to work. +/*tex + +Of course we had better declare another global variable, if the previous routines +are going to work. + +*/ -@c int global_old_setting; diff --git a/Build/source/texk/web2c/luatexdir/tex/scanning.c b/Build/source/texk/web2c/luatexdir/tex/scanning.c new file mode 100644 index 00000000000..8f7e1d59178 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/scanning.c @@ -0,0 +1,2743 @@ +/* + +Copyright 2009-2012 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +static void scan_expr(void); + +/*tex + + Let's turn now to some procedures that \TeX\ calls upon frequently to digest + certain kinds of patterns in the input. Most of these are quite simple; some + are quite elaborate. Almost all of the routines call |get_x_token|, which can + cause them to be invoked recursively. + + The |scan_left_brace| routine is called when a left brace is supposed to be + the next non-blank token. (The term ``left brace'' means, more precisely, a + character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to appear + before the |left_brace|. + +*/ + +/* This reads a mandatory |left_brace|: */ + +void scan_left_brace(void) +{ + /*tex Get the next non-blank non-relax non-call token */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd != left_brace_cmd) { + print_err("Missing { inserted"); + help4( + "A left brace was mandatory here, so I've put one in.", + "You might want to delete and/or insert some corrections", + "so that I will find a matching right brace soon.", + "If you're confused by all this, try typing `I}' now." + ); + back_error(); + cur_tok = left_brace_token + '{'; + cur_cmd = left_brace_cmd; + cur_chr = '{'; + incr(align_state); + } +} + +/*tex + + The |scan_optional_equals| routine looks for an optional `\.=' sign preceded + by optional spaces; `\.{\\relax}' is not ignored here. + +*/ + +void scan_optional_equals(void) +{ + /*tex Get the next non-blank non-call token */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok != other_token + '=') + back_input(); +} + +/*tex + + Here is a procedure that sounds an alarm when mu and non-mu units are being + switched. + +*/ + +static void mu_error(void) +{ + print_err("Incompatible glue units"); + help1("I'm going to assume that 1mu=1pt when they're mixed."); + error(); +} + +/*tex + + The next routine `|scan_something_internal|' is used to fetch internal + numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}' + when expanding constructions like `\.{\\the\\toks0}' and + `\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int| + procedure, which calls |scan_something_internal|; on the other hand, + |scan_something_internal| also calls |scan_int|, for constructions like + `\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we have to + declare |scan_int| as a |forward| procedure. A few other procedures are also + declared at this point. + + \TeX\ doesn't know exactly what to expect when |scan_something_internal| + begins. For example, an integer or dimension or glue value could occur + immediately after `\.{\\hskip}'; and one can even say \.{\\the} with respect + to token lists in constructions like `\.{\\xdef\\o\{\\the\\output\}}'. On the + other hand, only integers are allowed after a construction like + `\.{\\count}'. To handle the various possibilities, |scan_something_internal| + has a |level| parameter, which tells the ``highest'' kind of quantity that + |scan_something_internal| is allowed to produce. Eight levels are + distinguished, namely |int_val|, |attr_val|, |dimen_val|, |glue_val|, + |mu_val|, |dir_val|, |ident_val|, and |tok_val|. + + The output of |scan_something_internal| (and of the other routines + |scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global + variable |cur_val|, and its level is put into |cur_val_level|. The highest + values of |cur_val_level| are special: |mu_val| is used only when |cur_val| + points to something in a ``muskip'' register, or to one of the three + parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip}; |ident_val| + is used only when |cur_val| points to a font identifier; |tok_val| is used + only when |cur_val| points to |null| or to the reference count of a token + list. The last two cases are allowed only when |scan_something_internal| is + called with |level=tok_val|. + + If the output is glue, |cur_val| will point to a glue specification, and the + reference count of that glue will have been updated to reflect this + reference; if the output is a nonempty token list, |cur_val| will point to + its reference count, but in this case the count will not have been updated. + Otherwise |cur_val| will contain the integer or scaled value in question. + +*/ + +/*tex The value returned by numeric scanners: */ + +int cur_val; + +/*tex Delcodes are sometimes 51 digits: */ + +int cur_val1; + +/*tex The level of this value: */ + +int cur_val_level; + +#define scanned_result(A,B) do { \ + cur_val=A; \ + cur_val_level=B; \ +} while (0) + +/*tex + + When a |glue_val| changes to a |dimen_val|, we use the width component of the + glue; there is no need to decrease the reference count, since it has not yet + been increased. When a |dimen_val| changes to an |int_val|, we use scaled + points so that the value doesn't actually change. And when a |mu_val| changes + to a |glue_val|, the value doesn't change either. + +*/ + +static void downgrade_cur_val(boolean delete_glue) +{ + if (cur_val_level == glue_val_level) { + halfword m = cur_val; + cur_val = width(m); + if (delete_glue) + flush_node(m); + } else if (cur_val_level == mu_val_level) { + mu_error(); + } + decr(cur_val_level); +} + +void negate_cur_val(boolean modify_glue) +{ + if (cur_val_level >= glue_val_level) { + if (modify_glue) { + /*tex We modify in-place. */ + } else { + cur_val = new_spec(cur_val); + } + negate(width(cur_val)); + negate(stretch(cur_val)); + negate(shrink(cur_val)); + } else { + negate(cur_val); + } +} + +/*tex + + Some of the internal items can be fetched both routines, and these have been + split off into the next routine, that returns true if the command code was + understood. + +*/ + +static boolean short_scan_something_internal(int cmd, int chr, int level, boolean negative) +{ + /*tex |chr_code| part of the operand token */ + halfword m; + /*tex general purpose index */ + halfword q; + /*tex index into |nest| */ + int p; + int save_cur_chr; + boolean succeeded = true; + m = chr; + switch (cmd) { + case assign_toks_cmd: + scanned_result(equiv(m), tok_val_level); + break; + case assign_int_cmd: + scanned_result(eqtb[m].cint, int_val_level); + break; + case assign_attr_cmd: + scanned_result(eqtb[m].cint, int_val_level); + break; + case assign_dimen_cmd: + scanned_result(eqtb[m].cint, dimen_val_level); + break; + case assign_glue_cmd: + scanned_result(equiv(m), glue_val_level); + break; + case assign_mu_glue_cmd: + scanned_result(equiv(m), mu_val_level); + break; + case assign_direction_cmd: + if (m == (int_base + line_direction_code)) { + m = int_base + text_direction_code; + } + scanned_result(eqtb[m].cint, int_val_level); + break; + case assign_dir_cmd: + if (m == (int_base + line_direction_code)) { + m = int_base + text_direction_code; + } + scanned_result(eqtb[m].cint, dir_val_level); + break; + case math_style_cmd: + scanned_result(m, int_val_level); + break; + case set_aux_cmd: + /*tex Fetch the |space_factor| or the |prev_depth|. */ + if (abs(cur_list.mode_field) != m) { + print_err("Improper "); + print_cmd_chr(set_aux_cmd, m); + help4( + "You can refer to \\spacefactor only in horizontal mode;", + "you can refer to \\prevdepth only in vertical mode; and", + "neither of these is meaningful inside \\write. So", + "I'm forgetting what you said and using zero instead." + ); + error(); + if (level != tok_val_level) + scanned_result(0, dimen_val_level); + else + scanned_result(0, int_val_level); + } else if (m == vmode) { + scanned_result(prev_depth_par, dimen_val_level); + } else { + scanned_result(space_factor_par, int_val_level); + } + break; + case set_prev_graf_cmd: + /*tex Fetch the |prev_graf| */ + if (cur_list.mode_field == 0) { + /*tex |prev_graf=0| within \.{\\write} */ + scanned_result(0, int_val_level); + } else { + p = nest_ptr; + while (abs(nest[p].mode_field) != vmode) + decr(p); + scanned_result(nest[p].pg_field, int_val_level); + } + break; + case set_page_int_cmd: + /*tex Fetch the |dead_cycles| or the |insert_penalties| */ + if (m == 0) + cur_val = dead_cycles; + else if (m == 2) + cur_val = interaction; + else + cur_val = insert_penalties; + cur_val_level = int_val_level; + break; + case set_page_dimen_cmd: + /*tex Fetch something on the |page_so_far|. */ + if ((page_contents == empty) && (!output_active)) { + if (m == 0) + cur_val = max_dimen; + else + cur_val = 0; + } else { + cur_val = page_so_far[m]; + } + cur_val_level = dimen_val_level; + break; + case set_tex_shape_cmd: + /*tex Fetch the |par_shape| size. */ + if (par_shape_par_ptr == null) + cur_val = 0; + else + cur_val = vinfo(par_shape_par_ptr + 1); + cur_val_level = int_val_level; + break; + case set_etex_shape_cmd: + /*tex Fetch a penalties array element. */ + scan_int(); + if ((equiv(m) == null) || (cur_val < 0)) { + cur_val = 0; + } else { + if (cur_val > penalty(equiv(m))) + cur_val = penalty(equiv(m)); + cur_val = penalty(equiv(m) + cur_val); + } + cur_val_level = int_val_level; + break; + case char_given_cmd: + case math_given_cmd: + case xmath_given_cmd: + scanned_result(cur_chr, int_val_level); + break; + case last_item_cmd: + /*tex + + Because the items in this case directly refer to |cur_chr|, it + needs to be saved and restored. + + */ + save_cur_chr = cur_chr; + cur_chr = chr; + /*tex + + Fetch an item in the current node, if appropriate. Here is where + \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\ } are implemented. + The reference count for \.{\\lastskip} will be updated later. + + We also handle \.{\\inputlineno} and \.{\\badness} here, because + they are legal in similar contexts. + + */ + if (m >= input_line_no_code) { + if (m >= eTeX_glue) { + /*tex Process an expression and |return|. */ + if (m < eTeX_mu) { + if (m == mu_to_glue_code) { + scan_mu_glue(); + }; + cur_val_level = glue_val_level; + } else if (m < eTeX_expr) { + if (m == glue_to_mu_code) { + scan_normal_glue(); + } + cur_val_level = mu_val_level; + } else { + cur_val_level = m - eTeX_expr + int_val_level; + scan_expr(); + } + /*tex + + This code for reducing |cur_val_level| and\slash or + negating the result is similar to the one for all the + other cases of |scan_something_internal|; we free a + glue_spec when needed. + + */ + while (cur_val_level > level) { + downgrade_cur_val(true); + } + if (negative) { + /*tex + + We get a new glue spec node with negated values and + the old intermediate is deleted. + + */ + negate_cur_val(true); + } + return succeeded; + } else if (m >= eTeX_dim) { + switch (m) { + case font_char_wd_code: + case font_char_ht_code: + case font_char_dp_code: + case font_char_ic_code: + scan_font_ident(); + q = cur_val; + scan_char_num(); + if (char_exists(q, cur_val)) { + switch (m) { + case font_char_wd_code: + cur_val = char_width(q, cur_val); + break; + case font_char_ht_code: + cur_val = char_height(q, cur_val); + break; + case font_char_dp_code: + cur_val = char_depth(q, cur_val); + break; + case font_char_ic_code: + cur_val = char_italic(q, cur_val); + break; + } + } else { + cur_val = 0; + } + break; + case par_shape_length_code: + case par_shape_indent_code: + case par_shape_dimen_code: + q = cur_chr - par_shape_length_code; + scan_int(); + if ((par_shape_par_ptr == null) || (cur_val <= 0)) { + cur_val = 0; + } else { + if (q == 2) { + q = cur_val % 2; + cur_val = (cur_val + q) / 2; + } + if (cur_val > vinfo(par_shape_par_ptr + 1)) + cur_val = vinfo(par_shape_par_ptr + 1); + cur_val = + varmem[par_shape_par_ptr + 2 * cur_val - q + 1].cint; + } + cur_val_level = dimen_val_level; + break; + case glue_stretch_code: + case glue_shrink_code: + scan_normal_glue(); + q = cur_val; + if (m == glue_stretch_code) + cur_val = stretch(q); + else + cur_val = shrink(q); + flush_node(q); + break; + } + cur_val_level = dimen_val_level; + } else { + switch (m) { + case input_line_no_code: + cur_val = line; + break; + case badness_code: + cur_val = last_badness; + break; + case luatex_version_code: + cur_val = get_luatexversion(); + break; + case last_saved_box_resource_index_code: + cur_val = last_saved_box_index; + break; + case last_saved_image_resource_index_code: + cur_val = last_saved_image_index; + break; + case last_saved_image_resource_pages_code: + cur_val = last_saved_image_pages; + break; + case last_x_pos_code: + cur_val = last_position.h; + break; + case last_y_pos_code: + cur_val = last_position.v; + break; + case random_seed_code: + cur_val = random_seed; + break; + case eTeX_version_code: + cur_val = eTeX_version; + break; + case eTeX_minor_version_code: + cur_val = eTeX_minor_version; + break; + case current_group_level_code: + cur_val = cur_level - level_one; + break; + case current_group_type_code: + cur_val = cur_group; + break; + case current_if_level_code: + q = cond_ptr; + cur_val = 0; + while (q != null) { + incr(cur_val); + q = vlink(q); + } + break; + case current_if_type_code: + if (cond_ptr == null) + cur_val = 0; + else if (cur_if < unless_code) + cur_val = cur_if + 1; + else + cur_val = -(cur_if - unless_code + 1); + break; + case current_if_branch_code: + if ((if_limit == or_code) || (if_limit == else_code)) + cur_val = 1; + else if (if_limit == fi_code) + cur_val = -1; + else + cur_val = 0; + break; + case glue_stretch_order_code: + case glue_shrink_order_code: + scan_normal_glue(); + q = cur_val; + if (m == glue_stretch_order_code) + cur_val = stretch_order(q); + else + cur_val = shrink_order(q); + flush_node(q); + break; + } + cur_val_level = int_val_level; + } + } else { + if (cur_chr == glue_val_level) + cur_val = zero_glue; + else + cur_val = 0; + if (cur_chr == last_node_type_code) { + cur_val_level = int_val_level; + if ((cur_list.tail_field == cur_list.head_field) + || (cur_list.mode_field == 0)) + cur_val = -1; + } else { + /*tex assumes identical values */ + cur_val_level = cur_chr; + } + if ((cur_list.tail_field != contrib_head) && + !is_char_node(cur_list.tail_field) && + (cur_list.mode_field != 0)) { + switch (cur_chr) { + case lastpenalty_code: + if (type(cur_list.tail_field) == penalty_node) + cur_val = penalty(cur_list.tail_field); + break; + case lastkern_code: + if (type(cur_list.tail_field) == kern_node) + cur_val = width(cur_list.tail_field); + break; + case lastskip_code: + if (type(cur_list.tail_field) == glue_node) + cur_val = cur_list.tail_field; + if (subtype(cur_list.tail_field) == mu_glue) + cur_val_level = mu_val_level; + break; + case last_node_type_code: + cur_val = visible_last_node_type(cur_list.tail_field); + break; + } + } else if ((cur_list.mode_field == vmode) && (cur_list.tail_field == cur_list.head_field)) { + switch (cur_chr) { + case lastpenalty_code: + cur_val = last_penalty; + break; + case lastkern_code: + cur_val = last_kern; + break; + case lastskip_code: + if (last_glue != max_halfword) + cur_val = last_glue; + break; + case last_node_type_code: + cur_val = last_node_type; + break; + } + } + } + cur_chr = save_cur_chr; + break; + default: + succeeded = false; + } + if (succeeded) { + while (cur_val_level > level) { + /*tex Convert |cur_val| to a lower level. */ + downgrade_cur_val(false); + } + /*tex + + Fix the reference count, if any, and negate |cur_val| if |negative|. + If |cur_val| points to a glue specification at this point, the + reference count for the glue does not yet include the reference by + |cur_val|. If |negative| is |true|, |cur_val_level| is known to be + |<=mu_val|. + + */ + if (negative) { + /*tex We create a new (negated) glue spec and keep the old one. */ + negate_cur_val(false); + } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) { + cur_val = new_spec(cur_val); + } + } + return succeeded; +} + +/*tex + + First, here is a short routine that is called from lua code. All the real + work is delegated to |short_scan_something_internal| that is shared between + this routine and |scan_something_internal|. + +*/ + +void scan_something_simple(halfword cmd, halfword subitem) +{ + /*tex Negative is never true. */ + if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) { + /*tex Complain that |texlib| can not do this; give zero result. */ + print_err("You can't use `"); + print_cmd_chr((quarterword) cmd, subitem); + tprint("' as tex library index"); + help1("I'm forgetting what you said and using zero instead."); + error(); + scanned_result(0, int_val_level); + } +} + +/*tex + + OK, we're ready for |scan_something_internal| itself. A second parameter, + |negative|, is set |true| if the value that is found should be negated. It is + assumed that |cur_cmd| and |cur_chr| represent the first token of the + internal quantity to be scanned; an error will be signalled if + |cur_cmd<min_internal| or |cur_cmd>max_internal|. + +*/ + +/*tex Fetch an internal parameter: */ + +void scan_something_internal(int level, boolean negative) +{ + /*tex |chr_code| part of the operand token */ + halfword m; + /*tex accumulators */ + int n, k; + RESTART: + m = cur_chr; + if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) { + switch (cur_cmd) { + case def_char_code_cmd: + /*tex Fetch a character code from some table */ + scan_char_num(); + if (m == math_code_base) { + cur_val1 = get_math_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == lc_code_base) { + cur_val1 = get_lc_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == uc_code_base) { + cur_val1 = get_uc_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == sf_code_base) { + cur_val1 = get_sf_code(cur_val); + scanned_result(cur_val1, int_val_level); + } else if (m == cat_code_base) { + cur_val1 = get_cat_code(cat_code_table_par, cur_val); + scanned_result(cur_val1, int_val_level); + } else { + confusion("def_char"); + } + break; + case def_del_code_cmd: + case extdef_del_code_cmd: + /*tex Fetch a character code from some table. */ + scan_char_num(); + cur_val1 = get_del_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + break; + case extdef_math_code_cmd: + /*tex Fetch an extended math code table value. */ + scan_char_num(); + cur_val1 = get_math_code_num(cur_val); + scanned_result(cur_val1, int_val_level); + break; + case toks_register_cmd: + case set_font_cmd: + case def_font_cmd: + case letterspace_font_cmd: + case copy_font_cmd: + /*tex Fetch a token list or font identifier, provided that |level=tok_val|. */ + if (level != tok_val_level) { + print_err("Missing number, treated as zero"); + help3( + "A number should have been here; I inserted `0'.", + "(If you can't figure out why I needed to see a number,", + "look up `weird error' in the index to The TeXbook.)" + ); + back_error(); + scanned_result(0, dimen_val_level); + } else if (cur_cmd == toks_register_cmd) { + scan_register_num(); + m = toks_base + cur_val; + scanned_result(equiv(m), tok_val_level); + } else { + back_input(); + scan_font_ident(); + scanned_result(font_id_base + cur_val, ident_val_level); + } + break; + case set_font_id_cmd: + scan_int(); + scanned_result(font_id_base + cur_val, ident_val_level); + break; + case def_family_cmd: + /*tex Fetch a math font identifier. */ + scan_char_num(); + cur_val1 = fam_fnt(cur_val, m); + scanned_result(font_id_base + cur_val1, ident_val_level); + break; + case set_math_param_cmd: + /*tex Fetch a math parameter. */ + cur_val1 = cur_chr; + get_token(); + if (cur_cmd != math_style_cmd) { + print_err("Missing math style, treated as \\displaystyle"); + help1("A style should have been here; I inserted `\\displaystyle'."); + cur_val = display_style; + back_error(); + } else { + cur_val = cur_chr; + } + if (cur_val1 < math_param_first_mu_glue) { + if (cur_val1 == math_param_radical_degree_raise) { + cur_val1 = get_math_param(cur_val1, cur_chr); + scanned_result(cur_val1, int_val_level); + } else { + cur_val1 = get_math_param(cur_val1, cur_chr); + scanned_result(cur_val1, dimen_val_level); + } + } else { + cur_val1 = get_math_param(cur_val1, cur_chr); + if (cur_val1 == thin_mu_skip_code) + cur_val1 = thin_mu_skip_par; + else if (cur_val1 == med_mu_skip_code) + cur_val1 = med_mu_skip_par; + else if (cur_val1 == thick_mu_skip_code) + cur_val1 = thick_mu_skip_par; + scanned_result(cur_val1, mu_val_level); + } + break; + case assign_box_dir_cmd: + scan_register_num(); + m = cur_val; + if (box(m) != null) + cur_val = box_dir(box(m)); + else + cur_val = 0; + cur_val_level = dir_val_level; + break; + case set_box_dimen_cmd: + /*tex Fetch a box dimension. */ + scan_register_num(); + if (box(cur_val) == null) + cur_val = 0; + else + cur_val = varmem[box(cur_val) + m].cint; + cur_val_level = dimen_val_level; + break; + case assign_font_dimen_cmd: + /*tex Fetch a font dimension. */ + get_font_dimen(); + break; + case assign_font_int_cmd: + /*tex Fetch a font integer. */ + scan_font_ident(); + if (m == 0) { + scanned_result(hyphen_char(cur_val), int_val_level); + } else if (m == 1) { + scanned_result(skew_char(cur_val), int_val_level); + } else if (m == no_lig_code) { + scanned_result(test_no_ligatures(cur_val), int_val_level); + } else { + n = cur_val; + scan_char_num(); + k = cur_val; + switch (m) { + case lp_code_base: + scanned_result(get_lp_code(n, k), int_val_level); + break; + case rp_code_base: + scanned_result(get_rp_code(n, k), int_val_level); + break; + case ef_code_base: + scanned_result(get_ef_code(n, k), int_val_level); + break; + case tag_code: + scanned_result(get_tag_code(n, k), int_val_level); + break; + } + } + break; + case register_cmd: + /*tex Fetch a register */ + scan_register_num(); + switch (m) { + case int_val_level: + cur_val = count(cur_val); + break; + case attr_val_level: + cur_val = attribute(cur_val); + break; + case dimen_val_level: + cur_val = dimen(cur_val); + break; + case glue_val_level: + cur_val = skip(cur_val); + break; + case mu_val_level: + cur_val = mu_skip(cur_val); + break; + } + cur_val_level = m; + break; + case ignore_spaces_cmd: + /*tex Trap unexpandable primitives. */ + if (cur_chr == 1) { + /*tex + + Reset |cur_tok| for unexpandable primitives, goto + restart. This block deals with unexpandable + \.{\\primitive} appearing at a spot where an integer or + an internal values should have been found. It fetches the + next token then resets |cur_cmd|, |cur_cs|, and + |cur_tok|, based on the primitive value of that token. No + expansion takes place, because the next token may be all + sorts of things. This could trigger further expansion + creating new errors. + + */ + get_token(); + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + } else { + cur_cmd = relax_cmd; + cur_chr = 0; + cur_tok = cs_token_flag + frozen_relax; + cur_cs = frozen_relax; + } + goto RESTART; + } + break; + case hyph_data_cmd: + switch (cur_chr) { + case 0: + case 1: + goto DEFAULT; + break; + case 2: + cur_val = get_pre_hyphen_char(language_par); + cur_val_level = int_val_level; + break; + case 3: + cur_val = get_post_hyphen_char(language_par); + cur_val_level = int_val_level; + break; + case 4: + cur_val = get_pre_exhyphen_char(language_par); + cur_val_level = int_val_level; + break; + case 5: + cur_val = get_post_exhyphen_char(language_par); + cur_val_level = int_val_level; + break; + case 6: + cur_val = get_hyphenation_min(language_par); + cur_val_level = int_val_level; + break; + case 7: + scan_int(); + cur_val = get_hj_code(language_par,cur_val); + cur_val_level = int_val_level; + break; + } + break; + default: + DEFAULT: + /*tex Complain that \.{\\the} can not do this; give zero result. */ + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + tprint("' after \\the"); + help1("I'm forgetting what you said and using zero instead."); + error(); + if (level != tok_val_level) + scanned_result(0, dimen_val_level); + else + scanned_result(0, int_val_level); + break; + } + while (cur_val_level > level) { + /*tex Convert |cur_val| to a lower level. */ + downgrade_cur_val(false); + } + /*tex + + Fix the reference count, if any, and negate |cur_val| if |negative|. + If |cur_val| points to a glue specification at this point, the + reference count for the glue does not yet include the reference by + |cur_val|. If |negative| is |true|, |cur_val_level| is known to be + |<=mu_val|. + + */ + if (negative) { + /*tex We create a new (negated) glue spec and keep the old one. */ + negate_cur_val(false); + } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) { + cur_val = new_spec(cur_val); + } + } +} + +/*tex + + It is nice to have routines that say what they do, so the original + |scan_eight_bit_int| is superceded by |scan_register_num| and + |scan_mark_num|. It may become split up even further in the future. + + Many of the |restricted classes| routines are the essentially the same except + for the upper limit and the error message, so it makes sense to combine these + all into one function. + +*/ + +void scan_limited_int(int max, const char *name) +{ + char hlp[80]; + scan_int(); + if ((cur_val < 0) || (cur_val > max)) { + if (name == NULL) { + snprintf(hlp, 80, "Since I expected to read a number between 0 and %d,", max); + print_err("Bad number"); + } else { + char msg[80]; + snprintf(hlp, 80, "A %s must be between 0 and %d.", name, max); + snprintf(msg, 80, "Bad %s", name); + print_err(msg); + } + help2(hlp, "I changed this one to zero."); + int_error(cur_val); + cur_val = 0; + } +} + +void scan_fifteen_bit_int(void) +{ + scan_real_fifteen_bit_int(); + cur_val = ((cur_val / 0x1000) * 0x1000000) + (((cur_val % 0x1000) / 0x100) * 0x10000) + (cur_val % 0x100); +} + +void scan_fifty_one_bit_int(void) +{ + int iiii; + scan_int(); + if ((cur_val < 0) || (cur_val > 0777777777)) { + print_err("Bad delimiter code"); + help2( + "A numeric delimiter (first part) must be between 0 and 2^{27}-1.", + "I changed this one to zero." + ); + int_error(cur_val); + cur_val = 0; + } + iiii = cur_val; + scan_int(); + if ((cur_val < 0) || (cur_val > 0xFFFFFF)) { + print_err("Bad delimiter code"); + help2( + "A numeric delimiter (second part) must be between 0 and 2^{24}-1.", + "I changed this one to zero." + ); + int_error(cur_val); + cur_val = 0; + } + cur_val1 = cur_val; + cur_val = iiii; +} + +/*tex + + An integer number can be preceded by any number of spaces and `\.+' or `\.-' + signs. Then comes either a decimal constant (i.e., radix 10), an octal + constant (i.e., radix 8, preceded by~'), a hexadecimal constant (radix 16, + preceded by~"), an alphabetic constant (preceded by~`), or an internal + variable. After scanning is complete, |cur_val| will contain the answer, + which must be at most $2^{31}-1=2147483647$ in absolute value. The value of + |radix| is set to 10, 8, or 16 in the cases of decimal, octal, or hexadecimal + constants, otherwise |radix| is set to zero. An optional space follows a + constant. + +*/ + +/*tex |scan_int| sets this to 8, 10, 16, or zero */ + +int radix; + +/*tex + + The |scan_int| routine is used also to scan the integer part of a fraction; + for example, the `\.3' in `\.{3.14159}' will be found by |scan_int|. The + |scan_dimen| routine assumes that |cur_tok=point_token| after the integer + part of such a fraction has been scanned by |scan_int|, and that the decimal + point has been backed up to be scanned again. + +*/ + +/*tex Sets |cur_val| to an integer: */ + +void scan_int(void) +{ + /*tex should the answer be negated? */ + boolean negative; + /*tex |$2^{31}$ / radix|, the threshold of danger */ + int m; + /*tex the digit just scanned */ + int d; + /*tex have no digits appeared? */ + boolean vacuous; + /*tex has an error message been issued? */ + boolean OK_so_far; + radix = 0; + OK_so_far = true; + /*tex Get the next non-blank non-sign token; set |negative| appropriately. */ + negative = false; + do { + /*tex Get the next non-blank non-call token. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '-') { + negative = !negative; + cur_tok = other_token + '+'; + } + } while (cur_tok == other_token + '+'); + + RESTART: + if (cur_tok == alpha_token) { + /*tex + + Scan an alphabetic character code into |cur_val|. A space is ignored + after an alphabetic character constant, so that such constants behave + like numeric ones. + + */ + /*tex Suppress macro expansion: */ + get_token(); + if (cur_tok < cs_token_flag) { + cur_val = cur_chr; + if (cur_cmd <= right_brace_cmd) { + if (cur_cmd == right_brace_cmd) + incr(align_state); + else + decr(align_state); + } + } else { + /*tex The value of a csname in this context is its name. */ + str_number txt = cs_text(cur_tok - cs_token_flag); + if (is_active_cs(txt)) + cur_val = active_cs_value(txt); + else if (single_letter(txt)) + cur_val = pool_to_unichar(str_string(txt)); + else + cur_val = (biggest_char + 1); + } + if (cur_val > biggest_char) { + print_err("Improper alphabetic constant"); + help2( + "A one-character control sequence belongs after a ` mark.", + "So I'm essentially inserting \\0 here." + ); + cur_val = '0'; + back_error(); + } else { + /*tex Scan an optional space. */ + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + } + + } else if (cur_tok == cs_token_flag + frozen_primitive) { + /*tex + + Reset |cur_tok| for unexpandable primitives, goto restart This block + deals with unexpandable \.{\\primitive} appearing at a spot where an + integer or an internal values should have been found. It fetches the + next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on + the primitive value of that token. No expansion takes place, because + the next token may be all sorts of things. This could trigger further + expansion creating new errors. + + */ + get_token(); + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = token_val(cur_cmd, cur_chr); + } else { + cur_cmd = relax_cmd; + cur_chr = 0; + cur_tok = cs_token_flag + frozen_relax; + cur_cs = frozen_relax; + } + goto RESTART; + } else if (cur_cmd == math_style_cmd) { + cur_val = cur_chr; + } else if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + scan_something_internal(int_val_level, false); + } else { + /*tex Scan a numeric constant. */ + radix = 10; + m = 214748364; + if (cur_tok == octal_token) { + radix = 8; + m = 02000000000; + get_x_token(); + } else if (cur_tok == hex_token) { + radix = 16; + m = 01000000000; + get_x_token(); + } + vacuous = true; + cur_val = 0; + /*tex Accumulate the constant until |cur_tok| is not a suitable digit. */ + while (1) { + if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token) && (cur_tok <= nine_token)) { + d = cur_tok - zero_token; + } else if (radix == 16) { + if ((cur_tok <= A_token + 5) && (cur_tok >= A_token)) { + d = cur_tok - A_token + 10; + } else if ((cur_tok <= other_A_token + 5) && (cur_tok >= other_A_token)) { + d = cur_tok - other_A_token + 10; + } else { + break; + } + } else { + break; + } + vacuous = false; + if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) { + if (OK_so_far) { + print_err("Number too big"); + help2( + "I can only go up to 2147483647='17777777777=\"7FFFFFFF,", + "so I'm using that number instead of yours." + ); + error(); + cur_val = infinity; + OK_so_far = false; + } + } else { + cur_val = cur_val * radix + d; + } + get_x_token(); + } + if (vacuous) { + /*tex Express astonishment that no number was here */ + print_err("Missing number, treated as zero"); + help3( + "A number should have been here; I inserted `0'.", + "(If you can't figure out why I needed to see a number,", + "look up `weird error' in the index to The TeXbook.)" + ); + back_error(); + } else if (cur_cmd != spacer_cmd) { + back_input(); + } + } + if (negative) + negate(cur_val); +} + +/*tex + + The following code is executed when |scan_something_internal| was called + asking for |mu_val|, when we really wanted a ``mudimen'' instead of + ``muglue.'' + +*/ + +static void coerce_glue(void) +{ + int v; + if (cur_val_level >= glue_val_level) { + v = width(cur_val); + flush_node(cur_val); + cur_val = v; + } +} + +/*tex + + The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to a + |scaled| value, i.e., an integral number of sp. One of its main tasks is + therefore to interpret the abbreviations for various kinds of units and to + convert measurements to scaled points. + + There are three parameters: |mu| is |true| if the finite units must be + `\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed; |inf| is + |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}' are + permitted; and |shortcut| is |true| if |cur_val| already contains an integer + and only the units need to be considered. + + The order of infinity that was found in the case of infinite glue is returned + in the global variable |cur_order|. + +*/ + +/*tex The order of infinity found by |scan_dimen|: */ + +int cur_order; + +/*tex + + Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen| may + begin with |scan_int|. This explains why it is convenient to use |scan_int| + also for the integer part of a decimal fraction. + + Several branches of |scan_dimen| work with |cur_val| as an integer and with + an auxiliary fraction |f|, so that the actual quantity of interest is + $|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked'' + representation is put into the single word |cur_val|, which suddenly switches + significance from |integer| to |scaled|. + + The necessary conversion factors can all be specified exactly as fractions + whose numerator and denominator add to 32768 or less. According to the + definitions here, $\rm2660\,dd\approx1000.33297\,mm$; this agrees well with + the value $\rm1000.333\,mm$ cited by Bosshard \^{Bosshard, Hans Rudolf} in + {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). The Didot + point has been newly standardized in 1978; it's now exactly $\rm + 1\,nd=0.375\,mm$. Conversion uses the equation + $0.375=21681/20320/72.27\cdot25.4$. The new Cicero follows the new Didot + point; $\rm 1\,nc=12\,nd$. These would lead to the ratios $21681/20320$ and + $65043/5080$, respectively. The closest approximations supported by the + algorithm would be $11183/10481$ and $1370/107$. In order to maintain the + relation $\rm 1\,nc=12\,nd$, we pick the ratio $685/642$ for $\rm nd$, + however. + +*/ + +static void scan_dimen_mu_error(void) { + print_err("Illegal unit of measure (mu inserted)"); + help4( + "The unit of measurement in math glue must be mu.", + "To recover gracefully from this error, it's best to", + "delete the erroneous units; e.g., type `2' to delete", + "two letters. (See Chapter 27 of The TeXbook.)" + ); + error(); +} + +static void scan_dimen_unknown_unit_error(void) { + print_err("Illegal unit of measure (pt inserted)"); + help6( + "Dimensions can be in units of em, ex, in, pt, pc,", + "cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!", + "I'll assume that you meant to say pt, for printer's points.", + "To recover gracefully from this error, it's best to", + "delete the erroneous units; e.g., type `2' to delete", + "two letters. (See Chapter 27 of The TeXbook.)" + ); + error(); +} + +static void scan_dimen_out_of_range_error(void) { + print_err("Dimension too large"); + help2( + "I can't work with sizes bigger than about 19 feet.", + "Continue and I'll use the largest value I can." + ); + error(); +} + +#define set_conversion(A,B) do { num=(A); denom=(B); } while(0) + +/*tex + + This function sets |cur_val| to a dimension. It could be optimized a bit more + (but not now, something for luatex > 1). + +*/ + +void scan_dimen(boolean mu, boolean inf, boolean shortcut) +{ + /*tex should the answer be negated? */ + boolean negative = false; + boolean is_true = false; + /*tex numerator of a fraction whose denominator is $2^{16}$ */ + int f = 0; + /*tex conversion ratio for the scanned units */ + int num = 0; + int denom = 0; + /*tex top of decimal digit stack */ + halfword q; + /*tex an internal dimension */ + scaled v; + /*tex temporary storage of |cur_val| */ + int save_cur_val; + arith_error = false; + cur_order = normal; + if (!shortcut) { + /*tex Get the next non-blank non-sign. */ + do { + /*tex Get the next non-blank non-call token. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == minus_token) { + negative = !negative; + cur_tok = plus_token; + } + } while (cur_tok == plus_token); + if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + /*tex Fetch an internal dimension and |goto attach_sign|, or fetch an internal integer. */ + if (mu) { + scan_something_internal(mu_val_level, false); + coerce_glue(); + if (cur_val_level == mu_val_level) { + goto ATTACH_SIGN; + } else if (cur_val_level != int_val_level) { + mu_error(); + } + } else { + scan_something_internal(dimen_val_level, false); + if (cur_val_level == dimen_val_level) { + goto ATTACH_SIGN; + } + } + } else { + back_input(); + if (cur_tok == continental_point_token) { + cur_tok = point_token; + } + if (cur_tok != point_token) { + scan_int(); + } else { + radix = 10; + cur_val = 0; + } + if (cur_tok == continental_point_token) { + cur_tok = point_token; + } + if ((radix == 10) && (cur_tok == point_token)) { + /*tex + + Scan decimal fraction. When the following code is executed, + we have |cur_tok=point_token|, but this token has been backed + up using |back_input|; we must first discard it. It turns out + that a decimal point all by itself is equivalent to + `\.{0.0}'. Let's hope people don't use that fact. + + */ + int k = 0; + halfword p = null; + int kk; + /*tex The |point_token| is being re-scanned. */ + get_token(); + while (1) { + get_x_token(); + if ((cur_tok > nine_token) || (cur_tok < zero_token)) + break; + if (k < 17) { + /*tex Digits for |k>=17| cannot affect the result. */ + q = get_avail(); + set_token_link(q, p); + set_token_info(q, cur_tok - zero_token); + p = q; + incr(k); + } + } + for (kk = k; kk >= 1; kk--) { + dig[kk - 1] = token_info(p); + q = p; + p = token_link(p); + free_avail(q); + } + f = round_decimals(k); + if (cur_cmd != spacer_cmd) { + back_input(); + } + } + } + } + if (cur_val < 0) { + /*tex In this case |f=0|. */ + negative = !negative; + negate(cur_val); + } + /*tex + + Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there + are |x| sp per unit; |goto attach_sign| if the units are internal. Now + comes the harder part: At this point in the program, |cur_val| is a + nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1; + we want to multiply the sum of these two quantities by the appropriate + factor, based on the specified units, in order to produce a |scaled| + result, and we want to do the calculation with fixed point arithmetic + that does not overflow. + + */ + if (inf) { + if (scan_keyword("fi")) { + cur_order = sfi; + if (scan_keyword("l")) { + cur_order = fil; + if (scan_keyword("l")) { + cur_order = fill; + if (scan_keyword("l")) { + cur_order = filll; + } + } + } + goto ATTACH_FRACTION; + } + } + /*tex + + Scan for (u)units that are internal dimensions; |goto attach_sign| with + |cur_val| set if found. + + */ + save_cur_val = cur_val; + /*tex + + Get the next non-blank non-call; a pitty if just backed up the input. + + */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if ((cur_cmd < min_internal_cmd) || (cur_cmd > max_internal_cmd)) { + back_input(); + } else { + /*tex |math_given_cmd|, |xmath_given_cmd| and |last_item_cmd} */ + if (mu) { + scan_something_internal(mu_val_level, false); + coerce_glue(); + if (cur_val_level != mu_val_level) { + mu_error(); + } + } else { + scan_something_internal(dimen_val_level, false); + } + v = cur_val; + goto FOUND; + } + /*tex Bah |true| forces to split the unit scanner. */ + if (mu) { + /*tex Scan for (m)\.{mu} units and |goto attach_fraction|. */ + if (! scan_keyword("mu")) { + scan_dimen_mu_error(); + } + goto ATTACH_FRACTION; + } else if (scan_keyword("em")) { + v = quad(get_cur_font()); + } else if (scan_keyword("ex")) { + v = x_height(get_cur_font()); + } else if (scan_keyword("px")) { + v = px_dimen_par; + } else { + goto PICKUP_UNIT; + } + /*tex Scan an optional space (after em, ex or px) */ + get_x_token(); + if (cur_cmd != spacer_cmd) { + back_input(); + } + FOUND: + cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000)); + goto ATTACH_SIGN; + /*tex + + Scan for (a)all other units and adjust |cur_val| and |f| accordingly; + |goto done| in the case of scaled points. + + */ + PICKUP_UNIT: + if (scan_keyword("pt")) { + /*tex The easy case: */ + goto SCALE_VALUE; + } else if (scan_keyword("mm")) { + set_conversion(7227, 2540); + goto SCALE_VALUE; + } else if (scan_keyword("cm")) { + set_conversion(7227, 254); + goto SCALE_VALUE; + } else if (scan_keyword("sp")) { + goto DONE; + } else if (scan_keyword("bp")) { + set_conversion(7227, 7200); + goto SCALE_VALUE; + } else if (scan_keyword("in")) { + set_conversion(7227, 100); + goto SCALE_VALUE; + } else if (scan_keyword("dd")) { + set_conversion(1238, 1157); + goto SCALE_VALUE; + } else if (scan_keyword("cc")) { + set_conversion(14856, 1157); + goto SCALE_VALUE; + } else if (scan_keyword("pc")) { + set_conversion(12, 1); + goto SCALE_VALUE; + } else if (scan_keyword("nd")) { + set_conversion(685, 642); + goto SCALE_VALUE; + } else if (scan_keyword("nc")) { + set_conversion(1370, 107); + goto SCALE_VALUE; + } else if (!is_true && scan_keyword("true")) { + is_true = true; + goto PICKUP_UNIT; + } + /*tex Complain about unknown unit and |goto done2|. */ + scan_dimen_unknown_unit_error(); + goto BAD_NEWS; + SCALE_VALUE: + /*tex Adjust |f| for the magnification ratio. */ + if (is_true) { + /*tex Maybe at some point we will drop mag completely, even in \DVI\ mode. */ + if (output_mode_used <= OMODE_DVI) { + prepare_mag(); + if (mag_par != 1000) { + cur_val = xn_over_d(cur_val, 1000, mag_par); + f = (1000 * f + 0200000 * tex_remainder) / mag_par; + cur_val = cur_val + (f / 0200000); + f = f % 0200000; + } + } else { + /*tex in \PDF\ mode we're always |true|. */ + one_true_inch = one_inch; + } + } + if (num) { + cur_val = xn_over_d(cur_val, num, denom); + f = (num * f + 0200000 * tex_remainder) / denom; + cur_val = cur_val + (f / 0200000); + f = f % 0200000; + } + BAD_NEWS: + ATTACH_FRACTION: + if (cur_val >= 040000) { + arith_error = true; + } else { + cur_val = cur_val * unity + f; + } + DONE: + /*tex Scan an optional space; this happens too often. */ + get_x_token(); + if (cur_cmd != spacer_cmd) { + back_input(); + } + ATTACH_SIGN: + if (arith_error || (abs(cur_val) >= 010000000000)) { + /*tex Report that this dimension is out of range. */ + scan_dimen_out_of_range_error(); + cur_val = max_dimen; + arith_error = false; + } + if (negative) { + negate(cur_val); + } +} + +/*tex + + The final member of \TeX's value-scanning trio is |scan_glue|, which makes + |cur_val| point to a glue specification. The reference count of that glue + spec will take account of the fact that |cur_val| is pointing to~it. + + The |level| parameter should be either |glue_val| or |mu_val|. + + Since |scan_dimen| was so much more complex than |scan_int|, we might expect + |scan_glue| to be even worse. But fortunately, it is very simple, since most + of the work has already been done. + +*/ + +void scan_glue(int level) +{ + /*tex should the answer be negated? */ + boolean negative = false; + /*tex new glue specification */ + halfword q = null; + /*tex does |level=mu_val|? */ + boolean mu = (level == mu_val_level); + /*tex Get the next non-blank non-sign. */ + do { + /*tex Get the next non-blank non-call token. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == minus_token) { + negative = !negative; + cur_tok = plus_token; + } + } while (cur_tok == plus_token); + if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { + scan_something_internal(level, negative); + if (cur_val_level >= glue_val_level) { + if (cur_val_level != level) + mu_error(); + return; + } + if (cur_val_level == int_val_level) + scan_dimen(mu, false, true); + else if (level == mu_val_level) + mu_error(); + } else { + back_input(); + scan_dimen(mu, false, false); + if (negative) + negate(cur_val); + } + /*tex + + Create a new glue specification whose width is |cur_val|; scan for its + stretch and shrink components. + + */ + q = new_spec(zero_glue); + width(q) = cur_val; + if (scan_keyword("plus")) { + scan_dimen(mu, true, false); + stretch(q) = cur_val; + stretch_order(q) = (quarterword) cur_order; + } + if (scan_keyword("minus")) { + scan_dimen(mu, true, false); + shrink(q) = cur_val; + shrink_order(q) = (quarterword) cur_order; + } + cur_val = q; +} + +/*tex + + This procedure is supposed to scan something like `\.{\\skip\\count12}', + i.e., whatever can follow `\.{\\the}', and it constructs a token list + containing something like `\.{-3.0pt minus 0.5fill}'. + +*/ + +halfword the_toks(void) +{ + /*tex holds |selector| setting */ + int old_setting; + /*tex used for copying a token list */ + halfword p, q, r; + /*tex value of |cur_chr| */ + int c; + str_number s; + halfword retval; + /*tex Handle \.{\\unexpanded} or \.{\\detokenize} and |return|. */ + if (odd(cur_chr)) { + c = cur_chr; + scan_general_text(); + if (c == 1) { + return cur_val; + } else { + old_setting = selector; + selector = new_string; + p = get_avail(); + set_token_link(p, token_link(temp_token_head)); + token_show(p); + flush_list(p); + selector = old_setting; + s = make_string(); + retval = str_toks(str_lstring(s)); + flush_str(s); + return retval; + } + } + get_x_token(); + scan_something_internal(tok_val_level, false); + if (cur_val_level >= ident_val_level) { + /*tex Copy the token list */ + p = temp_token_head; + set_token_link(p, null); + if (cur_val_level == ident_val_level) { + store_new_token(cs_token_flag + cur_val); + } else if (cur_val != null) { + /*tex Do not copy the reference count! */ + r = token_link(cur_val); + while (r != null) { + fast_store_new_token(token_info(r)); + r = token_link(r); + } + } + return p; + } else { + old_setting = selector; + selector = new_string; + switch (cur_val_level) { + case int_val_level: + print_int(cur_val); + break; + case attr_val_level: + print_int(cur_val); + break; + case dir_val_level: + print_dir_par(cur_val); + break; + case dimen_val_level: + print_scaled(cur_val); + tprint("pt"); + break; + case glue_val_level: + print_spec(cur_val, "pt"); + flush_node(cur_val); + break; + case mu_val_level: + print_spec(cur_val, "mu"); + flush_node(cur_val); + break; + } + selector = old_setting; + s = make_string(); + retval = str_toks(str_lstring(s)); + flush_str(s); + return retval; + } +} + +str_number the_scanned_result(void) +{ + /*tex holds |selector| setting */ + int old_setting; + /*tex return value */ + str_number r; + old_setting = selector; + selector = new_string; + if (cur_val_level >= ident_val_level) { + if (cur_val != null) { + show_token_list(token_link(cur_val), null, -1); + r = make_string(); + } else { + r = get_nullstr(); + } + } else { + switch (cur_val_level) { + case int_val_level: + print_int(cur_val); + break; + case attr_val_level: + print_int(cur_val); + break; + case dir_val_level: + print_dir_par(cur_val); + break; + case dimen_val_level: + print_scaled(cur_val); + tprint("pt"); + break; + case glue_val_level: + print_spec(cur_val, "pt"); + flush_node(cur_val); + break; + case mu_val_level: + print_spec(cur_val, "mu"); + flush_node(cur_val); + break; + } + r = make_string(); + } + selector = old_setting; + return r; +} + +/*tex + + The following routine is used to implement `\.{\\fontdimen} |n| |f|'. The + boolean parameter |writing| is set |true| if the calling program intends to + change the parameter value. + +*/ + +static void font_param_error(int f) +{ + print_err("Font "); + print_esc(font_id_text(f)); + tprint(" has only "); + print_int(font_params(f)); + tprint(" fontdimen parameters"); + help2( + "To increase the number of font parameters, you must", + "use \\fontdimen immediately after the \\font is loaded." + ); + error(); +} + +void set_font_dimen(void) +{ + internal_font_number f; + int n; + scan_int(); + n = cur_val; + scan_font_ident(); + f = cur_val; + if (n <= 0) { + font_param_error(f); + } else if (n > font_params(f)) { + if (font_used(f)) { + font_param_error(f); + } else { + /*tex Increase the number of parameters in the font. */ + do { + set_font_param(f, (font_params(f) + 1), 0); + } while (n != font_params(f)); + } + } + scan_optional_equals(); + scan_normal_dimen(); + set_font_param(f, n, cur_val); +} + +void get_font_dimen(void) +{ + internal_font_number f; + /*tex The parameter number: */ + int n; + scan_int(); + n = cur_val; + scan_font_ident(); + f = cur_val; + /*tex Initialize return value: */ + cur_val = 0; + if (n <= 0) { + font_param_error(f); + goto EXIT; + } else if (n > font_params(f)) { + if (font_used(f)) { + font_param_error(f); + goto EXIT; + } else { + /*tex Increase the number of parameters in the font. */ + do { + set_font_param(f, (font_params(f) + 1), 0); + } while (n != font_params(f)); + } + } + cur_val = font_param(f, n); + EXIT: + scanned_result(cur_val, dimen_val_level); +} + +/*tex + + Here's a similar procedure that returns a pointer to a rule node. This + routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule}; + therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store + the default rule dimensions in the node, then to override them if + `\.{height}' or `\.{width}' or `\.{depth}' specifications are found (in any + order). + +*/ + +halfword scan_rule_spec(void) +{ + /*tex |width|, |depth|, and |height| all equal |null_flag| now */ + halfword q; + if (cur_cmd == no_vrule_cmd) { + q = new_rule(empty_rule); + cur_cmd = vrule_cmd; + } else if (cur_cmd == no_hrule_cmd) { + q = new_rule(empty_rule); + cur_cmd = hrule_cmd; + } else { + q = new_rule(normal_rule); + } + if (cur_cmd == vrule_cmd) { + width(q) = default_rule; + rule_dir(q) = body_direction_par; + } else { + height(q) = default_rule; + depth(q) = 0; + rule_dir(q) = text_direction_par; + } + RESWITCH: + if (scan_keyword("width")) { + scan_normal_dimen(); + width(q) = cur_val; + goto RESWITCH; + } + if (scan_keyword("height")) { + scan_normal_dimen(); + height(q) = cur_val; + goto RESWITCH; + } + if (scan_keyword("depth")) { + scan_normal_dimen(); + depth(q) = cur_val; + goto RESWITCH; + } + return q; +} + +/*tex Declare procedures that scan font-related stuff. */ + +void scan_font_ident(void) +{ + internal_font_number f; + halfword m; + /*tex Get the next non-blank non-call. */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + if ((cur_cmd == def_font_cmd) || (cur_cmd == letterspace_font_cmd) || (cur_cmd == copy_font_cmd)) { + f = get_cur_font(); + } else if (cur_cmd == set_font_cmd) { + f = cur_chr; + set_font_touched(f, 1); + } else if (cur_cmd == def_family_cmd) { + m = cur_chr; + scan_math_family_int(); + f = fam_fnt(cur_val, m); + set_font_touched(f, 1); + } else { + print_err("Missing font identifier"); + help2( + "I was looking for a control sequence whose", + "current meaning has been defined by \\font." + ); + back_error(); + f = null_font; + } + cur_val = f; +} + +/*tex + + The |scan_general_text| procedure is much like |scan_toks(false,false)|, but + will be invoked via |expand|, i.e., recursively. + + The token list (balanced text) created by |scan_general_text| begins at + |link(temp_token_head)| and ends at |cur_val|. (If |cur_val=temp_token_head|, + the list is empty.) + +*/ + +void scan_general_text(void) +{ + /*tex Here we save |scanner_status|: */ + int s; + /*tex Here we save |warning_index|: */ + halfword w; + /*tex Here we save |def_ref|: */ + halfword d; + /*tex The tail of the token list being built: */ + halfword p; + /*tex The new node being added to the token list via |store_new_token|: */ + halfword q; + /*tex The number of unmatched left braces: */ + halfword unbalance; + s = scanner_status; + w = warning_index; + d = def_ref; + scanner_status = absorbing; + warning_index = cur_cs; + p = get_avail(); + def_ref = p; + set_token_ref_count(def_ref, 0); + p = def_ref; + /*tex Remove the compulsory left brace. */ + scan_left_brace(); + unbalance = 1; + while (1) { + get_token(); + if (cur_tok < right_brace_limit) { + if (cur_cmd < right_brace_cmd) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + break; + } + } + store_new_token(cur_tok); + } + q = token_link(def_ref); + /*tex Discard reference count. */ + free_avail(def_ref); + if (q == null) + cur_val = temp_token_head; + else + cur_val = p; + set_token_link(temp_token_head, q); + scanner_status = s; + warning_index = w; + def_ref = d; +} + +/*tex + + The |get_x_or_protected| procedure is like |get_x_token| except that + protected macros are not expanded. It sets |cur_cmd|, |cur_chr|, + |cur_tok|,and expands non-protected macros. + +*/ + +void get_x_or_protected(void) +{ + + while (1) { + get_token(); + if (cur_cmd <= max_command_cmd) + return; + if ((cur_cmd >= call_cmd) && (cur_cmd < end_template_cmd)) { + if (token_info(token_link(cur_chr)) == protected_token) + return; + } + expand(); + } +} + +/*tex + + |scan_toks|. This function returns a pointer to the tail of a new token list, + and it also makes |def_ref| point to the reference count at the head of that + list. + + There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| is + true, the goal is to create the token list for a macro definition; otherwise + the goal is to create the token list for some other \TeX\ primitive: + \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase}, \.{\\uppercase}, + \.{\\message}, \.{\\errmessage}, \.{\\write}, or \.{\\special}. In the latter + cases a left brace must be scanned next; this left brace will not be part of + the token list, nor will the matching right brace that comes at the end. If + |xpand| is false, the token list will simply be copied from the input using + |get_token|. Otherwise all expandable tokens will be expanded until + unexpandable tokens are left, except that the results of expanding + `\.{\\the}' are not expanded further. If both |macro_def| and |xpand| are + true, the expansion applies only to the macro body (i.e., to the material + following the first |left_brace| character). + + The value of |cur_cs| when |scan_toks| begins should be the |eqtb| address of + the control sequence to display in ``runaway'' error messages. + +*/ + +halfword scan_toks(boolean macro_def, boolean xpand) +{ + /*tex token representing the highest parameter number */ + halfword t; + /*tex saved token */ + halfword s; + /*tex tail of the token list being built */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex number of unmatched left braces */ + halfword unbalance; + /*tex possible `\.{\#\{}' token */ + halfword hash_brace; + if (macro_def) + scanner_status = defining; + else + scanner_status = absorbing; + warning_index = cur_cs; + p = get_avail(); + def_ref = p; + set_token_ref_count(def_ref, 0); + p = def_ref; + hash_brace = 0; + t = zero_token; + if (macro_def) { + /*tex Scan and build the parameter part of the macro definition. */ + while (1) { + /*tex Set |cur_cmd|, |cur_chr|, |cur_tok|: */ + get_token(); + if (cur_tok < right_brace_limit) + break; + if (cur_cmd == mac_param_cmd) { + /*tex + + If the next character is a parameter number, make + |cur_tok| a |match| token; but if it is a left brace, + store `|left_brace|, |end_match|', set |hash_brace|, and + |goto done|. + + */ + s = match_token + cur_chr; + get_token(); + if (cur_cmd == left_brace_cmd) { + hash_brace = cur_tok; + store_new_token(cur_tok); + store_new_token(end_match_token); + goto DONE; + } + if (t == nine_token) { + print_err("You already have nine parameters"); + help1("I'm going to ignore the # sign you just used."); + error(); + } else { + incr(t); + if (cur_tok != t) { + print_err("Parameters must be numbered consecutively"); + help2( + "I've inserted the digit you should have used after the #.", + "Type `1' to delete what you did use." + ); + back_error(); + } + cur_tok = s; + } + } + store_new_token(cur_tok); + } + store_new_token(end_match_token); + if (cur_cmd == right_brace_cmd) { + /*tex Express shock at the missing left brace; |goto found|. */ + print_err("Missing { inserted"); + incr(align_state); + help2( + "Where was the left brace? You said something like `\\def\\a}',", + "which I'm going to interpret as `\\def\\a{}'." + ); + error(); + goto FOUND; + } + + } else { + /*tex Remove the compulsory left brace. */ + scan_left_brace(); + } + DONE: + /*tex Scan and build the body of the token list; |goto found| when finished. */ + unbalance = 1; + while (1) { + if (xpand) { + /*tex + + Expand the next part of the input. Here we insert an entire token + list created by |the_toks| without expanding it further. + + */ + while (1) { + get_next(); + if (cur_cmd >= call_cmd) { + if (token_info(token_link(cur_chr)) == protected_token) { + cur_cmd = relax_cmd; + cur_chr = no_expand_flag; + } + } + if (cur_cmd <= max_command_cmd) + break; + if (cur_cmd != the_cmd) { + expand(); + } else { + q = the_toks(); + if (token_link(temp_token_head) != null) { + set_token_link(p, token_link(temp_token_head)); + p = q; + } + } + } + x_token(); + } else { + get_token(); + } + if (cur_tok < right_brace_limit) { + if (cur_cmd < right_brace_cmd) { + incr(unbalance); + } else { + decr(unbalance); + if (unbalance == 0) + goto FOUND; + } + } else if (cur_cmd == mac_param_cmd) { + if (macro_def) { + /*tex Look for parameter number or \.{\#\#}. */ + s = cur_tok; + if (xpand) + get_x_token(); + else + get_token(); + if (cur_cmd != mac_param_cmd) { + if ((cur_tok <= zero_token) || (cur_tok > t)) { + print_err("Illegal parameter number in definition of "); + sprint_cs(warning_index); + help3( + "You meant to type ## instead of #, right?", + "Or maybe a } was forgotten somewhere earlier, and things", + "are all screwed up? I'm going to assume that you meant ##." + ); + back_error(); + cur_tok = s; + } else { + cur_tok = out_param_token - '0' + cur_chr; + } + } + } + } + store_new_token(cur_tok); + } + FOUND: + scanner_status = normal; + if (hash_brace != 0) + store_new_token(hash_brace); + return p; +} + +/*tex + + Here we declare two trivial procedures in order to avoid mutually recursive + procedures with parameters. + +*/ + +void scan_normal_glue(void) +{ + scan_glue(glue_val_level); +} + +void scan_mu_glue(void) +{ + scan_glue(mu_val_level); +} + +/*tex + + The |scan_expr| procedure scans and evaluates an expression. Evaluating an + expression is a recursive process: When the left parenthesis of a + subexpression is scanned we descend to the next level of recursion; the + previous level is resumed with the matching right parenthesis. + +*/ + +typedef enum { + /*tex \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */ + expr_none = 0, + /*tex \.( $\langle\it expr\rangle$ \.+ seen */ + expr_add = 1, + /*tex \.( $\langle\it expr\rangle$ \.- seen */ + expr_sub = 2, + /*tex $\langle\it term\rangle$ \.* seen */ + expr_mult = 3, + /*tex $\langle\it term\rangle$ \./ seen */ + expr_div = 4, + /*tex $\langle\it term\rangle$ \.* $\langle\it factor\rangle$ \./ seen */ + expr_scale = 5, +} expression_states; + +/*tex + + We want to make sure that each term and (intermediate) result is in the + proper range. Integer values must not exceed |infinity| ($2^{31}-1$) in + absolute value, dimensions must not exceed |max_dimen| ($2^{30}-1$). We avoid + the absolute value of an integer, because this might fail for the value + $-2^{31}$ using 32-bit arithmetic. + +*/ + +/* Clear a number or dimension and set |arith_error|: */ + +#define num_error(A) do { \ + arith_error=true; \ + A=0; \ +} while (0) + +/*tex Clear a glue spec and set |arith_error|: */ + +#define glue_error(A) do { \ + arith_error=true; \ + reset_glue_to_zero(A); \ +} while (0) + +#define normalize_glue(A) do { \ + if (stretch(A)==0) stretch_order(A)=normal; \ + if (shrink(A)==0) shrink_order(A)=normal; \ +} while (0) + +/*tex + + Parenthesized subexpressions can be inside expressions, and this nesting has + a stack. Seven local variables represent the top of the expression stack: |p| + points to pushed-down entries, if any; |l| specifies the type of expression + currently beeing evaluated; |e| is the expression so far and |r| is the state + of its evaluation; |t| is the term so far and |s| is the state of its + evaluation; finally |n| is the numerator for a combined multiplication and + division, if any. + +*/ + +#define expr_add_sub(A,B,C) add_or_sub((A),(B),(C),(r==expr_sub)) +#define expr_a(A,B) expr_add_sub((A),(B),max_dimen) + +/*tex + + The function |add_or_sub(x,y,max_answer,negative)| computes the sum (for + |negative=false|) or difference (for |negative=true|) of |x| and |y|, + provided the absolute value of the result does not exceed |max_answer|. + +*/ + +inline static int add_or_sub(int x, int y, int max_answer, boolean negative) +{ + int a; + if (negative) + negate(y); + if (x >= 0) { + if (y <= max_answer - x) + a = x + y; + else + num_error(a); + } else if (y >= -max_answer - x) { + a = x + y; + } else { + num_error(a); + } + return a; +} + +#define expr_m(A) A = nx_plus_y((A),f,0) +#define expr_d(A) A=quotient((A),f) + +/*tex + + The function |quotient(n,d)| computes the rounded quotient $q=\lfloor + n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive. + +*/ + +inline static int quotient(int n, int d) +{ + /*tex Should the answer be negated? */ + boolean negative; + /*tex The answer: */ + int a; + if (d == 0) { + num_error(a); + } else { + if (d > 0) { + negative = false; + } else { + negate(d); + negative = true; + } + if (n < 0) { + negate(n); + negative = !negative; + } + a = n / d; + n = n - a * d; + /*tex Avoid certain compiler optimizations! */ + d = n - d; + if (d + n >= 0) + incr(a); + if (negative) + negate(a); + } + return a; +} + +#define expr_s(A) A=fract((A),n,f,max_dimen) + +/*tex + + Finally, the function |fract(x,n,d,max_answer)| computes the integer + $q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive and + the result does not exceed |max_answer|. We can't use floating point + arithmetic since the routine must produce identical results in all cases; and + it would be too dangerous to multiply by~|n| and then divide by~|d|, in + separate operations, since overflow might well occur. Hence this subroutine + simulates double precision arithmetic, somewhat analogous to Metafont's + |make_fraction| and |take_fraction| routines. + +*/ + +int fract(int x, int n, int d, int max_answer) +{ + /*tex should the answer be negated? */ + boolean negative; + /*tex the answer */ + int a; + /*tex a proper fraction */ + int f; + /*tex smallest integer such that |2*h>=d| */ + int h; + /*tex intermediate remainder */ + int r; + /*tex temp variable */ + int t; + if (d == 0) + goto TOO_BIG; + a = 0; + if (d > 0) { + negative = false; + } else { + negate(d); + negative = true; + } + if (x < 0) { + negate(x); + negative = !negative; + } else if (x == 0) { + goto DONE; + } + if (n < 0) { + negate(n); + negative = !negative; + } + t = n / d; + if (t > max_answer / x) + goto TOO_BIG; + a = t * x; + n = n - t * d; + if (n == 0) + goto FOUND; + t = x / d; + if (t > (max_answer - a) / n) + goto TOO_BIG; + a = a + t * n; + x = x - t * d; + if (x == 0) + goto FOUND; + if (x < n) { + t = x; + x = n; + n = t; + } + /*tex + + Now |0<n<=x<d| and we compute $f=\lfloor xn/d+{1\over2}\rfloor$. The loop + here preserves the following invariant relations between |f|, |x|, |n|, + and~|r|: (i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor + x_0n_0/d+{1\over2}\rfloor$; (ii)~|-d<=r<0<n<=x<d|, where $x_0$, $n_0$ are + the original values of~$x$ and $n$. + + Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|, + because the latter could overflow. + + */ + f = 0; + r = (d / 2) - d; + h = -r; + while (1) { + if (odd(n)) { + r = r + x; + if (r >= 0) { + r = r - d; + incr(f); + } + } + n = n / 2; + if (n == 0) + break; + if (x < h) { + x = x + x; + } else { + t = x - d; + x = t + x; + f = f + n; + if (x < n) { + if (x == 0) + break; + t = x; + x = n; + n = t; + } + } + } + if (f > (max_answer - a)) + goto TOO_BIG; + a = a + f; + FOUND: + if (negative) + negate(a); + goto DONE; + TOO_BIG: + num_error(a); + DONE: + return a; +} + +/*tex Scans and evaluates an expression: */ + +static void scan_expr(void) +{ + /*tex saved values of |arith_error| */ + boolean a, b; + /*tex type of expression */ + int l; + /*tex state of expression so far */ + int r; + /*tex state of term so far */ + int s; + /*tex next operation or type of next factor */ + int o; + /*tex expression so far */ + int e; + /*tex term so far */ + int t; + /*tex current factor */ + int f; + /*tex numerator of combined multiplication and division */ + int n; + /*tex top of expression stack */ + halfword p; + /*tex for stack manipulations */ + halfword q; + l = cur_val_level; + a = arith_error; + b = false; + p = null; + /*tex Scan and evaluate an expression |e| of type |l|. */ + RESTART: + r = expr_none; + e = 0; + s = expr_none; + t = 0; + n = 0; + CONTINUE: + if (s == expr_none) + o = l; + else + o = int_val_level; + /*tex + + Scan a factor |f| of type |o| or start a subexpression. Get the next + non-blank non-call token. + + */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if (cur_tok == other_token + '(') { + /*tex Push the expression stack and |goto restart|. */ + q = new_node(expr_node, 0); + vlink(q) = p; + expr_type(q) = (quarterword) l; + expr_state(q) = (quarterword) (4 * s + r); + expr_e_field(q) = e; + expr_t_field(q) = t; + expr_n_field(q) = n; + p = q; + l = o; + goto RESTART; + } + back_input(); + if ((o == int_val_level) || (o == attr_val_level)) + scan_int(); + else if (o == dimen_val_level) + scan_normal_dimen(); + else if (o == glue_val_level) + scan_normal_glue(); + else + scan_mu_glue(); + f = cur_val; + + FOUND: + /*tex + + Scan the next operator and set |o| and Get the next non-blank non-call + token. + + */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + + if (cur_tok == other_token + '+') { + o = expr_add; + } else if (cur_tok == other_token + '-') { + o = expr_sub; + } else if (cur_tok == other_token + '*') { + o = expr_mult; + } else if (cur_tok == other_token + '/') { + o = expr_div; + } else { + o = expr_none; + if (p == null) { + if (cur_cmd != relax_cmd) + back_input(); + } else if (cur_tok != other_token + ')') { + print_err("Missing ) inserted for expression"); + help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't."); + back_error(); + } + } + arith_error = b; + /*tex Make sure that |f| is in the proper range. */ + if (((l == int_val_level) || (l == attr_val_level)) || (s > expr_sub)) { + if ((f > infinity) || (f < -infinity)) + num_error(f); + } else if (l == dimen_val_level) { + if (abs(f) > max_dimen) + num_error(f); + } else { + if ((abs(width(f)) > max_dimen) || (abs(stretch(f)) > max_dimen) || (abs(shrink(f)) > max_dimen)) + glue_error(f); + } + /*tex Cases for evaluation of the current term. */ + switch (s) { + case expr_none: + /*tex + + Applying the factor |f| to the partial term |t| (with the + operator |s|) is delayed until the next operator |o| has been + scanned. Here we handle the first factor of a partial term. A + glue spec has to be copied unless the next operator is a right + parenthesis; this allows us later on to simply modify the glue + components. + + */ + t = f; + if ((l >= glue_val_level) && (o != expr_none)) { + /*tex Do we really need to copy here? */ + normalize_glue(t); + } else { + t = f; + } + break; + case expr_mult: + /*tex + + If a multiplication is followed by a division, the two operations + are combined into a `scaling' operation. Otherwise the term |t| + is multiplied by the factor |f|. + + */ + if (o == expr_div) { + n = f; + o = expr_scale; + } else if ((l == int_val_level) || (l == attr_val_level)) { + t = mult_integers(t, f); + } else if (l == dimen_val_level) { + expr_m(t); + } else { + expr_m(width(t)); + expr_m(stretch(t)); + expr_m(shrink(t)); + } + break; + case expr_div: + /*tex Here we divide the term |t| by the factor |f|. */ + if (l < glue_val_level) { + expr_d(t); + } else { + expr_d(width(t)); + expr_d(stretch(t)); + expr_d(shrink(t)); + } + break; + case expr_scale: + /*tex Here the term |t| is multiplied by the quotient $n/f$. */ + if ((l == int_val_level) || (l == attr_val_level)) { + t = fract(t, n, f, infinity); + } else if (l == dimen_val_level) { + expr_s(t); + } else { + expr_s(width(t)); + expr_s(stretch(t)); + expr_s(shrink(t)); + } + break; + } + if (o > expr_sub) { + s = o; + } else { + /*tex + + Evaluate the current expression. When a term |t| has been completed + it is copied to, added to, or subtracted from the expression |e|. + + */ + s = expr_none; + if (r == expr_none) { + e = t; + } else if ((l == int_val_level) || (l == attr_val_level)) { + e = expr_add_sub(e, t, infinity); + } else if (l == dimen_val_level) { + e = expr_a(e, t); + } else { + /*tex + + Compute the sum or difference of two glue specs. We know that + |stretch_order(e)>normal| implies |stretch(e)<>0| and + |shrink_order(e)>normal| implies |shrink(e)<>0|. + + */ + width(e) = expr_a(width(e), width(t)); + if (stretch_order(e) == stretch_order(t)) { + stretch(e) = expr_a(stretch(e), stretch(t)); + } else if ((stretch_order(e) < stretch_order(t)) && (stretch(t) != 0)) { + stretch(e) = stretch(t); + stretch_order(e) = stretch_order(t); + } + if (shrink_order(e) == shrink_order(t)) { + shrink(e) = expr_a(shrink(e), shrink(t)); + } else if ((shrink_order(e) < shrink_order(t)) && (shrink(t) != 0)) { + shrink(e) = shrink(t); + shrink_order(e) = shrink_order(t); + } + flush_node(t); + normalize_glue(e); + } + r = o; + } + b = arith_error; + if (o != expr_none) + goto CONTINUE; + if (p != null) { + /*tex Pop the expression stack and |goto found|. */ + f = e; + q = p; + e = expr_e_field(q); + t = expr_t_field(q); + n = expr_n_field(q); + s = expr_state(q) / 4; + r = expr_state(q) % 4; + l = expr_type(q); + p = vlink(q); + flush_node(q); + goto FOUND; + } + + if (b) { + print_err("Arithmetic overflow"); + help2( + "I can't evaluate this expression,", + "since the result is out of range." + ); + error(); + if (l >= glue_val_level) { + reset_glue_to_zero(e); + } else { + e = 0; + } + } + arith_error = a; + cur_val = e; + cur_val_level = l; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/scanning.w b/Build/source/texk/web2c/luatexdir/tex/scanning.w deleted file mode 100644 index 7bed4d29016..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/scanning.w +++ /dev/null @@ -1,2621 +0,0 @@ -% scanning.w -% -% Copyright 2009-2012 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - -#include "ptexlib.h" - -@ @c -static void scan_expr(void); - -@ Let's turn now to some procedures that \TeX\ calls upon frequently to digest -certain kinds of patterns in the input. Most of these are quite simple; -some are quite elaborate. Almost all of the routines call |get_x_token|, -which can cause them to be invoked recursively. - -The |scan_left_brace| routine is called when a left brace is supposed to be -the next non-blank token. (The term ``left brace'' means, more precisely, -a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to -appear before the |left_brace|. - -@c -void scan_left_brace(void) -{ /* reads a mandatory |left_brace| */ - /* Get the next non-blank non-relax non-call token */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - - if (cur_cmd != left_brace_cmd) { - print_err("Missing { inserted"); - help4("A left brace was mandatory here, so I've put one in.", - "You might want to delete and/or insert some corrections", - "so that I will find a matching right brace soon.", - "If you're confused by all this, try typing `I}' now."); - back_error(); - cur_tok = left_brace_token + '{'; - cur_cmd = left_brace_cmd; - cur_chr = '{'; - incr(align_state); - } -} - -@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded -by optional spaces; `\.{\\relax}' is not ignored here. - -@c -void scan_optional_equals(void) -{ - /* Get the next non-blank non-call token */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok != other_token + '=') - back_input(); -} - -@ Here is a procedure that sounds an alarm when mu and non-mu units -are being switched. - -@c -static void mu_error(void) -{ - print_err("Incompatible glue units"); - help1("I'm going to assume that 1mu=1pt when they're mixed."); - error(); -} - -@ The next routine `|scan_something_internal|' is used to fetch internal -numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}' -when expanding constructions like `\.{\\the\\toks0}' and -`\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int| -procedure, which calls |scan_something_internal|; on the other hand, -|scan_something_internal| also calls |scan_int|, for constructions like -`\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we -have to declare |scan_int| as a |forward| procedure. A few other -procedures are also declared at this point. - -\TeX\ doesn't know exactly what to expect when -|scan_something_internal| begins. For example, an integer or -dimension or glue value could occur immediately after `\.{\\hskip}'; -and one can even say \.{\\the} with respect to token lists in -constructions like `\.{\\xdef\\o\{\\the\\output\}}'. On the other -hand, only integers are allowed after a construction like -`\.{\\count}'. To handle the various possibilities, -|scan_something_internal| has a |level| parameter, which tells the -``highest'' kind of quantity that |scan_something_internal| is allowed -to produce. Eight levels are distinguished, namely |int_val|, -|attr_val|, |dimen_val|, |glue_val|, |mu_val|, |dir_val|, |ident_val|, -and |tok_val|. - -The output of |scan_something_internal| (and of the other routines -|scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global -variable |cur_val|, and its level is put into |cur_val_level|. The highest -values of |cur_val_level| are special: |mu_val| is used only when -|cur_val| points to something in a ``muskip'' register, or to one of the -three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip}; -|ident_val| is used only when |cur_val| points to a font identifier; -|tok_val| is used only when |cur_val| points to |null| or to the reference -count of a token list. The last two cases are allowed only when -|scan_something_internal| is called with |level=tok_val|. - -If the output is glue, |cur_val| will point to a glue specification, and -the reference count of that glue will have been updated to reflect this -reference; if the output is a nonempty token list, |cur_val| will point to -its reference count, but in this case the count will not have been updated. -Otherwise |cur_val| will contain the integer or scaled value in question. - -@c -int cur_val; /* value returned by numeric scanners */ -int cur_val1; /* delcodes are sometimes 51 digits */ -int cur_val_level; /* the ``level'' of this value */ - -#define scanned_result(A,B) do { \ - cur_val=A; \ - cur_val_level=B; \ -} while (0) - -@ When a |glue_val| changes to a |dimen_val|, we use the width component -of the glue; there is no need to decrease the reference count, since it -has not yet been increased. When a |dimen_val| changes to an |int_val|, -we use scaled points so that the value doesn't actually change. And when a -|mu_val| changes to a |glue_val|, the value doesn't change either. - -@c -static void downgrade_cur_val(boolean delete_glue) -{ - if (cur_val_level == glue_val_level) { - halfword m = cur_val; - cur_val = width(m); - if (delete_glue) - flush_node(m); - } else if (cur_val_level == mu_val_level) { - mu_error(); - } - decr(cur_val_level); -} - -/* -void negate_cur_val(boolean delete_glue) -{ - if (cur_val_level >= glue_val_level) { - halfword m = cur_val; - cur_val = new_spec(m); - if (delete_glue) - flush_node(m); - negate(width(cur_val)); - negate(stretch(cur_val)); - negate(shrink(cur_val)); - } else { - negate(cur_val); - } -} -*/ - -void negate_cur_val(boolean modify_glue) -{ - if (cur_val_level >= glue_val_level) { - if (modify_glue) { - /* we modify in-place */ - } else { - cur_val = new_spec(cur_val); - } - negate(width(cur_val)); - negate(stretch(cur_val)); - negate(shrink(cur_val)); - } else { - negate(cur_val); - } -} - -@ Some of the internal items can be fetched both routines, -and these have been split off into the next routine, that -returns true if the command code was understood - -@c -static boolean short_scan_something_internal(int cmd, int chr, int level, - boolean negative) -{ - halfword m; /* |chr_code| part of the operand token */ - halfword q; /* general purpose index */ - int p; /* index into |nest| */ - int save_cur_chr; - boolean succeeded = true; - m = chr; - switch (cmd) { - case assign_toks_cmd: - scanned_result(equiv(m), tok_val_level); - break; - case assign_int_cmd: - scanned_result(eqtb[m].cint, int_val_level); - break; - case assign_attr_cmd: - scanned_result(eqtb[m].cint, int_val_level); - break; - case assign_dir_cmd: - if (m == (int_base + line_direction_code)) { - m = int_base + text_direction_code; - } - scanned_result(eqtb[m].cint, dir_val_level); - break; - case assign_dimen_cmd: - scanned_result(eqtb[m].cint, dimen_val_level); - break; - case assign_glue_cmd: - scanned_result(equiv(m), glue_val_level); - break; - case assign_mu_glue_cmd: - scanned_result(equiv(m), mu_val_level); - break; - case math_style_cmd: - scanned_result(m, int_val_level); - break; - case set_aux_cmd: - /* Fetch the |space_factor| or the |prev_depth| */ - if (abs(cur_list.mode_field) != m) { - print_err("Improper "); - print_cmd_chr(set_aux_cmd, m); - help4("You can refer to \\spacefactor only in horizontal mode;", - "you can refer to \\prevdepth only in vertical mode; and", - "neither of these is meaningful inside \\write. So", - "I'm forgetting what you said and using zero instead."); - error(); - if (level != tok_val_level) - scanned_result(0, dimen_val_level); - else - scanned_result(0, int_val_level); - } else if (m == vmode) { - scanned_result(prev_depth_par, dimen_val_level); - } else { - scanned_result(space_factor_par, int_val_level); - } - break; - case set_prev_graf_cmd: - /* Fetch the |prev_graf| */ - if (cur_list.mode_field == 0) { - scanned_result(0, int_val_level); /* |prev_graf=0| within \.{\\write} */ - } else { - p = nest_ptr; - while (abs(nest[p].mode_field) != vmode) - decr(p); - scanned_result(nest[p].pg_field, int_val_level); - } - break; - case set_page_int_cmd: - /* Fetch the |dead_cycles| or the |insert_penalties| */ - if (m == 0) - cur_val = dead_cycles; - else if (m == 2) - cur_val = interaction; /* interactionmode */ - else - cur_val = insert_penalties; - cur_val_level = int_val_level; - break; - case set_page_dimen_cmd: - /* Fetch something on the |page_so_far| */ - if ((page_contents == empty) && (!output_active)) { - if (m == 0) - cur_val = max_dimen; - else - cur_val = 0; - } else { - cur_val = page_so_far[m]; - } - cur_val_level = dimen_val_level; - break; - case set_tex_shape_cmd: - /* Fetch the |par_shape| size */ - if (par_shape_par_ptr == null) - cur_val = 0; - else - cur_val = vinfo(par_shape_par_ptr + 1); - cur_val_level = int_val_level; - break; - case set_etex_shape_cmd: - /* Fetch a penalties array element */ - scan_int(); - if ((equiv(m) == null) || (cur_val < 0)) { - cur_val = 0; - } else { - if (cur_val > penalty(equiv(m))) - cur_val = penalty(equiv(m)); - cur_val = penalty(equiv(m) + cur_val); - } - cur_val_level = int_val_level; - break; - case char_given_cmd: - case math_given_cmd: - case xmath_given_cmd: - scanned_result(cur_chr, int_val_level); - break; - case last_item_cmd: - /* Because the items in this case directly refer to |cur_chr|, - it needs to be saved and restored */ - save_cur_chr = cur_chr; - cur_chr = chr; - /* Fetch an item in the current node, if appropriate */ - /* Here is where \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\ } are - implemented. The reference count for \.{\\lastskip} will be updated later. - - We also handle \.{\\inputlineno} and \.{\\badness} here, because they are - legal in similar contexts. */ - - if (m >= input_line_no_code) { - if (m >= eTeX_glue) { - /* Process an expression and |return| */ - if (m < eTeX_mu) { - switch (m) { - case mu_to_glue_code: - scan_mu_glue(); - break; - }; /* there are no other cases */ - cur_val_level = glue_val_level; - } else if (m < eTeX_expr) { - switch (m) { - case glue_to_mu_code: - scan_normal_glue(); - break; - } /* there are no other cases */ - cur_val_level = mu_val_level; - } else { - cur_val_level = m - eTeX_expr + int_val_level; - scan_expr(); - } - /* This code for reducing |cur_val_level| and\slash or negating the - result is similar to the one for all the other cases of - |scan_something_internal|; we free a glue_spec when needed. - */ - while (cur_val_level > level) { - downgrade_cur_val(true); - } - if (negative) { - /* - we get a new glue spec node with negated values and the old - intermediate is deleted - */ - negate_cur_val(true); - } - return succeeded; - - } else if (m >= eTeX_dim) { - switch (m) { - case font_char_wd_code: - case font_char_ht_code: - case font_char_dp_code: - case font_char_ic_code: - scan_font_ident(); - q = cur_val; - scan_char_num(); - if (char_exists(q, cur_val)) { - switch (m) { - case font_char_wd_code: - cur_val = char_width(q, cur_val); - break; - case font_char_ht_code: - cur_val = char_height(q, cur_val); - break; - case font_char_dp_code: - cur_val = char_depth(q, cur_val); - break; - case font_char_ic_code: - cur_val = char_italic(q, cur_val); - break; - } /* there are no other cases */ - } else { - cur_val = 0; - } - break; - case par_shape_length_code: - case par_shape_indent_code: - case par_shape_dimen_code: - q = cur_chr - par_shape_length_code; - scan_int(); - if ((par_shape_par_ptr == null) || (cur_val <= 0)) { - cur_val = 0; - } else { - if (q == 2) { - q = cur_val % 2; - cur_val = (cur_val + q) / 2; - } - if (cur_val > vinfo(par_shape_par_ptr + 1)) - cur_val = vinfo(par_shape_par_ptr + 1); - cur_val = - varmem[par_shape_par_ptr + 2 * cur_val - q + 1].cint; - } - cur_val_level = dimen_val_level; - break; - case glue_stretch_code: - case glue_shrink_code: - scan_normal_glue(); - q = cur_val; - if (m == glue_stretch_code) - cur_val = stretch(q); - else - cur_val = shrink(q); - flush_node(q); - break; - } /* there are no other cases */ - cur_val_level = dimen_val_level; - } else { - switch (m) { - case input_line_no_code: - cur_val = line; - break; - case badness_code: - cur_val = last_badness; - break; - case luatex_version_code: - cur_val = get_luatexversion(); - break; - case last_saved_box_resource_index_code: - cur_val = last_saved_box_index; - break; - case last_saved_image_resource_index_code: - cur_val = last_saved_image_index; - break; - case last_saved_image_resource_pages_code: - cur_val = last_saved_image_pages; - break; - case last_x_pos_code: - cur_val = last_position.h; - break; - case last_y_pos_code: - cur_val = last_position.v; - break; - case random_seed_code: - cur_val = random_seed; - break; - case eTeX_version_code: - cur_val = eTeX_version; - break; - case eTeX_minor_version_code: - cur_val = eTeX_minor_version; - break; - case current_group_level_code: - cur_val = cur_level - level_one; - break; - case current_group_type_code: - cur_val = cur_group; - break; - case current_if_level_code: - q = cond_ptr; - cur_val = 0; - while (q != null) { - incr(cur_val); - q = vlink(q); - } - break; - case current_if_type_code: - if (cond_ptr == null) - cur_val = 0; - else if (cur_if < unless_code) - cur_val = cur_if + 1; - else - cur_val = -(cur_if - unless_code + 1); - break; - case current_if_branch_code: - if ((if_limit == or_code) || (if_limit == else_code)) - cur_val = 1; - else if (if_limit == fi_code) - cur_val = -1; - else - cur_val = 0; - break; - case glue_stretch_order_code: - case glue_shrink_order_code: - scan_normal_glue(); - q = cur_val; - if (m == glue_stretch_order_code) - cur_val = stretch_order(q); - else - cur_val = shrink_order(q); - flush_node(q); - break; - } /* there are no other cases */ - cur_val_level = int_val_level; - } - } else { - if (cur_chr == glue_val_level) - cur_val = zero_glue; /* a pointer */ - else - cur_val = 0; - if (cur_chr == last_node_type_code) { - cur_val_level = int_val_level; - if ((cur_list.tail_field == cur_list.head_field) - || (cur_list.mode_field == 0)) - cur_val = -1; - } else { - cur_val_level = cur_chr; /* assumes identical values */ - } - if ((cur_list.tail_field != contrib_head) && - !is_char_node(cur_list.tail_field) && - (cur_list.mode_field != 0)) { - switch (cur_chr) { - case lastpenalty_code: - if (type(cur_list.tail_field) == penalty_node) - cur_val = penalty(cur_list.tail_field); - break; - case lastkern_code: - if (type(cur_list.tail_field) == kern_node) - cur_val = width(cur_list.tail_field); - break; - case lastskip_code: - if (type(cur_list.tail_field) == glue_node) - // cur_val = new_spec(cur_list.tail_field); - cur_val = cur_list.tail_field; - if (subtype(cur_list.tail_field) == mu_glue) - cur_val_level = mu_val_level; - break; - case last_node_type_code: - cur_val = visible_last_node_type(cur_list.tail_field); - break; - } /* there are no other cases */ - } else if ((cur_list.mode_field == vmode) - && (cur_list.tail_field == cur_list.head_field)) { - switch (cur_chr) { - case lastpenalty_code: - cur_val = last_penalty; - break; - case lastkern_code: - cur_val = last_kern; - break; - case lastskip_code: - if (last_glue != max_halfword) - cur_val = last_glue; - break; - case last_node_type_code: - cur_val = last_node_type; - break; - } /* there are no other cases */ - } - } - cur_chr = save_cur_chr; - break; - default: - succeeded = false; - } - if (succeeded) { - while (cur_val_level > level) { - /* Convert |cur_val| to a lower level */ - downgrade_cur_val(false); - } - /* Fix the reference count, if any, and negate |cur_val| if |negative| */ - /* If |cur_val| points to a glue specification at this point, the reference - count for the glue does not yet include the reference by |cur_val|. - If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|. - */ - if (negative) { - /* we create a new (negated) glue spec and keep the old one */ - negate_cur_val(false); - } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) { - cur_val = new_spec(cur_val); - } - } - return succeeded; -} - -@ First, here is a short routine that is called from lua code. All -the real work is delegated to |short_scan_something_internal| that -is shared between this routine and |scan_something_internal|. - -@c -void scan_something_simple(halfword cmd, halfword subitem) -{ - /* negative is never true */ - if (!short_scan_something_internal(cmd, subitem, tok_val_level, false)) { - /* Complain that |texlib| can not do this; give zero result */ - print_err("You can't use `"); - print_cmd_chr((quarterword) cmd, subitem); - tprint("' as tex library index"); - help1("I'm forgetting what you said and using zero instead."); - error(); - scanned_result(0, int_val_level); - } -} - -@ OK, we're ready for |scan_something_internal| itself. A second parameter, -|negative|, is set |true| if the value that is found should be negated. -It is assumed that |cur_cmd| and |cur_chr| represent the first token of -the internal quantity to be scanned; an error will be signalled if -|cur_cmd<min_internal| or |cur_cmd>max_internal|. - -@c -void scan_something_internal(int level, boolean negative) -{ - /* fetch an internal parameter */ - halfword m; /* |chr_code| part of the operand token */ - int n, k; /* accumulators */ - RESTART: - m = cur_chr; - if (!short_scan_something_internal(cur_cmd, cur_chr, level, negative)) { - switch (cur_cmd) { - case def_char_code_cmd: - /* Fetch a character code from some table */ - scan_char_num(); - if (m == math_code_base) { - cur_val1 = get_math_code_num(cur_val); - scanned_result(cur_val1, int_val_level); - } else if (m == lc_code_base) { - cur_val1 = get_lc_code(cur_val); - scanned_result(cur_val1, int_val_level); - } else if (m == uc_code_base) { - cur_val1 = get_uc_code(cur_val); - scanned_result(cur_val1, int_val_level); - } else if (m == sf_code_base) { - cur_val1 = get_sf_code(cur_val); - scanned_result(cur_val1, int_val_level); - } else if (m == cat_code_base) { - cur_val1 = get_cat_code(cat_code_table_par, cur_val); - scanned_result(cur_val1, int_val_level); - } else { - confusion("def_char"); - } - break; - case def_del_code_cmd: - case extdef_del_code_cmd: /* bonus */ - /* Fetch a character code from some table */ - scan_char_num(); - cur_val1 = get_del_code_num(cur_val); - scanned_result(cur_val1, int_val_level); - break; - case extdef_math_code_cmd: - /* Fetch an extended math code table value */ - scan_char_num(); - cur_val1 = get_math_code_num(cur_val); - scanned_result(cur_val1, int_val_level); - break; - case toks_register_cmd: - case set_font_cmd: - case def_font_cmd: - case letterspace_font_cmd: - case copy_font_cmd: - /* Fetch a token list or font identifier, provided that |level=tok_val| */ - if (level != tok_val_level) { - print_err("Missing number, treated as zero"); - help3("A number should have been here; I inserted `0'.", - "(If you can't figure out why I needed to see a number,", - "look up `weird error' in the index to The TeXbook.)"); - back_error(); - scanned_result(0, dimen_val_level); - } else if (cur_cmd == toks_register_cmd) { - scan_register_num(); - m = toks_base + cur_val; - scanned_result(equiv(m), tok_val_level); - } else { - back_input(); - scan_font_ident(); - scanned_result(font_id_base + cur_val, ident_val_level); - } - break; - case set_font_id_cmd: - scan_int(); - scanned_result(font_id_base + cur_val, ident_val_level); - break; - case def_family_cmd: - /* Fetch a math font identifier */ - scan_char_num(); - cur_val1 = fam_fnt(cur_val, m); - scanned_result(font_id_base + cur_val1, ident_val_level); - break; - case set_math_param_cmd: - /* Fetch a math param */ - cur_val1 = cur_chr; - get_token(); - if (cur_cmd != math_style_cmd) { - print_err("Missing math style, treated as \\displaystyle"); - help1("A style should have been here; I inserted `\\displaystyle'."); - cur_val = display_style; - back_error(); - } else { - cur_val = cur_chr; - } - if (cur_val1 < math_param_first_mu_glue) { - if (cur_val1 == math_param_radical_degree_raise) { - cur_val1 = get_math_param(cur_val1, cur_chr); - scanned_result(cur_val1, int_val_level); - } else { - cur_val1 = get_math_param(cur_val1, cur_chr); - scanned_result(cur_val1, dimen_val_level); - } - } else { - cur_val1 = get_math_param(cur_val1, cur_chr); - if (cur_val1 == thin_mu_skip_code) - cur_val1 = thin_mu_skip_par; - else if (cur_val1 == med_mu_skip_code) - cur_val1 = med_mu_skip_par; - else if (cur_val1 == thick_mu_skip_code) - cur_val1 = thick_mu_skip_par; - scanned_result(cur_val1, mu_val_level); - } - break; - case assign_box_dir_cmd: - scan_register_num(); - m = cur_val; - if (box(m) != null) - cur_val = box_dir(box(m)); - else - cur_val = 0; - cur_val_level = dir_val_level; - break; - case set_box_dimen_cmd: - /* Fetch a box dimension */ - scan_register_num(); - if (box(cur_val) == null) - cur_val = 0; - else - cur_val = varmem[box(cur_val) + m].cint; - cur_val_level = dimen_val_level; - break; - case assign_font_dimen_cmd: - /* Fetch a font dimension */ - get_font_dimen(); - break; - case assign_font_int_cmd: - /* Fetch a font integer */ - scan_font_ident(); - if (m == 0) { - scanned_result(hyphen_char(cur_val), int_val_level); - } else if (m == 1) { - scanned_result(skew_char(cur_val), int_val_level); - } else if (m == no_lig_code) { - scanned_result(test_no_ligatures(cur_val), int_val_level); - } else { - n = cur_val; - scan_char_num(); - k = cur_val; - switch (m) { - case lp_code_base: - scanned_result(get_lp_code(n, k), int_val_level); - break; - case rp_code_base: - scanned_result(get_rp_code(n, k), int_val_level); - break; - case ef_code_base: - scanned_result(get_ef_code(n, k), int_val_level); - break; - case tag_code: - scanned_result(get_tag_code(n, k), int_val_level); - break; - } - } - break; - case register_cmd: - /* Fetch a register */ - scan_register_num(); - switch (m) { - case int_val_level: - cur_val = count(cur_val); - break; - case attr_val_level: - cur_val = attribute(cur_val); - break; - case dimen_val_level: - cur_val = dimen(cur_val); - break; - case glue_val_level: - cur_val = skip(cur_val); - break; - case mu_val_level: - cur_val = mu_skip(cur_val); - break; - } /* there are no other cases */ - cur_val_level = m; - break; - case ignore_spaces_cmd: /* trap unexpandable primitives */ - if (cur_chr == 1) { - /* Reset |cur_tok| for unexpandable primitives, goto restart */ - /* This block deals with unexpandable \.{\\primitive} appearing at a spot where - an integer or an internal values should have been found. It fetches the - next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on the - primitive value of that token. No expansion takes place, because the - next token may be all sorts of things. This could trigger further - expansion creating new errors. - */ - get_token(); - cur_cs = prim_lookup(cs_text(cur_cs)); - if (cur_cs != undefined_primitive) { - cur_cmd = get_prim_eq_type(cur_cs); - cur_chr = get_prim_equiv(cur_cs); - cur_tok = token_val(cur_cmd, cur_chr); - } else { - cur_cmd = relax_cmd; - cur_chr = 0; - cur_tok = cs_token_flag + frozen_relax; - cur_cs = frozen_relax; - } - goto RESTART; - } - break; - case hyph_data_cmd: - switch (cur_chr) { - case 0: - case 1: - goto DEFAULT; - break; - case 2: - cur_val = get_pre_hyphen_char(language_par); - cur_val_level = int_val_level; - break; - case 3: - cur_val = get_post_hyphen_char(language_par); - cur_val_level = int_val_level; - break; - case 4: - cur_val = get_pre_exhyphen_char(language_par); - cur_val_level = int_val_level; - break; - case 5: - cur_val = get_post_exhyphen_char(language_par); - cur_val_level = int_val_level; - break; - case 6: - cur_val = get_hyphenation_min(language_par); - cur_val_level = int_val_level; - break; - case 7: - scan_int(); - cur_val = get_hj_code(language_par,cur_val); - cur_val_level = int_val_level; - break; - } - break; - default: - DEFAULT: - /* Complain that \.{\\the} can not do this; give zero result */ - print_err("You can't use `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - tprint("' after \\the"); - help1("I'm forgetting what you said and using zero instead."); - error(); - if (level != tok_val_level) - scanned_result(0, dimen_val_level); - else - scanned_result(0, int_val_level); - break; - } - while (cur_val_level > level) { - /* Convert |cur_val| to a lower level */ - downgrade_cur_val(false); - } - /* Fix the reference count, if any, and negate |cur_val| if |negative| */ - /* If |cur_val| points to a glue specification at this point, the reference - count for the glue does not yet include the reference by |cur_val|. - If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|. - */ - if (negative) { - /* we create a new (negated) glue spec and keep the old one */ - negate_cur_val(false); - } else if ((cur_val_level >= glue_val_level) && (cur_val_level <= mu_val_level)) { - cur_val = new_spec(cur_val); - } - } -} - -@ It is nice to have routines that say what they do, so the original -|scan_eight_bit_int| is superceded by |scan_register_num| and -|scan_mark_num|. It may become split up even further in the future. - -Many of the |restricted classes| routines are the essentially -the same except for the upper limit and the error message, so it makes -sense to combine these all into one function. - -@c -void scan_limited_int(int max, const char *name) -{ - char hlp[80]; - scan_int(); - if ((cur_val < 0) || (cur_val > max)) { - if (name == NULL) { - snprintf(hlp, 80, - "Since I expected to read a number between 0 and %d,", - max); - print_err("Bad number"); - } else { - char msg[80]; - snprintf(hlp, 80, "A %s must be between 0 and %d.", name, max); - snprintf(msg, 80, "Bad %s", name); - print_err(msg); - } - help2(hlp, "I changed this one to zero."); - int_error(cur_val); - cur_val = 0; - } -} - -@ @c -void scan_fifteen_bit_int(void) -{ - scan_real_fifteen_bit_int(); - cur_val = ((cur_val / 0x1000) * 0x1000000) + - (((cur_val % 0x1000) / 0x100) * 0x10000) + (cur_val % 0x100); -} - -@ @c -void scan_fifty_one_bit_int(void) -{ - int iiii; - scan_int(); - if ((cur_val < 0) || (cur_val > 0777777777)) { - print_err("Bad delimiter code"); - help2 - ("A numeric delimiter (first part) must be between 0 and 2^{27}-1.", - "I changed this one to zero."); - int_error(cur_val); - cur_val = 0; - } - iiii = cur_val; - scan_int(); - if ((cur_val < 0) || (cur_val > 0xFFFFFF)) { - print_err("Bad delimiter code"); - help2 - ("A numeric delimiter (second part) must be between 0 and 2^{24}-1.", - "I changed this one to zero."); - int_error(cur_val); - cur_val = 0; - } - cur_val1 = cur_val; - cur_val = iiii; -} - -@ An integer number can be preceded by any number of spaces and `\.+' or -`\.-' signs. Then comes either a decimal constant (i.e., radix 10), an -octal constant (i.e., radix 8, preceded by~'), a hexadecimal constant -(radix 16, preceded by~"), an alphabetic constant (preceded by~`), or -an internal variable. After scanning is complete, -|cur_val| will contain the answer, which must be at most -$2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to -10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants, -otherwise |radix| is set to zero. An optional space follows a constant. - -@c -int radix; /* |scan_int| sets this to 8, 10, 16, or zero */ - -@ The |scan_int| routine is used also to scan the integer part of a - fraction; for example, the `\.3' in `\.{3.14159}' will be found by - |scan_int|. The |scan_dimen| routine assumes that |cur_tok=point_token| - after the integer part of such a fraction has been scanned by |scan_int|, - and that the decimal point has been backed up to be scanned again. - -@c -void scan_int(void) -{ /* sets |cur_val| to an integer */ - boolean negative; /* should the answer be negated? */ - int m; /* |$2^{31}$ / radix|, the threshold of danger */ - int d; /* the digit just scanned */ - boolean vacuous; /* have no digits appeared? */ - boolean OK_so_far; /* has an error message been issued? */ - radix = 0; - OK_so_far = true; - /* Get the next non-blank non-sign token; set |negative| appropriately */ - negative = false; - do { - /* Get the next non-blank non-call token */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '-') { - negative = !negative; - cur_tok = other_token + '+'; - } - } while (cur_tok == other_token + '+'); - - RESTART: - if (cur_tok == alpha_token) { - /* Scan an alphabetic character code into |cur_val| */ - /* A space is ignored after an alphabetic character constant, so that - such constants behave like numeric ones. */ - get_token(); /* suppress macro expansion */ - if (cur_tok < cs_token_flag) { - cur_val = cur_chr; - if (cur_cmd <= right_brace_cmd) { - if (cur_cmd == right_brace_cmd) - incr(align_state); - else - decr(align_state); - } - } else { /* the value of a csname in this context is its name */ - str_number txt = cs_text(cur_tok - cs_token_flag); - if (is_active_cs(txt)) - cur_val = active_cs_value(txt); - else if (single_letter(txt)) - cur_val = pool_to_unichar(str_string(txt)); - else - cur_val = (biggest_char + 1); - } - if (cur_val > biggest_char) { - print_err("Improper alphabetic constant"); - help2("A one-character control sequence belongs after a ` mark.", - "So I'm essentially inserting \\0 here."); - cur_val = '0'; - back_error(); - } else { - /* Scan an optional space */ - get_x_token(); - if (cur_cmd != spacer_cmd) - back_input(); - } - - } else if (cur_tok == cs_token_flag + frozen_primitive) { - /* Reset |cur_tok| for unexpandable primitives, goto restart */ - /* This block deals with unexpandable \.{\\primitive} appearing at a spot where - an integer or an internal values should have been found. It fetches the - next token then resets |cur_cmd|, |cur_cs|, and |cur_tok|, based on the - primitive value of that token. No expansion takes place, because the - next token may be all sorts of things. This could trigger further - expansion creating new errors. - */ - get_token(); - cur_cs = prim_lookup(cs_text(cur_cs)); - if (cur_cs != undefined_primitive) { - cur_cmd = get_prim_eq_type(cur_cs); - cur_chr = get_prim_equiv(cur_cs); - cur_tok = token_val(cur_cmd, cur_chr); - } else { - cur_cmd = relax_cmd; - cur_chr = 0; - cur_tok = cs_token_flag + frozen_relax; - cur_cs = frozen_relax; - } - goto RESTART; - } else if (cur_cmd == math_style_cmd) { - cur_val = cur_chr; - } else if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { - scan_something_internal(int_val_level, false); - } else { - /* Scan a numeric constant */ - radix = 10; - m = 214748364; - if (cur_tok == octal_token) { - radix = 8; - m = 02000000000; - get_x_token(); - } else if (cur_tok == hex_token) { - radix = 16; - m = 01000000000; - get_x_token(); - } - vacuous = true; - cur_val = 0; - /* Accumulate the constant until |cur_tok| is not a suitable digit */ - while (1) { - if ((cur_tok < zero_token + radix) && (cur_tok >= zero_token) - && (cur_tok <= zero_token + 9)) { - d = cur_tok - zero_token; - } else if (radix == 16) { - if ((cur_tok <= A_token + 5) && (cur_tok >= A_token)) { - d = cur_tok - A_token + 10; - } else if ((cur_tok <= other_A_token + 5) - && (cur_tok >= other_A_token)) { - d = cur_tok - other_A_token + 10; - } else { - break; - } - } else { - break; - } - vacuous = false; - if ((cur_val >= m) && ((cur_val > m) || (d > 7) || (radix != 10))) { - if (OK_so_far) { - print_err("Number too big"); - help2 - ("I can only go up to 2147483647='17777777777=\"7FFFFFFF,", - "so I'm using that number instead of yours."); - error(); - cur_val = infinity; - OK_so_far = false; - } - } else { - cur_val = cur_val * radix + d; - } - get_x_token(); - } - if (vacuous) { - /* Express astonishment that no number was here */ - print_err("Missing number, treated as zero"); - help3("A number should have been here; I inserted `0'.", - "(If you can't figure out why I needed to see a number,", - "look up `weird error' in the index to The TeXbook.)"); - back_error(); - } else if (cur_cmd != spacer_cmd) { - back_input(); - } - } - if (negative) - negate(cur_val); -} - -@ The following code is executed when |scan_something_internal| was -called asking for |mu_val|, when we really wanted a ``mudimen'' instead -of ``muglue.'' - -@c -static void coerce_glue(void) -{ - int v; - if (cur_val_level >= glue_val_level) { - v = width(cur_val); - flush_node(cur_val); - cur_val = v; - } -} - -@ The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to -a |scaled| value, i.e., an integral number of sp. One of its main tasks -is therefore to interpret the abbreviations for various kinds of units and -to convert measurements to scaled points. - -There are three parameters: |mu| is |true| if the finite units must be -`\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed; -|inf| is |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}' -are permitted; and |shortcut| is |true| if |cur_val| already contains -an integer and only the units need to be considered. - -The order of infinity that was found in the case of infinite glue is returned -in the global variable |cur_order|. - -@c -int cur_order; /* order of infinity found by |scan_dimen| */ - - -@ Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen| -may begin with |scan_int|. This explains why it is convenient to use -|scan_int| also for the integer part of a decimal fraction. - -Several branches of |scan_dimen| work with |cur_val| as an integer and -with an auxiliary fraction |f|, so that the actual quantity of interest is -$|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked'' -representation is put into the single word |cur_val|, which suddenly -switches significance from |integer| to |scaled|. - -@ -The necessary conversion factors can all be specified exactly as -fractions whose numerator and denominator add to 32768 or less. -According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$; -this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard -\^{Bosshard, Hans Rudolf} -in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). -The Didot point has been newly standardized in 1978; -it's now exactly $\rm 1\,nd=0.375\,mm$. -Conversion uses the equation $0.375=21681/20320/72.27\cdot25.4$. -The new Cicero follows the new Didot point; $\rm 1\,nc=12\,nd$. -These would lead to the ratios $21681/20320$ and $65043/5080$, -respectively. -The closest approximations supported by the algorithm would be -$11183/10481$ and $1370/107$. In order to maintain the -relation $\rm 1\,nc=12\,nd$, we pick the ratio $685/642$ for -$\rm nd$, however. - -@c - -static void scan_dimen_mu_error(void) { - print_err("Illegal unit of measure (mu inserted)"); - help4("The unit of measurement in math glue must be mu.", - "To recover gracefully from this error, it's best to", - "delete the erroneous units; e.g., type `2' to delete", - "two letters. (See Chapter 27 of The TeXbook.)"); - error(); -} - -static void scan_dimen_unknown_unit_error(void) { - print_err("Illegal unit of measure (pt inserted)"); - help6("Dimensions can be in units of em, ex, in, pt, pc,", - "cm, mm, dd, cc, nd, nc, bp, or sp; but yours is a new one!", - "I'll assume that you meant to say pt, for printer's points.", - "To recover gracefully from this error, it's best to", - "delete the erroneous units; e.g., type `2' to delete", - "two letters. (See Chapter 27 of The TeXbook.)"); - error(); -} - -static void scan_dimen_out_of_range_error(void) { - print_err("Dimension too large"); - help2("I can't work with sizes bigger than about 19 feet.", - "Continue and I'll use the largest value I can."); - error(); -} - -#define set_conversion(A,B) do { num=(A); denom=(B); } while(0) - -/* - This function sets |cur_val| to a dimension. It could be optimized a bit - more (but not now, something for luatex > 1). -*/ - -void scan_dimen(boolean mu, boolean inf, boolean shortcut) -{ - boolean negative = false; /* should the answer be negated? */ - boolean is_true = false; - int f = 0; /* numerator of a fraction whose denominator is $2^{16}$ */ - int num = 0; /* conversion ratio for the scanned units */ - int denom = 0; - halfword q; /* top of decimal digit stack */ - scaled v; /* an internal dimension */ - int save_cur_val; /* temporary storage of |cur_val| */ - arith_error = false; - cur_order = normal; - if (!shortcut) { - /* Get the next non-blank non-sign... */ - do { - /* Get the next non-blank non-call token */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '-') { - negative = !negative; - cur_tok = other_token + '+'; - } - } while (cur_tok == other_token + '+'); - if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { - /* Fetch an internal dimension and |goto attach_sign|, or fetch an internal integer */ - if (mu) { - scan_something_internal(mu_val_level, false); - coerce_glue(); - if (cur_val_level == mu_val_level) { - goto ATTACH_SIGN; - } else if (cur_val_level != int_val_level) { - mu_error(); - } - } else { - scan_something_internal(dimen_val_level, false); - if (cur_val_level == dimen_val_level) { - goto ATTACH_SIGN; - } - } - } else { - back_input(); - if (cur_tok == continental_point_token) { - cur_tok = point_token; - } - if (cur_tok != point_token) { - scan_int(); - } else { - radix = 10; - cur_val = 0; - } - if (cur_tok == continental_point_token) { - cur_tok = point_token; - } - if ((radix == 10) && (cur_tok == point_token)) { - /* - Scan decimal fraction. When the following code is executed, we have - |cur_tok=point_token|, but this token has been backed up using |back_input|; - we must first discard it. It turns out that a decimal point all by itself - is equivalent to `\.{0.0}'. Let's hope people don't use that fact. - */ - int k = 0; - halfword p = null; - int kk; - get_token(); /* |point_token| is being re-scanned */ - while (1) { - get_x_token(); - if ((cur_tok > zero_token + 9) || (cur_tok < zero_token)) - break; - if (k < 17) { - /* digits for |k>=17| cannot affect the result */ - q = get_avail(); - set_token_link(q, p); - set_token_info(q, cur_tok - zero_token); - p = q; - incr(k); - } - } - for (kk = k; kk >= 1; kk--) { - dig[kk - 1] = token_info(p); - q = p; - p = token_link(p); - free_avail(q); - } - f = round_decimals(k); - if (cur_cmd != spacer_cmd) { - back_input(); - } - } - } - } - if (cur_val < 0) { - /* in this case |f=0| */ - negative = !negative; - negate(cur_val); - } - /* - Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there - are |x| sp per unit; |goto attach_sign| if the units are internal. Now comes - the harder part: At this point in the program, |cur_val| is a nonnegative - integer and $f/2^{16}$ is a nonnegative fraction less than 1; we want to - multiply the sum of these two quantities by the appropriate factor, based - on the specified units, in order to produce a |scaled| result, and we want - to do the calculation with fixed point arithmetic that does not overflow. - */ - if (inf) { - /* - Scan for (f)\.{fil} units; |goto attach_fraction| if found. In traditional - \TeX, a specification like `\.{filllll}' or `\.{fill L L L}' will lead to - two error messages (one for each additional keyword \.{"l"}). Not so for - \LuaTeX, it just parses the construct in reverse. - - if (scan_keyword("filll")) { - cur_order = filll; - goto ATTACH_FRACTION; - } else if (scan_keyword("fill")) { - cur_order = fill; - goto ATTACH_FRACTION; - } else if (scan_keyword("fil")) { - cur_order = fil; - goto ATTACH_FRACTION; - } else if (scan_keyword("fi")) { - cur_order = sfi; - goto ATTACH_FRACTION; - } - - But ... it failed in alignments so now we do this. And, as we support an extra - l we don't issue an error message (we didn't do that anyway). - */ - if (scan_keyword("fi")) { - cur_order = sfi; - if (scan_keyword("l")) { - cur_order = fil; - if (scan_keyword("l")) { - cur_order = fill; - if (scan_keyword("l")) { - cur_order = filll; - } - } - } - goto ATTACH_FRACTION; - } - } - /* - Scan for (u)units that are internal dimensions; |goto attach_sign| with - |cur_val| set if found - */ - save_cur_val = cur_val; - /* Get the next non-blank non-call... a pitty if just backed up the input */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - - if ((cur_cmd < min_internal_cmd) || (cur_cmd > max_internal_cmd)) { - back_input(); - } else { - /* math_given_cmd xmath_given_cmd last_item_cmd */ - if (mu) { - scan_something_internal(mu_val_level, false); - coerce_glue(); - if (cur_val_level != mu_val_level) { - mu_error(); - } - } else { - scan_something_internal(dimen_val_level, false); - } - v = cur_val; - goto FOUND; - } - /* bah ... true forces to split the unit scanner */ - if (mu) { - /* Scan for (m)\.{mu} units and |goto attach_fraction| */ - if (! scan_keyword("mu")) { - scan_dimen_mu_error(); - } - goto ATTACH_FRACTION; - } else if (scan_keyword("em")) { - v = quad(get_cur_font()); - } else if (scan_keyword("ex")) { - v = x_height(get_cur_font()); - } else if (scan_keyword("px")) { - v = px_dimen_par; - } else { - goto PICKUP_UNIT; - } - /* Scan an optional space (after em, ex or px) */ - get_x_token(); - if (cur_cmd != spacer_cmd) { - back_input(); - } - FOUND: - cur_val = nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000)); - goto ATTACH_SIGN; - /* - Scan for (a)all other units and adjust |cur_val| and |f| accordingly; - |goto done| in the case of scaled points - */ - PICKUP_UNIT: - if (scan_keyword("pt")) { - goto SCALE_VALUE; /* the easy case */ - } else if (scan_keyword("mm")) { - set_conversion(7227, 2540); - goto SCALE_VALUE; - } else if (scan_keyword("cm")) { - set_conversion(7227, 254); - goto SCALE_VALUE; - } else if (scan_keyword("sp")) { - goto DONE; - } else if (scan_keyword("bp")) { - set_conversion(7227, 7200); - goto SCALE_VALUE; - } else if (scan_keyword("in")) { - set_conversion(7227, 100); - goto SCALE_VALUE; - } else if (scan_keyword("dd")) { - set_conversion(1238, 1157); - goto SCALE_VALUE; - } else if (scan_keyword("cc")) { - set_conversion(14856, 1157); - goto SCALE_VALUE; - } else if (scan_keyword("pc")) { - set_conversion(12, 1); - goto SCALE_VALUE; - } else if (scan_keyword("nd")) { - set_conversion(685, 642); - goto SCALE_VALUE; - } else if (scan_keyword("nc")) { - set_conversion(1370, 107); - goto SCALE_VALUE; - } else if (!is_true && scan_keyword("true")) { - is_true = true; - goto PICKUP_UNIT; - } - /* Complain about unknown unit and |goto done2| */ - scan_dimen_unknown_unit_error(); - goto BAD_NEWS; - SCALE_VALUE: - /* Adjust (f) for the magnification ratio */ - if (is_true) { - /* maybe at some point we will drop mag completely, even in dvi mode */ - if (output_mode_used <= OMODE_DVI) { - prepare_mag(); - if (mag_par != 1000) { - cur_val = xn_over_d(cur_val, 1000, mag_par); - f = (1000 * f + 0200000 * tex_remainder) / mag_par; - cur_val = cur_val + (f / 0200000); - f = f % 0200000; - } - } else { - /* in pdf mode we're always true */ - one_true_inch = one_inch; /* saveguard */ - } - } - /* */ - if (num) { - cur_val = xn_over_d(cur_val, num, denom); - f = (num * f + 0200000 * tex_remainder) / denom; - cur_val = cur_val + (f / 0200000); - f = f % 0200000; - } - BAD_NEWS: - ATTACH_FRACTION: - if (cur_val >= 040000) { - arith_error = true; - } else { - cur_val = cur_val * unity + f; - } - DONE: - /* Scan an optional space */ /* happens too often */ - get_x_token(); - if (cur_cmd != spacer_cmd) { - back_input(); - } - ATTACH_SIGN: - if (arith_error || (abs(cur_val) >= 010000000000)) { - /* Report that this dimension is out of range */ - scan_dimen_out_of_range_error(); - cur_val = max_dimen; - arith_error = false; - } - if (negative) { - negate(cur_val); - } -} - -@ The final member of \TeX's value-scanning trio is |scan_glue|, which -makes |cur_val| point to a glue specification. The reference count of that -glue spec will take account of the fact that |cur_val| is pointing to~it. - -The |level| parameter should be either |glue_val| or |mu_val|. - -Since |scan_dimen| was so much more complex than |scan_int|, we might expect -|scan_glue| to be even worse. But fortunately, it is very simple, since -most of the work has already been done. - -@c -void scan_glue(int level) -{ - boolean negative = false; /* should the answer be negated? */ - halfword q = null; /* new glue specification */ - boolean mu = (level == mu_val_level); /* does |level=mu_val|? */ - /* Get the next non-blank non-sign ... */ - do { - /* Get the next non-blank non-call token */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '-') { - negative = !negative; - cur_tok = other_token + '+'; - } - } while (cur_tok == other_token + '+'); - if ((cur_cmd >= min_internal_cmd) && (cur_cmd <= max_internal_cmd)) { - scan_something_internal(level, negative); - if (cur_val_level >= glue_val_level) { - if (cur_val_level != level) - mu_error(); - return; - } - if (cur_val_level == int_val_level) - scan_dimen(mu, false, true); - else if (level == mu_val_level) - mu_error(); - } else { - back_input(); - scan_dimen(mu, false, false); - if (negative) - negate(cur_val); - } - /* - Create a new glue specification whose width is |cur_val|; scan for its - stretch and shrink components. - */ - q = new_spec(zero_glue); - width(q) = cur_val; - if (scan_keyword("plus")) { - scan_dimen(mu, true, false); - stretch(q) = cur_val; - stretch_order(q) = (quarterword) cur_order; - } - if (scan_keyword("minus")) { - scan_dimen(mu, true, false); - shrink(q) = cur_val; - shrink_order(q) = (quarterword) cur_order; - } - cur_val = q; -} - -@ This is an omega routine -@c -void scan_scaled(void) -{ /* sets |cur_val| to a scaled value */ - boolean negative; /* should the answer be negated? */ - int f; /* numerator of a fraction whose denominator is $2^{16}$ */ - int k, kk; /* number of digits in a decimal fraction */ - halfword p, q; /* top of decimal digit stack */ - f = 0; - arith_error = false; - negative = false; - /* Get the next non-blank non-sign... */ - do { - /* Get the next non-blank non-call token */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '-') { - negative = !negative; - cur_tok = other_token + '+'; - } - } while (cur_tok == other_token + '+'); - - back_input(); - if (cur_tok == continental_point_token) - cur_tok = point_token; - if (cur_tok != point_token) { - scan_int(); - } else { - radix = 10; - cur_val = 0; - } - if (cur_tok == continental_point_token) - cur_tok = point_token; - if ((radix == 10) && (cur_tok == point_token)) { - /* Scan decimal fraction */ - /* TODO: merge this with the same block in |scan_dimen| */ - /* When the following code is executed, we have |cur_tok=point_token|, but this - token has been backed up using |back_input|; we must first discard it. - - It turns out that a decimal point all by itself is equivalent to `\.{0.0}'. - Let's hope people don't use that fact. */ - - k = 0; - p = null; - get_token(); /* |point_token| is being re-scanned */ - while (1) { - get_x_token(); - if ((cur_tok > zero_token + 9) || (cur_tok < zero_token)) - break; - if (k < 17) { /* digits for |k>=17| cannot affect the result */ - q = get_avail(); - set_token_link(q, p); - set_token_info(q, cur_tok - zero_token); - p = q; - incr(k); - } - } - for (kk = k; kk >= 1; kk--) { - dig[kk - 1] = token_info(p); - q = p; - p = token_link(p); - free_avail(q); - } - f = round_decimals(k); - if (cur_cmd != spacer_cmd) - back_input(); - - } - if (cur_val < 0) { /* in this case |f=0| */ - negative = !negative; - negate(cur_val); - } - if (cur_val > 040000) - arith_error = true; - else - cur_val = cur_val * unity + f; - if (arith_error || (abs(cur_val) >= 010000000000)) { - print_err("Stack number too large"); - error(); - } - if (negative) - negate(cur_val); -} - -@ This procedure is supposed to scan something like `\.{\\skip\\count12}', -i.e., whatever can follow `\.{\\the}', and it constructs a token list -containing something like `\.{-3.0pt minus 0.5fill}'. - -@c -halfword the_toks(void) -{ - int old_setting; /* holds |selector| setting */ - halfword p, q, r; /* used for copying a token list */ - int c; /* value of |cur_chr| */ - str_number s; - halfword retval; - /* Handle \.{\\unexpanded} or \.{\\detokenize} and |return| */ - if (odd(cur_chr)) { - c = cur_chr; - scan_general_text(); - if (c == 1) { - return cur_val; - } else { - old_setting = selector; - selector = new_string; - p = get_avail(); - set_token_link(p, token_link(temp_token_head)); - token_show(p); - flush_list(p); - selector = old_setting; - s = make_string(); - retval = str_toks(str_lstring(s)); - flush_str(s); - return retval; - } - } - get_x_token(); - scan_something_internal(tok_val_level, false); - if (cur_val_level >= ident_val_level) { - /* Copy the token list */ - p = temp_token_head; - set_token_link(p, null); - if (cur_val_level == ident_val_level) { - store_new_token(cs_token_flag + cur_val); - } else if (cur_val != null) { - r = token_link(cur_val); /* do not copy the reference count */ - while (r != null) { - fast_store_new_token(token_info(r)); - r = token_link(r); - } - } - return p; - } else { - old_setting = selector; - selector = new_string; - switch (cur_val_level) { - case int_val_level: - print_int(cur_val); - break; - case attr_val_level: - print_int(cur_val); - break; - case dir_val_level: - print_dir(cur_val); - break; - case dimen_val_level: - print_scaled(cur_val); - tprint("pt"); - break; - case glue_val_level: - print_spec(cur_val, "pt"); - flush_node(cur_val); - break; - case mu_val_level: - print_spec(cur_val, "mu"); - flush_node(cur_val); - break; - } /* there are no other cases */ - selector = old_setting; - s = make_string(); - retval = str_toks(str_lstring(s)); - flush_str(s); - return retval; - } -} - -@ @c -str_number the_scanned_result(void) -{ - int old_setting; /* holds |selector| setting */ - str_number r; /* return value * */ - old_setting = selector; - selector = new_string; - if (cur_val_level >= ident_val_level) { - if (cur_val != null) { - show_token_list(token_link(cur_val), null, -1); - r = make_string(); - } else { - r = get_nullstr(); - } - } else { - switch (cur_val_level) { - case int_val_level: - print_int(cur_val); - break; - case attr_val_level: - print_int(cur_val); - break; - case dir_val_level: - print_dir(cur_val); - break; - case dimen_val_level: - print_scaled(cur_val); - tprint("pt"); - break; - case glue_val_level: - print_spec(cur_val, "pt"); - flush_node(cur_val); - break; - case mu_val_level: - print_spec(cur_val, "mu"); - flush_node(cur_val); - break; - } /* there are no other cases */ - r = make_string(); - } - selector = old_setting; - return r; -} - -@ The following routine is used to implement `\.{\\fontdimen} |n| |f|'. -The boolean parameter |writing| is set |true| if the calling program -intends to change the parameter value. - -@c -static void font_param_error(int f) -{ - print_err("Font "); - print_esc(font_id_text(f)); - tprint(" has only "); - print_int(font_params(f)); - tprint(" fontdimen parameters"); - help2("To increase the number of font parameters, you must", - "use \\fontdimen immediately after the \\font is loaded."); - error(); -} - -void set_font_dimen(void) -{ - internal_font_number f; - int n; /* the parameter number */ - scan_int(); - n = cur_val; - scan_font_ident(); - f = cur_val; - if (n <= 0) { - font_param_error(f); - } else { - if (n > font_params(f)) { - if (font_used(f)) { - font_param_error(f); - } else { - /* Increase the number of parameters in the font */ - do { - set_font_param(f, (font_params(f) + 1), 0); - } while (n != font_params(f)); - } - } - } - scan_optional_equals(); - scan_normal_dimen(); - set_font_param(f, n, cur_val); -} - -void get_font_dimen(void) -{ - internal_font_number f; - int n; /* the parameter number */ - scan_int(); - n = cur_val; - scan_font_ident(); - f = cur_val; - cur_val = 0; /* initialize return value */ - if (n <= 0) { - font_param_error(f); - goto EXIT; - } else { - if (n > font_params(f)) { - if (font_used(f)) { - font_param_error(f); - goto EXIT; - } else { - /* Increase the number of parameters in the font */ - do { - set_font_param(f, (font_params(f) + 1), 0); - } while (n != font_params(f)); - - } - } - } - cur_val = font_param(f, n); - EXIT: - scanned_result(cur_val, dimen_val_level); -} - -@ Here's a similar procedure that returns a pointer to a rule node. This -routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule}; -therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store -the default rule dimensions in the node, then to override them if -`\.{height}' or `\.{width}' or `\.{depth}' specifications are -found (in any order). - -@c -halfword scan_rule_spec(void) -{ - /* |width|, |depth|, and |height| all equal |null_flag| now */ - halfword q; - if (cur_cmd == no_vrule_cmd) { - q = new_rule(empty_rule); - cur_cmd = vrule_cmd; - } else if (cur_cmd == no_hrule_cmd) { - q = new_rule(empty_rule); - cur_cmd = hrule_cmd; - } else { - q = new_rule(normal_rule); - } - if (cur_cmd == vrule_cmd) { - width(q) = default_rule; - rule_dir(q) = body_direction_par; - } else { - height(q) = default_rule; - depth(q) = 0; - rule_dir(q) = text_direction_par; - } - RESWITCH: - if (scan_keyword("width")) { - scan_normal_dimen(); - width(q) = cur_val; - goto RESWITCH; - } - if (scan_keyword("height")) { - scan_normal_dimen(); - height(q) = cur_val; - goto RESWITCH; - } - if (scan_keyword("depth")) { - scan_normal_dimen(); - depth(q) = cur_val; - goto RESWITCH; - } - return q; -} - - -@ Declare procedures that scan font-related stuff - -@c -void scan_font_ident(void) -{ - internal_font_number f; - halfword m; - /* Get the next non-blank non-call... */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - - if ((cur_cmd == def_font_cmd) || (cur_cmd == letterspace_font_cmd) || (cur_cmd == copy_font_cmd)) { - f = get_cur_font(); - } else if (cur_cmd == set_font_cmd) { - f = cur_chr; - set_font_touched(f, 1); - } else if (cur_cmd == def_family_cmd) { - m = cur_chr; - scan_math_family_int(); - f = fam_fnt(cur_val, m); - set_font_touched(f, 1); - } else { - print_err("Missing font identifier"); - help2("I was looking for a control sequence whose", - "current meaning has been defined by \\font."); - back_error(); - f = null_font; - } - cur_val = f; -} - -@ The |scan_general_text| procedure is much like |scan_toks(false,false)|, -but will be invoked via |expand|, i.e., recursively. - -The token list (balanced text) created by |scan_general_text| begins -at |link(temp_token_head)| and ends at |cur_val|. (If |cur_val=temp_token_head|, -the list is empty.) - -@c -void scan_general_text(void) -{ - int s; /* to save |scanner_status| */ - halfword w; /* to save |warning_index| */ - halfword d; /* to save |def_ref| */ - halfword p; /* tail of the token list being built */ - halfword q; /* new node being added to the token list via |store_new_token| */ - halfword unbalance; /* number of unmatched left braces */ - s = scanner_status; - w = warning_index; - d = def_ref; - scanner_status = absorbing; - warning_index = cur_cs; - p = get_avail(); - def_ref = p; - set_token_ref_count(def_ref, 0); - p = def_ref; - scan_left_brace(); /* remove the compulsory left brace */ - unbalance = 1; - while (1) { - get_token(); - if (cur_tok < right_brace_limit) { - if (cur_cmd < right_brace_cmd) { - incr(unbalance); - } else { - decr(unbalance); - if (unbalance == 0) - break; - } - } - store_new_token(cur_tok); - } - q = token_link(def_ref); - free_avail(def_ref); /* discard reference count */ - if (q == null) - cur_val = temp_token_head; - else - cur_val = p; - set_token_link(temp_token_head, q); - scanner_status = s; - warning_index = w; - def_ref = d; -} - -@ The |get_x_or_protected| procedure is like |get_x_token| except that -protected macros are not expanded. - -@c -void get_x_or_protected(void) -{ /* sets |cur_cmd|, |cur_chr|, |cur_tok|, - and expands non-protected macros */ - while (1) { - get_token(); - if (cur_cmd <= max_command_cmd) - return; - if ((cur_cmd >= call_cmd) && (cur_cmd < end_template_cmd)) { - if (token_info(token_link(cur_chr)) == protected_token) - return; - } - expand(); - } -} - -@ |scan_toks|. This function returns a pointer to the tail of a new token -list, and it also makes |def_ref| point to the reference count at the -head of that list. - -There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| -is true, the goal is to create the token list for a macro definition; -otherwise the goal is to create the token list for some other \TeX\ -primitive: \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase}, -\.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or -\.{\\special}. In the latter cases a left brace must be scanned next; this -left brace will not be part of the token list, nor will the matching right -brace that comes at the end. If |xpand| is false, the token list will -simply be copied from the input using |get_token|. Otherwise all expandable -tokens will be expanded until unexpandable tokens are left, except that -the results of expanding `\.{\\the}' are not expanded further. -If both |macro_def| and |xpand| are true, the expansion applies -only to the macro body (i.e., to the material following the first -|left_brace| character). - -The value of |cur_cs| when |scan_toks| begins should be the |eqtb| -address of the control sequence to display in ``runaway'' error -messages. - -@c -halfword scan_toks(boolean macro_def, boolean xpand) -{ - halfword t; /* token representing the highest parameter number */ - halfword s; /* saved token */ - halfword p; /* tail of the token list being built */ - halfword q; /* new node being added to the token list via |store_new_token| */ - halfword unbalance; /* number of unmatched left braces */ - halfword hash_brace; /* possible `\.{\#\{}' token */ - if (macro_def) - scanner_status = defining; - else - scanner_status = absorbing; - warning_index = cur_cs; - p = get_avail(); - def_ref = p; - set_token_ref_count(def_ref, 0); - p = def_ref; - hash_brace = 0; - t = zero_token; - if (macro_def) { - /* Scan and build the parameter part of the macro definition */ - while (1) { - get_token(); /* set |cur_cmd|, |cur_chr|, |cur_tok| */ - if (cur_tok < right_brace_limit) - break; - if (cur_cmd == mac_param_cmd) { - /* If the next character is a parameter number, make |cur_tok| - a |match| token; but if it is a left brace, store - `|left_brace|, |end_match|', set |hash_brace|, and |goto done|; - */ - s = match_token + cur_chr; - get_token(); - if (cur_cmd == left_brace_cmd) { - hash_brace = cur_tok; - store_new_token(cur_tok); - store_new_token(end_match_token); - goto DONE; - } - if (t == zero_token + 9) { - print_err("You already have nine parameters"); - help1("I'm going to ignore the # sign you just used."); - error(); - } else { - incr(t); - if (cur_tok != t) { - print_err("Parameters must be numbered consecutively"); - help2 - ("I've inserted the digit you should have used after the #.", - "Type `1' to delete what you did use."); - back_error(); - } - cur_tok = s; - } - } - store_new_token(cur_tok); - } - store_new_token(end_match_token); - if (cur_cmd == right_brace_cmd) { - /* Express shock at the missing left brace; |goto found| */ - print_err("Missing { inserted"); - incr(align_state); - help2 - ("Where was the left brace? You said something like `\\def\\a}',", - "which I'm going to interpret as `\\def\\a{}'."); - error(); - goto FOUND; - } - - } else { - scan_left_brace(); /* remove the compulsory left brace */ - } - DONE: - /* Scan and build the body of the token list; |goto found| when finished */ - unbalance = 1; - while (1) { - if (xpand) { - /* Expand the next part of the input */ - /* Here we insert an entire token list created by |the_toks| without - expanding it further. */ - while (1) { - get_next(); - if (cur_cmd >= call_cmd) { - if (token_info(token_link(cur_chr)) == protected_token) { - cur_cmd = relax_cmd; - cur_chr = no_expand_flag; - } - } - if (cur_cmd <= max_command_cmd) - break; - if (cur_cmd != the_cmd) { - expand(); - } else { - q = the_toks(); - if (token_link(temp_token_head) != null) { - set_token_link(p, token_link(temp_token_head)); - p = q; - } - } - } - x_token(); - - } else { - get_token(); - } - if (cur_tok < right_brace_limit) { - if (cur_cmd < right_brace_cmd) { - incr(unbalance); - } else { - decr(unbalance); - if (unbalance == 0) - goto FOUND; - } - } else if (cur_cmd == mac_param_cmd) { - if (macro_def) { - /* Look for parameter number or \.{\#\#} */ - s = cur_tok; - if (xpand) - get_x_token(); - else - get_token(); - if (cur_cmd != mac_param_cmd) { - if ((cur_tok <= zero_token) || (cur_tok > t)) { - print_err("Illegal parameter number in definition of "); - sprint_cs(warning_index); - help3("You meant to type ## instead of #, right?", - "Or maybe a } was forgotten somewhere earlier, and things", - "are all screwed up? I'm going to assume that you meant ##."); - back_error(); - cur_tok = s; - } else { - cur_tok = out_param_token - '0' + cur_chr; - } - } - } - } - store_new_token(cur_tok); - } - FOUND: - scanner_status = normal; - if (hash_brace != 0) - store_new_token(hash_brace); - return p; -} - -@ Here we declare two trivial procedures in order to avoid mutually -recursive procedures with parameters. - -@c -void scan_normal_glue(void) -{ - scan_glue(glue_val_level); -} - -void scan_mu_glue(void) -{ - scan_glue(mu_val_level); -} - -@ The |scan_expr| procedure scans and evaluates an expression. - -@ Evaluating an expression is a recursive process: When the left -parenthesis of a subexpression is scanned we descend to the next level -of recursion; the previous level is resumed with the matching right -parenthesis. - -@c -typedef enum { - expr_none = 0, /* \.( seen, or \.( $\langle\it expr\rangle$ \.) seen */ - expr_add = 1, /* \.( $\langle\it expr\rangle$ \.+ seen */ - expr_sub = 2, /* \.( $\langle\it expr\rangle$ \.- seen */ - expr_mult = 3, /* $\langle\it term\rangle$ \.* seen */ - expr_div = 4, /* $\langle\it term\rangle$ \./ seen */ - expr_scale = 5, /* $\langle\it term\rangle$ \.* $\langle\it factor\rangle$ \./ seen */ -} expression_states; - -@ We want to make sure that each term and (intermediate) result is in - the proper range. Integer values must not exceed |infinity| - ($2^{31}-1$) in absolute value, dimensions must not exceed |max_dimen| - ($2^{30}-1$). We avoid the absolute value of an integer, because this - might fail for the value $-2^{31}$ using 32-bit arithmetic. - -@ clear a number or dimension and set |arith_error| - -@c -#define num_error(A) do { \ - arith_error=true; \ - A=0; \ -} while (0) - -@ clear a glue spec and set |arith_error| - -@c -#define glue_error(A) do { \ - arith_error=true; \ - reset_glue_to_zero(A); \ -} while (0) - -#define normalize_glue(A) do { \ - if (stretch(A)==0) stretch_order(A)=normal; \ - if (shrink(A)==0) shrink_order(A)=normal; \ -} while (0) - -@ Parenthesized subexpressions can be inside expressions, and this -nesting has a stack. Seven local variables represent the top of the -expression stack: |p| points to pushed-down entries, if any; |l| -specifies the type of expression currently beeing evaluated; |e| is the -expression so far and |r| is the state of its evaluation; |t| is the -term so far and |s| is the state of its evaluation; finally |n| is the -numerator for a combined multiplication and division, if any. - -@c -#define expr_add_sub(A,B,C) add_or_sub((A),(B),(C),(r==expr_sub)) -#define expr_a(A,B) expr_add_sub((A),(B),max_dimen) - -@ - The function |add_or_sub(x,y,max_answer,negative)| computes the sum - (for |negative=false|) or difference (for |negative=true|) of |x| and - |y|, provided the absolute value of the result does not exceed - |max_answer|. - -@c -inline static int add_or_sub(int x, int y, int max_answer, boolean negative) -{ - int a; /* the answer */ - if (negative) - negate(y); - if (x >= 0) { - if (y <= max_answer - x) - a = x + y; - else - num_error(a); - } else if (y >= -max_answer - x) { - a = x + y; - } else { - num_error(a); - } - return a; -} - -#define expr_m(A) A = nx_plus_y((A),f,0) -#define expr_d(A) A=quotient((A),f) - -@ The function |quotient(n,d)| computes the rounded quotient -$q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive. - -@c -inline static int quotient(int n, int d) -{ - boolean negative; /* should the answer be negated? */ - int a; /* the answer */ - if (d == 0) { - num_error(a); - } else { - if (d > 0) { - negative = false; - } else { - negate(d); - negative = true; - } - if (n < 0) { - negate(n); - negative = !negative; - } - a = n / d; - n = n - a * d; - d = n - d; /* avoid certain compiler optimizations! */ - if (d + n >= 0) - incr(a); - if (negative) - negate(a); - } - return a; -} - -#define expr_s(A) A=fract((A),n,f,max_dimen) - -@ Finally, the function |fract(x,n,d,max_answer)| computes the integer -$q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive -and the result does not exceed |max_answer|. We can't use floating -point arithmetic since the routine must produce identical results in all -cases; and it would be too dangerous to multiply by~|n| and then divide -by~|d|, in separate operations, since overflow might well occur. Hence -this subroutine simulates double precision arithmetic, somewhat -analogous to Metafont's |make_fraction| and |take_fraction| routines. - -@c -int fract(int x, int n, int d, int max_answer) -{ - boolean negative; /* should the answer be negated? */ - int a; /* the answer */ - int f; /* a proper fraction */ - int h; /* smallest integer such that |2*h>=d| */ - int r; /* intermediate remainder */ - int t; /* temp variable */ - if (d == 0) - goto TOO_BIG; - a = 0; - if (d > 0) { - negative = false; - } else { - negate(d); - negative = true; - } - if (x < 0) { - negate(x); - negative = !negative; - } else if (x == 0) { - goto DONE; - } - if (n < 0) { - negate(n); - negative = !negative; - } - t = n / d; - if (t > max_answer / x) - goto TOO_BIG; - a = t * x; - n = n - t * d; - if (n == 0) - goto FOUND; - t = x / d; - if (t > (max_answer - a) / n) - goto TOO_BIG; - a = a + t * n; - x = x - t * d; - if (x == 0) - goto FOUND; - if (x < n) { - t = x; - x = n; - n = t; - } - /* now |0<n<=x<d| */ - /* Compute $f=\lfloor xn/d+{1\over2}\rfloor$; */ - /* The loop here preserves the following invariant relations - between |f|, |x|, |n|, and~|r|: - (i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor x_0n_0/d+{1\over2}\rfloor$; - (ii)~|-d<=r<0<n<=x<d|, where $x_0$, $n_0$ are the original values of~$x$ - and $n$. */ - /* Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|, - because the latter could overflow. */ - f = 0; - r = (d / 2) - d; - h = -r; - while (1) { - if (odd(n)) { - r = r + x; - if (r >= 0) { - r = r - d; - incr(f); - } - } - n = n / 2; - if (n == 0) - break; - if (x < h) { - x = x + x; - } else { - t = x - d; - x = t + x; - f = f + n; - if (x < n) { - if (x == 0) - break; - t = x; - x = n; - n = t; - } - } - } - - if (f > (max_answer - a)) - goto TOO_BIG; - a = a + f; - FOUND: - if (negative) - negate(a); - goto DONE; - TOO_BIG: - num_error(a); - DONE: - return a; -} - -@ @c -static void scan_expr(void) -{ /* scans and evaluates an expression */ - boolean a, b; /* saved values of |arith_error| */ - int l; /* type of expression */ - int r; /* state of expression so far */ - int s; /* state of term so far */ - int o; /* next operation or type of next factor */ - int e; /* expression so far */ - int t; /* term so far */ - int f; /* current factor */ - int n; /* numerator of combined multiplication and division */ - halfword p; /* top of expression stack */ - halfword q; /* for stack manipulations */ - l = cur_val_level; - a = arith_error; - b = false; - p = null; - /* Scan and evaluate an expression |e| of type |l| */ - RESTART: - r = expr_none; - e = 0; - s = expr_none; - t = 0; - n = 0; - CONTINUE: - if (s == expr_none) - o = l; - else - o = int_val_level; - /* Scan a factor |f| of type |o| or start a subexpression */ - /* Get the next non-blank non-call token */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - - if (cur_tok == other_token + '(') { - /* Push the expression stack and |goto restart| */ - q = new_node(expr_node, 0); - vlink(q) = p; - expr_type(q) = (quarterword) l; - expr_state(q) = (quarterword) (4 * s + r); - expr_e_field(q) = e; - expr_t_field(q) = t; - expr_n_field(q) = n; - p = q; - l = o; - goto RESTART; - } - back_input(); - if ((o == int_val_level) || (o == attr_val_level)) - scan_int(); - else if (o == dimen_val_level) - scan_normal_dimen(); - else if (o == glue_val_level) - scan_normal_glue(); - else - scan_mu_glue(); - f = cur_val; - - FOUND: - /* Scan the next operator and set |o| */ - /* Get the next non-blank non-call token */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - - if (cur_tok == other_token + '+') { - o = expr_add; - } else if (cur_tok == other_token + '-') { - o = expr_sub; - } else if (cur_tok == other_token + '*') { - o = expr_mult; - } else if (cur_tok == other_token + '/') { - o = expr_div; - } else { - o = expr_none; - if (p == null) { - if (cur_cmd != relax_cmd) - back_input(); - } else if (cur_tok != other_token + ')') { - print_err("Missing ) inserted for expression"); - help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't."); - back_error(); - } - } - - arith_error = b; - /* Make sure that |f| is in the proper range */ - if (((l == int_val_level) || (l == attr_val_level)) || (s > expr_sub)) { - if ((f > infinity) || (f < -infinity)) - num_error(f); - } else if (l == dimen_val_level) { - if (abs(f) > max_dimen) - num_error(f); - } else { - if ((abs(width(f)) > max_dimen) || (abs(stretch(f)) > max_dimen) || (abs(shrink(f)) > max_dimen)) - glue_error(f); - } - - switch (s) { - /* Cases for evaluation of the current term */ - case expr_none: - /* - Applying the factor |f| to the partial term |t| (with the operator - |s|) is delayed until the next operator |o| has been scanned. Here we - handle the first factor of a partial term. A glue spec has to be copied - unless the next operator is a right parenthesis; this allows us later on - to simply modify the glue components. - */ - t = f; - if ((l >= glue_val_level) && (o != expr_none)) { - /* do we really need to copy here ? */ -// t = new_spec(f); -// flush_node(f); - normalize_glue(t); - } else { - t = f; - } - break; - case expr_mult: - /* If a multiplication is followed by a division, the two operations are - combined into a `scaling' operation. Otherwise the term |t| is - multiplied by the factor |f|. */ - if (o == expr_div) { - n = f; - o = expr_scale; - } else if ((l == int_val_level) || (l == attr_val_level)) { - t = mult_integers(t, f); - } else if (l == dimen_val_level) { - expr_m(t); - } else { - expr_m(width(t)); - expr_m(stretch(t)); - expr_m(shrink(t)); - } - break; - case expr_div: - /* Here we divide the term |t| by the factor |f| */ - if (l < glue_val_level) { - expr_d(t); - } else { - expr_d(width(t)); - expr_d(stretch(t)); - expr_d(shrink(t)); - } - break; - case expr_scale: - /* Here the term |t| is multiplied by the quotient $n/f$. */ - if ((l == int_val_level) || (l == attr_val_level)) { - t = fract(t, n, f, infinity); - } else if (l == dimen_val_level) { - expr_s(t); - } else { - expr_s(width(t)); - expr_s(stretch(t)); - expr_s(shrink(t)); - } - break; - } /* there are no other cases */ - if (o > expr_sub) { - s = o; - } else { - /* Evaluate the current expression */ - /* When a term |t| has been completed it is copied to, added to, or - subtracted from the expression |e|. */ - s = expr_none; - if (r == expr_none) { - e = t; - } else if ((l == int_val_level) || (l == attr_val_level)) { - e = expr_add_sub(e, t, infinity); - } else if (l == dimen_val_level) { - e = expr_a(e, t); - } else { - /* Compute the sum or difference of two glue specs */ - /* We know that |stretch_order(e)>normal| implies |stretch(e)<>0| and - |shrink_order(e)>normal| implies |shrink(e)<>0|. */ - width(e) = expr_a(width(e), width(t)); - if (stretch_order(e) == stretch_order(t)) { - stretch(e) = expr_a(stretch(e), stretch(t)); - } else if ((stretch_order(e) < stretch_order(t)) && (stretch(t) != 0)) { - stretch(e) = stretch(t); - stretch_order(e) = stretch_order(t); - } - if (shrink_order(e) == shrink_order(t)) { - shrink(e) = expr_a(shrink(e), shrink(t)); - } else if ((shrink_order(e) < shrink_order(t)) && (shrink(t) != 0)) { - shrink(e) = shrink(t); - shrink_order(e) = shrink_order(t); - } - flush_node(t); - normalize_glue(e); - } - r = o; - } - b = arith_error; - if (o != expr_none) - goto CONTINUE; - if (p != null) { - /* Pop the expression stack and |goto found| */ - f = e; - q = p; - e = expr_e_field(q); - t = expr_t_field(q); - n = expr_n_field(q); - s = expr_state(q) / 4; - r = expr_state(q) % 4; - l = expr_type(q); - p = vlink(q); - flush_node(q); - goto FOUND; - } - - if (b) { - print_err("Arithmetic overflow"); - help2("I can't evaluate this expression,", - "since the result is out of range."); - error(); - if (l >= glue_val_level) { - reset_glue_to_zero(e); - } else { - e = 0; - } - } - arith_error = a; - cur_val = e; - cur_val_level = l; -} diff --git a/Build/source/texk/web2c/luatexdir/tex/stringpool.w b/Build/source/texk/web2c/luatexdir/tex/stringpool.c index 5972657f56e..bc09f814b00 100644 --- a/Build/source/texk/web2c/luatexdir/tex/stringpool.w +++ b/Build/source/texk/web2c/luatexdir/tex/stringpool.c @@ -1,69 +1,89 @@ -% stringpool.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* +stringpool.w + +Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ Control sequence names and diagnostic messages are variable-length strings -of eight-bit characters. Since PASCAL did not have a well-developed string +/*tex + +Control sequence names and diagnostic messages are variable-length strings of +eight-bit characters. Since PASCAL did not have a well-developed string mechanism, \TeX\ did all of its string processing by homegrown methods. -Elaborate facilities for dynamic strings are not needed, so all of the -necessary operations can be handled with a simple data structure. -The array |str_pool| contains all of the (eight-bit) bytes off all -of the strings, and the array |str_start| contains indices of the starting -points of each string. Strings are referred to by integer numbers, so that -string number |s| comprises the characters |str_pool[j]| for -|str_start_macro(s)<=j<str_start_macro(s+1)|. Additional integer variables -|pool_ptr| and |str_ptr| indicate the number of entries used so far -in |str_pool| and |str_start|, respectively; locations -|str_pool[pool_ptr]| and |str_start_macro(str_ptr)| are -ready for the next string to be allocated. - -String numbers 0 to |biggest_char| are reserved for strings that correspond to +Elaborate facilities for dynamic strings are not needed, so all of the necessary +operations can be handled with a simple data structure. The array |str_pool| +contains all of the (eight-bit) bytes off all of the strings, and the array +|str_start| contains indices of the starting points of each string. Strings are +referred to by integer numbers, so that string number |s| comprises the +characters |str_pool[j]| for |str_start_macro(s)<=j<str_start_macro(s+1)|. +Additional integer variables |pool_ptr| and |str_ptr| indicate the number of +entries used so far in |str_pool| and |str_start|, respectively; locations +|str_pool[pool_ptr]| and |str_start_macro(str_ptr)| are ready for the next string +to be allocated. + +String numbers 0 to |biggest_char| are reserved for strings that correspond to single UNICODE characters. This is in accordance with the conventions of \.{WEB} -which converts single-character strings into the ASCII code number of the -single character involved. +which converts single-character strings into the ASCII code number of the single +character involved. + +*/ + +/*tex The array of strings: */ + +lstring *string_pool; + +/*tex + This variable lives |STRING_OFFSET| below |string_pool| (handy for debugging: + |_string_pool[str_ptr] == str_string(str_ptr)|: +*/ + +lstring *_string_pool; + +/*tex The number of the current string being created: */ + +str_number str_ptr = (STRING_OFFSET + 1); + +/*tex The starting value of |str_ptr|: */ -@c -lstring *string_pool; /* the array of strings */ -lstring *_string_pool; /* this variable lives |STRING_OFFSET| below |string_pool| - (handy for debugging: - |_string_pool[str_ptr] == str_string(str_ptr)| */ +str_number init_str_ptr; -str_number str_ptr = (STRING_OFFSET + 1); /* number of the current string being created */ -str_number init_str_ptr; /* the starting value of |str_ptr| */ +/*tex + The current string buffer, the current index in that buffer, the malloced + size of |cur_string| and the occupied byte count: +*/ -unsigned char *cur_string; /* current string buffer */ -unsigned cur_length; /* current index in that buffer */ -unsigned cur_string_size; /* malloced size of |cur_string| */ -unsigned pool_size; /* occupied byte count */ +unsigned char *cur_string; +unsigned cur_length; +unsigned cur_string_size; +unsigned pool_size; +/*tex -@ Once a sequence of characters has been appended to |cur_string|, it -officially becomes a string when the function |make_string| is called. -This function returns the identification number of the new string as its -value. +Once a sequence of characters has been appended to |cur_string|, it officially +becomes a string when the function |make_string| is called. This function returns +the identification number of the new string as its value. + +*/ -@c void reset_cur_string(void) { cur_length = 0; @@ -72,44 +92,41 @@ void reset_cur_string(void) memset(cur_string, 0, 256); } -@ current string enters the pool -@c str_number make_string(void) { - if (str_ptr == (max_strings + STRING_OFFSET)) - overflow("number of strings", - (unsigned) (max_strings - init_str_ptr + STRING_OFFSET)); + if (str_ptr == (max_strings + STRING_OFFSET)) { + overflow( + "number of strings", + (unsigned) (max_strings - init_str_ptr + STRING_OFFSET) + ); + } str_room(1); cur_string[cur_length] = '\0'; /* now |lstring.s| is always a valid C string */ str_string(str_ptr) = (unsigned char *) cur_string; str_length(str_ptr) = cur_length; pool_size += cur_length; reset_cur_string(); -#if 0 - printf("Made a string: %s (s=%d)\n", (char *)str_string(str_ptr), (int)str_ptr); -#endif str_ptr++; return (str_ptr - 1); } -@ @c int pool_to_unichar(unsigned char *t) { return (int) str2uni(t); } +/*tex +The following subroutine compares string |s| with another string of the same +length that appears in |buffer| starting at position |k|; the result is |true| if +and only if the strings are equal. Empirical tests indicate that |str_eq_buf| is +used in such a way that it tends to return |true| about 80 percent of the time. -@ The following subroutine compares string |s| with another string of the -same length that appears in |buffer| starting at position |k|; -the result is |true| if and only if the strings are equal. -Empirical tests indicate that |str_eq_buf| is used in such a way that -it tends to return |true| about 80 percent of the time. +*/ -@c boolean str_eq_buf(str_number s, int k) -{ /* test equality of strings */ - int a; /* a unicode character */ +{ + int a; if (s < STRING_OFFSET) { a = buffer_to_unichar(k); if (a != s) @@ -125,15 +142,17 @@ boolean str_eq_buf(str_number s, int k) return true; } +/*tex + +Here is a similar routine, but it compares two strings in the string pool, and it +does not assume that they have the same length. -@ Here is a similar routine, but it compares two strings in the string pool, -and it does not assume that they have the same length. +*/ -@c boolean str_eq_str(str_number s, str_number t) -{ /* test equality of strings */ - int a = 0; /* a utf char */ - unsigned char *j, *k, *l; /* running indices */ +{ + int a = 0; + unsigned char *j, *k, *l; if (s < STRING_OFFSET) { if (t >= STRING_OFFSET) { k = str_string(t); @@ -167,8 +186,8 @@ boolean str_eq_str(str_number s, str_number t) return true; } -@ string compare -@c +/*tex A string compare helper: */ + boolean str_eq_cstr(str_number r, const char *s, size_t l) { if (l != (size_t) str_length(r)) @@ -177,43 +196,47 @@ boolean str_eq_cstr(str_number r, const char *s, size_t l) } -@ The initial values of |str_pool|, |str_start|, |pool_ptr|, -and |str_ptr| are computed by the \.{INITEX} program, based in part -on the information that \.{WEB} has output while processing \TeX. +/*tex -The first |string_offset| strings are single-characters strings matching -Unicode. There is no point in generating all of these. But |str_ptr| has -initialized properly, otherwise |print_char| cannot see the difference -between characters and strings. +The initial values of |str_pool|, |str_start|, |pool_ptr|, and |str_ptr| are +computed by the \.{INITEX} program, based in part on the information that \.{WEB} +has output while processing \TeX. +The first |string_offset| strings are single-characters strings matching Unicode. +There is no point in generating all of these. But |str_ptr| has initialized +properly, otherwise |print_char| cannot see the difference between characters and +strings. + +*/ -@ initializes the string pool, but returns |false| if something goes wrong -@c boolean get_strings_started(void) { reset_cur_string(); return true; } -@ The string recycling routines. - \TeX{} uses 2 upto 4 {\it new\/} strings when scanning a filename in an - \.{\\input}, \.{\\openin}, or \.{\\openout} operation. These strings are - normally lost because the reference to them are not saved after finishing - the operation. |search_string| searches through the string pool for the - given string and returns either 0 or the found string number. +/*tex + +The string recycling routines. \TeX{} uses 2 upto 4 {\it new\/} strings when +scanning a filename in an \.{\\input}, \.{\\openin}, or \.{\\openout} operation. +These strings are normally lost because the reference to them are not saved after +finishing the operation. |search_string| searches through the string pool for the +given string and returns either 0 or the found string number. + +*/ -@c str_number search_string(str_number search) { - str_number s; /* running index */ - size_t len; /* length of searched string */ + str_number s; + size_t len; len = str_length(search); if (len == 0) { return get_nullstr(); } else { - s = search - 1; /* start search with newest string below |s|; |search>1|! */ + /*tex We start the search with newest string below |s|; |search>1|! */ + s = search - 1; while (s >= STRING_OFFSET) { - /* first |string_offset| strings depend on implementation!! */ + /* The first |string_offset| of strings depend on the implementation! */ if (str_length(s) == len) if (str_eq_str(s, search)) return s; @@ -223,7 +246,6 @@ str_number search_string(str_number search) return 0; } -@ @c str_number maketexstring(const char *s) { if (s == NULL || *s == 0) @@ -231,7 +253,6 @@ str_number maketexstring(const char *s) return maketexlstring(s, strlen(s)); } -@ @c str_number maketexlstring(const char *s, size_t l) { if (s == NULL || l == 0) @@ -243,8 +264,12 @@ str_number maketexlstring(const char *s, size_t l) return (str_ptr - 1); } -@ append a C string to a TeX string -@c +/*tex + + This appends a C string to a \TEX\ string: + +*/ + void append_string(const unsigned char *s, unsigned l) { if (s == NULL || *s == 0) @@ -256,14 +281,12 @@ void append_string(const unsigned char *s, unsigned l) return; } -@ @c char *makecstring(int s) { size_t l; return makeclstring(s, &l); } -@ @c char *makeclstring(int s, size_t * len) { if (s < STRING_OFFSET) { @@ -279,7 +302,6 @@ char *makeclstring(int s, size_t * len) } } -@ @c int dump_string_pool(void) { int j; @@ -297,7 +319,6 @@ int dump_string_pool(void) return (k - STRING_OFFSET); } -@ @c int undump_string_pool(void) { int j; @@ -325,7 +346,6 @@ int undump_string_pool(void) return str_ptr; } -@ @c void init_string_pool_array(unsigned s) { string_pool = xmallocarray(lstring, s); @@ -336,14 +356,16 @@ void init_string_pool_array(unsigned s) string_pool[0].s[0] = '\0'; } -@ To destroy an already made string, we say |flush_str|. -@c +/*tex + + To destroy an already made string, we say |flush_str|. + +*/ + void flush_str(str_number s) { -#if 0 - printf("Flushing a string: %s (s=%d,str_ptr=%d)\n", (char *)str_string(s), (int)s, (int)str_ptr); -#endif - if (s > STRING_OFFSET) { /* don't ever delete the null string */ + if (s > STRING_OFFSET) { + /*tex Don't ever delete the null string! */ pool_size -= (unsigned) str_length(s); str_length(s) = 0; xfree(str_string(s)); diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.w b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c index 7fe5ae1e18c..f0b057d40b1 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texdeffont.w +++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c @@ -1,57 +1,73 @@ -% texdeffont.w -% -% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* +texdeffont.w + +Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number -to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the -|eqtb| location of a ``frozen'' control sequence that will always select -the font. +/*tex + +When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number to the +user's font~\.{\\f}. Adding this number to |font_id_base| gives the |eqtb| +location of a ``frozen'' control sequence that will always select the font. + +The variable |a| in the following code indicates the global nature of the value +to be set. It's used in the |define| macro. Here we're never global. + +There's not much scanner code here because the other scanners are defined where +they make most sense. + +*/ -@c int font_bytes; void set_cur_font(internal_font_number f) { - int a = 0; /* never global */ + int a = 0; define(cur_font_loc, data_cmd, f); } -@ @c +/*tex + + This prints a scaled real, rounded to five digits. + +*/ + static char *scaled_to_string(scaled s) -{ /* prints scaled real, rounded to five digits */ +{ static char result[16]; int n, k; - scaled delta; /* amount of allowable inaccuracy */ + /*tex The amount of allowable inaccuracy: */ + scaled delta; k = 0; if (s < 0) { + /*tex Only print the sign, if negative */ result[k++] = '-'; - s = -s; /* print the sign, if negative */ + s = -s; } { int l = 0; char digs[8] = { 0 }; n = s / unity; - /* process the integer part */ + /*tex Process the integer part: */ do { digs[l++] = (char) (n % 10); n = n / 10;; @@ -64,29 +80,37 @@ static char *scaled_to_string(scaled s) s = 10 * (s % unity) + 5; delta = 10; do { - if (delta > unity) - s = s + 0100000 - 050000; /* round the last digit */ + if (delta > unity) { + /*tex Round the last digit: */ + s = s + 0100000 - 050000; + } result[k++] = (char) ('0' + (s / unity)); s = 10 * (s % unity); delta = delta * 10; } while (s > delta); - result[k] = 0; return (char *) result; } -@ @c void tex_def_font(small_number a) { - pointer u; /* user's font identifier */ - internal_font_number f; /* runs through existing fonts */ - str_number t; /* name for the frozen font identifier */ - int old_setting; /* holds |selector| setting */ - scaled s = -1000; /* stated ``at'' size, or negative of scaled magnification */ - int natural_dir = -1; /* the natural direction of the font */ + /*tex The user's font identifier. */ + pointer u; + /*tex This runs through existing fonts. */ + internal_font_number f; + /*tex The name for the frozen font identifier. */ + str_number t; + /*tex Thos holds the |selector| setting. */ + int old_setting; + /*tex Stated `at' size, or negative of scaled magnification. */ + scaled s = -1000; + /*tex The natural direction of the font. */ + int natural_dir = -1; char *fn; - if (job_name == 0) - open_log_file(); /* avoid confusing \.{texput} with the font name */ + if (job_name == 0) { + /*tex Avoid confusing \.{texput} with the font name. */ + open_log_file(); + } get_r_token(); u = cur_cs; if (u >= null_cs) @@ -99,7 +123,7 @@ void tex_def_font(small_number a) eq_define(u, set_font_cmd, null_font); } scan_optional_equals(); - /* Get the next non-blank non-call token; */ + /*tex Get the next non-blank non-call token. */ do { get_x_token(); } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); @@ -108,8 +132,10 @@ void tex_def_font(small_number a) back_input(); scan_file_name(); if (cur_area != get_nullstr() || cur_ext != get_nullstr()) { - /* Have to do some rescue-ing here, fonts only have a name, - no area nor extension */ + /*tex + Have to do some rescue-ing here, fonts only have a name, no area + nor extension. + */ old_setting = selector; selector = new_string; if (cur_area != get_nullstr()) { @@ -134,26 +160,27 @@ void tex_def_font(small_number a) token_show(def_ref); selector = old_setting; flush_list(def_ref); - /* |str_room(1)|; *//* what did that do ? */ cur_name = make_string(); cur_ext = get_nullstr(); cur_area = get_nullstr(); } - /* Scan the font size specification; */ - name_in_progress = true; /* this keeps |cur_name| from being changed */ + /*tex + Scan the font size specification. The next variable keeps |cur_name| from + being changed + */ + name_in_progress = true; if (scan_keyword("at")) { - /* Put the positive `at' size into |s| */ + /*tex Put the positive `at' size into |s|. */ scan_normal_dimen(); s = cur_val; if ((s <= 0) || (s >= 01000000000)) { char err[256]; - const char *errhelp[] = - { "I can only handle fonts at positive sizes that are", + const char *errhelp[] = { + "I can only handle fonts at positive sizes that are", "less than 2048pt, so I've changed what you said to 10pt.", NULL }; - snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt", - scaled_to_string(s)); + snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt", scaled_to_string(s)); tex_error(err, errhelp); s = 10 * unity; } @@ -162,27 +189,31 @@ void tex_def_font(small_number a) s = -cur_val; if ((cur_val <= 0) || (cur_val > 32768)) { char err[256]; - const char *errhelp[] = - { "The magnification ratio must be between 1 and 32768.", + const char *errhelp[] = { + "The magnification ratio must be between 1 and 32768.", NULL }; - snprintf(err, 255, - "Illegal magnification has been changed to 1000 (%d)", - (int) cur_val); + snprintf(err, 255, "Illegal magnification has been changed to 1000 (%d)", (int) cur_val); tex_error(err, errhelp); s = -1000; } } + /*tex + There is no real reason to support this obsolete key as there are no useful + fonts out there so let's get rid of this overhead. This also means that + |natural_dir| can go away. + */ + /* if (scan_keyword("naturaldir")) { scan_direction(); natural_dir = cur_val; } + */ name_in_progress = false; fn = makecstring(cur_name); f = read_font_info(u, fn, s, natural_dir); xfree(fn); equiv(u) = f; - eqtb[font_id_base + f] = eqtb[u]; cs_text(font_id_base + f) = t; } diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.c b/Build/source/texk/web2c/luatexdir/tex/texfileio.c new file mode 100644 index 00000000000..143e694e1ba --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texfileio.c @@ -0,0 +1,1509 @@ +/* + +Copyright 2009-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" + +#include <string.h> +#include <kpathsea/absolute.h> + +/*tex + +The bane of portability is the fact that different operating systems treat input +and output quite differently, perhaps because computer scientists have not given +sufficient attention to this problem. People have felt somehow that input and +output are not part of ``real'' programming. Well, it is true that some kinds of +programming are more fun than others. With existing input/output conventions +being so diverse and so messy, the only sources of joy in such parts of the code +are the rare occasions when one can find a way to make the program a little less +bad than it might have been. We have two choices, either to attack I/O now and +get it over with, or to postpone I/O until near the end. Neither prospect is very +attractive, so let's get it over with. + +The basic operations we need to do are (1)~inputting and outputting of text, to +or from a file or the user's terminal; (2)~inputting and outputting of eight-bit +bytes, to or from a file; (3)~instructing the operating system to initiate +(``open'') or to terminate (``close'') input or output from a specified file; +(4)~testing whether the end of an input file has been reached. + +\TeX\ needs to deal with two kinds of files. We shall use the term |alpha_file| +for a file that contains textual data, and the term |byte_file| for a file that +contains eight-bit binary information. These two types turn out to be the same on +many computers, but sometimes there is a significant distinction, so we shall be +careful to distinguish between them. Standard protocols for transferring such +files from computer to computer, via high-speed networks, are now becoming +available to more and more communities of users. + +The program actually makes use also of a third kind of file, called a +|word_file|, when dumping and reloading base information for its own +initialization. We shall define a word file later; but it will be possible for us +to specify simple operations on word files before they are defined. + +We finally did away with |nameoffile| and |namelength|, but the variables have to +be kept otherwise there will be link errors from |openclose.c| in the web2c +library + +*/ + +char *nameoffile; +int namelength; + +/*tex + + When input files are opened via a callback, they will also be read using + callbacks. for that purpose, the |open_read_file_callback| returns an integer + to uniquely identify a callback table. This id replaces the file point |f| in + this case, because the input does not have to be a file in the traditional + sense. + + Signalling this fact is achieved by having two arrays of integers. + +*/ + +int *input_file_callback_id; +int read_file_callback_id[17]; + +/*tex + + Here we handle |-output-directory|. We assume that it is OK to look here + first. Possibly it would be better to replace lookups in "." with lookups in + the |output_directory| followed by "." but to do this requires much more + invasive surgery in libkpathsea. + +*/ + +static char *find_in_output_directory(const char *s) +{ + if (output_directory && !kpse_absolute_p(s, false)) { + FILE *f_ptr; + char *ftemp = concat3(output_directory, DIR_SEP_STRING, s); + /*tex This code is used for input files only. */ + f_ptr = fopen(ftemp, "rb"); + if (f_ptr) { + fclose(f_ptr); + return ftemp; + } else { + free(ftemp); + + } + } + return NULL; +} + +/*tex + + Find an \.{\\input} or \.{\\read} file. |n| differentiates between those + case. + +*/ + +int kpse_available(const char *m) { + if (!kpse_init) { + fprintf(stdout,"missing kpse replacement callback '%s', quitting\n",m); + exit(1); + } + return 1 ; +} + +char *luatex_find_read_file(const char *s, int n, int callback_index) +{ + char *ftemp = NULL; + int callback_id = callback_defined(callback_index); + if (callback_id > 0) { + (void) run_callback(callback_id, "dS->R", n, s, &ftemp); + } else if (kpse_available("find_read_file")) { + /*tex Use kpathsea here. */ + ftemp = find_in_output_directory(s); + if (!ftemp) + ftemp = kpse_find_file(s, kpse_tex_format, 1); + } + if (ftemp) { + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = xstrdup(ftemp); + } + return ftemp; +} + +/*tex Find other files types. */ + +char *luatex_find_file(const char *s, int callback_index) +{ + char *ftemp = NULL; + int callback_id = callback_defined(callback_index); + if (callback_id > 0) { + (void) run_callback(callback_id, "S->R", s, &ftemp); + } else if (kpse_available("find_read_file")) { + /*tex Use kpathsea here. */ + switch (callback_index) { + case find_enc_file_callback: + ftemp = kpse_find_file(s, kpse_enc_format, 0); + break; + case find_map_file_callback: + ftemp = kpse_find_file(s, kpse_fontmap_format, 0); + break; + case find_type1_file_callback: + ftemp = kpse_find_file(s, kpse_type1_format, 0); + break; + case find_truetype_file_callback: + ftemp = kpse_find_file(s, kpse_truetype_format, 0); + break; + case find_opentype_file_callback: + ftemp = kpse_find_file(s, kpse_opentype_format, 0); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_truetype_format, 0); + break; + case find_data_file_callback: + ftemp = find_in_output_directory(s); + if (!ftemp) + ftemp = kpse_find_file(s, kpse_tex_format, 1); + break; + case find_font_file_callback: + ftemp = kpse_find_file(s, kpse_ofm_format, 1); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_tfm_format, 1); + break; + case find_vf_file_callback: + ftemp = kpse_find_file(s, kpse_ovf_format, 0); + if (ftemp == NULL) + ftemp = kpse_find_file(s, kpse_vf_format, 0); + break; + case find_cidmap_file_callback: + ftemp = kpse_find_file(s, kpse_cid_format, 0); + break; + default: + printf("luatex_find_file(): do not know how to handle file %s of type %d\n", s, callback_index); + break; + } + } + return ftemp; +} + +/*tex + + \LUATEX\ used to have private functions for these that did not use kpathsea, + but since the file paranoia tests have to come from kpathsea anyway, that is + no longer useful. The only downside to using luatex is that if one wants to + disable kpathsea via the Lua startup script, it is now an absolute + requirement that all file discovery callbacks are specified. Just using the + find_read_file, but not setting open_read_file, for example, does not work + any more if kpathsea is not to be used at all. + +*/ + +#define openoutnameok(A) kpse_out_name_ok (A) +#define openinnameok(A) kpse_in_name_ok (A) + +/*tex + + Open an input file F, using the kpathsea format FILEFMT and passing + |FOPEN_MODE| to fopen. The filename is in `fn'. We return whether or not the + open succeeded. + +*/ + +boolean luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, const_string fopen_mode, boolean must_exist) +{ + /*tex We haven't found anything yet. */ + string fname = NULL; + *f_ptr = NULL; + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = NULL; + fname = kpse_find_file(fn, (kpse_file_format_type) filefmt, must_exist); + if (fname) { + fullnameoffile = xstrdup(fname); + /*tex + + If we found the file in the current directory, don't leave the `./' + at the beginning of `fn', since it looks dumb when `tex foo' says + `(./foo.tex ... )'. On the other hand, if the user said `tex ./foo', + and that's what we opened, then keep it -- the user specified it, so + we shouldn't remove it. + + */ + if (fname[0] == '.' && IS_DIR_SEP(fname[1]) && (fn[0] != '.' || !IS_DIR_SEP(fn[1]))) { + unsigned i = 0; + while (fname[i + 2] != 0) { + fname[i] = fname[i + 2]; + i++; + } + fname[i] = 0; + } + /*tex This fopen is not allowed to fail. */ + *f_ptr = xfopen(fname, fopen_mode); + } + if (*f_ptr) { + recorder_record_input(fname); + } + return *f_ptr != NULL; +} + +boolean luatex_open_output(FILE ** f_ptr, const char *fn, const_string fopen_mode) +{ + char *fname; + boolean absolute = kpse_absolute_p(fn, false); + /*tex If we have an explicit output directory, use it. */ + if (output_directory && !absolute) { + fname = concat3(output_directory, DIR_SEP_STRING, fn); + } else { + fname = xstrdup(fn); + } + /*tex Is the filename openable as given? */ + *f_ptr = fopen(fname, fopen_mode); + if (!*f_ptr) { + /*tex Can't open as given. Try the envvar. */ + string texmfoutput = kpse_var_value("TEXMFOUTPUT"); + if (texmfoutput && *texmfoutput && !absolute) { + fname = concat3(texmfoutput, DIR_SEP_STRING, fn); + *f_ptr = fopen(fname, fopen_mode); + } + } + if (*f_ptr) { + recorder_record_output(fname); + } + free(fname); + return *f_ptr != NULL; +} + +boolean lua_a_open_in(alpha_file * f, char *fn, int n) +{ + int k; + char *fnam; + int callback_id; + boolean ret = true; + boolean file_ok = true; + if (n == 0) { + input_file_callback_id[iindex] = 0; + } else { + read_file_callback_id[n] = 0; + } + if (*fn == '|') + fnam = fn; + else + fnam = luatex_find_read_file(fn, n, find_read_file_callback); + if (!fnam) + return false; + callback_id = callback_defined(open_read_file_callback); + if (callback_id > 0) { + k = run_and_save_callback(callback_id, "S->", fnam); + if (k > 0) { + ret = true; + if (n == 0) + input_file_callback_id[iindex] = k; + else + read_file_callback_id[n] = k; + } else { + /*tex read failed */ + file_ok = false; + } + } else { + /*tex no read callback */ + if (openinnameok(fnam)) { + ret = open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, (n == 0 ? true : false)); + } else { + /*tex open failed */ + file_ok = false; + } + } + if (!file_ok) { + ret = false; + } + return ret; +} + +boolean lua_a_open_out(alpha_file * f, char *fn, int n) +{ + boolean test; + str_number fnam; + int callback_id; + boolean ret = false; + callback_id = callback_defined(find_write_file_callback); + if (callback_id > 0) { + fnam = 0; + test = run_callback(callback_id, "dS->s", n, fn, &fnam); + if ((test) && (fnam != 0) && (str_length(fnam) > 0)) { + /*tex + + There is no message here because if that is needed the macro + package should do that in the callback code. As elsewhere, + messaging is left to \LUA\ then. + + */ + ret = open_outfile(f, fn, FOPEN_W_MODE); + } + } else { + if (openoutnameok(fn)) { + if (n > 0 && selector != term_only) { + /*tex + + This message to the log is for downward compatibility with + other tex's as there are scripts out there that act on this + message. An alternative is to let a macro package write an + explicit message. + + */ + fprintf(log_file,"\n\\openout%i = %s\n",n-1,fn); + } + ret = open_out_or_pipe(f, fn, FOPEN_W_MODE); + } + } + return ret; +} + +boolean lua_b_open_out(alpha_file * f, char *fn) +{ + boolean test; + str_number fnam; + int callback_id; + boolean ret = false; + callback_id = callback_defined(find_output_file_callback); + if (callback_id > 0) { + fnam = 0; + test = run_callback(callback_id, "S->s", fn, &fnam); + if ((test) && (fnam != 0) && (str_length(fnam) > 0)) { + ret = open_outfile(f, fn, FOPEN_WBIN_MODE); + } + } else { + if (openoutnameok(fn)) { + ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE); + } + } + return ret; +} + +void lua_a_close_in(alpha_file f, int n) +{ + int callback_id; + if (n == 0) + callback_id = input_file_callback_id[iindex]; + else + callback_id = read_file_callback_id[n]; + if (callback_id > 0) { + run_saved_callback(callback_id, "close", "->"); + destroy_saved_callback(callback_id); + if (n == 0) + input_file_callback_id[iindex] = 0; + else + read_file_callback_id[n] = 0; + } else { + close_file_or_pipe(f); + } +} + +void lua_a_close_out(alpha_file f) +{ + close_file_or_pipe(f); +} + +/*tex + + Binary input and output are done with C's ordinary procedures, so we don't + have to make any other special arrangements for binary~I/O. Text output is + also easy to do with standard routines. The treatment of text input is more + difficult, however, because of the necessary translation to |ASCII_code| + values. \TeX's conventions should be efficient, and they should blend nicely + with the user's operating environment. + + Input from text files is read one line at a time, using a routine called + |lua_input_ln|. This function is defined in terms of global variables called + |buffer|, |first|, and |last| that will be described in detail later; for + now, it suffices for us to know that |buffer| is an array of |ASCII_code| + values, and that |first| and |last| are indices into this array representing + the beginning and ending of a line of text. + +*/ + +/*tex lines of characters being read */ + +packed_ASCII_code *buffer; + +/*tex the first unused position in |buffer| */ + +int first; + +/*tex end of the line just input to |buffer| */ + +int last; + +/*tex largest index used in |buffer| */ + +int max_buf_stack; + +/*tex + + The |lua_input_ln| function brings the next line of input from the specified + file into available positions of the buffer array and returns the value + |true|, unless the file has already been entirely read, in which case it + returns |false| and sets |last:=first|. In general, the |ASCII_code| numbers + that represent the next line of the file are input into |buffer[first]|, + |buffer[first+1]|, \dots, |buffer[last-1]|; and the global variable |last| is + set equal to |first| plus the length of the line. Trailing blanks are removed + from the line; thus, either |last=first| (in which case the line was entirely + blank) or |buffer[last-1]<>" "|. + + An overflow error is given, however, if the normal actions of |lua_input_ln| + would make |last>=buf_size|; this is done so that other parts of \TeX\ can + safely look at the contents of |buffer[last+1]| without overstepping the + bounds of the |buffer| array. Upon entry to |lua_input_ln|, the condition + |first<buf_size| will always hold, so that there is always room for an + ``empty'' line. + + The variable |max_buf_stack|, which is used to keep track of how large the + |buf_size| parameter must be to accommodate the present job, is also kept up + to date by |lua_input_ln|. + + If the |bypass_eoln| parameter is |true|, |lua_input_ln| will do a |get| + before looking at the first character of the line; this skips over an |eoln| + that was in |f^|. The procedure does not do a |get| when it reaches the end + of the line; therefore it can be used to acquire input from the user's + terminal as well as from ordinary text files. + + Since the inner loop of |lua_input_ln| is part of \TeX's ``inner + loop''---each character of input comes in at this place---it is wise to + reduce system overhead by making use of special routines that read in an + entire array of characters at once, if such routines are available. + +*/ + +boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln) +{ + boolean lua_result; + int last_ptr; + int callback_id; + /*tex Todo: variable can be removed: */ + (void) bypass_eoln; + if (n == 0) + callback_id = input_file_callback_id[iindex]; + else + callback_id = read_file_callback_id[n]; + if (callback_id > 0) { + last = first; + last_ptr = first; + lua_result = + run_saved_callback(callback_id, "reader", "->l", &last_ptr); + if ((lua_result == true) && (last_ptr != 0)) { + last = last_ptr; + if (last > max_buf_stack) + max_buf_stack = last; + } else { + lua_result = false; + } + } else { + lua_result = input_ln(f, bypass_eoln); + } + if (lua_result == true) { + /*tex Fix up the input buffer using callbacks */ + if (last >= first) { + callback_id = callback_defined(process_input_buffer_callback); + if (callback_id > 0) { + last_ptr = first; + lua_result = + run_callback(callback_id, "l->l", (last - first), + &last_ptr); + if ((lua_result == true) && (last_ptr != 0)) { + last = last_ptr; + if (last > max_buf_stack) + max_buf_stack = last; + } + } + } + return true; + } + return false; +} + +/*tex + + We need a special routine to read the first line of \TeX\ input from the + user's terminal. This line is different because it is read before we have + opened the transcript file; there is sort of a ``chicken and egg'' problem + here. If the user types `\.{\\input paper}' on the first line, or if some + macro invoked by that line does such an \.{\\input}, the transcript file will + be named `\.{paper.log}'; but if no \.{\\input} commands are performed during + the first line of terminal input, the transcript file will acquire its + default name `\.{texput.log}'. (The transcript file will not contain error + messages generated by the first line before the first \.{\\input} command.) + + The first line is special also because it may be read before \TeX\ has input + a format file. In such cases, normal error messages cannot yet be given. The + following code uses concepts that will be explained later. + + Different systems have different ways to get started. But regardless of what + conventions are adopted, the routine that initializes the terminal should + satisfy the following specifications: + + \startitemize[n] + + \startitem + It should open file |term_in| for input from the terminal. (The file + |term_out| will already be open for output to the terminal.) + \stopitem + + \startitem + If the user has given a command line, this line should be considered + the first line of terminal input. Otherwise the user should be + prompted with `\.{**}', and the first line of input should be + whatever is typed in response. + \stopitem + + \startitem + The first line of input, which might or might not be a command line, + should appear in locations |first| to |last-1| of the |buffer| array. + \stopitem + + \startitem + The global variable |loc| should be set so that the character to be + read next by \TeX\ is in |buffer[loc]|. This character should not be + blank, and we should have |loc<last|. + \stopitem + + \stopitemize + + It may be necessary to prompt the user several times before a non-blank line + comes in. The prompt is `\.{**}' instead of the later `\.*' because the + meaning is slightly different: `\.{\\input}' need not be typed immediately + after~`\.{**}'.) + + The following program does the required initialization. Iff anything has been + specified on the command line, then |t_open_in| will return with |last > + first|. + +*/ + +boolean init_terminal(void) +{ + /*tex This gets the terminal input started. */ + t_open_in(); + if (last > first) { + iloc = first; + while ((iloc < last) && (buffer[iloc] == ' ')) + incr(iloc); + if (iloc < last) { + return true; + } + } + while (1) { + wake_up_terminal(); + fputs("**", term_out); + update_terminal(); + if (!input_ln(term_in, true)) { + /*tex This shouldn't happen. */ + fputs("\n! End of file on the terminal... why?\n", term_out); + return false; + } + iloc = first; + while ((iloc < last) && (buffer[iloc] == ' ')) { + incr(iloc); + } + /*tex Return unless the line was all blank. */ + if (iloc < last) { + return true; + } + fputs("Please type the name of your input file.\n", term_out); + } +} + + +/*tex + + Here is a procedure that asks the user to type a line of input, assuming that + the |selector| setting is either |term_only| or |term_and_log|. The input is + placed into locations |first| through |last-1| of the |buffer| array, and + echoed on the transcript file if appropriate. + +*/ + +void term_input(void) +{ + /*tex Index into |buffer|: */ + int k; + /*tex Now the user sees the prompt for sure: */ + update_terminal(); + if (!input_ln(term_in, true)) + fatal_error("End of file on the terminal!"); + /*tex The user's line ended with \.{<return>}: */ + term_offset = 0; + /*tex Prepare to echo the input. */ + decr(selector); + if (last != first) { + for (k = first; k <= last - 1; k++) + print_char(buffer[k]); + } + print_ln(); + /*tex Restore previous status. */ + incr(selector); +} + +/*tex + + It's time now to fret about file names. Besides the fact that different + operating systems treat files in different ways, we must cope with the fact + that completely different naming conventions are used by different groups of + people. The following programs show what is required for one particular + operating system; similar routines for other systems are not difficult to + devise. + + \TeX\ assumes that a file name has three parts: the name proper; its + ``extension''; and a ``file area'' where it is found in an external file + system. The extension of an input file or a write file is assumed to be + `\.{.tex}' unless otherwise specified; it is `\.{.log}' on the transcript + file that records each run of \TeX; it is `\.{.tfm}' on the font metric files + that describe characters in the fonts \TeX\ uses; it is `\.{.dvi}' on the + output files that specify typesetting information; and it is `\.{.fmt}' on + the format files written by \.{INITEX} to initialize \TeX. The file area can + be arbitrary on input files, but files are usually output to the user's + current area. If an input file cannot be found on the specified area, \TeX\ + will look for it on a special system area; this special area is intended for + commonly used input files like \.{webmac.tex}. + + Simple uses of \TeX\ refer only to file names that have no explicit extension + or area. For example, a person usually says `\.{\\input} \.{paper}' or + `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input} \.{paper.new}' + or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file names are best, + because they make the \TeX\ source files portable; whenever a file name + consists entirely of letters and digits, it should be treated in the same way + by all implementations of \TeX. However, users need the ability to refer to + other files in their environment, especially when responding to error + messages concerning unopenable files; therefore we want to let them use the + syntax that appears in their favorite operating system. + + The following procedures don't allow spaces to be part of file names; but + some users seem to like names that are spaced-out. System-dependent changes + to allow such things should probably be made with reluctance, and only when + an entire file name that includes spaces is ``quoted'' somehow. + + Here are the global values that file names will be scanned into. + +*/ + +/*tex name of file just scanned */ + +str_number cur_name; + +/*tex file area just scanned, or \.{""} */ + +str_number cur_area; + +/*tex file extension just scanned, or \.{""} */ + +str_number cur_ext; + +/*tex + + The file names we shall deal with have the following structure: If the name + contains `\./' or `\.:' (for Amiga only), the file area consists of all + characters up to and including the final such character; otherwise the file + area is null. If the remaining file name contains `\..', the file extension + consists of all such characters from the last `\..' to the end, otherwise the + file extension is null. + + We can scan such file names easily by using two global variables that keep + track of the occurrences of area and extension delimiters: + +*/ + +/*tex the most recent `\./', if any */ + +pool_pointer area_delimiter; + +/*tex the relevant `\..', if any */ + +pool_pointer ext_delimiter; + +/*tex + + Input files that can't be found in the user's area may appear in a standard + system area called |TEX_area|. Font metric files whose areas are not given + explicitly are assumed to appear in a standard system area called + |TEX_font_area|. $\Omega$'s compiled translation process files whose areas + are not given explicitly are assumed to appear in a standard system area. + These system area names will, of course, vary from place to place. + +*/ + +#define append_to_fn(A) do { \ + c=(A); \ + if (c!='"') { \ + if (k<file_name_size) fn[k++]=(unsigned char)(c); \ + } \ + } while (0) + + +char *pack_file_name(str_number n, str_number a, str_number e) +{ + /*tex character being packed */ + ASCII_code c; + /*tex index into |str_pool| */ + unsigned char *j; + /*tex number of positions filled in |fn| */ + int k = 0; + unsigned char *fn = xmallocarray(packed_ASCII_code, str_length(a) + str_length(n) + str_length(e) + 1); + for (j = str_string(a); j < str_string(a) + str_length(a); j++) + append_to_fn(*j); + for (j = str_string(n); j < str_string(n) + str_length(n); j++) + append_to_fn(*j); + for (j = str_string(e); j < str_string(e) + str_length(e); j++) + append_to_fn(*j); + fn[k] = 0; + return (char *) fn; +} + +/*tex + + A messier routine is also needed, since format file names must be scanned + before \TeX's string mechanism has been initialized. We shall use the global + variable |TEX_format_default| to supply the text for default system areas and + extensions related to format files. + + Under \UNIX\ we don't give the area part, instead depending on the path + searching that will happen during file opening. Also, the length will be set + in the main program. + +*/ + +char *TEX_format_default; + + +/*tex + + This part of the program becomes active when a ``virgin'' \TeX\ is trying to + get going, just after the preliminary initialization, or when the user is + substituting another format file by typing `\.\&' after the initial `\.{**}' + prompt. The buffer contains the first line of input in + |buffer[loc..(last-1)]|, where |loc<last| and |buffer[loc]<>" "|. + +*/ + +char *open_fmt_file(void) +{ + /*tex The first space after the format file name: */ + int j; + char *fmt = NULL; + int dist; + j = iloc; + if (buffer[iloc] == '&') { + incr(iloc); + j = iloc; + buffer[last] = ' '; + while (buffer[j] != ' ') + incr(j); + fmt = xmalloc((unsigned) (j - iloc + 1)); + strncpy(fmt, (char *) (buffer + iloc), (size_t) (j - iloc)); + fmt[j - iloc] = 0; + dist = (int) (strlen(fmt) - strlen(DUMP_EXT)); + if (!(strstr(fmt, DUMP_EXT) == fmt + dist)) + fmt = concat(fmt, DUMP_EXT); + if (zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) + goto FOUND; + wake_up_terminal(); + fprintf(stdout, "Sorry, I can't find the format `%s'; will try `%s'.\n", + fmt, TEX_format_default); + update_terminal(); + } + /*tex Now pull out all the stops: try for the system \.{plain} file. */ + fmt = TEX_format_default; + if (!zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) { + wake_up_terminal(); + fprintf(stdout, "I can't find the format file `%s'!\n", + TEX_format_default); + return NULL; + } + FOUND: + iloc = j; + return fmt; +} + +/*tex + + The global variable |name_in_progress| is used to prevent recursive use of + |scan_file_name|, since the |begin_name| and other procedures communicate via + global variables. Recursion would arise only by devious tricks like + `\.{\\input\\input f}'; such attempts at sabotage must be thwarted. + Furthermore, |name_in_progress| prevents \.{\\input} from being initiated + when a font size specification is being scanned. + + Another global variable, |job_name|, contains the file name that was first + \.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}' + and `\.{.fmt}' in the names of \TeX's output files. + +*/ + +/*tex is a file name being scanned? */ + +boolean name_in_progress; + +/*tex principal file name */ + +str_number job_name; + +/*tex has the transcript file been opened? */ + +boolean log_opened_global; + +/*tex + + Initially |job_name=0|; it becomes nonzero as soon as the true name is known. + We have |job_name=0| if and only if the `\.{log}' file has not been opened, + except of course for a short time just after |job_name| has become nonzero. + +*/ + +/*tex full name of the log file */ + +unsigned char *texmf_log_name; + +/*tex + + The |open_log_file| routine is used to open the transcript file and to help + it catch up to what has previously been printed on the terminal. + +*/ + +void open_log_file(void) +{ + /*tex previous |selector| setting */ + int old_setting; + /*tex index into |buffer| */ + int k; + /*tex end of first input line */ + int l; + char *fn; + old_setting = selector; + if (job_name == 0) + job_name = getjobname(maketexstring("texput")); + fn = pack_job_name(".fls"); + recorder_change_filename(fn); + fn = pack_job_name(".log"); + while (!lua_a_open_out(&log_file, fn, 0)) { + /*tex + + Try to get a different log file name. Sometimes |open_log_file| is + called at awkward moments when \TeX\ is unable to print error + messages or even to |show_context|. The |prompt_file_name| routine + can result in a |fatal_error|, but the |error| routine will not be + invoked because |log_opened| will be false. + + The normal idea of |batch_mode| is that nothing at all should be + written on the terminal. However, in the unusual case that no log + file could be opened, we make an exception and allow an explanatory + message to be seen. + + Incidentally, the program always refers to the log file as a + `\.{transcript file}', because some systems cannot use the extension + `\.{.log}' for this file. + */ + selector = term_only; + fn = prompt_file_name("transcript file name", ".log"); + } + texmf_log_name = (unsigned char *) xstrdup(fn); + selector = log_only; + log_opened_global = true; + if (callback_defined(start_run_callback) == 0) { + /*tex Print the banner line, including current date and time. */ + log_banner(luatex_version_string); + /*tex Make sure bottom level is in memory. */ + input_stack[input_ptr] = cur_input; + tprint_nl("**"); + /*tex The last position of first line. */ + l = input_stack[0].limit_field; + if (buffer[l] == end_line_char_par) { + /*tex maybe also handle multichar endlinechar */ + decr(l); + } + for (k = 1; k <= l; k++) { + print_char(buffer[k]); + } + /*tex now the transcript file contains the first line of input */ + print_ln(); + } + /*tex should be done always */ + flush_loggable_info(); + /*tex should be done always */ + selector = old_setting + 2; +} + +/*tex + + This function is needed by synctex to make its log appear in the right spot + when |output_directory| is set. + +*/ + +char *get_full_log_name (void) +{ + if (output_directory) { + char *ret = xmalloc(strlen((char *)texmf_log_name)+2+strlen(output_directory)); + ret = strcpy(ret, output_directory); + strcat(ret, "/"); + strcat(ret, (char *)texmf_log_name); + return ret; + } else { + return xstrdup((const char*)texmf_log_name); + } +} + +/*tex Synctex uses this to get the anchored path of an input file. */ + +char *luatex_synctex_get_current_name (void) +{ + char *pwdbuf = NULL, *ret; + if (kpse_absolute_p(fullnameoffile, false)) { + return xstrdup(fullnameoffile); + } + pwdbuf = xgetcwd(); + ret = concat3(pwdbuf, DIR_SEP_STRING, fullnameoffile); + free(pwdbuf) ; + return ret; +} + +/*tex + + Let's turn now to the procedure that is used to initiate file reading when an + `\.{\\input}' command is being processed. + +*/ + +void start_input(void) +{ + str_number temp_str; + char *fn; + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + back_input(); + if (cur_cmd != left_brace_cmd) { + /*tex Set |cur_name| to desired file name. */ + scan_file_name(); + } else { + scan_file_name_toks(); + } + fn = pack_file_name(cur_name, cur_area, cur_ext); + while (1) { + /*tex Set up |cur_file| and new level of input. */ + begin_file_reading(); + if (lua_a_open_in(&cur_file, fn, 0)) { + break; + } + /*tex Remove the level that didn't work. */ + end_file_reading(); + fn = prompt_file_name("input file name", ""); + } + iname = maketexstring(fullnameoffile); + /*tex + + Now that we have |fullnameoffile|, it is time to post-adjust |cur_name| + and |cur_ext| for trailing |.tex|. + + */ + { + char *n, *p; + n = p = fullnameoffile + strlen(fullnameoffile); + while (p>fullnameoffile) { + p--; + if (IS_DIR_SEP(*p)) { + break; + } + } + if (IS_DIR_SEP(*p)) { + p++; + } + while (n>fullnameoffile) { + n--; + if (*n == '.') { + break; + } + } + if (n>p) { + int q = *n; + cur_ext = maketexstring(n); + *n = 0; + cur_name = maketexstring(p); + *n = q; + } + } + source_filename_stack[in_open] = iname; + full_source_filename_stack[in_open] = xstrdup(fullnameoffile); + /*tex We can try to conserve string pool space now. */ + temp_str = search_string(iname); + if (temp_str > 0) { + flush_str(iname); + iname = temp_str; + } + if (job_name == 0) { + job_name = getjobname(cur_name); + open_log_file(); + } + /*tex + + |open_log_file| doesn't |show_context|, so |limit| and |loc| needn't be + set to meaningful values yet. + + */ + report_start_file(filetype_tex,fullnameoffile); + incr(open_parens); + update_terminal(); + istate = new_line; + /*tex Prepare new file {\sl Sync\TeX} information. */ + if (! synctex_get_no_files()) { + /*tex Give control to the {\sl Sync\TeX} controller. */ + synctexstartinput(); + } + /*tex + + Read the first line of the new file. Here we have to remember to tell the + |lua_input_ln| routine not to start with a |get|. If the file is empty, + it is considered to contain a single blank line. + + */ + line = 1; + if (lua_input_ln(cur_file, 0, false)) { + ; + } + firm_up_the_line(); + if (end_line_char_inactive) + decr(ilimit); + else + buffer[ilimit] = (packed_ASCII_code) end_line_char_par; + first = ilimit + 1; + iloc = istart; +} + +/*tex + + Because the format is zipped we read and write dump files through zlib. + Earlier versions recast |*f| from |FILE *| to |gzFile|, but there is no + guarantee that these have the same size, so a static variable is needed. + +*/ + +static gzFile gz_fmtfile = NULL; + +/*tex + + As distributed, the dump files are architecture dependent; specifically, + BigEndian and LittleEndian architectures produce different files. These + routines always output BigEndian files. This still does not guarantee them to + be architecture-independent, because it is possible to make a format that + dumps a glue ratio, i.e., a floating-point number. Fortunately, none of the + standard formats do that. + +*/ + +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + +/*tex + + This macro is always invoked as a statement. It assumes a variable `temp'. + +*/ + +# define SWAP(x, y) do { temp = x; x = y; y = temp; } while (0) + +/*tex + + Make the NITEMS items pointed at by P, each of size SIZE, be the + opposite-endianness of whatever they are now. + +*/ + +static void swap_items(char *pp, int nitems, int size) +{ + char temp; + unsigned total = (unsigned) (nitems * size); + char *q = xmalloc(total); + char *p = q; + memcpy(p,pp,total); + /*tex + + Since `size' does not change, we can write a while loop for each case, + and avoid testing `size' for each time. + + */ + switch (size) { + case 16: + /*tex + + 16-byte items happen on the DEC Alpha machine when we are not doing + sharable memory dumps. + + */ + while (nitems--) { + SWAP(p[0], p[15]); + SWAP(p[1], p[14]); + SWAP(p[2], p[13]); + SWAP(p[3], p[12]); + SWAP(p[4], p[11]); + SWAP(p[5], p[10]); + SWAP(p[6], p[9]); + SWAP(p[7], p[8]); + p += size; + } + break; + + case 12: + while (nitems--) { + SWAP(p[0], p[11]); + SWAP(p[1], p[10]); + SWAP(p[2], p[9]); + SWAP(p[3], p[8]); + SWAP(p[4], p[7]); + SWAP(p[5], p[6]); + p += size; + } + break; + + case 8: + while (nitems--) { + SWAP(p[0], p[7]); + SWAP(p[1], p[6]); + SWAP(p[2], p[5]); + SWAP(p[3], p[4]); + p += size; + } + break; + + case 4: + while (nitems--) { + SWAP(p[0], p[3]); + SWAP(p[1], p[2]); + p += size; + } + break; + + case 2: + while (nitems--) { + SWAP(p[0], p[1]); + p += size; + } + break; + case 1: + /*tex Nothing to do. */ + break; + default: + FATAL1("Can't swap a %d-byte item for (un)dumping", size); + } + memcpy(pp,q,total); + xfree(q); +} +#endif + +/*tex + + That second swap is to make sure following calls don't get confused in the + case of |dump_things|. + +*/ + +void do_zdump(char *p, int item_size, int nitems, FILE * out_file) +{ + int err; + (void) out_file; + if (nitems == 0) + return; +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items(p, nitems, item_size); +#endif + if (gzwrite(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) != + item_size * nitems) { + fprintf(stderr, "! Could not write %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err)); + uexit(1); + } +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items(p, nitems, item_size); +#endif +} + +void do_zundump(char *p, int item_size, int nitems, FILE * in_file) +{ + int err; + (void) in_file; + if (nitems == 0) + return; + if (gzread(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) <= 0) { + fprintf(stderr, "Could not undump %d %d-byte item(s): %s.\n", nitems, item_size, gzerror(gz_fmtfile, &err)); + uexit(1); + } +#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) + swap_items(p, nitems, item_size); +#endif +} + +/*tex + + Tests has shown that a level 3 compression is the most optimal tradeoff + between file size and load time. + +*/ + +#define COMPRESSION "R3" + +boolean zopen_w_input(FILE ** f, const char *fname, int format, const_string fopen_mode) +{ + int callbackid; + int res; + char *fnam; + callbackid = callback_defined(find_format_file_callback); + if (callbackid > 0) { + res = run_callback(callbackid, "S->R", fname, &fnam); + if (res && fnam && strlen(fnam) > 0) { + *f = fopen(fnam, fopen_mode); + if (*f == NULL) { + return 0; + } + } else { + return 0; + } + } else { + res = luatex_open_input(f, fname, format, fopen_mode, true); + } + if (res) { + gz_fmtfile = gzdopen(fileno(*f), "rb" COMPRESSION); + } + return res; +} + +boolean zopen_w_output(FILE ** f, const char *s, const_string fopen_mode) +{ + int res = 1; + if (luainit) { + *f = fopen(s, fopen_mode); + if (*f == NULL) { + return 0; + } + } else { + res = luatex_open_output(f, s, fopen_mode); + } + if (res) { + gz_fmtfile = gzdopen(fileno(*f), "wb" COMPRESSION); + } + return res; +} + +void zwclose(FILE * f) +{ + (void) f; + gzclose(gz_fmtfile); +} + +/*tex Create the \DVI\ or \PDF\ file. */ + +int open_outfile(FILE ** f, const char *name, const char *mode) +{ + FILE *res; + res = fopen(name, mode); + if (res != NULL) { + *f = res; + return 1; + } + return 0; +} + +/*tex The caller should set |tfm_buffer=NULL| and |tfm_size=0|. */ + +int readbinfile(FILE * f, unsigned char **tfm_buffer, int *tfm_size) +{ + void *buf; + int size; + if (fseek(f, 0, SEEK_END) == 0) { + size = (int) ftell(f); + if (size > 0) { + buf = xmalloc((unsigned) size); + if (fseek(f, 0, SEEK_SET) == 0) { + if (fread((void *) buf, (size_t) size, 1, f) == 1) { + *tfm_buffer = (unsigned char *) buf; + *tfm_size = size; + return 1; + } + } + } else { + *tfm_buffer = NULL; + *tfm_size = 0; + return 1; + } + } + /*tex Either seek failed or we have a zero-sized file. */ + return 0; +} + +/*tex + + Like |os.execute()|, the |runpopen()| function is called only when + |shellenabledp == 1|. Unlike |os.execute()| we write errors to stderr, since + we have nowhere better to use; and of course we return a file handle (or + NULL) instead of a status indicator. + +*/ + +static FILE *runpopen(char *cmd, const char *mode) +{ + FILE *f = NULL; + char *safecmd = NULL; + char *cmdname = NULL; + int allow; +#ifdef WIN32 + char *pp; + + for (pp = cmd; *pp; pp++) { + if (*pp == '\'') *pp = '"'; + } +#endif + /*tex If |restrictedshell| is zero, any command is allowed. */ + if (restrictedshell == 0) { + allow = 1; + } else { + const char *thecmd = cmd; + allow = shell_cmd_is_allowed(thecmd, &safecmd, &cmdname); + } + if (allow == 1) + f = popen(cmd, mode); + else if (allow == 2) + f = popen(safecmd, mode); + else if (allow == -1) + fprintf(stderr, "\nrunpopen quotation error in command line: %s\n", cmd); + else + fprintf(stderr, "\nrunpopen command not allowed: %s\n", cmdname); + if (safecmd) + free(safecmd); + if (cmdname) + free(cmdname); + return f; +} + +/*tex + + The code that implements |popen()| needs an array for tracking possible pipe + file pointers, because these need to be closed using |pclose()|. + +*/ + +#define NUM_PIPES 16 +static FILE *pipes[NUM_PIPES]; + +#ifdef WIN32 +FILE *Poptr; +#endif + +boolean open_in_or_pipe(FILE ** f_ptr, char *fn, int filefmt, const_string fopen_mode, boolean must_exist) +{ + string fname = NULL; + int i; + /*tex + + Opening a read pipe is straightforward, only have to skip past the pipe + symbol in the file name. filename quoting is assumed to happen elsewhere + (it does :-)) + + */ + if (shellenabledp && *fn == '|') { + /*tex The user requested a pipe. */ + *f_ptr = NULL; + fname = (string) xmalloc((unsigned) (strlen(fn) + 1)); + strcpy(fname, fn); + if (fullnameoffile) + free(fullnameoffile); + fullnameoffile = xstrdup(fname); + recorder_record_input(fname + 1); + *f_ptr = runpopen(fname + 1, "r"); + free(fname); + for (i = 0; i < NUM_PIPES; i++) { + if (pipes[i] == NULL) { + pipes[i] = *f_ptr; + break; + } + } + if (*f_ptr) + setvbuf(*f_ptr, (char *) NULL, _IONBF, 0); +#ifdef WIN32 + Poptr = *f_ptr; +#endif + return *f_ptr != NULL; + } + return luatex_open_input(f_ptr, fn, filefmt, fopen_mode, must_exist); +} + + +boolean open_out_or_pipe(FILE ** f_ptr, char *fn, const_string fopen_mode) +{ + string fname; + int i; + /*tex + + Opening a write pipe takes a little bit more work, because TeX will + perhaps have appended ".tex". To avoid user confusion as much as + possible, this extension is stripped only when the command is a bare + word. Some small string trickery is needed to make sure the correct + number of bytes is free()-d afterwards. + */ + if (shellenabledp && *fn == '|') { + /*tex The user requested a pipe. */ + fname = (string) xmalloc((unsigned) (strlen(fn) + 1)); + strcpy(fname, fn); + if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) { + /*tex + + \METAPOST\ and \METAFIONT\ currently do not use this code, but it + is better to be prepared. Hm, what has this todo with \LUATEX ? + + */ + if (STREQ((fname + strlen(fname) - 3), "tex")) + *(fname + strlen(fname) - 4) = 0; + *f_ptr = runpopen(fname + 1, "w"); + *(fname + strlen(fname)) = '.'; + } else { + *f_ptr = runpopen(fname + 1, "w"); + } + recorder_record_output(fname + 1); + free(fname); + for (i = 0; i < NUM_PIPES; i++) { + if (pipes[i] == NULL) { + pipes[i] = *f_ptr; + break; + } + } + if (*f_ptr) + setvbuf(*f_ptr, (char *) NULL, _IONBF, 0); + return *f_ptr != NULL; + } + return luatex_open_output(f_ptr, fn, fopen_mode); +} + + +void close_file_or_pipe(FILE * f) +{ + int i; + if (shellenabledp) { + for (i = 0; i <= 15; i++) { + /*tex If this file was a pipe, |pclose()| it and return. */ + if (pipes[i] == f) { + if (f) { + pclose(f); +#ifdef WIN32 + Poptr = NULL; +#endif + } + pipes[i] = NULL; + return; + } + } + } + close_file(f); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texfileio.w b/Build/source/texk/web2c/luatexdir/tex/texfileio.w deleted file mode 100644 index a10e9ba63d0..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/texfileio.w +++ /dev/null @@ -1,1389 +0,0 @@ -% texfileio.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - - -#include "ptexlib.h" - -#include <string.h> -#include <kpathsea/absolute.h> - -@ The bane of portability is the fact that different operating systems treat -input and output quite differently, perhaps because computer scientists -have not given sufficient attention to this problem. People have felt somehow -that input and output are not part of ``real'' programming. Well, it is true -that some kinds of programming are more fun than others. With existing -input/output conventions being so diverse and so messy, the only sources of -joy in such parts of the code are the rare occasions when one can find a -way to make the program a little less bad than it might have been. We have -two choices, either to attack I/O now and get it over with, or to postpone -I/O until near the end. Neither prospect is very attractive, so let's -get it over with. - -The basic operations we need to do are (1)~inputting and outputting of -text, to or from a file or the user's terminal; (2)~inputting and -outputting of eight-bit bytes, to or from a file; (3)~instructing the -operating system to initiate (``open'') or to terminate (``close'') input or -output from a specified file; (4)~testing whether the end of an input -file has been reached. - -\TeX\ needs to deal with two kinds of files. -We shall use the term |alpha_file| for a file that contains textual data, -and the term |byte_file| for a file that contains eight-bit binary information. -These two types turn out to be the same on many computers, but -sometimes there is a significant distinction, so we shall be careful to -distinguish between them. Standard protocols for transferring -such files from computer to computer, via high-speed networks, are -now becoming available to more and more communities of users. - -The program actually makes use also of a third kind of file, called a -|word_file|, when dumping and reloading base information for its own -initialization. We shall define a word file later; but it will be possible -for us to specify simple operations on word files before they are defined. - -@ We finally did away with |nameoffile| and |namelength|, but the variables -have to be kept otherwise there will be link errors from |openclose.c| in -the web2c library - -@c -char *nameoffile; -int namelength; - - -@ When input files are opened via a callback, they will also be read using -callbacks. for that purpose, the |open_read_file_callback| returns an -integer to uniquely identify a callback table. This id replaces the file -point |f| in this case, because the input does not have to be a file -in the traditional sense. - -Signalling this fact is achieved by having two arrays of integers. - -@c -int *input_file_callback_id; -int read_file_callback_id[17]; - -@ Handle -output-directory. - -We assume that it is OK to look here first. Possibly it -would be better to replace lookups in "." with lookups in the -|output_directory| followed by "." but to do this requires much more -invasive surgery in libkpathsea. - -@c -static char *find_in_output_directory(const char *s) -{ - if (output_directory && !kpse_absolute_p(s, false)) { - FILE *f_ptr; - char *ftemp = concat3(output_directory, DIR_SEP_STRING, s); - f_ptr = fopen(ftemp, "rb"); /* this code is used for input files only */ - if (f_ptr) { - fclose(f_ptr); - return ftemp; - } else { - free(ftemp); - - } - } - return NULL; -} - -@ find an \.{\\input} or \.{\\read} file. |n| differentiates between those case. - -@c -int kpse_available(const char *m) { - if (!kpse_init) { - fprintf(stdout,"missing kpse replacement callback '%s', quitting\n",m); - exit(1); - } - return 1 ; -} - -char *luatex_find_read_file(const char *s, int n, int callback_index) -{ - char *ftemp = NULL; - int callback_id = callback_defined(callback_index); - if (callback_id > 0) { - (void) run_callback(callback_id, "dS->R", n, s, &ftemp); - } else if (kpse_available("find_read_file")) { - /* use kpathsea here */ - ftemp = find_in_output_directory(s); - if (!ftemp) - ftemp = kpse_find_file(s, kpse_tex_format, 1); - } - if (ftemp) { - if (fullnameoffile) - free(fullnameoffile); - fullnameoffile = xstrdup(ftemp); - } - return ftemp; -} - -@ find other files types -@c -char *luatex_find_file(const char *s, int callback_index) -{ - char *ftemp = NULL; - int callback_id = callback_defined(callback_index); - if (callback_id > 0) { - (void) run_callback(callback_id, "S->R", s, &ftemp); - - } else if (kpse_available("find_read_file")) { - /* use kpathsea here */ - switch (callback_index) { - case find_enc_file_callback: - ftemp = kpse_find_file(s, kpse_enc_format, 0); - break; - case find_map_file_callback: - ftemp = kpse_find_file(s, kpse_fontmap_format, 0); - break; - case find_type1_file_callback: - ftemp = kpse_find_file(s, kpse_type1_format, 0); - break; - case find_truetype_file_callback: - ftemp = kpse_find_file(s, kpse_truetype_format, 0); - break; - case find_opentype_file_callback: - ftemp = kpse_find_file(s, kpse_opentype_format, 0); - if (ftemp == NULL) - ftemp = kpse_find_file(s, kpse_truetype_format, 0); - break; - case find_data_file_callback: - ftemp = find_in_output_directory(s); - if (!ftemp) - ftemp = kpse_find_file(s, kpse_tex_format, 1); - break; - case find_font_file_callback: - ftemp = kpse_find_file(s, kpse_ofm_format, 1); - if (ftemp == NULL) - ftemp = kpse_find_file(s, kpse_tfm_format, 1); - break; - case find_vf_file_callback: - ftemp = kpse_find_file(s, kpse_ovf_format, 0); - if (ftemp == NULL) - ftemp = kpse_find_file(s, kpse_vf_format, 0); - break; - case find_cidmap_file_callback: - ftemp = kpse_find_file(s, kpse_cid_format, 0); - break; - default: - printf - ("luatex_find_file(): do not know how to handle file %s of type %d\n", - s, callback_index); - break; - } - } - return ftemp; -} - - -@ LuaTeX used to have private functions for these that did not use kpathsea, -but since the file paranoia tests have to come from kpathsea anyway, that is no -longer useful. The only downside to using luatex is that if one wants to disable -kpathsea via the Lua startup script, it is now an absolute requirement that all -file discovery callbacks are specified. Just using the find_read_file, but not -setting open_read_file, for example, does not work any more if kpathsea is not -to be used at all. - -@c -#define openoutnameok(A) kpse_out_name_ok (A) -#define openinnameok(A) kpse_in_name_ok (A) - -@ Open an input file F, using the kpathsea format FILEFMT and passing - |FOPEN_MODE| to fopen. The filename is in `fn'. We return whether or - not the open succeeded. - -@c -boolean -luatex_open_input(FILE ** f_ptr, const char *fn, int filefmt, - const_string fopen_mode, boolean must_exist) -{ - string fname = NULL; - /* We havent found anything yet. */ - *f_ptr = NULL; - if (fullnameoffile) - free(fullnameoffile); - fullnameoffile = NULL; - fname = kpse_find_file(fn, (kpse_file_format_type) filefmt, must_exist); - if (fname) { - fullnameoffile = xstrdup(fname); - /* If we found the file in the current directory, don't leave - the `./' at the beginning of `fn', since it looks - dumb when `tex foo' says `(./foo.tex ... )'. On the other - hand, if the user said `tex ./foo', and that's what we - opened, then keep it -- the user specified it, so we - shouldn't remove it. */ - if (fname[0] == '.' && IS_DIR_SEP(fname[1]) - && (fn[0] != '.' || !IS_DIR_SEP(fn[1]))) { - unsigned i = 0; - while (fname[i + 2] != 0) { - fname[i] = fname[i + 2]; - i++; - } - fname[i] = 0; - } - /* This fopen is not allowed to fail. */ - *f_ptr = xfopen(fname, fopen_mode); - } - if (*f_ptr) { - recorder_record_input(fname); - } - return *f_ptr != NULL; -} - -@ @c -boolean luatex_open_output(FILE ** f_ptr, const char *fn, - const_string fopen_mode) -{ - char *fname; - boolean absolute = kpse_absolute_p(fn, false); - - /* If we have an explicit output directory, use it. */ - if (output_directory && !absolute) { - fname = concat3(output_directory, DIR_SEP_STRING, fn); - } else { - fname = xstrdup(fn); - } - - /* Is the filename openable as given? */ - *f_ptr = fopen(fname, fopen_mode); - - if (!*f_ptr) { - /* Can't open as given. Try the envvar. */ - string texmfoutput = kpse_var_value("TEXMFOUTPUT"); - - if (texmfoutput && *texmfoutput && !absolute) { - fname = concat3(texmfoutput, DIR_SEP_STRING, fn); - *f_ptr = fopen(fname, fopen_mode); - } - } - if (*f_ptr) { - recorder_record_output(fname); - } - free(fname); - return *f_ptr != NULL; -} - - -@ @c -boolean lua_a_open_in(alpha_file * f, char *fn, int n) -{ - int k; - char *fnam; /* string returned by find callback */ - int callback_id; - boolean ret = true; /* return value */ - boolean file_ok = true; /* the status so far */ - if (n == 0) { - input_file_callback_id[iindex] = 0; - } else { - read_file_callback_id[n] = 0; - } - if (*fn == '|') - fnam = fn; - else - fnam = luatex_find_read_file(fn, n, find_read_file_callback); - if (!fnam) - return false; - callback_id = callback_defined(open_read_file_callback); - if (callback_id > 0) { - k = run_and_save_callback(callback_id, "S->", fnam); - if (k > 0) { - ret = true; - if (n == 0) - input_file_callback_id[iindex] = k; - else - read_file_callback_id[n] = k; - } else { - file_ok = false; /* read failed */ - } - } else { /* no read callback */ - if (openinnameok(fnam)) { - ret = - open_in_or_pipe(f, fnam, kpse_tex_format, FOPEN_RBIN_MODE, - (n == 0 ? true : false)); - } else { - file_ok = false; /* open failed */ - } - } - if (!file_ok) { - ret = false; - } - return ret; -} - - -@ @c -boolean lua_a_open_out(alpha_file * f, char *fn, int n) -{ - boolean test; - str_number fnam; - int callback_id; - boolean ret = false; - callback_id = callback_defined(find_write_file_callback); - if (callback_id > 0) { - fnam = 0; - test = run_callback(callback_id, "dS->s", n, fn, &fnam); - if ((test) && (fnam != 0) && (str_length(fnam) > 0)) { - /* There is no message here because if that is needed the macro package */ - /* should do that in the callback code. As elsewhere, messaging is left */ - /* to lua then. */ - ret = open_outfile(f, fn, FOPEN_W_MODE); - } - } else { - if (openoutnameok(fn)) { - if (n > 0 && selector != term_only) { - /* This message to the log is for downward compatibility with other tex's */ - /* as there are scripts out there that act on this message. An alternative */ - /* is to let a macro package write an explicit message. */ - fprintf(log_file,"\n\\openout%i = %s\n",n-1,fn); - } - ret = open_out_or_pipe(f, fn, FOPEN_W_MODE); - } - } - return ret; -} - - -@ @c -boolean lua_b_open_out(alpha_file * f, char *fn) -{ - boolean test; - str_number fnam; - int callback_id; - boolean ret = false; - callback_id = callback_defined(find_output_file_callback); - if (callback_id > 0) { - fnam = 0; - test = run_callback(callback_id, "S->s", fn, &fnam); - if ((test) && (fnam != 0) && (str_length(fnam) > 0)) { - ret = open_outfile(f, fn, FOPEN_WBIN_MODE); - } - } else { - if (openoutnameok(fn)) { - ret = luatex_open_output(f, fn, FOPEN_WBIN_MODE); - } - } - return ret; -} - -@ @c -void lua_a_close_in(alpha_file f, int n) -{ /* close a text file */ - int callback_id; - if (n == 0) - callback_id = input_file_callback_id[iindex]; - else - callback_id = read_file_callback_id[n]; - if (callback_id > 0) { - run_saved_callback(callback_id, "close", "->"); - destroy_saved_callback(callback_id); - if (n == 0) - input_file_callback_id[iindex] = 0; - else - read_file_callback_id[n] = 0; - } else { - close_file_or_pipe(f); - } -} - -@ @c -void lua_a_close_out(alpha_file f) -{ /* close a text file */ - close_file_or_pipe(f); -} - - -@ Binary input and output are done with C's ordinary -procedures, so we don't have to make any other special arrangements for -binary~I/O. Text output is also easy to do with standard routines. -The treatment of text input is more difficult, however, because -of the necessary translation to |ASCII_code| values. -\TeX's conventions should be efficient, and they should -blend nicely with the user's operating environment. - -Input from text files is read one line at a time, using a routine called -|lua_input_ln|. This function is defined in terms of global variables called -|buffer|, |first|, and |last| that will be described in detail later; for -now, it suffices for us to know that |buffer| is an array of |ASCII_code| -values, and that |first| and |last| are indices into this array -representing the beginning and ending of a line of text. - -@c -packed_ASCII_code *buffer; /* lines of characters being read */ -int first; /* the first unused position in |buffer| */ -int last; /* end of the line just input to |buffer| */ -int max_buf_stack; /* largest index used in |buffer| */ - - -@ The |lua_input_ln| function brings the next line of input from the specified -file into available positions of the buffer array and returns the value -|true|, unless the file has already been entirely read, in which case it -returns |false| and sets |last:=first|. In general, the |ASCII_code| -numbers that represent the next line of the file are input into -|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the -global variable |last| is set equal to |first| plus the length of the -line. Trailing blanks are removed from the line; thus, either |last=first| -(in which case the line was entirely blank) or |buffer[last-1]<>" "|. - -An overflow error is given, however, if the normal actions of |lua_input_ln| -would make |last>=buf_size|; this is done so that other parts of \TeX\ -can safely look at the contents of |buffer[last+1]| without overstepping -the bounds of the |buffer| array. Upon entry to |lua_input_ln|, the condition -|first<buf_size| will always hold, so that there is always room for an -``empty'' line. - -The variable |max_buf_stack|, which is used to keep track of how large -the |buf_size| parameter must be to accommodate the present job, is -also kept up to date by |lua_input_ln|. - -If the |bypass_eoln| parameter is |true|, |lua_input_ln| will do a |get| -before looking at the first character of the line; this skips over -an |eoln| that was in |f^|. The procedure does not do a |get| when it -reaches the end of the line; therefore it can be used to acquire input -from the user's terminal as well as from ordinary text files. - -Since the inner loop of |lua_input_ln| is part of \TeX's ``inner loop''---each -character of input comes in at this place---it is wise to reduce system -overhead by making use of special routines that read in an entire array -of characters at once, if such routines are available. -@^inner loop@> - -@c -boolean lua_input_ln(alpha_file f, int n, boolean bypass_eoln) -{ - boolean lua_result; - int last_ptr; - int callback_id; - (void) bypass_eoln; /* todo: variable can be removed */ - if (n == 0) - callback_id = input_file_callback_id[iindex]; - else - callback_id = read_file_callback_id[n]; - if (callback_id > 0) { - last = first; - last_ptr = first; - lua_result = - run_saved_callback(callback_id, "reader", "->l", &last_ptr); - if ((lua_result == true) && (last_ptr != 0)) { - last = last_ptr; - if (last > max_buf_stack) - max_buf_stack = last; - } else { - lua_result = false; - } - } else { - lua_result = input_ln(f, bypass_eoln); - } - if (lua_result == true) { - /* Fix up the input buffer using callbacks */ - if (last >= first) { - callback_id = callback_defined(process_input_buffer_callback); - if (callback_id > 0) { - last_ptr = first; - lua_result = - run_callback(callback_id, "l->l", (last - first), - &last_ptr); - if ((lua_result == true) && (last_ptr != 0)) { - last = last_ptr; - if (last > max_buf_stack) - max_buf_stack = last; - } - } - } - return true; - } - return false; -} - - -@ We need a special routine to read the first line of \TeX\ input from -the user's terminal. This line is different because it is read before we -have opened the transcript file; there is sort of a ``chicken and -egg'' problem here. If the user types `\.{\\input paper}' on the first -line, or if some macro invoked by that line does such an \.{\\input}, -the transcript file will be named `\.{paper.log}'; but if no \.{\\input} -commands are performed during the first line of terminal input, the transcript -file will acquire its default name `\.{texput.log}'. (The transcript file -will not contain error messages generated by the first line before the -first \.{\\input} command.) -@.texput@> - -The first line is special also because it may be read before \TeX\ has -input a format file. In such cases, normal error messages cannot yet -be given. The following code uses concepts that will be explained later. - -@ Different systems have different ways to get started. But regardless of -what conventions are adopted, the routine that initializes the terminal -should satisfy the following specifications: - -\yskip\textindent{1)}It should open file |term_in| for input from the - terminal. (The file |term_out| will already be open for output to the - terminal.) - -\textindent{2)}If the user has given a command line, this line should be - considered the first line of terminal input. Otherwise the - user should be prompted with `\.{**}', and the first line of input - should be whatever is typed in response. - -\textindent{3)}The first line of input, which might or might not be a - command line, should appear in locations |first| to |last-1| of the - |buffer| array. - -\textindent{4)}The global variable |loc| should be set so that the - character to be read next by \TeX\ is in |buffer[loc]|. This - character should not be blank, and we should have |loc<last|. - -\yskip\noindent(It may be necessary to prompt the user several times -before a non-blank line comes in. The prompt is `\.{**}' instead of the -later `\.*' because the meaning is slightly different: `\.{\\input}' need -not be typed immediately after~`\.{**}'.) - -The following program does the required initialization. -Iff anything has been specified on the command line, then |t_open_in| -will return with |last > first|. -@^system dependencies@> - -@c -boolean init_terminal(void) -{ /* gets the terminal input started */ - t_open_in(); - if (last > first) { - iloc = first; - while ((iloc < last) && (buffer[iloc] == ' ')) - incr(iloc); - if (iloc < last) { - return true; - } - } - while (1) { - wake_up_terminal(); - fputs("**", term_out); - update_terminal(); - if (!input_ln(term_in, true)) { - /* this shouldn't happen */ - fputs("\n! End of file on the terminal... why?\n", term_out); - return false; - } - iloc = first; - while ((iloc < last) && (buffer[iloc] == ' ')) - incr(iloc); - if (iloc < last) { - return true; /* return unless the line was all blank */ - } - fputs("Please type the name of your input file.\n", term_out); - } -} - - -@ Here is a procedure that asks the user to type a line of input, -assuming that the |selector| setting is either |term_only| or |term_and_log|. -The input is placed into locations |first| through |last-1| of the -|buffer| array, and echoed on the transcript file if appropriate. - -@c -void term_input(void) -{ /* gets a line from the terminal */ - int k; /* index into |buffer| */ - update_terminal(); /* now the user sees the prompt for sure */ - if (!input_ln(term_in, true)) - fatal_error("End of file on the terminal!"); - term_offset = 0; /* the user's line ended with \.{<return>} */ - decr(selector); /* prepare to echo the input */ - if (last != first) { - for (k = first; k <= last - 1; k++) - print_char(buffer[k]); - } - print_ln(); - incr(selector); /* restore previous status */ -} - - -@ It's time now to fret about file names. Besides the fact that different -operating systems treat files in different ways, we must cope with the -fact that completely different naming conventions are used by different -groups of people. The following programs show what is required for one -particular operating system; similar routines for other systems are not -difficult to devise. -@^fingers@> -@^system dependencies@> - -\TeX\ assumes that a file name has three parts: the name proper; its -``extension''; and a ``file area'' where it is found in an external file -system. The extension of an input file or a write file is assumed to be -`\.{.tex}' unless otherwise specified; it is `\.{.log}' on the -transcript file that records each run of \TeX; it is `\.{.tfm}' on the font -metric files that describe characters in the fonts \TeX\ uses; it is -`\.{.dvi}' on the output files that specify typesetting information; and it -is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX. -The file area can be arbitrary on input files, but files are usually -output to the user's current area. If an input file cannot be -found on the specified area, \TeX\ will look for it on a special system -area; this special area is intended for commonly used input files like -\.{webmac.tex}. - -Simple uses of \TeX\ refer only to file names that have no explicit -extension or area. For example, a person usually says `\.{\\input} \.{paper}' -or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input} -\.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file -names are best, because they make the \TeX\ source files portable; -whenever a file name consists entirely of letters and digits, it should be -treated in the same way by all implementations of \TeX. However, users -need the ability to refer to other files in their environment, especially -when responding to error messages concerning unopenable files; therefore -we want to let them use the syntax that appears in their favorite -operating system. - -The following procedures don't allow spaces to be part of -file names; but some users seem to like names that are spaced-out. -System-dependent changes to allow such things should probably -be made with reluctance, and only when an entire file name that -includes spaces is ``quoted'' somehow. - -Here are the global values that file names will be scanned into. - -@c -str_number cur_name; /* name of file just scanned */ -str_number cur_area; /* file area just scanned, or \.{""} */ -str_number cur_ext; /* file extension just scanned, or \.{""} */ - - -@ The file names we shall deal with have the -following structure: If the name contains `\./' or `\.:' -(for Amiga only), the file area -consists of all characters up to and including the final such character; -otherwise the file area is null. If the remaining file name contains -`\..', the file extension consists of all such characters from the last -`\..' to the end, otherwise the file extension is null. - -We can scan such file names easily by using two global variables that keep track -of the occurrences of area and extension delimiters: - -@c -pool_pointer area_delimiter; /* the most recent `\./', if any */ -pool_pointer ext_delimiter; /* the relevant `\..', if any */ - - -@ Input files that can't be found in the user's area may appear in a standard -system area called |TEX_area|. Font metric files whose areas are not given -explicitly are assumed to appear in a standard system area called -|TEX_font_area|. $\Omega$'s compiled translation process files whose areas -are not given explicitly are assumed to appear in a standard system area. -These system area names will, of course, vary from place to place. - -@c -#define append_to_fn(A) do { \ - c=(A); \ - if (c!='"') { \ - if (k<file_name_size) fn[k++]=(unsigned char)(c); \ - } \ - } while (0) - - -char *pack_file_name(str_number n, str_number a, str_number e) -{ - ASCII_code c; /* character being packed */ - unsigned char *j; /* index into |str_pool| */ - int k = 0; /* number of positions filled in |fn| */ - unsigned char *fn = xmallocarray(packed_ASCII_code, - str_length(a) + str_length(n) + - str_length(e) + 1); - for (j = str_string(a); j < str_string(a) + str_length(a); j++) - append_to_fn(*j); - for (j = str_string(n); j < str_string(n) + str_length(n); j++) - append_to_fn(*j); - for (j = str_string(e); j < str_string(e) + str_length(e); j++) - append_to_fn(*j); - fn[k] = 0; - return (char *) fn; -} - - - -@ A messier routine is also needed, since format file names must be scanned -before \TeX's string mechanism has been initialized. We shall use the -global variable |TEX_format_default| to supply the text for default system areas -and extensions related to format files. -@^system dependencies@> - -Under {\mc UNIX} we don't give the area part, instead depending -on the path searching that will happen during file opening. Also, the -length will be set in the main program. - -@c -char *TEX_format_default; - - -@ This part of the program becomes active when a ``virgin'' \TeX\ is trying to get going, -just after the preliminary initialization, or when the user is substituting another -format file by typing `\.\&' after the initial `\.{**}' prompt. The buffer -contains the first line of input in |buffer[loc..(last-1)]|, where -|loc<last| and |buffer[loc]<>" "|. - -@c -char *open_fmt_file(void) -{ - int j; /* the first space after the format file name */ - char *fmt = NULL; - int dist; - j = iloc; - if (buffer[iloc] == '&') { - incr(iloc); - j = iloc; - buffer[last] = ' '; - while (buffer[j] != ' ') - incr(j); - fmt = xmalloc((unsigned) (j - iloc + 1)); - strncpy(fmt, (char *) (buffer + iloc), (size_t) (j - iloc)); - fmt[j - iloc] = 0; - dist = (int) (strlen(fmt) - strlen(DUMP_EXT)); - if (!(strstr(fmt, DUMP_EXT) == fmt + dist)) - fmt = concat(fmt, DUMP_EXT); - if (zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) - goto FOUND; - wake_up_terminal(); - fprintf(stdout, "Sorry, I can't find the format `%s'; will try `%s'.\n", - fmt, TEX_format_default); - update_terminal(); - } - /* now pull out all the stops: try for the system \.{plain} file */ - fmt = TEX_format_default; - if (!zopen_w_input(&fmt_file, fmt, DUMP_FORMAT, FOPEN_RBIN_MODE)) { - wake_up_terminal(); - fprintf(stdout, "I can't find the format file `%s'!\n", - TEX_format_default); - return NULL; - } - FOUND: - iloc = j; - return fmt; -} - - -@ The global variable |name_in_progress| is used to prevent recursive -use of |scan_file_name|, since the |begin_name| and other procedures -communicate via global variables. Recursion would arise only by -devious tricks like `\.{\\input\\input f}'; such attempts at sabotage -must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input} -@^recursion@> -from being initiated when a font size specification is being scanned. - -Another global variable, |job_name|, contains the file name that was first -\.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}' -and `\.{.fmt}' in the names of \TeX's output files. - -@c -boolean name_in_progress; /* is a file name being scanned? */ -str_number job_name; /* principal file name */ -boolean log_opened_global; /* has the transcript file been opened? */ - - -@ Initially |job_name=0|; it becomes nonzero as soon as the true name is known. -We have |job_name=0| if and only if the `\.{log}' file has not been opened, -except of course for a short time just after |job_name| has become nonzero. - -@c -unsigned char *texmf_log_name; /* full name of the log file */ - -@ The |open_log_file| routine is used to open the transcript file and to help -it catch up to what has previously been printed on the terminal. - -@c -void open_log_file(void) -{ - int old_setting; /* previous |selector| setting */ - int k; /* index into |buffer| */ - int l; /* end of first input line */ - char *fn; - old_setting = selector; - if (job_name == 0) - job_name = getjobname(maketexstring("texput")); /* TODO */ - fn = pack_job_name(".fls"); - recorder_change_filename(fn); - fn = pack_job_name(".log"); - while (!lua_a_open_out(&log_file, fn, 0)) { - /* Try to get a different log file name */ - /* Sometimes |open_log_file| is called at awkward moments when \TeX\ is - unable to print error messages or even to |show_context|. - The |prompt_file_name| routine can result in a |fatal_error|, but the |error| - routine will not be invoked because |log_opened| will be false. - - The normal idea of |batch_mode| is that nothing at all should be written - on the terminal. However, in the unusual case that - no log file could be opened, we make an exception and allow - an explanatory message to be seen. - - Incidentally, the program always refers to the log file as a `\.{transcript - file}', because some systems cannot use the extension `\.{.log}' for - this file. - */ - selector = term_only; - fn = prompt_file_name("transcript file name", ".log"); - } - texmf_log_name = (unsigned char *) xstrdup(fn); - selector = log_only; - log_opened_global = true; - if (callback_defined(start_run_callback) == 0) { - /* Print the banner line, including current date and time */ - log_banner(luatex_version_string); - - input_stack[input_ptr] = cur_input; /* make sure bottom level is in memory */ - tprint_nl("**"); - l = input_stack[0].limit_field; /* last position of first line */ - if (buffer[l] == end_line_char_par) - decr(l); /* TODO: multichar endlinechar */ - for (k = 1; k <= l; k++) - print_char(buffer[k]); - print_ln(); /* now the transcript file contains the first line of input */ - } - flush_loggable_info(); /* should be done always */ - selector = old_setting + 2; /* |log_only| or |term_and_log| */ -} - -@ This function is needed by synctex to make its log appear in the right -spot when |output_directory| is set. - -@c -char *get_full_log_name (void) -{ - if (output_directory) { - char *ret = xmalloc(strlen((char *)texmf_log_name)+2+strlen(output_directory)); - ret = strcpy(ret, output_directory); - strcat(ret, "/"); - strcat(ret, (char *)texmf_log_name); - return ret; - } else { - return xstrdup((const char*)texmf_log_name); - } -} - -@ Synctex uses this to get the anchored path of an input file. - -@c -char *luatex_synctex_get_current_name (void) -{ - char *pwdbuf = NULL, *ret; - if (kpse_absolute_p(fullnameoffile, false)) { - return xstrdup(fullnameoffile); - } - pwdbuf = xgetcwd(); - ret = concat3(pwdbuf, DIR_SEP_STRING, fullnameoffile); - free(pwdbuf) ; - return ret; -} - - -@ Let's turn now to the procedure that is used to initiate file reading -when an `\.{\\input}' command is being processed. - -@c -void start_input(void) -{ /* \TeX\ will \.{\\input} something */ - str_number temp_str; - char *fn; - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - - back_input(); - if (cur_cmd != left_brace_cmd) { - scan_file_name(); /* set |cur_name| to desired file name */ - } else { - scan_file_name_toks(); - } - fn = pack_file_name(cur_name, cur_area, cur_ext); - while (1) { - begin_file_reading(); /* set up |cur_file| and new level of input */ - if (lua_a_open_in(&cur_file, fn, 0)) - break; - end_file_reading(); /* remove the level that didn't work */ - fn = prompt_file_name("input file name", ""); - } - iname = maketexstring(fullnameoffile); - /* Now that we have |fullnameoffile|, it is time to post-adjust - |cur_name| and |cur_ext| for trailing |.tex| */ - { - char *n, *p; - n = p = fullnameoffile + strlen(fullnameoffile); - while (p>fullnameoffile) { - p--; - if (IS_DIR_SEP(*p)) { - break; - } - } - if (IS_DIR_SEP(*p)) { - p++; - } - while (n>fullnameoffile) { - n--; - if (*n == '.') { - break; - } - } - if (n>p) { - int q = *n; - cur_ext = maketexstring(n); - *n = 0; - cur_name = maketexstring(p); - *n = q; - } - } - - - source_filename_stack[in_open] = iname; - full_source_filename_stack[in_open] = xstrdup(fullnameoffile); - /* we can try to conserve string pool space now */ - temp_str = search_string(iname); - if (temp_str > 0) { - flush_str(iname); - iname = temp_str; - } - if (job_name == 0) { - job_name = getjobname(cur_name); - open_log_file(); - } - /* |open_log_file| doesn't |show_context|, so |limit| - and |loc| needn't be set to meaningful values yet */ - report_start_file(filetype_tex,fullnameoffile); - incr(open_parens); - update_terminal(); - istate = new_line; - /* Prepare new file {\sl Sync\TeX} information */ - if (! synctex_get_no_files()) { - synctexstartinput(); /* Give control to the {\sl Sync\TeX} controller */ - } - /* Read the first line of the new file */ - /* Here we have to remember to tell the |lua_input_ln| routine not to - start with a |get|. If the file is empty, it is considered to - contain a single blank line. */ - line = 1; - if (lua_input_ln(cur_file, 0, false)) { - ; - } - firm_up_the_line(); - if (end_line_char_inactive) - decr(ilimit); - else - buffer[ilimit] = (packed_ASCII_code) end_line_char_par; - first = ilimit + 1; - iloc = istart; -} - -@ Read and write dump files through zlib - -@ Earlier versions recast |*f| from |FILE *| to |gzFile|, but there is -no guarantee that these have the same size, so a static variable -is needed. - -@c -static gzFile gz_fmtfile = NULL; - -@ As distributed, the dump files are -architecture dependent; specifically, BigEndian and LittleEndian -architectures produce different files. These routines always output -BigEndian files. This still does not guarantee them to be -architecture-independent, because it is possible to make a format -that dumps a glue ratio, i.e., a floating-point number. Fortunately, -none of the standard formats do that. - -@c -#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) - -/* This macro is always invoked as a statement. It assumes a variable - `temp'. */ -# define SWAP(x, y) do { temp = x; x = y; y = temp; } while (0) - -/* Make the NITEMS items pointed at by P, each of size SIZE, be the - opposite-endianness of whatever they are now. */ -static void -swap_items(char *pp, int nitems, int size) -{ - char temp; - unsigned total = (unsigned) (nitems * size); - char *q = xmalloc(total); - char *p = q; - memcpy(p,pp,total); - /* Since `size' does not change, we can write a while loop for each - case, and avoid testing `size' for each time. */ - switch (size) { - /* 16-byte items happen on the DEC Alpha machine when we are not - doing sharable memory dumps. */ - case 16: - while (nitems--) { - SWAP(p[0], p[15]); - SWAP(p[1], p[14]); - SWAP(p[2], p[13]); - SWAP(p[3], p[12]); - SWAP(p[4], p[11]); - SWAP(p[5], p[10]); - SWAP(p[6], p[9]); - SWAP(p[7], p[8]); - p += size; - } - break; - - case 12: - while (nitems--) { - SWAP(p[0], p[11]); - SWAP(p[1], p[10]); - SWAP(p[2], p[9]); - SWAP(p[3], p[8]); - SWAP(p[4], p[7]); - SWAP(p[5], p[6]); - p += size; - } - break; - - case 8: - while (nitems--) { - SWAP(p[0], p[7]); - SWAP(p[1], p[6]); - SWAP(p[2], p[5]); - SWAP(p[3], p[4]); - p += size; - } - break; - - case 4: - while (nitems--) { - SWAP(p[0], p[3]); - SWAP(p[1], p[2]); - p += size; - } - break; - - case 2: - while (nitems--) { - SWAP(p[0], p[1]); - p += size; - } - break; - - case 1: - /* Nothing to do. */ - break; - - default: - FATAL1("Can't swap a %d-byte item for (un)dumping", size); - } - memcpy(pp,q,total); - xfree(q); -} -#endif /* not WORDS_BIGENDIAN and not NO_DUMP_SHARE */ - -@ That second swap is to make sure following calls don't get -confused in the case of |dump_things|. - -@c -void do_zdump(char *p, int item_size, int nitems, FILE * out_file) -{ - int err; - (void) out_file; - if (nitems == 0) - return; -#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) - swap_items(p, nitems, item_size); -#endif - if (gzwrite(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) != - item_size * nitems) { - fprintf(stderr, "! Could not write %d %d-byte item(s): %s.\n", nitems, - item_size, gzerror(gz_fmtfile, &err)); - uexit(1); - } -#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) - swap_items(p, nitems, item_size); -#endif -} - -@ @c -void do_zundump(char *p, int item_size, int nitems, FILE * in_file) -{ - int err; - (void) in_file; - if (nitems == 0) - return; - if (gzread(gz_fmtfile, (void *) p, (unsigned) (item_size * nitems)) <= 0) { - fprintf(stderr, "Could not undump %d %d-byte item(s): %s.\n", - nitems, item_size, gzerror(gz_fmtfile, &err)); - uexit(1); - } -#if !defined (WORDS_BIGENDIAN) && !defined (NO_DUMP_SHARE) - swap_items(p, nitems, item_size); -#endif -} - -@ @c -#define COMPRESSION "R3" - -boolean zopen_w_input(FILE ** f, const char *fname, int format, - const_string fopen_mode) -{ - int callbackid; - int res; - char *fnam; - callbackid = callback_defined(find_format_file_callback); - if (callbackid > 0) { - res = run_callback(callbackid, "S->R", fname, &fnam); - if (res && fnam && strlen(fnam) > 0) { - *f = fopen(fnam, fopen_mode); - if (*f == NULL) { - return 0; - } - } else { - return 0; - } - } else { - res = luatex_open_input(f, fname, format, fopen_mode, true); - } - if (res) { - gz_fmtfile = gzdopen(fileno(*f), "rb" COMPRESSION); - } - return res; -} - -@ @c -boolean zopen_w_output(FILE ** f, const char *s, const_string fopen_mode) -{ - int res = 1; - if (luainit) { - *f = fopen(s, fopen_mode); - if (*f == NULL) { - return 0; - } - } else { - res = luatex_open_output(f, s, fopen_mode); - } - if (res) { - gz_fmtfile = gzdopen(fileno(*f), "wb" COMPRESSION); - } - return res; -} - -@ @c -void zwclose(FILE * f) -{ - (void) f; - gzclose(gz_fmtfile); -} - -@ create the dvi or pdf file -@c -int open_outfile(FILE ** f, const char *name, const char *mode) -{ - FILE *res; - res = fopen(name, mode); - if (res != NULL) { - *f = res; - return 1; - } - return 0; -} - - -@ the caller should set |tfm_buffer=NULL| and |tfm_size=0| -@c -int readbinfile(FILE * f, unsigned char **tfm_buffer, int *tfm_size) -{ - void *buf; - int size; - if (fseek(f, 0, SEEK_END) == 0) { - size = (int) ftell(f); - if (size > 0) { - buf = xmalloc((unsigned) size); - if (fseek(f, 0, SEEK_SET) == 0) { - if (fread((void *) buf, (size_t) size, 1, f) == 1) { - *tfm_buffer = (unsigned char *) buf; - *tfm_size = size; - return 1; - } - } - } else { - *tfm_buffer = NULL; - *tfm_size = 0; - return 1; - } - } /* seek failed, or zero-sized file */ - return 0; -} - -@ Like |os.execute()|, the |runpopen()| function is called only when -|shellenabledp == 1|. Unlike |os.execute()| we write errors to stderr, since we -have nowhere better to use; and of course we return a file handle (or NULL) -instead of a status indicator. - -@c -static FILE *runpopen(char *cmd, const char *mode) -{ - FILE *f = NULL; - char *safecmd = NULL; - char *cmdname = NULL; - int allow; - -#ifdef WIN32 - char *pp; - - for (pp = cmd; *pp; pp++) { - if (*pp == '\'') *pp = '"'; - } -#endif - - /* If restrictedshell == 0, any command is allowed. */ - if (restrictedshell == 0) { - allow = 1; - } else { - const char *thecmd = cmd; - allow = shell_cmd_is_allowed(thecmd, &safecmd, &cmdname); - } - if (allow == 1) - f = popen(cmd, mode); - else if (allow == 2) - f = popen(safecmd, mode); - else if (allow == -1) - fprintf(stderr, "\nrunpopen quotation error in command line: %s\n", - cmd); - else - fprintf(stderr, "\nrunpopen command not allowed: %s\n", cmdname); - - if (safecmd) - free(safecmd); - if (cmdname) - free(cmdname); - return f; -} - -@ piped I/O - - -@ The code that implements |popen()| needs an array for tracking - possible pipe file pointers, because these need to be - closed using |pclose()|. - -@c -#define NUM_PIPES 16 -static FILE *pipes[NUM_PIPES]; - -#ifdef WIN32 -FILE *Poptr; -#endif - -boolean open_in_or_pipe(FILE ** f_ptr, char *fn, int filefmt, - const_string fopen_mode, boolean must_exist) -{ - string fname = NULL; - int i; /* iterator */ - - /* opening a read pipe is straightforward, only have to - skip past the pipe symbol in the file name. filename - quoting is assumed to happen elsewhere (it does :-)) */ - - if (shellenabledp && *fn == '|') { - /* the user requested a pipe */ - *f_ptr = NULL; - fname = (string) xmalloc((unsigned) (strlen(fn) + 1)); - strcpy(fname, fn); - if (fullnameoffile) - free(fullnameoffile); - fullnameoffile = xstrdup(fname); - recorder_record_input(fname + 1); - *f_ptr = runpopen(fname + 1, "r"); - free(fname); - for (i = 0; i < NUM_PIPES; i++) { - if (pipes[i] == NULL) { - pipes[i] = *f_ptr; - break; - } - } - if (*f_ptr) - setvbuf(*f_ptr, (char *) NULL, _IONBF, 0); -#ifdef WIN32 - Poptr = *f_ptr; -#endif - - return *f_ptr != NULL; - } - - return luatex_open_input(f_ptr, fn, filefmt, fopen_mode, must_exist); -} - - -boolean open_out_or_pipe(FILE ** f_ptr, char *fn, const_string fopen_mode) -{ - string fname; - int i; /* iterator */ - - /* opening a write pipe takes a little bit more work, because TeX - will perhaps have appended ".tex". To avoid user confusion as - much as possible, this extension is stripped only when the command - is a bare word. Some small string trickery is needed to make - sure the correct number of bytes is free()-d afterwards */ - - if (shellenabledp && *fn == '|') { - /* the user requested a pipe */ - fname = (string) xmalloc((unsigned) (strlen(fn) + 1)); - strcpy(fname, fn); - if (strchr(fname, ' ') == NULL && strchr(fname, '>') == NULL) { - /* mp and mf currently do not use this code, but it - is better to be prepared */ - if (STREQ((fname + strlen(fname) - 3), "tex")) - *(fname + strlen(fname) - 4) = 0; - *f_ptr = runpopen(fname + 1, "w"); - *(fname + strlen(fname)) = '.'; - } else { - *f_ptr = runpopen(fname + 1, "w"); - } - recorder_record_output(fname + 1); - free(fname); - - for (i = 0; i < NUM_PIPES; i++) { - if (pipes[i] == NULL) { - pipes[i] = *f_ptr; - break; - } - } - - if (*f_ptr) - setvbuf(*f_ptr, (char *) NULL, _IONBF, 0); - - return *f_ptr != NULL; - } - - return luatex_open_output(f_ptr, fn, fopen_mode); -} - - -void close_file_or_pipe(FILE * f) -{ - int i; /* iterator */ - - if (shellenabledp) { - for (i = 0; i <= 15; i++) { - /* if this file was a pipe, |pclose()| it and return */ - if (pipes[i] == f) { - if (f) { - pclose(f); -#ifdef WIN32 - Poptr = NULL; -#endif - } - pipes[i] = NULL; - return; - } - } - } - close_file(f); -} diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.w b/Build/source/texk/web2c/luatexdir/tex/texmath.c index 6d4b87a3864..69e58b0ea77 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texmath.w +++ b/Build/source/texk/web2c/luatexdir/tex/texmath.c @@ -1,46 +1,43 @@ -% texmath.w -% -% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +Copyright 2008-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + #include "ptexlib.h" -@ @c #define mode mode_par #define tail tail_par #define head head_par #define dir_save dirs_par -/* - - \mathdisplayskipmode +/*tex - tex normally always inserts before and only after when larger than zero + Concerning display skips, \TEX\ normally always inserts before and only after + when larger than zero. THis can ow be controlled with |\mathdisplayskipmode|: - 0 = normal tex - 1 = always - 2 = non-zero - 3 = ignore + \starttabulate + \NC 0 \NC normal \TEX \NC \NR + \NC 1 \NC always \NC \NR + \NC 2 \NC non-zero \NC \NR + \NC 3 \NC ignore \NC \NR + \stoptabulate */ -@ TODO: not sure if this is the right order -@c #define back_error(A,B) do { \ OK_to_interrupt=false; \ back_input(); \ @@ -48,90 +45,107 @@ tex_error(A,B); \ } while (0) -@ @c int scan_math(pointer, int); int scan_math_style(pointer, int); pointer fin_mlist(pointer); -@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an -{\sl mlist}, which is essentially a tree structure representing that -formula. An mlist is a linear sequence of items, but we can regard it as -a tree structure because mlists can appear within mlists. For example, many -of the entries can be subscripted or superscripted, and such ``scripts'' -are mlists in their own right. - -An entire formula is parsed into such a tree before any of the actual -typesetting is done, because the current style of type is usually not -known until the formula has been fully scanned. For example, when the -formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell -that `\.{a+b}' will be in script size until `\.{\\over}' has appeared. - -During the scanning process, each element of the mlist being built is -classified as a relation, a binary operator, an open parenthesis, etc., -or as a construct like `\.{\\sqrt}' that must be built up. This classification -appears in the mlist data structure. - -After a formula has been fully scanned, the mlist is converted to an hlist -so that it can be incorporated into the surrounding text. This conversion is -controlled by a recursive procedure that decides all of the appropriate -styles by a ``top-down'' process starting at the outermost level and working -in towards the subformulas. The formula is ultimately pasted together using -combinations of horizontal and vertical boxes, with glue and penalty nodes -inserted as necessary. - -An mlist is represented internally as a linked list consisting chiefly -of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat -similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are -allowed to appear in mlists together with the noads; \TeX\ tells the difference -by means of the |type| field, since a noad's |type| is always greater than -that of a node. An mlist does not contain character nodes, hlist nodes, vlist -nodes, math nodes or unset nodes; in particular, each mlist item appears in the -variable-size part of |mem|, so the |type| field is always present. - -Each noad is five or more words long. The first word contains the -|type| and |subtype| and |link| fields that are already so familiar to -us; the second contains the attribute list pointer, and the third, -fourth an fifth words are called the noad's |nucleus|, |subscr|, and -|supscr| fields. (This use of a combined attribute list is temporary. -Eventually, each of fields need their own list) - -Consider, for example, the simple formula `\.{\$x\^2\$}', which would be -parsed into an mlist containing a single element called an |ord_noad|. -The |nucleus| of this noad is a representation of `\.x', the |subscr| is -empty, and the |supscr| is a representation of `\.2'. - -The |nucleus|, |subscr|, and |supscr| fields are further broken into -subfields. If |p| points to a noad, and if |q| is one of its principal -fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the -corresponding attribute of noad |p| is not present). Otherwise, there are -several possibilities for the subfields, depending on the |type| of |q|. - -\yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of -the sixteen font families, and |character(q)| is the number of a character -within a font of that family, as in a character node. - -\yskip\hang|type(q)=math_text_char_node| is similar, but the character is -unsubscripted and unsuperscripted and it is followed immediately by another -character from the same font. (This |type| setting appears only -briefly during the processing; it is used to suppress unwanted italic -corrections.) - -\yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box -node (either an |hlist_node| or a |vlist_node|) that should be used as the -value of the field. The |shift_amount| in the subsidiary box node is the -amount by which that box will be shifted downward. - -\yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to -an mlist; the mlist must be converted to an hlist in order to obtain -the value of this field. - -\yskip\noindent In the latter case, we might have |math_list(q)=null|. This -is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}' -and `\.{\$P\$}' produce different results (the former will not have the -``italic correction'' added to the width of |P|, but the ``script skip'' -will be added). - -@c +/*tex + + When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an + {\sl mlist}, which is essentially a tree structure representing that formula. + An mlist is a linear sequence of items, but we can regard it as a tree + structure because mlists can appear within mlists. For example, many of the + entries can be subscripted or superscripted, and such ``scripts'' are mlists + in their own right. + + An entire formula is parsed into such a tree before any of the actual + typesetting is done, because the current style of type is usually not known + until the formula has been fully scanned. For example, when the formula + `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell that + `\.{a+b}' will be in script size until `\.{\\over}' has appeared. + + During the scanning process, each element of the mlist being built is + classified as a relation, a binary operator, an open parenthesis, etc., or as + a construct like `\.{\\sqrt}' that must be built up. This classification + appears in the mlist data structure. + + After a formula has been fully scanned, the mlist is converted to an hlist so + that it can be incorporated into the surrounding text. This conversion is + controlled by a recursive procedure that decides all of the appropriate + styles by a ``top-down'' process starting at the outermost level and working + in towards the subformulas. The formula is ultimately pasted together using + combinations of horizontal and vertical boxes, with glue and penalty nodes + inserted as necessary. + + An mlist is represented internally as a linked list consisting chiefly of + ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat + similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are + allowed to appear in mlists together with the noads; \TeX\ tells the + difference by means of the |type| field, since a noad's |type| is always + greater than that of a node. An mlist does not contain character nodes, hlist + nodes, vlist nodes, math nodes or unset nodes; in particular, each mlist item + appears in the variable-size part of |mem|, so the |type| field is always + present. + + Each noad is five or more words long. The first word contains the |type| and + |subtype| and |link| fields that are already so familiar to us; the second + contains the attribute list pointer, and the third, fourth an fifth words are + called the noad's |nucleus|, |subscr|, and |supscr| fields. (This use of a + combined attribute list is temporary. Eventually, each of fields need their + own list) + + Consider, for example, the simple formula `\.{\$x\^2\$}', which would be + parsed into an mlist containing a single element called an |ord_noad|. The + |nucleus| of this noad is a representation of `\.x', the |subscr| is empty, + and the |supscr| is a representation of `\.2'. + + The |nucleus|, |subscr|, and |supscr| fields are further broken into + subfields. If |p| points to a noad, and if |q| is one of its principal fields + (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the + corresponding attribute of noad |p| is not present). Otherwise, there are + several possibilities for the subfields, depending on the |type| of |q|. + + \startitemize + + \startitem + |type(q)=math_char_node| means that |math_fam(q)| refers to one of + the sixteen font families, and |character(q)| is the number of a + character within a font of that family, as in a character node. + \stopitem + + \startitem + |type(q)=math_text_char_node| is similar, but the character is + unsubscripted and unsuperscripted and it is followed immediately by + another character from the same font. (This |type| setting appears + only briefly during the processing; it is used to suppress unwanted + italic corrections.) + \stopitem + + \startitem + |type(q)=sub_box_node| means that |math_list(q)| points to a box node + (either an |hlist_node| or a |vlist_node|) that should be used as the + value of the field. The |shift_amount| in the subsidiary box node is + the amount by which that box will be shifted downward. + \stopitem + + \startitem + |type(q)=sub_mlist_node| means that |math_list(q)| points to an + mlist; the mlist must be converted to an hlist in order to obtain the + value of this field. + \stopitem + + \startitem + In the latter case, we might have |math_list(q)=null|. This is not + the same as |q=null|; for example, `\.{\$P\_\{\}\$}' and `\.{\$P\$}' + produce different results (the former will not have the ``italic + correction'' added to the width of |P|, but the ``script skip'' will + be added). + \stopitem + + \startitemize + +*/ + static void unsave_math(void) { unsave(); @@ -141,10 +155,13 @@ static void unsave_math(void) text_dir_ptr = saved_value(0); } -@ Sometimes it is necessary to destroy an mlist. The following -subroutine empties the current list, assuming that |abs(mode)=mmode|. +/*tex + + Sometimes it is necessary to destroy an mlist. The following subroutine + empties the current list, assuming that |abs(mode)=mmode|. + +*/ -@c void flush_math(void) { flush_node_list(vlink(head)); @@ -154,15 +171,13 @@ void flush_math(void) incompleat_noad_par = null; } -@ Before we can do anything in math mode, we need fonts. +/*tex Before we can do anything in math mode, we need fonts. */ -@c #define MATHFONTSTACK 8 -#define MATHFONTDEFAULT 0 /* == nullfont */ +#define MATHFONTDEFAULT 0 static sa_tree math_fam_head = NULL; -@ @c int fam_fnt(int fam_id, int size_id) { int n = fam_id + (256 * size_id); @@ -189,7 +204,6 @@ void def_fam_fnt(int fam_id, int size_id, int f, int lvl) } } -@ @c static void unsave_math_fam_data(int gl) { sa_stack_item st; @@ -201,7 +215,7 @@ static void unsave_math_fam_data(int gl) st = math_fam_head->stack[math_fam_head->stack_ptr]; if (st.level > 0) { rawset_sa_item(math_fam_head, st.code, st.value); - /* now do a trace message, if requested */ + /*tex Now do a trace message, if requested. */ if (tracing_restores_par > 1) { int size_id = st.code / 256; int fam_id = st.code % 256; @@ -220,15 +234,13 @@ static void unsave_math_fam_data(int gl) } } -@ and parameters +/*tex Parameters */ -@c #define MATHPARAMSTACK 8 #define MATHPARAMDEFAULT undefined_math_parameter static sa_tree math_param_head = NULL; -@ @c void def_math_param(int param_id, int style_id, scaled value, int lvl) { int n = param_id + (256 * style_id); @@ -254,7 +266,6 @@ scaled get_math_param(int param_id, int style_id) return (scaled) get_sa_item(math_param_head, n).int_value; } -@ @c static void unsave_math_param_data(int gl) { sa_stack_item st; @@ -266,7 +277,7 @@ static void unsave_math_param_data(int gl) st = math_param_head->stack[math_param_head->stack_ptr]; if (st.level > 0) { rawset_sa_item(math_param_head, st.code, st.value); - /* now do a trace message, if requested */ + /*tex Do a trace message, if requested. */ if (tracing_restores_par > 1) { int param_id = st.code % 256; int style_id = st.code / 256; @@ -285,17 +296,16 @@ static void unsave_math_param_data(int gl) } } -@ saving and unsaving of both +/*tex Saving and unsaving of both: */ -@c void unsave_math_data(int gl) { unsave_math_fam_data(gl); unsave_math_param_data(gl); } -@ Dumping and undumping -@c +/*tex Dumping and undumping: */ + void dump_math_data(void) { sa_tree_item sa_value = { 0 }; @@ -317,7 +327,6 @@ void undump_math_data(void) math_param_head = undump_sa_tree("mathparameters"); } -@ @c void initialize_math(void) { sa_tree_item sa_value = { 0 }; @@ -333,62 +342,63 @@ void initialize_math(void) return; } -@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, -Clo, Pun, or Inn, for purposes of spacing and line breaking. An -|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, -|punct_noad|, or |inner_noad| is used to represent portions of the various -types. For example, an `\.=' sign in a formula leads to the creation of a -|rel_noad| whose |nucleus| field is a representation of an equals sign -(usually |fam=0|, |character=075|). A formula preceded by \.{\\mathrel} -also results in a |rel_noad|. When a |rel_noad| is followed by an -|op_noad|, say, and possibly separated by one or more ordinary nodes (not -noads), \TeX\ will insert a penalty node (with the current |rel_penalty|) -just after the formula that corresponds to the |rel_noad|, unless there -already was a penalty immediately following; and a ``thick space'' will be -inserted just before the formula that corresponds to the |op_noad|. - -A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually -has a |subtype=normal|. The only exception is that an |op_noad| might -have |subtype=limits| or |no_limits|, if the normal positioning of -limits has been overridden for this operator. - -A |radical_noad| also has a |left_delimiter| field, which usually -represents a square root sign. - -A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|. - -Delimiter fields have four subfields -called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields -represent variable-size delimiters by giving the ``small'' and ``large'' -starting characters, as explained in Chapter~17 of {\sl The \TeX book}. -@:TeXbook}{\sl The \TeX book@> - -A |fraction_noad| is actually quite different from all other noads. -It has |thickness|, |denominator|, and |numerator| fields instead of -|nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value -that tells how thick to make a fraction rule; however, the special -value |default_code| is used to stand for the -|default_rule_thickness| of the current size. The |numerator| and -|denominator| point to mlists that define a fraction; we always have -$$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The -|left_delimiter| and |right_delimiter| fields specify delimiters that will -be placed at the left and right of the fraction. In this way, a -|fraction_noad| is able to represent all of \TeX's operators \.{\\over}, -\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and - \.{\\abovewithdelims}. - -@ The |new_noad| function creates an |ord_noad| that is completely null - -@c +/*tex + + Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, Clo, Pun, + or Inn, for purposes of spacing and line breaking. An |ord_noad|, |op_noad|, + |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, |punct_noad|, or + |inner_noad| is used to represent portions of the various types. For example, + an `\.=' sign in a formula leads to the creation of a |rel_noad| whose + |nucleus| field is a representation of an equals sign (usually |fam=0|, + |character=075|). A formula preceded by \.{\\mathrel} also results in a + |rel_noad|. When a |rel_noad| is followed by an |op_noad|, say, and possibly + separated by one or more ordinary nodes (not noads), \TeX\ will insert a + penalty node (with the current |rel_penalty|) just after the formula that + corresponds to the |rel_noad|, unless there already was a penalty immediately + following; and a ``thick space'' will be inserted just before the formula + that corresponds to the |op_noad|. + + A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually has a + |subtype=normal|. The only exception is that an |op_noad| might have + |subtype=limits| or |no_limits|, if the normal positioning of limits has been + overridden for this operator. + + A |radical_noad| also has a |left_delimiter| field, which usually represents + a square root sign. + + A |fraction_noad| has a |right_delimiter| field as well as a + |left_delimiter|. + + Delimiter fields have four subfields called |small_fam|, |small_char|, + |large_fam|, |large_char|. These subfields represent variable-size delimiters + by giving the ``small'' and ``large'' starting characters, as explained in + Chapter~17 of {\sl The \TeX book}. + + A |fraction_noad| is actually quite different from all other noads. It has + |thickness|, |denominator|, and |numerator| fields instead of |nucleus|, + |subscr|, and |supscr|. The |thickness| is a scaled value that tells how + thick to make a fraction rule; however, the special value |default_code| is + used to stand for the |default_rule_thickness| of the current size. The + |numerator| and |denominator| point to mlists that define a fraction; we + always have $$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The + |left_delimiter| and |right_delimiter| fields specify delimiters that will be + placed at the left and right of the fraction. In this way, a |fraction_noad| + is able to represent all of \TeX's operators \.{\\over}, \.{\\atop}, + \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and + \.{\\abovewithdelims}. + + The |new_noad| function creates an |ord_noad| that is completely null + +*/ + pointer new_noad(void) { pointer p; p = new_node(simple_noad, ord_noad_type); - /* all noad fields are zero after this */ + /*tex All noad fields are zero after this. */ return p; } -@ @c pointer new_sub_box(pointer curbox) { pointer p, q; @@ -399,40 +409,41 @@ pointer new_sub_box(pointer curbox) return p; } -@ A few more kinds of noads will complete the set: An |under_noad| has its -nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places -an accent over its nucleus; the accent character appears as -|math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad| -centers its nucleus vertically with respect to the axis of the formula; -in such noads we always have |type(nucleus(p))=sub_box|. - -And finally, we have the |fence_noad| type, to implement -\TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}. -The |nucleus| of such noads is -replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces -a |fence_noad| such that |delimiter(p)| holds the family and character -codes for all left parentheses. A |fence_noad| of subtype |left_noad_side| -never appears in an mlist except as the first element, and a |fence_noad| -with subtype |right_noad_side| never appears in an mlist -except as the last element; furthermore, we either have both a |left_noad_side| -and a |right_noad_side|, or neither one is present. - -@ Math formulas can also contain instructions like \.{\\textstyle} that -override \TeX's normal style rules. A |style_node| is inserted into the -data structure to record such instructions; it is three words long, so it -is considered a node instead of a noad. The |subtype| is either |display_style| -or |text_style| or |script_style| or |script_script_style|. The -second and third words of a |style_node| are not used, but they are -present because a |choice_node| is converted to a |style_node|. - -\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles -|display_style|, \dots, |script_script_style|, and adds~1 to get the -``cramped'' versions of these styles. This gives a numerical order that -is backwards from the convention of Appendix~G in {\sl The \TeX book\/}; -i.e., a smaller style has a larger numerical value. -@:TeXbook}{\sl The \TeX book@> - -@c +/*tex + + A few more kinds of noads will complete the set: An |under_noad| has its + nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places + an accent over its nucleus; the accent character appears as + |math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A + |vcenter_noad| centers its nucleus vertically with respect to the axis of the + formula; in such noads we always have |type(nucleus(p))=sub_box|. + + And finally, we have the |fence_noad| type, to implement \TeX's \.{\\left} + and \.{\\right} as well as eTeX's \.{\\middle}. The |nucleus| of such noads + is replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces + a |fence_noad| such that |delimiter(p)| holds the family and character codes + for all left parentheses. A |fence_noad| of subtype |left_noad_side| never + appears in an mlist except as the first element, and a |fence_noad| with + subtype |right_noad_side| never appears in an mlist except as the last + element; furthermore, we either have both a |left_noad_side| and a + |right_noad_side|, or neither one is present. + + Math formulas can also contain instructions like \.{\\textstyle} that + override \TeX's normal style rules. A |style_node| is inserted into the data + structure to record such instructions; it is three words long, so it is + considered a node instead of a noad. The |subtype| is either |display_style| + or |text_style| or |script_style| or |script_script_style|. The second and + third words of a |style_node| are not used, but they are present because a + |choice_node| is converted to a |style_node|. + + \TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles + |display_style|, \dots, |script_script_style|, and adds~1 to get the + ``cramped'' versions of these styles. This gives a numerical order that is + backwards from the convention of Appendix~G in {\sl The \TeX book\/}; i.e., a + smaller style has a larger numerical value. + +*/ + const char *math_style_names[] = { "display", "crampeddisplay", "text", "crampedtext", @@ -482,93 +493,96 @@ const char *math_param_names[] = { NULL }; -@ @c pointer new_style(small_number s) -{ /* create a style node */ +{ m_style = s; return new_node(style_node, s); } -@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which -has special subfields |display_mlist|, |text_mlist|, |script_mlist|, -and |script_script_mlist| pointing to the mlists for each style. +/*tex + + Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which has + special subfields |display_mlist|, |text_mlist|, |script_mlist|, and + |script_script_mlist| pointing to the mlists for each style. + +*/ -@c static pointer new_choice(void) -{ /* create a choice node */ - return new_node(choice_node, 0); /* the |subtype| is not used */ -} - -@ Let's consider now the previously unwritten part of |show_node_list| -that displays the things that can only be present in mlists; this -program illustrates how to access the data structures just defined. - -In the context of the following program, |p| points to a node or noad that -should be displayed, and the current string contains the ``recursion history'' -that leads to this point. The recursion history consists of a dot for each -outer level in which |p| is subsidiary to some node, or in which |p| is -subsidiary to the |nucleus| field of some noad; the dot is replaced by -`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr| -or |supscr| or |denominator| or |numerator| fields of noads. For example, -the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for -|x| in the (ridiculous) formula -`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'. - -@c -void display_normal_noad(pointer p); /* forward */ -void display_fence_noad(pointer p); /* forward */ -void display_fraction_noad(pointer p); /* forward */ +{ + return new_node(choice_node, 0); +} + +/*tex + + Let's consider now the previously unwritten part of |show_node_list| that + displays the things that can only be present in mlists; this program + illustrates how to access the data structures just defined. + + In the context of the following program, |p| points to a node or noad that + should be displayed, and the current string contains the ``recursion + history'' that leads to this point. The recursion history consists of a dot + for each outer level in which |p| is subsidiary to some node, or in which |p| + is subsidiary to the |nucleus| field of some noad; the dot is replaced by + `\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr| or + |supscr| or |denominator| or |numerator| fields of noads. For example, the + current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for |x| + in the (ridiculous) formula `\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over + x+y\}\}\}\}\$}'. + +*/ + +void display_normal_noad(pointer p); +void display_fence_noad(pointer p); +void display_fraction_noad(pointer p); void show_math_node(pointer p) { switch (type(p)) { - case style_node: - print_cmd_chr(math_style_cmd, subtype(p)); - break; - case choice_node: - tprint_esc("mathchoice"); - append_char('D'); - show_node_list(display_mlist(p)); - flush_char(); - append_char('T'); - show_node_list(text_mlist(p)); - flush_char(); - append_char('S'); - show_node_list(script_mlist(p)); - flush_char(); - append_char('s'); - show_node_list(script_script_mlist(p)); - flush_char(); - break; - case simple_noad: - case radical_noad: - case accent_noad: - display_normal_noad(p); - break; - case fence_noad: - display_fence_noad(p); - break; - case fraction_noad: - display_fraction_noad(p); - break; - default: - tprint("Unknown node type!"); - break; + case style_node: + print_cmd_chr(math_style_cmd, subtype(p)); + break; + case choice_node: + tprint_esc("mathchoice"); + append_char('D'); + show_node_list(display_mlist(p)); + flush_char(); + append_char('T'); + show_node_list(text_mlist(p)); + flush_char(); + append_char('S'); + show_node_list(script_mlist(p)); + flush_char(); + append_char('s'); + show_node_list(script_script_mlist(p)); + flush_char(); + break; + case simple_noad: + case radical_noad: + case accent_noad: + display_normal_noad(p); + break; + case fence_noad: + display_fence_noad(p); + break; + case fraction_noad: + display_fraction_noad(p); + break; + default: + tprint("Unknown node type!"); + break; } } -@ Here are some simple routines used in the display of noads. +/*tex Here are some simple routines used in the display of noads. */ -@c static void print_fam_and_char(pointer p) -{ /* prints family and character */ +{ tprint_esc("fam"); print_int(math_fam(p)); print_char(' '); print(math_character(p)); } -@ @c static void print_delimiter(pointer p) { int a; @@ -598,36 +612,38 @@ static void print_delimiter(pointer p) tprint(" "); } if (small_fam(p) < 0) { - print_int(-1); /* this should never happen */ - } else if (small_fam(p) < 16 && large_fam(p) < 16 && - small_char(p) < 256 && large_char(p) < 256) { - /* traditional tex style */ + /*tex This should never happen. */ + print_int(-1); + } else if (small_fam(p) < 16 && large_fam(p) < 16 && small_char(p) < 256 && large_char(p) < 256) { + /*tex Traditional tex style. */ a = small_fam(p) * 256 + small_char(p); a = a * 0x1000 + large_fam(p) * 256 + large_char(p); - print_hex(a); - } else if ((large_fam(p) == 0 && large_char(p) == 0) || - small_char(p) > 65535 || large_char(p) > 65535) { - /* modern xetex/luatex style */ - print_hex(small_fam(p)); - print_hex(small_char(p)); + print_qhex(a); + } else if ((large_fam(p) == 0 && large_char(p) == 0) || small_char(p) > 65535 || large_char(p) > 65535) { + /*tex \LUATEX\ style. */ + print_qhex(small_fam(p)); + print_qhex(small_char(p)); } } -@ The next subroutine will descend to another level of recursion when a -subsidiary mlist needs to be displayed. The parameter |c| indicates what -character is to become part of the recursion history. An empty mlist is -distinguished from a missing field, because these are not equivalent -(as explained above). -@^recursion@> +/*tex + + The next subroutine will descend to another level of recursion when a + subsidiary mlist needs to be displayed. The parameter |c| indicates what + character is to become part of the recursion history. An empty mlist is + distinguished from a missing field, because these are not equivalent (as + explained above). + +*/ -@c static void print_subsidiary_data(pointer p, ASCII_code c) -{ /* display a noad field */ +{ if ((int) cur_length >= depth_threshold) { if (p != null) tprint(" []"); } else { - append_char(c); /* include |c| in the recursion history */ + /*tex Include |c| in the recursion history. */ + append_char(c); if (p != null) { switch (type(p)) { case math_char_node: @@ -649,58 +665,58 @@ static void print_subsidiary_data(pointer p, ASCII_code c) break; } } - flush_char(); /* remove |c| from the recursion history */ + /*tex Remove |c| from the recursion history. */ + flush_char(); } } -@ @c void display_normal_noad(pointer p) { switch (type(p)) { case simple_noad: switch (subtype(p)) { - case ord_noad_type: - tprint_esc("mathord"); - break; - case op_noad_type_normal: - case op_noad_type_limits: - case op_noad_type_no_limits: - tprint_esc("mathop"); - if (subtype(p) == op_noad_type_limits) - tprint_esc("limits"); - else if (subtype(p) == op_noad_type_no_limits) - tprint_esc("nolimits"); - break; - case bin_noad_type: - tprint_esc("mathbin"); - break; - case rel_noad_type: - tprint_esc("mathrel"); - break; - case open_noad_type: - tprint_esc("mathopen"); - break; - case close_noad_type: - tprint_esc("mathclose"); - break; - case punct_noad_type: - tprint_esc("mathpunct"); - break; - case inner_noad_type: - tprint_esc("mathinner"); - break; - case over_noad_type: - tprint_esc("overline"); - break; - case under_noad_type: - tprint_esc("underline"); - break; - case vcenter_noad_type: - tprint_esc("vcenter"); - break; - default: - tprint("<unknown noad type!>"); - break; + case ord_noad_type: + tprint_esc("mathord"); + break; + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + tprint_esc("mathop"); + if (subtype(p) == op_noad_type_limits) + tprint_esc("limits"); + else if (subtype(p) == op_noad_type_no_limits) + tprint_esc("nolimits"); + break; + case bin_noad_type: + tprint_esc("mathbin"); + break; + case rel_noad_type: + tprint_esc("mathrel"); + break; + case open_noad_type: + tprint_esc("mathopen"); + break; + case close_noad_type: + tprint_esc("mathclose"); + break; + case punct_noad_type: + tprint_esc("mathpunct"); + break; + case inner_noad_type: + tprint_esc("mathinner"); + break; + case over_noad_type: + tprint_esc("overline"); + break; + case under_noad_type: + tprint_esc("underline"); + break; + case vcenter_noad_type: + tprint_esc("vcenter"); + break; + default: + tprint("<unknown noad type!>"); + break; } break; case radical_noad: @@ -810,7 +826,6 @@ void display_normal_noad(pointer p) print_subsidiary_data(subscr(p), '_'); } -@ @c void display_fence_noad(pointer p) { if (subtype(p) == right_noad_side) @@ -822,7 +837,6 @@ void display_fence_noad(pointer p) print_delimiter(delimiter(p)); } -@ @c void display_fraction_noad(pointer p) { tprint_esc("fraction, thickness "); @@ -831,18 +845,14 @@ void display_fraction_noad(pointer p) else print_scaled(thickness(p)); if ((left_delimiter(p) != null) && - ((small_fam(left_delimiter(p)) != 0) || - (small_char(left_delimiter(p)) != 0) || - (large_fam(left_delimiter(p)) != 0) || - (large_char(left_delimiter(p)) != 0))) { + ((small_fam(left_delimiter(p)) != 0) || (small_char(left_delimiter(p)) != 0) || + (large_fam(left_delimiter(p)) != 0) || (large_char(left_delimiter(p)) != 0))) { tprint(", left-delimiter "); print_delimiter(left_delimiter(p)); } if ((right_delimiter(p) != null) && - ((small_fam(right_delimiter(p)) != 0) || - (small_char(right_delimiter(p)) != 0) || - (large_fam(right_delimiter(p)) != 0) || - (large_char(right_delimiter(p)) != 0))) { + ((small_fam(right_delimiter(p)) != 0) || (small_char(right_delimiter(p)) != 0) || + (large_fam(right_delimiter(p)) != 0) || (large_char(right_delimiter(p)) != 0))) { tprint(", right-delimiter "); print_delimiter(right_delimiter(p)); } @@ -850,16 +860,19 @@ void display_fraction_noad(pointer p) print_subsidiary_data(denominator(p), '/'); } -@ The routines that \TeX\ uses to create mlists are similar to those we have -just seen for the generation of hlists and vlists. But it is necessary to -make ``noads'' as well as nodes, so the reader should review the -discussion of math mode data structures before trying to make sense out of -the following program. +/*tex + + The routines that \TeX\ uses to create mlists are similar to those we have + just seen for the generation of hlists and vlists. But it is necessary to + make ``noads'' as well as nodes, so the reader should review the discussion + of math mode data structures before trying to make sense out of the following + program. -Here is a little routine that needs to be done whenever a subformula -is about to be processed. The parameter is a code like |math_group|. + Here is a little routine that needs to be done whenever a subformula is about + to be processed. The parameter is a code like |math_group|. + +*/ -@c static void new_save_level_math(group_code c) { set_saved_record(0, saved_textdir, 0, text_dir_ptr); @@ -871,7 +884,6 @@ static void new_save_level_math(group_code c) eq_word_define(int_base + text_direction_code, math_direction_par); } -@ @c static void push_math(group_code c, int mstyle) { if (math_direction_par != text_direction_par) @@ -883,7 +895,6 @@ static void push_math(group_code c, int mstyle) new_save_level_math(c); } -@ @c static void enter_ordinary_math(void) { push_math(math_shift_group, text_style); @@ -892,19 +903,21 @@ static void enter_ordinary_math(void) begin_token_list(every_math_par, every_math_text); } -@ @c void enter_display_math(void); -@ We get into math mode from horizontal mode when a `\.\$' (i.e., a -|math_shift| character) is scanned. We must check to see whether this -`\.\$' is immediately followed by another, in case display math mode is -called for. +/*tex + + We get into math mode from horizontal mode when a `\.\$' (i.e., a + |math_shift| character) is scanned. We must check to see whether this `\.\$' + is immediately followed by another, in case display math mode is called for. + +*/ -@c void init_math(void) { if (cur_cmd == math_shift_cmd) { - get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ + /*tex |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ + get_token(); if ((cur_cmd == math_shift_cmd) && (mode > 0)) { enter_display_math(); } else { @@ -920,15 +933,17 @@ void init_math(void) } } -@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or -`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; -the value of |cur_chr| is placed onto |save_stack| for safe keeping. +/*tex -@ When \TeX\ is in display math mode, |cur_group=math_shift_group|, -so it is not necessary for the |start_eq_no| procedure to test for -this condition. + We get into ordinary math mode from display math mode when `\.{\\eqno}' or + `\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; + the value of |cur_chr| is placed onto |save_stack| for safe keeping. + + When \TeX\ is in display math mode, |cur_group=math_shift_group|, so it is + not necessary for the |start_eq_no| procedure to test for this condition. + +*/ -@c void start_eq_no(void) { set_saved_record(0, saved_eqno, 0, cur_chr); @@ -936,21 +951,24 @@ void start_eq_no(void) enter_ordinary_math(); } -@ Subformulas of math formulas cause a new level of math mode to be entered, -on the semantic nest as well as the save stack. These subformulas arise in -several ways: (1)~A left brace by itself indicates the beginning of a -subformula that will be put into a box, thereby freezing its glue and -preventing line breaks. (2)~A subscript or superscript is treated as a -subformula if it is not a single character; the same applies to -the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive -initiates a subformula that will be terminated by a matching \.{\\right}. -The group codes placed on |save_stack| in these three cases are -|math_group|, |math_group|, and |math_left_group|, respectively. +/*tex + + Subformulas of math formulas cause a new level of math mode to be entered, on + the semantic nest as well as the save stack. These subformulas arise in + several ways: (1)~A left brace by itself indicates the beginning of a + subformula that will be put into a box, thereby freezing its glue and + preventing line breaks. (2)~A subscript or superscript is treated as a + subformula if it is not a single character; the same applies to the nucleus + of things like \.{\\underline}. (3)~The \.{\\left} primitive initiates a + subformula that will be terminated by a matching \.{\\right}. The group codes + placed on |save_stack| in these three cases are |math_group|, |math_group|, + and |math_left_group|, respectively. -Here is the code that handles case (1); the other cases are not quite as -trivial, so we shall consider them later. + Here is the code that handles case (1); the other cases are not quite as + trivial, so we shall consider them later. + +*/ -@c void math_left_brace(void) { pointer q; @@ -961,18 +979,20 @@ void math_left_brace(void) (void) scan_math(nucleus(tail), m_style); } -@ If the inline directions of \.{\\pardir} and \.{\\mathdir} are -opposite, then this function will return true. Discovering that fact -is somewhat odd because it needs traversal of the |save_stack|. -The occurance of displayed equations is weird enough that this is -probably still better than having yet another field in the |input_stack| -structures. +/*tex + + If the inline directions of \.{\\pardir} and \.{\\mathdir} are opposite, then + this function will return true. Discovering that fact is somewhat odd because + it needs traversal of the |save_stack|. The occurance of displayed equations + is weird enough that this is probably still better than having yet another + field in the |input_stack| structures. -None of this makes much sense if the inline direction of either one of -\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current -math machinery is ill suited anyway so I do not bother to test that. + None of this makes much sense if the inline direction of either one of + \.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current math + machinery is ill suited anyway so I do not bother to test that. + +*/ -@c static boolean math_and_text_reversed_p(void) { int i = save_ptr - 1; @@ -989,26 +1009,43 @@ static boolean math_and_text_reversed_p(void) return false; } -@ When we enter display math mode, we need to call |line_break| to -process the partial paragraph that has just been interrupted by the -display. Then we can set the proper values of |display_width| and -|display_indent| and |pre_display_size|. +/*tex + + When we enter display math mode, we need to call |line_break| to process the + partial paragraph that has just been interrupted by the display. Then we can + set the proper values of |display_width| and |display_indent| and + |pre_display_size|. + +*/ -@c void enter_display_math(void) { - scaled w; /* new or partial |pre_display_size| */ - scaled l; /* new |display_width| */ - scaled s; /* new |display_indent| */ + /*tex new or partial |pre_display_size| */ + scaled w; + /*tex new |display_width| */ + scaled l; + /*tex new |display_indent| */ + scaled s; pointer p; - int n; /* scope of paragraph shape specification */ - if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ - (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */ + /*tex scope of paragraph shape specification */ + int n; + /*tex + + `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' or the 2nd of \.{\$\${ }\$\$} + \.{\$\${ }\$\$} + + */ + if (head == tail || + (vlink(head) == tail && type(tail) == local_par_node && vlink(tail) == null)) { if (vlink(head) == tail) { - /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if - there is another display immediately following, we have to get rid - of that node */ + /*tex + + |resume_after_display| inserts a |local_par_node|, but if there + is another display immediately following, we have to get rid of + that node. + + */ flush_node(tail); } pop_nest(); @@ -1017,9 +1054,13 @@ void enter_display_math(void) line_break(true, math_shift_group); w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par); } - /* now we are in vertical mode, working on the list that will contain the display */ - /* A displayed equation is considered to be three lines long, so we - calculate the length and offset of line number |prev_graf+2|. */ + /*tex + + Now we are in vertical mode, working on the list that will contain the + display. A displayed equation is considered to be three lines long, so we + calculate the length and offset of line number |prev_graf+2|. + + */ if (par_shape_par_ptr == null) { if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) { halfword used_hang_indent = swap_hang_indent(hang_indent_par); @@ -1042,7 +1083,6 @@ void enter_display_math(void) l = varmem[p].cint; s = swap_parshape_indent(s,l); } - push_math(math_shift_group, display_style); mode = mmode; eq_word_define(int_base + cur_fam_code, -1); @@ -1058,12 +1098,15 @@ void enter_display_math(void) } } -@ The next routine parses all variations of a delimiter code. The |extcode| - tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the - |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.). - (the class is passed on for conversion to \.{\\mathchar}). +/*tex + + The next routine parses all variations of a delimiter code. The |extcode| + tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the |doclass| + tells whether or not read a math class also (for \.{\\delimiter} c.s.). (the + class is passed on for conversion to \.{\\mathchar}). + +*/ -@c static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) { const char *hlp[] = { @@ -1072,9 +1115,14 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) }; delcodeval d; int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; - if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */ + if (extcode == tex_mathcode) { + /*tex + + \.{\\delcode}, this is the easiest + + */ scan_int(); - /* "MFCCFCC or "FCCFCC */ + /*tex "MFCCFCC or "FCCFCC */ if (doclass) { mcls = (cur_val / 0x1000000); cur_val = (cur_val & 0xFFFFFF); @@ -1087,8 +1135,12 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) mschr = (cur_val % 0x100000) / 0x1000; mlfam = (cur_val & 0xFFF) / 0x100; mlchr = (cur_val % 0x100); - } else if (extcode == umath_mathcode) { /* \.{\\Udelcode} */ - /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */ + } else if (extcode == umath_mathcode) { + /*tex + + \.{\\Udelcode}: <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> + + */ if (doclass) { scan_int(); mcls = cur_val; @@ -1104,11 +1156,13 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) } mlfam = 0; mlchr = 0; - } else if (extcode == umathnum_mathcode) { /* \.{\\Udelcodenum} */ - /* "FF<21bits> */ - /* the largest numeric value is $2^29-1$, but - the top of bit 21 can't be used as it contains invalid USV's - */ + } else if (extcode == umathnum_mathcode) { + /*tex + + \.{\\Udelcodenum}:"FF<21bits>; the largest numeric value is $2^29-1$, + but the top of bit 21 can't be used as it contains invalid USV's. + + */ if (doclass) { /* such a primitive doesn't exist */ confusion("umathnum_mathcode"); } @@ -1123,7 +1177,7 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) mlfam = 0; mlchr = 0; } else { - /* something's gone wrong */ + /*tex Something's gone wrong! */ confusion("unknown_extcode"); } d.class_value = mcls; @@ -1134,7 +1188,6 @@ static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) return d; } -@ @c void scan_extdef_del_code(int level, int extcode) { delcodeval d; @@ -1144,11 +1197,9 @@ void scan_extdef_del_code(int level, int extcode) scan_optional_equals(); d = do_scan_extdef_del_code(extcode, false); set_del_code(p, d.small_family_value, d.small_character_value, - d.large_family_value, d.large_character_value, - (quarterword) (level)); + d.large_family_value, d.large_character_value, (quarterword) (level)); } -@ @c mathcodeval scan_mathchar(int extcode) { char errstr[255] = { 0 }; @@ -1158,15 +1209,11 @@ mathcodeval scan_mathchar(int extcode) }; mathcodeval d; int mcls = 0, mfam = 0, mchr = 0; - if (extcode == tex_mathcode) { /* \.{\\mathcode} */ - /* "TFCC */ + if (extcode == tex_mathcode) { + /*tex \.{\\mathcode}: "TFCC */ scan_int(); if (cur_val > 0x8000) { - /* - tex_error("Invalid math code", hlp); - cur_val = 0; - */ - /* needed for latex: fallback to umathnum_mathcode */ + /*tex Needed for latex: fallback to umathnum_mathcode. */ mfam = (cur_val / 0x200000) & 0x7FF; mcls = mfam % 0x08; mfam = mfam / 0x08; @@ -1188,7 +1235,7 @@ mathcodeval scan_mathchar(int extcode) mchr = (cur_val % 0x100); } } else if (extcode == umath_mathcode) { - /* <0-0x7> <0-0xFF> <0-0x10FFFF> */ + /*tex <0-0x7> <0-0xFF> <0-0x10FFFF> */ scan_int(); mcls = cur_val; scan_int(); @@ -1202,11 +1249,14 @@ mathcodeval scan_mathchar(int extcode) mcls = 0; } } else if (extcode == umathnum_mathcode) { - /* "FFT<21bits> */ - /* the largest numeric value is $2^32-1$, but - the top of bit 21 can't be used as it contains invalid USV's - */ - /* Note: |scan_int| won't accept families 128-255 because these use bit 32 */ + /*tex + + "FFT<21bits>: the largest numeric value is $2^32-1$, but the top of + bit 21 can't be used as it contains invalid USV's + + Note: |scan_int| won't accept families 128-255 because these use bit 32 + + */ scan_int(); mfam = (cur_val / 0x200000) & 0x7FF; mcls = mfam % 0x08; @@ -1219,7 +1269,7 @@ mathcodeval scan_mathchar(int extcode) mchr = 0; } } else { - /* something's gone wrong */ + /*tex Something's gone wrong. */ confusion("unknown_extcode"); } d.class_value = mcls; @@ -1228,7 +1278,6 @@ mathcodeval scan_mathchar(int extcode) return d; } -@ @c void scan_extdef_math_code(int level, int extcode) { mathcodeval d; @@ -1237,12 +1286,11 @@ void scan_extdef_math_code(int level, int extcode) p = cur_val; scan_optional_equals(); d = scan_mathchar(extcode); - set_math_code(p, d.class_value, - d.family_value, d.character_value, (quarterword) (level)); + set_math_code(p, d.class_value, d.family_value, d.character_value, (quarterword) (level)); } -@ this reads in a delcode when actually a mathcode is needed -@c +/*tex This reads in a delcode when actually a mathcode is needed. */ + mathcodeval scan_delimiter_as_mathchar(int extcode) { delcodeval dval; @@ -1254,14 +1302,17 @@ mathcodeval scan_delimiter_as_mathchar(int extcode) return mval; } -@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad -are broken down into subfields called |type| and either |math_list| or -|(math_fam,math_character)|. The job of |scan_math| is to figure out -what to place in one of these principal fields; it looks at the -subformula that comes next in the input, and places an encoding of -that subformula into a given word of |mem|. +/*tex + + Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are broken + down into subfields called |type| and either |math_list| or + |(math_fam,math_character)|. The job of |scan_math| is to figure out what to + place in one of these principal fields; it looks at the subformula that comes + next in the input, and places an encoding of that subformula into a given + word of |mem|. + +*/ -@c #define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) int scan_math_style(pointer p, int mstyle) @@ -1277,7 +1328,6 @@ int scan_math_style(pointer p, int mstyle) int scan_math(pointer p, int mstyle) { - /* label restart,reswitch,exit; */ mathcodeval mval = { 0, 0, 0 }; assert(p != null); RESTART: @@ -1289,7 +1339,7 @@ int scan_math(pointer p, int mstyle) case char_given_cmd: mval = get_math_code(cur_chr); if (mval.class_value == 8) { - /* An active character that is an |outer_call| is allowed here */ + /*tex An active character that is an |outer_call| is allowed here. */ cur_cs = active_to_cs(cur_chr, true); cur_cmd = eq_type(cur_cs); cur_chr = equiv(cur_cs); @@ -1329,8 +1379,12 @@ int scan_math(pointer p, int mstyle) confusion("scan_math"); break; default: - /* The pointer |p| is placed on |save_stack| while a complex subformula - is being scanned. */ + /*tex + + The pointer |p| is placed on |save_stack| while a complex subformula + is being scanned. + + */ back_input(); scan_left_brace(); set_saved_record(0, saved_math, 0, p); @@ -1347,12 +1401,15 @@ int scan_math(pointer p, int mstyle) return 0; } -@ The |set_math_char| procedure creates a new noad appropriate to a given -math code, and appends it to the current mlist. However, if the math code -is sufficiently large, the |cur_chr| is treated as an active character and -nothing is appended. +/*tex + + The |set_math_char| procedure creates a new noad appropriate to a given math + code, and appends it to the current mlist. However, if the math code is + sufficiently large, the |cur_chr| is treated as an active character and + nothing is appended. + +*/ -@c #define math_class_to_type(target,source) \ switch (source) { \ case 0: target = ord_noad_type; break; \ @@ -1366,9 +1423,9 @@ nothing is appended. void set_math_char(mathcodeval mval) { - pointer p; /* the new noad */ + pointer p; if (mval.class_value == 8) { - /* An active character that is an |outer_call| is allowed here */ + /*tex An active character that is an |outer_call| is allowed here */ cur_cs = active_to_cs(cur_chr, true); cur_cmd = eq_type(cur_cs); cur_chr = equiv(cur_cs); @@ -1393,17 +1450,20 @@ void set_math_char(mathcodeval mval) } } -@ The |math_char_in_text| procedure creates a new node representing a math char -in text code, and appends it to the current list. However, if the math code -is sufficiently large, the |cur_chr| is treated as an active character and -nothing is appended. +/*tex + + The |math_char_in_text| procedure creates a new node representing a math char + in text code, and appends it to the current list. However, if the math code + is sufficiently large, the |cur_chr| is treated as an active character and + nothing is appended. + +*/ -@c void math_char_in_text(mathcodeval mval) { - pointer p; /* the new node */ + pointer p; if (mval.class_value == 8) { - /* An active character that is an |outer_call| is allowed here */ + /*tex An active character that is an |outer_call| is allowed here */ cur_cs = active_to_cs(cur_chr, true); cur_cmd = eq_type(cur_cs); cur_chr = equiv(cur_cs); @@ -1416,7 +1476,6 @@ void math_char_in_text(mathcodeval mval) } } -@ @c void math_math_comp(void) { pointer q; @@ -1430,7 +1489,6 @@ void math_math_comp(void) (void) scan_math(nucleus(tail), m_style); } -@ @c void math_limit_switch(void) { const char *hlp[] = { @@ -1449,18 +1507,23 @@ void math_limit_switch(void) tex_error("Limit controls must follow a math operator", hlp); } -@ Delimiter fields of noads are filled in by the |scan_delimiter| routine. -The first parameter of this procedure is the |mem| address where the -delimiter is to be placed; the second tells if this delimiter follows -\.{\\radical} or not. +/*tex + + Delimiter fields of noads are filled in by the |scan_delimiter| routine. The + first parameter of this procedure is the |mem| address where the delimiter + is to be placed; the second tells if this delimiter follows \.{\\radical} or + not. + +*/ -@c static void scan_delimiter(pointer p, int r) { delcodeval dval = { 0, 0, 0, 0, 0 }; - if (r == tex_mathcode) { /* \.{\\radical} */ + if (r == tex_mathcode) { + /*tex \.{\\radical} */ dval = do_scan_extdef_del_code(tex_mathcode, true); - } else if (r == umath_mathcode) { /* \.{\\Uradical} */ + } else if (r == umath_mathcode) { + /*tex \.{\\Uradical} */ dval = do_scan_extdef_del_code(umath_mathcode, false); } else if (r == no_mathcode) { get_next_nb_nr(); @@ -1470,12 +1533,15 @@ static void scan_delimiter(pointer p, int r) dval = get_del_code(cur_chr); break; case delim_num_cmd: - if (cur_chr == 0) /* \.{\\delimiter} */ + if (cur_chr == 0) { + /*tex \.{\\delimiter} */ dval = do_scan_extdef_del_code(tex_mathcode, true); - else if (cur_chr == 1) /* \.{\\Udelimiter} */ + } else if (cur_chr == 1) { + /*tex \.{\\Udelimiter} */ dval = do_scan_extdef_del_code(umath_mathcode, true); - else + } else { confusion("scan_delimiter1"); + } break; default: dval.small_family_value = -1; @@ -1510,7 +1576,6 @@ static void scan_delimiter(pointer p, int r) return; } -@ @c void math_radical(void) { halfword q; @@ -1534,31 +1599,44 @@ void math_radical(void) } } radicaloptions(tail) = options; - if (chr_code == 0) /* \.{\\radical} */ + if (chr_code == 0) + /*tex \.{\\radical} */ scan_delimiter(left_delimiter(tail), tex_mathcode); - else if (chr_code == 1) /* \.{\\Uradical} */ + else if (chr_code == 1) + /*tex \.{\\Uradical} */ scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 2) /* \.{\\Uroot} */ + else if (chr_code == 2) + /*tex \.{\\Uroot} */ scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 3) /* \.{\\Uunderdelimiter} */ + else if (chr_code == 3) + /*tex \.{\\Uunderdelimiter} */ scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 4) /* \.{\\Uoverdelimiter} */ + else if (chr_code == 4) + /*tex \.{\\Uoverdelimiter} */ scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 5) /* \.{\\Udelimiterunder} */ + else if (chr_code == 5) + /*tex \.{\\Udelimiterunder} */ scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 6) /* \.{\\Udelimiterover} */ + else if (chr_code == 6) + /*tex \.{\\Udelimiterover} */ scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 7) /* \.{\\Uhextensible} */ + else if (chr_code == 7) + /*tex \.{\\Uhextensible} */ scan_delimiter(left_delimiter(tail), umath_mathcode); else confusion("math_radical"); if (chr_code == 7) { - q = new_node(sub_box_node, 0); /* type will change */ + /*tex type will change */ + q = new_node(sub_box_node, 0); nucleus(tail) = q; return; } else if (chr_code == 2) { - /* the trick with the |vlink(q)| is used by |scan_math| - to decide whether it needs to go on */ + /*tex + + The trick with the |vlink(q)| is used by |scan_math| to decide + whether it needs to go on. + + */ q = new_node(math_char_node, 0); vlink(q) = tail; degree(tail) = q; @@ -1575,7 +1653,6 @@ void math_radical(void) } } -@ @c void math_ac(void) { halfword q; @@ -1591,15 +1668,17 @@ void math_ac(void) tex_error("Please use \\mathaccent for accents in math mode", hlp); } tail_append(new_node(accent_noad, 0)); - if (cur_chr == 0) { /* \.{\\mathaccent} */ + if (cur_chr == 0) { + /*tex \.{\\mathaccent} */ t = scan_mathchar(tex_mathcode); - } else if (cur_chr == 1) { /* \.{\\Umathaccent} */ + } else if (cur_chr == 1) { + /*tex \.{\\Umathaccent} */ if (scan_keyword("fixed")) { - /* top */ + /*tex top */ subtype(tail) = 1; t = scan_mathchar(umath_mathcode); } else if (scan_keyword("both")) { - /* top bottom */ + /*tex top bottom */ if (scan_keyword("fixed")) { subtype(tail) = 1; } @@ -1609,13 +1688,13 @@ void math_ac(void) } b = scan_mathchar(umath_mathcode); } else if (scan_keyword("bottom")) { - /* bottom */ + /*tex bottom */ if (scan_keyword("fixed")) { subtype(tail) = 2; } b = scan_mathchar(umath_mathcode); } else if (scan_keyword("top")) { - /* top */ + /*tex top */ if (scan_keyword("fixed")) { subtype(tail) = 1; } @@ -1627,7 +1706,7 @@ void math_ac(void) } o = scan_mathchar(umath_mathcode); } else { - /* top */ + /*tex top */ t = scan_mathchar(umath_mathcode); } if (scan_keyword("fraction")) { @@ -1669,7 +1748,6 @@ void math_ac(void) (void) scan_math(nucleus(tail), cramped_style(m_style)); } -@ @c pointer math_vcenter_group(pointer p) { pointer q, r; @@ -1681,10 +1759,13 @@ pointer math_vcenter_group(pointer p) return q; } -@ The routine that scans the four mlists of a \.{\\mathchoice} is very -much like the routine that builds discretionary nodes. +/*tex + + The routine that scans the four mlists of a \.{\\mathchoice} is very much + like the routine that builds discretionary nodes. + +*/ -@c void append_choices(void) { tail_append(new_choice()); @@ -1694,10 +1775,9 @@ void append_choices(void) scan_left_brace(); } -@ @c void build_choices(void) { - pointer p; /* the current mlist */ + pointer p; int prev_style; prev_style = m_style; unsave_math(); @@ -1718,16 +1798,19 @@ void build_choices(void) decr(save_ptr); return; break; - } /* there are no other cases */ + } set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1)); push_math(math_choice_group, (prev_style + 2)); scan_left_brace(); } -@ Subscripts and superscripts are attached to the previous nucleus by the -action procedure called |sub_sup|. +/*tex + + Subscripts and superscripts are attached to the previous nucleus by the + action procedure called |sub_sup|. + +*/ -@c static void do_sub_sup(int no) { pointer q; @@ -1736,7 +1819,8 @@ static void do_sub_sup(int no) q = new_node(sub_mlist_node, 0); nucleus(tail) = q; } - if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */ + if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { + /*tex |super_sub_script| */ if (supscr(tail) != null) { const char *hlp[] = { "I treat `x^1^2' essentially like `x^1{}^2'.", NULL @@ -1781,19 +1865,21 @@ void no_sub_sup(void) do_sub_sup(1); } +/*tex -@ An operation like `\.{\\over}' causes the current mlist to go into a -state of suspended animation: |incompleat_noad| points to a |fraction_noad| -that contains the mlist-so-far as its numerator, while the denominator -is yet to come. Finally when the mlist is finished, the denominator will -go into the incompleat fraction noad, and that noad will become the -whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' -delimiters. + An operation like `\.{\\over}' causes the current mlist to go into a state of + suspended animation: |incompleat_noad| points to a |fraction_noad| that + contains the mlist-so-far as its numerator, while the denominator is yet to + come. Finally when the mlist is finished, the denominator will go into the + incompleat fraction noad, and that noad will become the whole formula, unless + it is surrounded by `\.{\\left}' and `\.{\\right}' delimiters. + +*/ -@c void math_fraction(void) { - halfword c; /* the type of generalized fraction we are scanning */ + /*tex The type of generalized fraction we are scanning: */ + halfword c; pointer q; halfword options = 0; halfword temp_value; @@ -1870,16 +1956,19 @@ void math_fraction(void) } } -@ At the end of a math formula or subformula, the |fin_mlist| routine is -called upon to return a pointer to the newly completed mlist, and to -pop the nest back to the enclosing semantic level. The parameter to -|fin_mlist|, if not null, points to a |fence_noad| that ends the -current mlist; this |fence_noad| has not yet been appended. +/*tex + + At the end of a math formula or subformula, the |fin_mlist| routine is called + upon to return a pointer to the newly completed mlist, and to pop the nest + back to the enclosing semantic level. The parameter to |fin_mlist|, if not + null, points to a |fence_noad| that ends the current mlist; this |fence_noad| + has not yet been appended. + +*/ -@c pointer fin_mlist(pointer p) { - pointer q; /* the mlist to return */ + pointer q; if (incompleat_noad_par != null) { if (denominator(incompleat_noad_par) != null) { type(denominator(incompleat_noad_par)) = sub_mlist_node; @@ -1892,9 +1981,8 @@ pointer fin_mlist(pointer p) q = incompleat_noad_par; } else { q = math_list(numerator(incompleat_noad_par)); - if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) - || (delim_par == null)) - confusion("right"); /* this can't happen */ + if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) || (delim_par == null)) + confusion("right"); math_list(numerator(incompleat_noad_par)) = vlink(delim_par); vlink(delim_par) = incompleat_noad_par; vlink(incompleat_noad_par) = p; @@ -1907,26 +1995,50 @@ pointer fin_mlist(pointer p) return q; } -@ Now at last we're ready to see what happens when a right brace occurs -in a math formula. Two special cases are simplified here: Braces are effectively -removed when they surround a single Ord without sub/superscripts, or when they -surround an accent that is the nucleus of an Ord atom. +/*tex + + Now at last we're ready to see what happens when a right brace occurs in a + math formula. Two special cases are simplified here: Braces are effectively + removed when they surround a single Ord without sub/superscripts, or when + they surround an accent that is the nucleus of an Ord atom. + +*/ -@c void close_math_group(pointer p) { int old_style = m_style; unsave_math(); - decr(save_ptr); assert(saved_type(0) == saved_math); type(saved_value(0)) = sub_mlist_node; p = fin_mlist(null); math_list(saved_value(0)) = p; - if (p != null) { - if (vlink(p) == null) { - if (type(p) == simple_noad && subtype(p) == ord_noad_type) { - if (subscr(p) == null && supscr(p) == null) { + if (p != null && vlink(p) == null) { + if (type(p) == simple_noad) { + if (subscr(p) == null && supscr(p) == null) { + /*tex (subtype(p) == ord_noad_type) */ + int flatten = 0; + int modepar = math_flatten_mode_par; + switch (subtype(p)) { + case ord_noad_type : + flatten = (modepar & 1) == 1; + break; + case bin_noad_type : + flatten = (modepar & 2) == 2; + break; + case rel_noad_type : + flatten = (modepar & 4) == 4; + break; + case punct_noad_type : + flatten = (modepar & 8) == 8; + break; + case inner_noad_type : + flatten = (modepar & 16) == 16; + break; + default: + break; + } + if (flatten) { type(saved_value(0)) = type(nucleus(p)); if (type(nucleus(p)) == math_char_node) { math_fam(saved_value(0)) = math_fam(nucleus(p)); @@ -1941,24 +2053,21 @@ void close_math_group(pointer p) node_attr(nucleus(p)) = null; flush_node(p); } - } else if (type(p) == accent_noad) { - if (saved_value(0) == nucleus(tail)) { - if (type(tail) == simple_noad - && subtype(tail) == ord_noad_type) { - pointer q = head; - while (vlink(q) != tail) - q = vlink(q); - vlink(q) = p; - nucleus(tail) = null; - subscr(tail) = null; - supscr(tail) = null; - delete_attribute_ref(node_attr(p)); - node_attr(p) = node_attr(tail); - node_attr(tail) = null; - flush_node(tail); - tail = p; - } - } + } + } else if (type(p) == accent_noad) { + if (saved_value(0) == nucleus(tail) && type(tail) == simple_noad && subtype(tail) == ord_noad_type) { + pointer q = head; + while (vlink(q) != tail) + q = vlink(q); + vlink(q) = p; + nucleus(tail) = null; + subscr(tail) = null; + supscr(tail) = null; + delete_attribute_ref(node_attr(p)); + node_attr(p) = node_attr(tail); + node_attr(tail) = null; + flush_node(tail); + tail = p; } } } @@ -1969,30 +2078,36 @@ void close_math_group(pointer p) vlink(saved_value(0)) = null; saved_value(0) = q; (void) scan_math(saved_value(0), old_style); - /* restart */ + /*tex restart */ } } -@ We have dealt with all constructions of math mode except `\.{\\left}' and -`\.{\\right}', so the picture is completed by the following sections of -the program. The |middle| feature of eTeX allows one ore several \.{\\middle} -delimiters to appear between \.{\\left} and \.{\\right}. +/*tex + + We have dealt with all constructions of math mode except `\.{\\left}' and + `\.{\\right}', so the picture is completed by the following sections of the + program. The |middle| feature of eTeX allows one ore several \.{\\middle} + delimiters to appear between \.{\\left} and \.{\\right}. + +*/ -@c void math_left_right(void) { - halfword t; /* |left_noad_side| .. |right_noad_side| */ - pointer p; /* new noad */ - pointer q; /* resulting mlist */ - pointer r; /* temporary */ + /*tex |left_noad_side| .. |right_noad_side| */ + halfword t; + /*tex new noad */ + pointer p; + /*tex resulting mlist */ + pointer q; + /*tex temporary */ + pointer r; halfword ht = 0; halfword dp = 0; halfword options = 0; halfword type = -1 ; t = cur_chr; - if (t > 10) { - /* we have \Uleft \Uright \Umiddle */ + /*tex we have \Uleft \Uright \Umiddle */ t = t - 10; while (1) { if (scan_keyword("height")) { @@ -2015,7 +2130,6 @@ void math_left_right(void) } } } - if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) { if (cur_group == math_shift_group) { scan_delimiter(null, no_mathcode); @@ -2041,14 +2155,12 @@ void math_left_right(void) subtype(p) = (quarterword) t; r = new_node(delim_node, 0); delimiter(p) = r; - delimiterheight(p) = ht; delimiterdepth(p) = dp; delimiteroptions(p) = options; delimiterclass(p) = type; delimiteritalic(p) = 0; delimitersamesize(p) = 0; - scan_delimiter(delimiter(p), no_mathcode); if (t == no_noad_side) { tail_append(new_noad()); @@ -2058,7 +2170,6 @@ void math_left_right(void) math_list(nucleus(tail)) = p; return ; } - if (t == left_noad_side) { q = p; } else { @@ -2080,10 +2191,13 @@ void math_left_right(void) } } -@ \TeX\ gets to the following part of the program when -the first `\.\$' ending a display has been scanned. +/*tex + + \TeX\ gets to the following part of the program when the first `\.\$' ending + a display has been scanned. + +*/ -@c static void check_second_math_shift(void) { get_x_token(); @@ -2119,7 +2233,6 @@ static void check_inline_math_end(void) } } -@ @c static void resume_after_display(void) { if (cur_group != math_shift_group) @@ -2129,7 +2242,7 @@ static void resume_after_display(void) push_nest(); mode = hmode; space_factor_par = 1000; - /* this needs to be intercepted in the display math start ! */ + /*tex This needs to be intercepted in the display math start! */ tail_append(make_local_par_node(penalty_par_code)); get_x_token(); if (cur_cmd != spacer_cmd) @@ -2140,30 +2253,40 @@ static void resume_after_display(void) } } -@ The fussiest part of math mode processing occurs when a displayed formula is -being centered and placed with an optional equation number. +/*tex + + The fussiest part of math mode processing occurs when a displayed formula is + being centered and placed with an optional equation number. -At this time we are in vertical mode (or internal vertical mode). + At this time we are in vertical mode (or internal vertical mode). - |p| points to the mlist for the formula. - |a| is either |null| or it points to a box containing the equation number. - |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box). + \starttabulate + \NC \type {p} \NC points to the mlist for the formula \NC \NR + \NC \type {a} \NC is either |null| or it points to a box containing the equation number \NC \NR + \NC \type {l} \NC is true if there was an \.{\\leqno}/ (so |a| is a horizontal box) \NC \NR + \stoptabulate + +*/ -@c #define inject_display_skip_before(g) \ if (g > 0) { \ switch (display_skip_mode_par) { \ - case 0 : /* normal tex | always */ \ - case 1 : /* always */ \ + case 0 : \ + /*tex normal tex | always */ \ + case 1 : \ + /*tex always */ \ tail_append(new_param_glue(g)); \ break; \ - case 2 : /* non-zero */ \ + case 2 : \ + /*tex non-zero */ \ if (! glue_is_zero(glue_par(g))) \ tail_append(new_param_glue(g)); \ break; \ - case 3: /* ignore */ \ + case 3: \ + /*tex ignore */ \ break; \ - default: /* > 3 reserved for future use */ \ + default: \ + /*tex > 3 reserved for future use */ \ tail_append(new_param_glue(g)); \ break; \ } \ @@ -2172,17 +2295,22 @@ At this time we are in vertical mode (or internal vertical mode). #define inject_display_skip_after(g) \ if (g > 0) { \ switch (display_skip_mode_par) { \ - case 0 : /* normal tex | always */ \ - case 1 : /* always */ \ + case 0 : \ + /*tex normal tex | always */ \ + case 1 : \ + /*tex always */ \ tail_append(new_param_glue(g)); \ break; \ - case 2 : /* non-zero */ \ + case 2 : \ + /*tex non-zero */ \ if (! glue_is_zero(glue_par(g))) \ tail_append(new_param_glue(g)); \ break; \ - case 3: /* ignore */ \ + case 3: \ + /*tex ignore */ \ break; \ - default: /* > 3 reserved for future use */ \ + default: \ + /*tex > 3 reserved for future use */ \ tail_append(new_param_glue(g)); \ break; \ } \ @@ -2190,18 +2318,30 @@ At this time we are in vertical mode (or internal vertical mode). static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) { - pointer eq_box; /* box containing the equation */ - scaled eq_w; /* width of the equation */ - scaled line_w; /* width of the line */ - scaled eqno_w; /* width of equation number */ - scaled eqno_w2; /* width of equation number plus space to separate from equation */ - scaled line_s; /* move the line right this much */ - scaled d; /* displacement of equation in the line */ - small_number g1, g2; /* glue parameter codes for before and after */ - pointer r,s; /* kern nodes used to position the display */ - pointer t; /* tail of adjustment list */ - pointer pre_t; /* tail of pre-adjustment list */ - boolean swap_dir; /* true if the math and surrounding text dirs are opposed */ + /*tex box containing the equation */ + pointer eq_box; + /*tex width of the equation */ + scaled eq_w; + /*tex width of the line */ + scaled line_w; + /*tex width of equation number */ + scaled eqno_w; + /*tex width of equation number plus space to separate from equation */ + scaled eqno_w2; + /*tex move the line right this much */ + scaled line_s; + /*tex displacement of equation in the line */ + scaled d; + /*tex glue parameter codes for before and after */ + small_number g1, g2; + /*tex kern nodes used to position the display */ + pointer r,s; + /*tex tail of adjustment list */ + pointer t; + /*tex tail of pre-adjustment list */ + pointer pre_t; + /*tex true if the math and surrounding text dirs are opposed */ + boolean swap_dir; scaled eqno_width; swap_dir = (pre_display_direction_par < 0 ? true : false ); if (eqno_box != null && swap_dir) @@ -2209,7 +2349,7 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) adjust_tail = adjust_head; pre_adjust_tail = pre_adjust_head; eq_box = hpack(p, 0, additional, -1); - subtype(eq_box) = equation_list; /* new */ + subtype(eq_box) = equation_list; build_attribute_list(eq_box); p = list_ptr(eq_box); t = adjust_tail; @@ -2227,21 +2367,23 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) eqno_w = width(eqno_box); eqno_width = eqno_w; eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000); - subtype(eqno_box) = equation_number_list; /* new */ - /* build_attribute_list(eqno_box); */ /* probably already set */ - } + subtype(eqno_box) = equation_number_list; + /*tex build_attribute_list(eqno_box); */ + } if (eq_w + eqno_w2 > line_w) { - /* The user can force the equation number to go on a separate line - by causing its width to be zero. */ + /*tex + + The user can force the equation number to go on a separate line by + causing its width to be zero. + + */ if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w) - || (total_shrink[sfi] != 0) - || (total_shrink[fil] != 0) - || (total_shrink[fill] != 0) - || (total_shrink[filll] != 0))) { + || (total_shrink[sfi] != 0) || (total_shrink[fil] != 0) + || (total_shrink[fill] != 0) || (total_shrink[filll] != 0))) { list_ptr(eq_box) = null; flush_node(eq_box); eq_box = hpack(p, line_w - eqno_w2, exactly, -1); - subtype(eq_box) = equation_list; /* new */ + subtype(eq_box) = equation_list; build_attribute_list(eq_box); } else { eqno_w = 0; @@ -2249,47 +2391,52 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) list_ptr(eq_box) = null; flush_node(eq_box); eq_box = hpack(p, line_w, exactly, -1); - subtype(eq_box) = equation_list; /* new */ + subtype(eq_box) = equation_list; build_attribute_list(eq_box); } } eq_w = width(eq_box); } - /* We try first to center the display without regard to the existence of - the equation number. If that would make it too close (where ``too close'' - means that the space between display and equation number is less than the - width of the equation number), we either center it in the remaining space - or move it as far from the equation number as possible. The latter alternative - is taken only if the display begins with glue, since we assume that the - user put glue there to control the spacing precisely. - */ + /*tex + + We try first to center the display without regard to the existence of the + equation number. If that would make it too close (where ``too close'' + means that the space between display and equation number is less than the + width of the equation number), we either center it in the remaining space + or move it as far from the equation number as possible. The latter + alternative is taken only if the display begins with glue, since we + assume that the user put glue there to control the spacing precisely. + + */ d = half(line_w - eq_w); - if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */ + if ((eqno_w > 0) && (d < 2 * eqno_w)) { + /*tex too close */ d = half(line_w - eq_w - eqno_w); if (p != null) if (!is_char_node(p)) if (type(p) == glue_node) d = 0; } - tail_append(new_penalty(pre_display_penalty_par,after_display_penalty)); - if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */ + if ((d + line_s <= pre_display_size_par) || l) { + /*tex not enough clearance */ g1 = above_display_skip_code; g2 = below_display_skip_code; } else { g1 = above_display_short_skip_code; g2 = below_display_short_skip_code; } + /*tex + + If the equation number is set on a line by itself, either before or after + the formula, we append an infinite penalty so that no page break will + separate the display from its number; and we use the same size and + displacement for all three potential lines of the display, even though + `\.{\\parshape}' may specify them differently. - /* If the equation number is set on a line by itself, either before or - after the formula, we append an infinite penalty so that no page break will - separate the display from its number; and we use the same size and - displacement for all three potential lines of the display, even though - `\.{\\parshape}' may specify them differently. - */ - /* \.{\\leqno} on a forced single line due to |width=0| */ - /* it follows that |type(a)=hlist_node| */ + \.{\\leqno} on a forced single line due to |width=0|; it follows that |type(a)=hlist_node| + */ if (eqno_box && l && (eqno_w == 0)) { /* if (math_direction_par==dir_TLT) { */ shift_amount(eqno_box) = 0; @@ -2300,40 +2447,27 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) } else { inject_display_skip_before(g1); } - if (eqno_w != 0) { r = new_kern(line_w - eq_w - eqno_w - d); if (l) { if (swap_dir) { if (math_direction_par==dir_TLT) { - /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 1\n"); -#endif + /*tex TRT + TLT + \eqno: (swap_dir=true, math_direction_par=TLT, l=true) */ s = new_kern(width(r) + eqno_w); try_couple_nodes(eqno_box,r); try_couple_nodes(r,eq_box); try_couple_nodes(eq_box,s); } else { - /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 2\n"); -#endif + /*tex TLT + TRT + \eqno: (swap_dir=true, math_direction_par=TRT, l=true) */ try_couple_nodes(eqno_box,r); try_couple_nodes(r,eq_box); } } else { if (math_direction_par==dir_TLT) { - /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 3\n"); -#endif + /*tex TLT + TLT + \leqno: (swap_dir=false, math_direction_par=TLT, l=true) */ s = new_kern(width(r) + eqno_w); } else { - /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 4\n"); -#endif + /*tex TRT + TRT + \leqno: (swap_dir=false, math_direction_par=TRT, l=true) */ s = new_kern(width(r)); } try_couple_nodes(eqno_box,r); @@ -2344,30 +2478,18 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) } else { if (swap_dir) { if (math_direction_par==dir_TLT) { - /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 5\n"); -#endif + /*tex TRT + TLT + \leqno: (swap_dir=true, math_direction_par=TLT, l=false) */ } else { - /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 6\n"); -#endif + /*tex TLT + TRT + \leqno: (swap_dir=true, math_direction_par=TRT, l=false) */ } try_couple_nodes(eq_box,r); try_couple_nodes(r,eqno_box); } else { if (math_direction_par==dir_TLT) { - /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 7\n"); -#endif + /*tex TLT + TLT + \eqno: (swap_dir=false, math_direction_par=TLT, l=false) */ s = new_kern(d); } else { - /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 8\n"); -#endif + /*tex TRT + TRT + \eqno: (swap_dir=false, math_direction_par=TRT, l=false) */ s = new_kern(width(r) + eqno_w); } try_couple_nodes(s,eq_box); @@ -2383,9 +2505,8 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) } else { shift_amount(eq_box) = line_s + d; } -/* check for prev: */ + /*tex check for prev: */ append_to_vlist(eq_box,lua_key_index(equation)); - if ((eqno_box != null) && (eqno_w == 0) && !l) { tail_append(new_penalty(inf_penalty,equation_number_penalty)); /* if (math_direction_par==dir_TLT) { */ @@ -2395,15 +2516,14 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) append_to_vlist(eqno_box,lua_key_index(equation_number)); g2 = 0; } - if (t != adjust_head) { /* migrating material comes after equation number */ + if (t != adjust_head) { + /*tex migrating material comes after equation number */ vlink(tail) = vlink(adjust_head); - /* needs testing */ alink(adjust_tail) = alink(tail); tail = t; } if (pre_t != pre_adjust_head) { vlink(tail) = vlink(pre_adjust_head); - /* needs testing */ alink(pre_adjust_tail) = alink(tail); tail = pre_t; } @@ -2412,20 +2532,25 @@ static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) resume_after_display(); } -@ @c void after_math(void) { - int m; /* |mmode| or |-mmode| */ - pointer p; /* the formula */ - pointer a = null; /* box containing equation number */ - boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ + /*tex |mmode| or |-mmode| */ + int m; + /*tex the formula */ + pointer p; + /*tex box containing equation number */ + pointer a = null; + /*tex `\.{\\leqno}' instead of `\.{\\eqno}' */ + boolean l = false; m = mode; - p = fin_mlist(null); /* this pops the nest */ + /*tex this pops the nest */ + p = fin_mlist(null); if (cur_cmd == math_shift_cs_cmd && (cur_chr == text_style || cur_chr == display_style)) { you_cant(); } - if (mode == -m) { /* end of equation number */ + if (mode == -m) { + /*tex end of equation number */ if (cur_cmd == math_shift_cmd) { check_second_math_shift(); } else { @@ -2435,7 +2560,8 @@ void after_math(void) a = hpack(vlink(temp_head), 0, additional, -1); build_attribute_list(a); unsave_math(); - decr(save_ptr); /* now |cur_group=math_shift_group| */ + /*tex now |cur_group=math_shift_group| */ + decr(save_ptr); assert(saved_type(0) == saved_eqno); if (saved_value(0) == 1) l = true; @@ -2444,47 +2570,50 @@ void after_math(void) } if (m < 0) { - /* The |unsave| is done after everything else here; hence an appearance of - `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these - particular \.\$'s. This is consistent with the conventions of - `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the - space above that display. - */ + /*tex + + The |unsave| is done after everything else here; hence an appearance + of `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing + at these particular \.\$'s. This is consistent with the conventions + of `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display + affects the space above that display. + + */ if (cur_cmd == math_shift_cs_cmd) { check_inline_math_end(); } tail_append(new_math(math_surround_par, before)); - /* begin mathskip code */ + /*tex begin mathskip code */ switch (math_skip_mode) { case 0 : - /* obey mathsurround when zero glue */ + /*tex obey mathsurround when zero glue */ if (! glue_is_zero(math_skip_par)) { copy_glue_values(tail,math_skip_par); surround(tail) = 0; } break ; case 1 : - /* always left */ + /*tex always left */ case 3 : - /* always both */ + /*tex always both */ case 6 : - /* only when skip */ + /*tex only when skip */ copy_glue_values(tail,math_skip_par); surround(tail) = 0; break ; case 2 : - /* only right */ + /*tex only right */ surround(tail) = 0; break ; case 4 : - /* ignore, obey marthsurround */ + /*tex ignore, obey marthsurround */ break ; case 5: - /* all spacing disabled */ + /*tex all spacing disabled */ surround(tail) = 0; break ; } - /* end mathskip code */ + /*tex end mathskip code */ if (dir_math_save) { tail_append(new_dir(math_direction_par)); } @@ -2494,41 +2623,42 @@ void after_math(void) tail = vlink(tail); } if (dir_math_save) { - tail_append(new_dir(math_direction_par - dir_swap)); + tail_append(new_dir(math_direction_par)); + subtype(tail) = cancel_dir; } dir_math_save = false; tail_append(new_math(math_surround_par, after)); - /* begin mathskip code */ + /*tex begin mathskip code */ switch (math_skip_mode) { case 0 : - /* obey mathsurround when zero glue */ + /*tex obey mathsurround when zero glue */ if (! glue_is_zero(math_skip_par)) { copy_glue_values(tail,math_skip_par); surround(tail) = 0; } break ; case 2 : - /* always right */ + /*tex always right */ case 3 : - /* always both */ + /*tex always both */ case 6 : - /* only when skip */ + /*tex only when skip */ copy_glue_values(tail,math_skip_par); surround(tail) = 0; break ; case 1 : - /* only left */ + /*tex only left */ surround(tail) = 0; break ; case 4 : - /* ignore, obey marthsurround */ + /*tex ignore, obey marthsurround */ break ; case 5: - /* all spacing disabled */ + /*tex all spacing disabled */ surround(tail) = 0; break ; } - /* end mathskip code */ + /*tex end mathskip code */ space_factor_par = 1000; unsave_math(); } else { @@ -2544,12 +2674,15 @@ void after_math(void) } } -@ When \.{\\halign} appears in a display, the alignment routines operate -essentially as they do in vertical mode. Then the following program is -activated, with |p| and |q| pointing to the beginning and end of the -resulting list, and with |aux_save| holding the |prev_depth| value. +/*tex + + When \.{\\halign} appears in a display, the alignment routines operate + essentially as they do in vertical mode. Then the following program is + activated, with |p| and |q| pointing to the beginning and end of the + resulting list, and with |aux_save| holding the |prev_depth| value. + +*/ -@c void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) { do_assignments(); @@ -2570,9 +2703,8 @@ void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) resume_after_display(); } -@ Interface to \.{\\Umath} and \.{\\mathstyle} +/*tex Interface to \.{\\Umath} and \.{\\mathstyle}: */ -@c void setup_math_style(void) { pointer q; @@ -2582,7 +2714,6 @@ void setup_math_style(void) (void) scan_math_style(nucleus(tail), num_style(m_style)); } -@ @c void print_math_style(void) { if (abs(mode) == mmode) diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.c b/Build/source/texk/web2c/luatexdir/tex/texnodes.c new file mode 100644 index 00000000000..1dd28cdf56d --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.c @@ -0,0 +1,4770 @@ +/* + +Copyright 2006-2010 Taco Hoekwater <taco@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "ptexlib.h" +#include "lua/luatex-api.h" + +/*tex + + This module started out using NDEBUG to trigger checking invalid node usage, + something that is needed because users can mess up nodes in Lua. At some + point that code was always enabled so it is now always on but still can be + recognized as additional code. And as the performance hit is close to zero so + disabling makes no sense, not even to make it configureable. There is a + little more memory used but that is neglectable compared to other memory + usage. + +*/ + +#define MAX_CHAIN_SIZE 13 /* why not a bit larger */ +#define CHECK_NODE_USAGE 1 /* this triggers checking */ + +memory_word *volatile varmem = NULL; + +#ifdef CHECK_NODE_USAGE + char *varmem_sizes = NULL; +#endif + +halfword var_mem_max = 0; +halfword rover = 0; + +halfword free_chain[MAX_CHAIN_SIZE] = { null }; + +static int my_prealloc = 0; + +/*tex Used in font and lang: */ + +int fix_node_lists = 1; + +/*tex Defined below. */ + +halfword slow_get_node(int s); + +#define fake_node 100 +#define fake_node_size 2 +#define fake_node_name "fake" + +#define variable_node_size 2 + +/*tex + + The following definitions are used for keys at the \LUA\ end and + provide an efficient way to share hashed strings. + +*/ + +# define init_node_key(target,n,key) \ + target[n].lua = luaS_##key##_index; \ + target[n].name = luaS_##key##_ptr; + +# define init_field_key(target,n,key) \ + target[n].lua = luaS_##key##_index; \ + target[n].name = luaS_##key##_ptr; + +# define init_field_nop(target,n) \ + target[n].lua = 0; \ + target[n].name = NULL; + +/*tex The fields of nodes. */ + +field_info node_fields_accent[10]; +field_info node_fields_adjust[3]; +field_info node_fields_attribute[3]; +field_info node_fields_attribute_list[1]; +field_info node_fields_boundary[3]; +field_info node_fields_choice[6]; +field_info node_fields_delim[6]; +field_info node_fields_dir[4]; +field_info node_fields_disc[6]; +field_info node_fields_fence[8]; +field_info node_fields_fraction[10]; +field_info node_fields_glue[8]; +field_info node_fields_glue_spec[6]; +field_info node_fields_glyph[16]; +field_info node_fields_insert[7]; +field_info node_fields_inserting[9]; +field_info node_fields_kern[4]; +field_info node_fields_list[11]; +field_info node_fields_local_par[9]; +field_info node_fields_margin_kern[4]; +field_info node_fields_mark[4]; +field_info node_fields_math[8]; +field_info node_fields_math_char[4]; +field_info node_fields_math_text_char[4]; +field_info node_fields_noad[5]; +field_info node_fields_penalty[3]; +field_info node_fields_radical[9]; +field_info node_fields_rule[9]; +field_info node_fields_splitup[6]; +field_info node_fields_style[3]; +field_info node_fields_sub_box[3]; +field_info node_fields_sub_mlist[3]; +field_info node_fields_unset[12]; + +/*tex The fields of whatsit nodes. */ + +field_info node_fields_whatsit_close[3]; +field_info node_fields_whatsit_late_lua[6]; +field_info node_fields_whatsit_open[6]; +field_info node_fields_whatsit_save_pos[2]; +field_info node_fields_whatsit_special[3]; +field_info node_fields_whatsit_user_defined[5]; +field_info node_fields_whatsit_write[4]; + +field_info node_fields_whatsit_pdf_action[7]; +field_info node_fields_whatsit_pdf_annot[7]; +field_info node_fields_whatsit_pdf_colorstack[5]; +field_info node_fields_whatsit_pdf_dest[10]; +field_info node_fields_whatsit_pdf_end_link[2]; +field_info node_fields_whatsit_pdf_end_thread[2]; +field_info node_fields_whatsit_pdf_literal[4]; +field_info node_fields_whatsit_pdf_refobj[3]; +field_info node_fields_whatsit_pdf_restore[2]; +field_info node_fields_whatsit_pdf_save[2]; +field_info node_fields_whatsit_pdf_setmatrix[3]; +field_info node_fields_whatsit_pdf_start_link[8]; +field_info node_fields_whatsit_pdf_start_thread[8]; +field_info node_fields_whatsit_pdf_thread[8]; + +/*tex The values of fields. */ + +subtype_info node_values_dir[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { 3, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_pdf_destination[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { 3, NULL, 0 }, + { 4, NULL, 0 }, + { 5, NULL, 0 }, + { 6, NULL, 0 }, + { 7, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_pdf_action[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { 3, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_pdf_window[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_color_stack[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { 3, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_fill[] = { + { normal, NULL, 0 }, + { sfi, NULL, 0 }, + { fil, NULL, 0 }, + { fill, NULL, 0 }, + { filll, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_values_pdf_literal[] = { + { set_origin, NULL, 0 }, + { direct_page, NULL, 0 }, + { direct_always, NULL, 0 }, + { direct_raw, NULL, 0 }, + { direct_text, NULL, 0 }, + { direct_font, NULL, 0 }, + { scan_special, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info other_values_page_states[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { 2, NULL, 0 }, + { -1, NULL, 0 }, +}; + +/*tex The subtypes of nodes (most have one). */ + +subtype_info node_subtypes_dir[] = { + { normal_dir, NULL, 0 }, + { cancel_dir, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_glue[] = { + { user_skip_glue, NULL, 0 }, + { line_skip_glue, NULL, 0 }, + { baseline_skip_glue, NULL, 0 }, + { par_skip_glue, NULL, 0 }, + { above_display_skip_glue, NULL, 0 }, + { below_display_skip_glue, NULL, 0 }, + { above_display_short_skip_glue, NULL, 0 }, + { below_display_short_skip_glue, NULL, 0 }, + { left_skip_glue, NULL, 0 }, + { right_skip_glue, NULL, 0 }, + { top_skip_glue, NULL, 0 }, + { split_top_skip_glue, NULL, 0 }, + { tab_skip_glue, NULL, 0 }, + { space_skip_glue, NULL, 0 }, + { xspace_skip_glue, NULL, 0 }, + { par_fill_skip_glue, NULL, 0 }, + { math_skip_glue, NULL, 0 }, + { thin_mu_skip_glue, NULL, 0 }, + { med_mu_skip_glue, NULL, 0 }, + { thick_mu_skip_glue, NULL, 0 }, + /* math */ + { cond_math_glue, NULL, 0 }, + { mu_glue, NULL, 0 }, + /* leaders */ + { a_leaders, NULL, 0 }, + { c_leaders, NULL, 0 }, + { x_leaders, NULL, 0 }, + { g_leaders, NULL, 0 }, + { -1, NULL, 0 }, +}; + +/*tex + + Math glue and leaders have special numbers. At some point we might decide to + move them down so best don't use hard coded numbers! + +*/ + +subtype_info node_subtypes_mathglue[] = { /* 98+ */ + { cond_math_glue, NULL, 0 }, + { mu_glue, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_leader[] = { /* 100+ */ + { a_leaders, NULL, 0 }, + { c_leaders, NULL, 0 }, + { x_leaders, NULL, 0 }, + { g_leaders, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_boundary[] = { + { cancel_boundary, NULL, 0 }, + { user_boundary, NULL, 0 }, + { protrusion_boundary, NULL, 0 }, + { word_boundary, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_penalty[] = { + { user_penalty, NULL, 0 }, + { linebreak_penalty, NULL, 0 }, + { line_penalty, NULL, 0 }, + { word_penalty, NULL, 0 }, + { final_penalty, NULL, 0 }, + { noad_penalty, NULL, 0 }, + { before_display_penalty, NULL, 0 }, + { after_display_penalty, NULL, 0 }, + { equation_number_penalty, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_kern[] = { + { font_kern, NULL, 0 }, + { explicit_kern, NULL, 0 }, + { accent_kern, NULL, 0 }, + { italic_kern, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_rule[] = { + { normal_rule, NULL, 0 }, + { box_rule, NULL, 0 }, + { image_rule, NULL, 0 }, + { empty_rule, NULL, 0 }, + { user_rule, NULL, 0 }, + { math_over_rule, NULL, 0 }, + { math_under_rule, NULL, 0 }, + { math_fraction_rule, NULL, 0 }, + { math_radical_rule, NULL, 0 }, + { outline_rule, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_glyph[] = { + { glyph_unset, NULL, 0 }, + { glyph_character, NULL, 0 }, + { glyph_ligature, NULL, 0 }, + { glyph_ghost, NULL, 0 }, + { glyph_left, NULL, 0 }, + { glyph_right, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_disc[] = { + { discretionary_disc, NULL, 0 }, + { explicit_disc, NULL, 0 }, + { automatic_disc, NULL, 0 }, + { syllable_disc, NULL, 0 }, + { init_disc, NULL, 0 }, + { select_disc, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_marginkern[] = { + { left_side, NULL, 0 }, + { right_side, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_list[] = { + { unknown_list, NULL, 0 }, + { line_list, NULL, 0 }, + { hbox_list, NULL, 0 }, + { indent_list, NULL, 0 }, + { align_row_list, NULL, 0 }, + { align_cell_list, NULL, 0 }, + { equation_list, NULL, 0 }, + { equation_number_list, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_adjust[] = { + { 0, NULL, 0 }, + { 1, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_math[] = { + { before, NULL, 0 }, + { after, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_noad[] = { + { ord_noad_type, NULL, 0 }, + { op_noad_type_normal, NULL, 0 }, + { op_noad_type_limits, NULL, 0 }, + { op_noad_type_no_limits, NULL, 0 }, + { bin_noad_type, NULL, 0 }, + { rel_noad_type, NULL, 0 }, + { open_noad_type, NULL, 0 }, + { close_noad_type, NULL, 0 }, + { punct_noad_type, NULL, 0 }, + { inner_noad_type, NULL, 0 }, + { under_noad_type, NULL, 0 }, + { over_noad_type, NULL, 0 }, + { vcenter_noad_type, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_radical[] = { + { radical_noad_type, NULL, 0 }, + { uradical_noad_type, NULL, 0 }, + { uroot_noad_type, NULL, 0 }, + { uunderdelimiter_noad_type, NULL, 0 }, + { uoverdelimiter_noad_type, NULL, 0 }, + { udelimiterunder_noad_type, NULL, 0 }, + { udelimiterover_noad_type, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_accent[] = { + { bothflexible_accent, NULL, 0 }, + { fixedtop_accent, NULL, 0 }, + { fixedbottom_accent, NULL, 0 }, + { fixedboth_accent, NULL, 0 }, + { -1, NULL, 0 }, +}; + +subtype_info node_subtypes_fence[] = { + { unset_noad_side, NULL, 0 }, + { left_noad_side, NULL, 0 }, + { middle_noad_side, NULL, 0 }, + { right_noad_side, NULL, 0 }, + { no_noad_side, NULL, 0 }, + { -1, NULL, 0 }, +}; + +/*tes This brings all together */ + +node_info node_data[] = { + { hlist_node, box_node_size, node_subtypes_list, node_fields_list, NULL, 1, 0 }, + { vlist_node, box_node_size, node_subtypes_list, node_fields_list, NULL, 2, 0 }, + { rule_node, rule_node_size, node_subtypes_rule, node_fields_rule, NULL, 3, 0 }, + { ins_node, ins_node_size, NULL, node_fields_insert, NULL, 4, 0 }, + { mark_node, mark_node_size, NULL, node_fields_mark, NULL, 5, 0 }, + { adjust_node, adjust_node_size, node_subtypes_adjust, node_fields_adjust, NULL, 6, 0 }, + { boundary_node, boundary_node_size, node_subtypes_boundary, node_fields_boundary, NULL, -1, 0 }, + { disc_node, disc_node_size, node_subtypes_disc, node_fields_disc, NULL, 8, 0 }, + { whatsit_node, -1, NULL, NULL, NULL, 9, 0 }, + { local_par_node, local_par_size, NULL, node_fields_local_par, NULL, -1, 0 }, + { dir_node, dir_node_size, node_subtypes_dir, node_fields_dir, NULL, -1, 0 }, + { math_node, math_node_size, node_subtypes_math, node_fields_math, NULL, 10, 0 }, + { glue_node, glue_node_size, node_subtypes_glue, node_fields_glue, NULL, 11, 0 }, + { kern_node, kern_node_size, node_subtypes_kern, node_fields_kern, NULL, 12, 0 }, + { penalty_node, penalty_node_size, node_subtypes_penalty, node_fields_penalty, NULL, 13, 0 }, + { unset_node, box_node_size, NULL, node_fields_unset, NULL, 14, 0 }, + { style_node, style_node_size, NULL, node_fields_style, NULL, 15, 0 }, + { choice_node, style_node_size, NULL, node_fields_choice, NULL, 15, 0 }, + { simple_noad, noad_size, node_subtypes_noad, node_fields_noad, NULL, 15, 0 }, + { radical_noad, radical_noad_size, node_subtypes_radical, node_fields_radical, NULL, 15, 0 }, + { fraction_noad, fraction_noad_size, NULL, node_fields_fraction, NULL, 15, 0 }, + { accent_noad, accent_noad_size, node_subtypes_accent, node_fields_accent, NULL, 15, 0 }, + { fence_noad, fence_noad_size, node_subtypes_fence, node_fields_fence, NULL, 15, 0 }, + { math_char_node, math_kernel_node_size, NULL, node_fields_math_char, NULL, 15, 0 }, + { sub_box_node, math_kernel_node_size, NULL, node_fields_sub_box, NULL, 15, 0 }, + { sub_mlist_node, math_kernel_node_size, NULL, node_fields_sub_mlist, NULL, 15, 0 }, + { math_text_char_node, math_kernel_node_size, NULL, node_fields_math_text_char, NULL, 15, 0 }, + { delim_node, math_shield_node_size, NULL, node_fields_delim, NULL, 15, 0 }, + { margin_kern_node, margin_kern_node_size, node_subtypes_marginkern, node_fields_margin_kern, NULL, -1, 0 }, + { glyph_node, glyph_node_size, node_subtypes_glyph, node_fields_glyph, NULL, 0, 0 }, + { align_record_node, box_node_size, NULL, NULL, NULL, -1, 0 }, + { pseudo_file_node, pseudo_file_node_size, NULL, NULL, NULL, -1, 0 }, + { pseudo_line_node, variable_node_size, NULL, NULL, NULL, -1, 0 }, + { inserting_node, page_ins_node_size, NULL, node_fields_inserting, NULL, -1, 0 }, + { split_up_node, page_ins_node_size, NULL, node_fields_splitup, NULL, -1, 0 }, + { expr_node, expr_node_size, NULL, NULL, NULL, -1, 0 }, + { nesting_node, nesting_node_size, NULL, NULL, NULL, -1, 0 }, + { span_node, span_node_size, NULL, NULL, NULL, -1, 0 }, + { attribute_node, attribute_node_size, NULL, node_fields_attribute, NULL, -1, 0 }, + { glue_spec_node, glue_spec_size, NULL, node_fields_glue_spec, NULL, -1, 0 }, + { attribute_list_node, attribute_node_size, NULL, node_fields_attribute_list, NULL, -1, 0 }, + { temp_node, temp_node_size, NULL, NULL, NULL, -1, 0 }, + { align_stack_node, align_stack_node_size, NULL, NULL, NULL, -1, 0 }, + { movement_node, movement_node_size, NULL, NULL, NULL, -1, 0 }, + { if_node, if_node_size, NULL, NULL, NULL, -1, 0 }, + { unhyphenated_node, active_node_size, NULL, NULL, NULL, -1, 0 }, + { hyphenated_node, active_node_size, NULL, NULL, NULL, -1, 0 }, + { delta_node, delta_node_size, NULL, NULL, NULL, -1, 0 }, + { passive_node, passive_node_size, NULL, NULL, NULL, -1, 0 }, + { shape_node, variable_node_size, NULL, NULL, NULL, -1, 0 }, + { -1, -1, NULL, NULL, NULL, -1, 0 } +}; + +void l_set_node_data(void) { + init_node_key(node_data, hlist_node, hlist) + init_node_key(node_data, vlist_node, vlist) + init_node_key(node_data, rule_node, rule) + init_node_key(node_data, ins_node, ins) + init_node_key(node_data, mark_node, mark) + init_node_key(node_data, adjust_node, adjust) + init_node_key(node_data, boundary_node, boundary) + init_node_key(node_data, disc_node, disc) + init_node_key(node_data, whatsit_node, whatsit) + init_node_key(node_data, local_par_node, local_par) + init_node_key(node_data, dir_node, dir) + init_node_key(node_data, math_node, math) + init_node_key(node_data, glue_node, glue) + init_node_key(node_data, kern_node, kern) + init_node_key(node_data, penalty_node, penalty) + init_node_key(node_data, unset_node, unset) + init_node_key(node_data, style_node, style) + init_node_key(node_data, choice_node, choice) + init_node_key(node_data, simple_noad, noad) + init_node_key(node_data, radical_noad, radical) + init_node_key(node_data, fraction_noad, fraction) + init_node_key(node_data, accent_noad, accent) + init_node_key(node_data, fence_noad, fence) + init_node_key(node_data, math_char_node, math_char) + init_node_key(node_data, sub_box_node, sub_box) + init_node_key(node_data, sub_mlist_node, sub_mlist) + init_node_key(node_data, math_text_char_node, math_text_char) + init_node_key(node_data, delim_node, delim) + init_node_key(node_data, margin_kern_node, margin_kern) + init_node_key(node_data, glyph_node, glyph) + init_node_key(node_data, align_record_node, align_record) + init_node_key(node_data, pseudo_file_node, pseudo_file) + init_node_key(node_data, pseudo_line_node, pseudo_line) + init_node_key(node_data, inserting_node, page_insert) + init_node_key(node_data, split_up_node, split_insert) + init_node_key(node_data, expr_node, expr_stack) + init_node_key(node_data, nesting_node, nested_list) + init_node_key(node_data, span_node, span) + init_node_key(node_data, attribute_node, attribute) + init_node_key(node_data, glue_spec_node, glue_spec) + init_node_key(node_data, attribute_list_node, attribute_list) + init_node_key(node_data, temp_node, temp) + init_node_key(node_data, align_stack_node, align_stack) + init_node_key(node_data, movement_node, movement_stack) + init_node_key(node_data, if_node, if_stack) + init_node_key(node_data, unhyphenated_node, unhyphenated) + init_node_key(node_data, hyphenated_node, hyphenated) + init_node_key(node_data, delta_node, delta) + init_node_key(node_data, passive_node, passive) + init_node_key(node_data, shape_node, shape) + + init_node_key(node_subtypes_dir, normal_dir, normal) + init_node_key(node_subtypes_dir, cancel_dir, cancel) + + init_node_key(node_subtypes_glue, user_skip_glue, userskip) + init_node_key(node_subtypes_glue, line_skip_glue, lineskip) + init_node_key(node_subtypes_glue, baseline_skip_glue, baselineskip) + init_node_key(node_subtypes_glue, par_skip_glue, parskip) + init_node_key(node_subtypes_glue, above_display_skip_glue, abovedisplayskip) + init_node_key(node_subtypes_glue, below_display_skip_glue, belowdisplayskip) + init_node_key(node_subtypes_glue, above_display_short_skip_glue, abovedisplayshortskip) + init_node_key(node_subtypes_glue, below_display_short_skip_glue, belowdisplayshortskip) + init_node_key(node_subtypes_glue, left_skip_glue, leftskip) + init_node_key(node_subtypes_glue, right_skip_glue, rightskip) + init_node_key(node_subtypes_glue, top_skip_glue, topskip) + init_node_key(node_subtypes_glue, split_top_skip_glue, splittopskip) + init_node_key(node_subtypes_glue, tab_skip_glue, tabskip) + init_node_key(node_subtypes_glue, space_skip_glue, spaceskip) + init_node_key(node_subtypes_glue, xspace_skip_glue, xspaceskip) + init_node_key(node_subtypes_glue, par_fill_skip_glue, parfillskip) + init_node_key(node_subtypes_glue, math_skip_glue, mathskip) + init_node_key(node_subtypes_glue, thin_mu_skip_glue, thinmuskip) + init_node_key(node_subtypes_glue, med_mu_skip_glue, medmuskip) + init_node_key(node_subtypes_glue, thick_mu_skip_glue, thickmuskip) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 1, conditionalmathskip) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 2, muglue) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 3, leaders) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 4, cleaders) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 5, xleaders) + init_node_key(node_subtypes_glue, thick_mu_skip_glue + 6, gleaders) + + init_node_key(node_subtypes_mathglue, 0, conditionalmathskip) + init_node_key(node_subtypes_mathglue, 1, muglue) + + init_node_key(node_subtypes_leader, 0, leaders) + init_node_key(node_subtypes_leader, 1, cleaders) + init_node_key(node_subtypes_leader, 2, xleaders) + init_node_key(node_subtypes_leader, 3, gleaders) + + init_node_key(node_subtypes_boundary, cancel_boundary, cancel) + init_node_key(node_subtypes_boundary, user_boundary, user) + init_node_key(node_subtypes_boundary, protrusion_boundary, protrusion) + init_node_key(node_subtypes_boundary, word_boundary, word) + + init_node_key(node_subtypes_penalty, user_penalty, userpenalty) + init_node_key(node_subtypes_penalty, linebreak_penalty, linebreakpenalty) + init_node_key(node_subtypes_penalty, line_penalty, linepenalty) + init_node_key(node_subtypes_penalty, word_penalty, wordpenalty) + init_node_key(node_subtypes_penalty, final_penalty, finalpenalty) + init_node_key(node_subtypes_penalty, noad_penalty, noadpenalty) + init_node_key(node_subtypes_penalty, before_display_penalty, beforedisplaypenalty) + init_node_key(node_subtypes_penalty, after_display_penalty, afterdisplaypenalty) + init_node_key(node_subtypes_penalty, equation_number_penalty, equationnumberpenalty) + + init_node_key(node_subtypes_kern, font_kern, fontkern) + init_node_key(node_subtypes_kern, explicit_kern, userkern) + init_node_key(node_subtypes_kern, accent_kern, accentkern) + init_node_key(node_subtypes_kern, italic_kern, italiccorrection) + + init_node_key(node_subtypes_rule, normal_rule, normal) + init_node_key(node_subtypes_rule, box_rule, box) + init_node_key(node_subtypes_rule, image_rule, image) + init_node_key(node_subtypes_rule, empty_rule, empty) + init_node_key(node_subtypes_rule, user_rule, user) + init_node_key(node_subtypes_rule, math_over_rule, over) + init_node_key(node_subtypes_rule, math_under_rule, under) + init_node_key(node_subtypes_rule, math_fraction_rule, fraction) + init_node_key(node_subtypes_rule, math_radical_rule, radical) + init_node_key(node_subtypes_rule, outline_rule, outline) + + init_node_key(node_subtypes_glyph, 0, unset) + init_node_key(node_subtypes_glyph, 1, character) + init_node_key(node_subtypes_glyph, 2, ligature) + init_node_key(node_subtypes_glyph, 3, ghost) + init_node_key(node_subtypes_glyph, 4, left) + init_node_key(node_subtypes_glyph, 5, right) + + init_node_key(node_subtypes_disc, discretionary_disc, discretionary) + init_node_key(node_subtypes_disc, explicit_disc, explicit) + init_node_key(node_subtypes_disc, automatic_disc, automatic) + init_node_key(node_subtypes_disc, syllable_disc, regular) + init_node_key(node_subtypes_disc, init_disc, first) + init_node_key(node_subtypes_disc, select_disc, second) + + init_node_key(node_subtypes_fence, unset_noad_side, unset) + init_node_key(node_subtypes_fence, left_noad_side, left) + init_node_key(node_subtypes_fence, middle_noad_side, middle) + init_node_key(node_subtypes_fence, right_noad_side, right) + init_node_key(node_subtypes_fence, no_noad_side, no) + + init_node_key(node_subtypes_list, unknown_list, unknown) + init_node_key(node_subtypes_list, line_list, line) + init_node_key(node_subtypes_list, hbox_list, box) + init_node_key(node_subtypes_list, indent_list, indent) + init_node_key(node_subtypes_list, align_row_list, alignment) + init_node_key(node_subtypes_list, align_cell_list, cell) + init_node_key(node_subtypes_list, equation_list, equation) + init_node_key(node_subtypes_list, equation_number_list, equationnumber) + + init_node_key(node_subtypes_math, before, beginmath) + init_node_key(node_subtypes_math, after, endmath) + + init_node_key(node_subtypes_marginkern, left_side, left) + init_node_key(node_subtypes_marginkern, right_side, right) + + init_node_key(node_subtypes_adjust, 0, normal) + init_node_key(node_subtypes_adjust, 1, pre) + + init_node_key(node_subtypes_noad, ord_noad_type, ord) + init_node_key(node_subtypes_noad, op_noad_type_normal, opdisplaylimits) + init_node_key(node_subtypes_noad, op_noad_type_limits, oplimits) + init_node_key(node_subtypes_noad, op_noad_type_no_limits, opnolimits) + init_node_key(node_subtypes_noad, bin_noad_type, bin) + init_node_key(node_subtypes_noad, rel_noad_type, rel) + init_node_key(node_subtypes_noad, open_noad_type, open) + init_node_key(node_subtypes_noad, close_noad_type, close) + init_node_key(node_subtypes_noad, punct_noad_type, punct) + init_node_key(node_subtypes_noad, inner_noad_type, inner) + init_node_key(node_subtypes_noad, under_noad_type, under) + init_node_key(node_subtypes_noad, over_noad_type, over) + init_node_key(node_subtypes_noad, vcenter_noad_type, vcenter) + + init_node_key(node_subtypes_radical, radical_noad_type, radical) + init_node_key(node_subtypes_radical, uradical_noad_type, uradical) + init_node_key(node_subtypes_radical, uroot_noad_type, uroot) + init_node_key(node_subtypes_radical, uunderdelimiter_noad_type, uunderdelimiter) + init_node_key(node_subtypes_radical, uoverdelimiter_noad_type, uoverdelimiter) + init_node_key(node_subtypes_radical, udelimiterunder_noad_type, udelimiterunder) + init_node_key(node_subtypes_radical, udelimiterover_noad_type, udelimiterover) + + init_node_key(node_subtypes_accent, bothflexible_accent, bothflexible) + init_node_key(node_subtypes_accent, fixedtop_accent, fixedtop) + init_node_key(node_subtypes_accent, fixedbottom_accent, fixedbottom) + init_node_key(node_subtypes_accent, fixedboth_accent, fixedboth) + + init_node_key(node_values_fill, normal, normal) + init_node_key(node_values_fill, sfi, fi) + init_node_key(node_values_fill, fil, fil) + init_node_key(node_values_fill, fill, fill) + init_node_key(node_values_fill, filll, filll) + + init_node_key(node_values_dir, 0, TLT) + init_node_key(node_values_dir, 1, TRT) + init_node_key(node_values_dir, 2, LTL) + init_node_key(node_values_dir, 3, RTT) + + init_node_key(other_values_page_states, 0, empty) + init_node_key(other_values_page_states, 1, box_there) + init_node_key(other_values_page_states, 2, inserts_only) + + init_field_key(node_fields_accent, 0, attr); + init_field_key(node_fields_accent, 1, nucleus); + init_field_key(node_fields_accent, 2, sub); + init_field_key(node_fields_accent, 3, sup); + init_field_key(node_fields_accent, 4, accent); + init_field_key(node_fields_accent, 5, bot_accent); + init_field_key(node_fields_accent, 6, top_accent); + init_field_key(node_fields_accent, 7, overlay_accent); + init_field_key(node_fields_accent, 8, fraction); + init_field_nop(node_fields_accent, 9); + + init_field_key(node_fields_adjust, 0, attr); + init_field_key(node_fields_adjust, 1, head); + init_field_nop(node_fields_adjust, 2); + + init_field_key(node_fields_attribute, 0, number); + init_field_key(node_fields_attribute, 1, value); + init_field_nop(node_fields_attribute, 2); + + init_field_nop(node_fields_attribute_list,0); + + init_field_key(node_fields_boundary, 0, attr); + init_field_key(node_fields_boundary, 1, value); + init_field_nop(node_fields_boundary, 2); + + init_field_key(node_fields_choice, 0, attr); + init_field_key(node_fields_choice, 1, display); + init_field_key(node_fields_choice, 2, text); + init_field_key(node_fields_choice, 3, script); + init_field_key(node_fields_choice, 4, scriptscript); + init_field_nop(node_fields_choice, 5); + + init_field_key(node_fields_delim, 0, attr); + init_field_key(node_fields_delim, 1, small_fam); + init_field_key(node_fields_delim, 2, small_char); + init_field_key(node_fields_delim, 3, large_fam); + init_field_key(node_fields_delim, 4, large_char); + init_field_nop(node_fields_delim, 5); + + init_field_key(node_fields_dir, 0, attr); + init_field_key(node_fields_dir, 1, dir); + init_field_key(node_fields_dir, 2, level); + init_field_nop(node_fields_dir, 3); + + init_field_key(node_fields_disc, 0, attr); + init_field_key(node_fields_disc, 1, pre); + init_field_key(node_fields_disc, 2, post); + init_field_key(node_fields_disc, 3, replace); + init_field_key(node_fields_disc, 4, penalty); + init_field_nop(node_fields_disc, 5); + + init_field_key(node_fields_fence, 0, attr); + init_field_key(node_fields_fence, 1, delim); + init_field_key(node_fields_fence, 2, italic); + init_field_key(node_fields_fence, 3, height); + init_field_key(node_fields_fence, 4, depth); + init_field_key(node_fields_fence, 5, options); + init_field_key(node_fields_fence, 6, class); + init_field_nop(node_fields_fence, 7); + + init_field_key(node_fields_fraction, 0, attr); + init_field_key(node_fields_fraction, 1, width); + init_field_key(node_fields_fraction, 2, num); + init_field_key(node_fields_fraction, 3, denom); + init_field_key(node_fields_fraction, 4, left); + init_field_key(node_fields_fraction, 5, right); + init_field_key(node_fields_fraction, 6, middle); + init_field_key(node_fields_fraction, 7, fam); + init_field_key(node_fields_fraction, 8, options); + init_field_nop(node_fields_fraction, 9); + + init_field_key(node_fields_glue, 0, attr); + init_field_key(node_fields_glue, 1, leader); + init_field_key(node_fields_glue, 2, width); + init_field_key(node_fields_glue, 3, stretch); + init_field_key(node_fields_glue, 4, shrink); + init_field_key(node_fields_glue, 5, stretch_order); + init_field_key(node_fields_glue, 6, shrink_order); + init_field_nop(node_fields_glue, 7); + + init_field_key(node_fields_glue_spec, 0, width); + init_field_key(node_fields_glue_spec, 1, stretch); + init_field_key(node_fields_glue_spec, 2, shrink); + init_field_key(node_fields_glue_spec, 3, stretch_order); + init_field_key(node_fields_glue_spec, 4, shrink_order); + init_field_nop(node_fields_glue_spec, 5); + + init_field_key(node_fields_glyph, 0, attr); + init_field_key(node_fields_glyph, 1, char); + init_field_key(node_fields_glyph, 2, font); + init_field_key(node_fields_glyph, 3, lang); + init_field_key(node_fields_glyph, 4, left); + init_field_key(node_fields_glyph, 5, right); + init_field_key(node_fields_glyph, 6, uchyph); + init_field_key(node_fields_glyph, 7, components); + init_field_key(node_fields_glyph, 8, xoffset); + init_field_key(node_fields_glyph, 9, yoffset); + init_field_key(node_fields_glyph, 10, width); + init_field_key(node_fields_glyph, 11, height); + init_field_key(node_fields_glyph, 12, depth); + init_field_key(node_fields_glyph, 13, expansion_factor); + init_field_key(node_fields_glyph, 14, data); + init_field_nop(node_fields_glyph, 15); + + init_field_key(node_fields_insert, 0, attr); + init_field_key(node_fields_insert, 1, cost); + init_field_key(node_fields_insert, 2, depth); + init_field_key(node_fields_insert, 3, height); + init_field_key(node_fields_insert, 4, spec); + init_field_key(node_fields_insert, 5, head); + init_field_nop(node_fields_insert, 6); + + init_field_key(node_fields_inserting, 0, height); + init_field_key(node_fields_inserting, 1, last_ins_ptr); + init_field_key(node_fields_inserting, 2, best_ins_ptr); + init_field_key(node_fields_inserting, 3, width); + init_field_key(node_fields_inserting, 4, stretch); + init_field_key(node_fields_inserting, 5, shrink); + init_field_key(node_fields_inserting, 6, stretch_order); + init_field_key(node_fields_inserting, 7, shrink_order); + init_field_nop(node_fields_inserting, 8); + + init_field_key(node_fields_kern, 0, attr); + init_field_key(node_fields_kern, 1, kern); + init_field_key(node_fields_kern, 2, expansion_factor); + init_field_nop(node_fields_kern, 3); + + init_field_key(node_fields_list, 0, attr); + init_field_key(node_fields_list, 1, width); + init_field_key(node_fields_list, 2, depth); + init_field_key(node_fields_list, 3, height); + init_field_key(node_fields_list, 4, dir); + init_field_key(node_fields_list, 5, shift); + init_field_key(node_fields_list, 6, glue_order); + init_field_key(node_fields_list, 7, glue_sign); + init_field_key(node_fields_list, 8, glue_set); + init_field_key(node_fields_list, 9, head); + init_field_nop(node_fields_list, 10); + + init_field_key(node_fields_local_par, 0, attr); + init_field_key(node_fields_local_par, 1, pen_inter); + init_field_key(node_fields_local_par, 2, pen_broken); + init_field_key(node_fields_local_par, 3, dir); + init_field_key(node_fields_local_par, 4, box_left); + init_field_key(node_fields_local_par, 5, box_left_width); + init_field_key(node_fields_local_par, 6, box_right); + init_field_key(node_fields_local_par, 7, box_right_width); + init_field_nop(node_fields_local_par, 8); + + init_field_key(node_fields_margin_kern, 0, attr); + init_field_key(node_fields_margin_kern, 1, width); + init_field_key(node_fields_margin_kern, 2, glyph); + init_field_nop(node_fields_margin_kern, 3); + + init_field_key(node_fields_mark, 0, attr); + init_field_key(node_fields_mark, 1, class); + init_field_key(node_fields_mark, 2, mark); + init_field_nop(node_fields_mark, 3); + + init_field_key(node_fields_math, 0, attr); + init_field_key(node_fields_math, 1, surround); + init_field_key(node_fields_math, 2, width); + init_field_key(node_fields_math, 3, stretch); + init_field_key(node_fields_math, 4, shrink); + init_field_key(node_fields_math, 5, stretch_order); + init_field_key(node_fields_math, 6, shrink_order); + init_field_nop(node_fields_math, 7); + + init_field_key(node_fields_math_char, 0, attr); + init_field_key(node_fields_math_char, 1, fam); + init_field_key(node_fields_math_char, 2, char); + init_field_nop(node_fields_math_char, 3); + + init_field_key(node_fields_math_text_char, 0, attr); + init_field_key(node_fields_math_text_char, 1, fam); + init_field_key(node_fields_math_text_char, 2, char); + init_field_nop(node_fields_math_text_char, 3); + + init_field_key(node_fields_noad, 0, attr); + init_field_key(node_fields_noad, 1, nucleus); + init_field_key(node_fields_noad, 2, sub); + init_field_key(node_fields_noad, 3, sup); + init_field_nop(node_fields_noad, 4); + + init_field_key(node_fields_penalty, 0, attr); + init_field_key(node_fields_penalty, 1, penalty); + init_field_nop(node_fields_penalty, 2); + + init_field_key(node_fields_radical, 0, attr); + init_field_key(node_fields_radical, 1, nucleus); + init_field_key(node_fields_radical, 2, sub); + init_field_key(node_fields_radical, 3, sup); + init_field_key(node_fields_radical, 4, left); + init_field_key(node_fields_radical, 5, degree); + init_field_key(node_fields_radical, 6, width); + init_field_key(node_fields_radical, 7, options); + init_field_nop(node_fields_radical, 8); + + init_field_key(node_fields_rule, 0, attr); + init_field_key(node_fields_rule, 1, width); + init_field_key(node_fields_rule, 2, depth); + init_field_key(node_fields_rule, 3, height); + init_field_key(node_fields_rule, 4, dir); + init_field_key(node_fields_rule, 5, index); + init_field_key(node_fields_rule, 6, left); + init_field_key(node_fields_rule, 7, right); + init_field_nop(node_fields_rule, 8); + + init_field_key(node_fields_splitup, 0, height); + init_field_key(node_fields_splitup, 1, last_ins_ptr); + init_field_key(node_fields_splitup, 2, best_ins_ptr); + init_field_key(node_fields_splitup, 3, broken_ptr); + init_field_key(node_fields_splitup, 4, broken_ins); + init_field_nop(node_fields_splitup, 5); + + init_field_key(node_fields_style, 0, attr); + init_field_key(node_fields_style, 1, style); + init_field_nop(node_fields_style, 2); + + init_field_key(node_fields_sub_box, 0, attr); + init_field_key(node_fields_sub_box, 1, head); + init_field_nop(node_fields_sub_box, 2); + + init_field_key(node_fields_sub_mlist, 0, attr); + init_field_key(node_fields_sub_mlist, 1, head); + init_field_nop(node_fields_sub_mlist, 2); + + init_field_key(node_fields_unset, 0, attr); + init_field_key(node_fields_unset, 1, width); + init_field_key(node_fields_unset, 2, depth); + init_field_key(node_fields_unset, 3, height); + init_field_key(node_fields_unset, 4, dir); + init_field_key(node_fields_unset, 5, shrink); + init_field_key(node_fields_unset, 6, glue_order); + init_field_key(node_fields_unset, 7, glue_sign); + init_field_key(node_fields_unset, 8, stretch); + init_field_key(node_fields_unset, 9, span); + init_field_key(node_fields_unset, 10, head); + init_field_nop(node_fields_unset, 11); + +} + +node_info whatsit_node_data[] = { + + /*tex These are always there. The fake nodes are historical. */ + + { open_node, open_node_size, NULL, node_fields_whatsit_open, NULL, -1, 0 }, + { write_node, write_node_size, NULL, node_fields_whatsit_write, NULL, -1, 0 }, + { close_node, close_node_size, NULL, node_fields_whatsit_close, NULL, -1, 0 }, + { special_node, special_node_size, NULL, node_fields_whatsit_special, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { save_pos_node, save_pos_node_size, NULL, node_fields_whatsit_save_pos, NULL, -1, 0 }, + { late_lua_node, late_lua_node_size, NULL, node_fields_whatsit_late_lua, NULL, -1, 0 }, + { user_defined_node, user_defined_node_size, NULL, node_fields_whatsit_user_defined, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 }, + + /*tex Here starts the \DVI\ backend section, todo: a separate list. */ + + /*tex {\em There is nothing here.} */ + + /*tex Here starts the \PDF\ backend section, todo: a separate list. */ + + { pdf_literal_node, write_node_size, NULL, node_fields_whatsit_pdf_literal, NULL, -1, 0 }, + { pdf_refobj_node, pdf_refobj_node_size, NULL, node_fields_whatsit_pdf_refobj, NULL, -1, 0 }, + { pdf_annot_node, pdf_annot_node_size, NULL, node_fields_whatsit_pdf_annot, NULL, -1, 0 }, + { pdf_start_link_node, pdf_annot_node_size, NULL, node_fields_whatsit_pdf_start_link, NULL, -1, 0 }, + { pdf_end_link_node, pdf_end_link_node_size, NULL, node_fields_whatsit_pdf_end_link, NULL, -1, 0 }, + { pdf_dest_node, pdf_dest_node_size, NULL, node_fields_whatsit_pdf_dest, NULL, -1, 0 }, + { pdf_action_node, pdf_action_size, NULL, node_fields_whatsit_pdf_action, NULL, -1, 0 }, + { pdf_thread_node, pdf_thread_node_size, NULL, node_fields_whatsit_pdf_thread, NULL, -1, 0 }, + { pdf_start_thread_node, pdf_thread_node_size, NULL, node_fields_whatsit_pdf_start_thread, NULL, -1, 0 }, + { pdf_end_thread_node, pdf_end_thread_node_size, NULL, node_fields_whatsit_pdf_end_thread, NULL, -1, 0 }, + { pdf_thread_data_node, pdf_thread_node_size, NULL, NULL, NULL, -1, 0 }, + { pdf_link_data_node, pdf_annot_node_size, NULL, NULL, NULL, -1, 0 }, + { pdf_colorstack_node, pdf_colorstack_node_size, NULL, node_fields_whatsit_pdf_colorstack, NULL, -1, 0 }, + { pdf_setmatrix_node, pdf_setmatrix_node_size, NULL, node_fields_whatsit_pdf_setmatrix, NULL, -1, 0 }, + { pdf_save_node, pdf_save_node_size, NULL, node_fields_whatsit_pdf_save, NULL, -1, 0 }, + { pdf_restore_node, pdf_restore_node_size, NULL, node_fields_whatsit_pdf_restore, NULL, -1, 0 }, + + /*tex That's it. */ + + { -1, -1, NULL, NULL, NULL, -1, 0 }, + +}; + +void l_set_whatsit_data(void) { + init_node_key(whatsit_node_data, open_node, open) + init_node_key(whatsit_node_data, write_node, write) + init_node_key(whatsit_node_data, close_node, close) + init_node_key(whatsit_node_data, special_node, special) + init_node_key(whatsit_node_data, save_pos_node, save_pos) + init_node_key(whatsit_node_data, late_lua_node, late_lua) + init_node_key(whatsit_node_data, user_defined_node, user_defined) + + init_field_key(node_fields_whatsit_close, 0, attr); + init_field_key(node_fields_whatsit_close, 1, stream); + init_field_nop(node_fields_whatsit_close, 2); + + init_field_key(node_fields_whatsit_late_lua, 0, attr); + init_field_key(node_fields_whatsit_late_lua, 1, reg); + init_field_key(node_fields_whatsit_late_lua, 2, data); + init_field_key(node_fields_whatsit_late_lua, 3, name); + init_field_key(node_fields_whatsit_late_lua, 4, string); + init_field_nop(node_fields_whatsit_late_lua, 5); + + init_field_key(node_fields_whatsit_open, 0, attr); + init_field_key(node_fields_whatsit_open, 1, stream); + init_field_key(node_fields_whatsit_open, 2, name); + init_field_key(node_fields_whatsit_open, 3, area); + init_field_key(node_fields_whatsit_open, 4, ext); + init_field_nop(node_fields_whatsit_open, 5); + + init_field_key(node_fields_whatsit_save_pos, 0, attr); + init_field_nop(node_fields_whatsit_save_pos, 1); + + init_field_key(node_fields_whatsit_special, 0, attr); + init_field_key(node_fields_whatsit_special, 1, data); + init_field_nop(node_fields_whatsit_special, 2); + + init_field_key(node_fields_whatsit_user_defined, 0, attr); + init_field_key(node_fields_whatsit_user_defined, 1, user_id); + init_field_key(node_fields_whatsit_user_defined, 2, type); + init_field_key(node_fields_whatsit_user_defined, 3, value); + init_field_nop(node_fields_whatsit_user_defined, 4); + + init_field_key(node_fields_whatsit_write, 0, attr); + init_field_key(node_fields_whatsit_write, 1, stream); + init_field_key(node_fields_whatsit_write, 2, data); + init_field_nop(node_fields_whatsit_write, 3); + + init_node_key(whatsit_node_data, pdf_literal_node, pdf_literal) + init_node_key(whatsit_node_data, pdf_refobj_node, pdf_refobj) + init_node_key(whatsit_node_data, pdf_annot_node, pdf_annot) + init_node_key(whatsit_node_data, pdf_start_link_node, pdf_start_link) + init_node_key(whatsit_node_data, pdf_end_link_node, pdf_end_link) + init_node_key(whatsit_node_data, pdf_dest_node, pdf_dest) + init_node_key(whatsit_node_data, pdf_action_node, pdf_action) + init_node_key(whatsit_node_data, pdf_thread_node, pdf_thread) + init_node_key(whatsit_node_data, pdf_start_thread_node,pdf_start_thread) + init_node_key(whatsit_node_data, pdf_end_thread_node, pdf_end_thread) + init_node_key(whatsit_node_data, pdf_thread_data_node, pdf_thread_data) + init_node_key(whatsit_node_data, pdf_link_data_node, pdf_link_data) + init_node_key(whatsit_node_data, pdf_colorstack_node, pdf_colorstack) + init_node_key(whatsit_node_data, pdf_setmatrix_node, pdf_setmatrix) + init_node_key(whatsit_node_data, pdf_save_node, pdf_save) + init_node_key(whatsit_node_data, pdf_restore_node, pdf_restore) + + init_node_key(node_values_pdf_destination, 0, xyz) + init_node_key(node_values_pdf_destination, 1, fit) + init_node_key(node_values_pdf_destination, 2, fith) + init_node_key(node_values_pdf_destination, 3, fitv) + init_node_key(node_values_pdf_destination, 4, fitb) + init_node_key(node_values_pdf_destination, 5, fitbh) + init_node_key(node_values_pdf_destination, 6, fitbv) + init_node_key(node_values_pdf_destination, 7, fitr) + + init_node_key(node_values_pdf_literal, set_origin, origin) + init_node_key(node_values_pdf_literal, direct_page, page) + init_node_key(node_values_pdf_literal, direct_always, always) + init_node_key(node_values_pdf_literal, direct_raw, raw) + init_node_key(node_values_pdf_literal, direct_text, text) + init_node_key(node_values_pdf_literal, direct_font, font) + init_node_key(node_values_pdf_literal, scan_special, special) + + init_node_key(node_values_pdf_action, 0, page) + init_node_key(node_values_pdf_action, 1, goto) + init_node_key(node_values_pdf_action, 2, thread) + init_node_key(node_values_pdf_action, 3, user) + + init_node_key(node_values_pdf_window, 0, unset) + init_node_key(node_values_pdf_window, 1, new) + init_node_key(node_values_pdf_window, 2, nonew) + + init_node_key(node_values_color_stack, 0, set) + init_node_key(node_values_color_stack, 1, push) + init_node_key(node_values_color_stack, 2, pop) + init_node_key(node_values_color_stack, 3, current) + + init_field_key(node_fields_whatsit_pdf_action, 0, action_type); + init_field_key(node_fields_whatsit_pdf_action, 1, named_id); + init_field_key(node_fields_whatsit_pdf_action, 2, action_id); + init_field_key(node_fields_whatsit_pdf_action, 3, file); + init_field_key(node_fields_whatsit_pdf_action, 4, new_window); + init_field_key(node_fields_whatsit_pdf_action, 5, data); + init_field_nop(node_fields_whatsit_pdf_action, 6); + + init_field_key(node_fields_whatsit_pdf_annot, 0, attr); + init_field_key(node_fields_whatsit_pdf_annot, 1, width); + init_field_key(node_fields_whatsit_pdf_annot, 2, depth); + init_field_key(node_fields_whatsit_pdf_annot, 3, height); + init_field_key(node_fields_whatsit_pdf_annot, 4, objnum); + init_field_key(node_fields_whatsit_pdf_annot, 5, data); + init_field_nop(node_fields_whatsit_pdf_annot, 6); + + init_field_key(node_fields_whatsit_pdf_colorstack, 0, attr); + init_field_key(node_fields_whatsit_pdf_colorstack, 1, stack); + init_field_key(node_fields_whatsit_pdf_colorstack, 2, cmd); + init_field_key(node_fields_whatsit_pdf_colorstack, 3, data); + init_field_nop(node_fields_whatsit_pdf_colorstack, 4); + + init_field_key(node_fields_whatsit_pdf_dest, 0, attr); + init_field_key(node_fields_whatsit_pdf_dest, 1, width); + init_field_key(node_fields_whatsit_pdf_dest, 2, depth); + init_field_key(node_fields_whatsit_pdf_dest, 3, height); + init_field_key(node_fields_whatsit_pdf_dest, 4, named_id); + init_field_key(node_fields_whatsit_pdf_dest, 5, dest_id); + init_field_key(node_fields_whatsit_pdf_dest, 6, dest_type); + init_field_key(node_fields_whatsit_pdf_dest, 7, xyz_zoom); + init_field_key(node_fields_whatsit_pdf_dest, 8, objnum); + init_field_nop(node_fields_whatsit_pdf_dest, 9); + + init_field_key(node_fields_whatsit_pdf_end_link, 0, attr); + init_field_nop(node_fields_whatsit_pdf_end_link, 1); + + init_field_key(node_fields_whatsit_pdf_end_thread, 0, attr); + init_field_nop(node_fields_whatsit_pdf_end_thread, 1); + + init_field_key(node_fields_whatsit_pdf_literal, 0, attr); + init_field_key(node_fields_whatsit_pdf_literal, 1, mode); + init_field_key(node_fields_whatsit_pdf_literal, 2, data); + init_field_nop(node_fields_whatsit_pdf_literal, 3); + + init_field_key(node_fields_whatsit_pdf_refobj, 0, attr); + init_field_key(node_fields_whatsit_pdf_refobj, 1, objnum); + init_field_nop(node_fields_whatsit_pdf_refobj, 2); + + init_field_key(node_fields_whatsit_pdf_restore, 0, attr); + init_field_nop(node_fields_whatsit_pdf_restore, 1); + + init_field_key(node_fields_whatsit_pdf_save, 0, attr); + init_field_nop(node_fields_whatsit_pdf_save, 1); + + init_field_key(node_fields_whatsit_pdf_setmatrix, 0, attr); + init_field_key(node_fields_whatsit_pdf_setmatrix, 1, data); + init_field_nop(node_fields_whatsit_pdf_setmatrix, 2); + + init_field_key(node_fields_whatsit_pdf_start_link, 0, attr); + init_field_key(node_fields_whatsit_pdf_start_link, 1, width); + init_field_key(node_fields_whatsit_pdf_start_link, 2, depth); + init_field_key(node_fields_whatsit_pdf_start_link, 3, height); + init_field_key(node_fields_whatsit_pdf_start_link, 4, objnum); + init_field_key(node_fields_whatsit_pdf_start_link, 5, link_attr); + init_field_key(node_fields_whatsit_pdf_start_link, 6, action); + init_field_nop(node_fields_whatsit_pdf_start_link, 7); + + init_field_key(node_fields_whatsit_pdf_start_thread, 0, attr); + init_field_key(node_fields_whatsit_pdf_start_thread, 1, width); + init_field_key(node_fields_whatsit_pdf_start_thread, 2, depth); + init_field_key(node_fields_whatsit_pdf_start_thread, 3, height); + init_field_key(node_fields_whatsit_pdf_start_thread, 4, named_id); + init_field_key(node_fields_whatsit_pdf_start_thread, 5, thread_id); + init_field_key(node_fields_whatsit_pdf_start_thread, 6, thread_attr); + init_field_nop(node_fields_whatsit_pdf_start_thread, 7); + + init_field_key(node_fields_whatsit_pdf_thread, 0, attr); + init_field_key(node_fields_whatsit_pdf_thread, 1, width); + init_field_key(node_fields_whatsit_pdf_thread, 2, depth); + init_field_key(node_fields_whatsit_pdf_thread, 3, height); + init_field_key(node_fields_whatsit_pdf_thread, 4, named_id); + init_field_key(node_fields_whatsit_pdf_thread, 5, thread_id); + init_field_key(node_fields_whatsit_pdf_thread, 6, thread_attr); + init_field_nop(node_fields_whatsit_pdf_thread, 7); + +} + +#define last_whatsit_node pdf_restore_node + +/*tex + + When we copy a node list, there are several possibilities: we do the same as + a new node, we copy the entry to the table in properties (a reference), we do + a deep copy of a table in the properties, we create a new table and give it + the original one as a metatable. After some experiments (that also included + timing) with these scenarios I decided that a deep copy made no sense, nor + did nilling. In the end both the shallow copy and the metatable variant were + both ok, although the second ons is slower. The most important aspect to keep + in mind is that references to other nodes in properties no longer can be + valid for that copy. We could use two tables (one unique and one shared) or + metatables but that only complicates matters. + + When defining a new node, we could already allocate a table but it is rather + easy to do that at the lua end e.g. using a metatable __index method. That + way it is under macro package control. + + When deleting a node, we could keep the slot (e.g. setting it to false) but + it could make memory consumption raise unneeded when we have temporary large + node lists and after that only small lists. + + So, in the end this is what we ended up with. For the record, I also + experimented with the following: + + \startitemize + + \startitem + Copy attributes to the properties so that we have fast access at the + lua end: in the end the overhead is not compensated by speed and + convenience, in fact, attributes are not that slow when it comes to + accessing them. + \stopitem + + \startitem + A bitset in the node but again the gain compared to attributes is + neglectable and it also demands a pretty string agreement over what + bit represents what, and this is unlikely to succeed in the tex + community (I could use it for font handling, which is cross package, + but decided that it doesn't pay off. + \stopitem + + \stopitemize + + In case one wonders why properties make sense then, well, it is not so much + speed that we gain, but more convenience: storing all kind of (temporary) + data in attributes is no fun and this mechanism makes sure that properties + are cleaned up when a node is freed. Also, the advantage of a more or less + global properties table is that we stay at the lua end. An alternative is to + store a reference in the node itself but that is complicated by the fact that + the register has some limitations (no numeric keys) and we also don't want to + mess with it too much. + +*/ + +int lua_properties_level = 0 ; +int lua_properties_enabled = 0 ; +int lua_properties_use_metatable = 0 ; + +/*tex + + We keep track of nesting so that we don't oveflow the stack, and, what is + more important, don't keep resolving the registry index. + +*/ + +#define lua_properties_push do { \ + if (lua_properties_enabled) { \ + lua_properties_level = lua_properties_level + 1 ; \ + if (lua_properties_level == 1) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + } \ + } \ +} while(0) + +#define lua_properties_pop do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 1) \ + lua_pop(Luas,1); \ + lua_properties_level = lua_properties_level - 1 ; \ + } \ +} while(0) + +/*tex No setting is needed: */ + +#define lua_properties_set(target) do { \ +} while(0) + +/*tex Resetting boils down to nilling. */ + +#define lua_properties_reset(target) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_pushnil(Luas); \ + lua_rawseti(Luas,-2,target); \ + lua_pop(Luas,1); \ + } else { \ + lua_pushnil(Luas); \ + lua_rawseti(Luas,-2,target); \ + } \ + } \ +} while(0) + +/*tex + + For a moment I considered supporting all kind of data types but in practice + that makes no sense. So we stick to a cheap shallow copy with as option a + metatable. BTW, a deep copy would look like this: + + \starttyping + static void copy_lua_table(lua_State* L, int index) { + lua_newtable(L); + lua_pushnil(L); + while(lua_next(L, index-1) != 0) { + lua_pushvalue(L, -2); + lua_insert(L, -2); + if (lua_type(L,-1)==LUA_TTABLE) + copy_lua_table(L,-1); + lua_settable(L, -4); + } + lua_pop(L,1); + } + + #define lua_properties_copy(target, source) do { \ + if (lua_properties_enabled) { \ + lua_pushinteger(Luas,source); \ + lua_rawget(Luas,-2); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + copy_lua_table(Luas,-1); \ + lua_pushinteger(Luas,target); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas,-3); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } while(0) + \stoptyping + +*/ + +/*tex Isn't there a faster way to metatable? */ + +/*tex + + \starttyping + #define lua_properties_copy(target,source) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setfield(Luas,-2,"__index"); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + lua_pop(Luas,1); \ + } else { \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setfield(Luas,-2,"__index"); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } \ + } while(0) + \stoptyping + +*/ + +/*tex + + A simple testrun on many pages of dumb text shows 1% gain (of course it + depends on how properties are used but some other tests confirm it). + +*/ + +#define lua_properties_copy(target,source) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_push_string_by_name(Luas,__index); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas, -3); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + lua_pop(Luas,1); \ + } else { \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_push_string_by_name(Luas,__index); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas, -3); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } \ +} while(0) + +/*tex Here end the property handlers. */ + +int valid_node(halfword p) +{ + if (p > my_prealloc && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + if (varmem_sizes[p] > 0) { + return 1; + } +#else + return 1; +#endif + } + return 0; +} + +static int test_count = 1; + +#define dorangetest(a,b,c) do { \ + if (!(b>=0 && b<c)) { \ + fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \ + (int)a, (int)b, (int)c, __LINE__,test_count); \ + confusion("node range test failed"); \ + } } while (0) + +#define dotest(a,b,c) do { \ + if (b!=c) { \ + fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \ + (int)a, (int)b, (int)c, __LINE__,test_count); \ + confusion("node test failed"); \ + } } while (0) + +#define check_action_ref(a) { dorangetest(p,a,var_mem_max); } +#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); } + +/*tex hm, we can just pass |p| then. */ + +#define check_token_ref(p) { \ + if (type(p) == whatsit_node) { \ + formatted_error("nodes","fuzzy token cleanup in whatsit node with type %s and subtype %i",node_data[type(p)].name,subtype(p)); \ + } else { \ + formatted_error("nodes","fuzzy token cleanup in node with type %s",node_data[type(p)].name); \ + } \ +} + +#ifdef CHECK_NODE_USAGE + +static void check_static_node_mem(void) +{ + dotest(zero_glue, width(zero_glue), 0); + dotest(zero_glue, type(zero_glue), glue_spec_node); + dotest(zero_glue, vlink(zero_glue), null); + dotest(zero_glue, stretch(zero_glue), 0); + dotest(zero_glue, stretch_order(zero_glue), normal); + dotest(zero_glue, shrink(zero_glue), 0); + dotest(zero_glue, shrink_order(zero_glue), normal); + + dotest(sfi_glue, width(sfi_glue), 0); + dotest(sfi_glue, type(sfi_glue), glue_spec_node); + dotest(sfi_glue, vlink(sfi_glue), null); + dotest(sfi_glue, stretch(sfi_glue), 0); + dotest(sfi_glue, stretch_order(sfi_glue), sfi); + dotest(sfi_glue, shrink(sfi_glue), 0); + dotest(sfi_glue, shrink_order(sfi_glue), normal); + + dotest(fil_glue, width(fil_glue), 0); + dotest(fil_glue, type(fil_glue), glue_spec_node); + dotest(fil_glue, vlink(fil_glue), null); + dotest(fil_glue, stretch(fil_glue), unity); + dotest(fil_glue, stretch_order(fil_glue), fil); + dotest(fil_glue, shrink(fil_glue), 0); + dotest(fil_glue, shrink_order(fil_glue), normal); + + dotest(fill_glue, width(fill_glue), 0); + dotest(fill_glue, type(fill_glue), glue_spec_node); + dotest(fill_glue, vlink(fill_glue), null); + dotest(fill_glue, stretch(fill_glue), unity); + dotest(fill_glue, stretch_order(fill_glue), fill); + dotest(fill_glue, shrink(fill_glue), 0); + dotest(fill_glue, shrink_order(fill_glue), normal); + + dotest(ss_glue, width(ss_glue), 0); + dotest(ss_glue, type(ss_glue), glue_spec_node); + dotest(ss_glue, vlink(ss_glue), null); + dotest(ss_glue, stretch(ss_glue), unity); + dotest(ss_glue, stretch_order(ss_glue), fil); + dotest(ss_glue, shrink(ss_glue), unity); + dotest(ss_glue, shrink_order(ss_glue), fil); + + dotest(fil_neg_glue, width(fil_neg_glue), 0); + dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node); + dotest(fil_neg_glue, vlink(fil_neg_glue), null); + dotest(fil_neg_glue, stretch(fil_neg_glue), -unity); + dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil); + dotest(fil_neg_glue, shrink(fil_neg_glue), 0); + dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal); +} + +static void node_mem_dump(halfword p) +{ + halfword r; + for (r = my_prealloc + 1; r < var_mem_max; r++) { + if (vlink(r) == p) { + halfword s = r; + while (s > my_prealloc && varmem_sizes[s] == 0) { + s--; + } + if (s != null + && s != my_prealloc + && s != var_mem_max + && (r - s) < get_node_size(type(s), subtype(s)) + && alink(s) != p) { + if (type(s) == disc_node) { + fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", + get_node_name(type(s), subtype(s)), (int) s, + (int) vlink(s), (int) alink(s)); + fprintf(stdout, "pre_break(%d,%d,%d), ", + (int) vlink_pre_break(s), (int) tlink(pre_break(s)), + (int) alink(pre_break(s))); + fprintf(stdout, "post_break(%d,%d,%d), ", + (int) vlink_post_break(s), + (int) tlink(post_break(s)), + (int) alink(post_break(s))); + fprintf(stdout, "no_break(%d,%d,%d)", + (int) vlink_no_break(s), (int) tlink(no_break(s)), + (int) alink(no_break(s))); + fprintf(stdout, "\n"); + } else { + if (vlink(s) == p + || (type(s) == glyph_node && lig_ptr (s) == p) + || (type(s) == vlist_node && list_ptr(s) == p) + || (type(s) == hlist_node && list_ptr(s) == p) + || (type(s) == unset_node && list_ptr(s) == p) + || (type(s) == ins_node && ins_ptr (s) == p) + ) { + fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", + get_node_name(type(s), subtype(s)), (int) s, + (int) vlink(s), (int) alink(s)); + if (type(s) == glyph_node) { + fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); + } else if (type(s) == vlist_node || type(s) == hlist_node) { + fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); + } + fprintf(stdout, "\n"); + } else { + if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) { + fprintf(stdout, " pointed to from %s node %d\n", + get_node_name(type(s), subtype(s)), (int) s); + } + } + } + } + } + } +} + +#endif + +static int free_error(halfword p) +{ + if (p > my_prealloc && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + int i; + if (varmem_sizes[p] == 0) { + check_static_node_mem(); + for (i = (my_prealloc + 1); i < var_mem_max; i++) { + if (varmem_sizes[i] > 0) { + check_node(i); + } + } + test_count++; + if (type(p) == glyph_node) { + formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p); + } else { + formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); + } + node_mem_dump(p); + return 1; + } +#endif + } else { + formatted_error("nodes", "attempt to free an impossible node %d", (int) p); + return 1; + } + return 0; +} + +static int copy_error(halfword p) +{ + if (p >= 0 && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + if (p > my_prealloc && varmem_sizes[p] == 0) { + if (type(p) == glyph_node) { + formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p); + } else { + formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); + } + return 1; + } +#endif + } else { + formatted_error("nodes", "attempt to copy an impossible node %d", (int) p); + return 1; + } + return 0; +} + +/*tex + + Because of the 5-10\% overhead that \SYNTEX\ creates some options have been + implemented controlled by |synctex_anyway_mode|. + + \startabulate + \NC \type {1} \NC all but glyphs \NC \NR + \NC \type {2} \NC also glyphs \NC \NR + \NC \type {3} \NC glyphs and glue \NC \NR + \NC \type {4} \NC only glyphs \NC \NR + \stoptabulate + +*/ + +static halfword synctex_anyway_mode = 0; +static halfword synctex_line_field = 0; +static halfword synctex_no_files = 0; + +void synctex_set_mode(int m) +{ + synctex_anyway_mode = m; +}; + +int synctex_get_mode(void) +{ + return synctex_anyway_mode; +}; + +void synctex_set_no_files(int f) +{ + synctex_no_files = f; +}; + +int synctex_get_no_files(void) +{ + return (int) synctex_no_files ; +}; + +void synctex_set_tag(int t) +{ + cur_input.synctex_tag_field = t; +}; + +int synctex_get_tag(void) +{ + return (int) cur_input.synctex_tag_field; +}; + +int synctex_get_line(void) +{ + return (int) synctex_line_field; +}; + +static int forced_tag = 0; +static int forced_line = 0; + +void synctex_force_tag(int t) +{ + forced_tag = t; +}; + +void synctex_force_line(int t) +{ + forced_line = t; +}; + +void synctex_set_line(int l) +{ + synctex_line_field = l; +}; + +/*tex |if_stack| is called a lot so maybe optimize that one. */ + +halfword new_node(int i, int j) +{ + int s = get_node_size(i, j); + halfword n = get_node(s); + /*tex + + It should be possible to do this memset at |free_node()|. Both type() and + subtype() will be set below, and vlink() is set to null by |get_node()|, + so we can do we clearing one word less than |s|. + + */ + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1))); + switch (i) { + case glyph_node: + init_lang_data(n); + break; + case hlist_node: + case vlist_node: + box_dir(n) = -1; + break; + case disc_node: + pre_break(n) = pre_break_head(n); + type(pre_break(n)) = nesting_node; + subtype(pre_break(n)) = pre_break_head(0); + post_break(n) = post_break_head(n); + type(post_break(n)) = nesting_node; + subtype(post_break(n)) = post_break_head(0); + no_break(n) = no_break_head(n); + type(no_break(n)) = nesting_node; + subtype(no_break(n)) = no_break_head(0); + break; + case rule_node: + depth(n) = null_flag; + height(n) = null_flag; + width(n) = null_flag; + rule_dir(n) = -1; + rule_index(n) = 0; + rule_transform(n) = 0; + break; + case whatsit_node: + if (j == open_node) { + open_name(n) = get_nullstr(); + open_area(n) = open_name(n); + open_ext(n) = open_name(n); + } + break; + case unset_node: + width(n) = null_flag; + break; + case pseudo_line_node: + case shape_node: + /*tex + + This is a trick that makes |pseudo_files| slightly slower, but + the overall allocation faster then an explicit test at the top of + |new_node()|. + + */ + if (j>0) { + free_node(n, variable_node_size); + n = slow_get_node(j); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1))); + } + break; + case fraction_noad: + fraction_fam(n) = -1; + break; + case simple_noad: + noad_fam(n) = -1; + break; + default: + break; + } + if (synctex_anyway_mode) { + /*tex See table above. */ + switch (i) { + case glyph_node: + if (synctex_anyway_mode > 1) { + synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case glue_node: + if (synctex_anyway_mode < 4) { + synctex_tag_glue(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glue(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case kern_node: + if (synctex_anyway_mode < 3) { + synctex_tag_kern(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_kern(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case hlist_node: + case vlist_node: + case unset_node: + /*tex Rather useless: */ + if (synctex_anyway_mode < 3) { + synctex_tag_box(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_box(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case rule_node: + if (synctex_anyway_mode < 3) { + synctex_tag_rule(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_rule(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + case math_node: + /*tex Noads probably make more sense but let's not change that. */ + if (synctex_anyway_mode < 3) { + synctex_tag_math(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_math(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + break; + } + } else if (synctex_par) { + /*tex Handle the \SYNTEX\ extension. */ + switch (i) { + case glue_node: + synctex_tag_glue(n) = cur_input.synctex_tag_field; + synctex_line_glue(n) = line; + break; + case kern_node: + if (j != 0) { + synctex_tag_kern(n) = cur_input.synctex_tag_field; + synctex_line_kern(n) = line; + } + break; + case hlist_node: + case vlist_node: + case unset_node: + synctex_tag_box(n) = cur_input.synctex_tag_field; + synctex_line_box(n) = line; + break; + case rule_node: + synctex_tag_rule(n) = cur_input.synctex_tag_field; + synctex_line_rule(n) = line; + break; + case math_node: + synctex_tag_math(n) = cur_input.synctex_tag_field; + synctex_line_math(n) = line; + break; + } + } + /*tex Take care of attributes. */ + if (nodetype_has_attributes(i)) { + build_attribute_list(n); + /*tex No need for |lua_properties_set|. */ + } + type(n) = (quarterword) i; + subtype(n) = (quarterword) j; + return n; +} + +halfword raw_glyph_node(void) +{ + register halfword n = get_node(glyph_node_size); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); + if (synctex_anyway_mode > 1) { + synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + type(n) = glyph_node; + subtype(n) = 0; + return n; +} + +halfword new_glyph_node(void) +{ + register halfword n = get_node(glyph_node_size); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); + if (synctex_anyway_mode > 1) { + synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + type(n) = glyph_node; + subtype(n) = 0; + build_attribute_list(n); + /*tex No need for |lua_properties_set|. */ + return n; +} + +/*tex + + This makes a duplicate of the node list that starts at |p| and returns a + pointer to the new list. + +*/ + +halfword do_copy_node_list(halfword p, halfword end) +{ + /*tex previous position in new list */ + halfword q = null; + /*tex head of the list */ + halfword h = null; + register halfword s ; + /*tex saves stack and time */ + lua_properties_push; + while (p != end) { + s = copy_node(p); + if (h == null) { + h = s; + } else { + couple_nodes(q, s); + } + q = s; + p = vlink(p); + } + /*tex saves stack and time */ + lua_properties_pop; + return h; +} + +halfword copy_node_list(halfword p) +{ + return do_copy_node_list(p, null); +} + +#define copy_sub_list(target,source) do { \ + if (source != null) { \ + s = do_copy_node_list(source, null); \ + target = s; \ + } else { \ + target = null; \ + } \ + } while (0) + +#define copy_sub_node(target,source) do { \ + if (source != null) { \ + s = copy_node(source); \ + target = s ; \ + } else { \ + target = null; \ + } \ +} while (0) + +/*tex Make a dupe of a single node. */ + +static void copy_node_wrapup_core(halfword p, halfword r) +{ + halfword s ; + switch (subtype(p)) { + case write_node: + case special_node: + add_token_ref(write_tokens(p)); + break; + case late_lua_node: + copy_late_lua(r, p); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + add_node_attr_ref(user_node_value(p)); + break; + case 'l': + copy_user_lua(r, p); + break; + case 'n': + s = copy_node_list(user_node_value(p)); + user_node_value(r) = s; + break; + case 's': + /* |add_string_ref(user_node_value(p));| */ + break; + case 't': + add_token_ref(user_node_value(p)); + break; + } + break; + default: + break ; + } +} + +void copy_node_wrapup_dvi(halfword p, halfword r) +{ +} + +void copy_node_wrapup_pdf(halfword p, halfword r) +{ + switch(subtype(p)) { + case pdf_literal_node: + copy_pdf_literal(r, p); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + add_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + add_token_ref(pdf_setmatrix_data(p)); + break; + case pdf_annot_node: + add_token_ref(pdf_annot_data(p)); + break; + case pdf_start_link_node: + if (pdf_link_attr(r) != null) + add_token_ref(pdf_link_attr(r)); + add_action_ref(pdf_link_action(r)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + add_token_ref(pdf_dest_id(p)); + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + add_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + add_token_ref(pdf_thread_attr(p)); + break; + default: + break; + } +} + +halfword copy_node(const halfword p) +{ + /*tex current node being fabricated for new list */ + halfword r; + /*tex whatsit subtype */ + halfword w; + /*tex type of node */ + halfword t; + /*tex a helper variable for copying into variable mem */ + register halfword s; + register int i; + if (copy_error(p)) { + r = new_node(temp_node, 0); + return r; + } + t = type(p); + i = get_node_size(t,subtype(p)); + r = get_node(i); + (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i)); + /*tex A possible speedup: + + \starttyping + if t == glue_spec) { + return r; + } + \stoptyping + + */ + if (synctex_anyway_mode) { + /*tex Not: + + \starttyping + if (t == glyph_node) { + if (synctex_anyway_mode > 1) { + synctex_tag_glyph(r) = forced_tag ? forced_tag : cur_input.synctex_tag_field; + synctex_line_glyph(r) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; + } + } + \stoptyping + */ + } else if (synctex_par) { + /*tex Handle synctex extension. */ + switch (t) { + case math_node: + synctex_tag_math(r) = cur_input.synctex_tag_field; + synctex_line_math(r) = line; + break; + case kern_node: + synctex_tag_kern(r) = cur_input.synctex_tag_field; + synctex_line_kern(r) = line; + break; + } + } + if (nodetype_has_attributes(t)) { + add_node_attr_ref(node_attr(p)); + alink(r) = null; + lua_properties_copy(r,p); + } + vlink(r) = null; + switch (t) { + case glyph_node: + copy_sub_list(lig_ptr(r),lig_ptr(p)) ; + break; + case glue_node: + copy_sub_list(leader_ptr(r),leader_ptr(p)) ; + break; + case hlist_node: + case vlist_node: + case unset_node: + copy_sub_list(list_ptr(r),list_ptr(p)) ; + break; + case disc_node: + pre_break(r) = pre_break_head(r); + if (vlink_pre_break(p) != null) { + s = copy_node_list(vlink_pre_break(p)); + alink(s) = pre_break(r); + tlink_pre_break(r) = tail_of_list(s); + vlink_pre_break(r) = s; + } else { + assert(tlink(pre_break(r)) == null); + } + post_break(r) = post_break_head(r); + if (vlink_post_break(p) != null) { + s = copy_node_list(vlink_post_break(p)); + alink(s) = post_break(r); + tlink_post_break(r) = tail_of_list(s); + vlink_post_break(r) = s; + } else { + assert(tlink_post_break(r) == null); + } + no_break(r) = no_break_head(r); + if (vlink(no_break(p)) != null) { + s = copy_node_list(vlink_no_break(p)); + alink(s) = no_break(r); + tlink_no_break(r) = tail_of_list(s); + vlink_no_break(r) = s; + } else { + assert(tlink_no_break(r) == null); + } + break; + case math_node: + break; + case ins_node: + copy_sub_list(ins_ptr(r),ins_ptr(p)) ; + break; + case margin_kern_node: + copy_sub_node(margin_char(r),margin_char(p)); + break; + case mark_node: + add_token_ref(mark_ptr(p)); + break; + case adjust_node: + copy_sub_list(adjust_ptr(r),adjust_ptr(p)); + break; + case choice_node: + copy_sub_list(display_mlist(r),display_mlist(p)) ; + copy_sub_list(text_mlist(r),text_mlist(p)) ; + copy_sub_list(script_mlist(r),script_mlist(p)) ; + copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ; + break; + case simple_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + break; + case radical_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + copy_sub_node(left_delimiter(r),left_delimiter(p)) ; + copy_sub_list(degree(r),degree(p)) ; + break; + case accent_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ; + copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ; + copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ; + break; + case fence_noad: + copy_sub_node(delimiter(r),delimiter(p)) ; + break; + case sub_box_node: + case sub_mlist_node: + copy_sub_list(math_list(r),math_list(p)) ; + break; + case fraction_noad: + copy_sub_list(numerator(r),numerator(p)) ; + copy_sub_list(denominator(r),denominator(p)) ; + copy_sub_node(left_delimiter(r),left_delimiter(p)) ; + copy_sub_node(right_delimiter(r),right_delimiter(p)) ; + break; + case glue_spec_node: + break; + case dir_node: + break; + case local_par_node: + copy_sub_list(local_box_left(r),local_box_left(p)); + copy_sub_list(local_box_right(r),local_box_right(p)); + case boundary_node: + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + copy_node_wrapup_pdf(p,r); + } else if (w >= backend_first_dvi_whatsit) { + copy_node_wrapup_dvi(p,r); + } else { + copy_node_wrapup_core(p,r); + } + break; + } + return r; +} + +#define free_sub_list(source) if (source != null) flush_node_list(source); +#define free_sub_node(source) if (source != null) flush_node(source); + +static void flush_node_wrapup_core(halfword p) +{ + switch (subtype(p)) { + case open_node: + case write_node: + case close_node: + case save_pos_node: + break; + case special_node: + delete_token_ref(write_tokens(p)); + break; + case late_lua_node: + free_late_lua(p); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + delete_attribute_ref(user_node_value(p)); + break; + case 'd': + break; + case 'l': + free_user_lua(user_node_value(p)); + break; + case 'n': + flush_node_list(user_node_value(p)); + break; + case 's': + /*tex |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */ + break; + case 't': + delete_token_ref(user_node_value(p)); + break; + default: + { + const char *hlp[] = { + "The type of the value in a user defined whatsit node should be one", + "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),", + "or 't' (tokenlist). Yours has an unknown type, and therefore I don't", + "know how to free the node's value. A memory leak may result.", + NULL + }; + tex_error("Unidentified user defined whatsit", hlp); + } + break; + } + break; + } +} + +void flush_node_wrapup_dvi(halfword p) +{ +} + +void flush_node_wrapup_pdf(halfword p) +{ + switch(subtype(p)) { + case pdf_save_node: + case pdf_restore_node: + case pdf_refobj_node: + case pdf_end_link_node: + case pdf_end_thread_node: + break; + case pdf_literal_node: + free_pdf_literal(p); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + delete_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + delete_token_ref(pdf_setmatrix_data(p)); + break; + case pdf_annot_node: + delete_token_ref(pdf_annot_data(p)); + break; + case pdf_link_data_node: + break; + case pdf_start_link_node: + if (pdf_link_attr(p) != null) + delete_token_ref(pdf_link_attr(p)); + delete_action_ref(pdf_link_action(p)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + delete_token_ref(pdf_dest_id(p)); + break; + case pdf_action_node: + if (pdf_action_type(p) == pdf_action_user) { + delete_token_ref(pdf_action_tokens(p)); + } else { + if (pdf_action_file(p) != null) + delete_token_ref(pdf_action_file(p)); + if (pdf_action_type(p) == pdf_action_page) + delete_token_ref(pdf_action_tokens(p)); + else if (pdf_action_named_id(p) > 0) + delete_token_ref(pdf_action_id(p)); + } + break; + case pdf_thread_data_node: + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + delete_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + delete_token_ref(pdf_thread_attr(p)); + break; + } +} + +void flush_node(halfword p) +{ + halfword w; + if (p == null){ + /*tex legal, but no-op. */ + return; + } + if (free_error(p)) + return; + switch (type(p)) { + case glyph_node: + free_sub_list(lig_ptr(p)); + break; + case glue_node: + free_sub_list(leader_ptr(p)); + break; + case hlist_node: + case vlist_node: + case unset_node: + free_sub_list(list_ptr(p)); + break; + case disc_node: + /*tex Watch the start at temp node hack! */ + free_sub_list(vlink(pre_break(p))); + free_sub_list(vlink(post_break(p))); + free_sub_list(vlink(no_break(p))); + break; + case rule_node: + case kern_node: + case penalty_node: + case math_node: + break; + case glue_spec_node: + /*tex This allows free-ing of lua-allocated glue specs. */ + break ; + case dir_node: + break; + case local_par_node: + free_sub_list(local_box_left(p)); + free_sub_list(local_box_right(p)); + break; + case boundary_node: + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + flush_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + flush_node_wrapup_dvi(p); + } else { + flush_node_wrapup_core(p); + } + break; + case ins_node: + flush_node_list(ins_ptr(p)); + break; + case margin_kern_node: + flush_node(margin_char(p)); + break; + case mark_node: + delete_token_ref(mark_ptr(p)); + break; + case adjust_node: + flush_node_list(adjust_ptr(p)); + break; + case style_node: + /*tex Nothing to do. */ + break; + case choice_node: + free_sub_list(display_mlist(p)); + free_sub_list(text_mlist(p)); + free_sub_list(script_mlist(p)); + free_sub_list(script_script_mlist(p)); + break; + case simple_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + break; + case radical_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + free_sub_node(left_delimiter(p)); + free_sub_list(degree(p)); + break; + case accent_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + free_sub_list(top_accent_chr(p)); + free_sub_list(bot_accent_chr(p)); + free_sub_list(overlay_accent_chr(p)); + break; + case fence_noad: + free_sub_list(delimiter(p)); + break; + case delim_node: + case math_char_node: + case math_text_char_node: + /*tex Nothing to do. */ + break; + case sub_box_node: + case sub_mlist_node: + free_sub_list(math_list(p)); + break; + case fraction_noad: + free_sub_list(numerator(p)); + free_sub_list(denominator(p)); + free_sub_node(left_delimiter(p)); + free_sub_node(right_delimiter(p)); + break; + case pseudo_file_node: + free_sub_list(pseudo_lines(p)); + break; + case pseudo_line_node: + case shape_node: + free_node(p, subtype(p)); + return; + break; + case align_stack_node: + case span_node: + case movement_node: + case if_node: + case nesting_node: + case unhyphenated_node: + case hyphenated_node: + case delta_node: + case passive_node: + case inserting_node: + case split_up_node: + case expr_node: + case attribute_node: + case attribute_list_node: + case temp_node: + break; + default: + formatted_error("nodes","flushing weird node type %d", type(p)); + return; + } + if (nodetype_has_attributes(type(p))) { + delete_attribute_ref(node_attr(p)); + lua_properties_reset(p); + } + free_node(p, get_node_size(type(p), subtype(p))); + return; +} + +/*tex Erase the list of nodes starting at |pp|. */ + +void flush_node_list(halfword pp) +{ + register halfword p = pp; + if (p == null) { + /*tex Legal, but no-op. */ + return; + } + if (free_error(p)) + return; + /*tex Saves stack and time. */ + lua_properties_push; + while (p != null) { + register halfword q = vlink(p); + flush_node(p); + p = q; + } + /*tex Saves stack and time. */ + lua_properties_pop; +} + +static void check_node_wrapup_core(halfword p) +{ + switch (subtype(p)) { + /*tex Frontend code. */ + case special_node: + check_token_ref(p); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + check_attribute_ref(user_node_value(p)); + break; + case 't': + check_token_ref(p); + break; + case 'n': + dorangetest(p, user_node_value(p), var_mem_max); + break; + case 's': + case 'd': + break; + default: + confusion("unknown user node type"); + break; + } + break; + case open_node: + case write_node: + case close_node: + case save_pos_node: + break; + } +} + +void check_node_wrapup_dvi(halfword p) +{ +} + +void check_node_wrapup_pdf(halfword p) +{ + switch (subtype(p)) { + case pdf_literal_node: + if (pdf_literal_type(p) == normal) + check_token_ref(p); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + check_token_ref(p); + break; + case pdf_setmatrix_node: + check_token_ref(p); + break; + case late_lua_node: + if (late_lua_name(p) > 0) + check_token_ref(p); + if (late_lua_type(p) == normal) + check_token_ref(p); + break; + case pdf_annot_node: + check_token_ref(p); + break; + case pdf_start_link_node: + if (pdf_link_attr(p) != null) + check_token_ref(p); + check_action_ref(pdf_link_action(p)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + check_token_ref(p); + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + check_token_ref(p); + if (pdf_thread_attr(p) != null) + check_token_ref(p); + break; + case pdf_save_node: + case pdf_restore_node: + case pdf_refobj_node: + case pdf_end_link_node: + case pdf_end_thread_node: + break; + default: + confusion("wrapup pdf nodes"); + break; + } +} + +void check_node(halfword p) +{ + halfword w ; + switch (type(p)) { + case glyph_node: + dorangetest(p, lig_ptr(p), var_mem_max); + break; + case glue_node: + dorangetest(p, leader_ptr(p), var_mem_max); + break; + case hlist_node: + case vlist_node: + case unset_node: + case align_record_node: + dorangetest(p, list_ptr(p), var_mem_max); + break; + case ins_node: + dorangetest(p, ins_ptr(p), var_mem_max); + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + check_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + check_node_wrapup_dvi(p); + } else { + check_node_wrapup_core(p); + } + break; + case margin_kern_node: + check_node(margin_char(p)); + break; + case math_node: + break; + case disc_node: + dorangetest(p, vlink(pre_break(p)), var_mem_max); + dorangetest(p, vlink(post_break(p)), var_mem_max); + dorangetest(p, vlink(no_break(p)), var_mem_max); + break; + case adjust_node: + dorangetest(p, adjust_ptr(p), var_mem_max); + break; + case pseudo_file_node: + dorangetest(p, pseudo_lines(p), var_mem_max); + break; + case pseudo_line_node: + case shape_node: + break; + case choice_node: + dorangetest(p, display_mlist(p), var_mem_max); + dorangetest(p, text_mlist(p), var_mem_max); + dorangetest(p, script_mlist(p), var_mem_max); + dorangetest(p, script_script_mlist(p), var_mem_max); + break; + case fraction_noad: + dorangetest(p, numerator(p), var_mem_max); + dorangetest(p, denominator(p), var_mem_max); + dorangetest(p, left_delimiter(p), var_mem_max); + dorangetest(p, right_delimiter(p), var_mem_max); + break; + case simple_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + break; + case radical_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + dorangetest(p, degree(p), var_mem_max); + dorangetest(p, left_delimiter(p), var_mem_max); + break; + case accent_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + dorangetest(p, top_accent_chr(p), var_mem_max); + dorangetest(p, bot_accent_chr(p), var_mem_max); + dorangetest(p, overlay_accent_chr(p), var_mem_max); + break; + case fence_noad: + dorangetest(p, delimiter(p), var_mem_max); + break; + case local_par_node: + dorangetest(p, local_box_left(p), var_mem_max); + dorangetest(p, local_box_right(p), var_mem_max); + break; + /*tex + + There is no need for useless cases: + + \starttyping + case rule_node: + case kern_node: + case penalty_node: + case mark_node: + case style_node: + case attribute_list_node: + case attribute_node: + case glue_spec_node: + case temp_node: + case align_stack_node: + case movement_node: + case if_node: + case nesting_node: + case span_node: + case unhyphenated_node: + case hyphenated_node: + case delta_node: + case passive_node: + case expr_node: + case dir_node: + case boundary_node: + break; + default: + fprintf(stdout, "check_node: type is %d\n", type(p)); + \stoptyping + + */ + } +} + +halfword fix_node_list(halfword head) +{ + halfword next, tail; + if (head == null) + return null; + tail = head; + next = vlink(head); + while (next != null) { + alink(next) = tail; + tail = next; + next = vlink(tail); + } + return tail; +} + +halfword get_node(int s) +{ + register halfword r; + + if (s < MAX_CHAIN_SIZE) { + r = free_chain[s]; + if (r != null) { + free_chain[s] = vlink(r); +#ifdef CHECK_NODE_USAGE + varmem_sizes[r] = (char) s; +#endif + vlink(r) = null; + /*tex Maintain usage statistics. */ + var_used += s; + return r; + } + /*tex This is the end of the \quote {inner loop}. */ + return slow_get_node(s); + } else { + normal_error("nodes","there is a problem in getting a node, case 1"); + return null; + } +} + +void free_node(halfword p, int s) +{ + if (p <= my_prealloc) { + formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p)); + return; + } +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + if (s < MAX_CHAIN_SIZE) { + vlink(p) = free_chain[s]; + free_chain[s] = p; + } else { + /*tex Todo: it is perhaps possible to merge this node with an existing rover? */ + node_size(p) = s; + vlink(p) = rover; + while (vlink(rover) != vlink(p)) { + rover = vlink(rover); + } + vlink(rover) = p; + } + /*tex Maintain statistics. */ + var_used -= s; +} + +static void free_node_chain(halfword q, int s) +{ + register halfword p = q; + while (vlink(p) != null) { +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + var_used -= s; + p = vlink(p); + } + var_used -= s; +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + vlink(p) = free_chain[s]; + free_chain[s] = q; +} + +/*tex + + At the start of the node memory area we reserve some special nodes, for + instance frequently used glue specifications. We could as well just use + new_glue here but for the moment we stick to the traditional approach. + +*/ + +#define initialize_glue(n,wi,st,sh,sto,sho) \ + vlink(n) = null; \ + type(n) = glue_spec_node; \ + width(n) = wi; \ + stretch(n) = st; \ + shrink(n) = sh; \ + stretch_order(n) = sto; \ + shrink_order(n) = sho; + +#define initialize_whatever(n,t) \ + vinfo(n) = 0; \ + type(n) = t; \ + vlink(n) = null; \ + alink(n) = null; + +#define initialize_point(n) \ + type(n) = glyph_node; \ + subtype(n) = 0; \ + vlink(n) = null; \ + vinfo(n + 1) = null; \ + alink(n) = null; \ + font(n) = 0; \ + character(n) = '.'; \ + vinfo(n + 3) = 0; \ + vlink(n + 3) = 0; \ + vinfo(n + 4) = 0; \ + vlink(n + 4) = 0; + +void init_node_mem(int t) +{ + my_prealloc = var_mem_stat_max; + + varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t); + if (varmem == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word)); +#ifdef CHECK_NODE_USAGE + varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t); + if (varmem_sizes == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t); +#endif + var_mem_max = t; + rover = var_mem_stat_max + 1; + vlink(rover) = rover; + node_size(rover) = (t - rover); + var_used = 0; + + /*tex Initialize static glue specs. */ + + initialize_glue(zero_glue,0,0,0,0,0); + initialize_glue(sfi_glue,0,0,0,sfi,0); + initialize_glue(fil_glue,0,unity,0,fil,0); + initialize_glue(fill_glue,0,unity,0,fill,0); + initialize_glue(ss_glue,0,unity,unity,fil,fil); + initialize_glue(fil_neg_glue,0,-unity,0,fil,0); + + /*tex Initialize node list heads. */ + + initialize_whatever(page_ins_head,temp_node); + initialize_whatever(contrib_head,temp_node); + initialize_whatever(page_head,temp_node); + initialize_whatever(temp_head,temp_node); + initialize_whatever(hold_head,temp_node); + initialize_whatever(adjust_head,temp_node); + initialize_whatever(pre_adjust_head,temp_node); + initialize_whatever(align_head,temp_node); + + initialize_whatever(active,unhyphenated_node); + initialize_whatever(end_span,span_node); + + initialize_point(begin_point); + initialize_point(end_point); +} + +void dump_node_mem(void) +{ + dump_int(var_mem_max); + dump_int(rover); + dump_things(varmem[0], var_mem_max); +#ifdef CHECK_NODE_USAGE + dump_things(varmem_sizes[0], var_mem_max); +#endif + dump_things(free_chain[0], MAX_CHAIN_SIZE); + dump_int(var_used); + dump_int(my_prealloc); +} + +/*tex + + It makes sense to enlarge the varmem array immediately. + +*/ + +void undump_node_mem(void) +{ + int x; + undump_int(x); + undump_int(rover); + var_mem_max = (x < 100000 ? 100000 : x); + varmem = xmallocarray(memory_word, (unsigned) var_mem_max); + undump_things(varmem[0], x); +#ifdef CHECK_NODE_USAGE + varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); + memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char)); + undump_things(varmem_sizes[0], x); +#endif + undump_things(free_chain[0], MAX_CHAIN_SIZE); + undump_int(var_used); + undump_int(my_prealloc); + if (var_mem_max > x) { + /*tex Todo: is it perhaps possible to merge the new node with an existing rover? */ + vlink(x) = rover; + node_size(x) = (var_mem_max - x); + while (vlink(rover) != vlink(x)) { + rover = vlink(rover); + } + vlink(rover) = x; + } +} + +halfword slow_get_node(int s) +{ + register int t; + + RETRY: + t = node_size(rover); + if (vlink(rover) < var_mem_max && vlink(rover) != 0) { + if (t > s) { + /*tex Allocating from the bottom helps decrease page faults. */ + register halfword r = rover; + rover += s; + vlink(rover) = vlink(r); + node_size(rover) = node_size(r) - s; + if (vlink(rover) != r) { + /*tex The list is longer than one. */ + halfword q = r; + while (vlink(q) != r) { + q = vlink(q); + } + vlink(q) += s; + } else { + vlink(rover) += s; + } + if (vlink(rover) < var_mem_max) { +#ifdef CHECK_NODE_USAGE + varmem_sizes[r] = (char) (s > 127 ? 127 : s); +#endif + vlink(r) = null; + /*tex Maintain usage statistics. */ + var_used += s; + /*tex This is the only exit. */ + return r; + } else { + normal_error("nodes","there is a problem in getting a node, case 2"); + return null; + } + } else { + /*tex Attempt to keep the free list small. */ + int x; + if (vlink(rover) != rover) { + if (t < MAX_CHAIN_SIZE) { + halfword l = vlink(rover); + vlink(rover) = free_chain[t]; + free_chain[t] = rover; + rover = l; + while (vlink(l) != free_chain[t]) { + l = vlink(l); + } + vlink(l) = rover; + goto RETRY; + } else { + halfword l = rover; + while (vlink(rover) != l) { + if (node_size(rover) > s) { + goto RETRY; + } + rover = vlink(rover); + } + } + } + /*tex If we are still here, it was apparently impossible to get a match. */ + x = (var_mem_max >> 2) + s; + varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x)); + if (varmem == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word)); +#ifdef CHECK_NODE_USAGE + varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x)); + if (varmem_sizes == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char)); +#endif + /*tex Todo: is it perhaps possible to merge the new memory with an existing rover? */ + vlink(var_mem_max) = rover; + node_size(var_mem_max) = x; + while (vlink(rover) != vlink(var_mem_max)) { + rover = vlink(rover); + } + vlink(rover) = var_mem_max; + rover = var_mem_max; + var_mem_max += x; + goto RETRY; + } + } else { + normal_error("nodes","there is a problem in getting a node, case 3"); + return null; + } +} + +char *sprint_node_mem_usage(void) +{ + char *s; +#ifdef CHECK_NODE_USAGE + char *ss; + int i; + int b = 0; + char msg[256]; + int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; + s = strdup(""); + for (i = (var_mem_max - 1); i > my_prealloc; i--) { + if (varmem_sizes[i] > 0) { + if (type(i) > last_normal_node + last_whatsit_node) { + node_counts[last_normal_node + last_whatsit_node + 1]++; + } else if (type(i) == whatsit_node) { + node_counts[(subtype(i) + last_normal_node + 1)]++; + } else { + node_counts[type(i)]++; + } + } + } + for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) { + if (node_counts[i] > 0) { + int j = + (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0); + snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], + get_node_name((i > last_normal_node ? whatsit_node : i), j)); + ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1)); + strcpy(ss, s); + strcat(ss, msg); + free(s); + s = ss; + b = 1; + } + } +#else + s = strdup(""); +#endif + return s; +} + +halfword list_node_mem_usage(void) +{ + halfword q = null; +#ifdef CHECK_NODE_USAGE + halfword p = null; + halfword i, j; + char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); + memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max); + for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { + if (saved_varmem_sizes[i] > 0) { + j = copy_node(i); + if (p == null) { + q = j; + } else { + vlink(p) = j; + } + p = j; + } + } + free(saved_varmem_sizes); +#endif + return q; +} + +void print_node_mem_stats(void) +{ + int i, b; + halfword j; + char msg[256]; + char *s; + int free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; + snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc)); + tprint_nl(msg); + s = sprint_node_mem_usage(); + tprint_nl(" "); + tprint(s); + free(s); + tprint(" nodes"); + tprint_nl(" avail lists: "); + b = 0; + for (i = 1; i < MAX_CHAIN_SIZE; i++) { + for (j = free_chain[i]; j != null; j = vlink(j)) + free_chain_counts[i]++; + if (free_chain_counts[i] > 0) { + snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]); + tprint(msg); + b = 1; + } + } + /*tex A newline, if needed: */ + print_nlp(); +} + +halfword new_span_node(halfword n, int s, scaled w) +{ + halfword p = new_node(span_node, 0); + span_link(p) = n; + span_span(p) = s; + width(p) = w; + return p; +} + +/* Now comes some attribute stuff. */ + +static halfword new_attribute_node(unsigned int i, int v) +{ + register halfword r = get_node(attribute_node_size); + type(r) = attribute_node; + attribute_id(r) = (halfword) i; + attribute_value(r) = v; + /* not used but nicer in print */ + subtype(r) = 0; + return r; +} + +halfword copy_attribute_list(halfword n) +{ + halfword q = get_node(attribute_node_size); + register halfword p = q; + type(p) = attribute_list_node; + attr_list_ref(p) = 0; + n = vlink(n); + while (n != null) { + register halfword r = get_node(attribute_node_size); + /*tex The link will be fixed automatically in the next loop. */ + (void) memcpy((void *) (varmem + r), (void *) (varmem + n), (sizeof(memory_word) * attribute_node_size)); + vlink(p) = r; + p = r; + n = vlink(n); + } + return q; +} + +void update_attribute_cache(void) +{ + halfword p; + register int i; + attr_list_cache = get_node(attribute_node_size); + type(attr_list_cache) = attribute_list_node; + attr_list_ref(attr_list_cache) = 0; + p = attr_list_cache; + for (i = 0; i <= max_used_attr; i++) { + register int v = attribute(i); + if (v > UNUSED_ATTRIBUTE) { + register halfword r = new_attribute_node((unsigned) i, v); + vlink(p) = r; + p = r; + } + } + if (vlink(attr_list_cache) == null) { + free_node(attr_list_cache, attribute_node_size); + attr_list_cache = null; + } + return; +} + +void build_attribute_list(halfword b) +{ + if (max_used_attr >= 0) { + if (attr_list_cache == cache_disabled|| attr_list_cache == null) { + update_attribute_cache(); + if (attr_list_cache == null) + return; + } + attr_list_ref(attr_list_cache)++; + node_attr(b) = attr_list_cache; + } +} + +halfword current_attribute_list(void) +{ + if (max_used_attr >= 0) { + if (attr_list_cache == cache_disabled) { + update_attribute_cache(); + } + return attr_list_cache ; + } + return null ; +} + +void reassign_attribute(halfword n, halfword new) +{ + halfword old; + old = node_attr(n); + if (new == null) { + /*tex There is nothing to assign but we need to check for an old value. */ + if (old != null) { + /*tex This also nulls |attr| field of |n|. */ + delete_attribute_ref(old); + } + } else if (old == null) { + /*tex Nothing is assigned so we just do that now. */ + assign_attribute_ref(n,new); + } else if (old != new) { + /*tex Something is assigned so we need to clean up and assign then. */ + delete_attribute_ref(old); + assign_attribute_ref(n,new); + } + /*tex The same value so there is no need to assign and change the refcount. */ + node_attr(n) = new ; +} + +void delete_attribute_ref(halfword b) +{ + if (b != null) { + if (type(b) == attribute_list_node){ + attr_list_ref(b)--; + if (attr_list_ref(b) == 0) { + if (b == attr_list_cache) + attr_list_cache = cache_disabled; + free_node_chain(b, attribute_node_size); + } + /*tex Maintain sanity. */ + if (attr_list_ref(b) < 0) { + attr_list_ref(b) = 0; + } + } else { + normal_error("nodes","trying to delete an attribute reference of a non attribute node"); + } + } +} + +void reset_node_properties(halfword b) +{ + if (b != null) { + lua_properties_reset(b); + } +} + +/*tex Here |p| is an attr list head, or zero. */ + +halfword do_set_attribute(halfword p, int i, int val) +{ + register halfword q; + register int j = 0; + if (p == null) { + /*tex Add a new head \& node. */ + q = get_node(attribute_node_size); + type(q) = attribute_list_node; + attr_list_ref(q) = 1; + p = new_attribute_node((unsigned) i, val); + vlink(q) = p; + return q; + } + q = p; + if (vlink(p) != null) { + while (vlink(p) != null) { + int t = attribute_id(vlink(p)); + if (t == i && attribute_value(vlink(p)) == val) { + /*tex There is no need to do anything. */ + return q; + } + if (t >= i) + break; + j++; + p = vlink(p); + } + p = q; + while (j-- > 0) + p = vlink(p); + if (attribute_id(vlink(p)) == i) { + attribute_value(vlink(p)) = val; + } else { + /*tex Add a new node. */ + halfword r = new_attribute_node((unsigned) i, val); + vlink(r) = vlink(p); + vlink(p) = r; + } + return q; + } else { + normal_error("nodes","trying to set an attribute fails, case 1"); + return null ; + } +} + +void set_attribute(halfword n, int i, int val) +{ + register halfword p; + register int j = 0; + /*tex Not all nodes can have an attribute list. */ + if (!nodetype_has_attributes(type(n))) + return; + /*tex If we have no list, we create one and quit. */ + p = node_attr(n); + if (p == null) { /* add a new head \& node */ + p = get_node(attribute_node_size); + type(p) = attribute_list_node; + attr_list_ref(p) = 1; + node_attr(n) = p; + p = new_attribute_node((unsigned) i, val); + vlink(node_attr(n)) = p; + return; + } + /*tex We check if we have this attribute already and quit if the value stays the same. */ + if (vlink(p) != null) { + while (vlink(p) != null) { + int t = attribute_id(vlink(p)); + if (t == i && attribute_value(vlink(p)) == val) + return; + if (t >= i) + break; + j++; + p = vlink(p); + } + /*tex If found |j| has now the position and we assume a sorted list ! */ + p = node_attr(n); + if (attr_list_ref(p) == 0 ) { + /*tex The list is invalid i.e. freed already. */ + formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n); + /*tex The still dangling list gets ref count 1. */ + attr_list_ref(p) = 1; + } else if (attr_list_ref(p) == 1) { + /*tex This can really happen! */ + if (p == attr_list_cache) { + /*tex + + We can invalidate the cache setting with |attr_list_cache = + cache_disabled| or or save the list, as done below. + + */ + p = copy_attribute_list(p); + node_attr(n) = p; + /*tex The copied list gets ref count 1. */ + attr_list_ref(p) = 1; + } + } else { + /*tex The list is used multiple times so we make a copy. */ + p = copy_attribute_list(p); + /*tex We decrement the ref count or the original. */ + delete_attribute_ref(node_attr(n)); + node_attr(n) = p; + /*tex The copied list gets ref count 1. */ + attr_list_ref(p) = 1; + } + /*tex We go to position |j| in the list. */ + while (j-- > 0) + p = vlink(p); + /*tex If we have a hit we just set the value otherwise we add a new node. */ + if (attribute_id(vlink(p)) == i) { + attribute_value(vlink(p)) = val; + } else { + /*tex Add a new node. */ + halfword r = new_attribute_node((unsigned) i, val); + vlink(r) = vlink(p); + vlink(p) = r; + } + } else { + normal_error("nodes","trying to set an attribute fails, case 2"); + } +} + +int unset_attribute(halfword n, int i, int val) +{ + register halfword p; + register int t; + register int j = 0; + if (!nodetype_has_attributes(type(n))) + return null; + p = node_attr(n); + if (p == null) + return UNUSED_ATTRIBUTE; + if (attr_list_ref(p) == 0) { + formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n); + return UNUSED_ATTRIBUTE; + } + if (vlink(p) != null) { + while (vlink(p) != null) { + t = attribute_id(vlink(p)); + if (t > i) + return UNUSED_ATTRIBUTE; + if (t == i) { + p = vlink(p); + break; + } + j++; + p = vlink(p); + } + if (attribute_id(p) != i) + return UNUSED_ATTRIBUTE; + /*tex If we are still here, the attribute exists. */ + p = node_attr(n); + if (attr_list_ref(p) > 1 || p == attr_list_cache) { + halfword q = copy_attribute_list(p); + if (attr_list_ref(p) > 1) { + delete_attribute_ref(node_attr(n)); + } + attr_list_ref(q) = 1; + node_attr(n) = q; + } + p = vlink(node_attr(n)); + while (j-- > 0) + p = vlink(p); + t = attribute_value(p); + if (val == UNUSED_ATTRIBUTE || t == val) { + attribute_value(p) = UNUSED_ATTRIBUTE; + } + return t; + } else { + normal_error("nodes","trying to unset an attribute fails"); + return null; + } +} + +int has_attribute(halfword n, int i, int val) +{ + register halfword p; + if (!nodetype_has_attributes(type(n))) + return UNUSED_ATTRIBUTE; + p = node_attr(n); + if (p == null || vlink(p) == null) + return UNUSED_ATTRIBUTE; + p = vlink(p); + while (p != null) { + if (attribute_id(p) == i) { + int ret = attribute_value(p); + if (val == UNUSED_ATTRIBUTE || val == ret) + return ret; + return UNUSED_ATTRIBUTE; + } else if (attribute_id(p) > i) { + return UNUSED_ATTRIBUTE; + } + p = vlink(p); + } + return UNUSED_ATTRIBUTE; +} + +void print_short_node_contents(halfword p) +{ + switch (type(p)) { + case hlist_node: + case vlist_node: + case ins_node: + case whatsit_node: + case mark_node: + case adjust_node: + case unset_node: + print_char('['); + print_char(']'); + break; + case rule_node: + print_char('|'); + break; + case glue_node: + if (! glue_is_zero(p)) + print_char(' '); + break; + case math_node: + print_char('$'); + break; + case disc_node: + short_display(vlink(pre_break(p))); + short_display(vlink(post_break(p))); + break; + } +} + +static void show_pdftex_whatsit_rule_spec(int p) +{ + tprint("("); + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); +} + +/*tex + + Each new type of node that appears in our data structure must be capable of + being displayed, copied, destroyed, and so on. The routines that we need for + write-oriented whatsits are somewhat like those for mark nodes; other + extensions might, of course, involve more subtlety here. + +*/ + +static void print_write_whatsit(const char *s, pointer p) +{ + tprint_esc(s); + if (write_stream(p) < 16) + print_int(write_stream(p)); + else if (write_stream(p) == 16) + print_char('*'); + else + print_char('-'); +} + +static void show_node_wrapup_core(int p) +{ + switch (subtype(p)) { + case open_node: + print_write_whatsit("openout", p); + print_char('='); + print_file_name(open_name(p), open_area(p), open_ext(p)); + break; + case write_node: + print_write_whatsit("write", p); + print_mark(write_tokens(p)); + break; + case close_node: + print_write_whatsit("closeout", p); + break; + case special_node: + tprint_esc("special"); + print_mark(write_tokens(p)); + break; + case late_lua_node: + show_late_lua(p); + break; + case save_pos_node: + tprint_esc("savepos"); + break; + case user_defined_node: + tprint_esc("whatsit"); + print_int(user_node_id(p)); + print_char('='); + switch (user_node_type(p)) { + case 'a': + tprint("<>"); + break; + case 'n': + tprint("["); + show_node_list(user_node_value(p)); + tprint("]"); + break; + case 's': + print_char('"'); + print(user_node_value(p)); + print_char('"'); + break; + case 't': + print_mark(user_node_value(p)); + break; + default: /* only 'd' */ + print_int(user_node_value(p)); + break; + } + break; + } +} + +void show_node_wrapup_dvi(int p) +{ +} + +void show_node_wrapup_pdf(int p) +{ + switch (subtype(p)) { + case pdf_literal_node: + show_pdf_literal(p); + break; + case pdf_colorstack_node: + tprint_esc("pdfcolorstack "); + print_int(pdf_colorstack_stack(p)); + switch (pdf_colorstack_cmd(p)) { + case colorstack_set: + tprint(" set "); + break; + case colorstack_push: + tprint(" push "); + break; + case colorstack_pop: + tprint(" pop"); + break; + case colorstack_current: + tprint(" current"); + break; + default: + confusion("colorstack"); + break; + } + if (pdf_colorstack_cmd(p) <= colorstack_data) + print_mark(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + tprint_esc("pdfsetmatrix"); + print_mark(pdf_setmatrix_data(p)); + break; + case pdf_save_node: + tprint_esc("pdfsave"); + break; + case pdf_restore_node: + tprint_esc("pdfrestore"); + break; + case pdf_refobj_node: + tprint_esc("pdfrefobj"); + if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) { + if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { + tprint(" attr"); + lua_rawgeti(Luas, LUA_REGISTRYINDEX, obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p))); + print_char(' '); + tprint((const char *) lua_tostring(Luas, -1)); + lua_pop(Luas, 1); + } + tprint(" stream"); + } + if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p))) + tprint(" file"); + if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { + lua_rawgeti(Luas, LUA_REGISTRYINDEX, obj_obj_data(static_pdf, pdf_obj_objnum(p))); + print_char(' '); + tprint((const char *) lua_tostring(Luas, -1)); + lua_pop(Luas, 1); + } + break; + case pdf_annot_node: + tprint_esc("pdfannot"); + show_pdftex_whatsit_rule_spec(p); + print_mark(pdf_annot_data(p)); + break; + case pdf_start_link_node: + tprint_esc("pdfstartlink"); + show_pdftex_whatsit_rule_spec(p); + if (pdf_link_attr(p) != null) { + tprint(" attr"); + print_mark(pdf_link_attr(p)); + } + tprint(" action"); + if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) { + tprint(" user"); + print_mark(pdf_action_tokens(pdf_link_action(p))); + return; + } + if (pdf_action_file(pdf_link_action(p)) != null) { + tprint(" file"); + print_mark(pdf_action_file(pdf_link_action(p))); + } + switch (pdf_action_type(pdf_link_action(p))) { + case pdf_action_goto: + if (pdf_action_named_id(pdf_link_action(p)) > 0) { + tprint(" goto name"); + print_mark(pdf_action_id(pdf_link_action(p))); + } else { + tprint(" goto num"); + print_int(pdf_action_id(pdf_link_action(p))); + } + break; + case pdf_action_page: + tprint(" page"); + print_int(pdf_action_id(pdf_link_action(p))); + print_mark(pdf_action_tokens(pdf_link_action(p))); + break; + case pdf_action_thread: + if (pdf_action_named_id(pdf_link_action(p)) > 0) { + tprint(" thread name"); + print_mark(pdf_action_id(pdf_link_action(p))); + } else { + tprint(" thread num"); + print_int(pdf_action_id(pdf_link_action(p))); + } + break; + default: + normal_error("pdf backend", "unknown action type for link"); + break; + } + break; + case pdf_end_link_node: + tprint_esc("pdfendlink"); + break; + case pdf_dest_node: + tprint_esc("pdfdest"); + if (pdf_dest_named_id(p) > 0) { + tprint(" name"); + print_mark(pdf_dest_id(p)); + } else { + tprint(" num"); + print_int(pdf_dest_id(p)); + } + print_char(' '); + switch (pdf_dest_type(p)) { + case pdf_dest_xyz: + tprint("xyz"); + if (pdf_dest_xyz_zoom(p) != null) { + tprint(" zoom"); + print_int(pdf_dest_xyz_zoom(p)); + } + break; + case pdf_dest_fitbh: + tprint("fitbh"); + break; + case pdf_dest_fitbv: + tprint("fitbv"); + break; + case pdf_dest_fitb: + tprint("fitb"); + break; + case pdf_dest_fith: + tprint("fith"); + break; + case pdf_dest_fitv: + tprint("fitv"); + break; + case pdf_dest_fitr: + tprint("fitr"); + show_pdftex_whatsit_rule_spec(p); + break; + case pdf_dest_fit: + tprint("fit"); + break; + default: + tprint("unknown!"); + break; + } + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (subtype(p) == pdf_thread_node) + tprint_esc("pdfthread"); + else + tprint_esc("pdfstartthread"); + tprint("("); + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); + if (pdf_thread_attr(p) != null) { + tprint(" attr"); + print_mark(pdf_thread_attr(p)); + } + if (pdf_thread_named_id(p) > 0) { + tprint(" name"); + print_mark(pdf_thread_id(p)); + } else { + tprint(" num"); + print_int(pdf_thread_id(p)); + } + break; + case pdf_end_thread_node: + tprint_esc("pdfendthread"); + break; + default: + break; + } +} + +/*tex + + Now we are ready for |show_node_list| itself. This procedure has been written + to be ``extra robust'' in the sense that it should not crash or get into a + loop even if the data structures have been messed up by bugs in the rest of + the program. You can safely call its parent routine |show_box(p)| for + arbitrary values of |p| when you are debugging \TeX. However, in the presence + of bad data, the procedure may fetch a |memory_word| whose variant is + different from the way it was stored; for example, it might try to read + |mem[p].hh| when |mem[p]| contains a scaled integer, if |p| is a pointer that + has been clobbered or chosen at random. + +*/ + +#define node_list_display(A) do { \ + append_char('.'); \ + show_node_list(A); \ + flush_char(); \ +} while (0) + +#define node_list_display_x(A,B) do { \ + if ((B) != null) { \ + append_char('.'); \ + append_char(A); \ + append_char(' '); \ + show_node_list(B); \ + flush_char(); \ + flush_char(); \ + flush_char(); \ + } \ +} while (0) + +/*tex Print a node list symbolically: */ + +void show_node_list(int p) +{ + /*tex The number of items already printed at this level: */ + int n = 0; + halfword w; + /*tex A glue ratio, as a floating point number: */ + real g; + if ((int) cur_length > depth_threshold) { + if (p > null) { + /*tex Indicate that there's been some truncation. */ + tprint(" []"); + } + return; + } + while (p != null) { + print_ln(); + print_current_string(); + /*tex Display the nesting history. */ + if (tracing_online_par < -2) + print_int(p); + incr(n); + if (n > breadth_max) { + /*tex Time to stop. */ + tprint("etc."); + return; + } + /*tex Display node |p|. */ + if (is_char_node(p)) { + print_font_and_char(p); + if (is_ligature(p)) { + /*tex Display ligature |p|. */ + tprint(" (ligature "); + if (is_leftboundary(p)) + print_char('|'); + font_in_short_display = font(p); + short_display(lig_ptr(p)); + if (is_rightboundary(p)) + print_char('|'); + print_char(')'); + } + } else { + switch (type(p)) { + case hlist_node: + case vlist_node: + case unset_node: + /*tex Display box |p|. */ + if (type(p) == hlist_node) + tprint_esc("h"); + else if (type(p) == vlist_node) + tprint_esc("v"); + else + tprint_esc("unset"); + tprint("box("); + print_scaled(height(p)); + print_char('+'); + print_scaled(depth(p)); + tprint(")x"); + print_scaled(width(p)); + if (type(p) == unset_node) { + /*tex Display special fields of the unset node |p|. */ + if (span_count(p) != min_quarterword) { + tprint(" ("); + print_int(span_count(p) + 1); + tprint(" columns)"); + } + if (glue_stretch(p) != 0) { + tprint(", stretch "); + print_glue(glue_stretch(p), glue_order(p), NULL); + } + if (glue_shrink(p) != 0) { + tprint(", shrink "); + print_glue(glue_shrink(p), glue_sign(p), NULL); + } + } else { + /*tex + + Display the value of |glue_set(p)|. The code will + have to change in this place if |glue_ratio| is a + structured type instead of an ordinary |real|. Note + that this routine should avoid arithmetic errors even + if the |glue_set| field holds an arbitrary random + value. The following code assumes that a properly + formed nonzero |real| number has absolute value + $2^{20}$ or more when it is regarded as an integer; + this precaution was adequate to prevent floating + point underflow on the author's computer. + + */ + g = (real) (glue_set(p)); + if ((g != 0.0) && (glue_sign(p) != normal)) { + tprint(", glue set "); + if (glue_sign(p) == shrinking) + tprint("- "); + if (g > 20000.0 || g < -20000.0) { + if (g > 0.0) + print_char('>'); + else + tprint("< -"); + print_glue(20000 * unity, glue_order(p), NULL); + } else { + print_glue(round(unity * g), glue_order(p), NULL); + } + } + if (shift_amount(p) != 0) { + tprint(", shifted "); + print_scaled(shift_amount(p)); + } + tprint(", direction "); + print_dir_par(box_dir(p)); + } + /*tex Recursive call: */ + node_list_display(list_ptr(p)); + break; + case rule_node: + /*tex Display rule |p|. */ + if (subtype(p) == normal_rule) { + tprint_esc("rule("); + } else if (subtype(p) == empty_rule) { + tprint_esc("norule("); + } else if (subtype(p) == user_rule) { + tprint_esc("userrule("); + } else if (subtype(p) == box_rule) { + tprint_esc("box("); + } else if (subtype(p) == image_rule) { + tprint_esc("image("); + } + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); + break; + case ins_node: + /*tex Display insertion |p|. */ + tprint_esc("insert"); + print_int(subtype(p)); + tprint(", natural size "); + print_scaled(height(p)); + tprint("; split("); + print_spec(split_top_ptr(p), NULL); + print_char(','); + print_scaled(depth(p)); + tprint("); float cost "); + print_int(float_cost(p)); + /*tex Recursive call. */ + node_list_display(ins_ptr(p)); + break; + case dir_node: + if (subtype(p) == cancel_dir) { + tprint_esc("enddir"); + } else { + tprint_esc("begindir"); + } + print_char(' '); + print_dir_par(dir_dir(p)); + break; + case local_par_node: + tprint_esc("localpar"); + append_char('.'); + print_ln(); + print_current_string(); + tprint_esc("localinterlinepenalty"); + print_char('='); + print_int(local_pen_inter(p)); + print_ln(); + print_current_string(); + tprint_esc("localbrokenpenalty"); + print_char('='); + print_int(local_pen_broken(p)); + print_ln(); + print_current_string(); + tprint_esc("localleftbox"); + if (local_box_left(p) == null) { + tprint("=null"); + } else { + append_char('.'); + show_node_list(local_box_left(p)); + decr(cur_length); + } + print_ln(); + print_current_string(); + tprint_esc("localrightbox"); + if (local_box_right(p) == null) { + tprint("=null"); + } else { + append_char('.'); + show_node_list(local_box_right(p)); + decr(cur_length); + } + decr(cur_length); + break; + case boundary_node: + if (subtype(p)==0) { + tprint_esc("noboundary"); + } else { + switch (subtype(p)) { + case 1: + tprint_esc("boundary"); + break; + case 2: + tprint_esc("protrusionboundary"); + break; + case 3: + tprint_esc("wordboundary"); + break; + default: + tprint_esc("boundary"); + print_char(':'); + print_int(subtype(p)); + break; + } + print_char('='); + print_int(boundary_value(p)); + } + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + show_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + show_node_wrapup_dvi(p); + } else { + show_node_wrapup_core(p); + } + break; + case glue_node: + /*tex Display glue |p|. */ + if (subtype(p) >= a_leaders) { + /*tex Display leaders |p|. */ + tprint_esc(""); + switch (subtype(p)) { + case a_leaders: + break; + case c_leaders: + print_char('c'); + break; + case x_leaders: + print_char('x'); + break; + case g_leaders: + print_char('g'); + break; + default: + normal_warning("nodes","weird glue leader subtype ignored"); + } + tprint("leaders "); + print_spec(p, NULL); + /*tex Recursive call: */ + node_list_display(leader_ptr(p)); + } else { + tprint_esc("glue"); + if (subtype(p) != normal) { + print_char('('); + if ((subtype(p) - 1) < thin_mu_skip_code) { + print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1)); + } else if (subtype(p) < cond_math_glue) { + print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1)); + } else if (subtype(p) == cond_math_glue) { + tprint_esc("nonscript"); + } else { + tprint_esc("mskip"); + } + print_char(')'); + } + if (subtype(p) != cond_math_glue) { + print_char(' '); + if (subtype(p) < cond_math_glue) + print_spec(p, NULL); + else + print_spec(p, "mu"); + } + } + break; + case margin_kern_node: + tprint_esc("kern"); + print_scaled(width(p)); + if (subtype(p) == left_side) + tprint(" (left margin)"); + else + tprint(" (right margin)"); + break; + case kern_node: + /*tex Display kern |p| */ + if (subtype(p) != mu_glue) { + tprint_esc("kern"); + /*tex + + \starttyping + if (subtype(p) != normal) { + print_char(' '); + } + \stoptyping + + */ + print_scaled(width(p)); + if (subtype(p) == font_kern) + tprint(" (font)"); + else if (subtype(p) == italic_kern) + tprint(" (italic)"); + else if (subtype(p) == accent_kern) + tprint(" (accent)"); + } else { + tprint_esc("mkern"); + print_scaled(width(p)); + tprint("mu"); + } + break; + case math_node: + /*tex Display math node |p|. */ + tprint_esc("math"); + if (subtype(p) == before) + tprint("on"); + else + tprint("off"); + if (!glue_is_zero(p)) { + tprint(", glued "); + print_spec(p, NULL); + } else if (surround(p) != 0) { + tprint(", surrounded "); + print_scaled(surround(p)); + } + break; + case penalty_node: + /*tex Display penalty |p|. */ + tprint_esc("penalty "); + print_int(penalty(p)); + break; + case disc_node: + /*tex + + Display discretionary |p|. The |post_break| list of a + discretionary node is indicated by a prefixed + `\.{\char'174}' instead of the `\..' before the + |pre_break| list. + + We're not compatible anyway so ... + + \starttyping + tprint_esc("discretionary"); + print_int(disc_penalty(p)); + print_char('|'); + if (vlink(no_break(p)) != null) { + tprint(" replacing "); + node_list_display(vlink(no_break(p))); + } + node_list_display(vlink(pre_break(p))); + append_char('|'); + show_node_list(vlink(post_break(p))); + flush_char(); + \stoptyping + + has become: + + */ + tprint_esc("discretionary"); + tprint(" (penalty "); + print_int(disc_penalty(p)); + print_char(')'); + node_list_display_x('<',vlink(pre_break(p))); + node_list_display_x('>',vlink(post_break(p))); + node_list_display_x('=',vlink(no_break(p))); + break; + case mark_node: + /*tex Display mark |p|. */ + tprint_esc("mark"); + if (mark_class(p) != 0) { + print_char('s'); + print_int(mark_class(p)); + } + print_mark(mark_ptr(p)); + break; + case adjust_node: + /*tex Display adjustment |p|. */ + tprint_esc("vadjust"); + if (subtype(p) != 0) + tprint(" pre "); + /*tex Recursive call. */ + node_list_display(adjust_ptr(p)); + break; + case glue_spec_node: + tprint("<glue_spec "); + print_spec(p, NULL); + tprint(">"); + break; + default: + show_math_node(p); + break; + } + } + p = vlink(p); + } +} + +/*tex + + This routine finds the 'base' width of a horizontal box, using the same logic + that \TeX82 used for \.{\\predisplaywidth}. + +*/ + +static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width) +{ + /*tex increment to |v| */ + scaled d; + /*tex calculated |size| */ + scaled w = -max_dimen; + /*tex |w| plus possible glue amount */ + scaled v = initial_width; + while (p != null) { + if (is_char_node(p)) { + d = glyph_width(p); + goto FOUND; + } + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + d = width(p); + goto FOUND; + break; + case margin_kern_node: + d = width(p); + break; + case kern_node: + d = width(p); + break; + case disc_node: + /*tex At the end of the line we should actually take the |pre|. */ + if (no_break(p) != null) { + d = get_actual_box_width(r,vlink_no_break(p),0); + if (d <= -max_dimen || d >= max_dimen) { + d = 0; + } + } else { + d = 0; + } + goto FOUND; + break; + case math_node: + /*tex Begin mathskip code. */ + if (glue_is_zero(p)) { + d = surround(p); + break; + } else { + /*tex Fall through. */ + } + /*tex End mathskip code. */ + case glue_node: + /*tex + + We need to be careful that |w|, |v|, and |d| do not depend on + any |glue_set| values, since such values are subject to + system-dependent rounding. System-dependent numbers are not + allowed to infiltrate parameters like |pre_display_size|, + since \TeX82 is supposed to make the same decisions on all + machines. + + */ + d = width(p); + if (glue_sign(r) == stretching) { + if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0)) + v = max_dimen; + } else if (glue_sign(r) == shrinking) { + if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0)) + v = max_dimen; + } + if (subtype(p) >= a_leaders) + goto FOUND; + break; + default: + d = 0; + break; + } + if (v < max_dimen) + v = v + d; + goto NOT_FOUND; + FOUND: + if (v < max_dimen) { + v = v + d; + w = v; + } else { + w = max_dimen; + break; + } + NOT_FOUND: + p = vlink(p); + } + return w; +} + +pointer actual_box_width(pointer r, scaled base_width) +{ + /*tex + + Often this is the same as: + + \starttyping + return + shift_amount(r) + base_width + + natural_sizes(list_ptr(r),null,(glue_ratio) glue_set(r),glue_sign(r),glue_order(r),box_dir(r)); + \stoptyping + */ + return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width); +} + +halfword tail_of_list(halfword p) +{ + halfword q = p; + while (vlink(q) != null) + q = vlink(q); + return q; +} + +int var_used; + +/*tex + + Attribute lists need two extra globals to increase processing efficiency. + |max_used_attr| limits the test loop that checks for set attributes, and + |attr_list_cache| contains a pointer to an already created attribute list. It + is set to the special value |cache_disabled| when the current value can no + longer be trusted: after an assignment to an attribute register, and after a + group has ended. + +*/ + +/*tex The maximum assigned attribute id: */ + +int max_used_attr; +halfword attr_list_cache; + +/*tex + + From the computer's standpoint, \TeX's chief mission is to create horizontal + and vertical lists. We shall now investigate how the elements of these lists + are represented internally as nodes in the dynamic memory. + + A horizontal or vertical list is linked together by |link| fields in the + first word of each node. Individual nodes represent boxes, glue, penalties, + or special things like discretionary hyphens; because of this variety, some + nodes are longer than others, and we must distinguish different kinds of + nodes. We do this by putting a `|type|' field in the first word, together + with the link and an optional `|subtype|'. + + Character nodes appear only in horizontal lists, never in vertical lists. + + An |hlist_node| stands for a box that was made from a horizontal list. Each + |hlist_node| is seven words long, and contains the following fields (in + addition to the mandatory |type| and |link|, which we shall not mention + explicitly when discussing the other node types): The |height| and |width| + and |depth| are scaled integers denoting the dimensions of the box. There is + also a |shift_amount| field, a scaled integer indicating how much this box + should be lowered (if it appears in a horizontal list), or how much it should + be moved to the right (if it appears in a vertical list). There is a + |list_ptr| field, which points to the beginning of the list from which this + box was fabricated; if |list_ptr| is |null|, the box is empty. Finally, there + are three fields that represent the setting of the glue: |glue_set(p)| is a + word of type |glue_ratio| that represents the proportionality constant for + glue setting; |glue_sign(p)| is |stretching| or |shrinking| or |normal| + depending on whether or not the glue should stretch or shrink or remain + rigid; and |glue_order(p)| specifies the order of infinity to which glue + setting applies (|normal|, |sfi|, |fil|, |fill|, or |filll|). The |subtype| + field is not used. + + The |new_null_box| function returns a pointer to an |hlist_node| in which all + subfields have the values corresponding to `\.{\\hbox\{\}}'. The |subtype| + field is set to |min_quarterword|, since that's the desired |span_count| + value if this |hlist_node| is changed to an |unset_node|. + +*/ + +/*tex Create a new box node. */ + +halfword new_null_box(void) +{ + halfword p = new_node(hlist_node, min_quarterword); + box_dir(p) = text_direction_par; + return p; +} + +/*tex + + A |vlist_node| is like an |hlist_node| in all respects except that it + contains a vertical list. + + A |rule_node| stands for a solid black rectangle; it has |width|, |depth|, + and |height| fields just as in an |hlist_node|. However, if any of these + dimensions is $-2^{30}$, the actual value will be determined by running the + rule up to the boundary of the innermost enclosing box. This is called a + ``running dimension.'' The |width| is never running in an hlist; the |height| + and |depth| are never running in a~vlist. + + A new rule node is delivered by the |new_rule| function. It makes all the + dimensions ``running,'' so you have to change the ones that are not allowed + to run. + +*/ + +halfword new_rule(int s) +{ + halfword p = new_node(rule_node,s); + return p; +} + +/*tex + + Insertions are represented by |ins_node| records, where the |subtype| + indicates the corresponding box number. For example, `\.{\\insert 250}' leads + to an |ins_node| whose |subtype| is |250+min_quarterword|. The |height| field + of an |ins_node| is slightly misnamed; it actually holds the natural height + plus depth of the vertical list being inserted. The |depth| field holds the + |split_max_depth| to be used in case this insertion is split, and the + |split_top_ptr| points to the corresponding |split_top_skip|. The + |float_cost| field holds the |floating_penalty| that will be used if this + insertion floats to a subsequent page after a split insertion of the same + class. There is one more field, the |ins_ptr|, which points to the beginning + of the vlist for the insertion. + + A |mark_node| has a |mark_ptr| field that points to the reference count of a + token list that contains the user's \.{\\mark} text. In addition there is a + |mark_class| field that contains the mark class. + + An |adjust_node|, which occurs only in horizontal lists, specifies material + that will be moved out into the surrounding vertical list; i.e., it is used + to implement \TeX's `\.{\\vadjust}' operation. The |adjust_ptr| field points + to the vlist containing this material. + + A |glyph_node|, which occurs only in horizontal lists, specifies a glyph in a + particular font, along with its attribute list. Older versions of \TeX\ could + use token memory for characters, because the font,char combination would fit + in a single word (both values were required to be strictly less than + $2^{16}$). In LuaTeX, room is needed for characters that are larger than + that, as well as a pointer to a potential attribute list, and the two + displacement values. + + In turn, that made the node so large that it made sense to merge ligature + glyphs as well, as that requires only one extra pointer. A few extra classes + of glyph nodes will be introduced later. The unification of all those types + makes it easier to manipulate lists of glyphs. The subtype differentiates + various glyph kinds. + + First, here is a function that returns a pointer to a glyph node for a given + glyph in a given font. If that glyph doesn't exist, |null| is returned + instead. Nodes of this subtype are directly created only for accents and + their base (through |make_accent|), and math nucleus items (in the conversion + from |mlist| to |hlist|). + +*/ + +halfword new_glyph(int f, int c) +{ + halfword p = null; + if ((f == 0) || (char_exists(f, c))) { + p = new_glyph_node(); + set_to_glyph(p); + font(p) = f; + character(p) = c; + } + return p; +} + +/*tex + + A subset of the glyphs nodes represent ligatures: characters fabricated from + the interaction of two or more actual characters. The characters that + generated the ligature have not been forgotten, since they are needed for + diagnostic messages; the |lig_ptr| field points to a linked list of character + nodes for all original characters that have been deleted. (This list might be + empty if the characters that generated the ligature were retained in other + nodes.) + + The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if the + original source of the ligature included implicit left and/or right + boundaries. These nodes are created by the C function |new_ligkern|. + + A third general type of glyphs could be called a character, as it only + appears in lists that are not yet processed by the ligaturing and kerning + steps of the program. + + |main_control| inserts these, and they are later converted to + |subtype_normal| by |new_ligkern|. + +*/ + +quarterword norm_min(int h) +{ + if (h <= 0) + return 1; + else if (h >= 255) + return 255; + else + return (quarterword) h; +} + +halfword new_char(int f, int c) +{ + halfword p; + p = new_glyph_node(); + set_to_character(p); + font(p) = f; + character(p) = c; + lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par); + return p; +} + +/*tex + + Left and right ghost glyph nodes are the result of \.{\\leftghost} and + \.{\\rightghost}, respectively. They are going to be removed by + |new_ligkern|, at the end of which they are no longer needed. + + Here are a few handy helpers used by the list output routines. + +*/ + +scaled glyph_width(halfword p) +{ + scaled w = char_width(font(p), character(p)); + return w; +} + +scaled glyph_height(halfword p) +{ + scaled w = char_height(font(p), character(p)) + y_displace(p); + if (w < 0) + w = 0; + return w; +} + +scaled glyph_depth(halfword p) +{ + scaled w = char_depth(font(p), character(p)); + if (y_displace(p) > 0) + w = w - y_displace(p); + if (w < 0) + w = 0; + return w; +} + +/*tex + + A |disc_node|, which occurs only in horizontal lists, specifies a + ``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the + text that starts at |pre_break(p)| will precede the break, the text that + starts at |post_break(p)| will follow the break, and text that appears in + |no_break(p)| nodes will be ignored. For example, an ordinary discretionary + hyphen, indicated by `\.{\\-}', yields a |disc_node| with |pre_break| + pointing to a |char_node| containing a hyphen, |post_break=null|, and + |no_break=null|. + + If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for + this break. Otherwise the |hyphen_penalty| will be charged. The texts will + actually be substituted into the list by the line-breaking algorithm if it + decides to make the break, and the discretionary node will disappear at that + time; thus, the output routine sees only discretionaries that were not + chosen. + +*/ + +halfword new_disc(void) +{ + halfword p = new_node(disc_node, 0); + disc_penalty(p) = hyphen_penalty_par; + return p; +} + +/* + + A |whatsit_node| is a wild card reserved for extensions to \TeX. The + |subtype| field in its first word says what `\\{whatsit}' it is, and + implicitly determines the node size (which must be 2 or more) and the format + of the remaining words. When a |whatsit_node| is encountered in a list, + special actions are invoked; knowledgeable people who are careful not to mess + up the rest of \TeX\ are able to make \TeX\ do new things by adding code at + the end of the program. For example, there might be a `\TeX nicolor' + extension to specify different colors of ink, and the whatsit node might + contain the desired parameters. + + The present implementation of \TeX\ treats the features associated with + `\.{\\write}' and `\.{\\special}' as if they were extensions, in order to + illustrate how such routines might be coded. We shall defer further + discussion of extensions until the end of this program. + + A |math_node|, which occurs only in horizontal lists, appears before and + after mathematical formulas. The |subtype| field is |before| before the + formula and |after| after it. There is a |surround| field, which represents + the amount of surrounding space inserted by \.{\\mathsurround}. + +*/ + +halfword new_math(scaled w, int s) +{ + halfword p = new_node(math_node, s); + surround(p) = w; + return p; +} + +/*tex + + \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, |rule_node|, + |ins_node|, |mark_node|, |adjust_node|, |disc_node|, |whatsit_node|, and + |math_node| are at the low end of the type codes, by permitting a break at + glue in a list if and only if the |type| of the previous node is less than + |math_node|. Furthermore, a node is discarded after a break if its type is + |math_node| or~more. + + A |glue_node| represents glue in a list. However, it is really only a pointer + to a separate glue specification, since \TeX\ makes use of the fact that many + essentially identical nodes of glue are usually present. If |p| points to a + |glue_node|, |glue_ptr(p)| points to another packet of words that specify the + stretch and shrink components, etc. + + Glue nodes also serve to represent leaders; the |subtype| is used to + distinguish between ordinary glue (which is called |normal|) and the three + kinds of leaders (which are called |a_leaders|, |c_leaders|, and + |x_leaders|). The |leader_ptr| field points to a rule node or to a box node + containing the leaders; it is set to |null| in ordinary glue nodes. + + Many kinds of glue are computed from \TeX's ``skip'' parameters, and it is + helpful to know which parameter has led to a particular glue node. Therefore + the |subtype| is set to indicate the source of glue, whenever it originated + as a parameter. We will be defining symbolic names for the parameter numbers + later (e.g., |line_skip_code=0|, |baseline_skip_code=1|, etc.); it suffices + for now to say that the |subtype| of parametric glue will be the same as the + parameter number, plus~one. + + In math formulas there are two more possibilities for the |subtype| in a glue + node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu} + instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}' + feature that cancels the glue node immediately following if it appears in a + subscript. + + A glue specification has a halfword reference count in its first word, + representing |null| plus the number of glue nodes that point to it (less + one). Note that the reference count appears in the same position as the + |link| field in list nodes; this is the field that is initialized to |null| + when a node is allocated, and it is also the field that is flagged by + |empty_flag| in empty nodes. + + Glue specifications also contain three |scaled| fields, for the |width|, + |stretch|, and |shrink| dimensions. Finally, there are two one-byte fields + called |stretch_order| and |shrink_order|; these contain the orders of + infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|) corresponding to the + stretch and shrink values. + + Here is a function that returns a pointer to a copy of a glue spec. The + reference count in the copy is |null|, because there is assumed to be exactly + one reference to the new specification. + +*/ + +halfword new_spec(halfword q) +{ + if (q == null) { + return copy_node(zero_glue); + } else if (type(q) == glue_spec_node) { + return copy_node(q); + } else if (type(q) == glue_node) { + halfword p = copy_node(zero_glue); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; + } else { + /*tex Alternatively we can issue a warning. */ + return copy_node(zero_glue); + } +} + +/*tex + + And here's a function that creates a glue node for a given parameter + identified by its code number; for example, |new_param_glue(line_skip_code)| + returns a pointer to a glue node for the current \.{\\lineskip}. + +*/ + +halfword new_param_glue(int n) +{ + halfword p = new_node(glue_node, n + 1); + halfword q = glue_par(n); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; +} + +/*tex + + Glue nodes that are more or less anonymous are created by |new_glue|, whose + argument points to a glue specification. + +*/ + +halfword new_glue(halfword q) +{ + halfword p = new_node(glue_node, normal); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; +} + +/*tex + + Still another subroutine is needed: This one is sort of a combination of + |new_param_glue| and |new_glue|. It creates a glue node for one of the + current glue parameters, but it makes a fresh copy of the glue specification, + since that specification will probably be subject to change, while the + parameter will stay put. + + The global variable |temp_ptr| is set to the address of the new spec. + +*/ + +halfword new_skip_param(int n) +{ + halfword p = new_node(glue_node, n + 1); + halfword q = glue_par(n); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; +} + +/*tex + + A |kern_node| has a |width| field to specify a (normally negative) amount of + spacing. This spacing correction appears in horizontal lists between letters + like A and V when the font designer said that it looks better to move them + closer together or further apart. A kern node can also appear in a vertical + list, when its `|width|' denotes additional spacing in the vertical + direction. The |subtype| is either |normal| (for kerns inserted from font + information or math mode calculations) or |explicit| (for kerns inserted from + \.{\\kern} and \.{\\/} commands) or |acc_kern| (for kerns inserted from + non-math accents) or |mu_glue| (for kerns inserted from \.{\\mkern} + specifications in math formulas). + + The |new_kern| function creates a kern node having a given width. + +*/ + +halfword new_kern(scaled w) +{ + halfword p = new_node(kern_node, normal); + width(p) = w; + return p; +} + +/*tex + + A |penalty_node| specifies the penalty associated with line or page breaking, + in its |penalty| field. This field is a fullword integer, but the full range + of integer values is not used: Any penalty |>=10000| is treated as infinity, + and no break will be allowed for such high values. Similarly, any penalty + |<=-10000| is treated as negative infinity, and a break will be forced. + + Anyone who has been reading the last few sections of the program will be able + to guess what comes next. + +*/ + +halfword new_penalty(int m, int s) +{ + halfword p = new_node(penalty_node, 0); + penalty(p) = m; + subtype(p) = s; + return p; +} + +/*tex + + You might think that we have introduced enough node types by now. Well, + almost, but there is one more: An |unset_node| has nearly the same format as + an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} or + \.{\\valign} that are not yet in their final form, since the box dimensions + are their ``natural'' sizes before any glue adjustment has been made. The + |glue_set| word is not present; instead, we have a |glue_stretch| field, + which contains the total stretch of order |glue_order| that is present in the + hlist or vlist being boxed. Similarly, the |shift_amount| field is replaced + by a |glue_shrink| field, containing the total shrink of order |glue_sign| + that is present. The |subtype| field is called |span_count|; an unset box + typically contains the data for |qo(span_count)+1| columns. Unset nodes will + be changed to box nodes when alignment is completed. + + In fact, there are still more types coming. When we get to math formula + processing we will see that a |style_node| has |type=14|; and a number of + larger type codes will also be defined, for use in math mode only. + + Warning: If any changes are made to these data structure layouts, such as + changing any of the node sizes or even reordering the words of nodes, the + |copy_node_list| procedure and the memory initialization code below may have + to be changed. Such potentially dangerous parts of the program are listed in + the index under `data structure assumptions'. However, other references to + the nodes are made symbolically in terms of the \.{WEB} macro definitions + above, so that format changes will leave \TeX's other algorithms intact. + +*/ + +halfword make_local_par_node(int mode) +{ + int callback_id; + halfword q; + halfword p = new_node(local_par_node,0); + local_pen_inter(p) = local_inter_line_penalty_par; + local_pen_broken(p) = local_broken_penalty_par; + if (local_left_box_par != null) { + q = copy_node_list(local_left_box_par); + local_box_left(p) = q; + local_box_left_width(p) = width(local_left_box_par); + } + if (local_right_box_par != null) { + q = copy_node_list(local_right_box_par); + local_box_right(p) = q; + local_box_right_width(p) = width(local_right_box_par); + } + local_par_dir(p) = par_direction_par; + /*tex Callback with node passed: */ + callback_id = callback_defined(insert_local_par_callback); + if (callback_id > 0) { + /*tex Todo: use general mechanism/wrapper. */ + int sfix = lua_gettop(Luas); + if (!get_callback(Luas, callback_id)) { + lua_settop(Luas, sfix); + } else { + int i; + nodelist_to_lua(Luas, p); + lua_push_local_par_mode(Luas,mode) + /*tex 2 arg, 0 result */ + i = lua_pcall(Luas, 2, 0, 0); + if (i != 0) { + lua_gc(Luas, LUA_GCCOLLECT, 0); + Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + } + lua_settop(Luas, sfix); + } + } + return p; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.w b/Build/source/texk/web2c/luatexdir/tex/texnodes.w deleted file mode 100644 index 0c8bdb83726..00000000000 --- a/Build/source/texk/web2c/luatexdir/tex/texnodes.w +++ /dev/null @@ -1,3910 +0,0 @@ -% texnodes.w -% -% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - -#include "ptexlib.h" -#include "lua/luatex-api.h" - -/* we can consider less mode sizes: 2 4 6 8 */ - -@ -This module started out using NDEBUG to trigger checking invalid node usage, -something that is needed because users can mess up nodes in Lua. At some point -that code was always enabled so it is now always on but still can be recognized -as additional code. And as the performance hit is close to zero so disabling -makes no sense, not even to make it configureable. There is a little more memory -used but that is neglectable compared to other memory usage. - -@c -#define MAX_CHAIN_SIZE 13 /* why not a bit larger */ -#define CHECK_NODE_USAGE 1 /* this triggers checking */ - -memory_word *volatile varmem = NULL; - -#ifdef CHECK_NODE_USAGE - char *varmem_sizes = NULL; -#endif - -halfword var_mem_max = 0; -halfword rover = 0; - -halfword free_chain[MAX_CHAIN_SIZE] = { null }; - -static int my_prealloc = 0; - -int fix_node_lists = 1; /* used in font and lang */ - -halfword slow_get_node(int s); /* defined below */ - -#define fake_node 100 -#define fake_node_size 2 -#define fake_node_name "fake" - -#define variable_node_size 2 - -/* core nodes */ - -const char *node_fields_list[] = { - "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign", - "glue_set", "head", NULL -}; -const char *node_fields_rule[] = { - "attr", "width", "depth", "height", "dir", "index", NULL -}; -const char *node_fields_insert[] = { - "attr", "cost", "depth", "height", "spec", "head", NULL -}; -const char *node_fields_mark[] = { - "attr", "class", "mark", NULL -}; -const char *node_fields_adjust[] = { - "attr", "head", NULL -}; -const char *node_fields_disc[] = { - "attr", "pre", "post", "replace", "penalty", NULL -}; -const char *node_fields_math[] = { - "attr", "surround", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_glue[] = { - "attr", "leader", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_kern[] = { - "attr", "kern", "expansion_factor", NULL -}; -const char *node_fields_penalty[] = { - "attr", "penalty", NULL -}; -const char *node_fields_unset[] = { - "attr", "width", "depth", "height", "dir", "shrink", "glue_order", - "glue_sign", "stretch", "span", "head", NULL -}; -const char *node_fields_margin_kern[] = { - "attr", "width", "glyph", NULL -}; -const char *node_fields_glyph[] = { - "attr", "char", "font", "lang", "left", "right", "uchyph", "components", - "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL -}; -const char *node_fields_inserting[] = { - "height", "last_ins_ptr", "best_ins_ptr", - "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_splitup[] = { - "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL -}; -const char *node_fields_attribute[] = { - "number", "value", NULL -}; -const char *node_fields_glue_spec[] = { - "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_attribute_list[] = { - NULL -}; -const char *node_fields_local_par[] = { - "attr", "pen_inter", "pen_broken", "dir", "box_left", "box_left_width", - "box_right", "box_right_width", NULL -}; -const char *node_fields_dir[] = { - "attr", "dir", "level", NULL -}; -const char *node_fields_boundary[] = { - "attr", "value", NULL -}; - -/* math nodes */ - -const char *node_fields_noad[] = { - "attr", "nucleus", "sub", "sup", NULL -}; - -const char *node_fields_style[] = { - "attr", "style", NULL -}; -const char *node_fields_choice[] = { - "attr", "display", "text", "script", "scriptscript", NULL -}; -const char *node_fields_radical[] = { - "attr", "nucleus", "sub", "sup", "left", "degree", "width", "options", NULL -}; -const char *node_fields_fraction[] = { - "attr", "width", "num", "denom", "left", "right", "middle", "options", NULL -}; -const char *node_fields_accent[] = { - "attr", "nucleus", "sub", "sup", "accent", "bot_accent", "top_accent", - "overlay_accent", "fraction", NULL -}; -const char *node_fields_fence[] = { - "attr", "delim", "italic", "height", "depth", "options", "class", NULL -}; -const char *node_fields_math_char[] = { - "attr", "fam", "char", NULL -}; -const char *node_fields_sub_box[] = { - "attr", "head", NULL -}; -const char *node_fields_sub_mlist[] = { - "attr", "head", NULL -}; -const char *node_fields_math_text_char[] = { - "attr", "fam", "char", NULL -}; -const char *node_fields_delim[] = { - "attr", "small_fam", "small_char", "large_fam", "large_char", NULL -}; - -/* whatsit nodes */ - -const char *node_fields_whatsit_open[] = { - "attr", "stream", "name", "area", "ext", NULL -}; -const char *node_fields_whatsit_write[] = { - "attr", "stream", "data", NULL -}; -const char *node_fields_whatsit_close[] = { - "attr", "stream", NULL -}; -const char *node_fields_whatsit_special[] = { - "attr", "data", NULL -}; -const char *node_fields_whatsit_save_pos[] = { - "attr", NULL -}; -const char *node_fields_whatsit_late_lua[] = { - "attr", "reg", "data", "name", "string", NULL -}; -const char *node_fields_whatsit_user_defined[] = { - "attr", "user_id", "type", "value", NULL -}; - -/* pdf backend whatsit nodes */ - -const char *node_fields_whatsit_pdf_literal[] = { - "attr", "mode", "data", NULL -}; -const char *node_fields_whatsit_pdf_refobj[] = { - "attr", "objnum", NULL -}; -const char *node_fields_whatsit_pdf_annot[] = { - "attr", "width", "depth", "height", "objnum", "data", NULL -}; -const char *node_fields_whatsit_pdf_start_link[] = { - "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL -}; -const char *node_fields_whatsit_pdf_end_link[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_dest[] = { - "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type", - "xyz_zoom", "objnum", NULL -}; -const char *node_fields_whatsit_pdf_action[] = { - "action_type", "named_id", "action_id", "file", "new_window", "data", NULL -}; -const char *node_fields_whatsit_pdf_thread[] = { - "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL -}; -const char *node_fields_whatsit_pdf_start_thread[] = { - "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL -}; -const char *node_fields_whatsit_pdf_end_thread[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_colorstack[] = { - "attr", "stack", "cmd", "data", NULL -}; -const char *node_fields_whatsit_pdf_setmatrix[] = { - "attr", "data", NULL -}; -const char *node_fields_whatsit_pdf_save[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_restore[] = { - "attr", NULL -}; - -/* subtypes */ - -const char *node_subtypes_glue[] = { - "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip", - "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip", - "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip", - "mathskip", "thinmuskip", "medmuskip", "thickmuskip", NULL -}; -const char *node_subtypes_mathglue[] = { /* 98+ */ - "conditionalmathskip", "muglue", NULL -}; -const char *node_subtypes_leader[] = { /* 100+ */ - "leaders", "cleaders", "xleaders", "gleaders", NULL -}; -const char *node_subtypes_fill[] = { - "stretch", "fi", "fil", "fill", "filll", NULL -}; -const char *node_subtypes_boundary[] = { - "cancel", "user", "protrusion", "word", NULL -}; -const char *node_subtypes_penalty[] = { - "userpenalty", "linebreakpenalty", "linepenalty", "wordpenalty", "finalpenalty", - "noadpenalty", "beforedisplaypenalty", "afterdisplaypenalty", "equationnumberpenalty", NULL -}; -const char *node_subtypes_kern[] = { - "fontkern", "userkern", "accentkern", "italiccorrection", NULL -}; -const char *node_subtypes_rule[] = { - "normal", "box", "image", "empty", "user", "over", "under", "fraction", "radical", NULL -}; -const char *node_subtypes_glyph[] = { - "character", "glyph", "ligature", "ghost", "left", "right", NULL -}; -const char *node_subtypes_disc[] = { - "discretionary", "explicit", "automatic", "regular", "first", "second", NULL -}; -const char *node_subtypes_marginkern[] = { - "left", "right", NULL -}; -const char *node_subtypes_list[] = { - "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL -}; -const char *node_subtypes_adjust[] = { - "normal", "pre", NULL -}; -const char *node_subtypes_math[] = { - "beginmath", "endmath", NULL -}; -const char *node_subtypes_noad[] = { - "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close", - "punct", "inner", "under", "over", "vcenter", NULL -}; -const char *node_subtypes_radical[] = { - "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder", - "udelimiterover", NULL -}; -const char *node_subtypes_accent[] = { - "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL, -}; -const char *node_subtypes_fence[] = { - "unset", "left", "middle", "right", NULL -}; - -node_info node_data[] = { /* the last entry in a row is the etex number */ - { hlist_node, box_node_size, node_fields_list, "hlist", 1 }, - { vlist_node, box_node_size, node_fields_list, "vlist", 2 }, - { rule_node, rule_node_size, node_fields_rule, "rule", 3 }, - { ins_node, ins_node_size, node_fields_insert, "ins", 4 }, - { mark_node, mark_node_size, node_fields_mark, "mark", 5 }, - { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 }, - { boundary_node, boundary_node_size, node_fields_boundary, "boundary", -1 }, - { disc_node, disc_node_size, node_fields_disc, "disc", 8 }, - { whatsit_node, -1, NULL, "whatsit", 9 }, - { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 }, - { dir_node, dir_node_size, node_fields_dir, "dir", -1 }, - { math_node, math_node_size, node_fields_math, "math", 10 }, - { glue_node, glue_node_size, node_fields_glue, "glue", 11 }, - { kern_node, kern_node_size, node_fields_kern, "kern", 12 }, - { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 }, - { unset_node, box_node_size, node_fields_unset, "unset", 14 }, - { style_node, style_node_size, node_fields_style, "style", 15 }, - { choice_node, style_node_size, node_fields_choice, "choice", 15 }, - { simple_noad, noad_size, node_fields_noad, "noad", 15 }, - { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 }, - { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 }, - { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 }, - { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 }, - { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 }, - { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 }, - { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 }, - { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 }, - { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 }, - { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 }, - { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 }, - { align_record_node, box_node_size, NULL, "align_record", -1 }, - { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 }, - { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 }, - { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 }, - { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 }, - { expr_node, expr_node_size, NULL, "expr_stack", -1 }, - { nesting_node, nesting_node_size, NULL, "nested_list", -1 }, - { span_node, span_node_size, NULL, "span", -1 }, - { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 }, - { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 }, - { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 }, - { temp_node, temp_node_size, NULL, "temp", -1 }, - { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 }, - { movement_node, movement_node_size, NULL, "movement_stack", -1 }, - { if_node, if_node_size, NULL, "if_stack", -1 }, - { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 }, - { hyphenated_node, active_node_size, NULL, "hyphenated", -1 }, - { delta_node, delta_node_size, NULL, "delta", -1 }, - { passive_node, passive_node_size, NULL, "passive", -1 }, - { shape_node, variable_node_size, NULL, "shape", -1 }, - { -1, -1, NULL, NULL, -1 }, -}; - -const char *node_subtypes_pdf_destination[] = { - "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL -}; -const char *node_subtypes_pdf_literal[] = { - "origin", "page", "direct", NULL -}; - -node_info whatsit_node_data[] = { - { open_node, open_node_size, node_fields_whatsit_open, "open", -1 }, - { write_node, write_node_size, node_fields_whatsit_write, "write", -1 }, - { close_node, close_node_size, node_fields_whatsit_close, "close", -1 }, - { special_node, special_node_size, node_fields_whatsit_special, "special", -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 }, - { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 }, - { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - /* here starts the dvi backend section, todo: a separate list */ - /* nothing for dvi */ - /* here starts the pdf backend section, todo: a separate list */ - { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 }, - { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 }, - { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 }, - { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 }, - { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 }, - { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 }, - { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 }, - { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 }, - { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 }, - { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 }, - { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 }, - { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 }, - { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 }, - { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 }, - { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 }, - { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 }, - /* done */ - { -1, -1, NULL, NULL, -1 }, -}; - -#define last_whatsit_node pdf_restore_node - -@ -When we copy a node list, there are several possibilities: we do the same as a new node, -we copy the entry to the table in properties (a reference), we do a deep copy of a table -in the properties, we create a new table and give it the original one as a metatable. -After some experiments (that also included timing) with these scenarios I decided that a -deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable -variant were both ok, although the second ons is slower. The most important aspect to keep -in mind is that references to other nodes in properties no longer can be valid for that -copy. We could use two tables (one unique and one shared) or metatables but that only -complicates matters. - -When defining a new node, we could already allocate a table but it is rather easy to do -that at the lua end e.g. using a metatable __index method. That way it is under macro -package control. - -When deleting a node, we could keep the slot (e.g. setting it to false) but it could make -memory consumption raise unneeded when we have temporary large node lists and after that -only small lists. - -So, in the end this is what we ended up with. For the record, I also experimented with the -following: - -- copy attributes to the properties so that we have fast access at the lua end: in the end - the overhead is not compensated by speed and convenience, in fact, attributes are not - that slow when it comes to accessing them - -- a bitset in the node but again the gain compared to attributes is neglectable and it also - demands a pretty string agreement over what bit represents what, and this is unlikely to - succeed in the tex community (I could use it for font handling, which is cross package, - but decided that it doesn't pay off - -In case one wonders why properties make sense then, well, it is not so much speed that we -gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and -this mechanism makes sure that properties are cleaned up when a node is freed. Also, the -advantage of a more or less global properties table is that we stay at the lua end. An -alternative is to store a reference in the node itself but that is complicated by the fact -that the register has some limitations (no numeric keys) and we also don't want to mess with -it too much. - -@c -int lua_properties_level = 0 ; /* can be private */ -int lua_properties_enabled = 0 ; -int lua_properties_use_metatable = 0 ; - -@ -We keep track of nesting so that we don't oveflow the stack, and, what is more -important, don't keep resolving the registry index. - -@c -#define lua_properties_push do { \ - if (lua_properties_enabled) { \ - lua_properties_level = lua_properties_level + 1 ; \ - if (lua_properties_level == 1) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - } \ - } \ -} while(0) - -#define lua_properties_pop do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 1) \ - lua_pop(Luas,1); \ - lua_properties_level = lua_properties_level - 1 ; \ - } \ -} while(0) - -/* No setting is needed: */ - -#define lua_properties_set(target) do { \ -} while(0) - -/* Resetting boils down to nilling. */ - -#define lua_properties_reset(target) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_pushnil(Luas); \ - lua_rawseti(Luas,-2,target); \ - lua_pop(Luas,1); \ - } else { \ - lua_pushnil(Luas); \ - lua_rawseti(Luas,-2,target); \ - } \ - } \ -} while(0) - -/* - For a moment I considered supporting all kind of data types but in practice - that makes no sense. So we stick to a cheap shallow copy with as option a - metatable. Btw, a deep copy would look like this: - - static void copy_lua_table(lua_State* L, int index) { - lua_newtable(L); - lua_pushnil(L); - while(lua_next(L, index-1) != 0) { - lua_pushvalue(L, -2); - lua_insert(L, -2); - if (lua_type(L,-1)==LUA_TTABLE) - copy_lua_table(L,-1); - lua_settable(L, -4); - } - lua_pop(L,1); - } - - #define lua_properties_copy(target, source) do { \ - if (lua_properties_enabled) { \ - lua_pushinteger(Luas,source); \ - lua_rawget(Luas,-2); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - copy_lua_table(Luas,-1); \ - lua_pushinteger(Luas,target); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas,-3); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } while(0) - -*/ - -/* isn't there a faster way to metatable? */ - -/* - -#define lua_properties_copy(target,source) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setfield(Luas,-2,"__index"); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - lua_pop(Luas,1); \ - } else { \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setfield(Luas,-2,"__index"); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } \ -} while(0) - -*/ - -/* - A simple testrun on many pages of dumb text shows 1% gain (of course it depends - on how properties are used but some other tests confirm it). -*/ - -#define lua_properties_copy(target,source) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_push_string_by_name(Luas,__index); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas, -3); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - lua_pop(Luas,1); \ - } else { \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_push_string_by_name(Luas,__index); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas, -3); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } \ -} while(0) - -/* Here end the property handlers. */ - -@ @c -int valid_node(halfword p) -{ - if (p > my_prealloc && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - if (varmem_sizes[p] > 0) { - return 1; - } -#else - return 1; -#endif - } - return 0; -} - -@ @c -static int test_count = 1; - -#define dorangetest(a,b,c) do { \ - if (!(b>=0 && b<c)) { \ - fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \ - (int)a, (int)b, (int)c, __LINE__,test_count); \ - confusion("node range test failed"); \ - } } while (0) - -#define dotest(a,b,c) do { \ - if (b!=c) { \ - fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \ - (int)a, (int)b, (int)c, __LINE__,test_count); \ - confusion("node test failed"); \ - } } while (0) - -#define check_action_ref(a) { dorangetest(p,a,var_mem_max); } -#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); } - -/* hm, we can just pass p then */ - -#define check_token_ref(p) { \ - if (type(p) == whatsit_node) { \ - formatted_error("nodes","fuzzy token cleanup in whatsit node with type %s and subtype %i",node_data[type(p)].name,subtype(p)); \ - } else { \ - formatted_error("nodes","fuzzy token cleanup in node with type %s",node_data[type(p)].name); \ - } \ -} - -#ifdef CHECK_NODE_USAGE - -static void check_static_node_mem(void) -{ - dotest(zero_glue, width(zero_glue), 0); - dotest(zero_glue, type(zero_glue), glue_spec_node); - dotest(zero_glue, vlink(zero_glue), null); - dotest(zero_glue, stretch(zero_glue), 0); - dotest(zero_glue, stretch_order(zero_glue), normal); - dotest(zero_glue, shrink(zero_glue), 0); - dotest(zero_glue, shrink_order(zero_glue), normal); - - dotest(sfi_glue, width(sfi_glue), 0); - dotest(sfi_glue, type(sfi_glue), glue_spec_node); - dotest(sfi_glue, vlink(sfi_glue), null); - dotest(sfi_glue, stretch(sfi_glue), 0); - dotest(sfi_glue, stretch_order(sfi_glue), sfi); - dotest(sfi_glue, shrink(sfi_glue), 0); - dotest(sfi_glue, shrink_order(sfi_glue), normal); - - dotest(fil_glue, width(fil_glue), 0); - dotest(fil_glue, type(fil_glue), glue_spec_node); - dotest(fil_glue, vlink(fil_glue), null); - dotest(fil_glue, stretch(fil_glue), unity); - dotest(fil_glue, stretch_order(fil_glue), fil); - dotest(fil_glue, shrink(fil_glue), 0); - dotest(fil_glue, shrink_order(fil_glue), normal); - - dotest(fill_glue, width(fill_glue), 0); - dotest(fill_glue, type(fill_glue), glue_spec_node); - dotest(fill_glue, vlink(fill_glue), null); - dotest(fill_glue, stretch(fill_glue), unity); - dotest(fill_glue, stretch_order(fill_glue), fill); - dotest(fill_glue, shrink(fill_glue), 0); - dotest(fill_glue, shrink_order(fill_glue), normal); - - dotest(ss_glue, width(ss_glue), 0); - dotest(ss_glue, type(ss_glue), glue_spec_node); - dotest(ss_glue, vlink(ss_glue), null); - dotest(ss_glue, stretch(ss_glue), unity); - dotest(ss_glue, stretch_order(ss_glue), fil); - dotest(ss_glue, shrink(ss_glue), unity); - dotest(ss_glue, shrink_order(ss_glue), fil); - - dotest(fil_neg_glue, width(fil_neg_glue), 0); - dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node); - dotest(fil_neg_glue, vlink(fil_neg_glue), null); - dotest(fil_neg_glue, stretch(fil_neg_glue), -unity); - dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil); - dotest(fil_neg_glue, shrink(fil_neg_glue), 0); - dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal); -} - -static void node_mem_dump(halfword p) -{ - halfword r; - for (r = my_prealloc + 1; r < var_mem_max; r++) { - if (vlink(r) == p) { - halfword s = r; - while (s > my_prealloc && varmem_sizes[s] == 0) { - s--; - } - if (s != null - && s != my_prealloc - && s != var_mem_max - && (r - s) < get_node_size(type(s), subtype(s)) - && alink(s) != p) { - if (type(s) == disc_node) { - fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", - get_node_name(type(s), subtype(s)), (int) s, - (int) vlink(s), (int) alink(s)); - fprintf(stdout, "pre_break(%d,%d,%d), ", - (int) vlink_pre_break(s), (int) tlink(pre_break(s)), - (int) alink(pre_break(s))); - fprintf(stdout, "post_break(%d,%d,%d), ", - (int) vlink_post_break(s), - (int) tlink(post_break(s)), - (int) alink(post_break(s))); - fprintf(stdout, "no_break(%d,%d,%d)", - (int) vlink_no_break(s), (int) tlink(no_break(s)), - (int) alink(no_break(s))); - fprintf(stdout, "\n"); - } else { - if (vlink(s) == p - || (type(s) == glyph_node && lig_ptr (s) == p) - || (type(s) == vlist_node && list_ptr(s) == p) - || (type(s) == hlist_node && list_ptr(s) == p) - || (type(s) == unset_node && list_ptr(s) == p) - || (type(s) == ins_node && ins_ptr (s) == p) - ) { - fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", - get_node_name(type(s), subtype(s)), (int) s, - (int) vlink(s), (int) alink(s)); - if (type(s) == glyph_node) { - fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); - } else if (type(s) == vlist_node || type(s) == hlist_node) { - fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); - } - fprintf(stdout, "\n"); - } else { - if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) { - fprintf(stdout, " pointed to from %s node %d\n", - get_node_name(type(s), subtype(s)), (int) s); - } - } - } - } - } - } -} - -#endif - -static int free_error(halfword p) -{ - if (p > my_prealloc && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - int i; - if (varmem_sizes[p] == 0) { - check_static_node_mem(); - for (i = (my_prealloc + 1); i < var_mem_max; i++) { - if (varmem_sizes[i] > 0) { - check_node(i); - } - } - test_count++; - if (type(p) == glyph_node) { - formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p); - } else { - formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); - } - node_mem_dump(p); - return 1; - } -#endif - } else { - formatted_error("nodes", "attempt to free an impossible node %d", (int) p); - return 1; - } - return 0; -} - -@ @c -static int copy_error(halfword p) -{ - if (p >= 0 && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - if (p > my_prealloc && varmem_sizes[p] == 0) { - if (type(p) == glyph_node) { - formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p); - } else { - formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); - } - return 1; - } -#endif - } else { - formatted_error("nodes", "attempt to copy an impossible node %d", (int) p); - return 1; - } - return 0; -} - -@ @c -static halfword synctex_anyway_mode = 0; /* 2 also glyphs */ -static halfword synctex_line_field = 0; -static halfword synctex_no_files = 0; - -void synctex_set_mode(int m) -{ - synctex_anyway_mode = m; -}; - -int synctex_get_mode(void) -{ - return synctex_anyway_mode; -}; - -void synctex_set_no_files(int f) -{ - synctex_no_files = f; -}; - -int synctex_get_no_files(void) -{ - return (int) synctex_no_files ; -}; - -void synctex_set_tag(int t) -{ - cur_input.synctex_tag_field = t; -}; - -int synctex_get_tag(void) -{ - return (int) cur_input.synctex_tag_field; -}; - - -int synctex_get_line(void) -{ - return (int) synctex_line_field; -}; - -static int forced_tag = 0; -static int forced_line = 0; - -void synctex_force_tag(int t) -{ - forced_tag = t; -}; - -void synctex_force_line(int t) -{ - forced_line = t; -}; - -void synctex_set_line(int l) -{ - synctex_line_field = l; -}; - -@ @c -/* if_stack is called a lot so maybe optimize */ - -halfword new_node(int i, int j) -{ - int s = get_node_size(i, j); - halfword n = get_node(s); - /* - It should be possible to do this memset at |free_node()|. - - Both type() and subtype() will be set below, and vlink() is - set to null by |get_node()|, so we can do we clearing one - word less than |s| - */ - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1))); - switch (i) { - case glyph_node: - init_lang_data(n); - break; - case hlist_node: - case vlist_node: - box_dir(n) = -1; - break; - case disc_node: - pre_break(n) = pre_break_head(n); - type(pre_break(n)) = nesting_node; - subtype(pre_break(n)) = pre_break_head(0); - post_break(n) = post_break_head(n); - type(post_break(n)) = nesting_node; - subtype(post_break(n)) = post_break_head(0); - no_break(n) = no_break_head(n); - type(no_break(n)) = nesting_node; - subtype(no_break(n)) = no_break_head(0); - break; - case rule_node: - depth(n) = null_flag; - height(n) = null_flag; - width(n) = null_flag; - rule_dir(n) = -1; - rule_index(n) = 0; - rule_transform(n) = 0; - break; - case whatsit_node: - if (j == open_node) { - open_name(n) = get_nullstr(); - open_area(n) = open_name(n); - open_ext(n) = open_name(n); - } - break; - case unset_node: - width(n) = null_flag; - break; - case pseudo_line_node: - case shape_node: - /* this is a trick that makes |pseudo_files| slightly slower, - but the overall allocation faster then an explicit test - at the top of |new_node()|. - */ - if (j>0) { - free_node(n, variable_node_size); - n = slow_get_node(j); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1))); - } - break; - default: - break; - } - if (synctex_anyway_mode) { - switch (i) { - /* 1 = all but glyphs */ - /* 2 = also glyphs */ - /* 3 = glyphs and glue */ - /* 4 = only glyphs */ - case glyph_node: - if (synctex_anyway_mode > 1) { - synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - break; - case glue_node: - if (synctex_anyway_mode < 4) { - synctex_tag_glue(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_glue(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - break; - case kern_node: - if (synctex_anyway_mode < 3) { - synctex_tag_kern(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_kern(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - break; - case hlist_node: - case vlist_node: - case unset_node: /* useless */ - if (synctex_anyway_mode < 3) { - synctex_tag_box(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_box(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - break; - case rule_node: - if (synctex_anyway_mode < 3) { - synctex_tag_rule(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_rule(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - break; - case math_node: /* noads probably make more sense */ - if (synctex_anyway_mode < 3) { - synctex_tag_math(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_math(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - break; - } - } else if (synctex_par) { - /* handle synctex extension */ - switch (i) { - case glue_node: - synctex_tag_glue(n) = cur_input.synctex_tag_field; - synctex_line_glue(n) = line; - break; - case kern_node: - if (j != 0) { - synctex_tag_kern(n) = cur_input.synctex_tag_field; - synctex_line_kern(n) = line; - } - break; - case hlist_node: - case vlist_node: - case unset_node: - synctex_tag_box(n) = cur_input.synctex_tag_field; - synctex_line_box(n) = line; - break; - case rule_node: - synctex_tag_rule(n) = cur_input.synctex_tag_field; - synctex_line_rule(n) = line; - break; - case math_node: - synctex_tag_math(n) = cur_input.synctex_tag_field; - synctex_line_math(n) = line; - break; - } - } - /* take care of attributes */ - if (nodetype_has_attributes(i)) { - build_attribute_list(n); - /* lua_properties_set */ - } - type(n) = (quarterword) i; - subtype(n) = (quarterword) j; - return n; -} - -halfword raw_glyph_node(void) -{ - register halfword n = get_node(glyph_node_size); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); - if (synctex_anyway_mode > 1) { - synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - type(n) = glyph_node; - subtype(n) = 0; - return n; -} - -halfword new_glyph_node(void) -{ - register halfword n = get_node(glyph_node_size); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); - if (synctex_anyway_mode > 1) { - synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_glyph(n) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - type(n) = glyph_node; - subtype(n) = 0; - build_attribute_list(n); - /* lua_properties_set */ - return n; -} - -@ makes a duplicate of the node list that starts at |p| and returns a -pointer to the new list - -@c -halfword do_copy_node_list(halfword p, halfword end) -{ - halfword q = null; /* previous position in new list */ - halfword h = null; /* head of the list */ - register halfword s ; - lua_properties_push; /* saves stack and time */ - while (p != end) { - s = copy_node(p); - if (h == null) { - h = s; - } else { - couple_nodes(q, s); - } - q = s; - p = vlink(p); - } - lua_properties_pop; /* saves stack and time */ - return h; -} - -halfword copy_node_list(halfword p) -{ - return do_copy_node_list(p, null); -} - -#define copy_sub_list(target,source) do { \ - if (source != null) { \ - s = do_copy_node_list(source, null); \ - target = s; \ - } else { \ - target = null; \ - } \ - } while (0) - -#define copy_sub_node(target,source) do { \ - if (source != null) { \ - s = copy_node(source); \ - target = s ; \ - } else { \ - target = null; \ - } \ -} while (0) - -@ make a dupe of a single node - -@c -static void copy_node_wrapup_core(halfword p, halfword r) -{ - halfword s ; - switch (subtype(p)) { - case write_node: - case special_node: - add_token_ref(write_tokens(p)); - break; - case late_lua_node: - copy_late_lua(r, p); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - add_node_attr_ref(user_node_value(p)); - break; - case 'l': - copy_user_lua(r, p); - break; - case 'n': - s = copy_node_list(user_node_value(p)); - user_node_value(r) = s; - break; - case 's': - /* |add_string_ref(user_node_value(p));| */ - break; - case 't': - add_token_ref(user_node_value(p)); - break; - } - break; - default: - break ; - } -} - -void copy_node_wrapup_dvi(halfword p, halfword r) -{ -} - -void copy_node_wrapup_pdf(halfword p, halfword r) -{ - switch(subtype(p)) { - case pdf_literal_node: - copy_pdf_literal(r, p); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - add_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - add_token_ref(pdf_setmatrix_data(p)); - break; - case pdf_annot_node: - add_token_ref(pdf_annot_data(p)); - break; - case pdf_start_link_node: - if (pdf_link_attr(r) != null) - add_token_ref(pdf_link_attr(r)); - add_action_ref(pdf_link_action(r)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - add_token_ref(pdf_dest_id(p)); - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - add_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - add_token_ref(pdf_thread_attr(p)); - break; - default: - break; - } -} - -halfword copy_node(const halfword p) -{ - halfword r; /* current node being fabricated for new list */ - halfword w; /* whatsit subtype */ - halfword t; /* type of node */ - register halfword s; /* a helper variable for copying into variable mem */ - register int i; - if (copy_error(p)) { - r = new_node(temp_node, 0); - return r; - } - t = type(p); - i = get_node_size(t,subtype(p)); - r = get_node(i); - - (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i)); - - /* possible speedup: */ - /* - if t == glue_spec) { - return r; - } - */ - - if (synctex_anyway_mode) { - /* - if (t == glyph_node) { - if (synctex_anyway_mode > 1) { - synctex_tag_glyph(r) = forced_tag ? forced_tag : cur_input.synctex_tag_field; - synctex_line_glyph(r) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line; - } - } - */ - } else if (synctex_par) { - /* handle synctex extension */ - switch (t) { - case math_node: - synctex_tag_math(r) = cur_input.synctex_tag_field; - synctex_line_math(r) = line; - break; - case kern_node: - synctex_tag_kern(r) = cur_input.synctex_tag_field; - synctex_line_kern(r) = line; - break; - } - } - if (nodetype_has_attributes(t)) { - add_node_attr_ref(node_attr(p)); - alink(r) = null; - lua_properties_copy(r,p); - } - vlink(r) = null; - - switch (t) { - case glyph_node: - copy_sub_list(lig_ptr(r),lig_ptr(p)) ; - break; - case glue_node: - copy_sub_list(leader_ptr(r),leader_ptr(p)) ; - break; - case hlist_node: - case vlist_node: - case unset_node: - copy_sub_list(list_ptr(r),list_ptr(p)) ; - break; - case disc_node: - pre_break(r) = pre_break_head(r); - if (vlink_pre_break(p) != null) { - s = copy_node_list(vlink_pre_break(p)); - alink(s) = pre_break(r); - tlink_pre_break(r) = tail_of_list(s); - vlink_pre_break(r) = s; - } else { - assert(tlink(pre_break(r)) == null); - } - post_break(r) = post_break_head(r); - if (vlink_post_break(p) != null) { - s = copy_node_list(vlink_post_break(p)); - alink(s) = post_break(r); - tlink_post_break(r) = tail_of_list(s); - vlink_post_break(r) = s; - } else { - assert(tlink_post_break(r) == null); - } - no_break(r) = no_break_head(r); - if (vlink(no_break(p)) != null) { - s = copy_node_list(vlink_no_break(p)); - alink(s) = no_break(r); - tlink_no_break(r) = tail_of_list(s); - vlink_no_break(r) = s; - } else { - assert(tlink_no_break(r) == null); - } - break; - case math_node: - break; - case ins_node: - copy_sub_list(ins_ptr(r),ins_ptr(p)) ; - break; - case margin_kern_node: - copy_sub_node(margin_char(r),margin_char(p)); - break; - case mark_node: - add_token_ref(mark_ptr(p)); - break; - case adjust_node: - copy_sub_list(adjust_ptr(r),adjust_ptr(p)); - break; - case choice_node: - copy_sub_list(display_mlist(r),display_mlist(p)) ; - copy_sub_list(text_mlist(r),text_mlist(p)) ; - copy_sub_list(script_mlist(r),script_mlist(p)) ; - copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ; - break; - case simple_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - break; - case radical_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - copy_sub_node(left_delimiter(r),left_delimiter(p)) ; - copy_sub_list(degree(r),degree(p)) ; - break; - case accent_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ; - copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ; - copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ; - break; - case fence_noad: - copy_sub_node(delimiter(r),delimiter(p)) ; - break; - case sub_box_node: - case sub_mlist_node: - copy_sub_list(math_list(r),math_list(p)) ; - break; - case fraction_noad: - copy_sub_list(numerator(r),numerator(p)) ; - copy_sub_list(denominator(r),denominator(p)) ; - copy_sub_node(left_delimiter(r),left_delimiter(p)) ; - copy_sub_node(right_delimiter(r),right_delimiter(p)) ; - break; - case glue_spec_node: - case dir_node: - case local_par_node: - case boundary_node: - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - copy_node_wrapup_pdf(p,r); - } else if (w >= backend_first_dvi_whatsit) { - copy_node_wrapup_dvi(p,r); - } else { - copy_node_wrapup_core(p,r); - } - break; - } - return r; -} - -/* x */ - -#define free_sub_list(source) if (source != null) flush_node_list(source); -#define free_sub_node(source) if (source != null) flush_node(source); - -@ @c - -static void flush_node_wrapup_core(halfword p) -{ - switch (subtype(p)) { - case open_node: - case write_node: - case close_node: - case save_pos_node: - break; - case special_node: - delete_token_ref(write_tokens(p)); - break; - case late_lua_node: - free_late_lua(p); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - delete_attribute_ref(user_node_value(p)); - break; - case 'd': - break; - case 'l': - free_user_lua(user_node_value(p)); - break; - case 'n': - flush_node_list(user_node_value(p)); - break; - case 's': - /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */ - break; - case 't': - delete_token_ref(user_node_value(p)); - break; - default: - { - const char *hlp[] = { - "The type of the value in a user defined whatsit node should be one", - "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),", - "or 't' (tokenlist). Yours has an unknown type, and therefore I don't", - "know how to free the node's value. A memory leak may result.", - NULL - }; - tex_error("Unidentified user defined whatsit", hlp); - } - break; - } - break; - } -} - -void flush_node_wrapup_dvi(halfword p) -{ -} - -void flush_node_wrapup_pdf(halfword p) -{ - switch(subtype(p)) { - case pdf_save_node: - case pdf_restore_node: - case pdf_refobj_node: - case pdf_end_link_node: - case pdf_end_thread_node: - break; - case pdf_literal_node: - free_pdf_literal(p); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - delete_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - delete_token_ref(pdf_setmatrix_data(p)); - break; - case pdf_annot_node: - delete_token_ref(pdf_annot_data(p)); - break; - case pdf_link_data_node: - break; - case pdf_start_link_node: - if (pdf_link_attr(p) != null) - delete_token_ref(pdf_link_attr(p)); - delete_action_ref(pdf_link_action(p)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - delete_token_ref(pdf_dest_id(p)); - break; - case pdf_action_node: - if (pdf_action_type(p) == pdf_action_user) { - delete_token_ref(pdf_action_tokens(p)); - } else { - if (pdf_action_file(p) != null) - delete_token_ref(pdf_action_file(p)); - if (pdf_action_type(p) == pdf_action_page) - delete_token_ref(pdf_action_tokens(p)); - else if (pdf_action_named_id(p) > 0) - delete_token_ref(pdf_action_id(p)); - } - break; - case pdf_thread_data_node: - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - delete_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - delete_token_ref(pdf_thread_attr(p)); - break; - } -} - -void flush_node(halfword p) -{ - halfword w; - if (p == null) /* legal, but no-op */ - return; - if (free_error(p)) - return; - switch (type(p)) { - case glyph_node: - free_sub_list(lig_ptr(p)); - break; - case glue_node: - free_sub_list(leader_ptr(p)); - break; - case hlist_node: - case vlist_node: - case unset_node: - free_sub_list(list_ptr(p)); - break; - case disc_node: - /* watch the start at temp node hack */ - free_sub_list(vlink(pre_break(p))); - free_sub_list(vlink(post_break(p))); - free_sub_list(vlink(no_break(p))); - break; - case rule_node: - case kern_node: - case penalty_node: - case math_node: - break; - case glue_spec_node: - /* this allows free-ing of lua-allocated glue specs */ -//if (valid_node(p)) { -// free_node(p, subtype(p)); -//} -// return ; - break ; - case dir_node: - case local_par_node: - case boundary_node: - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - flush_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - flush_node_wrapup_dvi(p); - } else { - flush_node_wrapup_core(p); - } - break; - case ins_node: - flush_node_list(ins_ptr(p)); - break; - case margin_kern_node: - flush_node(margin_char(p)); - break; - case mark_node: - delete_token_ref(mark_ptr(p)); - break; - case adjust_node: - flush_node_list(adjust_ptr(p)); - break; - case style_node: /* nothing to do */ - break; - case choice_node: - free_sub_list(display_mlist(p)); - free_sub_list(text_mlist(p)); - free_sub_list(script_mlist(p)); - free_sub_list(script_script_mlist(p)); - break; - case simple_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - break; - case radical_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - free_sub_node(left_delimiter(p)); - free_sub_list(degree(p)); - break; - case accent_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - free_sub_list(top_accent_chr(p)); - free_sub_list(bot_accent_chr(p)); - free_sub_list(overlay_accent_chr(p)); - break; - case fence_noad: - free_sub_list(delimiter(p)); - break; - case delim_node: /* nothing to do */ - case math_char_node: - case math_text_char_node: - break; - case sub_box_node: - case sub_mlist_node: - free_sub_list(math_list(p)); - break; - case fraction_noad: - free_sub_list(numerator(p)); - free_sub_list(denominator(p)); - free_sub_node(left_delimiter(p)); - free_sub_node(right_delimiter(p)); - break; - case pseudo_file_node: - free_sub_list(pseudo_lines(p)); - break; - case pseudo_line_node: - case shape_node: - free_node(p, subtype(p)); - return; - break; - case align_stack_node: - case span_node: - case movement_node: - case if_node: - case nesting_node: - case unhyphenated_node: - case hyphenated_node: - case delta_node: - case passive_node: - case inserting_node: - case split_up_node: - case expr_node: - case attribute_node: - case attribute_list_node: - case temp_node: - break; - default: - formatted_error("nodes","flushing weird node type %d", type(p)); - return; - } - if (nodetype_has_attributes(type(p))) { - delete_attribute_ref(node_attr(p)); - lua_properties_reset(p); - } - free_node(p, get_node_size(type(p), subtype(p))); - return; -} - -@ @c -void flush_node_list(halfword pp) -{ /* erase list of nodes starting at |p| */ - register halfword p = pp; - if (p == null) /* legal, but no-op */ - return; - if (free_error(p)) - return; - lua_properties_push; /* saves stack and time */ - while (p != null) { - register halfword q = vlink(p); - flush_node(p); - p = q; - } - lua_properties_pop; /* saves stack and time */ -} - -@ @c -static void check_node_wrapup_core(halfword p) -{ - switch (subtype(p)) { - /* frontend code */ - case special_node: - check_token_ref(p); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - check_attribute_ref(user_node_value(p)); - break; - case 't': - check_token_ref(p); - break; - case 'n': - dorangetest(p, user_node_value(p), var_mem_max); - break; - case 's': - case 'd': - break; - default: - confusion("unknown user node type"); - break; - } - break; - case open_node: - case write_node: - case close_node: - case save_pos_node: - break; - } -} - -void check_node_wrapup_dvi(halfword p) -{ -} - -void check_node_wrapup_pdf(halfword p) -{ - switch (subtype(p)) { - case pdf_literal_node: - if (pdf_literal_type(p) == normal) - check_token_ref(p); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - check_token_ref(p); - break; - case pdf_setmatrix_node: - check_token_ref(p); - break; - case late_lua_node: - if (late_lua_name(p) > 0) - check_token_ref(p); - if (late_lua_type(p) == normal) - check_token_ref(p); - break; - case pdf_annot_node: - check_token_ref(p); - break; - case pdf_start_link_node: - if (pdf_link_attr(p) != null) - check_token_ref(p); - check_action_ref(pdf_link_action(p)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - check_token_ref(p); - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - check_token_ref(p); - if (pdf_thread_attr(p) != null) - check_token_ref(p); - break; - case pdf_save_node: - case pdf_restore_node: - case pdf_refobj_node: - case pdf_end_link_node: - case pdf_end_thread_node: - break; - default: - confusion("wrapup pdf nodes"); - break; - } -} - -void check_node(halfword p) -{ - halfword w ; - switch (type(p)) { - case glyph_node: - dorangetest(p, lig_ptr(p), var_mem_max); - break; - case glue_node: - dorangetest(p, leader_ptr(p), var_mem_max); - break; - case hlist_node: - case vlist_node: - case unset_node: - case align_record_node: - dorangetest(p, list_ptr(p), var_mem_max); - break; - case ins_node: - dorangetest(p, ins_ptr(p), var_mem_max); - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - check_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - check_node_wrapup_dvi(p); - } else { - check_node_wrapup_core(p); - } - break; - case margin_kern_node: - check_node(margin_char(p)); - break; - case math_node: - break; - case disc_node: - dorangetest(p, vlink(pre_break(p)), var_mem_max); - dorangetest(p, vlink(post_break(p)), var_mem_max); - dorangetest(p, vlink(no_break(p)), var_mem_max); - break; - case adjust_node: - dorangetest(p, adjust_ptr(p), var_mem_max); - break; - case pseudo_file_node: - dorangetest(p, pseudo_lines(p), var_mem_max); - break; - case pseudo_line_node: - case shape_node: - break; - case choice_node: - dorangetest(p, display_mlist(p), var_mem_max); - dorangetest(p, text_mlist(p), var_mem_max); - dorangetest(p, script_mlist(p), var_mem_max); - dorangetest(p, script_script_mlist(p), var_mem_max); - break; - case fraction_noad: - dorangetest(p, numerator(p), var_mem_max); - dorangetest(p, denominator(p), var_mem_max); - dorangetest(p, left_delimiter(p), var_mem_max); - dorangetest(p, right_delimiter(p), var_mem_max); - break; - case simple_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - break; - case radical_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - dorangetest(p, degree(p), var_mem_max); - dorangetest(p, left_delimiter(p), var_mem_max); - break; - case accent_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - dorangetest(p, top_accent_chr(p), var_mem_max); - dorangetest(p, bot_accent_chr(p), var_mem_max); - dorangetest(p, overlay_accent_chr(p), var_mem_max); - break; - case fence_noad: - dorangetest(p, delimiter(p), var_mem_max); - break; - /* - case rule_node: - case kern_node: - case penalty_node: - case mark_node: - case style_node: - case attribute_list_node: - case attribute_node: - case glue_spec_node: - case temp_node: - case align_stack_node: - case movement_node: - case if_node: - case nesting_node: - case span_node: - case unhyphenated_node: - case hyphenated_node: - case delta_node: - case passive_node: - case expr_node: - case dir_node: - case boundary_node: - case local_par_node: - break; - default: - fprintf(stdout, "check_node: type is %d\n", type(p)); - */ - } -} - -@ @c -halfword fix_node_list(halfword head) -{ - halfword next, tail; - if (head == null) - return null; - tail = head; - next = vlink(head); - while (next != null) { - alink(next) = tail; - tail = next; - next = vlink(tail); - } - return tail; -} - -@ @c -halfword get_node(int s) -{ - register halfword r; - - if (s < MAX_CHAIN_SIZE) { - r = free_chain[s]; - if (r != null) { - free_chain[s] = vlink(r); -#ifdef CHECK_NODE_USAGE - varmem_sizes[r] = (char) s; -#endif - vlink(r) = null; - var_used += s; /* maintain usage statistics */ - return r; - } - /* this is the end of the 'inner loop' */ - return slow_get_node(s); - } else { - normal_error("nodes","there is a problem in getting a node, case 1"); - return null; - } -} - -@ @c -void free_node(halfword p, int s) -{ - if (p <= my_prealloc) { - formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p)); - return; - } -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - if (s < MAX_CHAIN_SIZE) { - vlink(p) = free_chain[s]; - free_chain[s] = p; - } else { - /* todo ? it is perhaps possible to merge this node with an existing rover */ - node_size(p) = s; - vlink(p) = rover; - while (vlink(rover) != vlink(p)) { - rover = vlink(rover); - } - vlink(rover) = p; - } - /* maintain statistics */ - var_used -= s; -} - -@ @c -static void free_node_chain(halfword q, int s) -{ - register halfword p = q; - while (vlink(p) != null) { -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - var_used -= s; - p = vlink(p); - } - var_used -= s; -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - vlink(p) = free_chain[s]; - free_chain[s] = q; -} - -@ At the start of the node memory area we reserve some special nodes, -for instance frequently used glue specifications. We could as well just -use new_glue here but for the moment we stick to the traditional approach. - -@c -#define initialize_glue(n,wi,st,sh,sto,sho) \ - vlink(n) = null; \ - type(n) = glue_spec_node; \ - width(n) = wi; \ - stretch(n) = st; \ - shrink(n) = sh; \ - stretch_order(n) = sto; \ - shrink_order(n) = sho; - -#define initialize_whatever(n,t) \ - vinfo(n) = 0; \ - type(n) = t; \ - vlink(n) = null; \ - alink(n) = null; - -#define initialize_point(n) \ - type(n) = glyph_node; \ - subtype(n) = 0; \ - vlink(n) = null; \ - vinfo(n + 1) = null; \ - alink(n) = null; \ - font(n) = 0; \ - character(n) = '.'; \ - vinfo(n + 3) = 0; \ - vlink(n + 3) = 0; \ - vinfo(n + 4) = 0; \ - vlink(n + 4) = 0; - -void init_node_mem(int t) -{ - my_prealloc = var_mem_stat_max; - - varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t); - if (varmem == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word)); -#ifdef CHECK_NODE_USAGE - varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t); - if (varmem_sizes == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t); -#endif - var_mem_max = t; - rover = var_mem_stat_max + 1; - vlink(rover) = rover; - node_size(rover) = (t - rover); - var_used = 0; - - /* initialize static glue specs */ - - initialize_glue(zero_glue,0,0,0,0,0); - initialize_glue(sfi_glue,0,0,0,sfi,0); - initialize_glue(fil_glue,0,unity,0,fil,0); - initialize_glue(fill_glue,0,unity,0,fill,0); - initialize_glue(ss_glue,0,unity,unity,fil,fil); - initialize_glue(fil_neg_glue,0,-unity,0,fil,0); - - /* initialize node list heads */ - - initialize_whatever(page_ins_head,temp_node); - initialize_whatever(contrib_head,temp_node); - initialize_whatever(page_head,temp_node); - initialize_whatever(temp_head,temp_node); - initialize_whatever(hold_head,temp_node); - initialize_whatever(adjust_head,temp_node); - initialize_whatever(pre_adjust_head,temp_node); - initialize_whatever(align_head,temp_node); - - initialize_whatever(active,unhyphenated_node); - initialize_whatever(end_span,span_node); - - initialize_point(begin_point); - initialize_point(end_point); -} - -@ @c -void dump_node_mem(void) -{ - dump_int(var_mem_max); - dump_int(rover); - dump_things(varmem[0], var_mem_max); -#ifdef CHECK_NODE_USAGE - dump_things(varmem_sizes[0], var_mem_max); -#endif - dump_things(free_chain[0], MAX_CHAIN_SIZE); - dump_int(var_used); - dump_int(my_prealloc); -} - -@ it makes sense to enlarge the varmem array immediately -@c - -void undump_node_mem(void) -{ - int x; - undump_int(x); - undump_int(rover); - var_mem_max = (x < 100000 ? 100000 : x); - varmem = xmallocarray(memory_word, (unsigned) var_mem_max); - undump_things(varmem[0], x); -#ifdef CHECK_NODE_USAGE - varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); - memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char)); - undump_things(varmem_sizes[0], x); -#endif - undump_things(free_chain[0], MAX_CHAIN_SIZE); - undump_int(var_used); - undump_int(my_prealloc); - if (var_mem_max > x) { - /* todo ? it is perhaps possible to merge the new node with an existing rover */ - vlink(x) = rover; - node_size(x) = (var_mem_max - x); - while (vlink(rover) != vlink(x)) { - rover = vlink(rover); - } - vlink(rover) = x; - } -} - -@ @c -halfword slow_get_node(int s) -{ - register int t; - - RETRY: - t = node_size(rover); - if (vlink(rover) < var_mem_max && vlink(rover) != 0) { - if (t > s) { - /* allocating from the bottom helps decrease page faults */ - register halfword r = rover; - rover += s; - vlink(rover) = vlink(r); - node_size(rover) = node_size(r) - s; - if (vlink(rover) != r) { /* list is longer than one */ - halfword q = r; - while (vlink(q) != r) { - q = vlink(q); - } - vlink(q) += s; - } else { - vlink(rover) += s; - } - if (vlink(rover) < var_mem_max) { -#ifdef CHECK_NODE_USAGE - varmem_sizes[r] = (char) (s > 127 ? 127 : s); -#endif - vlink(r) = null; - var_used += s; /* maintain usage statistics */ - return r; /* this is the only exit */ - } else { - normal_error("nodes","there is a problem in getting a node, case 2"); - return null; - } - } else { - /* attempt to keep the free list small */ - int x; - if (vlink(rover) != rover) { - if (t < MAX_CHAIN_SIZE) { - halfword l = vlink(rover); - vlink(rover) = free_chain[t]; - free_chain[t] = rover; - rover = l; - while (vlink(l) != free_chain[t]) { - l = vlink(l); - } - vlink(l) = rover; - goto RETRY; - } else { - halfword l = rover; - while (vlink(rover) != l) { - if (node_size(rover) > s) { - goto RETRY; - } - rover = vlink(rover); - } - } - } - /* if we are still here, it was apparently impossible to get a match */ - x = (var_mem_max >> 2) + s; - varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x)); - if (varmem == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word)); -#ifdef CHECK_NODE_USAGE - varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x)); - if (varmem_sizes == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char)); -#endif - /* todo ? it is perhaps possible to merge the new memory with an existing rover */ - vlink(var_mem_max) = rover; - node_size(var_mem_max) = x; - while (vlink(rover) != vlink(var_mem_max)) { - rover = vlink(rover); - } - vlink(rover) = var_mem_max; - rover = var_mem_max; - var_mem_max += x; - goto RETRY; - } - } else { - normal_error("nodes","there is a problem in getting a node, case 3"); - return null; - } -} - -@ @c -char *sprint_node_mem_usage(void) -{ - char *s; -#ifdef CHECK_NODE_USAGE - char *ss; - int i; - int b = 0; - char msg[256]; - int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; - s = strdup(""); - for (i = (var_mem_max - 1); i > my_prealloc; i--) { - if (varmem_sizes[i] > 0) { - if (type(i) > last_normal_node + last_whatsit_node) { - node_counts[last_normal_node + last_whatsit_node + 1]++; - } else if (type(i) == whatsit_node) { - node_counts[(subtype(i) + last_normal_node + 1)]++; - } else { - node_counts[type(i)]++; - } - } - } - for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) { - if (node_counts[i] > 0) { - int j = - (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0); - snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], - get_node_name((i > last_normal_node ? whatsit_node : i), j)); - ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1)); - strcpy(ss, s); - strcat(ss, msg); - free(s); - s = ss; - b = 1; - } - } -#else - s = strdup(""); -#endif - return s; -} - -@ @c -halfword list_node_mem_usage(void) -{ - halfword q = null; -#ifdef CHECK_NODE_USAGE - halfword p = null; - halfword i, j; - char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); - memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max); - for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { - if (saved_varmem_sizes[i] > 0) { - j = copy_node(i); - if (p == null) { - q = j; - } else { - vlink(p) = j; - } - p = j; - } - } - free(saved_varmem_sizes); -#endif - return q; -} - -@ @c -void print_node_mem_stats(void) -{ - int i, b; - halfword j; - char msg[256]; - char *s; - int free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; - snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc)); - tprint_nl(msg); - s = sprint_node_mem_usage(); - tprint_nl(" "); - tprint(s); - free(s); - tprint(" nodes"); - tprint_nl(" avail lists: "); - b = 0; - for (i = 1; i < MAX_CHAIN_SIZE; i++) { - for (j = free_chain[i]; j != null; j = vlink(j)) - free_chain_counts[i]++; - if (free_chain_counts[i] > 0) { - snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]); - tprint(msg); - b = 1; - } - } - /* newline, if needed */ - print_nlp(); -} - -/* this belongs in the web but i couldn't find the correct syntactic place */ - -halfword new_span_node(halfword n, int s, scaled w) -{ - halfword p = new_node(span_node, 0); - span_link(p) = n; - span_span(p) = s; - width(p) = w; - return p; -} - -@* Attribute stuff. - -@c -static halfword new_attribute_node(unsigned int i, int v) -{ - register halfword r = get_node(attribute_node_size); - type(r) = attribute_node; - attribute_id(r) = (halfword) i; - attribute_value(r) = v; - /* not used but nicer in print */ - subtype(r) = 0; - return r; -} - -@ @c -halfword copy_attribute_list(halfword n) -{ - halfword q = get_node(attribute_node_size); - register halfword p = q; - type(p) = attribute_list_node; - attr_list_ref(p) = 0; - n = vlink(n); - while (n != null) { - register halfword r = get_node(attribute_node_size); - /* the link will be fixed automatically in the next loop */ - (void) memcpy((void *) (varmem + r), (void *) (varmem + n), - (sizeof(memory_word) * attribute_node_size)); - vlink(p) = r; - p = r; - n = vlink(n); - } - return q; -} - -@ @c -void update_attribute_cache(void) -{ - halfword p; - register int i; - attr_list_cache = get_node(attribute_node_size); - type(attr_list_cache) = attribute_list_node; - attr_list_ref(attr_list_cache) = 0; - p = attr_list_cache; - for (i = 0; i <= max_used_attr; i++) { - register int v = attribute(i); - if (v > UNUSED_ATTRIBUTE) { - register halfword r = new_attribute_node((unsigned) i, v); - vlink(p) = r; - p = r; - } - } - if (vlink(attr_list_cache) == null) { - free_node(attr_list_cache, attribute_node_size); - attr_list_cache = null; - } - return; -} - -@ @c -void build_attribute_list(halfword b) -{ - if (max_used_attr >= 0) { - if (attr_list_cache == cache_disabled|| attr_list_cache == null) { - update_attribute_cache(); - if (attr_list_cache == null) - return; - } - attr_list_ref(attr_list_cache)++; - node_attr(b) = attr_list_cache; - } -} - -@ @c -halfword current_attribute_list(void) -{ - if (max_used_attr >= 0) { - if (attr_list_cache == cache_disabled) { - update_attribute_cache(); - } - return attr_list_cache ; - } - return null ; -} - - -@ @c -void reassign_attribute(halfword n, halfword new) -{ - halfword old; - old = node_attr(n); - if (new == null) { - /* there is nothing to assign but we need to check for an old value */ - if (old != null) - delete_attribute_ref(old); /* also nulls attr field of n */ - } else if (old == null) { - /* nothing is assigned so we just do that now */ - assign_attribute_ref(n,new); - } else if (old != new) { - /* something is assigned so we need to clean up and assign then */ - delete_attribute_ref(old); - assign_attribute_ref(n,new); - } - /* else: same value so there is no need to assign and change the refcount */ - node_attr(n) = new ; -} - -@ @c -void delete_attribute_ref(halfword b) -{ - if (b != null) { - if (type(b) == attribute_list_node){ - attr_list_ref(b)--; - if (attr_list_ref(b) == 0) { - if (b == attr_list_cache) - attr_list_cache = cache_disabled; - free_node_chain(b, attribute_node_size); - } - /* maintain sanity */ - if (attr_list_ref(b) < 0) { - attr_list_ref(b) = 0; - } - } else { - normal_error("nodes","trying to delete an attribute reference of a non attribute node"); - } - } -} - -void reset_node_properties(halfword b) -{ - if (b != null) { - lua_properties_reset(b); - } -} - -@ |p| is an attr list head, or zero -@c -halfword do_set_attribute(halfword p, int i, int val) -{ - register halfword q; - register int j = 0; - if (p == null) { /* add a new head \& node */ - q = get_node(attribute_node_size); - type(q) = attribute_list_node; - attr_list_ref(q) = 1; - p = new_attribute_node((unsigned) i, val); - vlink(q) = p; - return q; - } - q = p; - if (vlink(p) != null) { - while (vlink(p) != null) { - int t = attribute_id(vlink(p)); - if (t == i && attribute_value(vlink(p)) == val) - return q; /* no need to do anything */ - if (t >= i) - break; - j++; - p = vlink(p); - } - - p = q; - while (j-- > 0) - p = vlink(p); - if (attribute_id(vlink(p)) == i) { - attribute_value(vlink(p)) = val; - } else { /* add a new node */ - halfword r = new_attribute_node((unsigned) i, val); - vlink(r) = vlink(p); - vlink(p) = r; - } - return q; - } else { - normal_error("nodes","trying to set an attribute fails, case 1"); - return null ; - } -} - -@ @c -void set_attribute(halfword n, int i, int val) -{ - register halfword p; - register int j = 0; - /* not all nodes can have an attribute list */ - if (!nodetype_has_attributes(type(n))) - return; - /* if we have no list, we create one and quit */ - p = node_attr(n); - if (p == null) { /* add a new head \& node */ - p = get_node(attribute_node_size); - type(p) = attribute_list_node; - attr_list_ref(p) = 1; - node_attr(n) = p; - p = new_attribute_node((unsigned) i, val); - vlink(node_attr(n)) = p; - return; - } - /* we check if we have this attribute already and quit if the value stays the same */ - if (vlink(p) != null) { - while (vlink(p) != null) { - int t = attribute_id(vlink(p)); - if (t == i && attribute_value(vlink(p)) == val) - return; - if (t >= i) - break; - j++; - p = vlink(p); - } - /* j has now the position (if found) .. we assume a sorted list ! */ - p = node_attr(n); - - if (attr_list_ref(p) == 0 ) { - /* the list is invalid i.e. freed already */ - formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n); - /* the still dangling list gets ref count 1 */ - attr_list_ref(p) = 1; - } else if (attr_list_ref(p) == 1) { - /* this can really happen HH-LS */ - if (p == attr_list_cache) { - /* we can invalidate the cache setting */ - /* attr_list_cache = cache_disabled */ - /* or save the list, as done below */ - p = copy_attribute_list(p); - node_attr(n) = p; - /* the copied list gets ref count 1 */ - attr_list_ref(p) = 1; - } - } else { - /* the list is used multiple times so we make a copy */ - p = copy_attribute_list(p); - /* we decrement the ref count or the original */ - delete_attribute_ref(node_attr(n)); - node_attr(n) = p; - /* the copied list gets ref count 1 */ - attr_list_ref(p) = 1; - } - - - /* we go to position j in the list */ - while (j-- > 0) - p = vlink(p); - /* if we have a hit we just set the value otherwise we add a new node */ - if (attribute_id(vlink(p)) == i) { - attribute_value(vlink(p)) = val; - } else { /* add a new node */ - halfword r = new_attribute_node((unsigned) i, val); - vlink(r) = vlink(p); - vlink(p) = r; - } - } else { - normal_error("nodes","trying to set an attribute fails, case 2"); - } -} - -@ @c -int unset_attribute(halfword n, int i, int val) -{ - register halfword p; - register int t; - register int j = 0; - - if (!nodetype_has_attributes(type(n))) - return null; - p = node_attr(n); - if (p == null) - return UNUSED_ATTRIBUTE; - if (attr_list_ref(p) == 0) { - formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n); - return UNUSED_ATTRIBUTE; - } - if (vlink(p) != null) { - while (vlink(p) != null) { - t = attribute_id(vlink(p)); - if (t > i) - return UNUSED_ATTRIBUTE; - if (t == i) { - p = vlink(p); - break; - } - j++; - p = vlink(p); - } - if (attribute_id(p) != i) - return UNUSED_ATTRIBUTE; - /* if we are still here, the attribute exists */ - p = node_attr(n); - if (attr_list_ref(p) > 1 || p == attr_list_cache) { - halfword q = copy_attribute_list(p); - if (attr_list_ref(p) > 1) { - delete_attribute_ref(node_attr(n)); - } - attr_list_ref(q) = 1; - node_attr(n) = q; - } - p = vlink(node_attr(n)); - while (j-- > 0) - p = vlink(p); - t = attribute_value(p); - if (val == UNUSED_ATTRIBUTE || t == val) { - attribute_value(p) = UNUSED_ATTRIBUTE; - } - return t; - } else { - normal_error("nodes","trying to unset an attribute fails"); - return null; - } -} - -@ @c -int has_attribute(halfword n, int i, int val) -{ - register halfword p; - if (!nodetype_has_attributes(type(n))) - return UNUSED_ATTRIBUTE; - p = node_attr(n); - if (p == null || vlink(p) == null) - return UNUSED_ATTRIBUTE; - p = vlink(p); - while (p != null) { - if (attribute_id(p) == i) { - int ret = attribute_value(p); - if (val == UNUSED_ATTRIBUTE || val == ret) - return ret; - return UNUSED_ATTRIBUTE; - } else if (attribute_id(p) > i) { - return UNUSED_ATTRIBUTE; - } - p = vlink(p); - } - return UNUSED_ATTRIBUTE; -} - -@ @c -void print_short_node_contents(halfword p) -{ - switch (type(p)) { - case hlist_node: - case vlist_node: - case ins_node: - case whatsit_node: - case mark_node: - case adjust_node: - case unset_node: - print_char('['); - print_char(']'); - break; - case rule_node: - print_char('|'); - break; - case glue_node: - if (! glue_is_zero(p)) - print_char(' '); - break; - case math_node: - print_char('$'); - break; - case disc_node: - short_display(vlink(pre_break(p))); - short_display(vlink(post_break(p))); - break; - } -} - -@ @c -static void show_pdftex_whatsit_rule_spec(int p) -{ - tprint("("); - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); -} - -@ Each new type of node that appears in our data structure must be capable -of being displayed, copied, destroyed, and so on. The routines that we -need for write-oriented whatsits are somewhat like those for mark nodes; -other extensions might, of course, involve more subtlety here. - -@c -static void print_write_whatsit(const char *s, pointer p) -{ - tprint_esc(s); - if (write_stream(p) < 16) - print_int(write_stream(p)); - else if (write_stream(p) == 16) - print_char('*'); - else - print_char('-'); -} - -@ @c -static void show_node_wrapup_core(int p) -{ - switch (subtype(p)) { - case open_node: - print_write_whatsit("openout", p); - print_char('='); - print_file_name(open_name(p), open_area(p), open_ext(p)); - break; - case write_node: - print_write_whatsit("write", p); - print_mark(write_tokens(p)); - break; - case close_node: - print_write_whatsit("closeout", p); - break; - case special_node: - tprint_esc("special"); - print_mark(write_tokens(p)); - break; - case late_lua_node: - show_late_lua(p); - break; - case save_pos_node: - tprint_esc("savepos"); - break; - case user_defined_node: - tprint_esc("whatsit"); - print_int(user_node_id(p)); - print_char('='); - switch (user_node_type(p)) { - case 'a': - tprint("<>"); - break; - case 'n': - tprint("["); - show_node_list(user_node_value(p)); - tprint("]"); - break; - case 's': - print_char('"'); - print(user_node_value(p)); - print_char('"'); - break; - case 't': - print_mark(user_node_value(p)); - break; - default: /* only 'd' */ - print_int(user_node_value(p)); - break; - } - break; - } -} - -void show_node_wrapup_dvi(int p) -{ -} - -void show_node_wrapup_pdf(int p) -{ - switch (subtype(p)) { - case pdf_literal_node: - show_pdf_literal(p); - break; - case pdf_colorstack_node: - tprint_esc("pdfcolorstack "); - print_int(pdf_colorstack_stack(p)); - switch (pdf_colorstack_cmd(p)) { - case colorstack_set: - tprint(" set "); - break; - case colorstack_push: - tprint(" push "); - break; - case colorstack_pop: - tprint(" pop"); - break; - case colorstack_current: - tprint(" current"); - break; - default: - confusion("colorstack"); - break; - } - if (pdf_colorstack_cmd(p) <= colorstack_data) - print_mark(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - tprint_esc("pdfsetmatrix"); - print_mark(pdf_setmatrix_data(p)); - break; - case pdf_save_node: - tprint_esc("pdfsave"); - break; - case pdf_restore_node: - tprint_esc("pdfrestore"); - break; - case pdf_refobj_node: - tprint_esc("pdfrefobj"); - if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) { - if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { - tprint(" attr"); - lua_rawgeti(Luas, LUA_REGISTRYINDEX, - obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p))); - print_char(' '); - tprint((const char *) lua_tostring(Luas, -1)); - lua_pop(Luas, 1); - } - tprint(" stream"); - } - if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p))) - tprint(" file"); - if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { - lua_rawgeti(Luas, LUA_REGISTRYINDEX, - obj_obj_data(static_pdf, pdf_obj_objnum(p))); - print_char(' '); - tprint((const char *) lua_tostring(Luas, -1)); - lua_pop(Luas, 1); - } - break; - case pdf_annot_node: - tprint_esc("pdfannot"); - show_pdftex_whatsit_rule_spec(p); - print_mark(pdf_annot_data(p)); - break; - case pdf_start_link_node: - tprint_esc("pdfstartlink"); - show_pdftex_whatsit_rule_spec(p); - if (pdf_link_attr(p) != null) { - tprint(" attr"); - print_mark(pdf_link_attr(p)); - } - tprint(" action"); - if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) { - tprint(" user"); - print_mark(pdf_action_tokens(pdf_link_action(p))); - return; - } - if (pdf_action_file(pdf_link_action(p)) != null) { - tprint(" file"); - print_mark(pdf_action_file(pdf_link_action(p))); - } - switch (pdf_action_type(pdf_link_action(p))) { - case pdf_action_goto: - if (pdf_action_named_id(pdf_link_action(p)) > 0) { - tprint(" goto name"); - print_mark(pdf_action_id(pdf_link_action(p))); - } else { - tprint(" goto num"); - print_int(pdf_action_id(pdf_link_action(p))); - } - break; - case pdf_action_page: - tprint(" page"); - print_int(pdf_action_id(pdf_link_action(p))); - print_mark(pdf_action_tokens(pdf_link_action(p))); - break; - case pdf_action_thread: - if (pdf_action_named_id(pdf_link_action(p)) > 0) { - tprint(" thread name"); - print_mark(pdf_action_id(pdf_link_action(p))); - } else { - tprint(" thread num"); - print_int(pdf_action_id(pdf_link_action(p))); - } - break; - default: - normal_error("pdf backend", "unknown action type for link"); - break; - } - break; - case pdf_end_link_node: - tprint_esc("pdfendlink"); - break; - case pdf_dest_node: - tprint_esc("pdfdest"); - if (pdf_dest_named_id(p) > 0) { - tprint(" name"); - print_mark(pdf_dest_id(p)); - } else { - tprint(" num"); - print_int(pdf_dest_id(p)); - } - print_char(' '); - switch (pdf_dest_type(p)) { - case pdf_dest_xyz: - tprint("xyz"); - if (pdf_dest_xyz_zoom(p) != null) { - tprint(" zoom"); - print_int(pdf_dest_xyz_zoom(p)); - } - break; - case pdf_dest_fitbh: - tprint("fitbh"); - break; - case pdf_dest_fitbv: - tprint("fitbv"); - break; - case pdf_dest_fitb: - tprint("fitb"); - break; - case pdf_dest_fith: - tprint("fith"); - break; - case pdf_dest_fitv: - tprint("fitv"); - break; - case pdf_dest_fitr: - tprint("fitr"); - show_pdftex_whatsit_rule_spec(p); - break; - case pdf_dest_fit: - tprint("fit"); - break; - default: - tprint("unknown!"); - break; - } - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (subtype(p) == pdf_thread_node) - tprint_esc("pdfthread"); - else - tprint_esc("pdfstartthread"); - tprint("("); - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); - if (pdf_thread_attr(p) != null) { - tprint(" attr"); - print_mark(pdf_thread_attr(p)); - } - if (pdf_thread_named_id(p) > 0) { - tprint(" name"); - print_mark(pdf_thread_id(p)); - } else { - tprint(" num"); - print_int(pdf_thread_id(p)); - } - break; - case pdf_end_thread_node: - tprint_esc("pdfendthread"); - break; - default: - break; - } -} - -@ Now we are ready for |show_node_list| itself. This procedure has been - written to be ``extra robust'' in the sense that it should not crash or get - into a loop even if the data structures have been messed up by bugs in - the rest of the program. You can safely call its parent routine - |show_box(p)| for arbitrary values of |p| when you are debugging \TeX. - However, in the presence of bad data, the procedure may - fetch a |memory_word| whose variant is different from the way it was stored; - for example, it might try to read |mem[p].hh| when |mem[p]| - contains a scaled integer, if |p| is a pointer that has been - clobbered or chosen at random. - - -@ |str_room| need not be checked; see |show_box| - -@ Recursive calls on |show_node_list| therefore use the following pattern: -@c -#define node_list_display(A) do { \ - append_char('.'); \ - show_node_list(A); \ - flush_char(); \ -} while (0) - -#define node_list_display_x(A,B) do { \ - if ((B) != null) { \ - append_char('.'); \ - append_char(A); \ - append_char(' '); \ - show_node_list(B); \ - flush_char(); \ - flush_char(); \ - flush_char(); \ - } \ -} while (0) - -/* prints a node list symbolically */ - -void show_node_list(int p) -{ - int n = 0; /* the number of items already printed at this level */ - halfword w; - real g; /* a glue ratio, as a floating point number */ - if ((int) cur_length > depth_threshold) { - if (p > null) - tprint(" []"); /* indicate that there's been some truncation */ - return; - } - while (p != null) { - print_ln(); - print_current_string(); /* display the nesting history */ - if (tracing_online_par < -2) - print_int(p); - incr(n); - if (n > breadth_max) { /* time to stop */ - tprint("etc."); - return; - } - /* Display node |p| */ - if (is_char_node(p)) { - print_font_and_char(p); - if (is_ligature(p)) { - /* Display ligature |p|; */ - tprint(" (ligature "); - if (is_leftboundary(p)) - print_char('|'); - font_in_short_display = font(p); - short_display(lig_ptr(p)); - if (is_rightboundary(p)) - print_char('|'); - print_char(')'); - } - } else { - switch (type(p)) { - case hlist_node: - case vlist_node: - case unset_node: - /* Display box |p|; */ - if (type(p) == hlist_node) - tprint_esc("h"); - else if (type(p) == vlist_node) - tprint_esc("v"); - else - tprint_esc("unset"); - tprint("box("); - print_scaled(height(p)); - print_char('+'); - print_scaled(depth(p)); - tprint(")x"); - print_scaled(width(p)); - if (type(p) == unset_node) { - /* Display special fields of the unset node |p|; */ - if (span_count(p) != min_quarterword) { - tprint(" ("); - print_int(span_count(p) + 1); - tprint(" columns)"); - } - if (glue_stretch(p) != 0) { - tprint(", stretch "); - print_glue(glue_stretch(p), glue_order(p), NULL); - } - if (glue_shrink(p) != 0) { - tprint(", shrink "); - print_glue(glue_shrink(p), glue_sign(p), NULL); - } - } else { - /* Display the value of |glue_set(p)| */ - /* The code will have to change in this place if |glue_ratio| is - a structured type instead of an ordinary |real|. Note that this routine - should avoid arithmetic errors even if the |glue_set| field holds an - arbitrary random value. The following code assumes that a properly - formed nonzero |real| number has absolute value $2^{20}$ or more when - it is regarded as an integer; this precaution was adequate to prevent - floating point underflow on the author's computer. - */ - - g = (real) (glue_set(p)); - if ((g != 0.0) && (glue_sign(p) != normal)) { - tprint(", glue set "); - if (glue_sign(p) == shrinking) - tprint("- "); - if (g > 20000.0 || g < -20000.0) { - if (g > 0.0) - print_char('>'); - else - tprint("< -"); - print_glue(20000 * unity, glue_order(p), NULL); - } else { - print_glue(round(unity * g), glue_order(p), NULL); - } - } - - if (shift_amount(p) != 0) { - tprint(", shifted "); - print_scaled(shift_amount(p)); - } - tprint(", direction "); - print_dir(box_dir(p)); - } - node_list_display(list_ptr(p)); /* recursive call */ - break; - case rule_node: - /* Display rule |p|; */ - if (subtype(p) == normal_rule) { - tprint_esc("rule("); - } else if (subtype(p) == empty_rule) { - tprint_esc("norule("); - } else if (subtype(p) == user_rule) { - tprint_esc("userrule("); - } else if (subtype(p) == box_rule) { - tprint_esc("box("); - } else if (subtype(p) == image_rule) { - tprint_esc("image("); - } - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); - break; - case ins_node: - /* Display insertion |p|; */ - tprint_esc("insert"); - print_int(subtype(p)); - tprint(", natural size "); - print_scaled(height(p)); - tprint("; split("); - print_spec(split_top_ptr(p), NULL); - print_char(','); - print_scaled(depth(p)); - tprint("); float cost "); - print_int(float_cost(p)); - node_list_display(ins_ptr(p)); /* recursive call */ - break; - case dir_node: - if (dir_dir(p) < 0) { - tprint_esc("enddir"); - print_char(' '); - print_dir(dir_dir(p) + dir_swap); - } else { - tprint_esc("begindir"); - print_char(' '); - print_dir(dir_dir(p)); - } - break; - case local_par_node: - tprint_esc("localpar"); - append_char('.'); - print_ln(); - print_current_string(); - tprint_esc("localinterlinepenalty"); - print_char('='); - print_int(local_pen_inter(p)); - print_ln(); - print_current_string(); - tprint_esc("localbrokenpenalty"); - print_char('='); - print_int(local_pen_broken(p)); - print_ln(); - print_current_string(); - tprint_esc("localleftbox"); - if (local_box_left(p) == null) { - tprint("=null"); - } else { - append_char('.'); - show_node_list(local_box_left(p)); - decr(cur_length); - } - print_ln(); - print_current_string(); - tprint_esc("localrightbox"); - if (local_box_right(p) == null) { - tprint("=null"); - } else { - append_char('.'); - show_node_list(local_box_right(p)); - decr(cur_length); - } - decr(cur_length); - break; - case boundary_node: - if (subtype(p)==0) { - tprint_esc("noboundary"); - } else { - switch (subtype(p)) { - case 1: - tprint_esc("boundary"); - break; - case 2: - tprint_esc("protrusionboundary"); - break; - case 3: - tprint_esc("wordboundary"); - break; - default: - tprint_esc("boundary"); - print_char(':'); - print_int(subtype(p)); - break; - } - print_char('='); - print_int(boundary_value(p)); - } - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - show_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - show_node_wrapup_dvi(p); - } else { - show_node_wrapup_core(p); - } - break; - case glue_node: - /* Display glue |p|; */ - if (subtype(p) >= a_leaders) { - /* Display leaders |p|; */ - tprint_esc(""); - switch (subtype(p)) { - case a_leaders: - break; - case c_leaders: - print_char('c'); - break; - case x_leaders: - print_char('x'); - break; - case g_leaders: - print_char('g'); - break; - default: - normal_warning("nodes","weird glue leader subtype ignored"); - } - tprint("leaders "); - print_spec(p, NULL); - node_list_display(leader_ptr(p)); /* recursive call */ - } else { - tprint_esc("glue"); - if (subtype(p) != normal) { - print_char('('); - if ((subtype(p) - 1) < thin_mu_skip_code) { - print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1)); - } else if (subtype(p) < cond_math_glue) { - print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1)); - } else if (subtype(p) == cond_math_glue) { - tprint_esc("nonscript"); - } else { - tprint_esc("mskip"); - } - print_char(')'); - } - if (subtype(p) != cond_math_glue) { - print_char(' '); - if (subtype(p) < cond_math_glue) - print_spec(p, NULL); - else - print_spec(p, "mu"); - } - } - break; - case margin_kern_node: - tprint_esc("kern"); - print_scaled(width(p)); - if (subtype(p) == left_side) - tprint(" (left margin)"); - else - tprint(" (right margin)"); - break; - case kern_node: - /* Display kern |p|; */ - /* An ``explicit'' kern value is indicated implicitly by an explicit space. */ - if (subtype(p) != mu_glue) { - tprint_esc("kern"); - /* - if (subtype(p) != normal) - print_char(' '); - */ - print_scaled(width(p)); - if (subtype(p) == font_kern) - tprint(" (font)"); - else if (subtype(p) == italic_kern) - tprint(" (italic)"); - else if (subtype(p) == accent_kern) - tprint(" (accent)"); - } else { - tprint_esc("mkern"); - print_scaled(width(p)); - tprint("mu"); - } - break; - case math_node: - /* Display math node |p|; */ - tprint_esc("math"); - if (subtype(p) == before) - tprint("on"); - else - tprint("off"); - if (!glue_is_zero(p)) { - tprint(", glued "); - print_spec(p, NULL); - } else if (surround(p) != 0) { - tprint(", surrounded "); - print_scaled(surround(p)); - } - break; - case penalty_node: - /* Display penalty |p|; */ - tprint_esc("penalty "); - print_int(penalty(p)); - break; - case disc_node: - /* Display discretionary |p|; */ - /* The |post_break| list of a discretionary node is indicated by a prefixed - `\.{\char'174}' instead of the `\..' before the |pre_break| list. */ - /* We're not compatible anyway so ... - tprint_esc("discretionary"); - print_int(disc_penalty(p)); - print_char('|'); - if (vlink(no_break(p)) != null) { - tprint(" replacing "); - node_list_display(vlink(no_break(p))); - } - node_list_display(vlink(pre_break(p))); - append_char('|'); - show_node_list(vlink(post_break(p))); - flush_char(); - */ - tprint_esc("discretionary"); - tprint(" (penalty "); - print_int(disc_penalty(p)); - print_char(')'); - node_list_display_x('<',vlink(pre_break(p))); - node_list_display_x('>',vlink(post_break(p))); - node_list_display_x('=',vlink(no_break(p))); - break; - case mark_node: - /* Display mark |p|; */ - tprint_esc("mark"); - if (mark_class(p) != 0) { - print_char('s'); - print_int(mark_class(p)); - } - print_mark(mark_ptr(p)); - break; - case adjust_node: - /* Display adjustment |p|; */ - tprint_esc("vadjust"); - if (subtype(p) != 0) - tprint(" pre "); - node_list_display(adjust_ptr(p)); /* recursive call */ - break; - case glue_spec_node: - tprint("<glue_spec "); - print_spec(p, NULL); - tprint(">"); - break; - default: - show_math_node(p); - break; - } - } - p = vlink(p); - } -} - -@ This routine finds the 'base' width of a horizontal box, using the same logic - that \TeX82 used for \.{\\predisplaywidth} */ - -@c -static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width) -{ - scaled d; /* increment to |v| */ - scaled w = -max_dimen; /* calculated |size| */ - scaled v = initial_width; /* |w| plus possible glue amount */ - while (p != null) { - if (is_char_node(p)) { - d = glyph_width(p); - goto FOUND; - } - switch (type(p)) { - case hlist_node: - case vlist_node: - case rule_node: - d = width(p); - goto FOUND; - break; - case margin_kern_node: - d = width(p); - break; - case kern_node: - d = width(p); - break; - case disc_node: - /* at the end of the line we should actually take the pre */ - if (no_break(p) != null) { - d = get_actual_box_width(r,vlink_no_break(p),0); - if (d <= -max_dimen || d >= max_dimen) { - d = 0; - } - } else { - d = 0; - } - goto FOUND; - break; - case math_node: - /* begin mathskip code */ - if (glue_is_zero(p)) { - d = surround(p); - break; - } else { - /* fall through */ - } - /* end mathskip code */ - case glue_node: - /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set| - values, since such values are subject to system-dependent rounding. - System-dependent numbers are not allowed to infiltrate parameters like - |pre_display_size|, since \TeX82 is supposed to make the same decisions on all - machines. - */ - d = width(p); - if (glue_sign(r) == stretching) { - if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0)) - v = max_dimen; - } else if (glue_sign(r) == shrinking) { - if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0)) - v = max_dimen; - } - if (subtype(p) >= a_leaders) - goto FOUND; - break; - default: - d = 0; - break; - } - if (v < max_dimen) - v = v + d; - goto NOT_FOUND; - FOUND: - if (v < max_dimen) { - v = v + d; - w = v; - } else { - w = max_dimen; - break; - } - NOT_FOUND: - p = vlink(p); - } - return w; -} - -pointer actual_box_width(pointer r, scaled base_width) -{ - /* often this is the same as: - return + shift_amount(r) + base_width + - natural_sizes(list_ptr(r),null,(glue_ratio) glue_set(r),glue_sign(r),glue_order(r),box_dir(r)); - */ - return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width); -} - -@ @c -halfword tail_of_list(halfword p) -{ - halfword q = p; - while (vlink(q) != null) - q = vlink(q); - return q; -} - - - -@ @c -int var_used; - -@ Attribute lists need two extra globals to increase processing efficiency. -|max_used_attr| limits the test loop that checks for set attributes, and -|attr_list_cache| contains a pointer to an already created attribute list. It is -set to the special value |cache_disabled| when the current value can no longer be -trusted: after an assignment to an attribute register, and after a group has -ended. - -@c -int max_used_attr; /* maximum assigned attribute id */ -halfword attr_list_cache; - -@ From the computer's standpoint, \TeX's chief mission is to create -horizontal and vertical lists. We shall now investigate how the elements -of these lists are represented internally as nodes in the dynamic memory. - -A horizontal or vertical list is linked together by |link| fields in -the first word of each node. Individual nodes represent boxes, glue, -penalties, or special things like discretionary hyphens; because of this -variety, some nodes are longer than others, and we must distinguish different -kinds of nodes. We do this by putting a `|type|' field in the first word, -together with the link and an optional `|subtype|'. - -@ Character nodes appear only in horizontal lists, never in vertical lists. - -An |hlist_node| stands for a box that was made from a horizontal list. -Each |hlist_node| is seven words long, and contains the following fields -(in addition to the mandatory |type| and |link|, which we shall not -mention explicitly when discussing the other node types): The |height| and -|width| and |depth| are scaled integers denoting the dimensions of the -box. There is also a |shift_amount| field, a scaled integer indicating -how much this box should be lowered (if it appears in a horizontal list), -or how much it should be moved to the right (if it appears in a vertical -list). There is a |list_ptr| field, which points to the beginning of the -list from which this box was fabricated; if |list_ptr| is |null|, the box -is empty. Finally, there are three fields that represent the setting of -the glue: |glue_set(p)| is a word of type |glue_ratio| that represents -the proportionality constant for glue setting; |glue_sign(p)| is -|stretching| or |shrinking| or |normal| depending on whether or not the -glue should stretch or shrink or remain rigid; and |glue_order(p)| -specifies the order of infinity to which glue setting applies (|normal|, -|sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used. - -@ The |new_null_box| function returns a pointer to an |hlist_node| in -which all subfields have the values corresponding to `\.{\\hbox\{\}}'. -The |subtype| field is set to |min_quarterword|, since that's the desired -|span_count| value if this |hlist_node| is changed to an |unset_node|. - -@c -halfword new_null_box(void) -{ /* creates a new box node */ - halfword p = new_node(hlist_node, min_quarterword); - box_dir(p) = text_direction_par; - return p; -} - -@ A |vlist_node| is like an |hlist_node| in all respects except that it -contains a vertical list. - -@ A |rule_node| stands for a solid black rectangle; it has |width|, -|depth|, and |height| fields just as in an |hlist_node|. However, if -any of these dimensions is $-2^{30}$, the actual value will be determined -by running the rule up to the boundary of the innermost enclosing box. -This is called a ``running dimension.'' The |width| is never running in -an hlist; the |height| and |depth| are never running in a~vlist. - -@ A new rule node is delivered by the |new_rule| function. It -makes all the dimensions ``running,'' so you have to change the -ones that are not allowed to run. - -@c -halfword new_rule(int s) -{ - halfword p = new_node(rule_node,s); - return p; -} - -@ Insertions are represented by |ins_node| records, where the |subtype| -indicates the corresponding box number. For example, `\.{\\insert 250}' -leads to an |ins_node| whose |subtype| is |250+min_quarterword|. -The |height| field of an |ins_node| is slightly misnamed; it actually holds -the natural height plus depth of the vertical list being inserted. -The |depth| field holds the |split_max_depth| to be used in case this -insertion is split, and the |split_top_ptr| points to the corresponding -|split_top_skip|. The |float_cost| field holds the |floating_penalty| that -will be used if this insertion floats to a subsequent page after a -split insertion of the same class. There is one more field, the -|ins_ptr|, which points to the beginning of the vlist for the insertion. - -@ A |mark_node| has a |mark_ptr| field that points to the reference count -of a token list that contains the user's \.{\\mark} text. -In addition there is a |mark_class| field that contains the mark class. - -@ An |adjust_node|, which occurs only in horizontal lists, -specifies material that will be moved out into the surrounding -vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}' -operation. The |adjust_ptr| field points to the vlist containing this -material. - -@ A |glyph_node|, which occurs only in horizontal lists, specifies a -glyph in a particular font, along with its attribute list. Older -versions of \TeX\ could use token memory for characters, because the -font,char combination would fit in a single word (both values were -required to be strictly less than $2^{16}$). In LuaTeX, room is -needed for characters that are larger than that, as well as a pointer -to a potential attribute list, and the two displacement values. - -In turn, that made the node so large that it made sense to merge -ligature glyphs as well, as that requires only one extra pointer. A -few extra classes of glyph nodes will be introduced later. The -unification of all those types makes it easier to manipulate lists of -glyphs. The subtype differentiates various glyph kinds. - -First, here is a function that returns a pointer to a glyph node for a given -glyph in a given font. If that glyph doesn't exist, |null| is returned -instead. Nodes of this subtype are directly created only for accents -and their base (through |make_accent|), and math nucleus items (in the -conversion from |mlist| to |hlist|). - -@c -halfword new_glyph(int f, int c) -{ - halfword p = null; /* the new node */ - if ((f == 0) || (char_exists(f, c))) { - p = new_glyph_node(); - set_to_glyph(p); - font(p) = f; - character(p) = c; - } - return p; -} - -@ A subset of the glyphs nodes represent ligatures: characters -fabricated from the interaction of two or more actual characters. The -characters that generated the ligature have not been forgotten, since -they are needed for diagnostic messages; the |lig_ptr| field points to -a linked list of character nodes for all original characters that have -been deleted. (This list might be empty if the characters that -generated the ligature were retained in other nodes.) - -The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if -the original source of the ligature included implicit left and/or -right boundaries. These nodes are created by the C function |new_ligkern|. - -A third general type of glyphs could be called a character, as it -only appears in lists that are not yet processed by the ligaturing and -kerning steps of the program. - -|main_control| inserts these, and they are later converted to -|subtype_normal| by |new_ligkern|. - -@c -quarterword norm_min(int h) -{ - if (h <= 0) - return 1; - else if (h >= 255) - return 255; - else - return (quarterword) h; -} - -halfword new_char(int f, int c) -{ - halfword p; /* the new node */ - p = new_glyph_node(); - set_to_character(p); - font(p) = f; - character(p) = c; - lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par); - return p; -} - -@ Left and right ghost glyph nodes are the result of \.{\\leftghost} -and \.{\\rightghost}, respectively. They are going to be removed by -|new_ligkern|, at the end of which they are no longer needed. - -@ Here are a few handy helpers used by the list output routines. - -@c -scaled glyph_width(halfword p) -{ - scaled w = char_width(font(p), character(p)); - return w; -} - -scaled glyph_height(halfword p) -{ - scaled w = char_height(font(p), character(p)) + y_displace(p); - if (w < 0) - w = 0; - return w; -} - -scaled glyph_depth(halfword p) -{ - scaled w = char_depth(font(p), character(p)); - if (y_displace(p) > 0) - w = w - y_displace(p); - if (w < 0) - w = 0; - return w; -} - -@ A |disc_node|, which occurs only in horizontal lists, specifies a -``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text -that starts at |pre_break(p)| will precede the break, the text that starts at -|post_break(p)| will follow the break, and text that appears in -|no_break(p)| nodes will be ignored. For example, an ordinary -discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with -|pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|, -and |no_break=null|. - -{TODO: Knuth said: All three of the discretionary texts must be lists -that consist entirely of character, kern, box and rule nodes.} - -If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this -break. Otherwise the |hyphen_penalty| will be charged. The texts will -actually be substituted into the list by the line-breaking algorithm if it -decides to make the break, and the discretionary node will disappear at -that time; thus, the output routine sees only discretionaries that were -not chosen. - -@c -halfword new_disc(void) -{ /* creates an empty |disc_node| */ - halfword p = new_node(disc_node, 0); - disc_penalty(p) = hyphen_penalty_par; - return p; -} - -@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The -|subtype| field in its first word says what `\\{whatsit}' it is, and -implicitly determines the node size (which must be 2 or more) and the -format of the remaining words. When a |whatsit_node| is encountered -in a list, special actions are invoked; knowledgeable people who are -careful not to mess up the rest of \TeX\ are able to make \TeX\ do new -things by adding code at the end of the program. For example, there -might be a `\TeX nicolor' extension to specify different colors of ink, -@^extensions to \TeX@> -and the whatsit node might contain the desired parameters. - -The present implementation of \TeX\ treats the features associated with -`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to -illustrate how such routines might be coded. We shall defer further -discussion of extensions until the end of this program. - -@ A |math_node|, which occurs only in horizontal lists, appears before and -after mathematical formulas. The |subtype| field is |before| before the -formula and |after| after it. There is a |surround| field, which represents -the amount of surrounding space inserted by \.{\\mathsurround}. - -@c -halfword new_math(scaled w, int s) -{ - halfword p = new_node(math_node, s); - surround(p) = w; - return p; -} - -@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, -|rule_node|, |ins_node|, |mark_node|, |adjust_node|, -|disc_node|, |whatsit_node|, and |math_node| are at the low end of the -type codes, by permitting a break at glue in a list if and only if the -|type| of the previous node is less than |math_node|. Furthermore, a -node is discarded after a break if its type is |math_node| or~more. - -@ A |glue_node| represents glue in a list. However, it is really only -a pointer to a separate glue specification, since \TeX\ makes use of the -fact that many essentially identical nodes of glue are usually present. -If |p| points to a |glue_node|, |glue_ptr(p)| points to -another packet of words that specify the stretch and shrink components, etc. - -Glue nodes also serve to represent leaders; the |subtype| is used to -distinguish between ordinary glue (which is called |normal|) and the three -kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|). -The |leader_ptr| field points to a rule node or to a box node containing the -leaders; it is set to |null| in ordinary glue nodes. - -Many kinds of glue are computed from \TeX's ``skip'' parameters, and -it is helpful to know which parameter has led to a particular glue node. -Therefore the |subtype| is set to indicate the source of glue, whenever -it originated as a parameter. We will be defining symbolic names for the -parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|, -etc.); it suffices for now to say that the |subtype| of parametric glue -will be the same as the parameter number, plus~one. - -@ In math formulas there are two more possibilities for the |subtype| in a -glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu} -instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}' -feature that cancels the glue node immediately following if it appears -in a subscript. - -@ A glue specification has a halfword reference count in its first word, -@^reference counts@> -representing |null| plus the number of glue nodes that point to it (less one). -Note that the reference count appears in the same position as -the |link| field in list nodes; this is the field that is initialized -to |null| when a node is allocated, and it is also the field that is flagged -by |empty_flag| in empty nodes. - -Glue specifications also contain three |scaled| fields, for the |width|, -|stretch|, and |shrink| dimensions. Finally, there are two one-byte -fields called |stretch_order| and |shrink_order|; these contain the -orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|) -corresponding to the stretch and shrink values. - -@ Here is a function that returns a pointer to a copy of a glue spec. -The reference count in the copy is |null|, because there is assumed -to be exactly one reference to the new specification. - -@c -halfword new_spec(halfword q) /* safeguard for copying a glue node */ -{ - if (q == null) { - return copy_node(zero_glue); - } else if (type(q) == glue_spec_node) { - return copy_node(q); - } else if (type(q) == glue_node) { - halfword p = copy_node(zero_glue); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; - } else { - /* alternatively we can issue a warning */ - return copy_node(zero_glue); - } -} - -@ And here's a function that creates a glue node for a given parameter -identified by its code number; for example, -|new_param_glue(line_skip_code)| returns a pointer to a glue node for the -current \.{\\lineskip}. - -@c -halfword new_param_glue(int n) -{ - halfword p = new_node(glue_node, n + 1); - halfword q = glue_par(n); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; -} - -@ Glue nodes that are more or less anonymous are created by |new_glue|, -whose argument points to a glue specification. - -@c -halfword new_glue(halfword q) -{ - halfword p = new_node(glue_node, normal); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; -} - -@ Still another subroutine is needed: This one is sort of a combination -of |new_param_glue| and |new_glue|. It creates a glue node for one of -the current glue parameters, but it makes a fresh copy of the glue -specification, since that specification will probably be subject to change, -while the parameter will stay put. - -/* - The global variable |temp_ptr| is set to the address of the new spec. -*/ - -@c -halfword new_skip_param(int n) -{ - halfword p = new_node(glue_node, n + 1); - halfword q = glue_par(n); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; -} - -@ A |kern_node| has a |width| field to specify a (normally negative) -amount of spacing. This spacing correction appears in horizontal lists -between letters like A and V when the font designer said that it looks -better to move them closer together or further apart. A kern node can -also appear in a vertical list, when its `|width|' denotes additional -spacing in the vertical direction. The |subtype| is either |normal| (for -kerns inserted from font information or math mode calculations) or |explicit| -(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern| -(for kerns inserted from non-math accents) or |mu_glue| (for kerns -inserted from \.{\\mkern} specifications in math formulas). - -@ The |new_kern| function creates a kern node having a given width. - -@c -halfword new_kern(scaled w) -{ - halfword p = new_node(kern_node, normal); - width(p) = w; - return p; -} - -@ A |penalty_node| specifies the penalty associated with line or page -breaking, in its |penalty| field. This field is a fullword integer, but -the full range of integer values is not used: Any penalty |>=10000| is -treated as infinity, and no break will be allowed for such high values. -Similarly, any penalty |<=-10000| is treated as negative infinity, and a -break will be forced. - -@ Anyone who has been reading the last few sections of the program will -be able to guess what comes next. - -@c -halfword new_penalty(int m, int s) -{ - halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */ - penalty(p) = m; - subtype(p) = s; - return p; -} - -@ You might think that we have introduced enough node types by now. Well, -almost, but there is one more: An |unset_node| has nearly the same format -as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} -or \.{\\valign} that are not yet in their final form, since the box -dimensions are their ``natural'' sizes before any glue adjustment has been -made. The |glue_set| word is not present; instead, we have a |glue_stretch| -field, which contains the total stretch of order |glue_order| that is -present in the hlist or vlist being boxed. -Similarly, the |shift_amount| field is replaced by a |glue_shrink| field, -containing the total shrink of order |glue_sign| that is present. -The |subtype| field is called |span_count|; an unset box typically -contains the data for |qo(span_count)+1| columns. -Unset nodes will be changed to box nodes when alignment is completed. - -In fact, there are still more types coming. When we get to math formula -processing we will see that a |style_node| has |type=14|; and a number -of larger type codes will also be defined, for use in math mode only. - -Warning: If any changes are made to these data structure layouts, such as -changing any of the node sizes or even reordering the words of nodes, -the |copy_node_list| procedure and the memory initialization code -below may have to be changed. Such potentially dangerous parts of the -program are listed in the index under `data structure assumptions'. -@!@^data structure assumptions@> -However, other references to the nodes are made symbolically in terms of -the \.{WEB} macro definitions above, so that format changes will leave -\TeX's other algorithms intact. -@^system dependencies@> - -@ This function creates a |local_paragraph| node - -@c - -halfword make_local_par_node(int mode) -{ - int callback_id; - halfword q; - halfword p = new_node(local_par_node,0); - local_pen_inter(p) = local_inter_line_penalty_par; - local_pen_broken(p) = local_broken_penalty_par; - if (local_left_box_par != null) { - q = copy_node_list(local_left_box_par); - local_box_left(p) = q; - local_box_left_width(p) = width(local_left_box_par); - } - if (local_right_box_par != null) { - q = copy_node_list(local_right_box_par); - local_box_right(p) = q; - local_box_right_width(p) = width(local_right_box_par); - } - local_par_dir(p) = par_direction_par; - /* callback with node passed */ - callback_id = callback_defined(insert_local_par_callback); - if (callback_id > 0) { - int sfix = lua_gettop(Luas); - if (!get_callback(Luas, callback_id)) { - lua_settop(Luas, sfix); - return p; - } - nodelist_to_lua(Luas, p); - lua_push_local_par_mode(Luas,mode) - if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */ - char errmsg[256]; /* temp hack ... we will have a formatted error */ - snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1)); - errmsg[255]='\0'; - lua_settop(Luas, sfix); - normal_error("insert_local_par",errmsg); /* to be done */ - return p; - } - lua_settop(Luas, sfix); - } - /* done */ - return p; -} diff --git a/Build/source/texk/web2c/luatexdir/tex/textcodes.w b/Build/source/texk/web2c/luatexdir/tex/textcodes.c index 89e93684132..a6a0b3a55e3 100644 --- a/Build/source/texk/web2c/luatexdir/tex/textcodes.w +++ b/Build/source/texk/web2c/luatexdir/tex/textcodes.c @@ -1,27 +1,35 @@ -% textcodes.w -% -% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* + +textcodes.w + +Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> + +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your +option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +License for more details. + +You should have received a copy of the GNU General Public License along +with LuaTeX; if not, see <http://www.gnu.org/licenses/>. + +*/ #include "ptexlib.h" -@ catcodes @c +/*tex + +Contrary to traditional \TEX\ we have catcode tables so that we can switch +catcode regimes very fast. We can have many such regimes and they're stored in +trees. + +*/ #define CATCODESTACK 8 #define CATCODEDEFAULT 12 @@ -174,7 +182,11 @@ static void freecatcodes(void) xfree(catcode_valid); } -@ lccodes @c +/*tex + +The lowercase mapping codes are also stored in a tree. + +*/ #define LCCODESTACK 8 #define LCCODEDEFAULT 0 @@ -220,7 +232,11 @@ static void freelccodes(void) destroy_sa_tree(lccode_head); } -@ uccodes @c +/*tex + +And the uppercase mapping codes are again stored in a tree. + +*/ #define UCCODESTACK 8 #define UCCODEDEFAULT 0 @@ -266,7 +282,11 @@ static void freeuccodes(void) destroy_sa_tree(uccode_head); } -@ sfcodes @c +/*tex + +By now it will be no surprise that the space factors get stored in a tree. + +*/ #define SFCODESTACK 8 #define SFCODEDEFAULT 1000 @@ -312,19 +332,29 @@ static void freesfcodes(void) destroy_sa_tree(sfcode_head); } -@ hjcodes @c +/*tex + +The hyphenation codes are indeed stored in a tree and are used instead of +lowercase codes when deciding what characters to take into acccount when +hyphenating. They are bound to upto |HJCODE_MAX| languages. + +*/ #define HJCODESTACK 8 #define HJCODEDEFAULT 0 -#define HJCODE_MAX 16383 /* number of languages */ +#define HJCODE_MAX 16383 static sa_tree *hjcode_heads = NULL; static int hjcode_max = 0; static unsigned char *hjcode_valid = NULL; -@ Here we set codes but we don't initialize from lccodes. +/*tex + +Here we set codes but we don't initialize from lccodes. + +*/ -@c void set_hj_code(int h, int n, halfword v, quarterword gl) +void set_hj_code(int h, int n, halfword v, quarterword gl) { sa_tree_item sa_value = { 0 }; sa_tree s = hjcode_heads[h]; @@ -339,9 +369,13 @@ static unsigned char *hjcode_valid = NULL; set_sa_item(s, n, sa_value, gl); } -@ We just return the lccodes when nothing is set. +/*tex -@c halfword get_hj_code(int h, int n) +We just return the lccodes when nothing is set. + +*/ + +halfword get_hj_code(int h, int n) { sa_tree s = hjcode_heads[h]; if (s == NULL) { @@ -350,12 +384,14 @@ static unsigned char *hjcode_valid = NULL; return (halfword) get_sa_item(s, n).int_value; } -@ We don't restore as we can have more languages in a paragraph -and hyphenation takes place at a later stage so we would get -weird grouping side effects .. so, one can overload settings -but management is then upto the used, so no: +/*tex + +We don't restore as we can have more languages in a paragraph and hyphenation +takes place at a later stage so we would get weird grouping side effects .. so, +one can overload settings but management is then upto the used, so no: + +*/ -@c /* static void unsavehjcodes(quarterword gl) { } */ @@ -444,7 +480,11 @@ static void freehjcodes(void) xfree(hjcode_valid); } -/* management */ +/*tex + +The public management functions. + +*/ void unsave_text_codes(quarterword grouplevel) { diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.w b/Build/source/texk/web2c/luatexdir/tex/textoken.c index d58aa3dc49b..6056bba2517 100644 --- a/Build/source/texk/web2c/luatexdir/tex/textoken.w +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.c @@ -1,40 +1,26 @@ -% textoken.w -% -% Copyright 2006-2011 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c +/* -#include "ptexlib.h" +Copyright 2006-2011 Taco Hoekwater <taco@luatex.org> -@ @c -#define detokenized_line() (line_catcode_table==NO_CAT_TABLE) +This file is part of LuaTeX. + +LuaTeX is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License as published by the Free Software Foundation; +either version 2 of the License, or (at your option) any later version. + +LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + +You should have received a copy of the GNU General Public License along with +LuaTeX; if not, see <http://www.gnu.org/licenses/>. -/* -#define do_get_cat_code(a,b) do { \ - if (line_catcode_table<=-0xFF) \ - a= - line_catcode_table - 0xFF ; \ - else if (line_catcode_table!=DEFAULT_CAT_TABLE) \ - a=get_cat_code(line_catcode_table,b); \ - else \ - a=get_cat_code(cat_code_table_par,b); \ - } while (0) */ +#include "ptexlib.h" + +#define detokenized_line() (line_catcode_table==NO_CAT_TABLE) + #define do_get_cat_code(a,b) do { \ if (line_catcode_table==DEFAULT_CAT_TABLE) \ a=get_cat_code(cat_code_table_par,b); \ @@ -45,47 +31,85 @@ } while (0) -@ The \TeX\ system does nearly all of its own memory allocation, so that it can -readily be transported into environments that do not have automatic facilities -for strings, garbage collection, etc., and so that it can be in control of what -error messages the user receives. The dynamic storage requirements of \TeX\ are -handled by providing two large arrays called |fixmem| and |varmem| in which -consecutive blocks of words are used as nodes by the \TeX\ routines. +/*tex + + The \TeX\ system does nearly all of its own memory allocation, so that it can + readily be transported into environments that do not have automatic + facilities for strings, garbage collection, etc., and so that it can be in + control of what error messages the user receives. The dynamic storage + requirements of \TeX\ are handled by providing two large arrays called + |fixmem| and |varmem| in which consecutive blocks of words are used as nodes + by the \TeX\ routines. + + Pointer variables are indices into this array, or into another array called + |eqtb| that will be explained later. A pointer variable might also be a + special flag that lies outside the bounds of |mem|, so we allow pointers to + assume any |halfword| value. The minimum halfword value represents a null + pointer. \TeX\ does not assume that |mem[null]| exists. + + Locations in |fixmem| are used for storing one-word records; a conventional + \.{AVAIL} stack is used for allocation in this array. + +*/ + +/*tex the big dynamic storage area */ + +smemory_word *fixmem; + +/*tex the smallest location of one-word memory in use */ -Pointer variables are indices into this array, or into another array called -|eqtb| that will be explained later. A pointer variable might also be a special -flag that lies outside the bounds of |mem|, so we allow pointers to assume any -|halfword| value. The minimum halfword value represents a null pointer. \TeX\ -does not assume that |mem[null]| exists. +unsigned fix_mem_min; -@ Locations in |fixmem| are used for storing one-word records; a conventional -\.{AVAIL} stack is used for allocation in this array. +/*tex the largest location of one-word memory in use */ -@c -smemory_word *fixmem; /* the big dynamic storage area */ -unsigned fix_mem_min; /* the smallest location of one-word memory in use */ -unsigned fix_mem_max; /* the largest location of one-word memory in use */ +unsigned fix_mem_max; -@ In order to study the memory requirements of particular applications, it is -possible to prepare a version of \TeX\ that keeps track of current and maximum -memory usage. When code between the delimiters |@!stat| $\ldots$ |tats| is not -commented out, \TeX\ will run a bit slower but it will report these statistics -when |tracing_stats| is sufficiently large. +/*tex + + In order to study the memory requirements of particular applications, it is + possible to prepare a version of \TeX\ that keeps track of current and + maximum memory usage. When code between the delimiters |stat| $\ldots$ + |tats| is not commented out, \TeX\ will run a bit slower but it will report + these statistics when |tracing_stats| is sufficiently large. + +*/ -@c -int var_used, dyn_used; /* how much memory is in use */ +/*tex how much memory is in use */ -halfword avail; /* head of the list of available one-word nodes */ -unsigned fix_mem_end; /* the last one-word node used in |mem| */ +int var_used, dyn_used; -halfword garbage; /* head of a junk list, write only */ -halfword temp_token_head; /* head of a temporary list of some kind */ -halfword hold_token_head; /* head of a temporary list of another kind */ -halfword omit_template; /* a constant token list */ -halfword null_list; /* permanently empty list */ -halfword backup_head; /* head of token list built by |scan_keyword| */ +/*tex head of the list of available one-word nodes */ + +halfword avail; + +/*tex the last one-word node used in |mem| */ + +unsigned fix_mem_end; + +/*tex head of a junk list, write only */ + +halfword garbage; + +/*tex head of a temporary list of some kind */ + +halfword temp_token_head; + +/*tex head of a temporary list of another kind */ + +halfword hold_token_head; + +/*tex a constant token list */ + +halfword omit_template; + +/*tex permanently empty list */ + +halfword null_list; + +/*tex head of token list built by |scan_keyword| */ + +halfword backup_head; -@ @c void initialize_tokens(void) { halfword p; @@ -109,37 +133,44 @@ void initialize_tokens(void) p = get_avail(); garbage = p; set_token_info(garbage, 0); - dyn_used = 0; /* initialize statistics */ + dyn_used = 0; } -@ The function |get_avail| returns a pointer to a new one-word node whose |link| -field is null. However, \TeX\ will halt if there is no more room left. -@^inner loop@> +/*tex + + The function |get_avail| returns a pointer to a new one-word node whose + |link| field is null. However, \TeX\ will halt if there is no more room left. -If the available-space list is empty, i.e., if |avail=null|, we try first to -increase |fix_mem_end|. If that cannot be done, i.e., if -|fix_mem_end=fix_mem_max|, we try to reallocate array |fixmem|. If, that doesn't -work, we have to quit. + If the available-space list is empty, i.e., if |avail=null|, we try first to + increase |fix_mem_end|. If that cannot be done, i.e., if + |fix_mem_end=fix_mem_max|, we try to reallocate array |fixmem|. If, that + doesn't work, we have to quit. + + Single-word node allocation: +*/ -@c halfword get_avail(void) -{ /* single-word node allocation */ - unsigned p; /* the new node being got */ +{ + /*tex The new node being got: */ + unsigned p; unsigned t; - p = (unsigned) avail; /* get top location in the |avail| stack */ + /*tex Get top location in the |avail| stack. */ + p = (unsigned) avail; if (p != null) { - avail = token_link(avail); /* and pop it off */ - } else if (fix_mem_end < fix_mem_max) { /* or go into virgin territory */ + /*tex Pop it off. */ + avail = token_link(avail); + } else if (fix_mem_end < fix_mem_max) { + /*tex Go into virgin territory. */ incr(fix_mem_end); p = fix_mem_end; } else { - smemory_word *new_fixmem; /* the big dynamic storage area */ + /*tex The big dynamic storage area. */ + smemory_word *new_fixmem; t = (fix_mem_max / 5); - new_fixmem = - fixmemcast(realloc - (fixmem, sizeof(smemory_word) * (fix_mem_max + t + 1))); + new_fixmem = fixmemcast(realloc(fixmem, sizeof(smemory_word) * (fix_mem_max + t + 1))); if (new_fixmem == NULL) { - runaway(); /* if memory is exhausted, display possible runaway text */ + /*tex If memory is exhausted, display possible runaway text. */ + runaway(); overflow("token memory size", fix_mem_max); } else { fixmem = new_fixmem; @@ -148,111 +179,127 @@ halfword get_avail(void) fix_mem_max += t; p = ++fix_mem_end; } - token_link(p) = null; /* provide an oft-desired initialization of the new node */ - incr(dyn_used); /* maintain statistics */ + /*tex Provide an oft-desired initialization of the new node. */ + token_link(p) = null; + /*tex Maintain statistics. */ + incr(dyn_used); return (halfword) p; } -@ The procedure |flush_list(p)| frees an entire linked list of one-word nodes -that starts at position |p|. -@^inner loop@> +/*tex + + The procedure |flush_list(p)| frees an entire linked list of one-word nodes + that starts at position |p|. + + This makes list of single-word nodes available: + +*/ -@c void flush_list(halfword p) -{ /* makes list of single-word nodes available */ - halfword q, r; /* list traversers */ +{ + halfword q, r; if (p != null) { r = p; do { q = r; r = token_link(r); decr(dyn_used); - } while (r != null); /* now |q| is the last node on the list */ + } while (r != null); + /*tex Now |q| is the last node on the list. */ token_link(q) = avail; avail = p; } } -@ A \TeX\ token is either a character or a control sequence, and it is @^token@> -represented internally in one of two ways: (1)~A character whose ASCII code -number is |c| and whose command code is |m| is represented as the number -$2^{21}m+c$; the command code is in the range |1<=m<=14|. (2)~A control sequence -whose |eqtb| address is |p| is represented as the number |cs_token_flag+p|. Here -|cs_token_flag=@t$2^{25}-1$@>| is larger than $2^{21}m+c$, yet it is small enough -that |cs_token_flag+p< max_halfword|; thus, a token fits comfortably in a -halfword. - -A token |t| represents a |left_brace| command if and only if -|t<left_brace_limit|; it represents a |right_brace| command if and only if we -have |left_brace_limit<=t<right_brace_limit|; and it represents a |match| or -|end_match| command if and only if |match_token<=t<=end_match_token|. The -following definitions take care of these token-oriented constants and a few -others. - -@ A token list is a singly linked list of one-word nodes in |mem|, where each -word contains a token and a link. Macro definitions, output-routine definitions, -marks, \.{\\write} texts, and a few other things are remembered by \TeX\ in the -form of token lists, usually preceded by a node with a reference count in its -|token_ref_count| field. The token stored in location |p| is called |info(p)|. - -Three special commands appear in the token lists of macro definitions. When -|m=match|, it means that \TeX\ should scan a parameter for the current macro; -when |m=end_match|, it means that parameter matching should end and \TeX\ should -start reading the macro text; and when |m=out_param|, it means that \TeX\ should -insert parameter number |c| into the text at this point. - -The enclosing \.{\char'173} and \.{\char'175} characters of a macro definition -are omitted, but the final right brace of an output routine is included at the -end of its token list. - -Here is an example macro definition that illustrates these conventions. After -\TeX\ processes the text - -$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$ - -the definition of \.{\\mac} is represented as a token list containing - -$$\def\,{\hskip2pt} -\vbox{\halign{\hfil#\hfil\cr -(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ , -\.{\\b}, |end_match|,\cr -|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#, -|other_char|\,\.1,\cr -|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$ - -The procedure |scan_toks| builds such token lists, and |macro_call| does the -parameter matching. @^reference counts@> - -Examples such as $$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$ explain why reference -counts would be needed even if \TeX\ had no \.{\\let} operation: When the token -list for \.{\\m} is being read, the redefinition of \.{\\m} changes the |eqtb| -entry before the token list has been fully consumed, so we dare not simply -destroy a token list when its control sequence is being redefined. - -If the parameter-matching part of a definition ends with `\.{\#\{}', the -corresponding token list will have `\.\{' just before the `|end_match|' and also -at the very end. The first `\.\{' is used to delimit the parameter; the second -one keeps the first from disappearing. - -The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in symbolic form, -including the expansion of a macro or mark. - -@c +/*tex + + A \TeX\ token is either a character or a control sequence, and it is + represented internally in one of two ways: (1)~A character whose ASCII code + number is |c| and whose command code is |m| is represented as the number + $2^{21}m+c$; the command code is in the range |1<=m<=14|. (2)~A control + sequence whose |eqtb| address is |p| is represented as the number + |cs_token_flag+p|. Here |cs_token_flag=t=| $2^{25}-1$ is larger than + $2^{21}m+c$, yet it is small enough that |cs_token_flag+p< max_halfword|; + thus, a token fits comfortably in a halfword. + + A token |t| represents a |left_brace| command if and only if + |t<left_brace_limit|; it represents a |right_brace| command if and only if we + have |left_brace_limit<=t<right_brace_limit|; and it represents a |match| or + |end_match| command if and only if |match_token<=t<=end_match_token|. The + following definitions take care of these token-oriented constants and a few + others. + + A token list is a singly linked list of one-word nodes in |mem|, where each + word contains a token and a link. Macro definitions, output-routine + definitions, marks, \.{\\write} texts, and a few other things are remembered + by \TeX\ in the form of token lists, usually preceded by a node with a + reference count in its |token_ref_count| field. The token stored in location + |p| is called |info(p)|. + + Three special commands appear in the token lists of macro definitions. When + |m=match|, it means that \TeX\ should scan a parameter for the current macro; + when |m=end_match|, it means that parameter matching should end and \TeX\ + should start reading the macro text; and when |m=out_param|, it means that + \TeX\ should insert parameter number |c| into the text at this point. + + The enclosing \.{\char'173} and \.{\char'175} characters of a macro + definition are omitted, but the final right brace of an output routine is + included at the end of its token list. + + Here is an example macro definition that illustrates these conventions. After + \TeX\ processes the text + + \starttyping + \def\mac a#1#2 \b {#1\-a ##1#2 \#2\} + \stoptyping + + the definition of \.{\\mac} is represented as a token list containing + + \starttyping + (reference count) |letter|a |match|# |match|# |spacer| \b |end_match| + |out_param|1 \- |letter|a |spacer|, |mac_param|# |other_char|1 + |out_param|2 |spacer| |out_param|2 + \stoptyping + + The procedure |scan_toks| builds such token lists, and |macro_call| does the + parameter matching. + + Examples such as \type{\def\m{\def\m{a} b}} explain why reference + counts would be needed even if \TeX\ had no \.{\\let} operation: When the + token list for \.{\\m} is being read, the redefinition of \.{\\m} changes the + |eqtb| entry before the token list has been fully consumed, so we dare not + simply destroy a token list when its control sequence is being redefined. + + If the parameter-matching part of a definition ends with `\.{\#\{}', the + corresponding token list will have `\.\{' just before the `|end_match|' and + also at the very end. The first `\.\{' is used to delimit the parameter; the + second one keeps the first from disappearing. + + The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in symbolic + form, including the expansion of a macro or mark. + +*/ + void print_meaning(void) { - /* remap \mathchar onto \Umathchar */ -/* + /*tex + + This would make sense but some macro packages don't like it: + + \starttyping if (cur_cmd == math_given_cmd) { cur_cmd = xmath_given_cmd ; } -*/ + \stoptyping + + */ print_cmd_chr((quarterword) cur_cmd, cur_chr); if (cur_cmd >= call_cmd) { print_char(':'); print_ln(); token_show(cur_chr); } else { - /* Show the meaning of a mark node */ + /*tex Show the meaning of a mark node. */ if ((cur_cmd == top_bot_mark_cmd) && (cur_chr < marks_code)) { print_char(':'); print_ln(); @@ -277,31 +324,35 @@ void print_meaning(void) } } -@ The procedure |show_token_list|, which prints a symbolic form of the token list -that starts at a given node |p|, illustrates these conventions. The token list -being displayed should not begin with a reference count. However, the procedure -is intended to be robust, so that if the memory links are awry or if |p| is not -really a pointer to a token list, nothing catastrophic will happen. - -An additional parameter |q| is also given; this parameter is either null or it -points to a node in the token list where a certain magic computation takes place -that will be explained later. (Basically, |q| is non-null when we are printing -the two-line context information at the time of an error message; |q| marks the -place corresponding to where the second line should begin.) - -For example, if |p| points to the node containing the first \.a in the token list -above, then |show_token_list| will print the string $$\hbox{`\.{a\#1\#2\ \\b\ -->\#1\\-a\ \#\#1\#2\ \#2}';}$$ and if |q| points to the node containing the -second \.a, the magic computation will be performed just before the second \.a is -printed. - -The generation will stop, and `\.{\\ETC.}' will be printed, if the length of -printing exceeds a given limit~|l|. Anomalous entries are printed in the form of -control sequences that are not followed by a blank space, e.g., `\.{\\BAD.}'; -this cannot be confused with actual control sequences because a real control -sequence named \.{BAD} would come out `\.{\\BAD\ }'. - -@c +/*tex + + The procedure |show_token_list|, which prints a symbolic form of the token + list that starts at a given node |p|, illustrates these conventions. The + token list being displayed should not begin with a reference count. However, + the procedure is intended to be robust, so that if the memory links are awry + or if |p| is not really a pointer to a token list, nothing catastrophic will + happen. + + An additional parameter |q| is also given; this parameter is either null or + it points to a node in the token list where a certain magic computation takes + place that will be explained later. (Basically, |q| is non-null when we are + printing the two-line context information at the time of an error message; + |q| marks the place corresponding to where the second line should begin.) + + For example, if |p| points to the node containing the first \.a in the token + list above, then |show_token_list| will print the string $$\hbox{`\.{a\#1\#2\ + \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$ and if |q| points to the node containing + the second \.a, the magic computation will be performed just before the + second \.a is printed. + + The generation will stop, and `\.{\\ETC.}' will be printed, if the length of + printing exceeds a given limit~|l|. Anomalous entries are printed in the form + of control sequences that are not followed by a blank space, e.g., + `\.{\\BAD.}'; this cannot be confused with actual control sequences because a + real control sequence named \.{BAD} would come out `\.{\\BAD\ }'. + +*/ + #define not_so_bad(p) \ switch (m) { \ case assign_int_cmd: \ @@ -316,6 +367,18 @@ sequence named \.{BAD} would come out `\.{\\BAD\ }'. if (c >= (backend_toks_base) && c <= (backend_toks_last)) \ p("[internal backend tokenlist]"); \ break; \ + case node_cmd: \ + p("[internal node pointer]"); \ + break; \ + case lua_call_cmd: \ + p("[internal lua function call]"); \ + break; \ + case lua_expandable_call_cmd: \ + p("[internal expandable lua function call]"); \ + break; \ + case lua_local_call_cmd: \ + p("[internal local lua function call]"); \ + break; \ default: \ p("BAD"); \ break; \ @@ -323,18 +386,21 @@ sequence named \.{BAD} would come out `\.{\\BAD\ }'. void show_token_list(int p, int q, int l) { - int m, c; /* pieces of a token */ - ASCII_code match_chr = '#'; /* character used in a `|match|' */ - ASCII_code n = '0'; /* the highest parameter number, as an ASCII digit */ + /*tex pieces of a token */ + int m, c; + /*tex character used in a `|match|' */ + ASCII_code match_chr = '#'; + /*tex the highest parameter number, as an ASCII digit */ + ASCII_code n = '0'; tally = 0; if (l < 0) l = 0x3FFFFFFF; while ((p != null) && (tally < l)) { if (p == q) { - /* Do magic computation */ + /*tex Do magic computation. */ set_trick_count(); } - /* Display token |p|, and |return| if there are problems */ + /*tex Display token |p|, and |return| if there are problems. */ if ((p < (int) fix_mem_min) || (p > (int) fix_mem_end)) { tprint_esc("CLOBBERED."); return; @@ -349,11 +415,11 @@ void show_token_list(int p, int q, int l) tprint_esc("BAD"); } else { /* - Display the token $(|m|,|c|)$ - - The procedure usually ``learns'' the character code used for macro - parameters by seeing one in a |match| command before it runs into any + Display the token \type {(|m|,|c|)}. The procedure usually + ``learns'' the character code used for macro parameters by + seeing one in a |match| command before it runs into any |out_param| commands. + */ switch (m) { case left_brace_cmd: @@ -405,31 +471,36 @@ void show_token_list(int p, int q, int l) tprint_esc("ETC."); } -@ @c #define do_buffer_to_unichar(a,b) do { \ a = (halfword)str2uni(buffer+b); \ b += utf8_size(a); \ } while (0) -@ Here's the way we sometimes want to display a token list, given a pointer to -its reference count; the pointer may be null. +/*tex + + Here's the way we sometimes want to display a token list, given a pointer to + its reference count; the pointer may be null. + +*/ -@c void token_show(halfword p) { if (p != null) show_token_list(token_link(p), null, 10000000); } -@ |delete_token_ref|, is called when a pointer to a token list's reference count -is being removed. This means that the token list should disappear if the -reference count was |null|, otherwise the count should be decreased by one. -@^reference counts@> +/*tex -@ |p| points to the reference count of a token list that is losing one -reference. + |delete_token_ref|, is called when a pointer to a token list's reference + count is being removed. This means that the token list should disappear if + the reference count was |null|, otherwise the count should be decreased by + one. + + |p| points to the reference count of a token list that is losing one + reference. + +*/ -@c void delete_token_ref(halfword p) { if (token_ref_count(p) == 0) @@ -438,7 +509,6 @@ void delete_token_ref(halfword p) decr(token_ref_count(p)); } -@ @c int get_char_cat_code(int curchr) { int a; @@ -446,7 +516,6 @@ int get_char_cat_code(int curchr) return a; } -@ @c static void invalid_character_error(void) { const char *hlp[] = { @@ -459,10 +528,9 @@ static void invalid_character_error(void) deletions_allowed = true; } -@ @c -static boolean process_sup_mark(void); /* below */ +static boolean process_sup_mark(void); -static int scan_control_sequence(void); /* below */ +static int scan_control_sequence(void); typedef enum { next_line_ok, @@ -470,57 +538,83 @@ typedef enum { next_line_restart } next_line_retval; -static next_line_retval next_line(void); /* below */ +static next_line_retval next_line(void); -@ In case you are getting bored, here is a slightly less trivial routine: Given a -string of lowercase letters, like `\.{pt}' or `\.{plus}' or `\.{width}', the -|scan_keyword| routine checks to see whether the next tokens of input match this -string. The match must be exact, except that uppercase letters will match their -lowercase counterparts; uppercase equivalents are determined by subtracting -|"a"-"A"|, rather than using the |uc_code| table, since \TeX\ uses this routine -only for its own limited set of keywords. +/*tex -If a match is found, the characters are effectively removed from the input and -|true| is returned. Otherwise |false| is returned, and the input is left -essentially unchanged (except for the fact that some macros may have been -expanded, etc.). @^inner loop@> + In case you are getting bored, here is a slightly less trivial routine: Given + a string of lowercase letters, like `\.{pt}' or `\.{plus}' or `\.{width}', + the |scan_keyword| routine checks to see whether the next tokens of input + match this string. The match must be exact, except that uppercase letters + will match their lowercase counterparts; uppercase equivalents are determined + by subtracting |"a"-"A"|, rather than using the |uc_code| table, since \TeX\ + uses this routine only for its own limited set of keywords. + + If a match is found, the characters are effectively removed from the input + and |true| is returned. Otherwise |false| is returned, and the input is left + essentially unchanged (except for the fact that some macros may have been + expanded, etc.). + +*/ -@c boolean scan_keyword(const char *s) -{ /* look for a given string */ - halfword p; /* tail of the backup list */ - halfword q; /* new node being added to the token list via |store_new_token| */ - const char *k; /* index into |str_pool| */ +{ + /*tex tail of the backup list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex index into |str_pool| */ + const char *k; halfword save_cur_cs = cur_cs; - if (strlen(s) == 0) /* was assert (strlen(s) > 1); */ - return false ; /* but not with newtokenlib zero keyword simply doesn't match */ + if (strlen(s) == 0) { + /*tex but not with newtokenlib zero keyword simply doesn't match */ + return false ; + } p = backup_head; token_link(p) = null; k = s; while (*k) { - get_x_token(); /* recursion is possible here */ + /*tex Recursion is possible here! */ + get_x_token(); if ((cur_cs == 0) && ((cur_chr == *k) || (cur_chr == *k - 'a' + 'A'))) { store_new_token(cur_tok); k++; } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) { - /* - crashes on some alignments: + back_input(); + if (p != backup_head) { + begin_token_list(token_link(backup_head), backed_up); + } + cur_cs = save_cur_cs; + return false; + } + } + if (token_link(backup_head) != null) + flush_list(token_link(backup_head)); + cur_cs = save_cur_cs; + return true; +} - if (p != backup_head) { - q = get_avail(); - token_info(q) = cur_tok; - token_link(q) = null; - token_link(p) = q; - begin_token_list(token_link(backup_head), backed_up); - } else { - back_input(); - } - */ +boolean scan_keyword_case_sensitive(const char *s) +{ + halfword p; + halfword q; + const char *k; + halfword save_cur_cs = cur_cs; + if (strlen(s) == 0) + return false ; + p = backup_head; + token_link(p) = null; + k = s; + while (*k) { + get_x_token(); + if ((cur_cs == 0) && (cur_chr == *k)) { + store_new_token(cur_tok); + k++; + } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) { back_input(); if (p != backup_head) { begin_token_list(token_link(backup_head), backed_up); } - /* */ cur_cs = save_cur_cs; return false; } @@ -531,36 +625,11 @@ boolean scan_keyword(const char *s) return true; } -@ We can not return |undefined_control_sequence| under some conditions - (inside |shift_case|, for example). This needs thinking. +/*tex -@c + We can not return |undefined_control_sequence| under some conditions (inside + |shift_case|, for example). This needs thinking. -/* - halfword active_to_cs(int curchr, int force) - { - halfword curcs; - char *a, *b; - char *utfbytes = xmalloc(8); - int nncs = no_new_control_sequence; - a = (char *) uni2str(0xFFFF); - utfbytes = strcpy(utfbytes, a); - if (force) - no_new_control_sequence = false; - if (curchr > 0) { - b = (char *) uni2str((unsigned) curchr); - utfbytes = strcat(utfbytes, b); - free(b); - curcs = string_lookup(utfbytes, strlen(utfbytes)); - } else { - utfbytes[3] = '\0'; - curcs = string_lookup(utfbytes, 4); - } - no_new_control_sequence = nncs; - free(a); - free(utfbytes); - return curcs; - } */ /*static char * FFFF = "\xEF\xBF\xBF";*/ /* 0xFFFF */ @@ -581,7 +650,8 @@ halfword active_to_cs(int curchr, int force) curcs = string_lookup(utfbytes, utf8_size(curchr)+3); free(utfbytes); } else { - curcs = string_lookup("\xEF\xBF\xBF", 4); /* 0xFFFF ... why not 3 ? */ + /*tex 0xFFFF ... why not 3 ? */ + curcs = string_lookup("\xEF\xBF\xBF", 4); } no_new_control_sequence = nncs; return curcs; @@ -641,11 +711,12 @@ halfword active_to_cs(int curchr, int force) */ -@ TODO this function should listen to \.{\\escapechar} +/*tex -@ prints a control sequence + Maybe this function should listen to \.{\\escapechar} but we can do without. + +*/ -@c static char *cs_to_string(halfword p) { const char *s; @@ -687,9 +758,8 @@ static char *cs_to_string(halfword p) return (char *) ret; } -@ TODO this is a quick hack, will be solved differently soon +/*tex This is sort of a hack. */ -@c static char *cmd_chr_to_string(int cmd, int chr) { char *s; @@ -704,56 +774,76 @@ static char *cmd_chr_to_string(int cmd, int chr) return s; } -@ The heart of \TeX's input mechanism is the |get_next| procedure, which we shall -develop in the next few sections of the program. Perhaps we shouldn't actually -call it the ``heart,'' however, because it really acts as \TeX's eyes and mouth, -reading the source files and gobbling them up. And it also helps \TeX\ to -regurgitate stored token lists that are to be processed again. @^eyes and mouth@> - -The main duty of |get_next| is to input one token and to set |cur_cmd| and -|cur_chr| to that token's command code and modifier. Furthermore, if the input -token is a control sequence, the |eqtb| location of that control sequence is -stored in |cur_cs|; otherwise |cur_cs| is set to zero. - -Underlying this simple description is a certain amount of complexity because of -all the cases that need to be handled. However, the inner loop of |get_next| is -reasonably short and fast. - -When |get_next| is asked to get the next token of a \.{\\read} line, -it sets |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens -appear on that line. (There might not be any tokens at all, if the -|end_line_char| has |ignore| as its catcode.) - -The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity is -needed because a blank line of input is supposed to be exactly equivalent to the -appearance of \.{\\par}; we must set |cur_cs:=par_loc| when detecting a blank -line. - -@c -halfword par_loc; /* location of `\.{\\par}' in |eqtb| */ -halfword par_token; /* token representing `\.{\\par}' */ - -@ Parts |get_next| are executed more often than any other instructions of \TeX. -@^mastication@>@^inner loop@> - -The global variable |force_eof| is normally |false|; it is set |true| by an -\.{\\endinput} command. |luacstrings| is the number of lua print statements -waiting to be input, it is changed by |luatokencall|. - -@c -boolean force_eof; /* should the next \.{\\input} be aborted early? */ -int luacstrings; /* how many lua strings are waiting to be input? */ - -@ If the user has set the |pausing| parameter to some positive value, and if -nonstop mode has not been selected, each line of input is displayed on the -terminal and the transcript file, followed by `\.{=>}'. \TeX\ waits for a -response. If the response is simply |carriage_return|, the line is accepted as it -stands, otherwise the line typed is used instead of the line in the file. - -@c +/*tex + + The heart of \TeX's input mechanism is the |get_next| procedure, which we + shall develop in the next few sections of the program. Perhaps we shouldn't + actually call it the ``heart,'' however, because it really acts as \TeX's + eyes and mouth, reading the source files and gobbling them up. And it also + helps \TeX\ to regurgitate stored token lists that are to be processed again. + + The main duty of |get_next| is to input one token and to set |cur_cmd| and + |cur_chr| to that token's command code and modifier. Furthermore, if the + input token is a control sequence, the |eqtb| location of that control + sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero. + + Underlying this simple description is a certain amount of complexity because + of all the cases that need to be handled. However, the inner loop of + |get_next| is reasonably short and fast. + + When |get_next| is asked to get the next token of a \.{\\read} line, it sets + |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens appear on that + line. (There might not be any tokens at all, if the |end_line_char| has + |ignore| as its catcode.) + + The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity is + needed because a blank line of input is supposed to be exactly equivalent to + the appearance of \.{\\par}; we must set |cur_cs:=par_loc| when detecting a + blank line. + +*/ + +/*tex + + The location of `\.{\\par}' in |eqtb| adn the token representing `\.{\\par}. + +*/ + +halfword par_loc; +halfword par_token; + +/*tex + + Parts |get_next| are executed more often than any other instructions of \TeX. + + The global variable |force_eof| is normally |false|; it is set |true| by an + \.{\\endinput} command. |luacstrings| is the number of lua print statements + waiting to be input, it is changed by |luatokencall|. + + +*/ + +/*tex Should the next \.{\\input} be aborted early? */ +boolean force_eof; + +/*tex How many lua strings are waiting to be input? */ + +int luacstrings; + +/*tex + + If the user has set the |pausing| parameter to some positive value, and if + nonstop mode has not been selected, each line of input is displayed on the + terminal and the transcript file, followed by `\.{=>}'. \TeX\ waits for a + response. If the response is simply |carriage_return|, the line is accepted + as it stands, otherwise the line typed is used instead of the line in the + file. + +*/ + void firm_up_the_line(void) { - int k; /* an index into |buffer| */ + int k; ilimit = last; if (pausing_par > 0) { if (interaction > nonstop_mode) { @@ -764,9 +854,10 @@ void firm_up_the_line(void) print_char(buffer[k]); } first = ilimit; - prompt_input("=>"); /* wait for user response */ + prompt_input("=>"); if (last > first) { - for (k = first; k < +last - 1; k++) /* move line down in buffer */ + /*tex Move line down in buffer. */ + for (k = first; k < +last - 1; k++) buffer[k + istart - first] = buffer[k]; ilimit = istart + last - first; } @@ -774,31 +865,42 @@ void firm_up_the_line(void) } } -@ Before getting into |get_next|, let's consider the subroutine that is called -when an `\.{\\outer}' control sequence has been scanned or when the end of a file -has been reached. These two cases are distinguished by |cur_cs|, which is zero at -the end of a file. +/*tex + + Before getting into |get_next|, let's consider the subroutine that is called + when an `\.{\\outer}' control sequence has been scanned or when the end of a + file has been reached. These two cases are distinguished by |cur_cs|, which + is zero at the end of a file. + +*/ -@c void check_outer_validity(void) { - halfword p; /* points to inserted token list */ - halfword q; /* auxiliary pointer */ + /*tex points to inserted token list */ + halfword p; + /*tex auxiliary pointer */ + halfword q; if (suppress_outer_error_par) return; if (scanner_status != normal) { deletions_allowed = false; - /* Back up an outer control sequence so that it can be reread; */ - /* An outer control sequence that occurs in a \.{\\read} will not be reread, - since the error recovery for \.{\\read} is not very powerful. */ + /*tex + + Back up an outer control sequence so that it can be reread. An + outer control sequence that occurs in a \.{\\read} will not be + reread, since the error recovery for \.{\\read} is not very powerful. + + */ if (cur_cs != 0) { if ((istate == token_list) || (iname < 1) || (iname > 17)) { p = get_avail(); token_info(p) = cs_token_flag + cur_cs; - begin_token_list(p, backed_up); /* prepare to read the control sequence again */ + /*tex prepare to read the control sequence again */ + begin_token_list(p, backed_up); } + /*tex replace it by a space */ cur_cmd = spacer_cmd; - cur_chr = ' '; /* replace it by a space */ + cur_chr = ' '; } if (scanner_status > skipping) { const char *errhlp[] = { @@ -808,56 +910,64 @@ void check_outer_validity(void) "you'd better type `E' or `X' now and fix your file.", NULL }; - char errmsg[256]; + char errmsg[318]; const char *startmsg; const char *scannermsg; - /* Tell the user what has run away and try to recover */ - runaway(); /* print a definition, argument, or preamble */ + /*tex + + Tell the user what has run away and try to recover Print a + definition, argument, or preamble. + + */ + runaway(); if (cur_cs == 0) { startmsg = "File ended"; } else { cur_cs = 0; startmsg = "Forbidden control sequence found"; } - /* Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}', - and insert tokens that should lead to recovery; */ - /* The recovery procedure can't be fully understood without knowing more - about the \TeX\ routines that should be aborted, but we can sketch the - ideas here: For a runaway definition we will insert a right brace; for a - runaway preamble, we will insert a special \.{\\cr} token and a right - brace; and for a runaway argument, we will set |long_state| to - |outer_call| and insert \.{\\par}. */ + /*tex + + Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or + `\.{text}', and insert tokens that should lead to recovery. The + recovery procedure can't be fully understood without knowing more + about the \TeX\ routines that should be aborted, but we can + sketch the ideas here: For a runaway definition we will insert a + right brace; for a runaway preamble, we will insert a special + \.{\\cr} token and a right brace; and for a runaway argument, we + will set |long_state| to |outer_call| and insert \.{\\par}. + + */ p = get_avail(); switch (scanner_status) { - case defining: - scannermsg = "definition"; - token_info(p) = right_brace_token + '}'; - break; - case matching: - scannermsg = "use"; - token_info(p) = par_token; - long_state = outer_call_cmd; - break; - case aligning: - scannermsg = "preamble"; - token_info(p) = right_brace_token + '}'; - q = p; - p = get_avail(); - token_link(p) = q; - token_info(p) = cs_token_flag + frozen_cr; - align_state = -1000000; - break; - case absorbing: - scannermsg = "text"; - token_info(p) = right_brace_token + '}'; - break; - default: /* can't happen */ - scannermsg = "unknown"; - break; - } /*there are no other cases */ + case defining: + scannermsg = "definition"; + token_info(p) = right_brace_token + '}'; + break; + case matching: + scannermsg = "use"; + token_info(p) = par_token; + long_state = outer_call_cmd; + break; + case aligning: + scannermsg = "preamble"; + token_info(p) = right_brace_token + '}'; + q = p; + p = get_avail(); + token_link(p) = q; + token_info(p) = cs_token_flag + frozen_cr; + align_state = -1000000; + break; + case absorbing: + scannermsg = "text"; + token_info(p) = right_brace_token + '}'; + break; + default: + scannermsg = "unknown"; + break; + } begin_token_list(p, inserted); - snprintf(errmsg, 255, "%s while scanning %s of %s", - startmsg, scannermsg, cs_to_string(warning_index)); + snprintf(errmsg, 318, "%s while scanning %s of %s", startmsg, scannermsg, cs_to_string(warning_index)); tex_error(errmsg, errhlp); } else { char errmsg[256]; @@ -883,9 +993,9 @@ void check_outer_validity(void) snprintf(errmsg, 255, "Incomplete %s; all text was ignored after line %d", ss, (int) skip_line); free(ss); - /* Incomplete \\if... */ + /*tex Incomplete |\if...| */ cur_tok = cs_token_flag + frozen_fi; - /* back up one inserted token and call |error| */ + /*tex back up one inserted token and call |error|. */ { OK_to_interrupt = false; back_input(); @@ -898,61 +1008,65 @@ void check_outer_validity(void) } } -@ @c - #if 0 -/* - The other variant gives less clutter in tracing cache usage when profiling and for - some files (like the manual) also a bit of a speedup. +/*tex + + The other variant gives less clutter in tracing cache usage when profiling + and for some files (like the manual) also a bit of a speedup. + */ static boolean get_next_file(void) { SWITCH: if (iloc <= ilimit) { - /* current line not yet finished */ + /*tex current line not yet finished */ do_buffer_to_unichar(cur_chr, iloc); - RESWITCH: if (detokenized_line()) { cur_cmd = (cur_chr == ' ' ? 10 : 12); } else { do_get_cat_code(cur_cmd, cur_chr); } - /* + /*tex + Change state if necessary, and |goto switch| if the current character should be ignored, or |goto reswitch| if the current character changes to another; - The following 48-way switch accomplishes the scanning quickly, assuming - that a decent C compiler has translated the code. Note that the numeric - values for |mid_line|, |skip_blanks|, and |new_line| are spaced - apart from each other by |max_char_code+1|, so we can add a character's - command code to the state to get a single number that characterizes both. + The following 48-way switch accomplishes the scanning quickly, + assuming that a decent C compiler has translated the code. Note that + the numeric values for |mid_line|, |skip_blanks|, and |new_line| are + spaced apart from each other by |max_char_code+1|, so we can add a + character's command code to the state to get a single number that + characterizes both. - Remark [ls/hh]: checking performance indicated that this switch was the - cause of many branch prediction errors but changing it to: + Remark [ls/hh]: checking performance indicated that this switch was + the cause of many branch prediction errors but changing it to: - c = istate + cur_cmd; - if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) { - return true; - } else if (c >= new_line) { - switch (c) { - } - } else if (c >= skip_blanks) { - switch (c) { - } - } else if (c >= mid_line) { - switch (c) { - } - } else { - istate = mid_line; - return true; + \starttyping + c = istate + cur_cmd; + if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) { + return true; + } else if (c >= new_line) { + switch (c) { + } + } else if (c >= skip_blanks) { + switch (c) { + } + } else if (c >= mid_line) { + switch (c) { } + } else { + istate = mid_line; + return true; + } + \stoptyping + + gives as many prediction errors. So, we can indeed assume that the + compiler does the right job, or that there is simply no other way. - gives as many prediction errors. So, we can indeed assume that the compiler - does the right job, or that there is simply no other way. */ switch (istate + cur_cmd) { @@ -961,13 +1075,13 @@ static boolean get_next_file(void) case new_line + ignore_cmd: case skip_blanks + spacer_cmd: case new_line + spacer_cmd: - /* Cases where character is ignored */ + /*tex Cases where character is ignored. */ goto SWITCH; break; case mid_line + escape_cmd: case new_line + escape_cmd: case skip_blanks + escape_cmd: - /* Scan a control sequence ...; */ + /*tex Scan a control sequence. */ istate = (unsigned char) scan_control_sequence(); if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) check_outer_validity(); @@ -975,7 +1089,7 @@ static boolean get_next_file(void) case mid_line + active_char_cmd: case new_line + active_char_cmd: case skip_blanks + active_char_cmd: - /* Process an active-character */ + /*tex Process an active-character. */ cur_cs = active_to_cs(cur_chr, false); cur_cmd = eq_type(cur_cs); cur_chr = equiv(cur_cs); @@ -986,7 +1100,7 @@ static boolean get_next_file(void) case mid_line + sup_mark_cmd: case new_line + sup_mark_cmd: case skip_blanks + sup_mark_cmd: - /* If this |sup_mark| starts */ + /*tex If this |sup_mark| starts. */ if (process_sup_mark()) goto RESWITCH; else @@ -995,22 +1109,27 @@ static boolean get_next_file(void) case mid_line + invalid_char_cmd: case new_line + invalid_char_cmd: case skip_blanks + invalid_char_cmd: - /* Decry the invalid character and |goto restart|; */ + /*tex Decry the invalid character and |goto restart|. */ invalid_character_error(); - return false; /* because state may be |token_list| now */ + /*tex Because state may be |token_list| now: */ + return false; break; case mid_line + spacer_cmd: - /* Enter |skip_blanks| state, emit a space; */ + /*tex Enter |skip_blanks| state, emit a space. */ istate = skip_blanks; cur_chr = ' '; break; case mid_line + car_ret_cmd: - /* - Finish line, emit a space. When a character of type |spacer| gets through, its - character code is changed to $\.{"\ "}=040$. This means that the ASCII codes - for tab and space, and for the space inserted at the end of a line, will be - treated alike when macro parameters are being matched. We do this since such - characters are indistinguishable on most computer terminal displays. + /*tex + + Finish line, emit a space. When a character of type |spacer| + gets through, its character code is changed to $\.{"\ + "}=040$. This means that the ASCII codes for tab and space, + and for the space inserted at the end of a line, will be + treated alike when macro parameters are being matched. We do + this since such characters are indistinguishable on most + computer terminal displays. + */ iloc = ilimit + 1; cur_cmd = spacer_cmd; @@ -1020,12 +1139,12 @@ static boolean get_next_file(void) case mid_line + comment_cmd: case new_line + comment_cmd: case skip_blanks + comment_cmd: - /* Finish line, |goto switch|; */ + /*tex Finish line, |goto switch|; */ iloc = ilimit + 1; goto SWITCH; break; case new_line + car_ret_cmd: - /* Finish line, emit a \.{\\par}; */ + /*tex Finish line, emit a \.{\\par}; */ iloc = ilimit + 1; cur_cs = par_loc; cur_cmd = eq_type(cur_cs); @@ -1036,14 +1155,14 @@ static boolean get_next_file(void) case skip_blanks + left_brace_cmd: case new_line + left_brace_cmd: istate = mid_line; - /* fall through */ + /*tex Fall through. */ case mid_line + left_brace_cmd: align_state++; break; case skip_blanks + right_brace_cmd: case new_line + right_brace_cmd: istate = mid_line; - /* fall through */ + /*tex Fall through. */ case mid_line + right_brace_cmd: align_state--; break; @@ -1075,11 +1194,12 @@ static boolean get_next_file(void) } else { if (iname != 21) istate = new_line; - /* - Move to next line of file, - or |goto restart| if there is no next line, + /*tex + + Move to next line of file, or |goto restart| if there is no next line, or |return| if a \.{\\read} line has finished; - */ + + */ do { next_line_retval r = next_line(); if (r == next_line_return) { @@ -1096,13 +1216,10 @@ static boolean get_next_file(void) #else -/* 10 times less Bim in callgrind */ +/*tex + + This variant gives 10 times less Bim in vallgrind! -/* - escape_cmd left_brace_cmd right_brace_cmd math_shift_cmd - tab_mark_cmd car_ret_cmd mac_param_cmd sup_mark_cmd - sub_mark_cmd ignore_cmd spacer_cmd letter_cmd - other_char_cmd active_char_cmd comment_cmd invalid_char_cmd */ static boolean get_next_file(void) @@ -1110,7 +1227,7 @@ static boolean get_next_file(void) int c = 0; SWITCH: if (iloc <= ilimit) { - /* current line not yet finished */ + /*tex current line not yet finished */ do_buffer_to_unichar(cur_chr, iloc); RESWITCH: if (detokenized_line()) { @@ -1118,10 +1235,12 @@ static boolean get_next_file(void) } else { do_get_cat_code(cur_cmd, cur_chr); } - /* - Change state if necessary, and |goto switch| if the current - character should be ignored, or |goto reswitch| if the current - character changes to another; + /*tex + + Change state if necessary, and |goto switch| if the current character + should be ignored, or |goto reswitch| if the current character changes + to another. + */ c = istate + cur_cmd; if (c == (mid_line + letter_cmd) || c == (mid_line + other_char_cmd)) { @@ -1148,7 +1267,7 @@ static boolean get_next_file(void) istate = mid_line; return true; case car_ret_cmd: - /* Finish line, emit a \.{\\par}; */ + /*tex Finish line, emit a \.{\\par}. */ iloc = ilimit + 1; cur_cs = par_loc; cur_cmd = eq_type(cur_cs); @@ -1172,7 +1291,7 @@ static boolean get_next_file(void) goto SWITCH; return true; case spacer_cmd: - /* Cases where character is ignored */ + /*tex Cases where character is ignored. */ goto SWITCH; case letter_cmd: istate = mid_line; @@ -1192,8 +1311,9 @@ static boolean get_next_file(void) iloc = ilimit + 1; goto SWITCH; case invalid_char_cmd: + /*tex Because state may be |token_list| now. */ invalid_character_error(); - return false; /* because state may be |token_list| now */ + return false; default: istate = mid_line; return true; @@ -1201,7 +1321,7 @@ static boolean get_next_file(void) } else if (c >= skip_blanks) { switch (c-skip_blanks) { case escape_cmd: - /* Scan a control sequence ...; */ + /*tex Scan a control sequence. */ istate = (unsigned char) scan_control_sequence(); if (! suppress_outer_error_par && cur_cmd >= outer_call_cmd) check_outer_validity(); @@ -1227,7 +1347,6 @@ static boolean get_next_file(void) istate = mid_line; return true; case sup_mark_cmd: - /* If this |sup_mark| starts */ if (process_sup_mark()) goto RESWITCH; else @@ -1255,13 +1374,14 @@ static boolean get_next_file(void) check_outer_validity(); return true; case comment_cmd: - /* Finish line, |goto switch|; */ + /*tex Finish line, |goto switch|. */ iloc = ilimit + 1; goto SWITCH; case invalid_char_cmd: - /* Decry the invalid character and |goto restart|; */ + /*tex Decry the invalid character and |goto restart|. */ invalid_character_error(); - return false; /* because state may be |token_list| now */ + /*tex Because state may be |token_list| now. */ + return false; default: istate = mid_line; return true; @@ -1284,13 +1404,17 @@ static boolean get_next_file(void) case tab_mark_cmd: return true; case car_ret_cmd: - /* - Finish line, emit a space. When a character of type |spacer| gets through, its - character code is changed to $\.{"\ "}=040$. This means that the ASCII codes - for tab and space, and for the space inserted at the end of a line, will be - treated alike when macro parameters are being matched. We do this since such - characters are indistinguishable on most computer terminal displays. - */ + /*tex + + Finish line, emit a space. When a character of type + |spacer| gets through, its character code is changed to + $\.{"\ "}=040$. This means that the ASCII codes for tab + and space, and for the space inserted at the end of a + line, will be treated alike when macro parameters are + being matched. We do this since such characters are + indistinguishable on most computer terminal displays. + + */ iloc = ilimit + 1; cur_cmd = spacer_cmd; cur_chr = ' '; @@ -1308,7 +1432,7 @@ static boolean get_next_file(void) case ignore_cmd: goto SWITCH; case spacer_cmd: - /* Enter |skip_blanks| state, emit a space; */ + /*tex Enter |skip_blanks| state, emit a space. */ istate = skip_blanks; cur_chr = ' '; return true; @@ -1330,8 +1454,9 @@ static boolean get_next_file(void) iloc = ilimit + 1; goto SWITCH; case invalid_char_cmd: + /*tex Because state may be |token_list| now. */ invalid_character_error(); - return false; /* because state may be |token_list| now */ + return false; default: istate = mid_line; return true; @@ -1344,9 +1469,11 @@ static boolean get_next_file(void) if (iname != 21) { istate = new_line; } - /* - Move to next line of file, or |goto restart| if there is no next line, - or |return| if a \.{\\read} line has finished; + /*tex + + Move to next line of file, or |goto restart| if there is no next + line, or |return| if a \.{\\read} line has finished; + */ do { next_line_retval r = next_line(); @@ -1364,14 +1491,18 @@ static boolean get_next_file(void) #endif -@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit. -We only support a limited set: +/*tex -^^^^^^XXXXXX -^^^^XXXXXX -^^XX ^^<char> + Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit. + We only support a limited set: -@c + \starttyping + ^^^^^^XXXXXX + ^^^^XXXXXX + ^^XX ^^<char> + \stoptyping + +*/ #define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f')) @@ -1412,7 +1543,7 @@ static boolean process_sup_mark(void) if (iloc < ilimit) { if ((cur_chr == buffer[iloc + 1]) && (cur_chr == buffer[iloc + 2])) { if ((cur_chr == buffer[iloc + 3]) && (cur_chr == buffer[iloc + 4])) { - /* ^^^^^^XXXXXX */ + /*tex |^^^^^^XXXXXX| */ if ((iloc + 10) <= ilimit) { int c1 = buffer[iloc + 5]; int c2 = buffer[iloc + 6]; @@ -1432,7 +1563,7 @@ static boolean process_sup_mark(void) tex_error("^^^^^^ needs six hex digits, end of input", NULL); } } else { - /* ^^^^XXXX */ + /*tex |^^^^XXXX| */ if ((iloc + 6) <= ilimit) { int c1 = buffer[iloc + 3]; int c2 = buffer[iloc + 4]; @@ -1450,7 +1581,7 @@ static boolean process_sup_mark(void) } } } else { - /* ^^XX */ + /*tex |^^XX| */ if ((iloc + 2) <= ilimit) { int c1 = buffer[iloc + 1]; int c2 = buffer[iloc + 2]; @@ -1460,10 +1591,10 @@ static boolean process_sup_mark(void) return true; } } - /* go on, no error, good old tex */ + /*tex Go on, no error, good old \TEX . */ } } - /* the rest */ + /*tex The rest. */ { int c1 = buffer[iloc + 1]; if (c1 < 0200) { @@ -1484,37 +1615,43 @@ static boolean process_sup_mark(void) return false; } -@ Control sequence names are scanned only when they appear in some line of a -file; once they have been scanned the first time, their |eqtb| location serves as -a unique identification, so \TeX\ doesn't need to refer to the original name any -more except when it prints the equivalent in symbolic form. +/*tex + + Control sequence names are scanned only when they appear in some line of a + file; once they have been scanned the first time, their |eqtb| location + serves as a unique identification, so \TeX\ doesn't need to refer to the + original name any more except when it prints the equivalent in symbolic form. -The program that scans a control sequence has been written carefully in order to -avoid the blowups that might otherwise occur if a malicious user tried something -like `\.{\\catcode\'15=0}'. The algorithm might look at |buffer[ilimit+1]|, but -it never looks at |buffer[ilimit+2]|. + The program that scans a control sequence has been written carefully in order + to avoid the blowups that might otherwise occur if a malicious user tried + something like `\.{\\catcode\'15=0}'. The algorithm might look at + |buffer[ilimit+1]|, but it never looks at |buffer[ilimit+2]|. -If expanded characters like `\.{\^\^A}' or `\.{\^\^df}' appear in or just -following a control sequence name, they are converted to single characters in the -buffer and the process is repeated, slowly but surely. + If expanded characters like `\.{\^\^A}' or `\.{\^\^df}' appear in or just + following a control sequence name, they are converted to single characters in + the buffer and the process is repeated, slowly but surely. + +*/ -@c static boolean check_expanded_code(int *kk); /* below */ static int scan_control_sequence(void) { int retval = mid_line; if (iloc > ilimit) { - cur_cs = null_cs; /* |state| is irrelevant in this case */ + /*tex |state| is irrelevant in this case. */ + cur_cs = null_cs; } else { - register int cat; /* |cat_code(cur_chr)|, usually */ + /*tex |cat_code(cur_chr)|, usually: */ + register int cat; while (1) { int k = iloc; do_buffer_to_unichar(cur_chr, k); do_get_cat_code(cat, cur_chr); if (cat != letter_cmd || k > ilimit) { retval = (cat == spacer_cmd ? skip_blanks : mid_line); - if (cat == sup_mark_cmd && check_expanded_code(&k)) /* If an expanded...; */ + /*tex If an expanded \unknown */ + if (cat == sup_mark_cmd && check_expanded_code(&k)) continue; } else { retval = skip_blanks; @@ -1522,20 +1659,11 @@ static int scan_control_sequence(void) do_buffer_to_unichar(cur_chr, k); do_get_cat_code(cat, cur_chr); } while (cat == letter_cmd && k <= ilimit); - - if (cat == sup_mark_cmd && check_expanded_code(&k)) /* If an expanded...; */ + /*tex If an expanded \unknown */ + if (cat == sup_mark_cmd && check_expanded_code(&k)) continue; if (cat != letter_cmd) { - /* backtrack one character which can be utf */ - /* - decr(k); - if (cur_chr > 0xFFFF) - decr(k); - if (cur_chr > 0x7FF) - decr(k); - if (cur_chr > 0x7F) - decr(k); - */ + /*tex Backtrack one character which can be \UTF. */ if (cur_chr <= 0x7F) { k -= 1; /* in most cases */ } else if (cur_chr > 0xFFFF) { @@ -1545,7 +1673,7 @@ static int scan_control_sequence(void) } else /* if (cur_chr > 0x7F) */ { k -= 2; } - /* now |k| points to first nonletter */ + /*tex Now |k| points to first nonletter. */ } } cur_cs = id_lookup(iloc, k - iloc); @@ -1558,14 +1686,17 @@ static int scan_control_sequence(void) return retval; } -@ Whenever we reach the following piece of code, we will have -|cur_chr=buffer[k-1]| and |k<=ilimit+1| and -|cat=get_cat_code(cat_code_table,cur_chr)|. If an expanded code like \.{\^\^A} or -\.{\^\^df} appears in |buffer[(k-1)..(k+1)]| or |buffer[(k-1)..(k+2)]|, we will -store the corresponding code in |buffer[k-1]| and shift the rest of the buffer -left two or three places. +/*tex + + Whenever we reach the following piece of code, we will have + |cur_chr=buffer[k-1]| and |k<=ilimit+1| and + |cat=get_cat_code(cat_code_table,cur_chr)|. If an expanded code like + \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)..(k+1)]| or + |buffer[(k-1)..(k+2)]|, we will store the corresponding code in |buffer[k-1]| + and shift the rest of the buffer left two or three places. + +*/ -@c static boolean check_expanded_code(int *kk) { int l; @@ -1666,26 +1797,38 @@ static boolean check_expanded_code(int *kk) return false; } -@ All of the easy branches of |get_next| have now been taken care of. There is -one more branch. +/*tex -@c static next_line_retval next_line(void) + All of the easy branches of |get_next| have now been taken care of. There is + one more branch. + +*/ + +static next_line_retval next_line(void) { - boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */ + /*tex A way to end a pseudo file without trailing space: */ + boolean inhibit_eol = false; if (iname > 17) { - /* Read next line of file into |buffer|, or |goto restart| if the file has ended */ + /*tex + + Read next line of file into |buffer|, or |goto restart| if the file + has ended. + + */ incr(line); first = istart; if (!force_eof) { if (iname <= 20) { - if (pseudo_input()) { /* not end of file */ - firm_up_the_line(); /* this sets |ilimit| */ + if (pseudo_input()) { + /*tex Not end of file; set |ilimit|. */ + firm_up_the_line(); line_catcode_table = DEFAULT_CAT_TABLE; if ((iname == 19) && (pseudo_lines(pseudo_files) == null)) inhibit_eol = true; } else if ((every_eof_par != null) && !eof_seen[iindex]) { ilimit = first - 1; - eof_seen[iindex] = true; /* fake one empty line */ + /*tex Fake one empty line. */ + eof_seen[iindex] = true; if (iname != 19) begin_token_list(every_eof_par, every_eof_text); return next_line_restart; @@ -1694,30 +1837,58 @@ one more branch. } } else { if (iname == 21) { - if (luacstring_input()) { /* not end of strings */ - firm_up_the_line(); - line_catcode_table = (short) luacstring_cattable(); - line_partial = (signed char) luacstring_partial(); - if (luacstring_final_line() || line_partial - || line_catcode_table == NO_CAT_TABLE) - inhibit_eol = true; - if (!line_partial) - istate = new_line; - } else { - force_eof = true; + halfword n = null; + int t = luacstring_input(&n); + switch (t) { + case 0: + force_eof = true; + break; + case 1: + /*tex string */ + firm_up_the_line(); + line_catcode_table = (short) luacstring_cattable(); + line_partial = (signed char) luacstring_partial(); + if (luacstring_final_line() || line_partial || line_catcode_table == NO_CAT_TABLE) + inhibit_eol = true; + if (!line_partial) + istate = new_line; + break; + case 2: + /*tex token */ + cur_tok = n; + back_input(); + /*tex Needs checking. */ + return next_line_restart; + break; + case 3: + /*tex node */ + if (n < biggest_char) { + /*tex |0x10FFFF == 1114111| */ + cur_tok = token_val(node_cmd, n); + back_input(); + /*tex Needs checking. */ + return next_line_restart; + } else { + normal_warning("nodes","unable to store reference from lua in tex"); + force_eof = true; + } + break; + default: + force_eof = true; + break; } + } else if (lua_input_ln(cur_file, 0, true)) { + /*tex Not end of file, set |ilimit|. */ + firm_up_the_line(); + line_catcode_table = DEFAULT_CAT_TABLE; + } else if ((every_eof_par != null) && (!eof_seen[iindex])) { + ilimit = first - 1; + /* tex Fake one empty line. */ + eof_seen[iindex] = true; + begin_token_list(every_eof_par, every_eof_text); + return next_line_restart; } else { - if (lua_input_ln(cur_file, 0, true)) { /* not end of file */ - firm_up_the_line(); /* this sets |ilimit| */ - line_catcode_table = DEFAULT_CAT_TABLE; - } else if ((every_eof_par != null) && (!eof_seen[iindex])) { - ilimit = first - 1; - eof_seen[iindex] = true; /* fake one empty line */ - begin_token_list(every_eof_par, every_eof_text); - return next_line_restart; - } else { - force_eof = true; - } + force_eof = true; } } } @@ -1725,7 +1896,7 @@ one more branch. if (tracing_nesting_par > 0) if ((grp_stack[in_open] != cur_boundary) || (if_stack[in_open] != cond_ptr)) if (!((iname == 19) || (iname == 21))) { - /* give warning for some unfinished groups and/or conditionals */ + /*tex Give warning for some unfinished groups and/or conditionals. */ file_warning(); } if ((iname > 21) || (iname == 20)) { @@ -1733,7 +1904,7 @@ one more branch. decr(open_parens); } force_eof = false; - /* lua input or \.{\\scantextokens} */ + /*tex \LUA\ input or \.{\\scantextokens} */ if (iname == 21 || iname == 19) { end_file_reading(); } else { @@ -1748,18 +1919,20 @@ one more branch. else buffer[ilimit] = (packed_ASCII_code) end_line_char_par; first = ilimit + 1; - iloc = istart; /* ready to read */ + iloc = istart; + /*tex We're ready to read. */ } else { if (!terminal_input) { - /* \.{\\read} line has ended */ + /*tex \.{\\read} line has ended */ cur_cmd = 0; cur_chr = 0; - return next_line_return; /* OUTER */ + return next_line_return; } if (input_ptr > 0) { - /* text was inserted during error recovery */ + /*tex Text was inserted during error recovery. */ end_file_reading(); - return next_line_restart; /* resume previous level */ + /*tex Resume previous level. */ + return next_line_restart; } if (selector < log_only) open_log_file(); @@ -1767,12 +1940,13 @@ one more branch. if (end_line_char_inactive) ilimit++; if (ilimit == istart) { - /* previous line was empty */ + /*tex Previous line was empty. */ tprint_nl("(Please type a command or say `\\end')"); } print_ln(); first = istart; - prompt_input("*"); /* input on-line into |buffer| */ + /*tex Input on-line into |buffer| */ + prompt_input("*"); ilimit = last; if (end_line_char_inactive) ilimit--; @@ -1781,9 +1955,11 @@ one more branch. first = ilimit + 1; iloc = istart; } else { - /* + /*tex + Nonstop mode, which is intended for overnight batch processing, never waits for on-line input. + */ fatal_error("*** (job aborted, no legal \\end found)"); } @@ -1791,24 +1967,31 @@ one more branch. return next_line_ok; } -@ Let's consider now what happens when |get_next| is looking at a token list. +/*tex + + Let's consider now what happens when |get_next| is looking at a token list. + +*/ -@c static boolean get_next_tokenlist(void) { register halfword t = token_info(iloc); - iloc = token_link(iloc); /* move to next */ + /*tex Move to next. */ + iloc = token_link(iloc); if (t >= cs_token_flag) { - /* a control sequence token */ + /*tex A control sequence token */ cur_cs = t - cs_token_flag; cur_cmd = eq_type(cur_cs); if (cur_cmd >= outer_call_cmd) { if (cur_cmd == dont_expand_cmd) { - /* - Get the next token, suppressing expansion. The present point in the program - is reached only when the |expand| routine has inserted a special marker into - the input. In this special case, |token_info(iloc)| is known to be a control - sequence token, and |token_link(iloc)=null|. + /*tex + + Get the next token, suppressing expansion. The present point + in the program is reached only when the |expand| routine has + inserted a special marker into the input. In this special + case, |token_info(iloc)| is known to be a control sequence + token, and |token_link(iloc)=null|. + */ cur_cs = token_info(iloc) - cs_token_flag; iloc = null; @@ -1834,7 +2017,7 @@ static boolean get_next_tokenlist(void) align_state--; break; case out_param_cmd: - /* Insert macro parameter and |goto restart|; */ + /*tex Insert macro parameter and |goto restart|. */ begin_token_list(param_stack[param_start + cur_chr - 1], parameter); return false; break; @@ -1843,61 +2026,73 @@ static boolean get_next_tokenlist(void) return true; } -@ Now we're ready to take the plunge into |get_next| itself. Parts of this -routine are executed more often than any other instructions of \TeX. -@^mastication@>@^inner loop@> +/*tex + + Now we're ready to take the plunge into |get_next| itself. Parts of this + routine are executed more often than any other instructions of \TeX. -@ sets |cur_cmd|, |cur_chr|, |cur_cs| to next token + This sets |cur_cmd|, |cur_chr|, |cur_cs| to next token: + +*/ -@c void get_next(void) { RESTART: cur_cs = 0; if (istate != token_list) { - /* Input from external file, |goto restart| if no input found */ + /*tex Input from external file, |goto restart| if no input found. */ if (!get_next_file()) goto RESTART; } else { if (iloc == null) { end_token_list(); - goto RESTART; /* list exhausted, resume previous level */ + /*tex List exhausted, resume previous level. */ + goto RESTART; } else if (!get_next_tokenlist()) { - goto RESTART; /* parameter needs to be expanded */ + /*tex Parameter needs to be expanded. */ + goto RESTART; } } - /* If an alignment entry has just ended, take appropriate action */ + /*tex If an alignment entry has just ended, take appropriate action. */ if ((cur_cmd == tab_mark_cmd || cur_cmd == car_ret_cmd) && align_state == 0) { insert_vj_template(); goto RESTART; } } -@ Since |get_next| is used so frequently in \TeX, it is convenient to define -three related procedures that do a little more: - -\yskip\hang|get_token| not only sets |cur_cmd| and |cur_chr|, it also sets -|cur_tok|, a packed halfword version of the current token. - -\yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like |get_token|, -but if the current token turns out to be a user-defined control sequence (i.e., a -macro call), or a conditional, or something like \.{\\topmark} or -\.{\\expandafter} or \.{\\csname}, it is eliminated from the input by beginning -the expansion of the macro or the evaluation of the conditional. - -\yskip\hang|x_token| is like |get_x_token| except that it assumes that |get_next| -has already been called. - -\yskip\noindent In fact, these three procedures account for almost every use of -|get_next|. +/*tex + + Since |get_next| is used so frequently in \TeX, it is convenient to define + three related procedures that do a little more: + + \startitemize + \startitem + |get_token| not only sets |cur_cmd| and |cur_chr|, it also sets + |cur_tok|, a packed halfword version of the current token. + \stopitem + \startitem + |get_x_token|, meaning ``get an expanded token,'' is like + |get_token|, but if the current token turns out to be a user-defined + control sequence (i.e., a macro call), or a conditional, or something + like \.{\\topmark} or \.{\\expandafter} or \.{\\csname}, it is + eliminated from the input by beginning the expansion of the macro or + the evaluation of the conditional. + \stopitem + \startitem + |x_token| is like |get_x_token| except that it assumes that + |get_next| has already been called. + \stopitem + \stopitemize + + In fact, these three procedures account for almost every use of |get_next|. + No new control sequences will be defined except during a call of |get_token|, + or when \.{\\csname} compresses a token list, because + |no_new_control_sequence| is always |true| at other times. + + This sets |cur_cmd|, |cur_chr|, |cur_tok|: -No new control sequences will be defined except during a call of |get_token|, or -when \.{\\csname} compresses a token list, because |no_new_control_sequence| is -always |true| at other times. - -@ sets |cur_cmd|, |cur_chr|, |cur_tok| +*/ -@c void get_token(void) { no_new_control_sequence = false; @@ -1909,14 +2104,16 @@ void get_token(void) cur_tok = cs_token_flag + cur_cs; } -@ changes the string |s| to a token list +/*tex This changes the string |s| to a token list. */ -@c halfword string_to_toks(const char *ss) { - halfword p; /* tail of the token list */ - halfword q; /* new node being added to the token list via |store_new_token| */ - halfword t; /* token being appended */ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex token being appended */ + halfword t; const char *s = ss; const char *se = ss + strlen(s); p = temp_token_head; @@ -1933,29 +2130,37 @@ halfword string_to_toks(const char *ss) return token_link(temp_token_head); } -@ The token lists for macros and for other things like \.{\\mark} and -\.{\\output} and \.{\\write} are produced by a procedure called |scan_toks|. +/*tex -Before we get into the details of |scan_toks|, let's consider a much simpler -task, that of converting the current string into a token list. The |str_toks| -function does this; it classifies spaces as type |spacer| and everything else as -type |other_char|. + The token lists for macros and for other things like \.{\\mark} and + \.{\\output} and \.{\\write} are produced by a procedure called |scan_toks|. -The token list created by |str_toks| begins at |link(temp_token_head)| and ends -at the value |p| that is returned. (If |p=temp_token_head|, the list is empty.) + Before we get into the details of |scan_toks|, let's consider a much simpler + task, that of converting the current string into a token list. The |str_toks| + function does this; it classifies spaces as type |spacer| and everything else + as type |other_char|. -|lua_str_toks| is almost identical, but it also escapes the three symbols that -|lua| considers special while scanning a literal string + The token list created by |str_toks| begins at |link(temp_token_head)| and + ends at the value |p| that is returned. (If |p=temp_token_head|, the list is + empty.) -@ changes the string |str_pool[b..pool_ptr]| to a token list + |lua_str_toks| is almost identical, but it also escapes the three symbols + that |lua| considers special while scanning a literal string. + + This changes the string |str_pool[b..pool_ptr]| to a token list: + +*/ -@c halfword lua_str_toks(lstring b) { - halfword p; /* tail of the token list */ - halfword q; /* new node being added to the token list via |store_new_token| */ - halfword t; /* token being appended */ - unsigned char *k; /* index into string */ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex token being appended */ + halfword t; + /*tex index into string */ + unsigned char *k; p = temp_token_head; set_token_link(p, null); k = (unsigned char *) b.s; @@ -1978,18 +2183,25 @@ halfword lua_str_toks(lstring b) return p; } -@ Incidentally, the main reason for wanting |str_toks| is the function -|the_toks|, which has similar input/output characteristics. +/*tex + + Incidentally, the main reason for wanting |str_toks| is the function + |the_toks|, which has similar input/output characteristics. -@ changes the string |str_pool[b..pool_ptr]| to a token list + This changes the string |str_pool[b..pool_ptr]| to a token list: + +*/ -@c halfword str_toks(lstring s) { - halfword p; /* tail of the token list */ - halfword q; /* new node being added to the token list via |store_new_token| */ - halfword t; /* token being appended */ - unsigned char *k, *l; /* index into string */ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex token being appended */ + halfword t; + /*tex index into string */ + unsigned char *k, *l; p = temp_token_head; set_token_link(p, null); k = s.s; @@ -2006,18 +2218,25 @@ halfword str_toks(lstring s) return p; } -/* - hh: most of the converter is similar to the one i made for macro so at some point i - can make a helper; also todo: there is no need to go through the pool +/*tex + + Most of the converter is similar to the one i made for macro so at some point + I can make a helper; also todo: there is no need to go through the pool. */ +/*tex Change the string |str_pool[b..pool_ptr]| to a token list. */ + halfword str_scan_toks(int ct, lstring s) -{ /* changes the string |str_pool[b..pool_ptr]| to a token list */ - halfword p; /* tail of the token list */ - halfword q; /* new node being added to the token list via |store_new_token| */ - halfword t; /* token being appended */ - unsigned char *k, *l; /* index into string */ +{ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex token being appended */ + halfword t; + /*tex index into string */ + unsigned char *k, *l; int cc; p = temp_token_head; set_token_link(p, null); @@ -2028,7 +2247,7 @@ halfword str_scan_toks(int ct, lstring s) k += utf8_size(t); cc = get_cat_code(ct,t); if (cc == 0) { - /* we have a potential control sequence so we check for it */ + /*tex We have a potential control sequence so we check for it. */ int _lname = 0 ; int _s = 0 ; int _c = 0 ; @@ -2042,7 +2261,7 @@ halfword str_scan_toks(int ct, lstring s) k += _s ; _lname = _lname + _s ; } else if (_c == 10) { - /* we ignore a trailing space like normal scanning does */ + /*tex We ignore a trailing space like normal scanning does. */ k += _s ; break ; } else { @@ -2050,7 +2269,7 @@ halfword str_scan_toks(int ct, lstring s) } } if (_s > 0) { - /* we have a potential \cs */ + /*tex We have a potential |\cs|. */ _cs = string_lookup((const char *) _name, _lname); if (_cs == undefined_control_sequence) { /* let's play safe and backtrack */ @@ -2060,15 +2279,24 @@ halfword str_scan_toks(int ct, lstring s) t = cs_token_flag + _cs; } } else { - /* just a character with some meaning, so \unknown becomes effectively */ - /* \\unknown assuming that \\ has some useful meaning of course */ + /*tex + + Just a character with some meaning, so |\unknown| becomes + effectively |\unknown| assuming that |\\| has some useful + meaning of course. + + */ t = cc * (1<<21) + t ; k = _name ; } } else { - /* whatever token, so for instance $x^2$ just works given a tex */ - /* catcode regime */ + /*tex + + Whatever token, so for instance $x^2$ just works given a \TEX\ + catcode regime. + + */ t = cc * (1<<21) + t ; } fast_store_new_token(t); @@ -2077,171 +2305,227 @@ halfword str_scan_toks(int ct, lstring s) return p; } -@ Here's part of the |expand| subroutine that we are now ready to complete: +/*tex + + Here's part of the |expand| subroutine that we are now ready to complete: + +*/ -@c void ins_the_toks(void) { (void) the_toks(); ins_list(token_link(temp_token_head)); } -#define set_toks_register(n,t,g) { \ +#define set_toks_register(n,t,g) do { \ int a = (g>0) ? 4 : 0; \ halfword ref = get_avail(); \ set_token_ref_count(ref, 0); \ set_token_link(ref, token_link(t)); \ define(n + toks_base, call_cmd, ref); \ -} +} while (0) + +#define append_copied_toks_list(s,t) do { \ + halfword p; \ + halfword q; \ + p = temp_token_head; \ + set_token_link(p, null); \ + while (s != null) { \ + fast_store_new_token(token_info(s)); \ + s = token_link(s); \ + } \ + while (t != null) { \ + fast_store_new_token(token_info(t)); \ + t = token_link(t); \ + } \ + } while (0) + + +/*tex + + \starttabulate[|T||T||] + \NC 0 \NC \type {toksapp} \NC 1 \NC \type {etoksapp} \NC \NR + \NC 2 \NC \type {tokspre} \NC 3 \NC \type {etokspre} \NC \NR + \NC 4 \NC \type {gtoksapp} \NC 5 \NC \type {xtoksapp} \NC \NR + \NC 6 \NC \type {gtokspre} \NC 7 \NC \type {xtokspre} \NC \NR + \stoptabulate + +*/ void combine_the_toks(int how) { - halfword nt; + halfword source = null; + halfword target = null; + halfword append = (how == 0) || (how == 1) || (how == 4) || (how == 5); + halfword expand = odd(how); + halfword global = how > 3; + halfword nt, ns, s, t, p, q, h; get_x_token(); - /* target */ + /*tex The target. */ if (cur_cmd == assign_toks_cmd) { + /*tex Check range. */ nt = equiv(cur_cs) - toks_base; - /* check range */ } else { back_input(); - scan_int(); + scan_register_num(); nt = cur_val; } - /* source */ + /*tex The source. */ do { get_x_token(); } while (cur_cmd == spacer_cmd); if (cur_cmd == left_brace_cmd) { - halfword x, source; back_input(); - x = scan_toks(false,how > 1); /* expanded or not */ + scan_toks(false,expand); source = def_ref; - /* action */ + /*tex The action. */ if (source != null) { - halfword target = toks(nt); + target = toks(nt); if (target == null) { - set_toks_register(nt,source,0); + set_toks_register(nt,source,global); } else { - halfword s = token_link(source); + s = token_link(source); if (s != null) { - halfword t = token_link(target); + t = token_link(target); if (t == null) { - /* can this happen ? */ + /*tex Can this happen? */ set_token_link(target, s); - } else if (odd(how)) { - /* prepend */ - if (cur_level != eq_level_field(eqtb[toks_base+nt])) { - halfword p = temp_token_head; - halfword q; - set_token_link(p, s); /* s = head, x = tail */ - p = x; - while (t != null) { - fast_store_new_token(token_info(t)); - t = token_link(t); + } else if (append) { + /*tex Append. */ + if (token_ref_count(target) == 0) { + p = t; + while (token_link(p) != null) { + p = token_link(p); + } + while (s != null) { + fast_store_new_token(token_info(s)); + s = token_link(s); } - set_toks_register(nt,temp_token_head,0); } else { - set_token_link(x,t); - set_token_link(target,s); + token_ref_count(target)--; + append_copied_toks_list(t,s); + set_toks_register(nt,temp_token_head,global); } } else { - /* append */ - if (cur_level != eq_level_field(eqtb[toks_base+nt])) { - halfword p = temp_token_head; - halfword q; - set_token_link(p, null); - while (t != null) { - fast_store_new_token(token_info(t)); - t = token_link(t); + /* prepend */ + if (token_ref_count(target) == 0) { + h = null; + p = null ; + while (s != null) { + fast_store_new_token(token_info(s)); + if (h == null) { + h = p; + } + s = token_link(s); } - set_token_link(p,s); - set_toks_register(nt,temp_token_head,0); + set_token_link(p,t); + set_token_link(target,h); } else { - while (token_link(t) != null) { - t = token_link(t); - } - set_token_link(t,s); + token_ref_count(target)--; + append_copied_toks_list(s,t); + set_toks_register(nt,temp_token_head,global); } } } } } } else { - halfword source, ns; if (cur_cmd == assign_toks_cmd) { ns = equiv(cur_cs) - toks_base; - /* check range */ + /*tex Check range. */ } else { - back_input(); - scan_int(); + scan_register_num(); ns = cur_val; } - /* action */ + /*tex The action. */ source = toks(ns); if (source != null) { - halfword target = toks(nt); + target = toks(nt); if (target == null) { + /*tex The assign. */ + token_ref_count(source)++; equiv(toks_base+nt) = source; - equiv(toks_base+ns) = null; + return; + } + s = token_link(source); + t = token_link(target); + if (append) { + /*tex Append. */ + if (token_ref_count(target) == 0) { + p = t; + while (token_link(p) != null) { + p = token_link(p); + } + while (s != null) { + fast_store_new_token(token_info(s)); + s = token_link(s); + } + } else { + token_ref_count(target)--; + append_copied_toks_list(t,s); + set_toks_register(nt,temp_token_head,global); + } } else { - halfword s = token_link(source); - if (s != null) { - halfword t = token_link(target); - if (t == null) { - set_token_link(target, s); - } else if (odd(how)) { - /* prepend */ - halfword x = s; - while (token_link(x) != null) { - x = token_link(x); - } - set_token_link(x,t); - set_token_link(target,s); - } else { - /* append */ - while (token_link(t) != null) { - t = token_link(t); + /*tex Prepend. */ + if (token_ref_count(target) == 0) { + h = null; + p = null; + while (s != null) { + fast_store_new_token(token_info(s)); + if (h == null) { + h = p; } - set_token_link(t,s); + s = token_link(s); } - equiv(toks_base+ns) = null; + set_token_link(p,t); + set_token_link(target,h); + } else { + token_ref_count(target)--; + append_copied_toks_list(s,t); + set_toks_register(nt,temp_token_head,global); } } } } } -@ This routine, used in the next one, prints the job name, possibly modified by -the |process_jobname| callback. +/*tex + + This routine, used in the next one, prints the job name, possibly modified by + the |process_jobname| callback. + +*/ -@c static void print_job_name(void) { - if (job_name) { - char *s, *ss; /* C strings for jobname before and after processing */ - int callback_id, lua_retval; - s = (char*)str_string(job_name); - callback_id = callback_defined(process_jobname_callback); - if (callback_id > 0) { - lua_retval = run_callback(callback_id, "S->S", s, &ss); - if ((lua_retval == true) && (ss != NULL)) - s = ss; - } - tprint(s); - } else { - print(job_name); - } + if (job_name) { + /*tex C strings for jobname before and after processing. */ + char *s, *ss; + int callback_id, lua_retval; + s = (char*)str_string(job_name); + callback_id = callback_defined(process_jobname_callback); + if (callback_id > 0) { + lua_retval = run_callback(callback_id, "S->S", s, &ss); + if ((lua_retval == true) && (ss != NULL)) + s = ss; + } + tprint(s); + } else { + print(job_name); + } } -@ Here is a routine that print the result of a convert command, using the -argument |i|. It returns |false | if it does not know to print the code |c|. The -function exists because lua code and tex code can both call it to convert -something. +/*tex -@ Parse optional lua state integer, or an instance name to be stored in |sn| and -get the next non-blank non-relax non-call token. + Here is a routine that print the result of a convert command, using the + argument |i|. It returns |false | if it does not know to print the code |c|. + The function exists because lua code and tex code can both call it to convert + something. -@c + Parse optional \LUA\ state integer, or an instance name to be stored in |sn| + and get the next non-blank non-relax non-call token. + +*/ int scan_lua_state(void) { @@ -2263,15 +2547,18 @@ int scan_lua_state(void) return sn; } -@ The procedure |conv_toks| uses |str_toks| to insert the token list for -|convert| functions into the scanner; `\.{\\outer}' control sequences are allowed -to follow `\.{\\string}' and `\.{\\meaning}'. +/*tex + + The procedure |conv_toks| uses |str_toks| to insert the token list for + |convert| functions into the scanner; `\.{\\outer}' control sequences are + allowed to follow `\.{\\string}' and `\.{\\meaning}'. -The extra temp string |u| is needed because |pdf_scan_ext_toks| incorporates any -pending string in its output. In order to save such a pending string, we have to -create a temporary string that is destroyed immediately after. + The extra temp string |u| is needed because |pdf_scan_ext_toks| incorporates + any pending string in its output. In order to save such a pending string, we + have to create a temporary string that is destroyed immediately after. + +*/ -@c #define push_selector { \ old_setting = selector; \ selector = new_string; \ @@ -2327,6 +2614,8 @@ static int do_variable_pdf(halfword c) else if (scan_keyword("pkfixeddpi")) { do_variable_backend_int(c_pdf_pk_fixed_dpi); } else if (scan_keyword("suppressoptionalinfo")) { do_variable_backend_int(c_pdf_suppress_optional_info); } else if (scan_keyword("omitcidset")) { do_variable_backend_int(c_pdf_omit_cidset); } + else if (scan_keyword("omitcharset")) { do_variable_backend_int(c_pdf_omit_charset); } + else if (scan_keyword("recompress")) { do_variable_backend_int(c_pdf_recompress); } else if (scan_keyword("horigin")) { do_variable_backend_dimen(d_pdf_h_origin); } else if (scan_keyword("vorigin")) { do_variable_backend_dimen(d_pdf_v_origin); } @@ -2353,23 +2642,30 @@ static int do_feedback_dvi(halfword c) return 0; } -/* codes not really needed but cleaner when testing */ +/*tex Codes not really needed but cleaner when testing */ #define pdftex_version 140 /* these values will not change any more */ #define pdftex_revision "0" /* these values will not change any more */ static int do_feedback_pdf(halfword c) { - int old_setting; /* holds |selector| setting */ - int save_scanner_status; /* |scanner_status| upon entry */ - halfword save_def_ref; /* |def_ref| upon entry, important if inside `\.{\\message}' */ + /*tex holds |selector| setting */ + int old_setting; + /*tex |scanner_status| upon entry */ + int save_scanner_status; + /*tex |def_ref| upon entry, important if inside `\.{\\message}' */ + halfword save_def_ref; halfword save_warning_index; - boolean bool; /* temp boolean */ - str_number s; /* first temp string */ - int ff; /* for use with |set_ff| */ - str_number u = 0; /* third temp string, will become non-nil if a string is already being built */ - char *str; /* color stack init str */ - + /*tex temp boolean */ + boolean bool; + /*tex first temp string */ + str_number s; + /*tex for use with |set_ff| */ + int ff; + /*tex third temp string, will become non-nil if a string is already being built */ + str_number u = 0; + /*tex color stack init str */ + char *str; if (scan_keyword("lastlink")) { push_selector; print_int(pdf_last_link); @@ -2394,7 +2690,7 @@ static int do_feedback_pdf(halfword c) pop_selector; } else if (scan_keyword("creationdate")) { ins_list(string_to_toks(getcreationdate(static_pdf))); - /* no further action */ + /*tex No further action. */ return 2; } else if (scan_keyword("fontname")) { scan_font_ident(); @@ -2488,19 +2784,27 @@ static int do_feedback_pdf(halfword c) void conv_toks(void) { - int old_setting; /* holds |selector| setting */ + /*tex holds |selector| setting */ + int old_setting; halfword p, q; - int save_scanner_status; /* |scanner_status| upon entry */ - halfword save_def_ref; /* |def_ref| upon entry, important if inside `\.{\\message}' */ + /*tex |scanner_status| upon entry */ + int save_scanner_status; + /*tex |def_ref| upon entry, important if inside `\.{\\message}' */ + halfword save_def_ref; halfword save_warning_index; - boolean bool; /* temp boolean */ - str_number s; /* first temp string */ - int sn; /* lua chunk name */ - str_number u = 0; /* third temp string, will become non-nil if a string is already being built */ - int c = cur_chr; /* desired type of conversion */ + /*tex temp boolean */ + boolean bool; + /*tex first temp string */ + str_number s; + /*tex lua chunk name */ + int sn; + /*tex third temp string, will become non-nil if a string is already being built */ + str_number u = 0; + /*tex desired type of conversion */ + int c = cur_chr; str_number str; int i = 0; - /* Scan the argument for command |c| */ + /*tex Scan the argument for command |c|. */ switch (c) { case number_code: scan_int(); @@ -2520,7 +2824,20 @@ void conv_toks(void) if (luacstrings > 0) lua_string_start(); } - /* no further action */ + return; + break; + case lua_bytecode_code: + scan_int(); + if (cur_val < 0 || cur_val > 65535) { + normal_error("luabytecode", "invalid number"); + } else { + u = save_cur_string(); + luacstrings = 0; + luabytecodecall(cur_val); + restore_cur_string(u); + if (luacstrings > 0) + lua_string_start(); + } return; break; case lua_code: @@ -2537,10 +2854,10 @@ void conv_toks(void) luacstrings = 0; luatokencall(s, sn); delete_token_ref(s); - restore_cur_string(u); /* TODO: check this, was different */ + restore_cur_string(u); if (luacstrings > 0) lua_string_start(); - /* no further action */ + /*tex No further action. */ return; break; case expanded_code: @@ -2554,7 +2871,56 @@ void conv_toks(void) ins_list(token_link(def_ref)); def_ref = save_def_ref; restore_cur_string(u); - /* no further action */ + /*tex No further action. */ + return; + break; + case immediate_assignment_code: + case immediate_assigned_code: + /*tex + + This is on-the-road-to-bachotex brain-wave but it needs a bit + more testing. A first variant did more in sequence till a relax + of spacer was seen (like do_assignments). It permits for instance + setting counters in full expansion. + + */ + save_scanner_status = scanner_status; + save_warning_index = warning_index; + save_def_ref = def_ref; + u = save_cur_string(); + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (c == immediate_assignment_code) { + /*tex one-step do_assignment */ + if (cur_cmd > max_non_prefixed_command) { + set_box_allowed = false; + prefixed_command(); + set_box_allowed = true; + } + /*tex done */ + } else { + /*tex pseudo token list do_assignment */ + if (cur_cmd == left_brace_cmd) { + while (1) { + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd == right_brace_cmd) { + break; + } else { + set_box_allowed = false; + prefixed_command(); + set_box_allowed = true; + } + } + } + /*tex done */ + } + warning_index = save_warning_index; + scanner_status = save_scanner_status; + def_ref = save_def_ref; + restore_cur_string(u); return; break; case math_style_code: @@ -2629,7 +2995,7 @@ void conv_toks(void) free(escstr.s); return; } - /* no further action */ + /*tex no further action */ break; case font_id_code: scan_font_ident(); @@ -2672,9 +3038,12 @@ void conv_toks(void) p = list_ptr(box(cur_val)); if (p != null) { p = tail_of_list(p); - /* - there can be a leftskip, rightskip, penalty and yes, also a disc node with a nesting - node that points to glue spec ... and we don't want to analyze that messy lot + /*tex + + There can be a leftskip, rightskip, penalty and yes, also a + disc node with a nesting node that points to glue spec ... + and we don't want to analyze that messy lot. + */ while ((p != null) && (type(p) == glue_node)) { p = alink(p); @@ -2685,10 +3054,12 @@ void conv_toks(void) if ((q != null) && ((type(q) == margin_kern_node) && (subtype(q) == right_side))) { p = q; } else { - /* - officially we should look in the replace but currently protrusion doesn't - work anyway with "foo\discretionary{}{}{bar-} " (no following char) so we - don't need it now + /*tex + + Officially we should look in the replace but + currently protrusion doesn't work anyway with + "foo\discretionary{}{}{bar-} " (no following + char) so we don't need it now. */ } } @@ -2832,7 +3203,7 @@ void do_feedback(void) return ; } if (done==0) { - /* we recover */ + /*tex We recover. */ normal_warning("pdf backend","unexpected use of \\pdffeedback"); return; } else if (done==2) { @@ -2857,7 +3228,7 @@ void do_variable(void) case dvi_variable_code: done = do_variable_dvi(c); if (done==0) { - /* we recover */ + /*tex We recover. */ normal_warning("dvi backend","unexpected use of \\dvivariable"); } return; @@ -2865,7 +3236,7 @@ void do_variable(void) case pdf_variable_code: done = do_variable_pdf(c); if (done==0) { - /* we recover */ + /*tex We recover. */ normal_warning("pdf backend","unexpected use of \\pdfvariable"); } return; @@ -2876,33 +3247,8 @@ void do_variable(void) } } -/* - The following code is not used as we can only set math options and not query them. If - an option is really important we will provide a proper variable. Most options are not - meant for users anyway but for development. -*/ - -/* +/*tex This boolean is keeping track of the lua string escape state */ -#define do_mathoption_int(i) \ - cur_cmd = assign_int_cmd; \ - cur_val = mathoption_int_base + i; \ - cur_tok = token_val(cur_cmd, cur_val); \ - back_input(); - -void do_mathoption(void) -{ - if (scan_keyword("old")) { do_mathoption_int(c_mathoption_no_italic_compensation_code); } - if (scan_keyword("noitaliccompensation")) { do_mathoption_int(c_mathoption_no_char_italic_code); } - else if (scan_keyword("nocharitalic")) { do_mathoption_int(c_mathoption_use_old_fraction_scaling_code); } - else if (scan_keyword("useoldfractionscaling")) { do_mathoption_int(c_mathoption_old_code); } - else if (scan_keyword("umathcodemeaning")) { do_mathoption_int(c_mathoption_umathcode_meaning_code); } -} - -*/ - -@ This boolean is keeping track of the lua string escape state -@c boolean in_lua_escape; static int the_convert_string_dvi(halfword c, int i) @@ -2948,7 +3294,7 @@ static int the_convert_string_pdf(halfword c, int i) str_number the_convert_string(halfword c, int i) { - int old_setting; /* saved |selector| setting */ + int old_setting; str_number ret = 0; boolean done = true ; old_setting = selector; @@ -3017,14 +3363,14 @@ str_number the_convert_string(halfword c, int i) case font_identifier_code: print_font_identifier(i); break; - /* backend: this might become obsolete */ + /*tex Backend: this might become obsolete! */ case dvi_feedback_code: done = the_convert_string_dvi(c,i); break; case pdf_feedback_code: done = the_convert_string_pdf(c,i); break; - /* done */ + /*tex done */ default: done = false; break; @@ -3036,16 +3382,24 @@ str_number the_convert_string(halfword c, int i) return ret; } -@ Another way to create a token list is via the \.{\\read} command. The sixteen -files potentially usable for reading appear in the following global variables. -The value of |read_open[n]| will be |closed| if stream number |n| has not been -opened or if it has been fully read; |just_open| if an \.{\\openin} but not a -\.{\\read} has been done; and |normal| if it is open and ready to read the next -line. +/*tex + + Another way to create a token list is via the \.{\\read} command. The sixteen + files potentially usable for reading appear in the following global + variables. The value of |read_open[n]| will be |closed| if stream number |n| + has not been opened or if it has been fully read; |just_open| if an + \.{\\openin} but not a \.{\\read} has been done; and |normal| if it is open + and ready to read the next line. + +*/ + +/*tex used for \.{\\read} */ + +FILE *read_file[16]; -@c -FILE *read_file[16]; /* used for \.{\\read} */ -int read_open[17]; /* state of |read_file[n]| */ +/*tex state of |read_file[n]| */ + +int read_open[17]; void initialize_read(void) { @@ -3054,41 +3408,51 @@ void initialize_read(void) read_open[k] = closed; } -@ The |read_toks| procedure constructs a token list like that for any macro -definition, and makes |cur_val| point to it. Parameter |r| points to the control -sequence that will receive this token list. +/*tex + + The |read_toks| procedure constructs a token list like that for any macro + definition, and makes |cur_val| point to it. Parameter |r| points to the + control sequence that will receive this token list. + +*/ -@c void read_toks(int n, halfword r, halfword j) { - halfword p; /* tail of the token list */ - halfword q; /* new node being added to the token list via |store_new_token| */ - int s; /* saved value of |align_state| */ - int m; /* stream number */ + /*tex tail of the token list */ + halfword p; + /*tex new node being added to the token list via |store_new_token| */ + halfword q; + /*tex saved value of |align_state| */ + int s; + /*tex stream number */ + int m; scanner_status = defining; warning_index = r; p = get_avail(); def_ref = p; set_token_ref_count(def_ref, 0); - p = def_ref; /* the reference count */ + /*tex the reference count */ + p = def_ref; store_new_token(end_match_token); if ((n < 0) || (n > 15)) m = 16; else m = n; s = align_state; - align_state = 1000000; /* disable tab marks, etc. */ + /*tex disable tab marks, etc. */ + align_state = 1000000; do { - /* Input and store tokens from the next line of the file */ + /*tex Input and store tokens from the next line of the file. */ begin_file_reading(); iname = m + 1; if (read_open[m] == closed) { - /* - Input for \.{\\read} from the terminal + /*tex + + Input for \.{\\read} from the terminal. We input on-line into the + |buffer| array, prompting the user explicitly if |n>=0|. The + value of |n| is set negative so that additional prompts will not + be given in the case of multi-line input. - Here we input on-line into the |buffer| array, prompting the user explicitly - if |n>=0|. The value of |n| is set negative so that additional prompts - will not be given in the case of multi-line input. */ if (interaction > nonstop_mode) { if (n < 0) { @@ -3106,11 +3470,12 @@ void read_toks(int n, halfword r, halfword j) } } else if (read_open[m] == just_open) { - /* - Input the first line of |read_file[m]| + /*tex + + Input the first line of |read_file[m]|. The first line of a file + must be treated specially, since |lua_input_ln| must be told not + to start with |get|. - The first line of a file must be treated specially, since |lua_input_ln| - must be told not to start with |get|. */ if (lua_input_ln(read_file[m], (m + 1), false)) { read_open[m] = normal; @@ -3120,10 +3485,11 @@ void read_toks(int n, halfword r, halfword j) } } else { - /* - Input the next line of |read_file[m]| + /*tex + + Input the next line of |read_file[m]|. An empty line is appended + at the end of a |read_file|. - An empty line is appended at the end of a |read_file|. */ if (!lua_input_ln(read_file[m], (m + 1), true)) { lua_a_close_in(read_file[m], (m + 1)); @@ -3146,10 +3512,10 @@ void read_toks(int n, halfword r, halfword j) first = ilimit + 1; iloc = istart; istate = new_line; - /* Handle \.{\\readline} and |goto done|; */ + /*tex Handle \.{\\readline} and |goto done|. */ if (j == 1) { while (iloc <= ilimit) { - /* current line not yet finished */ + /*tex Current line not yet finished. */ do_buffer_to_unichar(cur_chr, iloc); if (cur_chr == ' ') cur_tok = space_token; @@ -3161,11 +3527,11 @@ void read_toks(int n, halfword r, halfword j) while (1) { get_token(); if (cur_tok == 0) { - /* |cur_cmd=cur_chr=0| will occur at the end of the line */ + /*tex |cur_cmd=cur_chr=0| will occur at the end of the line. */ break; } if (align_state < 1000000) { - /* unmatched `\.\}' aborts the line */ + /*tex Unmatched right brace aborts the line. */ do { get_token(); } while (cur_tok != 0); @@ -3183,9 +3549,8 @@ void read_toks(int n, halfword r, halfword j) align_state = s; } -@ return a string from tokens list +/*tex Return a string from tokens list: */ -@c str_number tokens_to_string(halfword p) { int old_setting; @@ -3198,11 +3563,20 @@ str_number tokens_to_string(halfword p) return make_string(); } -@ @c +/*tex + + Values like 512 and 128 also work ok. There is not much to gain in + optimization here. + +*/ + +#define alloci_default 1024 +#define alloci_step 256 + #define make_room(a) \ if ((unsigned)i+a+1>alloci) { \ - ret = xrealloc(ret,(alloci+64)); \ - alloci = alloci + 64; \ + ret = xrealloc(ret,(alloci+alloci_step)); \ + alloci = alloci + alloci_step; \ } #define append_i_byte(a) ret[i++] = (char)(a) @@ -3247,11 +3621,14 @@ str_number tokens_to_string(halfword p) #define is_cat_letter(a) \ (get_char_cat_code(pool_to_unichar(str_string((a)))) == 11) -@ the actual token conversion in this function is now functionally equivalent to -|show_token_list|, except that it always prints the whole token list. TODO: check -whether this causes problems in the lua library. +/*tex + + The actual token conversion in this function is now functionally equivalent + to |show_token_list|, except that it always prints the whole token list. + TODO: check whether this causes problems in the lua library. + +*/ -@c char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) { register int p, c, m; @@ -3262,7 +3639,7 @@ char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) char *ret; int match_chr = '#'; int n = '0'; - unsigned alloci = 1024; + unsigned alloci = alloci_default; int i = 0; p = pp; if (p == null) { @@ -3271,7 +3648,8 @@ char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) return NULL; } ret = xmalloc(alloci); - p = token_link(p); /* skip refcount */ + /*tex Skip refcount. */ + p = token_link(p); if (p != null) { e = escape_char_par; } @@ -3389,7 +3767,7 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz) char *ret; int match_chr = '#'; int n = '0'; - unsigned alloci = 1024; + unsigned alloci = alloci_default; int i = 0; int skipping = 1; p = pp; @@ -3399,13 +3777,14 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz) return NULL; } ret = xmalloc(alloci); - p = token_link(p); /* skip refcount */ + /*tex Skip refcount. */ + p = token_link(p); if (p != null) { e = escape_char_par; } while (p != null) { if (p < (int) fix_mem_min || p > (int) fix_mem_end) { - /* nothing */ + /*tex Nothing done. */ break; } infop = token_info(p); @@ -3413,13 +3792,12 @@ char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz) if (!(inhibit_par && infop == par_token)) { q = infop - cs_token_flag; if (q < hash_base) { - /* nothing */ + /*tex Nothing done. */ } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) { - /* nothing */ + /*tex Nothing done. */ } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) { - /* nothing */ - } else { -if (!skipping) { + /*tex Nothing done. */ + } else if (!skipping) { str_number txt = cs_text(q); sh = makecstring(txt); s = sh; @@ -3440,12 +3818,11 @@ if (!skipping) { } } free(sh); -} } } } else { if (infop < 0) { - /* nothing */ + /*tex Nothing done. */ } else { m = token_cmd(infop); c = token_chr(infop); @@ -3459,54 +3836,54 @@ if (!skipping) { case spacer_cmd: case letter_cmd: case other_char_cmd: -if (!skipping) { - Print_uchar(c); -} + if (!skipping) { + Print_uchar(c); + } break; case mac_param_cmd: -if (!skipping) { - if (!in_lua_escape && (is_in_csname==0)) + if (!skipping) { + if (!in_lua_escape && (is_in_csname==0)) + Print_uchar(c); Print_uchar(c); - Print_uchar(c); -} + } break; case out_param_cmd: -if (!skipping) { - Print_uchar(match_chr); -} + if (!skipping) { + Print_uchar(match_chr); + } if (c <= 9) { -if (!skipping) { - Print_char(c + '0'); -} + if (!skipping) { + Print_char(c + '0'); + } } else { - /* nothing */ + /*tex Nothing done. */ goto EXIT; } break; case match_cmd: match_chr = c; -if (!skipping) { - Print_uchar(c); -} + if (!skipping) { + Print_uchar(c); + } n++; -if (!skipping) { - Print_char(n); -} + if (!skipping) { + Print_char(n); + } if (n > '9') goto EXIT; break; case end_match_cmd: if (c == 0) { -if (!skipping) { - Print_char('-'); - Print_char('>'); -} + if (!skipping) { + Print_char('-'); + Print_char('>'); + } i = 0; -skipping = 0 ; + skipping = 0 ; } break; default: - /* nothing */ + /*tex Nothing done. */ break; } } @@ -3520,7 +3897,6 @@ skipping = 0 ; return ret; } -@ @c lstring *tokenlist_to_lstring(int pp, int inhibit_par) { int siz; @@ -3530,7 +3906,6 @@ lstring *tokenlist_to_lstring(int pp, int inhibit_par) return ret; } -@ @c void free_lstring(lstring * ls) { if (ls == NULL) |