diff options
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/filename.c | 339 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/linebreak.c | 62 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/math.c | 2255 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/mlist.c | 3864 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/postlinebreak.c | 50 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/primitive.c | 668 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/texdeffont.c | 189 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/texnodes.c | 1271 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/texpdf.c | 26 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/textoken.c | 659 |
10 files changed, 9125 insertions, 258 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/filename.c b/Build/source/texk/web2c/luatexdir/tex/filename.c new file mode 100644 index 00000000000..ed347c4ff55 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/filename.c @@ -0,0 +1,339 @@ +/* filename.c + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#include "luatex-api.h" +#include <ptexlib.h> +#include "tokens.h" +#include "commands.h" + +static const char _svn_version[] = + "$Id: filename.c 2086 2009-03-22 15:32:08Z oneiros $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/filename.c $"; + +#define wake_up_terminal() ; +#define clear_terminal() ; + +#define cur_length (pool_ptr - str_start_macro(str_ptr)) + +#define batch_mode 0 /* omits all stops and omits terminal output */ +#define nonstop_mode 1 /* omits all stops */ +#define scroll_mode 2 /* omits error stops */ +#define error_stop_mode 3 /* stops at every opportunity to interact */ +#define biggest_char 1114111 + +/* put |ASCII_code| \# at the end of |str_pool| */ +#define append_char(A) str_pool[pool_ptr++]=(A) +#define str_room(A) check_pool_overflow((pool_ptr+(A))) + +/* + In order to isolate the system-dependent aspects of file names, the + @^system dependencies@> + system-independent parts of \TeX\ are expressed in terms + of three system-dependent + procedures called |begin_name|, |more_name|, and |end_name|. In + essence, if the user-specified characters of the file name are $c_1\ldots c_n$, + the system-independent driver program does the operations + $$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); + \,|end_name|.$$ + These three procedures communicate with each other via global variables. + Afterwards the file name will appear in the string pool as three strings + called |cur_name|\penalty10000\hskip-.05em, + |cur_area|, and |cur_ext|; the latter two are null (i.e., + |""|), unless they were explicitly specified by the user. + + Actually the situation is slightly more complicated, because \TeX\ needs + to know when the file name ends. The |more_name| routine is a function + (with side effects) that returns |true| on the calls |more_name|$(c_1)$, + \dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ + returns |false|; or, it returns |true| and the token following $c_n$ is + something like `\.{\\hbox}' (i.e., not a character). In other words, + |more_name| is supposed to return |true| unless it is sure that the + file name has been completely scanned; and |end_name| is supposed to be able + to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of + whether $|more_name|(c_n)$ returned |true| or |false|. +*/ + +/* Here now is the first of the system-dependent routines for file name scanning. + @^system dependencies@> */ + +static void begin_name(void) +{ + area_delimiter = 0; + ext_delimiter = 0; + quoted_filename = false; +} + +/* And here's the second. The string pool might change as the file name is + being scanned, since a new \.{\\csname} might be entered; therefore we keep + |area_delimiter| and |ext_delimiter| relative to the beginning of the current + string, instead of assigning an absolute address like |pool_ptr| to them. + @^system dependencies@> */ + +static boolean more_name(ASCII_code c) +{ + if (c == ' ' && stop_at_space && (!quoted_filename)) { + return false; + } else if (c == '"') { + quoted_filename = !quoted_filename; + return true; + } else { + str_room(1); + append_char(c); /* contribute |c| to the current string */ + if (ISDIRSEP(c)) { + area_delimiter = cur_length; + ext_delimiter = 0; + } else if (c == '.') + ext_delimiter = cur_length; + return true; + } +} + +/* The third. + @^system dependencies@> + +*/ + +static void end_name(void) +{ + if (str_ptr + 3 > (max_strings + string_offset)) + overflow_string("number of strings", max_strings - init_str_ptr); + /* @:TeX capacity exceeded number of strings}{\quad number of strings@> */ + + if (area_delimiter == 0) { + cur_area = get_nullstr(); + } else { + cur_area = str_ptr; + str_start_macro(str_ptr + 1) = + str_start_macro(str_ptr) + area_delimiter; + incr(str_ptr); + } + if (ext_delimiter == 0) { + cur_ext = get_nullstr(); + cur_name = make_string(); + } else { + cur_name = str_ptr; + str_start_macro(str_ptr + 1) = + str_start_macro(str_ptr) + ext_delimiter - area_delimiter - 1; + incr(str_ptr); + cur_ext = make_string(); + } +} + +/* Now let's consider the ``driver'' routines by which \TeX\ deals with file names + in a system-independent manner. First comes a procedure that looks for a + file name in the input by calling |get_x_token| for the information. +*/ + +void scan_file_name(void) +{ + name_in_progress = true; + begin_name(); + /* @<Get the next non-blank non-call token@>; */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + while (true) { + if ((cur_cmd > other_char_cmd) || (cur_chr > biggest_char)) { /* not a character */ + back_input(); + break; + } + /* If |cur_chr| is a space and we're not scanning a token list, check + whether we're at the end of the buffer. Otherwise we end up adding + spurious spaces to file names in some cases. */ + if ((cur_chr == ' ') && (state != token_list) && (loc > limit) + && !quoted_filename) + break; + if (!more_name(cur_chr)) + break; + get_x_token(); + } + end_name(); + name_in_progress = false; +} + + +/* + Here is a routine that manufactures the output file names, assuming that + |job_name<>0|. It ignores and changes the current settings of |cur_area| + and |cur_ext|. +*/ + +#define pack_cur_name() pack_file_name(cur_name,cur_area,cur_ext) + +void pack_job_name(char *s) +{ /* |s = ".log"|, |".dvi"|, or |format_extension| */ + cur_area = get_nullstr(); + cur_ext = maketexstring(s); + cur_name = job_name; + pack_cur_name(); +} + +/* If some trouble arises when \TeX\ tries to open a file, the following + routine calls upon the user to supply another file name. Parameter~|s| + is used in the error message to identify the type of file; parameter~|e| + is the default extension if none is given. Upon exit from the routine, + variables |cur_name|, |cur_area|, |cur_ext|, and |nameoffile| are + ready for another attempt at file opening. +*/ + +void prompt_file_name(char *s, char *e) +{ + int k; /* index into |buffer| */ + str_number saved_cur_name; /* to catch empty terminal input */ + char prompt[256]; + str_number texprompt; + char *ar, *na, *ex; + saved_cur_name = cur_name; + if (interaction == scroll_mode) { + wake_up_terminal(); + } + ar = xstrdup(makecstring(cur_area)); + na = xstrdup(makecstring(cur_name)); + ex = xstrdup(makecstring(cur_ext)); + if (strcmp(s, "input file name") == 0) { /* @.I can't find file x@> */ + snprintf(prompt, 255, "I can't find file `%s%s%s'.", ar, na, ex); + } else { /*@.I can't write on file x@> */ + snprintf(prompt, 255, "I can't write on file `%s%s%s'.", ar, na, ex); + } + free(ar); + free(na); + free(ex); + texprompt = maketexstring((char *) prompt); + do_print_err(texprompt); + flush_str(texprompt); + if ((strcmp(e, ".tex") == 0) || (strcmp(e, "") == 0)) + show_context(); + tprint_nl("Please type another "); /*@.Please type...@> */ + tprint(s); + if (interaction < scroll_mode) + fatal_error(maketexstring + ("*** (job aborted, file error in nonstop mode)")); + clear_terminal(); + texprompt = maketexstring(": "); + prompt_input(texprompt); + flush_str(texprompt); + begin_name(); + k = first; + while ((buffer[k] == ' ') && (k < last)) + k++; + while (true) { + if (k == last) + break; + if (!more_name(buffer[k])) + break; + k++; + } + end_name(); + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(e); + if (length(cur_name) == 0) + cur_name = saved_cur_name; + pack_cur_name(); +} + + +str_number make_name_string(void) +{ + int k; /* index into |nameoffile| */ + pool_pointer save_area_delimiter, save_ext_delimiter; + boolean save_name_in_progress, save_stop_at_space; + str_number ret; + if ((pool_ptr + namelength > pool_size) || + (str_ptr == max_strings) || (cur_length > 0)) { + ret = maketexstring("?"); + } else { + for (k = 1; k <= namelength; k++) + append_char(nameoffile[k]); + ret = make_string(); + } + /* At this point we also reset |cur_name|, |cur_ext|, and |cur_area| to + match the contents of |nameoffile|. */ + save_area_delimiter = area_delimiter; + save_ext_delimiter = ext_delimiter; + save_name_in_progress = name_in_progress; + save_stop_at_space = stop_at_space; + name_in_progress = true; + begin_name(); + stop_at_space = false; + k = 1; + while ((k <= namelength) && (more_name(nameoffile[k]))) + k++; + stop_at_space = save_stop_at_space; + end_name(); + name_in_progress = save_name_in_progress; + area_delimiter = save_area_delimiter; + ext_delimiter = save_ext_delimiter; + return ret; +} + + + +void print_file_name(str_number n, str_number a, str_number e) +{ + boolean must_quote; /* whether to quote the filename */ + pool_pointer j; /* index into |str_pool| */ + must_quote = false; + if (a != 0) { + j = str_start_macro(a); + while ((!must_quote) && (j < str_start_macro(a + 1))) { + must_quote = (str_pool[j] == ' '); + incr(j); + } + } + if (n != 0) { + j = str_start_macro(n); + while ((!must_quote) && (j < str_start_macro(n + 1))) { + must_quote = (str_pool[j] == ' '); + incr(j); + } + } + if (e != 0) { + j = str_start_macro(e); + while ((!must_quote) && (j < str_start_macro(e + 1))) { + must_quote = (str_pool[j] == ' '); + incr(j); + } + } + /* FIXME: Alternative is to assume that any filename that has to be quoted has + at least one quoted component...if we pick this, a number of insertions + of |print_file_name| should go away. + |must_quote|:=((|a|<>0)and(|str_pool|[|str_start|[|a|]]=""""))or + ((|n|<>0)and(|str_pool|[|str_start|[|n|]]=""""))or + ((|e|<>0)and(|str_pool|[|str_start|[|e|]]="""")); */ + + if (must_quote) + print_char('"'); + if (a != 0) { + for (j = str_start_macro(a); j <= str_start_macro(a + 1) - 1; j++) + if (str_pool[j] != '"') + print_char(str_pool[j]); + } + if (n != 0) { + for (j = str_start_macro(n); j <= str_start_macro(n + 1) - 1; j++) + if (str_pool[j] != '"') + print_char(str_pool[j]); + } + if (e != 0) { + for (j = str_start_macro(e); j <= str_start_macro(e + 1) - 1; j++) + if (str_pool[j] != '"') + print_char(str_pool[j]); + } + if (must_quote) + print_char('"'); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.c b/Build/source/texk/web2c/luatexdir/tex/linebreak.c index 768d1999af8..6f86577dc20 100644 --- a/Build/source/texk/web2c/luatexdir/tex/linebreak.c +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.c @@ -1,9 +1,29 @@ -/* $Id: linebreak.c 1168 2008-04-15 13:43:34Z taco $ */ +/* linebreak.c + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ #include "luatex-api.h" #include <ptexlib.h> #include "nodes.h" +static const char _svn_version[] = + "$Id: linebreak.c 2086 2009-03-22 15:32:08Z oneiros $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/linebreak.c $"; + /* 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 @@ -407,18 +427,11 @@ want to do this without the overhead of |for| loops. The |do_all_six| macro makes such six-tuples convenient. */ -#define save_active_width(a) prev_active_width[(a)] = active_width[(a)] -#define restore_active_width(a) active_width[(a)] = prev_active_width[(a)] - static scaled active_width[10] = { 0 }; /*distance from first active node to~|cur_p| */ static scaled background[10] = { 0 }; /*length of an ``empty'' line */ static scaled break_width[10] = { 0 }; /*length being computed after current break */ static boolean auto_breaking; /*make |auto_breaking| accessible out of |line_break| */ -static boolean try_prev_break; /*force break at the previous legal breakpoint? */ -static halfword prev_legal; /*the previous legal breakpoint */ -static halfword rejected_cur_p; /*the last |cur_p| that has been rejected */ -static boolean before_rejected_cur_p; /*|cur_p| is still before |rejected_cur_p|? */ /* Let's state the principles of the delta nodes more precisely and concisely, so that the following programs will be less obscure. For each legal @@ -900,7 +913,7 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, #define set_break_width_to_background(a) break_width[a]=background[(a)] #define convert_to_break_width(a) \ - varmem[(prev_r+(a))].cint -= (cur_active_width[(a)]+break_width[(a)]) + 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)] @@ -919,8 +932,6 @@ print_feasible_break(halfword cur_p, pointer r, halfword b, integer pi, #define total_font_stretch cur_active_width[8] #define total_font_shrink cur_active_width[9] -#define max_dimen 07777777777 /* $2^{30}-1$ */ - #define left_side 0 #define right_side 1 @@ -1141,11 +1152,12 @@ ext_try_break(integer pi, then |goto continue| if a line from |r| to |cur_p| is infeasible, otherwise record a new feasible break@>; */ artificial_demerits = false; - shortfall = line_width - cur_active_width[1] - - ((break_node(r) == null) - ? init_internal_left_box_width - : passive_last_left_box_width(break_node(r))) - - internal_right_box_width; + 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 (pdf_protrude_chars > 1) { halfword l, o; l = (break_node(r) == null) ? first_p : cur_break(break_node(r)); @@ -1505,7 +1517,8 @@ ext_do_line_break(boolean d, halfword widow_penalties_ptr, int display_widow_penalty, int widow_penalty, - int broken_penalty, halfword final_par_glue) + int broken_penalty, + halfword final_par_glue, halfword pdf_ignored_dimen) { /* DONE,DONE1,DONE2,DONE3,DONE4,DONE5,CONTINUE; */ halfword cur_p, q, r, s; /* miscellaneous nodes of temporary interest */ @@ -1549,7 +1562,8 @@ ext_do_line_break(boolean d, } } else { last_special_line = vinfo(par_shape_ptr + 1) - 1; - second_indent = varmem[(par_shape_ptr + (2 * last_special_line))].cint; + 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; } @@ -1647,9 +1661,10 @@ ext_do_line_break(boolean d, /* /Create an active breakpoint representing the beginning of the paragraph */ auto_breaking = true; cur_p = vlink(temp_head); - assert(alink(cur_p) == temp_head); /* LOCAL: Initialize with first |local_paragraph| node */ - if ((type(cur_p) == whatsit_node) && (subtype(cur_p) == local_par_node)) { + if ((cur_p != null) && (type(cur_p) == whatsit_node) + && (subtype(cur_p) == local_par_node)) { + assert(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); @@ -1670,10 +1685,6 @@ ext_do_line_break(boolean d, } /* /LOCAL: Initialize with first |local_paragraph| node */ set_prev_char_p(null); - prev_legal = null; - rejected_cur_p = null; - try_prev_break = false; - before_rejected_cur_p = false; first_p = cur_p; /* to access the first node of paragraph as the first active node has |break_node=null| */ @@ -2156,7 +2167,8 @@ ext_post_line_break(d, best_bet, last_special_line, second_width, - second_indent, first_width, first_indent, best_line); + second_indent, first_width, first_indent, best_line, + pdf_ignored_dimen); /* /Break the paragraph at the chosen... */ /* Clean up the memory by removing the break nodes; */ clean_up_the_memory(); diff --git a/Build/source/texk/web2c/luatexdir/tex/math.c b/Build/source/texk/web2c/luatexdir/tex/math.c new file mode 100644 index 00000000000..d1e2c2a773b --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/math.c @@ -0,0 +1,2255 @@ +/* math.c + + Copyright 2008-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#include "luatex-api.h" +#include <ptexlib.h> + +#include "nodes.h" +#include "commands.h" +#include "managed-sa.h" + +#include "tokens.h" + +static const char _svn_version[] = + "$Id: math.c 2099 2009-03-24 10:35:01Z taco $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/math.c $"; + +#define mode cur_list.mode_field +#define head cur_list.head_field +#define tail cur_list.tail_field +#define prev_graf cur_list.pg_field +#define eTeX_aux cur_list.eTeX_aux_field +#define delim_ptr eTeX_aux +#define aux cur_list.aux_field +#define prev_depth aux.cint +#define space_factor aux.hh.lhfield +#define incompleat_noad aux.cint + +#define stretching 1 +#define shrinking 2 + +#define append_char(A) str_pool[pool_ptr++]=(A) +#define flush_char pool_ptr-- /* forget the last character in the pool */ +#define cur_length (pool_ptr - str_start_macro(str_ptr)) +#define saved(A) save_stack[save_ptr+(A)].cint + +#define vmode 1 +#define hmode (vmode+max_command_cmd+1) +#define mmode (hmode+max_command_cmd+1) + +#define cur_fam int_par(param_cur_fam_code) +#define text_direction zeqtb[param_text_direction_code].cint + +#define scan_normal_dimen() scan_dimen(false,false,false) + +#define var_code 7 + +extern void rawset_sa_item(sa_tree hed, integer n, integer v); + +/* TODO: not sure if this is the right order */ +#define back_error(A,B) do { \ + OK_to_interrupt=false; \ + back_input(); \ + OK_to_interrupt=true; \ + tex_error(A,B); \ + } while (0) + +int scan_math(pointer); +pointer fin_mlist(pointer); + +#define pre_display_size dimen_par(param_pre_display_size_code) +#define hsize dimen_par(param_hsize_code) +#define display_width dimen_par(param_display_width_code) +#define display_indent dimen_par(param_display_indent_code) +#define math_surround dimen_par(param_math_surround_code) +#define hang_indent dimen_par(param_hang_indent_code) +#define hang_after int_par(param_hang_after_code) +#define every_math loc_par(param_every_math_code) +#define every_display loc_par(param_every_display_code) +#define par_shape_ptr loc_par(param_par_shape_code) + +/* +When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an +{\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). + +*/ + + +void unsave_math(void) +{ + unsave(); + decr(save_ptr); + flush_node_list(text_dir_ptr); + text_dir_ptr = saved(0); +} + + +/* +Sometimes it is necessary to destroy an mlist. The following +subroutine empties the current list, assuming that |abs(mode)=mmode|. +*/ + +void flush_math(void) +{ + flush_node_list(vlink(head)); + flush_node_list(incompleat_noad); + vlink(head) = null; + tail = head; + incompleat_noad = null; +} + +/* Before we can do anything in math mode, we need fonts. */ + +#define MATHFONTSTACK 8 +#define MATHFONTDEFAULT 0 /* == nullfont */ + +static sa_tree math_fam_head = NULL; + +integer fam_fnt(integer fam_id, integer size_id) +{ + integer n = fam_id + (256 * size_id); + return (integer) get_sa_item(math_fam_head, n); +} + +void def_fam_fnt(integer fam_id, integer size_id, integer f, integer lvl) +{ + integer n = fam_id + (256 * size_id); + set_sa_item(math_fam_head, n, f, lvl); + fixup_math_parameters(fam_id, size_id, f, lvl); + if (int_par(param_tracing_assigns_code) > 0) { + begin_diagnostic(); + tprint("{assigning"); + print_char(' '); + print_size(size_id); + print_int(fam_id); + print_char('='); + print_font_identifier(fam_fnt(fam_id, size_id)); + print_char('}'); + end_diagnostic(false); + } +} + +void unsave_math_fam_data(integer gl) +{ + sa_stack_item st; + if (math_fam_head->stack == NULL) + return; + while (math_fam_head->stack_ptr > 0 && + abs(math_fam_head->stack[math_fam_head->stack_ptr].level) + >= (integer) gl) { + st = math_fam_head->stack[math_fam_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(math_fam_head, st.code, st.value); + /* now do a trace message, if requested */ + if (int_par(param_tracing_restores_code) > 0) { + int size_id = st.code / 256; + int fam_id = st.code % 256; + begin_diagnostic(); + tprint("{restoring"); + print_char(' '); + print_size(size_id); + print_int(fam_id); + print_char('='); + print_font_identifier(fam_fnt(fam_id, size_id)); + print_char('}'); + end_diagnostic(false); + } + } + (math_fam_head->stack_ptr)--; + } +} + + + +/* and parameters */ + +static char *math_param_names[] = { + "Umathquad", + "Umathaxis", + "Umathoperatorsize", + "Umathoverbarkern", + "Umathoverbarrule", + "Umathoverbarvgap", + "Umathunderbarkern", + "Umathunderbarrule", + "Umathunderbarvgap", + "Umathradicalkern", + "Umathradicalrule", + "Umathradicalvgap", + "Umathradicaldegreebefore", + "Umathradicaldegreeafter", + "Umathradicaldegreeraise", + "Umathstackvgap", + "Umathstacknumup", + "Umathstackdenomdown", + "Umathfractionrule", + "Umathfractionnumvgap", + "Umathfractionnumup", + "Umathfractiondenomvgap", + "Umathfractiondenomdown", + "Umathfractiondelsize", + "Umathlimitabovevgap", + "Umathlimitabovebgap", + "Umathlimitabovekern", + "Umathlimitdownvgap", + "Umathlimitdownbgap", + "Umathlimitdownkern", + "Umathunderdelimitervgap", + "Umathunderdelimiterbgap", + "Umathoverdelimitervgap", + "Umathoverdelimiterbgap", + "Umathsubshiftdrop", + "Umathsupshiftdrop", + "Umathsubshiftdown", + "Umathsubsupshiftdown", + "Umathsubtopmax", + "Umathsupshiftup", + "Umathsupbottommin", + "Umathsupsubbottommax", + "Umathsubsupvgap", + "Umathspaceafterscript", + "Umathconnectoroverlapmin", + "Umathordordspacing", + "Umathordopspacing", + "Umathordbinspacing", + "Umathordrelspacing", + "Umathordopenspacing", + "Umathordclosespacing", + "Umathordpunctspacing", + "Umathordinnerspacing", + "Umathopordspacing", + "Umathopopspacing", + "Umathopbinspacing", + "Umathoprelspacing", + "Umathopopenspacing", + "Umathopclosespacing", + "Umathoppunctspacing", + "Umathopinnerspacing", + "Umathbinordspacing", + "Umathbinopspacing", + "Umathbinbinspacing", + "Umathbinrelspacing", + "Umathbinopenspacing", + "Umathbinclosespacing", + "Umathbinpunctspacing", + "Umathbininnerspacing", + "Umathrelordspacing", + "Umathrelopspacing", + "Umathrelbinspacing", + "Umathrelrelspacing", + "Umathrelopenspacing", + "Umathrelclosespacing", + "Umathrelpunctspacing", + "Umathrelinnerspacing", + "Umathopenordspacing", + "Umathopenopspacing", + "Umathopenbinspacing", + "Umathopenrelspacing", + "Umathopenopenspacing", + "Umathopenclosespacing", + "Umathopenpunctspacing", + "Umathopeninnerspacing", + "Umathcloseordspacing", + "Umathcloseopspacing", + "Umathclosebinspacing", + "Umathcloserelspacing", + "Umathcloseopenspacing", + "Umathcloseclosespacing", + "Umathclosepunctspacing", + "Umathcloseinnerspacing", + "Umathpunctordspacing", + "Umathpunctopspacing", + "Umathpunctbinspacing", + "Umathpunctrelspacing", + "Umathpunctopenspacing", + "Umathpunctclosespacing", + "Umathpunctpunctspacing", + "Umathpunctinnerspacing", + "Umathinnerordspacing", + "Umathinneropspacing", + "Umathinnerbinspacing", + "Umathinnerrelspacing", + "Umathinneropenspacing", + "Umathinnerclosespacing", + "Umathinnerpunctspacing", + "Umathinnerinnerspacing", + NULL +}; + +#define MATHPARAMSTACK 8 +#define MATHPARAMDEFAULT undefined_math_parameter + +static sa_tree math_param_head = NULL; + +void print_math_param(int param_code) +{ + if (param_code >= 0 && param_code < math_param_last) + tprint_esc(math_param_names[param_code]); + else + tprint("Unknown math parameter code!"); +} + +void def_math_param(int param_id, int style_id, scaled value, int lvl) +{ + integer n = param_id + (256 * style_id); + set_sa_item(math_param_head, n, value, lvl); + if (int_par(param_tracing_assigns_code) > 0) { + begin_diagnostic(); + tprint("{assigning"); + print_char(' '); + print_math_param(param_id); + print_style(style_id); + print_char('='); + print_int(value); + print_char('}'); + end_diagnostic(false); + } +} + +scaled get_math_param(int param_id, int style_id) +{ + integer n = param_id + (256 * style_id); + return (scaled) get_sa_item(math_param_head, n); +} + + +void unsave_math_param_data(integer gl) +{ + sa_stack_item st; + if (math_param_head->stack == NULL) + return; + while (math_param_head->stack_ptr > 0 && + abs(math_param_head->stack[math_param_head->stack_ptr].level) + >= (integer) gl) { + st = math_param_head->stack[math_param_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(math_param_head, st.code, st.value); + /* now do a trace message, if requested */ + if (int_par(param_tracing_restores_code) > 0) { + int param_id = st.code % 256; + int style_id = st.code / 256; + begin_diagnostic(); + tprint("{restoring"); + print_char(' '); + print_math_param(param_id); + print_style(style_id); + print_char('='); + print_int(get_math_param(param_id, style_id)); + print_char('}'); + end_diagnostic(false); + } + } + (math_param_head->stack_ptr)--; + } +} + + +/* saving and unsaving of both */ + +void unsave_math_data(integer gl) +{ + unsave_math_fam_data(gl); + unsave_math_param_data(gl); +} + +void dump_math_data(void) +{ + if (math_fam_head == NULL) + math_fam_head = new_sa_tree(MATHFONTSTACK, MATHFONTDEFAULT); + dump_sa_tree(math_fam_head); + if (math_param_head == NULL) + math_param_head = new_sa_tree(MATHPARAMSTACK, MATHPARAMDEFAULT); + dump_sa_tree(math_param_head); +} + +void undump_math_data(void) +{ + math_fam_head = undump_sa_tree(); + math_param_head = undump_sa_tree(); +} + +/* */ + +void initialize_math(void) +{ + if (math_fam_head == NULL) + math_fam_head = new_sa_tree(MATHFONTSTACK, MATHFONTDEFAULT); + if (math_param_head == NULL) { + math_param_head = new_sa_tree(MATHPARAMSTACK, MATHPARAMDEFAULT); + initialize_math_spacing(); + } + 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=@'75|). 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 */ + +pointer new_noad(void) +{ + pointer p; + p = new_node(ord_noad, normal); + /* all noad fields are zero after this */ + return p; +} + +pointer new_sub_box(pointer cur_box) +{ + pointer p, q; + p = new_noad(); + q = new_node(sub_box_node, 0); + nucleus(p) = q; + math_list(nucleus(p)) = cur_box; + return p; +} + +/* +@ A few more kinds of noads will complete the set: An |under_noad| has its +nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places +an accent over its nucleus; the accent character appears as +|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@> +*/ + +const char *math_style_names[] = { "display", "crampeddisplay", + "text", "crampedtext", + "script", "crampedscript", + "scriptscript", "crampedscriptscript", + NULL +}; + +pointer new_style(small_number s) +{ /* create a style node */ + 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. +*/ + + +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\}\}\}\}\$}'. +*/ + + +void display_normal_noad(pointer p); /* forward */ +void display_fence_noad(pointer p); /* forward */ +void display_fraction_noad(pointer p); /* forward */ + +void show_math_node(pointer p) +{ + switch (type(p)) { + case style_node: + print_style(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 ord_noad: + case op_noad: + case bin_noad: + case rel_noad: + case open_noad: + case close_noad: + case punct_noad: + case inner_noad: + case radical_noad: + case over_noad: + case under_noad: + case vcenter_noad: + case accent_noad: + 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. */ + +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)); +} + +void print_delimiter(pointer p) +{ + integer a; + 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 */ + 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)); + } else { + /* assume this is omega-style */ + a = small_fam(p) * 65536 + small_char(p); + print_hex(a); + a = large_fam(p) * 65536 + large_char(p); + print_hex(a); + } +} + +/* +@ 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@> +*/ + + +void print_subsidiary_data(pointer p, ASCII_code c) +{ /* display a noad field */ + if (cur_length >= depth_threshold) { + if (p != null) + tprint(" []"); + } else { + append_char(c); /* include |c| in the recursion history */ + if (p != null) { + switch (type(p)) { + case math_char_node: + print_ln(); + print_current_string(); + print_fam_and_char(p); + break; + case sub_box_node: + show_node_list(math_list(p)); + break; + case sub_mlist_node: + if (math_list(p) == null) { + print_ln(); + print_current_string(); + tprint("{}"); + } else { + show_node_list(math_list(p)); + } + break; + } + } + flush_char; /* remove |c| from the recursion history */ + } +} + +void print_style(integer c) +{ + if (c <= cramped_script_script_style) { + tprint_esc((char *) math_style_names[c]); + tprint("style"); + } else { + tprint("Unknown style!"); + } +} + + +void display_normal_noad(pointer p) +{ + switch (type(p)) { + case ord_noad: + tprint_esc("mathord"); + break; + case op_noad: + tprint_esc("mathop"); + if (subtype(p) == limits) + tprint_esc("limits"); + else if (subtype(p) == no_limits) + tprint_esc("nolimits"); + break; + case bin_noad: + tprint_esc("mathbin"); + break; + case rel_noad: + tprint_esc("mathrel"); + break; + case open_noad: + tprint_esc("mathopen"); + break; + case close_noad: + tprint_esc("mathclose"); + break; + case punct_noad: + tprint_esc("mathpunct"); + break; + case inner_noad: + tprint_esc("mathinner"); + break; + case over_noad: + tprint_esc("overline"); + break; + case under_noad: + tprint_esc("underline"); + break; + case vcenter_noad: + tprint_esc("vcenter"); + break; + case radical_noad: + if (subtype(p) == 7) + tprint_esc("Udelimiterover"); + else if (subtype(p) == 6) + tprint_esc("Udelimiterunder"); + else if (subtype(p) == 5) + tprint_esc("Uoverdelimiter"); + else if (subtype(p) == 4) + tprint_esc("Uunderdelimiter"); + else if (subtype(p) == 3) + tprint_esc("Uroot"); + else + tprint_esc("radical"); + print_delimiter(left_delimiter(p)); + if (degree(p) != null) { + print_subsidiary_data(degree(p), '/'); + } + break; + case accent_noad: + if (accent_chr(p) != null) { + if (bot_accent_chr(p) != null) { + tprint_esc("Umathaccents"); + print_fam_and_char(accent_chr(p)); + print_fam_and_char(bot_accent_chr(p)); + } else { + tprint_esc("accent"); + print_fam_and_char(accent_chr(p)); + } + } else { + tprint_esc("Umathbotaccent"); + print_fam_and_char(bot_accent_chr(p)); + } + break; + } + print_subsidiary_data(nucleus(p), '.'); + print_subsidiary_data(supscr(p), '^'); + print_subsidiary_data(subscr(p), '_'); +} + +void display_fence_noad(pointer p) +{ + if (subtype(p) == right_noad_side) + tprint_esc("right"); + else if (subtype(p) == left_noad_side) + tprint_esc("left"); + else + tprint_esc("middle"); + print_delimiter(delimiter(p)); +} + +void display_fraction_noad(pointer p) +{ + tprint_esc("fraction, thickness "); + if (thickness(p) == default_code) + tprint("= default"); + else + print_scaled(thickness(p)); + if ((left_delimiter(p) != null) && + ((small_fam(left_delimiter(p)) != 0) || + (small_char(left_delimiter(p)) != 0) || + (large_fam(left_delimiter(p)) != 0) || + (large_char(left_delimiter(p)) != 0))) { + tprint(", left-delimiter "); + print_delimiter(left_delimiter(p)); + } + if ((right_delimiter(p) != null) && + ((small_fam(right_delimiter(p)) != 0) || + (small_char(right_delimiter(p)) != 0) || + (large_fam(right_delimiter(p)) != 0) || + (large_char(right_delimiter(p)) != 0))) { + tprint(", right-delimiter "); + print_delimiter(right_delimiter(p)); + } + print_subsidiary_data(numerator(p), '\\'); + print_subsidiary_data(denominator(p), '/'); +} + +/* This function is for |print_cmd_chr| only */ + +void print_math_comp(halfword chr_code) +{ + switch (chr_code) { + case ord_noad: + tprint_esc("mathord"); + break; + case op_noad: + tprint_esc("mathop"); + break; + case bin_noad: + tprint_esc("mathbin"); + break; + case rel_noad: + tprint_esc("mathrel"); + break; + case open_noad: + tprint_esc("mathopen"); + break; + case close_noad: + tprint_esc("mathclose"); + break; + case punct_noad: + tprint_esc("mathpunct"); + break; + case inner_noad: + tprint_esc("mathinner"); + break; + case under_noad: + tprint_esc("underline"); + break; + default: + tprint_esc("overline"); + break; + } +} + +void print_limit_switch(halfword chr_code) +{ + if (chr_code == limits) + tprint_esc("limits"); + else if (chr_code == no_limits) + tprint_esc("nolimits"); + else + tprint_esc("displaylimits"); +} + + +/* +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|. +*/ + +void new_save_level_math(group_code c) +{ + saved(0) = text_dir_ptr; + text_dir_ptr = new_dir(math_direction); + incr(save_ptr); + new_save_level(c); + eq_word_define(static_int_base + param_body_direction_code, math_direction); + eq_word_define(static_int_base + param_par_direction_code, math_direction); + eq_word_define(static_int_base + param_text_direction_code, math_direction); + eq_word_define(static_int_base + param_level_local_dir_code, cur_level); +} + +void push_math(group_code c) +{ + if (math_direction != text_direction) + dir_math_save = true; + push_nest(); + mode = -mmode; + incompleat_noad = null; + new_save_level_math(c); +} + +void enter_ordinary_math(void) +{ + push_math(math_shift_group); + eq_word_define(static_int_base + param_cur_fam_code, -1); + if (every_math != null) + begin_token_list(every_math, every_math_text); +} + +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. +*/ + +void init_math(void) +{ + get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ + if ((cur_cmd == math_shift_cmd) && (mode > 0)) { + enter_display_math(); + } else { + back_input(); + enter_ordinary_math(); + } +} + +/* +We get into ordinary math mode from display math mode when `\.{\\eqno}' or +`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; +the value of |cur_chr| is placed onto |save_stack| for safe keeping. +*/ + + +/* +When \TeX\ is in display math mode, |cur_group=math_shift_group|, +so it is not necessary for the |start_eq_no| procedure to test for +this condition. +*/ + +void start_eq_no(void) +{ + saved(0) = cur_chr; + incr(save_ptr); + 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. + +Here is the code that handles case (1); the other cases are not quite as +trivial, so we shall consider them later. +*/ + +void math_left_brace(void) +{ + pointer q; + tail_append(new_noad()); + q = new_node(math_char_node, 0); + nucleus(tail) = q; + back_input(); + (void) scan_math(nucleus(tail)); +} + +/* +When we enter display math mode, we need to call |line_break| to +process the partial paragraph that has just been interrupted by the +display. Then we can set the proper values of |display_width| and +|display_indent| and |pre_display_size|. +*/ + + +void enter_display_math(void) +{ + scaled w; /* new or partial |pre_display_size| */ + scaled l; /* new |display_width| */ + scaled s; /* new |display_indent| */ + pointer p; + integer n; /* scope of paragraph shape specification */ + if (head == tail) { /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ + pop_nest(); + w = -max_dimen; + } else { + line_break(true); + w = actual_box_width(just_box, (2 * quad(get_cur_font()))); + } + /* now we are in vertical mode, working on the list that will contain the display */ + /* 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_ptr == null) { + if ((hang_indent != 0) && + (((hang_after >= 0) && (prev_graf + 2 > hang_after)) || + (prev_graf + 1 < -hang_after))) { + l = hsize - abs(hang_indent); + if (hang_indent > 0) + s = hang_indent; + else + s = 0; + } else { + l = hsize; + s = 0; + } + } else { + n = vinfo(par_shape_ptr + 1); + if (prev_graf + 2 >= n) + p = par_shape_ptr + 2 * n + 1; + else + p = par_shape_ptr + 2 * (prev_graf + 2) + 1; + s = varmem[(p - 1)].cint; + l = varmem[p].cint; + } + + push_math(math_shift_group); + mode = mmode; + eq_word_define(static_int_base + param_cur_fam_code, -1); + eq_word_define(static_dimen_base + param_pre_display_size_code, w); + eq_word_define(static_dimen_base + param_display_width_code, l); + eq_word_define(static_dimen_base + param_display_indent_code, s); + if (every_display != null) + begin_token_list(every_display, every_display_text); + if (nest_ptr == 1) { + if (!output_active) + lua_node_filter_s(buildpage_filter_callback, "before_display"); + build_page(); + } +} + +/* The next routine parses all variations of a delimiter code. The |extcode| + * tells what syntax form to use (\TeX, \Aleph, \XeTeX, \XeTeXnum, ...) , the + * |doclass| tells whether or not read a math class also (for \delimiter c.s.). + * (the class is passed on for conversion to |\mathchar|). + */ + +#define fam_in_range ((cur_fam>=0)&&(cur_fam<256)) + +delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) +{ + char *hlp[] = { + "I'm going to use 0 instead of that illegal code value.", + NULL + }; + delcodeval d; + integer cur_val1; /* and the global |cur_val| */ + integer mcls, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; + mcls = 0; + if (extcode == tex_mathcode) { /* \delcode, this is the easiest */ + scan_int(); + /* "MFCCFCC or "FCCFCC */ + if (doclass) { + mcls = (cur_val / 0x1000000); + cur_val = (cur_val & 0xFFFFFF); + } + if (cur_val > 0xFFFFFF) { + tex_error("Invalid delimiter code", hlp); + cur_val = 0; + } + msfam = (cur_val / 0x100000); + mschr = (cur_val % 0x100000) / 0x1000; + mlfam = (cur_val & 0xFFF) / 0x100; + mlchr = (cur_val % 0x100); + } else if (extcode == aleph_mathcode) { /* \odelcode */ + /* "MFFCCCC"FFCCCC or "FFCCCC"FFCCCC */ + scan_int(); + if (doclass) { + mcls = (cur_val / 0x1000000); + cur_val = (cur_val & 0xFFFFFF); + } + cur_val1 = cur_val; + scan_int(); + if ((cur_val1 > 0xFFFFFF) || (cur_val > 0xFFFFFF)) { + tex_error("Invalid delimiter code", hlp); + cur_val1 = 0; + cur_val = 0; + } + msfam = (cur_val1 / 0x10000); + mschr = (cur_val1 % 0x10000); + mlfam = (cur_val / 0x10000); + mlchr = (cur_val % 0x10000); + } else if (extcode == xetex_mathcode) { /* \Udelcode */ + /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */ + if (doclass) { + scan_int(); + mcls = cur_val; + } + scan_int(); + msfam = cur_val; + scan_char_num(); + mschr = cur_val; + if (msfam < 0 || msfam > 255) { + tex_error("Invalid delimiter code", hlp); + msfam = 0; + mschr = 0; + } + mlfam = 0; + mlchr = 0; + } else if (extcode == xetexnum_mathcode) { /* \Udelcodenum */ + /* "FF<21bits> */ + /* the largest numeric value is $2^29-1$, but + the top of bit 21 can't be used as it contains invalid USV's + */ + if (doclass) { /* such a primitive doesn't exist */ + tconfusion("xetexnum_mathcode"); + } + scan_int(); + msfam = (cur_val / 0x200000); + mschr = cur_val & 0x1FFFFF; + if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) { + tex_error("Invalid delimiter code", hlp); + msfam = 0; + mschr = 0; + } + mlfam = 0; + mlchr = 0; + } else { + /* something's gone wrong */ + tconfusion("unknown_extcode"); + } + d.origin_value = extcode; + d.class_value = mcls; + d.small_family_value = msfam; + d.small_character_value = mschr; + d.large_family_value = mlfam; + d.large_character_value = mlchr; + return d; +} + +void scan_extdef_del_code(int level, int extcode) +{ + delcodeval d; + integer p; + scan_char_num(); + p = cur_val; + scan_optional_equals(); + d = do_scan_extdef_del_code(extcode, false); + set_del_code(p, extcode, d.small_family_value, d.small_character_value, + d.large_family_value, d.large_character_value, level); +} + +mathcodeval scan_mathchar(int extcode) +{ + char *hlp[] = { + "I'm going to use 0 instead of that illegal code value.", + NULL + }; + mathcodeval d; + integer mcls = 0, mfam = 0, mchr = 0; + if (extcode == tex_mathcode) { /* \mathcode */ + /* "TFCC */ + scan_int(); + if (cur_val > 0x8000) { + tex_error("Invalid math code", hlp); + cur_val = 0; + } + mcls = (cur_val / 0x1000); + mfam = ((cur_val % 0x1000) / 0x100); + mchr = (cur_val % 0x100); + } else if (extcode == aleph_mathcode) { /* \omathcode */ + /* "TFFCCCC */ + scan_int(); + if (cur_val > 0x8000000) { + tex_error("Invalid math code", hlp); + cur_val = 0; + } + mcls = (cur_val / 0x1000000); + mfam = ((cur_val % 0x1000000) / 0x10000); + mchr = (cur_val % 0x10000); + } else if (extcode == xetex_mathcode) { + /* <0-0x7> <0-0xFF> <0-0x10FFFF> */ + scan_int(); + mcls = cur_val; + scan_int(); + mfam = cur_val; + scan_char_num(); + mchr = cur_val; + if (mcls < 0 || mcls > 7 || mfam > 255) { + tex_error("Invalid math code", hlp); + mchr = 0; + mfam = 0; + mcls = 0; + } + } else if (extcode == xetexnum_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 */ + scan_int(); + mfam = (cur_val / 0x200000) & 0x7FF; + mcls = mfam % 0x08; + mfam = mfam / 0x08; + mchr = cur_val & 0x1FFFFF; + if (mchr > 0x10FFFF) { + tex_error("Invalid math code", hlp); + mcls = 0; + mfam = 0; + mchr = 0; + } + } else { + /* something's gone wrong */ + tconfusion("unknown_extcode"); + } + d.class_value = mcls; + d.family_value = mfam; + d.origin_value = extcode; + d.character_value = mchr; + return d; +} + + +void scan_extdef_math_code(int level, int extcode) +{ + mathcodeval d; + integer p; + scan_char_num(); + p = cur_val; + scan_optional_equals(); + d = scan_mathchar(extcode); + set_math_code(p, extcode, d.class_value, + d.family_value, d.character_value, level); +} + + +/* this reads in a delcode when actually a mathcode is needed */ + +mathcodeval scan_delimiter_as_mathchar(int extcode) +{ + delcodeval dval; + mathcodeval mval; + dval = do_scan_extdef_del_code(extcode, true); + mval.origin_value = 0; + mval.class_value = dval.class_value; + mval.family_value = dval.small_family_value; + mval.character_value = dval.small_character_value; + return mval; +} + +/* this has to match the inverse routine in the pascal code + * where the |\Umathchardef| is executed + */ + +mathcodeval mathchar_from_integer(integer value, int extcode) +{ + mathcodeval mval; + mval.origin_value = extcode; + if (extcode == tex_mathcode) { + mval.class_value = (value / 0x1000); + mval.family_value = ((value % 0x1000) / 0x100); + mval.character_value = (value % 0x100); + } else if (extcode == aleph_mathcode) { + mval.class_value = (value / 0x1000000); + mval.family_value = ((value % 0x1000000) / 0x10000); + mval.character_value = (value % 0x10000); + } else { /* some xetexended xetex thing */ + int mfam = (value / 0x200000) & 0x7FF; + mval.class_value = mfam % 0x08; + mval.family_value = mfam / 0x08; + mval.character_value = value & 0x1FFFFF; + } + 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|. +*/ + + +#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) + + +int scan_math(pointer p) +{ + /* label restart,reswitch,exit; */ + mathcodeval mval; + assert(p != null); + RESTART: + get_next_nb_nr(); + RESWITCH: + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + case char_given_cmd: + mval = get_math_code(cur_chr); + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + goto RESTART; + } + break; + case char_num_cmd: + scan_char_num(); + cur_chr = cur_val; + cur_cmd = char_given_cmd; + goto RESWITCH; + break; + case math_char_num_cmd: + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(aleph_mathcode); + else if (cur_chr == 2) + mval = scan_mathchar(xetex_mathcode); + else if (cur_chr == 3) + mval = scan_mathchar(xetexnum_mathcode); + else + tconfusion("scan_math"); + break; + case math_given_cmd: + mval = mathchar_from_integer(cur_chr, tex_mathcode); + break; + case omath_given_cmd: + mval = mathchar_from_integer(cur_chr, aleph_mathcode); + break; + case xmath_given_cmd: + mval = mathchar_from_integer(cur_chr, xetex_mathcode); + break; + case delim_num_cmd: + if (cur_chr == 0) + mval = scan_delimiter_as_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_delimiter_as_mathchar(aleph_mathcode); + else if (cur_chr == 2) + mval = scan_delimiter_as_mathchar(xetex_mathcode); + else + tconfusion("scan_math"); + break; + default: + /* The pointer |p| is placed on |save_stack| while a complex subformula + is being scanned. */ + back_input(); + scan_left_brace(); + saved(0) = p; + incr(save_ptr); + push_math(math_group); + return 1; + } + type(p) = math_char_node; + math_character(p) = mval.character_value; + if ((mval.class_value == var_code) && fam_in_range) + math_fam(p) = cur_fam; + else + math_fam(p) = mval.family_value; + 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. +*/ + +void set_math_char(mathcodeval mval) +{ + pointer p; /* the new noad */ + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + } else { + pointer q; + p = new_noad(); + q = new_node(math_char_node, 0); + nucleus(p) = q; + math_character(nucleus(p)) = mval.character_value; + math_fam(nucleus(p)) = mval.family_value; + if (mval.class_value == var_code) { + if (fam_in_range) + math_fam(nucleus(p)) = cur_fam; + type(p) = ord_noad; + } else { + type(p) = ord_noad + mval.class_value; + } + vlink(tail) = p; + tail = p; + } +} + + +/* + The |math_char_in_text| procedure creates a new node representing a math char +in text code, and appends it to the current list. However, if the math code +is sufficiently large, the |cur_chr| is treated as an active character and +nothing is appended. +*/ + +void math_char_in_text(mathcodeval mval) +{ + pointer p; /* the new node */ + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + } else { + p = new_char(fam_fnt(mval.family_value, text_size), + mval.character_value); + vlink(tail) = p; + tail = p; + } +} + + +void math_math_comp(void) +{ + pointer q; + tail_append(new_noad()); + type(tail) = cur_chr; + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail)); +} + + +void math_limit_switch(void) +{ + char *hlp[] = { + "I'm ignoring this misplaced \\limits or \\nolimits command.", + NULL + }; + if (head != tail) { + if (type(tail) == op_noad) { + subtype(tail) = cur_chr; + return; + } + } + tex_error("Limit controls must follow a math operator", hlp); +} + +/* + Delimiter fields of noads are filled in by the |scan_delimiter| routine. +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. +*/ + + +void scan_delimiter(pointer p, integer r) +{ + delcodeval dval; + if (r == tex_mathcode) { /* \radical */ + dval = do_scan_extdef_del_code(tex_mathcode, true); + } else if (r == aleph_mathcode) { /* \oradical */ + dval = do_scan_extdef_del_code(aleph_mathcode, true); + } else if (r == xetex_mathcode) { /* \Uradical */ + dval = do_scan_extdef_del_code(xetex_mathcode, false); + } else if (r == no_mathcode) { + get_next_nb_nr(); + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + dval = get_del_code(cur_chr); + break; + case delim_num_cmd: + if (cur_chr == 0) /* \delimiter */ + dval = do_scan_extdef_del_code(tex_mathcode, true); + else if (cur_chr == 1) /* \odelimiter */ + dval = do_scan_extdef_del_code(aleph_mathcode, true); + else if (cur_chr == 2) /* \Udelimiter */ + dval = do_scan_extdef_del_code(xetex_mathcode, true); + else + tconfusion("scan_delimiter1"); + break; + default: + dval.small_family_value = -1; + break; + } + } else { + tconfusion("scan_delimiter2"); + } + if (p == null) + return; + if (dval.small_family_value < 0) { + char *hlp[] = { + "I was expecting to see something like `(' or `\\{' or", + "`\\}' here. If you typed, e.g., `{' instead of `\\{', you", + "should probably delete the `{' by typing `1' now, so that", + "braces don't get unbalanced. Otherwise just proceed", + "Acceptable delimiters are characters whose \\delcode is", + "nonnegative, or you can use `\\delimiter <delimiter code>'.", + NULL + }; + back_error("Missing delimiter (. inserted)", hlp); + small_fam(p) = 0; + small_char(p) = 0; + large_fam(p) = 0; + large_char(p) = 0; + } else { + small_fam(p) = dval.small_family_value; + small_char(p) = dval.small_character_value; + large_fam(p) = dval.large_family_value; + large_char(p) = dval.large_character_value; + } + return; +} + + +void math_radical(void) +{ + halfword q; + int chr_code = cur_chr; + tail_append(new_node(radical_noad, chr_code)); + q = new_node(delim_node, 0); + left_delimiter(tail) = q; + if (chr_code == 0) /* \radical */ + scan_delimiter(left_delimiter(tail), tex_mathcode); + else if (chr_code == 1) /* \oradical */ + scan_delimiter(left_delimiter(tail), aleph_mathcode); + else if (chr_code == 2) /* \Uradical */ + scan_delimiter(left_delimiter(tail), xetex_mathcode); + else if (chr_code == 3) /* \Uroot */ + scan_delimiter(left_delimiter(tail), xetex_mathcode); + else if (chr_code == 4) /* \Uunderdelimiter */ + scan_delimiter(left_delimiter(tail), xetex_mathcode); + else if (chr_code == 5) /* \Uoverdelimiter */ + scan_delimiter(left_delimiter(tail), xetex_mathcode); + else if (chr_code == 6) /* \Udelimiterunder */ + scan_delimiter(left_delimiter(tail), xetex_mathcode); + else if (chr_code == 7) /* \Udelimiterover */ + scan_delimiter(left_delimiter(tail), xetex_mathcode); + else + tconfusion("math_radical"); + if (chr_code == 3) { + q = new_node(math_char_node, 0); + vlink(q) = tail; + degree(tail) = q; + if (!scan_math(degree(tail))) { + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail)); + } + } else { + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail)); + } +} + +void math_ac(void) +{ + halfword q; + mathcodeval t, b; + t.character_value = 0; + t.family_value = 0; + b.character_value = 0; + b.family_value = 0; + if (cur_cmd == accent_cmd) { + char *hlp[] = { + "I'm changing \\accent to \\mathaccent here; wish me luck.", + "(Accents are not the same in formulas as they are in text.)", + NULL + }; + tex_error("Please use \\mathaccent for accents in math mode", hlp); + } + tail_append(new_node(accent_noad, normal)); + if (cur_chr == 0) { /* \mathaccent */ + t = scan_mathchar(tex_mathcode); + } else if (cur_chr == 1) { /* \omathaccent */ + t = scan_mathchar(aleph_mathcode); + } else if (cur_chr == 2) { /* \Umathaccent */ + t = scan_mathchar(xetex_mathcode); + } else if (cur_chr == 3) { /* \Umathbotaccent */ + b = scan_mathchar(xetex_mathcode); + } else if (cur_chr == 4) { /* \Umathaccents */ + t = scan_mathchar(xetex_mathcode); + b = scan_mathchar(xetex_mathcode); + } else { + tconfusion("math_ac"); + } + if (!(t.character_value == 0 && t.family_value == 0)) { + q = new_node(math_char_node, 0); + accent_chr(tail) = q; + math_character(accent_chr(tail)) = t.character_value; + if ((t.class_value == var_code) && fam_in_range) + math_fam(accent_chr(tail)) = cur_fam; + else + math_fam(accent_chr(tail)) = t.family_value; + } + if (!(b.character_value == 0 && b.family_value == 0)) { + q = new_node(math_char_node, 0); + bot_accent_chr(tail) = q; + math_character(bot_accent_chr(tail)) = b.character_value; + if ((b.class_value == var_code) && fam_in_range) + math_fam(bot_accent_chr(tail)) = cur_fam; + else + math_fam(bot_accent_chr(tail)) = b.family_value; + } + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail)); +} + +pointer math_vcenter_group(pointer p) +{ + pointer q, r; + q = new_noad(); + type(q) = vcenter_noad; + r = new_node(sub_box_node, 0); + nucleus(q) = r; + math_list(nucleus(q)) = p; + return q; +} + +/* +The routine that scans the four mlists of a \.{\\mathchoice} is very +much like the routine that builds discretionary nodes. +*/ + +void append_choices(void) +{ + tail_append(new_choice()); + incr(save_ptr); + saved(-1) = 0; + push_math(math_choice_group); + scan_left_brace(); +} +void build_choices(void) +{ + pointer p; /* the current mlist */ + unsave_math(); + p = fin_mlist(null); + switch (saved(-1)) { + case 0: + display_mlist(tail) = p; + break; + case 1: + text_mlist(tail) = p; + break; + case 2: + script_mlist(tail) = p; + break; + case 3: + script_script_mlist(tail) = p; + decr(save_ptr); + return; + break; + } /* there are no other cases */ + incr(saved(-1)); + push_math(math_choice_group); + scan_left_brace(); +} + +/* + Subscripts and superscripts are attached to the previous nucleus by the + action procedure called |sub_sup|. +*/ + +void sub_sup(void) +{ + pointer q; + if (tail == head || (!scripts_allowed(tail))) { + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + } + if (cur_cmd == sup_mark_cmd) { + if (supscr(tail) != null) { + char *hlp[] = { + "I treat `x^1^2' essentially like `x^1{}^2'.", NULL + }; + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + tex_error("Double superscript", hlp); + } + q = new_node(math_char_node, 0); + supscr(tail) = q; + (void) scan_math(supscr(tail)); + } else { + if (subscr(tail) != null) { + char *hlp[] = { + "I treat `x_1_2' essentially like `x_1{}_2'.", NULL + }; + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + tex_error("Double subscript", hlp); + } + q = new_node(math_char_node, 0); + subscr(tail) = q; + (void) scan_math(subscr(tail)); + } +} + +/* +An operation like `\.{\\over}' causes the current mlist to go into a +state of suspended animation: |incompleat_noad| points to a |fraction_noad| +that contains the mlist-so-far as its numerator, while the denominator +is yet to come. Finally when the mlist is finished, the denominator will +go into the incompleat fraction noad, and that noad will become the +whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' +delimiters. +*/ + +void math_fraction(void) +{ + small_number c; /* the type of generalized fraction we are scanning */ + pointer q; + c = cur_chr; + if (incompleat_noad != null) { + char *hlp[] = { + "I'm ignoring this fraction specification, since I don't", + "know whether a construction like `x \\over y \\over z'", + "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.", + NULL + }; + if (c >= delimited_code) { + scan_delimiter(null, no_mathcode); + scan_delimiter(null, no_mathcode); + } + if ((c % delimited_code) == above_code) + scan_normal_dimen(); + tex_error("Ambiguous; you need another { and }", hlp); + } else { + incompleat_noad = new_node(fraction_noad, normal); + numerator(incompleat_noad) = new_node(sub_mlist_node, 0); + math_list(numerator(incompleat_noad)) = vlink(head); + vlink(head) = null; + tail = head; + + if (c >= delimited_code) { + q = new_node(delim_node, 0); + left_delimiter(incompleat_noad) = q; + q = new_node(delim_node, 0); + right_delimiter(incompleat_noad) = q; + scan_delimiter(left_delimiter(incompleat_noad), no_mathcode); + scan_delimiter(right_delimiter(incompleat_noad), no_mathcode); + } + switch (c % delimited_code) { + case above_code: + scan_normal_dimen(); + thickness(incompleat_noad) = cur_val; + break; + case over_code: + thickness(incompleat_noad) = default_code; + break; + case atop_code: + thickness(incompleat_noad) = 0; + break; + } /* there are no other cases */ + } +} + + + +/* At the end of a math formula or subformula, the |fin_mlist| routine is +called upon to return a pointer to the newly completed mlist, and to +pop the nest back to the enclosing semantic level. The parameter to +|fin_mlist|, if not null, points to a |fence_noad| that ends the +current mlist; this |fence_noad| has not yet been appended. +*/ + + +pointer fin_mlist(pointer p) +{ + pointer q; /* the mlist to return */ + if (incompleat_noad != null) { + if (denominator(incompleat_noad) != null) { + type(denominator(incompleat_noad)) = sub_mlist_node; + } else { + q = new_node(sub_mlist_node, 0); + denominator(incompleat_noad) = q; + } + math_list(denominator(incompleat_noad)) = vlink(head); + if (p == null) { + q = incompleat_noad; + } else { + q = math_list(numerator(incompleat_noad)); + if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) + || (delim_ptr == null)) + tconfusion("right"); /* this can't happen */ + math_list(numerator(incompleat_noad)) = vlink(delim_ptr); + vlink(delim_ptr) = incompleat_noad; + vlink(incompleat_noad) = p; + } + } else { + vlink(tail) = p; + q = vlink(head); + } + pop_nest(); + 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. +*/ + +void close_math_group(pointer p) +{ + pointer q; + unsave_math(); + + decr(save_ptr); + type(saved(0)) = sub_mlist_node; + p = fin_mlist(null); + math_list(saved(0)) = p; + if (p != null) { + if (vlink(p) == null) { + if (type(p) == ord_noad) { + if (subscr(p) == null && supscr(p) == null) { + type(saved(0)) = type(nucleus(p)); + if (type(nucleus(p)) == math_char_node) { + math_fam(saved(0)) = math_fam(nucleus(p)); + math_character(saved(0)) = math_character(nucleus(p)); + } else { + math_list(saved(0)) = math_list(nucleus(p)); + math_list(nucleus(p)) = null; + } + node_attr(saved(0)) = node_attr(nucleus(p)); + node_attr(nucleus(p)) = null; + flush_node(p); + } + } + } else { + if (type(p) == accent_noad) { + if (saved(0) == nucleus(tail)) { + /* todo: check this branch */ + if (type(tail) == ord_noad) { + q = head; + while (vlink(q) != tail) + q = vlink(q); + vlink(q) = p; + nucleus(tail) = null; + subscr(tail) = null; + supscr(tail) = null; + node_attr(p) = node_attr(tail); + node_attr(tail) = null; + flush_node(tail); + tail = p; + } + } + } + } + } + if (vlink(saved(0)) > 0) { + pointer q; + q = new_node(math_char_node, 0); + nucleus(vlink(saved(0))) = q; + vlink(saved(0)) = null; + saved(0) = q; + (void) scan_math(saved(0)); + /* 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}. +*/ + +void math_left_right(void) +{ + small_number t; /* |left_noad_side| .. |right_noad_side| */ + pointer p; /* new noad */ + pointer q; /* resulting mlist */ + pointer r; /* temporary */ + t = cur_chr; + if ((t != left_noad_side) && (cur_group != math_left_group)) { + if (cur_group == math_shift_group) { + scan_delimiter(null, no_mathcode); + if (t == middle_noad_side) { + char *hlp[] = { + "I'm ignoring a \\middle that had no matching \\left.", + NULL + }; + tex_error("Extra \\middle", hlp); + } else { + char *hlp[] = { + "I'm ignoring a \\right that had no matching \\left.", + NULL + }; + tex_error("Extra \\right", hlp); + } + } else { + off_save(); + } + } else { + p = new_noad(); + type(p) = fence_noad; + subtype(p) = t; + r = new_node(delim_node, 0); + delimiter(p) = r; + scan_delimiter(delimiter(p), no_mathcode); + if (t == left_noad_side) { + q = p; + } else { + q = fin_mlist(p); + unsave_math(); + } + if (t != right_noad_side) { + push_math(math_left_group); + vlink(head) = q; + tail = p; + delim_ptr = p; + } else { + tail_append(new_noad()); + type(tail) = inner_noad; + r = new_node(sub_mlist_node, 0); + nucleus(tail) = r; + math_list(nucleus(tail)) = q; + } + } +} + + +/* \TeX\ gets to the following part of the program when +the first `\.\$' ending a display has been scanned. +*/ + +static void check_second_math_shift(void) +{ + get_x_token(); + if (cur_cmd != math_shift_cmd) { + char *hlp[] = { + "The `$' that I just saw supposedly matches a previous `$$'.", + "So I shall assume that you typed `$$' both times.", + NULL + }; + back_error("Display math should end with $$", hlp); + } +} + +void finish_displayed_math(boolean l, boolean danger, pointer a); + +void after_math(void) +{ + boolean danger; /* not enough symbol fonts are present */ + integer m; /* |mmode| or |-mmode| */ + pointer p; /* the formula */ + pointer a = null; /* box containing equation number */ + boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ + danger = check_necessary_fonts(); + m = mode; + p = fin_mlist(null); /* this pops the nest */ + if (mode == -m) { /* end of equation number */ + check_second_math_shift(); + run_mlist_to_hlist(p, text_style, false); + a = hpack(vlink(temp_head), 0, additional); + unsave_math(); + decr(save_ptr); /* now |cur_group=math_shift_group| */ + if (saved(0) == 1) + l = true; + danger = check_necessary_fonts(); + m = mode; + p = fin_mlist(null); + } + if (m < 0) { + /* The |unsave| is done after everything else here; hence an appearance of + `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these + particular \.\$'s. This is consistent with the conventions of + `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the + space above that display. + */ + tail_append(new_math(math_surround, before)); + if (dir_math_save) { + tail_append(new_dir(math_direction)); + } + run_mlist_to_hlist(p, text_style, (mode > 0)); + vlink(tail) = vlink(temp_head); + while (vlink(tail) != null) + tail = vlink(tail); + if (dir_math_save) { + tail_append(new_dir(math_direction - 64)); + } + dir_math_save = false; + tail_append(new_math(math_surround, after)); + space_factor = 1000; + unsave_math(); + } else { + if (a == null) + check_second_math_shift(); + run_mlist_to_hlist(p, display_style, false); + finish_displayed_math(l, danger, a); + } +} + + +void resume_after_display(void) +{ + if (cur_group != math_shift_group) + tconfusion("display"); + unsave_math(); + prev_graf = prev_graf + 3; + push_nest(); + mode = hmode; + space_factor = 1000; + tail_append(make_local_par_node()); + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + if (nest_ptr == 1) { + lua_node_filter_s(buildpage_filter_callback, "after_display"); + build_page(); + } +} + + +/* +We have saved the worst for last: The fussiest part of math mode processing +occurs when a displayed formula is being centered and placed with an optional +equation number. +*/ +/* At this time |p| points to the mlist for the formula; |a| is either + |null| or it points to a box containing the equation number; and we are in + vertical mode (or internal vertical mode). +*/ + +void finish_displayed_math(boolean l, boolean danger, pointer a) +{ + pointer b; /* box containing the equation */ + scaled w; /* width of the equation */ + scaled z; /* width of the line */ + scaled e; /* width of equation number */ + scaled q; /* width of equation number plus space to separate from equation */ + scaled d; /* displacement of equation in the line */ + scaled s; /* move the line right this much */ + small_number g1, g2; /* glue parameter codes for before and after */ + pointer r; /* kern node used to position the display */ + pointer t; /* tail of adjustment list */ + pointer pre_t; /* tail of pre-adjustment list */ + pointer p; + p = vlink(temp_head); + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + b = hpack(p, 0, additional); + p = list_ptr(b); + t = adjust_tail; + adjust_tail = null; + pre_t = pre_adjust_tail; + pre_adjust_tail = null; + w = width(b); + z = display_width; + s = display_indent; + if ((a == null) || danger) { + e = 0; + q = 0; + } else { + e = width(a); + q = e + get_math_quad(text_size); + } + if (w + q > z) { + /* The user can force the equation number to go on a separate line + by causing its width to be zero. */ + if ((e != 0) && ((w - total_shrink[normal] + q <= z) || + (total_shrink[sfi] != 0) || (total_shrink[fil] != 0) || + (total_shrink[fill] != 0) + || (total_shrink[filll] != 0))) { + list_ptr(b) = null; + flush_node(b); + b = hpack(p, z - q, exactly); + } else { + e = 0; + if (w > z) { + list_ptr(b) = null; + flush_node(b); + b = hpack(p, z, exactly); + } + } + w = width(b); + } + /* 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(z - w); + if ((e > 0) && (d < 2 * e)) { /* too close */ + d = half(z - w - e); + if (p != null) + if (!is_char_node(p)) + if (type(p) == glue_node) + d = 0; + } + + /* 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. + */ + tail_append(new_penalty(int_par(param_pre_display_penalty_code))); + if ((d + s <= pre_display_size) || l) { /* not enough clearance */ + g1 = param_above_display_skip_code; + g2 = param_below_display_skip_code; + } else { + g1 = param_above_display_short_skip_code; + g2 = param_below_display_short_skip_code; + } + if (l && (e == 0)) { /* it follows that |type(a)=hlist_node| */ + shift_amount(a) = s; + append_to_vlist(a); + tail_append(new_penalty(inf_penalty)); + } else { + tail_append(new_param_glue(g1)); + } + + if (e != 0) { + r = new_kern(z - w - e - d); + if (l) { + vlink(a) = r; + vlink(r) = b; + b = a; + d = 0; + } else { + vlink(b) = r; + vlink(r) = a; + } + b = hpack(b, 0, additional); + } + shift_amount(b) = s + d; + append_to_vlist(b); + + if ((a != null) && (e == 0) && !l) { + tail_append(new_penalty(inf_penalty)); + shift_amount(a) = s + z - width(a); + append_to_vlist(a); + g2 = 0; + } + if (t != adjust_head) { /* migrating material comes after equation number */ + vlink(tail) = vlink(adjust_head); + tail = t; + } + if (pre_t != pre_adjust_head) { + vlink(tail) = vlink(pre_adjust_head); + tail = pre_t; + } + tail_append(new_penalty(int_par(param_post_display_penalty_code))); + if (g2 > 0) + tail_append(new_param_glue(g2)); + + resume_after_display(); +} + + +/* + When \.{\\halign} appears in a display, the alignment routines operate +essentially as they do in vertical mode. Then the following program is +activated, with |p| and |q| pointing to the beginning and end of the +resulting list, and with |aux_save| holding the |prev_depth| value. +*/ + + +void finish_display_alignment(pointer p, pointer q, memory_word aux_save) +{ + do_assignments(); + if (cur_cmd != math_shift_cmd) { + char *hlp[] = { + "Displays can use special alignments (like \\eqalignno)", + "only if nothing but the alignment itself is between $$'s.", + NULL + }; + back_error("Missing $$ inserted", hlp); + } else { + check_second_math_shift(); + } + pop_nest(); + tail_append(new_penalty(int_par(param_pre_display_penalty_code))); + tail_append(new_param_glue(param_above_display_skip_code)); + vlink(tail) = p; + if (p != null) + tail = q; + tail_append(new_penalty(int_par(param_post_display_penalty_code))); + tail_append(new_param_glue(param_below_display_skip_code)); + prev_depth = aux_save.cint; + resume_after_display(); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.c b/Build/source/texk/web2c/luatexdir/tex/mlist.c new file mode 100644 index 00000000000..51858c42899 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/mlist.c @@ -0,0 +1,3864 @@ +/* mlist.c + + Copyright 2006-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#include "luatex-api.h" +#include <ptexlib.h> + +#include "nodes.h" +#include "commands.h" + +static const char _svn_version[] = + "$Id: mlist.c 2099 2009-03-24 10:35:01Z taco $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/mlist.c $"; + +#define delimiter_factor int_par(param_delimiter_factor_code) +#define delimiter_shortfall dimen_par(param_delimiter_shortfall_code) +#define bin_op_penalty int_par(param_bin_op_penalty_code) +#define rel_penalty int_par(param_rel_penalty_code) +#define null_delimiter_space dimen_par(param_null_delimiter_space_code) +#define script_space dimen_par(param_script_space_code) +#define disable_lig int_par(param_disable_lig_code) +#define disable_kern int_par(param_disable_kern_code) + +#define reset_attributes(p,newatt) do { \ + delete_attribute_ref(node_attr(p)); \ + node_attr(p) = newatt; \ + if (newatt!=null) { \ + assert(type(newatt)==attribute_list_node); \ + add_node_attr_ref(node_attr(p)); \ + } \ + } while (0) + + +#define DEFINE_MATH_PARAMETERS(A,B,C,D) do { \ + if (B==text_size) { \ + def_math_param(A, text_style, (C),D); \ + def_math_param(A, cramped_text_style, (C),D); \ + } else if (B==script_size) { \ + def_math_param(A, script_style, (C),D); \ + def_math_param(A, cramped_script_style, (C),D); \ + } else if (B==script_script_size) { \ + def_math_param(A, script_script_style, (C),D); \ + def_math_param(A, cramped_script_script_style, (C),D); \ + } \ + } while (0) + +#define DEFINE_DMATH_PARAMETERS(A,B,C,D) do { \ + if (B==text_size) { \ + def_math_param(A, display_style,(C),D); \ + def_math_param(A, cramped_display_style,(C),D); \ + } \ + } while (0) + + +#define is_new_mathfont(A) (font_math_params(A)>0) +#define is_old_mathfont(A,B) (font_math_params(A)==0 && font_params(A)>=(B)) + + +#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 */ + +/* +@ 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@> +*/ + +#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@> +*/ + +#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 made a bunch of extensions cf. the MATH table in OpenType, but some of +the MathConstants values have no matching usage in \LuaTeX\ right now. + +ScriptPercentScaleDown, +ScriptScriptPercentScaleDown: + These should be handled by the macro package, on the engine + side there are three separate fonts + +DelimitedSubFormulaMinHeight: + This is perhaps related to word's natural math input? I have + no idea what to do about it + +MathLeading: + LuaTeX does not currently handle multi-line displays, and + the parameter does not seem to make much sense elsewhere + +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 + +SkewedFractionHorizontalGap, +SkewedFractionVerticalGap: + I am not sure it makes sense implementing skewed fractions, + so I would like to see an example first + +Also still TODO for OpenType Math: + * cutins (math_kern) + * extensible large operators + * bottom accents + * prescripts + +*/ + +/* this is not really a math parameter at all */ + +void math_param_error(char *param, int style) +{ + char s[256]; + char *hlp[] = { + "Sorry, but I can't typeset math unless various parameters have", + "been set. This is normally done by loading special math fonts", + "into the math family slots. Your font set is lacking at least", + "the parameter mentioned earlier.", + NULL + }; + snprintf(s, 256, "Math error: parameter \\Umath%s\\%sstyle is not set", + param, math_style_names[style]); + tex_error(s, hlp); + /* flush_math(); */ + return; +} + + +static scaled accent_base_height(integer f) +{ + scaled a; + a = x_height(f); + if (a == 0 && is_new_mathfont(f)) { + a = font_MATH_par(f, AccentBaseHeight); + if (a == undefined_math_parameter) + a = 0; + } + return a; +} + +/* the non-staticness of this function is for the benefit of |math.c| */ + +scaled get_math_quad(int var) +{ + scaled a = get_math_param(math_param_quad, var); + if (a == undefined_math_parameter) { + math_param_error("quad", var); + a = 0; + } + return a; +} + +/* this parameter is different because it is called with a size + specifier instead of a style specifier. */ + +static scaled math_axis(int b) +{ + scaled a; + int var; + if (b == script_size) + var = script_style; + else if (b == script_script_size) + var = script_script_style; + else + var = text_style; + a = get_math_param(math_param_axis, var); + if (a == undefined_math_parameter) { + math_param_error("axis", var); + a = 0; + } + return a; +} + +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. + */ + +static scaled radical_rule(int var) +{ + scaled a = get_math_param(math_param_radical_rule, var); + return a; +} + +static scaled radical_degree_before(int var) +{ + scaled a = get_math_param(math_param_radical_degree_before, var); + if (a == undefined_math_parameter) { + math_param_error("radicaldegreebefore", var); + a = 0; + } + return a; +} + +static scaled connector_overlap_min(int var) +{ + scaled a = get_math_param(math_param_connector_overlap_min, var); + if (a == undefined_math_parameter) { + math_param_error("connectoroverlapmin", var); + a = 0; + } + return a; +} + + +static scaled radical_degree_after(int var) +{ + scaled a = get_math_param(math_param_radical_degree_after, var); + if (a == undefined_math_parameter) { + math_param_error("radicaldegreeafter", var); + a = 0; + } + return a; +} + +static scaled radical_degree_raise(int var) +{ + scaled a = get_math_param(math_param_radical_degree_raise, var); + if (a == undefined_math_parameter) { + math_param_error("radicaldegreeraise", var); + a = 0; + } + return a; +} + + +/* now follow all the trivial functions for the math parameters */ + +static scaled overbar_kern(int var) +{ + scaled a = get_math_param(math_param_overbar_kern, var); + if (a == undefined_math_parameter) { + math_param_error("overbarkern", var); + a = 0; + } + return a; +} + +static scaled overbar_rule(int var) +{ + scaled a = get_math_param(math_param_overbar_rule, var); + if (a == undefined_math_parameter) { + math_param_error("overbarrule", var); + a = 0; + } + return a; +} + +static scaled overbar_vgap(int var) +{ + scaled a = get_math_param(math_param_overbar_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("overbarvgap", var); + a = 0; + } + return a; +} + +static scaled underbar_rule(int var) +{ + scaled a = get_math_param(math_param_underbar_rule, var); + if (a == undefined_math_parameter) { + math_param_error("underbarrule", var); + a = 0; + } + return a; +} + +static scaled underbar_vgap(int var) +{ + scaled a = get_math_param(math_param_underbar_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("underbarvgap", var); + a = 0; + } + return a; +} + +static scaled underbar_kern(int var) +{ + scaled a = get_math_param(math_param_underbar_kern, var); + if (a == undefined_math_parameter) { + math_param_error("underbarkern", var); + a = 0; + } + return a; +} + +static scaled under_delimiter_vgap(int var) +{ + scaled a = get_math_param(math_param_under_delimiter_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("underdelimitervgap", var); + a = 0; + } + return a; +} + +static scaled under_delimiter_bgap(int var) +{ + scaled a = get_math_param(math_param_under_delimiter_bgap, var); + if (a == undefined_math_parameter) { + math_param_error("underdelimiterbgap", var); + a = 0; + } + return a; +} + + +static scaled over_delimiter_vgap(int var) +{ + scaled a = get_math_param(math_param_over_delimiter_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("overdelimitervgap", var); + a = 0; + } + return a; +} + +static scaled over_delimiter_bgap(int var) +{ + scaled a = get_math_param(math_param_over_delimiter_bgap, var); + if (a == undefined_math_parameter) { + math_param_error("overdelimiterbgap", var); + a = 0; + } + return a; +} + + + +static scaled radical_vgap(int var) +{ + scaled a = get_math_param(math_param_radical_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("radicalvgap", var); + a = 0; + } + return a; +} + + +static scaled radical_kern(int var) +{ + scaled a = get_math_param(math_param_radical_kern, var); + if (a == undefined_math_parameter) { + math_param_error("radicalkern", var); + a = 0; + } + return a; +} + +static scaled stack_vgap(int var) +{ + scaled a = get_math_param(math_param_stack_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("stackvgap", var); + a = 0; + } + return a; +} + +static scaled stack_num_up(int var) +{ + scaled a = get_math_param(math_param_stack_num_up, var); + if (a == undefined_math_parameter) { + math_param_error("stacknumup", var); + a = 0; + } + return a; +} + +static scaled stack_denom_down(int var) +{ + scaled a = get_math_param(math_param_stack_denom_down, var); + if (a == undefined_math_parameter) { + math_param_error("stackdenomdown", var); + a = 0; + } + return a; +} + +static scaled fraction_rule(int var) +{ + scaled a = get_math_param(math_param_fraction_rule, var); + if (a == undefined_math_parameter) { + math_param_error("fractionrule", var); + a = 0; + } + return a; +} + + +static scaled fraction_num_vgap(int var) +{ + scaled a = get_math_param(math_param_fraction_num_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("fractionnumvgap", var); + a = 0; + } + return a; +} + +static scaled fraction_denom_vgap(int var) +{ + scaled a = get_math_param(math_param_fraction_denom_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("fractiondenomvgap", var); + a = 0; + } + return a; +} + +static scaled fraction_num_up(int var) +{ + scaled a = get_math_param(math_param_fraction_num_up, var); + if (a == undefined_math_parameter) { + math_param_error("fractionnumup", var); + a = 0; + } + return a; +} + +static scaled fraction_denom_down(int var) +{ + scaled a = get_math_param(math_param_fraction_denom_down, var); + if (a == undefined_math_parameter) { + math_param_error("fractiondenomdown", var); + a = 0; + } + return a; +} + +static scaled fraction_del_size(int var) +{ + scaled a = get_math_param(math_param_fraction_del_size, var); + if (a == undefined_math_parameter) { + math_param_error("fractiondelsize", var); + a = 0; + } + return a; +} + +static scaled limit_above_vgap(int var) +{ + scaled a = get_math_param(math_param_limit_above_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("limitabovevgap", var); + a = 0; + } + return a; +} + +static scaled limit_above_bgap(int var) +{ + scaled a = get_math_param(math_param_limit_above_bgap, var); + if (a == undefined_math_parameter) { + math_param_error("limitabovebgap", var); + a = 0; + } + return a; +} + +static scaled limit_above_kern(int var) +{ + scaled a = get_math_param(math_param_limit_above_kern, var); + if (a == undefined_math_parameter) { + math_param_error("limitabovekern", var); + a = 0; + } + return a; +} + +static scaled limit_below_vgap(int var) +{ + scaled a = get_math_param(math_param_limit_below_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("limitbelowvgap", var); + a = 0; + } + return a; +} + +static scaled limit_below_bgap(int var) +{ + scaled a = get_math_param(math_param_limit_below_bgap, var); + if (a == undefined_math_parameter) { + math_param_error("limitbelowbgap", var); + a = 0; + } + return a; +} + +static scaled limit_below_kern(int var) +{ + scaled a = get_math_param(math_param_limit_below_kern, var); + if (a == undefined_math_parameter) { + math_param_error("limitbelowkern", var); + a = 0; + } + return a; +} + + +static scaled sub_shift_drop(int var) +{ + scaled a = get_math_param(math_param_sub_shift_drop, var); + if (a == undefined_math_parameter) { + math_param_error("subshiftdrop", var); + a = 0; + } + return a; +} + +static scaled sup_shift_drop(int var) +{ + scaled a = get_math_param(math_param_sup_shift_drop, var); + if (a == undefined_math_parameter) { + math_param_error("supshiftdrop", var); + a = 0; + } + return a; +} + +static scaled sub_shift_down(int var) +{ + scaled a = get_math_param(math_param_sub_shift_down, var); + if (a == undefined_math_parameter) { + math_param_error("subshiftdown", var); + a = 0; + } + return a; +} + +static scaled sub_sup_shift_down(int var) +{ + scaled a = get_math_param(math_param_sub_sup_shift_down, var); + if (a == undefined_math_parameter) { + math_param_error("subsupshiftdown", var); + a = 0; + } + return a; +} + +static scaled sub_top_max(int var) +{ + scaled a = get_math_param(math_param_sub_top_max, var); + if (a == undefined_math_parameter) { + math_param_error("subtopmax", var); + a = 0; + } + return a; +} + +static scaled sup_shift_up(int var) +{ + scaled a = get_math_param(math_param_sup_shift_up, var); + if (a == undefined_math_parameter) { + math_param_error("supshiftup", var); + a = 0; + } + return a; +} + +static scaled sup_bottom_min(int var) +{ + scaled a = get_math_param(math_param_sup_bottom_min, var); + if (a == undefined_math_parameter) { + math_param_error("supbottommin", var); + a = 0; + } + return a; +} + +static scaled sup_sub_bottom_max(int var) +{ + scaled a = get_math_param(math_param_sup_sub_bottom_max, var); + if (a == undefined_math_parameter) { + math_param_error("supsubbottommax", var); + a = 0; + } + return a; +} + +static scaled subsup_vgap(int var) +{ + scaled a = get_math_param(math_param_subsup_vgap, var); + if (a == undefined_math_parameter) { + math_param_error("subsupvgap", var); + a = 0; + } + return a; +} + +static scaled space_after_script(int var) +{ + scaled a = get_math_param(math_param_space_after_script, var); + if (a == undefined_math_parameter) { + math_param_error("spaceafterscript", var); + a = 0; + } + return a; +} + + +/* This function is no longer useful */ + +boolean check_necessary_fonts(void) +{ + return false; /* temp */ +} + +void fixup_math_parameters(integer fam_id, integer size_id, integer f, + integer lvl) +{ + if (is_new_mathfont(f)) { /* fix all known parameters */ + + DEFINE_MATH_PARAMETERS(math_param_quad, size_id, font_size(f), lvl); + DEFINE_DMATH_PARAMETERS(math_param_quad, size_id, font_size(f), lvl); + DEFINE_MATH_PARAMETERS(math_param_axis, size_id, + font_MATH_par(f, AxisHeight), lvl); + DEFINE_DMATH_PARAMETERS(math_param_axis, size_id, + font_MATH_par(f, AxisHeight), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id, + font_MATH_par(f, OverbarExtraAscender), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id, + font_MATH_par(f, OverbarExtraAscender), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id, + font_MATH_par(f, OverbarRuleThickness), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id, + font_MATH_par(f, OverbarRuleThickness), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id, + font_MATH_par(f, OverbarVerticalGap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id, + font_MATH_par(f, OverbarVerticalGap), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id, + font_MATH_par(f, UnderbarExtraDescender), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id, + font_MATH_par(f, UnderbarExtraDescender), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id, + font_MATH_par(f, UnderbarRuleThickness), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id, + font_MATH_par(f, UnderbarRuleThickness), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id, + font_MATH_par(f, UnderbarVerticalGap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id, + font_MATH_par(f, UnderbarVerticalGap), lvl); + + DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id, + font_MATH_par(f, StretchStackGapAboveMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id, + font_MATH_par(f, StretchStackGapAboveMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id, + font_MATH_par(f, StretchStackBottomShiftDown), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id, + font_MATH_par(f, StretchStackBottomShiftDown), + lvl); + + DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id, + font_MATH_par(f, StretchStackGapBelowMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id, + font_MATH_par(f, StretchStackGapBelowMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, + font_MATH_par(f, StretchStackTopShiftUp), lvl); + DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, + font_MATH_par(f, StretchStackTopShiftUp), lvl); + + + DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id, + font_MATH_par(f, StackTopShiftUp), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id, + font_MATH_par(f, StackTopDisplayStyleShiftUp), + lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id, + font_MATH_par(f, StackBottomShiftDown), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id, + font_MATH_par(f, + StackBottomDisplayStyleShiftDown), + lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id, + font_MATH_par(f, StackGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id, + font_MATH_par(f, StackDisplayStyleGapMin), lvl); + + DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id, + font_MATH_par(f, RadicalExtraAscender), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id, + font_MATH_par(f, RadicalExtraAscender), lvl); + + DEFINE_DMATH_PARAMETERS(math_param_operator_size, size_id, + font_MATH_par(f, DisplayOperatorMinHeight), + lvl); + + DEFINE_MATH_PARAMETERS(math_param_radical_rule, size_id, + font_MATH_par(f, RadicalRuleThickness), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_rule, size_id, + font_MATH_par(f, RadicalRuleThickness), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id, + font_MATH_par(f, RadicalVerticalGap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_vgap, size_id, + font_MATH_par(f, + RadicalDisplayStyleVerticalGap), + lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_before, size_id, + font_MATH_par(f, RadicalKernBeforeDegree), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_before, size_id, + font_MATH_par(f, RadicalKernBeforeDegree), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_after, size_id, + font_MATH_par(f, RadicalKernAfterDegree), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_after, size_id, + font_MATH_par(f, RadicalKernAfterDegree), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, size_id, + font_MATH_par(f, + RadicalDegreeBottomRaisePercent), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, size_id, + font_MATH_par(f, + RadicalDegreeBottomRaisePercent), + lvl); + if (size_id == text_size) { + def_math_param(math_param_sup_shift_up, display_style, + font_MATH_par(f, SuperscriptShiftUp), lvl); + def_math_param(math_param_sup_shift_up, cramped_display_style, + font_MATH_par(f, SuperscriptShiftUpCramped), lvl); + def_math_param(math_param_sup_shift_up, text_style, + font_MATH_par(f, SuperscriptShiftUp), lvl); + def_math_param(math_param_sup_shift_up, cramped_text_style, + font_MATH_par(f, SuperscriptShiftUpCramped), lvl); + } else if (size_id == script_size) { + def_math_param(math_param_sup_shift_up, script_style, + font_MATH_par(f, SuperscriptShiftUp), lvl); + def_math_param(math_param_sup_shift_up, cramped_script_style, + font_MATH_par(f, SuperscriptShiftUpCramped), lvl); + } else if (size_id == script_script_size) { + def_math_param(math_param_sup_shift_up, script_script_style, + font_MATH_par(f, SuperscriptShiftUp), lvl); + def_math_param(math_param_sup_shift_up, cramped_script_script_style, + font_MATH_par(f, SuperscriptShiftUpCramped), lvl); + } + + DEFINE_MATH_PARAMETERS(math_param_sub_shift_drop, size_id, + font_MATH_par(f, SubscriptBaselineDropMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_shift_drop, size_id, + font_MATH_par(f, SubscriptBaselineDropMin), + lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_shift_drop, size_id, + font_MATH_par(f, SuperscriptBaselineDropMax), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_shift_drop, size_id, + font_MATH_par(f, SuperscriptBaselineDropMax), + lvl); + DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDown), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDown), lvl); + DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDown), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + font_MATH_par(f, SubscriptShiftDown), lvl); + DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id, + font_MATH_par(f, SubscriptTopMax), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id, + font_MATH_par(f, SubscriptTopMax), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id, + font_MATH_par(f, SuperscriptBottomMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id, + font_MATH_par(f, SuperscriptBottomMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, + font_MATH_par(f, + SuperscriptBottomMaxWithSubscript), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, + font_MATH_par(f, + SuperscriptBottomMaxWithSubscript), + lvl); + DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id, + font_MATH_par(f, SubSuperscriptGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id, + font_MATH_par(f, SubSuperscriptGapMin), lvl); + + DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id, + font_MATH_par(f, UpperLimitGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id, + font_MATH_par(f, UpperLimitGapMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id, + font_MATH_par(f, UpperLimitBaselineRiseMin), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id, + font_MATH_par(f, UpperLimitBaselineRiseMin), + lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id, 0, lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id, 0, lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id, + font_MATH_par(f, LowerLimitGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id, + font_MATH_par(f, LowerLimitGapMin), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id, + font_MATH_par(f, LowerLimitBaselineDropMin), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id, + font_MATH_par(f, LowerLimitBaselineDropMin), + lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id, 0, lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id, 0, lvl); + + DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id, + font_MATH_par(f, FractionRuleThickness), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id, + font_MATH_par(f, FractionRuleThickness), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id, + font_MATH_par(f, FractionNumeratorGapMin), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id, + font_MATH_par(f, + FractionNumeratorDisplayStyleGapMin), + lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id, + font_MATH_par(f, FractionNumeratorShiftUp), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id, + font_MATH_par(f, + FractionNumeratorDisplayStyleShiftUp), + lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id, + font_MATH_par(f, FractionDenominatorGapMin), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id, + font_MATH_par(f, + FractionDenominatorDisplayStyleGapMin), + lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id, + font_MATH_par(f, FractionDenominatorShiftDown), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id, + font_MATH_par(f, + FractionDenominatorDisplayStyleShiftDown), + lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id, 0, lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id, 0, lvl); + + DEFINE_MATH_PARAMETERS(math_param_space_after_script, size_id, + font_MATH_par(f, SpaceAfterScript), lvl); + DEFINE_DMATH_PARAMETERS(math_param_space_after_script, size_id, + font_MATH_par(f, SpaceAfterScript), lvl); + + DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id, + font_MATH_par(f, MinConnectorOverlap), lvl); + DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id, + font_MATH_par(f, MinConnectorOverlap), lvl); + + + } else if (fam_id == 2 && is_old_mathfont(f, total_mathsy_params)) { + /* fix old-style |sy| parameters */ + DEFINE_MATH_PARAMETERS(math_param_quad, size_id, math_quad(size_id), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_quad, size_id, math_quad(size_id), + lvl); + DEFINE_MATH_PARAMETERS(math_param_axis, size_id, axis_height(size_id), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_axis, size_id, axis_height(size_id), + lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_num_up, size_id, num3(size_id), + lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_num_up, size_id, num1(size_id), + lvl); + DEFINE_MATH_PARAMETERS(math_param_stack_denom_down, size_id, + denom2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_denom_down, size_id, + denom1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_num_up, size_id, + num2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_num_up, size_id, + num1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_denom_down, size_id, + denom2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_down, size_id, + denom1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_del_size, size_id, + delim2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_del_size, size_id, + delim1(size_id), lvl); + if (size_id == text_size) { + def_math_param(math_param_sup_shift_up, display_style, + sup1(size_id), lvl); + def_math_param(math_param_sup_shift_up, cramped_display_style, + sup3(size_id), lvl); + def_math_param(math_param_sup_shift_up, text_style, sup2(size_id), + lvl); + def_math_param(math_param_sup_shift_up, cramped_text_style, + sup3(size_id), lvl); + } else if (size_id == script_size) { + def_math_param(math_param_sub_shift_drop, display_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, cramped_display_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, text_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, cramped_text_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, display_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, cramped_display_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, text_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, cramped_text_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_up, script_style, sup2(size_id), + lvl); + def_math_param(math_param_sup_shift_up, cramped_script_style, + sup3(size_id), lvl); + } else if (size_id == script_script_size) { + def_math_param(math_param_sub_shift_drop, script_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, cramped_script_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, script_script_style, + sub_drop(size_id), lvl); + def_math_param(math_param_sub_shift_drop, + cramped_script_script_style, sub_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, script_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, cramped_script_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, script_script_style, + sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_drop, + cramped_script_script_style, sup_drop(size_id), lvl); + def_math_param(math_param_sup_shift_up, script_script_style, + sup2(size_id), lvl); + def_math_param(math_param_sup_shift_up, cramped_script_script_style, + sup3(size_id), lvl); + } + DEFINE_MATH_PARAMETERS(math_param_sub_shift_down, size_id, + sub1(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_shift_down, size_id, + sub1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + sub2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_sup_shift_down, size_id, + sub2(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_sub_top_max, size_id, + (abs(math_x_height(size_id) * 4) / 5), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sub_top_max, size_id, + (abs(math_x_height(size_id) * 4) / 5), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_bottom_min, size_id, + (abs(math_x_height(size_id)) / 4), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_bottom_min, size_id, + (abs(math_x_height(size_id)) / 4), lvl); + DEFINE_MATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, + (abs(math_x_height(size_id) * 4) / 5), lvl); + DEFINE_DMATH_PARAMETERS(math_param_sup_sub_bottom_max, size_id, + (abs(math_x_height(size_id) * 4) / 5), lvl); + + } else if (fam_id == 3 && is_old_mathfont(f, total_mathex_params)) { + /* fix old-style |ex| parameters */ + DEFINE_MATH_PARAMETERS(math_param_overbar_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_overbar_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_overbar_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_underbar_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_underbar_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_radical_kern, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_radical_kern, size_id, + default_rule_thickness(size_id), lvl); + /* The display-size radical_vgap is done in |finalize_math_pamaters| because it needs values from both the + sy and the ex font. */ + DEFINE_MATH_PARAMETERS(math_param_radical_vgap, size_id, + (default_rule_thickness(size_id) + + (abs(default_rule_thickness(size_id)) / 4)), + lvl); + + DEFINE_MATH_PARAMETERS(math_param_stack_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_stack_vgap, size_id, + 7 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_rule, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_num_vgap, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_num_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_fraction_denom_vgap, size_id, + default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_fraction_denom_vgap, size_id, + 3 * default_rule_thickness(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_vgap, size_id, + big_op_spacing1(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_vgap, size_id, + big_op_spacing1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_bgap, size_id, + big_op_spacing3(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_bgap, size_id, + big_op_spacing3(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_above_kern, size_id, + big_op_spacing5(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_above_kern, size_id, + big_op_spacing5(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_vgap, size_id, + big_op_spacing2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_vgap, size_id, + big_op_spacing2(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_bgap, size_id, + big_op_spacing4(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_bgap, size_id, + big_op_spacing4(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_limit_below_kern, size_id, + big_op_spacing5(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_limit_below_kern, size_id, + big_op_spacing5(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_subsup_vgap, size_id, + 4 * default_rule_thickness(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_subsup_vgap, size_id, + 4 * default_rule_thickness(size_id), lvl); + /* All of the |space_after_script|s are done in finalize_math_parameters because the + \.{\\scriptspace} may have been altered by the user + */ + DEFINE_MATH_PARAMETERS(math_param_connector_overlap_min, size_id, 0, + lvl); + DEFINE_DMATH_PARAMETERS(math_param_connector_overlap_min, size_id, 0, + lvl); + + DEFINE_MATH_PARAMETERS(math_param_under_delimiter_vgap, size_id, + big_op_spacing2(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_vgap, size_id, + big_op_spacing2(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_under_delimiter_bgap, size_id, + big_op_spacing4(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_under_delimiter_bgap, size_id, + big_op_spacing4(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_over_delimiter_vgap, size_id, + big_op_spacing1(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_vgap, size_id, + big_op_spacing1(size_id), lvl); + DEFINE_MATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, + big_op_spacing3(size_id), lvl); + DEFINE_DMATH_PARAMETERS(math_param_over_delimiter_bgap, size_id, + big_op_spacing3(size_id), lvl); + + + } +} + +/* this needs to be called just at the start of |mlist_to_hlist| */ +void finalize_math_parameters(void) +{ + integer saved_trace = int_par(param_tracing_assigns_code); + int_par(param_tracing_assigns_code) = 0; + if (get_math_param(math_param_space_after_script, display_style) == + undefined_math_parameter) { + def_math_param(math_param_space_after_script, display_style, + script_space, cur_level); + def_math_param(math_param_space_after_script, text_style, script_space, + cur_level); + def_math_param(math_param_space_after_script, script_style, + script_space, cur_level); + def_math_param(math_param_space_after_script, script_script_style, + script_space, cur_level); + def_math_param(math_param_space_after_script, cramped_display_style, + script_space, cur_level); + def_math_param(math_param_space_after_script, cramped_text_style, + script_space, cur_level); + def_math_param(math_param_space_after_script, cramped_script_style, + script_space, cur_level); + def_math_param(math_param_space_after_script, + cramped_script_script_style, script_space, cur_level); + } + if (get_math_param(math_param_radical_vgap, display_style) == + undefined_math_parameter) { + def_math_param(math_param_radical_vgap, display_style, + (default_rule_thickness(text_size) + + (abs(math_x_height(text_size)) / 4)), cur_level); + def_math_param(math_param_radical_vgap, cramped_display_style, + (default_rule_thickness(text_size) + + (abs(math_x_height(text_size)) / 4)), cur_level); + } + if (get_math_param(math_param_radical_degree_raise, display_style) == + undefined_math_parameter) { + DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, + script_script_size, 60, cur_level); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, script_size, 60, + cur_level); + DEFINE_MATH_PARAMETERS(math_param_radical_degree_raise, text_size, 60, + cur_level); + DEFINE_DMATH_PARAMETERS(math_param_radical_degree_raise, text_size, 60, + cur_level); + } + if (get_math_param(math_param_radical_degree_before, display_style) == + undefined_math_parameter) { + def_math_param(math_param_radical_degree_before, + cramped_script_script_style, + xn_over_d(get_math_quad(cramped_script_script_style), 5, + 18), cur_level); + def_math_param(math_param_radical_degree_before, script_script_style, + xn_over_d(get_math_quad(script_script_style), 5, 18), + cur_level); + def_math_param(math_param_radical_degree_before, cramped_script_style, + xn_over_d(get_math_quad(cramped_script_style), 5, 18), + cur_level); + def_math_param(math_param_radical_degree_before, script_style, + xn_over_d(get_math_quad(script_style), 5, 18), + cur_level); + def_math_param(math_param_radical_degree_before, cramped_text_style, + xn_over_d(get_math_quad(cramped_text_style), 5, 18), + cur_level); + def_math_param(math_param_radical_degree_before, text_style, + xn_over_d(get_math_quad(text_style), 5, 18), cur_level); + def_math_param(math_param_radical_degree_before, cramped_display_style, + xn_over_d(get_math_quad(cramped_display_style), 5, 18), + cur_level); + def_math_param(math_param_radical_degree_before, display_style, + xn_over_d(get_math_quad(display_style), 5, 18), + cur_level); + } + + if (get_math_param(math_param_radical_degree_after, display_style) == + undefined_math_parameter) { + def_math_param(math_param_radical_degree_after, + cramped_script_script_style, + -xn_over_d(get_math_quad(cramped_script_script_style), + 10, 18), cur_level); + def_math_param(math_param_radical_degree_after, script_script_style, + -xn_over_d(get_math_quad(script_script_style), 10, 18), + cur_level); + def_math_param(math_param_radical_degree_after, cramped_script_style, + -xn_over_d(get_math_quad(cramped_script_style), 10, 18), + cur_level); + def_math_param(math_param_radical_degree_after, script_style, + -xn_over_d(get_math_quad(script_style), 10, 18), + cur_level); + def_math_param(math_param_radical_degree_after, cramped_text_style, + -xn_over_d(get_math_quad(cramped_text_style), 10, 18), + cur_level); + def_math_param(math_param_radical_degree_after, text_style, + -xn_over_d(get_math_quad(text_style), 10, 18), + cur_level); + def_math_param(math_param_radical_degree_after, cramped_display_style, + -xn_over_d(get_math_quad(cramped_display_style), 10, 18), + cur_level); + def_math_param(math_param_radical_degree_after, display_style, + -xn_over_d(get_math_quad(display_style), 10, 18), + cur_level); + } + int_par(param_tracing_assigns_code) = saved_trace; + +} + + +/* +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.) +*/ + + +char *math_size_string(integer s) +{ + if (s == text_size) + return "textfont"; + else if (s == script_size) + return "scriptfont"; + else + return "scriptscriptfont"; +} + +void print_size(integer s) +{ + if (s == text_size) + tprint_esc("textfont"); + else if (s == script_size) + tprint_esc("scriptfont"); + else + tprint_esc("scriptscriptfont"); +} + + +/* + @ When the style changes, the following piece of program computes associated + information: +*/ + +#define setup_cur_size_and_mu() do { \ + if (cur_style==script_style || \ + cur_style==cramped_script_style) \ + cur_size=script_size; \ + else if (cur_style==script_script_style || \ + cur_style==cramped_script_script_style) \ + cur_size=script_script_size; \ + else cur_size=text_size; \ + cur_mu=x_over_n(get_math_quad(cur_size),18); \ + } while (0) + +/* a simple routine that creates a flat copy of a nucleus */ + +pointer math_clone(pointer q) +{ + pointer x; + if (q == null) + return null; + x = new_node(type(q), 0); + reset_attributes(x, node_attr(q)); + if (type(q) == math_char_node) { + math_fam(x) = math_fam(q); + math_character(x) = math_character(q); + } else { + math_list(x) = math_list(q); + } + return x; +} + + + + +/* + @ 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. +*/ + +pointer do_fraction_rule(scaled t, pointer att) +{ + pointer p; /* the new node */ + p = new_rule(); + rule_dir(p) = math_direction; + height(p) = t; + depth(p) = 0; + reset_attributes(p, att); + 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|. +*/ + +pointer overbar(pointer b, scaled k, scaled t, scaled ht, pointer att) +{ + pointer p, q; /* nodes being constructed */ + p = new_kern(k); + vlink(p) = b; + reset_attributes(p, att); + q = do_fraction_rule(t, att); + vlink(q) = p; + p = new_kern(ht); + reset_attributes(p, att); + vlink(p) = q; + pack_direction = math_direction; + q = vpackage(p, 0, additional, max_dimen); + reset_attributes(q, att); + 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. +*/ + +static pointer char_box(internal_font_number f, integer c, pointer bb) +{ + pointer b, p; /* the new box and its character node */ + b = new_null_box(); + width(b) = char_width(f, c) + char_italic(f, c); + height(b) = char_height(f, c); + depth(b) = char_depth(f, c); + reset_attributes(b, bb); + p = new_char(f, c); + reset_attributes(p, bb); + list_ptr(b) = p; + return b; +} + +/* + Another handy subroutine computes the height plus depth of + a given character: +*/ + +scaled height_plus_depth(internal_font_number f, integer 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|: +*/ + +scaled stack_into_box(pointer b, internal_font_number f, integer c) +{ + pointer p; /* new node placed into |b| */ + p = char_box(f, c, node_attr(b)); + vlink(p) = list_ptr(b); + list_ptr(b) = p; + height(b) = height(p); + return height_plus_depth(f, c); +} + + +scaled stack_into_hbox(pointer b, internal_font_number f, integer c) +{ + pointer p, q; /* new node placed into |b| */ + p = char_box(f, c, node_attr(b)); + q = list_ptr(b); + if (q == null) { + list_ptr(b) = p; + } else { + while (vlink(q) != null) + q = vlink(q); + vlink(q) = p; + } + if (height(b) < height(p)) + height(b) = height(p); + if (depth(b) < depth(p)) + depth(b) = depth(p); + return char_width(f, c); +} + + + +void add_delim_kern(pointer b, scaled s) +{ + pointer p; /* new node placed into |b| */ + p = new_kern(s); + reset_attributes(p, node_attr(b)); + vlink(p) = list_ptr(b); + list_ptr(b) = p; +} + +void add_delim_hkern(pointer b, scaled s) +{ + pointer p, q; /* new node placed into |b| */ + p = new_kern(s); + reset_attributes(p, node_attr(b)); + q = list_ptr(b); + if (q == null) { + list_ptr(b) = p; + } else { + while (vlink(q) != null) + q = vlink(q); + vlink(q) = p; + } +} + + + +/* */ + +pointer get_delim_box(extinfo * ext, internal_font_number f, scaled v, + pointer att, int boxtype) +{ + pointer b; + extinfo *cur; + scaled min_overlap, prev_overlap; + scaled b_max; /* natural (maximum) height of the stack */ + scaled s_max; /* amount of possible shrink in the stack */ + scaled a, wd, ht, dp, last_ht; + integer cc; /* a temporary character code for extensibles */ + integer i; /* a temporary counter number of extensible pieces */ + int with_extenders; + int num_extenders, num_normal, num_total; + scaled c, d, u; + scaled *max_shrinks = NULL; + assert(ext != NULL); + b = new_null_box(); + type(b) = boxtype; + reset_attributes(b, att); + min_overlap = connector_overlap_min(cur_style); + assert(min_overlap >= 0); + with_extenders = 0; + num_extenders = 0; + num_normal = 0; + cur = ext; + while (cur != NULL) { + if (!char_exists(f, cur->glyph)) { + char *hlp[] = { + "Each glyph part in an extensible item should exist in the font.", + "I will give up trying to find a suitable size for now. Fix your font!", + NULL + }; + tex_error("Variant part doesn't exist.", hlp); + width(b) = null_delimiter_space; + return b; + } + if (cur->extender > 0) + num_extenders++; + else + num_normal++; + /* no negative overlaps or advances are allowed */ + if (cur->start_overlap < 0 || cur->end_overlap < 0 || cur->advance < 0) { + char *hlp[] = { + "All measurements in extensible items should be positive.", + "To get around this problem, I have changed the font metrics.", + "Fix your font!", + NULL + }; + tex_error("Extensible recipe has negative fields.", hlp); + if (cur->start_overlap < 0) + cur->start_overlap = 0; + if (cur->end_overlap < 0) + cur->end_overlap = 0; + if (cur->advance < 0) + cur->advance = 0; + } + cur = cur->next; + } + if (num_normal == 0) { + char *hlp[] = { + "Each extensible recipe should have at least one non-repeatable part.", + "To get around this problem, I have changed the first part to be", + "non-repeatable. Fix your font!", + NULL + }; + tex_error("Extensible recipe has no fixed parts.", hlp); + ext->extender = 0; + num_normal = 1; + num_extenders--; + } + /* |ext| holds a linked list of numerous items that may or may not be + repeatable. For the total height, we have to figure out how many items + are needed to create a stack of at least |v|. + The next |while| loop does that. It has two goals: it finds out + the natural height |b_max| of the all the parts needed to reach + at least |v|, and it sets |with_extenders| to the number of times + each of the repeatable items in |ext| has to be repeated to reach + that height. + */ + cur = ext; + prev_overlap = -1; + b_max = 0; + s_max = 0; + for (cur = ext; cur != NULL; cur = cur->next) { + /* substract width of the current overlap if this is not the first */ + if (cur->extender == 0) { /* not an extender */ + a = cur->advance; + if (a == 0) { + if (boxtype == vlist_node) + a = height_plus_depth(f, cur->glyph); /* for tfm fonts */ + else + a = char_width(f, cur->glyph); /* for tfm fonts */ + assert(a > 0); + } + b_max += a; /* add the advance value */ + if (prev_overlap >= 0) { + c = min_overlap; + if (c >= a) + c = (a - 1); + b_max -= c; + d = c; + if (prev_overlap > cur->start_overlap) { + if (cur->start_overlap > d) + d = cur->start_overlap; + } else { + if (prev_overlap > d) + d = prev_overlap; + } + s_max += (d - c); + } + prev_overlap = cur->end_overlap; + } + } + if (b_max < v && num_extenders > 0) { /* not large enough, but can grow */ + RETRY: + with_extenders++; + cur = ext; + prev_overlap = -1; + b_max = 0; + s_max = 0; + i = with_extenders; + while (cur != NULL) { + a = cur->advance; + if (a == 0) { + if (boxtype == vlist_node) + a = height_plus_depth(f, cur->glyph); /* for tfm fonts */ + else + a = char_width(f, cur->glyph); + assert(a >= 0); + } + /* substract width of the current overlap if this is not the first */ + if (prev_overlap >= 0) { + c = min_overlap; + if (c >= a) + c = (a - 1); + b_max -= c; + d = c; + if (prev_overlap > cur->start_overlap) { + if (cur->start_overlap > d) + d = cur->start_overlap; + } else { + if (prev_overlap > d) + d = prev_overlap; + } + s_max += (d - c); + } + if (cur->extender == 0) { /* not an extender */ + i = 0; + prev_overlap = cur->end_overlap; + } else { + i--; + prev_overlap = cur->end_overlap; + } + b_max += a; /* add the advance value */ + if (i <= 0) { /* can be $-1$ if the first glyph is an extender */ + cur = cur->next; + i = with_extenders; + } + } + if (b_max < v) { /* not large enough, but can grow */ + goto RETRY; + } + } + /* now |b_max| is the natural height or width, |with_extenders| holds + the count of each extender that is needed, and the maximum + amount the stack can shrink by is |s_max|. + + |(b_max-v)| is the total amount of extra height or width that needs + to be gotten rid of, and the total number of items in the stack is + |(num_extenders*with_extenders)+num_normal| + */ + /* create an array of maximum shrinks and fill it */ + num_total = ((num_extenders * with_extenders) + num_normal); + if (num_total == 1) { + /* weird, but could happen */ + cc = ext->glyph; + (void) stack_into_box(b, f, cc); + width(b) = char_width(f, cc); + height(b) = char_height(f, cc); + depth(b) = char_depth(f, cc); + return b; + } + max_shrinks = xcalloc(num_total, sizeof(scaled)); + cur = ext; + prev_overlap = -1; + c = 0; + i = 0; + REDO: + while (cur != NULL) { + if (cur->extender == 0 || with_extenders) { + if (prev_overlap >= 0) { + d = prev_overlap; + if (d > cur->start_overlap) + d = cur->start_overlap; + if (d < min_overlap) + d = min_overlap; + max_shrinks[c++] = (d - min_overlap); + } + prev_overlap = cur->end_overlap; + if (cur->extender == 0) { + /* simple char, just reset |i| */ + i = 0; + } else { + if (i == 0) { /* first in loop */ + i = with_extenders; + if (i != 1) + goto REDO; + } else if (i == 1) { + /* done */ + i = 0; + } else { + i--; + if (i != 1) + goto REDO; + } + } + } + cur = cur->next; + } + /* now create the box contents */ + cur = ext; + wd = 0; + d = 0; + ht = 0; + dp = 0; + if (boxtype == vlist_node) { + while (cur != NULL) { + cc = cur->glyph; + if (char_width(f, cc) > wd) + wd = char_width(f, cc); + if (cur->extender > 0) { + i = with_extenders; + while (i > 0) { + ht += stack_into_box(b, f, cc); + if (d < (num_total - 1)) { + u = min_overlap; + if (s_max != 0) + u += xn_over_d(max_shrinks[d], (b_max - v), s_max); + add_delim_kern(b, -u); + ht -= u; + } + d++; + i--; + } + } else { + ht += stack_into_box(b, f, cc); + if (d < (num_total - 1)) { + u = min_overlap; + if (s_max != 0) + u += xn_over_d(max_shrinks[d], (b_max - v), s_max); + add_delim_kern(b, -u); + ht -= u; + } + d++; + } + cur = cur->next; + } + xfree(max_shrinks); + /* it is important to use |ht| here instead of |v| because if there + was not enough shrink to get the correct size, it has to be centered + based on its actual height. That actual height is not the same as + |b_max| either because |min_overlap| can have ben set by the user + outside of the font's control. + */ + last_ht = 0; + height(b) = ht; + depth(b) = 0; + /* the next correction is needed for radicals */ + if (list_ptr(b) != null && type(list_ptr(b)) == hlist_node && list_ptr(list_ptr(b)) != null && type(list_ptr(list_ptr(b))) == glyph_node) { /* and it should be */ + last_ht = + char_height(font(list_ptr(list_ptr(b))), + character(list_ptr(list_ptr(b)))); + height(b) = last_ht; + depth(b) = ht - last_ht; + } + /* + fprintf (stdout,"v=%f,b_max=%f,ht=%f,n=%d\n", (float)v/65536.0, + (float)b_max/65536.0,(float)height(b)/65536.0,num_total); + */ + width(b) = wd; + } else { + /* horizontal version */ + + while (cur != NULL) { + cc = cur->glyph; + if (char_height(f, cc) > ht) + ht = char_height(f, cc); + if (char_depth(f, cc) > dp) + dp = char_depth(f, cc); + if (cur->extender > 0) { + i = with_extenders; + while (i > 0) { + wd += stack_into_hbox(b, f, cc); + if (d < (num_total - 1)) { + u = min_overlap; + if (s_max != 0) + u += xn_over_d(max_shrinks[d], (b_max - v), s_max); + add_delim_hkern(b, -u); + wd -= u; + } + d++; + i--; + } + } else { + wd += stack_into_hbox(b, f, cc); + if (d < (num_total - 1)) { + u = min_overlap; + if (s_max != 0) + u += xn_over_d(max_shrinks[d], (b_max - v), s_max); + add_delim_hkern(b, -u); + wd -= u; + } + d++; + } + cur = cur->next; + } + xfree(max_shrinks); + /* it is important to use |wd| here instead of |v| because if there + was not enough shrink to get the correct size, it has to be centered + based on its actual width. That actual width is not the same as + |b_max| either because |min_overlap| can have ben set by the user + outside of the font's control. + */ + width(b) = wd; + } + return b; +} + +pointer get_delim_vbox(extinfo * ext, internal_font_number f, scaled v, + pointer att) +{ + return get_delim_box(ext, f, v, att, vlist_node); +} + +pointer get_delim_hbox(extinfo * ext, internal_font_number f, scaled v, + pointer att) +{ + return get_delim_box(ext, f, v, att, hlist_node); +} + + + +/* + @ 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. +*/ + +void endless_loop_error(internal_font_number g, integer y) +{ + char s[256]; + char *hlp[] = { + "You managed to create a seemingly endless charlist chain in the current", + "font. I have counted until 10000 already and still have not escaped, so" + "I will jump out of the loop all by myself now. Fix your font!", + NULL + }; + snprintf(s, 256, "Math error: endless loop in charlist (U+%04x in %s)", + (int) y, font_name(g)); + tex_error(s, hlp); +} + +pointer var_delimiter(pointer d, integer s, scaled v) +{ + /* label found,continue; */ + pointer b; /* the box that will be constructed */ + internal_font_number f, g; /* best-so-far and tentative font codes */ + integer c, i, x, y; /* best-so-far and tentative character codes */ + scaled u; /* height-plus-depth of a tentative character */ + scaled w; /* largest height-plus-depth so far */ + integer z; /* runs through font family members */ + boolean large_attempt; /* are we trying the ``large'' variant? */ + pointer att; /* to save the current attribute list */ + extinfo *ext; + att = null; + f = null_font; + c = 0; + w = 0; + large_attempt = false; + if (d == null) + goto FOUND; + z = small_fam(d); + x = small_char(d); + i = 0; + while (true) { + /* The search process is complicated slightly by the facts that some of the + characters might not be present in some of the fonts, and they might not + be probed in increasing order of height. */ + if ((z != 0) || (x != 0)) { + g = fam_fnt(z, s); + if (g != null_font) { + y = x; + CONTINUE: + i++; + if (char_exists(g, y)) { + if (char_tag(g, y) == ext_tag) { + f = g; + c = y; + goto FOUND; + } + u = height_plus_depth(g, y); + if (u > w) { + f = g; + c = y; + w = u; + if (u >= v) + goto FOUND; + } + if (i > 10000) { + /* endless loop */ + endless_loop_error(g, y); + goto FOUND; + } + if (char_tag(g, y) == list_tag) { + y = char_remainder(g, y); + goto CONTINUE; + } + } + } + } + if (large_attempt) + goto FOUND; /* there were none large enough */ + large_attempt = true; + z = large_fam(d); + x = large_char(d); + } + FOUND: + if (d != null) { + att = node_attr(d); + node_attr(d) = null; + flush_node(d); + } + if (f != null_font) { + /* When the following code is executed, |char_tag(q)| will be equal to + |ext_tag| if and only if a built-up symbol is supposed to be returned. + */ + ext = NULL; + if ((char_tag(f, c) == ext_tag) && + ((ext = get_charinfo_vert_variants(char_info(f, c))) != NULL)) { + b = get_delim_vbox(ext, f, v, att); + width(b) += char_italic(f, c); + } else { + b = char_box(f, c, att); + } + } else { + b = new_null_box(); + reset_attributes(b, att); + width(b) = null_delimiter_space; /* use this width if no delimiter was found */ + } + shift_amount(b) = half(height(b) - depth(b)) - math_axis(s); + delete_attribute_ref(att); + return b; +} + +pointer flat_var_delimiter(pointer d, integer s, scaled v) +{ + /* label found,continue; */ + pointer b; /* the box that will be constructed */ + internal_font_number f, g; /* best-so-far and tentative font codes */ + integer c, i, x, y; /* best-so-far and tentative character codes */ + scaled u; /* height-plus-depth of a tentative character */ + scaled w; /* largest height-plus-depth so far */ + integer z; /* runs through font family members */ + boolean large_attempt; /* are we trying the ``large'' variant? */ + pointer att; /* to save the current attribute list */ + extinfo *ext; + f = null_font; + c = 0; + w = 0; + att = null; + large_attempt = false; + if (d == null) + goto FOUND; + z = small_fam(d); + x = small_char(d); + i = 0; + while (true) { + /* The search process is complicated slightly by the facts that some of the + characters might not be present in some of the fonts, and they might not + be probed in increasing order of height. */ + if ((z != 0) || (x != 0)) { + g = fam_fnt(z, s); + if (g != null_font) { + y = x; + CONTINUE: + i++; + if (char_exists(g, y)) { + if (char_tag(g, y) == ext_tag) { + f = g; + c = y; + goto FOUND; + } + u = char_width(g, y); + if (u > w) { + f = g; + c = y; + w = u; + if (u >= v) + goto FOUND; + } + if (i > 10000) { + /* endless loop */ + endless_loop_error(g, y); + goto FOUND; + } + if (char_tag(g, y) == list_tag) { + y = char_remainder(g, y); + goto CONTINUE; + } + } + } + } + if (large_attempt) + goto FOUND; /* there were none large enough */ + large_attempt = true; + z = large_fam(d); + x = large_char(d); + } + FOUND: + if (d != null) { + att = node_attr(d); + node_attr(d) = null; + flush_node(d); + } + if (f != null_font) { + /* When the following code is executed, |char_tag(q)| will be equal to + |ext_tag| if and only if a built-up symbol is supposed to be returned. + */ + ext = NULL; + if ((char_tag(f, c) == ext_tag) && + ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) { + b = get_delim_hbox(ext, f, v, att); + width(b) += char_italic(f, c); + } else { + b = char_box(f, c, att); + } + } else { + b = new_null_box(); + reset_attributes(b, att); + width(b) = 0; /* use this width if no delimiter was found */ + } + 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. + + +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. +*/ + +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 */ + + if ((width(b) != w) && (list_ptr(b) != null)) { + if (type(b) == vlist_node) { + p = hpack(b, 0, additional); + reset_attributes(p, node_attr(b)); + b = p; + } + p = list_ptr(b); + att = node_attr(b); + add_node_attr_ref(att); + if ((is_char_node(p)) && (vlink(p) == null)) { + f = font(p); + v = char_width(f, character(p)); + if (v != width(b)) { + q = new_kern(width(b) - v); + reset_attributes(q, att); + vlink(p) = q; + } + } + list_ptr(b) = null; + flush_node(b); + b = new_glue(ss_glue); + reset_attributes(b, att); + vlink(b) = p; + while (vlink(p) != null) + p = vlink(p); + q = new_glue(ss_glue); + reset_attributes(q, att); + vlink(p) = q; + r = hpack(b, w, exactly); + reset_attributes(r, att); + delete_attribute_ref(att); + return r; + } else { + width(b) = w; + return b; + } +} + +/* + Here is a subroutine that creates a new glue specification from another +one that is expressed in `\.{mu}', given the value of the math unit. +*/ + +#define mu_mult(A) mult_and_add(n,(A),xn_over_d((A),f,unity),max_dimen) + +pointer math_glue(pointer g, scaled m) +{ + pointer p; /* the new glue specification */ + integer n; /* integer part of |m| */ + scaled f; /* fraction part of |m| */ + n = x_over_n(m, unity); + f = tex_remainder; + if (f < 0) { + decr(n); + f = f + unity; + } + p = new_node(glue_spec_node, 0); + width(p) = mu_mult(width(g)); /* convert \.{mu} to \.{pt} */ + stretch_order(p) = stretch_order(g); + if (stretch_order(p) == normal) + stretch(p) = mu_mult(stretch(g)); + else + stretch(p) = stretch(g); + shrink_order(p) = shrink_order(g); + if (shrink_order(p) == normal) + shrink(p) = mu_mult(shrink(g)); + else + shrink(p) = shrink(g); + return p; +} + +/* +The |math_kern| subroutine removes |mu_glue| from a kern node, given +the value of the math unit. +*/ + +void math_kern(pointer p, scaled m) +{ + integer n; /* integer part of |m| */ + scaled f; /* fraction part of |m| */ + if (subtype(p) == mu_glue) { + n = x_over_n(m, unity); + f = tex_remainder; + if (f < 0) { + decr(n); + f = f + unity; + } + width(p) = mu_mult(width(p)); + subtype(p) = explicit; + } +} + +/* + \TeX's most important routine for dealing with formulas is called +|mlist_to_hlist|. After a formula has been scanned and represented as an +mlist, this routine converts it to an hlist that can be placed into a box +or incorporated into the text of a paragraph. There are three implicit +parameters, passed in global variables: |cur_mlist| points to the first +node or noad in the given mlist (and it might be |null|); |cur_style| is a +style code; and |mlist_penalties| is |true| if penalty nodes for potential +line breaks are to be inserted into the resulting hlist. After +|mlist_to_hlist| has acted, |vlink(temp_head)| points to the translated hlist. + +Since mlists can be inside mlists, the procedure is recursive. And since this +is not part of \TeX's inner loop, the program has been written in a manner +that stresses compactness over efficiency. +@^recursion@> +*/ + +pointer cur_mlist; /* beginning of mlist to be translated */ +integer cur_style; /* style code at current place in the list */ +integer cur_size; /* size code corresponding to |cur_style| */ +scaled cur_mu; /* the math unit width corresponding to |cur_size| */ +boolean mlist_penalties; /* should |mlist_to_hlist| insert penalties? */ + +void run_mlist_to_hlist(pointer p, integer m_style, boolean penalties) +{ + int callback_id; + int a, sfix; + lua_State *L = Luas; + finalize_math_parameters(); + callback_id = callback_defined(mlist_to_hlist_callback); + if (p != null && callback_id > 0) { + sfix = lua_gettop(L); + if (!get_callback(L, callback_id)) { + lua_settop(L, sfix); + return; + } + nodelist_to_lua(L, p); /* arg 1 */ + lua_pushstring(L, math_style_names[m_style]); /* arg 2 */ + lua_pushboolean(L, penalties); /* arg 3 */ + if (lua_pcall(L, 3, 1, 0) != 0) { /* 3 args, 1 result */ + fprintf(stdout, "error: %s\n", lua_tostring(L, -1)); + lua_settop(L, sfix); + error(); + return; + } + a = nodelist_from_lua(L); + lua_settop(L, sfix); + vlink(temp_head) = a; + } else { + cur_mlist = p; + cur_style = m_style; + mlist_penalties = penalties; + mlist_to_hlist(); + } +} + + +/* +@ 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@> + + +The box returned by |clean_box| is ``clean'' in the +sense that its |shift_amount| is zero. +*/ + +pointer clean_box(pointer p, integer s) +{ + pointer q; /* beginning of a list to be boxed */ + integer save_style; /* |cur_style| to be restored */ + pointer x; /* box to be returned */ + pointer r; /* temporary pointer */ + switch (type(p)) { + case math_char_node: + cur_mlist = new_noad(); + r = math_clone(p); + nucleus(cur_mlist) = r; + break; + case sub_box_node: + q = math_list(p); + goto FOUND; + break; + case sub_mlist_node: + cur_mlist = math_list(p); + break; + default: + q = new_null_box(); + goto FOUND; + } + save_style = cur_style; + cur_style = s; + mlist_penalties = false; + mlist_to_hlist(); + q = vlink(temp_head); /* recursive call */ + cur_style = save_style; /* restore the style */ + setup_cur_size_and_mu(); + FOUND: + if (is_char_node(q) || (q == null)) + x = hpack(q, 0, additional); + else if ((vlink(q) == null) && (type(q) <= vlist_node) + && (shift_amount(q) == 0)) + x = q; /* it's already clean */ + else + x = hpack(q, 0, additional); + if (x != q && q != null) + reset_attributes(x, node_attr(q)); + /* Here we save memory space in a common case. */ + q = list_ptr(x); + if (is_char_node(q)) { + r = vlink(q); + if (r != null) { + if (vlink(r) == null) { + if (!is_char_node(r)) { + if (type(r) == kern_node) { + /* unneeded italic correction */ + flush_node(r); + vlink(q) = null; + } + } + } + } + } + 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|. +*/ +/* The outputs of |fetch| are placed in global variables. */ + +internal_font_number cur_f; /* the |font| field of a |math_char| */ +integer cur_c; /* the |character| field of a |math_char| */ + +void fetch(pointer a) +{ /* unpack the |math_char| field |a| */ + cur_c = math_character(a); + cur_f = fam_fnt(math_fam(a), cur_size); + if (cur_f == null_font) { + char *msg; + char *hlp[] = { + "Somewhere in the math formula just ended, you used the", + "stated character from an undefined font family. For example,", + "plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,", + "and I'll try to forget that I needed that character.", + NULL + }; + msg = xmalloc(256); + snprintf(msg, 255, "\\%s%d is undefined (character %d)", + math_size_string(cur_size), (int) math_fam(a), (int) cur_c); + tex_error(msg, hlp); + free(msg); + } else { + if (!(char_exists(cur_f, cur_c))) { + char_warning(cur_f, cur_c); + } + } +} + + +/* +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 second pass eliminates all noads and inserts the correct glue and +penalties between nodes. +*/ + +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 ord_noad: + case op_noad: + case bin_noad: + case rel_noad: + case open_noad: + case close_noad: + case punct_noad: + case inner_noad: + case over_noad: + case under_noad: + case vcenter_noad: + case accent_noad: + if (nucleus(q) != null) { + math_list(nucleus(q)) = null; + flush_node(nucleus(q)); + nucleus(q) = null; + } + break; + } + new_hlist(q) = r; +} + +#define choose_mlist(A) do { p=A(q); A(q)=null; } while (0) + +/* +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. +*/ + +void make_over(pointer q) +{ + pointer p; + p = overbar(clean_box(nucleus(q), cramped_style(cur_style)), + overbar_vgap(cur_style), + overbar_rule(cur_style), + overbar_kern(cur_style), node_attr(nucleus(q))); + math_list(nucleus(q)) = p; + type(nucleus(q)) = sub_box_node; +} + +void make_under(pointer q) +{ + pointer p, x, y, r; /* temporary registers for box construction */ + scaled delta; /* overall height plus depth */ + x = clean_box(nucleus(q), cur_style); + p = new_kern(underbar_vgap(cur_style)); + reset_attributes(p, node_attr(q)); + vlink(x) = p; + r = do_fraction_rule(underbar_rule(cur_style), node_attr(q)); + vlink(p) = r; + pack_direction = math_direction; + y = vpackage(x, 0, additional, max_dimen); + reset_attributes(y, node_attr(q)); + delta = height(y) + depth(y) + underbar_kern(cur_style); + height(y) = height(x); + depth(y) = delta - height(y); + math_list(nucleus(q)) = y; + type(nucleus(q)) = sub_box_node; +} + +void make_vcenter(pointer q) +{ + pointer v; /* the box that should be centered vertically */ + scaled delta; /* its height plus depth */ + v = math_list(nucleus(q)); + if (type(v) != vlist_node) + tconfusion("vcenter"); /* this can't happen vcenter */ + delta = height(v) + depth(v); + height(v) = math_axis(cur_size) + half(delta); + depth(v) = delta - height(v); +} + +/* +@ According to the rules in the \.{DVI} file specifications, we ensure alignment +@^square roots@> +between a square root sign and the rule above its nucleus by assuming that the +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. +*/ + +void make_radical(pointer q) +{ + pointer x, y, p; /* temporary registers for box construction */ + scaled delta, clr, theta, h; /* dimensions involved in the calculation */ + x = clean_box(nucleus(q), cramped_style(cur_style)); + clr = radical_vgap(cur_style); + theta = radical_rule(cur_style); + if (theta == undefined_math_parameter) { + theta = fraction_rule(cur_style); + y = var_delimiter(left_delimiter(q), cur_size, + height(x) + depth(x) + clr + theta); + left_delimiter(q) = null; + theta = height(y); + delta = depth(y) - (height(x) + depth(x) + clr); + if (delta > 0) + clr = clr + half(delta); /* increase the actual clearance */ + shift_amount(y) = -(height(x) + clr); + h = shift_amount(y) + height(y); + } else { + y = var_delimiter(left_delimiter(q), cur_size, + height(x) + depth(x) + clr + theta); + left_delimiter(q) = null; + delta = height(y) - (height(x) + clr + theta); + shift_amount(y) = delta; + h = -(height(y) - shift_amount(y)); + } + p = overbar(x, clr, theta, radical_kern(cur_style), node_attr(y)); + vlink(y) = p; + if (degree(q) != null) { + scaled wr, br, ar; + pointer r = clean_box(degree(q), script_script_style); + reset_attributes(r, node_attr(degree(q))); + wr = width(r); + if (wr == 0) { + flush_node(r); + } else { + br = radical_degree_before(cur_style); + ar = radical_degree_after(cur_style); + if (-ar > (wr + br)) + ar = -(wr + br); + x = new_kern(ar); + reset_attributes(x, node_attr(degree(q))); + vlink(x) = y; + shift_amount(r) = + (xn_over_d(h, radical_degree_raise(cur_style), 100)); + vlink(r) = x; + x = new_kern(br); + reset_attributes(x, node_attr(degree(q))); + vlink(x) = r; + y = x; + } + degree(q) = null; + } + p = hpack(y, 0, additional); + reset_attributes(p, node_attr(q)); + math_list(nucleus(q)) = p; + type(nucleus(q)) = sub_box_node; +} + + +/* Construct a vlist box */ +static pointer +wrapup_delimiter (pointer x, pointer y, pointer q, + scaled shift_up, scaled shift_down) { + pointer p; /* temporary register for box construction */ + pointer v = new_null_box(); + type(v) = vlist_node; + height(v) = shift_up + height(x); + depth(v) = depth(y) + shift_down; + reset_attributes(v, node_attr(q)); + p = new_kern((shift_up - depth(x)) - (height(y) - shift_down)); + reset_attributes(p, node_attr(q)); + vlink(p) = y; + vlink(x) = p; + list_ptr(v) = x; + return v; +} + +#define fixup_widths(x,y) do { \ + if (width(y) >= width(x)) { \ + width(x) = width(y); \ + } else { \ + width(y) = width(x); \ + } \ + } while (0) + +/* this has the |nucleus| box |x| as a limit above an extensible delimiter |y| */ + +void make_over_delimiter(pointer q) +{ + pointer x, y, v; /* temporary registers for box construction */ + scaled shift_up, shift_down, clr, delta; + x = clean_box(nucleus(q), sub_style(cur_style)); + y = flat_var_delimiter(left_delimiter(q), cur_size, width(x)); + left_delimiter(q) = null; + fixup_widths(x,y); + shift_up = over_delimiter_bgap(cur_style); + shift_down = 0; /* under_delimiter_bgap(cur_style); */ + clr = over_delimiter_vgap(cur_style); + delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); + if (delta > 0) { + shift_up = shift_up + delta; + } + v = wrapup_delimiter (x, y, q, shift_up, shift_down); + width(v) = width(x); /* this also equals |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| */ + +void make_delimiter_over(pointer q) +{ + pointer x, y, v; /* temporary registers for box construction */ + scaled shift_up, shift_down, clr, delta; + y = clean_box(nucleus(q), cur_style); + x = flat_var_delimiter(left_delimiter(q), cur_size+(cur_size==script_script_size?0:1), width(y)); + left_delimiter(q) = null; + fixup_widths(x,y); + shift_up = over_delimiter_bgap(cur_style); + shift_down = 0; + clr = over_delimiter_vgap(cur_style); + delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); + if (delta > 0) { + shift_up = shift_up + delta; + } + v = wrapup_delimiter (x, y, q, shift_up, shift_down); + width(v) = width(x); /* this also equals |width(y)| */ + 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| */ + +void make_delimiter_under(pointer q) +{ + pointer x, y, v; /* temporary registers for box construction */ + scaled shift_up, shift_down, clr, delta; + x = clean_box(nucleus(q), cur_style); + y = flat_var_delimiter(left_delimiter(q), cur_size+(cur_size==script_script_size?0:1) , width(x)); + left_delimiter(q) = null; + fixup_widths(x,y); + shift_up = 0; /* over_delimiter_bgap(cur_style); */ + shift_down = under_delimiter_bgap(cur_style); + clr = under_delimiter_vgap(cur_style); + delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); + if (delta > 0) { + shift_down = shift_down + delta; + } + v = wrapup_delimiter (x, y, q, shift_up, shift_down); + width(v) = width(y); /* this also equals |width(y)| */ + math_list(nucleus(q)) = v; + type(nucleus(q)) = sub_box_node; + +} + +/* this has the extensible delimiter |x| as a limit below |nucleus| box |y| */ + +void make_under_delimiter(pointer q) +{ + pointer x, y, v; /* temporary registers for box construction */ + scaled shift_up, shift_down, clr, delta; + y = clean_box(nucleus(q), sup_style(cur_style)); + x = flat_var_delimiter(left_delimiter(q), cur_size, width(y)); + left_delimiter(q) = null; + fixup_widths(x,y); + shift_up = 0; /* over_delimiter_bgap(cur_style); */ + shift_down = under_delimiter_bgap(cur_style); + clr = under_delimiter_vgap(cur_style); + delta = clr - ((shift_up - depth(x)) - (height(y) - shift_down)); + if (delta > 0) { + shift_down = shift_down + delta; + } + v = wrapup_delimiter (x, y, q, shift_up, shift_down); + width(v) = width(y); /* this also equals |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. +*/ + +#define TOP_CODE 1 +#define BOT_CODE 2 + +void do_make_math_accent(pointer q, internal_font_number f, integer c, + int top_or_bot) +{ + 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| */ + extinfo *ext; + pointer attr_p; + attr_p = (top_or_bot == TOP_CODE ? accent_chr(q) : bot_accent_chr(q)); + s_is_absolute = false; + c = cur_c; + f = cur_f; + /* Compute the amount of skew, or set |s| to an alignment point */ + s = 0; + if (type(nucleus(q)) == math_char_node) { + fetch(nucleus(q)); + if (top_or_bot == TOP_CODE) { + s = char_top_accent(cur_f, cur_c); + if (s != 0) { + s_is_absolute = true; + } else { + s = get_kern(cur_f, cur_c, skew_char(cur_f)); + } + } else { /* new skewchar madness for bot accents */ + s = char_bot_accent(cur_f, cur_c); + if (s != 0) { + s_is_absolute = true; + } + } + } + x = clean_box(nucleus(q), cramped_style(cur_style)); + w = width(x); + h = height(x); + /* Switch to a larger accent if available and appropriate */ + y = null; + while (1) { + ext = NULL; + if ((char_tag(f, c) == ext_tag) && + ((ext = get_charinfo_hor_variants(char_info(f, c))) != NULL)) { + scaled w1 = xn_over_d(w, delimiter_factor, 1000); + if (w - w1 > delimiter_shortfall) + w1 = w - delimiter_shortfall; + y = get_delim_hbox(ext, f, w1, node_attr(attr_p)); + break; + } else if (char_tag(f, c) != list_tag) { + break; + } else { + integer yy = char_remainder(f, c); + if (!char_exists(f, yy)) + break; + if (char_width(f, yy) > w) + break; + c = yy; + } + } + if (y == null) { + y = char_box(f, c, node_attr(attr_p)); + } + if (top_or_bot == TOP_CODE) { + if (h < accent_base_height(f)) + delta = h; + else + delta = accent_base_height(f); + } 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| */ + flush_node_list(x); + x = new_noad(); + r = math_clone(nucleus(q)); + nucleus(x) = r; + supscr(x) = supscr(q); + supscr(q) = null; + subscr(x) = subscr(q); + subscr(q) = null; + type(nucleus(q)) = sub_mlist_node; + math_list(nucleus(q)) = x; + x = clean_box(nucleus(q), cur_style); + delta = delta + height(x) - h; + h = height(x); + } + } + if (s_is_absolute) { + scaled sa; + if (top_or_bot == TOP_CODE) + sa = char_top_accent(f, c); + else + sa = char_bot_accent(f, c); + if (sa == 0) { + sa = half(width(y)); /* just take the center */ + } + shift_amount(y) = s - sa; + } else { + shift_amount(y) = s + half(w - width(y)); + } + width(y) = 0; + if (top_or_bot == TOP_CODE) { + p = new_kern(-delta); + vlink(p) = x; + vlink(y) = p; + } else { + /* + p = new_kern(-delta); + vlink(x) = p; + vlink(p) = y; + y = x; + */ + vlink(x) = y; + y = x; + } + pack_direction = math_direction; + r = vpackage(y, 0, additional, max_dimen); + reset_attributes(r, node_attr(q)); + width(r) = w; + y = r; + if (top_or_bot == TOP_CODE) { + if (height(y) < h) { + /* Make the height of box |y| equal to |h| */ + p = new_kern(h - height(y)); + reset_attributes(p, node_attr(q)); + vlink(p) = list_ptr(y); + list_ptr(y) = p; + height(y) = h; + } + } else { + shift_amount(y) = -(h - height(y)); + } + math_list(nucleus(q)) = y; + type(nucleus(q)) = sub_box_node; +} + +void make_math_accent(pointer q) +{ + if (accent_chr(q) != null) { + fetch(accent_chr(q)); + if (char_exists(cur_f, cur_c)) { + do_make_math_accent(q, cur_f, cur_c, TOP_CODE); + } + flush_node(accent_chr(q)); + accent_chr(q) = null; + } + if (bot_accent_chr(q) != null) { + fetch(bot_accent_chr(q)); + if (char_exists(cur_f, cur_c)) { + do_make_math_accent(q, cur_f, cur_c, BOT_CODE); + } + flush_node(bot_accent_chr(q)); + bot_accent_chr(q) = null; + } +} + +/* +The |make_fraction| procedure is a bit different because it sets +|new_hlist(q)| directly rather than making a sub-box. +*/ + +void make_fraction(pointer q) +{ + pointer p, v, x, y, z; /* temporary registers for box construction */ + scaled delta, delta1, delta2, shift_up, shift_down, clr; + /* dimensions for box calculations */ + if (thickness(q) == default_code) + thickness(q) = fraction_rule(cur_style); + /* Create equal-width boxes |x| and |z| for the numerator and denominator, + and compute the default amounts |shift_up| and |shift_down| by which they + are displaced from the baseline */ + x = clean_box(numerator(q), num_style(cur_style)); + z = clean_box(denominator(q), denom_style(cur_style)); + if (width(x) < width(z)) + x = rebox(x, width(z)); + else + z = rebox(z, width(x)); + 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|. */ + clr = stack_vgap(cur_style); + delta = half(clr - ((shift_up - depth(x)) - (height(z) - shift_down))); + if (delta > 0) { + shift_up = shift_up + delta; + shift_down = shift_down + delta; + } + } else { + shift_up = fraction_num_up(cur_style); + shift_down = fraction_denom_down(cur_style); + /* In the case of a fraction line, the minimum clearance depends on the actual + thickness of the line. */ + delta = half(thickness(q)); + clr = fraction_num_vgap(cur_style); + clr = ext_xn_over_d(clr, thickness(q), fraction_rule(cur_style)); + delta1 = clr - ((shift_up - depth(x)) - (math_axis(cur_size) + delta)); + if (delta1 > 0) + shift_up = shift_up + delta1; + clr = fraction_denom_vgap(cur_style); + clr = ext_xn_over_d(clr, thickness(q), fraction_rule(cur_style)); + delta2 = + clr - ((math_axis(cur_size) - delta) - (height(z) - shift_down)); + if (delta2 > 0) + shift_down = shift_down + delta2; + } + /* 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)| */ + reset_attributes(v, node_attr(q)); + if (thickness(q) == 0) { + p = new_kern((shift_up - depth(x)) - (height(z) - shift_down)); + vlink(p) = z; + } else { + y = do_fraction_rule(thickness(q), node_attr(q)); + p = new_kern((math_axis(cur_size) - delta) - (height(z) - shift_down)); + reset_attributes(p, node_attr(q)); + vlink(y) = p; + vlink(p) = z; + p = new_kern((shift_up - depth(x)) - (math_axis(cur_size) + delta)); + vlink(p) = y; + } + reset_attributes(p, node_attr(q)); + vlink(x) = p; + list_ptr(v) = x; + /* Put the fraction into a box with its delimiters, and make |new_hlist(q)| + point to it */ + delta = fraction_del_size(cur_style); + x = var_delimiter(left_delimiter(q), cur_size, delta); + left_delimiter(q) = null; + vlink(x) = v; + z = var_delimiter(right_delimiter(q), cur_size, delta); + right_delimiter(q) = null; + vlink(v) = z; + y = hpack(x, 0, additional); + reset_attributes(y, node_attr(q)); + assign_new_hlist(q, y); +} + + +/* +If the nucleus of an |op_noad| is a single character, it is to be +centered vertically with respect to the axis, after first being enlarged +(via a character list in the font) if we are in display style. The normal +convention for placing displayed limits is to put them above and below the +operator in display style. + +The italic correction is removed from the character if there is a subscript +and the limits are not being displayed. The |make_op| +routine returns the value that should be used as an offset between +subscript and superscript. + +After |make_op| has acted, |subtype(q)| will be |limits| if and only if +the limits have been set above and below the operator. In that case, +|new_hlist(q)| will already contain the desired final box. +*/ + +scaled make_op(pointer q) +{ + scaled delta; /* offset between subscript and superscript */ + pointer p, v, x, y, z; /* temporary registers for box construction */ + integer c; /* register for character examination */ + scaled shift_up, shift_down; /* dimensions for box calculation */ + scaled ok_size; + if ((subtype(q) == normal) && (cur_style < text_style)) + subtype(q) = limits; + if (type(nucleus(q)) == math_char_node) { + fetch(nucleus(q)); + if (cur_style < text_style) { /* try to make it larger */ + if (minimum_operator_size(cur_style) != undefined_math_parameter) + ok_size = minimum_operator_size(cur_style); + else + ok_size = height_plus_depth(cur_f, cur_c) + 1; + while ((char_tag(cur_f, cur_c) == list_tag) && + height_plus_depth(cur_f, cur_c) < ok_size) { + c = char_remainder(cur_f, cur_c); + if (!char_exists(cur_f, c)) + break; + cur_c = c; + math_character(nucleus(q)) = c; + } + } + delta = char_italic(cur_f, cur_c); + x = clean_box(nucleus(q), cur_style); + if ((subscr(q) != null) && (subtype(q) != limits)) + width(x) = width(x) - delta; /* remove italic correction */ + shift_amount(x) = half(height(x) - depth(x)) - math_axis(cur_size); + /* center vertically */ + type(nucleus(q)) = sub_box_node; + math_list(nucleus(q)) = x; + } else { + delta = 0; + } + if (subtype(q) == limits) { + /* The following program builds a vlist box |v| for displayed limits. The + width of the box is not affected by the fact that the limits may be skewed. */ + x = clean_box(supscr(q), sup_style(cur_style)); + y = clean_box(nucleus(q), cur_style); + z = clean_box(subscr(q), sub_style(cur_style)); + v = new_null_box(); + reset_attributes(v, node_attr(q)); + type(v) = vlist_node; + width(v) = width(y); + if (width(x) > width(v)) + width(v) = width(x); + if (width(z) > width(v)) + width(v) = width(z); + x = rebox(x, width(v)); + y = rebox(y, width(v)); + z = rebox(z, width(v)); + shift_amount(x) = half(delta); + shift_amount(z) = -shift_amount(x); + height(v) = height(y); + depth(v) = depth(y); + /* Attach the limits to |y| and adjust |height(v)|, |depth(v)| to + account for their presence */ + /* We use |shift_up| and |shift_down| in the following program for the + amount of glue between the displayed operator |y| and its limits |x| and + |z|. The vlist inside box |v| will consist of |x| followed by |y| followed + by |z|, with kern nodes for the spaces between and around them. */ + if (supscr(q) == null) { + list_ptr(x) = null; + flush_node(x); + list_ptr(v) = y; + } else { + shift_up = limit_above_bgap(cur_style) - depth(x); + if (shift_up < limit_above_vgap(cur_style)) + shift_up = limit_above_vgap(cur_style); + p = new_kern(shift_up); + reset_attributes(p, node_attr(q)); + vlink(p) = y; + vlink(x) = p; + p = new_kern(limit_above_kern(cur_style)); + reset_attributes(p, node_attr(q)); + vlink(p) = x; + list_ptr(v) = p; + height(v) = + height(v) + limit_above_kern(cur_style) + height(x) + depth(x) + + shift_up; + } + if (subscr(q) == null) { + list_ptr(z) = null; + flush_node(z); + } else { + shift_down = limit_below_bgap(cur_style) - height(z); + if (shift_down < limit_below_vgap(cur_style)) + shift_down = limit_below_vgap(cur_style); + p = new_kern(shift_down); + reset_attributes(p, node_attr(q)); + vlink(y) = p; + vlink(p) = z; + p = new_kern(limit_below_kern(cur_style)); + reset_attributes(p, node_attr(q)); + vlink(z) = p; + depth(v) = + depth(v) + limit_below_kern(cur_style) + height(z) + depth(z) + + shift_down; + } + if (subscr(q) != null) { + math_list(subscr(q)) = null; + flush_node(subscr(q)); + subscr(q) = null; + } + if (supscr(q) != null) { + math_list(supscr(q)) = null; + flush_node(supscr(q)); + supscr(q) = null; + } + assign_new_hlist(q, v); + } + 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|. + +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. +*/ + +void make_ord(pointer q) +{ + integer 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 */ + RESTART: + if (subscr(q) == null && + supscr(q) == null && type(nucleus(q)) == math_char_node) { + p = vlink(q); + if ((p != null) && + (type(p) >= ord_noad) && (type(p) <= punct_noad) && + (type(nucleus(p)) == math_char_node) && + (math_fam(nucleus(p)) == math_fam(nucleus(q)))) { + type(nucleus(q)) = math_text_char_node; + fetch(nucleus(q)); + a = cur_c; + 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| */ + + /* 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. + But 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 == 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 */ + switch (lig_type(lig)) { + case 1: + case 5: + math_character(nucleus(q)) = lig_replacement(lig); /* \.{=:|}, \.{=:|>} */ + break; + case 2: + case 6: + math_character(nucleus(p)) = lig_replacement(lig); /* \.{|=:}, \.{|=:>} */ + break; + case 3: + case 7: + case 11: + r = new_noad(); /* \.{|=:|}, \.{|=:|>}, \.{|=:|>>} */ + reset_attributes(r, node_attr(q)); + s = new_node(math_char_node, 0); + reset_attributes(s, node_attr(q)); + nucleus(r) = s; + math_character(nucleus(r)) = lig_replacement(lig); + math_fam(nucleus(r)) = math_fam(nucleus(q)); + vlink(q) = r; + vlink(r) = p; + if (lig_type(lig) < 11) + type(nucleus(r)) = math_char_node; + else + type(nucleus(r)) = math_text_char_node; /* prevent combination */ + break; + default: + vlink(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)); + flush_node(p); + break; + } + if (lig_type(lig) > 3) + return; + type(nucleus(q)) = math_char_node; + goto RESTART; + } + } + if (disable_kern == 0 && has_kern(cur_f, a)) { + k = get_kern(cur_f, a, cur_c); + if (k != 0) { + p = new_kern(k); + reset_attributes(p, node_attr(q)); + vlink(p) = vlink(q); + vlink(q) = p; + return; + } + } + } + } + } +} + + +/* +The purpose of |make_scripts(q,delta)| 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 appear to the right of the subscript by a given distance |delta|. + +We set |shift_down| and |shift_up| to the minimum amounts to shift the +baseline of subscripts and superscripts based on the given nucleus. +*/ + +void make_scripts(pointer q, scaled delta) +{ + pointer p, x, y, z; /* temporary registers for box construction */ + scaled shift_up, shift_down, clr; /* dimensions in the calculation */ + p = new_hlist(q); + if (is_char_node(p)) { + shift_up = 0; + shift_down = 0; + } else { + z = hpack(p, 0, additional); + shift_up = height(z) - sup_shift_drop(cur_style); /* r18 */ + shift_down = depth(z) + sub_shift_drop(cur_style); /* r19 */ + list_ptr(z) = null; + flush_node(z); + } + if (supscr(q) == null) { + /* 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)); + width(x) = width(x) + space_after_script(cur_style); + if (shift_down < sub_shift_down(cur_style)) + shift_down = sub_shift_down(cur_style); + clr = height(x) - sub_top_max(cur_style); + if (shift_down < clr) + shift_down = clr; + shift_amount(x) = shift_down; + } else { + /* Construct a superscript box |x| */ + /*The bottom of a superscript should never descend below the baseline plus + one-fourth of the x-height. */ + x = clean_box(supscr(q), sup_style(cur_style)); + width(x) = width(x) + space_after_script(cur_style); + clr = sup_shift_up(cur_style); + if (shift_up < clr) + shift_up = clr; + clr = depth(x) + sup_bottom_min(cur_style); + if (shift_up < clr) + shift_up = clr; + + if (subscr(q) == null) { + shift_amount(x) = -shift_up; + } else { + /* Construct a sub/superscript combination box |x|, with the + superscript offset by |delta| */ + /* When both subscript and superscript are present, the subscript must be + separated from the superscript by at least four times |default_rule_thickness|. + If this condition would be violated, the subscript moves down, after which + both subscript and superscript move up so that the bottom of the superscript + is at least as high as the baseline plus four-fifths of the x-height. */ + + y = clean_box(subscr(q), sub_style(cur_style)); + width(y) = width(y) + space_after_script(cur_style); + if (shift_down < sub_sup_shift_down(cur_style)) + shift_down = sub_sup_shift_down(cur_style); + clr = subsup_vgap(cur_style) - + ((shift_up - depth(x)) - (height(y) - shift_down)); + if (clr > 0) { + shift_down = shift_down + clr; + clr = sup_sub_bottom_max(cur_style) - (shift_up - depth(x)); + if (clr > 0) { + shift_up = shift_up + clr; + shift_down = shift_down - clr; + } + } + shift_amount(x) = delta; /* superscript is |delta| to the right of the subscript */ + p = new_kern((shift_up - depth(x)) - (height(y) - shift_down)); + reset_attributes(p, node_attr(q)); + vlink(x) = p; + vlink(p) = y; + pack_direction = math_direction; + x = vpackage(x, 0, additional, max_dimen); + reset_attributes(x, node_attr(q)); + shift_amount(x) = shift_down; + } + } + if (new_hlist(q) == null) { + new_hlist(q) = x; + } else { + p = new_hlist(q); + while (vlink(p) != null) + p = vlink(p); + vlink(p) = x; + } + if (subscr(q) != null) { + math_list(subscr(q)) = null; + flush_node(subscr(q)); + subscr(q) = null; + } + if (supscr(q) != null) { + math_list(supscr(q)) = null; + flush_node(supscr(q)); + supscr(q) = null; + } + +} + +/* +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. +*/ + +small_number make_left_right(pointer q, integer style, scaled max_d, + scaled max_hv) +{ + scaled delta, delta1, delta2; /* dimensions used in the calculation */ + pointer tmp; + cur_style = style; + setup_cur_size_and_mu(); + delta2 = max_d + math_axis(cur_size); + delta1 = max_hv + max_d - delta2; + if (delta2 > delta1) + delta1 = delta2; /* |delta1| is max distance from axis */ + delta = (delta1 / 500) * delimiter_factor; + delta2 = delta1 + delta1 - delimiter_shortfall; + if (delta < delta2) + delta = delta2; + tmp = var_delimiter(delimiter(q), cur_size, delta); + delimiter(q) = null; + assign_new_hlist(q, tmp); + if (subtype(q) == left_noad_side) + return open_noad; + else + return close_noad; +} + + +/* +The inter-element spacing in math formulas depends on a $8\times8$ table that +\TeX\ preloads as a 64-digit string. The elements of this string have the +following significance: +$$\vbox{\halign{#\hfil\cr +\.0 means no space;\cr +\.1 means a conditional thin space (\.{\\nonscript\\mskip\\thinmuskip});\cr +\.2 means a thin space (\.{\\mskip\\thinmuskip});\cr +\.3 means a conditional medium space + (\.{\\nonscript\\mskip\\medmuskip});\cr +\.4 means a conditional thick space + (\.{\\nonscript\\mskip\\thickmuskip});\cr +\.* means an impossible case.\cr}}$$ +This is all pretty cryptic, but {\sl The \TeX book\/} explains what is +supposed to happen, and the string makes it happen. +@:TeXbook}{\sl The \TeX book@> +*/ + +char math_spacing[] = + "0234000122*4000133**3**344*0400400*000000234000111*1111112341011"; + +#define level_one 1 +#define thin_mu_skip param_thin_mu_skip_code +#define med_mu_skip param_med_mu_skip_code +#define thick_mu_skip param_thick_mu_skip_code + + +#define TEXT_STYLES(A,B) do { \ + def_math_param(A,display_style,(B),level_one); \ + def_math_param(A,cramped_display_style,(B),level_one); \ + def_math_param(A,text_style,(B),level_one); \ + def_math_param(A,cramped_text_style,(B),level_one); \ + } while (0) + +#define SCRIPT_STYLES(A,B) do { \ + def_math_param(A,script_style,(B),level_one); \ + def_math_param(A,cramped_script_style,(B),level_one); \ + def_math_param(A,script_script_style,(B),level_one); \ + def_math_param(A,cramped_script_script_style,(B),level_one); \ + } while (0) + +#define ALL_STYLES(A,B) do { \ + TEXT_STYLES(A,(B)); \ + SCRIPT_STYLES(A,(B)); \ + } while (0) + +#define SPLIT_STYLES(A,B,C) do { \ + TEXT_STYLES(A,(B)); \ + SCRIPT_STYLES(A,(C)); \ + } while (0) + + +void initialize_math_spacing(void) +{ + /* *INDENT-OFF* */ + ALL_STYLES (math_param_ord_ord_spacing, 0); + ALL_STYLES (math_param_ord_op_spacing, thin_mu_skip); + SPLIT_STYLES (math_param_ord_bin_spacing, med_mu_skip, 0); + SPLIT_STYLES (math_param_ord_rel_spacing, thick_mu_skip, 0); + ALL_STYLES (math_param_ord_open_spacing, 0); + ALL_STYLES (math_param_ord_close_spacing, 0); + ALL_STYLES (math_param_ord_punct_spacing, 0); + SPLIT_STYLES (math_param_ord_inner_spacing, thin_mu_skip, 0); + + ALL_STYLES (math_param_op_ord_spacing, thin_mu_skip); + ALL_STYLES (math_param_op_op_spacing, thin_mu_skip); + ALL_STYLES (math_param_op_bin_spacing, 0); + SPLIT_STYLES (math_param_op_rel_spacing, thick_mu_skip, 0); + ALL_STYLES (math_param_op_open_spacing, 0); + ALL_STYLES (math_param_op_close_spacing, 0); + ALL_STYLES (math_param_op_punct_spacing, 0); + SPLIT_STYLES (math_param_op_inner_spacing, thin_mu_skip, 0); + + SPLIT_STYLES (math_param_bin_ord_spacing, med_mu_skip, 0); + SPLIT_STYLES (math_param_bin_op_spacing, med_mu_skip, 0); + ALL_STYLES (math_param_bin_bin_spacing, 0); + ALL_STYLES (math_param_bin_rel_spacing, 0); + SPLIT_STYLES (math_param_bin_open_spacing, med_mu_skip, 0); + ALL_STYLES (math_param_bin_close_spacing, 0); + ALL_STYLES (math_param_bin_punct_spacing, 0); + SPLIT_STYLES (math_param_bin_inner_spacing, med_mu_skip, 0); + + SPLIT_STYLES (math_param_rel_ord_spacing, thick_mu_skip, 0); + SPLIT_STYLES (math_param_rel_op_spacing, thick_mu_skip, 0); + ALL_STYLES (math_param_rel_bin_spacing, 0); + ALL_STYLES (math_param_rel_rel_spacing, 0); + SPLIT_STYLES (math_param_rel_open_spacing, thick_mu_skip, 0); + ALL_STYLES (math_param_rel_close_spacing, 0); + ALL_STYLES (math_param_rel_punct_spacing, 0); + SPLIT_STYLES (math_param_rel_inner_spacing, thick_mu_skip, 0); + + ALL_STYLES (math_param_open_ord_spacing, 0); + ALL_STYLES (math_param_open_op_spacing, 0); + ALL_STYLES (math_param_open_bin_spacing, 0); + ALL_STYLES (math_param_open_rel_spacing, 0); + ALL_STYLES (math_param_open_open_spacing, 0); + ALL_STYLES (math_param_open_close_spacing, 0); + ALL_STYLES (math_param_open_punct_spacing, 0); + ALL_STYLES (math_param_open_inner_spacing, 0); + + ALL_STYLES (math_param_close_ord_spacing, 0); + ALL_STYLES (math_param_close_op_spacing, thin_mu_skip); + SPLIT_STYLES (math_param_close_bin_spacing, med_mu_skip, 0); + SPLIT_STYLES (math_param_close_rel_spacing, thick_mu_skip, 0); + ALL_STYLES (math_param_close_open_spacing, 0); + ALL_STYLES (math_param_close_close_spacing, 0); + ALL_STYLES (math_param_close_punct_spacing, 0); + SPLIT_STYLES (math_param_close_inner_spacing, thin_mu_skip, 0); + + SPLIT_STYLES (math_param_punct_ord_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_punct_op_spacing, thin_mu_skip, 0); + ALL_STYLES (math_param_punct_bin_spacing, 0); + SPLIT_STYLES (math_param_punct_rel_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_punct_open_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_punct_close_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_punct_punct_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_punct_inner_spacing, thin_mu_skip, 0); + + SPLIT_STYLES (math_param_inner_ord_spacing, thin_mu_skip, 0); + ALL_STYLES (math_param_inner_op_spacing, thin_mu_skip); + SPLIT_STYLES (math_param_inner_bin_spacing, med_mu_skip, 0); + SPLIT_STYLES (math_param_inner_rel_spacing, thick_mu_skip, 0); + SPLIT_STYLES (math_param_inner_open_spacing, thin_mu_skip, 0); + ALL_STYLES (math_param_inner_close_spacing, 0); + SPLIT_STYLES (math_param_inner_punct_spacing, thin_mu_skip, 0); + SPLIT_STYLES (math_param_inner_inner_spacing, thin_mu_skip, 0); + /* *INDENT-ON* */ +} + + +pointer math_spacing_glue_old(int l_type, int r_type, int m_style) +{ + pointer y, z; + int x; + z = null; + switch (math_spacing[l_type * 8 + r_type - 9 * ord_noad]) { + case '0': + x = 0; + break; + case '1': + x = (m_style < script_style ? param_thin_mu_skip_code : 0); + break; + case '2': + x = param_thin_mu_skip_code; + break; + case '3': + x = (m_style < script_style ? param_med_mu_skip_code : 0); + break; + case '4': + x = (m_style < script_style ? param_thick_mu_skip_code : 0); + break; + default: + tconfusion("mlist4"); /* this can't happen mlist4 */ + x = -1; + break; + } + if (x != 0) { + y = math_glue(glue_par(x), cur_mu); + z = new_glue(y); + glue_ref_count(y) = null; + subtype(z) = x + 1; /* store a symbolic subtype */ + } + return z; +} + +#define both_types(A,B) ((A)*8+B) + +pointer math_spacing_glue(int l_type, int r_type, int m_style) +{ + int x = -1; + pointer z = null; + switch (both_types(l_type, r_type)) { + /* *INDENT-OFF* */ + case both_types(ord_noad, ord_noad ): x = get_math_param(math_param_ord_ord_spacing,m_style); break; + case both_types(ord_noad, op_noad ): x = get_math_param(math_param_ord_op_spacing,m_style); break; + case both_types(ord_noad, bin_noad ): x = get_math_param(math_param_ord_bin_spacing,m_style); break; + case both_types(ord_noad, rel_noad ): x = get_math_param(math_param_ord_rel_spacing,m_style); break; + case both_types(ord_noad, open_noad ): x = get_math_param(math_param_ord_open_spacing,m_style); break; + case both_types(ord_noad, close_noad): x = get_math_param(math_param_ord_close_spacing,m_style); break; + case both_types(ord_noad, punct_noad): x = get_math_param(math_param_ord_punct_spacing,m_style); break; + case both_types(ord_noad, inner_noad): x = get_math_param(math_param_ord_inner_spacing,m_style); break; + case both_types(op_noad, ord_noad ): x = get_math_param(math_param_op_ord_spacing,m_style); break; + case both_types(op_noad, op_noad ): x = get_math_param(math_param_op_op_spacing,m_style); break; + /*case both_types(op_noad, bin_noad ): x = get_math_param(math_param_op_bin_spacing,m_style); break;*/ + case both_types(op_noad, rel_noad ): x = get_math_param(math_param_op_rel_spacing,m_style); break; + case both_types(op_noad, open_noad ): x = get_math_param(math_param_op_open_spacing,m_style); break; + case both_types(op_noad, close_noad): x = get_math_param(math_param_op_close_spacing,m_style); break; + case both_types(op_noad, punct_noad): x = get_math_param(math_param_op_punct_spacing,m_style); break; + case both_types(op_noad, inner_noad): x = get_math_param(math_param_op_inner_spacing,m_style); break; + case both_types(bin_noad, ord_noad ): x = get_math_param(math_param_bin_ord_spacing,m_style); break; + case both_types(bin_noad, op_noad ): x = get_math_param(math_param_bin_op_spacing,m_style); break; + /*case both_types(bin_noad, bin_noad ): x = get_math_param(math_param_bin_bin_spacing,m_style); break;*/ + /*case both_types(bin_noad, rel_noad ): x = get_math_param(math_param_bin_rel_spacing,m_style); break;*/ + case both_types(bin_noad, open_noad ): x = get_math_param(math_param_bin_open_spacing,m_style); break; + /*case both_types(bin_noad, close_noad): x = get_math_param(math_param_bin_close_spacing,m_style); break;*/ + /*case both_types(bin_noad, punct_noad): x = get_math_param(math_param_bin_punct_spacing,m_style); break;*/ + case both_types(bin_noad, inner_noad): x = get_math_param(math_param_bin_inner_spacing,m_style); break; + case both_types(rel_noad, ord_noad ): x = get_math_param(math_param_rel_ord_spacing,m_style); break; + case both_types(rel_noad, op_noad ): x = get_math_param(math_param_rel_op_spacing,m_style); break; + /*case both_types(rel_noad, bin_noad ): x = get_math_param(math_param_rel_bin_spacing,m_style); break;*/ + case both_types(rel_noad, rel_noad ): x = get_math_param(math_param_rel_rel_spacing,m_style); break; + case both_types(rel_noad, open_noad ): x = get_math_param(math_param_rel_open_spacing,m_style); break; + case both_types(rel_noad, close_noad): x = get_math_param(math_param_rel_close_spacing,m_style); break; + case both_types(rel_noad, punct_noad): x = get_math_param(math_param_rel_punct_spacing,m_style); break; + case both_types(rel_noad, inner_noad): x = get_math_param(math_param_rel_inner_spacing,m_style); break; + case both_types(open_noad, ord_noad ): x = get_math_param(math_param_open_ord_spacing,m_style); break; + case both_types(open_noad, op_noad ): x = get_math_param(math_param_open_op_spacing,m_style); break; + /*case both_types(open_noad, bin_noad ): x = get_math_param(math_param_open_bin_spacing,m_style); break;*/ + case both_types(open_noad, rel_noad ): x = get_math_param(math_param_open_rel_spacing,m_style); break; + case both_types(open_noad, open_noad ): x = get_math_param(math_param_open_open_spacing,m_style); break; + case both_types(open_noad, close_noad): x = get_math_param(math_param_open_close_spacing,m_style); break; + case both_types(open_noad, punct_noad): x = get_math_param(math_param_open_punct_spacing,m_style); break; + case both_types(open_noad, inner_noad): x = get_math_param(math_param_open_inner_spacing,m_style); break; + case both_types(close_noad,ord_noad ): x = get_math_param(math_param_close_ord_spacing,m_style); break; + case both_types(close_noad,op_noad ): x = get_math_param(math_param_close_op_spacing,m_style); break; + case both_types(close_noad,bin_noad ): x = get_math_param(math_param_close_bin_spacing,m_style); break; + case both_types(close_noad,rel_noad ): x = get_math_param(math_param_close_rel_spacing,m_style); break; + case both_types(close_noad,open_noad ): x = get_math_param(math_param_close_open_spacing,m_style); break; + case both_types(close_noad,close_noad): x = get_math_param(math_param_close_close_spacing,m_style); break; + case both_types(close_noad,punct_noad): x = get_math_param(math_param_close_punct_spacing,m_style); break; + case both_types(close_noad,inner_noad): x = get_math_param(math_param_close_inner_spacing,m_style); break; + case both_types(punct_noad,ord_noad ): x = get_math_param(math_param_punct_ord_spacing,m_style); break; + case both_types(punct_noad,op_noad ): x = get_math_param(math_param_punct_op_spacing,m_style); break; + /*case both_types(punct_noad,bin_noad ): x = get_math_param(math_param_punct_bin_spacing,m_style); break;*/ + case both_types(punct_noad,rel_noad ): x = get_math_param(math_param_punct_rel_spacing,m_style); break; + case both_types(punct_noad,open_noad ): x = get_math_param(math_param_punct_open_spacing,m_style); break; + case both_types(punct_noad,close_noad): x = get_math_param(math_param_punct_close_spacing,m_style); break; + case both_types(punct_noad,punct_noad): x = get_math_param(math_param_punct_punct_spacing,m_style); break; + case both_types(punct_noad,inner_noad): x = get_math_param(math_param_punct_inner_spacing,m_style); break; + case both_types(inner_noad,ord_noad ): x = get_math_param(math_param_inner_ord_spacing,m_style); break; + case both_types(inner_noad,op_noad ): x = get_math_param(math_param_inner_op_spacing,m_style); break; + case both_types(inner_noad,bin_noad ): x = get_math_param(math_param_inner_bin_spacing,m_style); break; + case both_types(inner_noad,rel_noad ): x = get_math_param(math_param_inner_rel_spacing,m_style); break; + case both_types(inner_noad,open_noad ): x = get_math_param(math_param_inner_open_spacing,m_style); break; + case both_types(inner_noad,close_noad): x = get_math_param(math_param_inner_close_spacing,m_style); break; + case both_types(inner_noad,punct_noad): x = get_math_param(math_param_inner_punct_spacing,m_style); break; + case both_types(inner_noad,inner_noad): x = get_math_param(math_param_inner_inner_spacing,m_style); break; + /* *INDENT-ON* */ + } + if (x < 0) { + tconfusion("mathspacing"); + } + if (x != 0) { + pointer y; + if (x <= thick_mu_skip) { /* trap thin/med/thick settings cf. old TeX */ + y = math_glue(glue_par(x), cur_mu); + z = new_glue(y); + glue_ref_count(y) = null; + subtype(z) = x + 1; /* store a symbolic subtype */ + } else { + y = math_glue(x, cur_mu); + z = new_glue(y); + glue_ref_count(y) = null; + } + } + return z; +} + + + +/* Here is the overall plan of |mlist_to_hlist|, and the list of its + local variables. +*/ + +void mlist_to_hlist(void) +{ + pointer mlist; /* beginning of the given list */ + boolean penalties; /* should penalty nodes be inserted? */ + integer style; /* the given style */ + integer save_style; /* holds |cur_style| during recursion */ + pointer q; /* runs through the mlist */ + pointer r; /* the most recent noad preceding |q| */ + integer r_type; /* the |type| of noad |r|, or |op_noad| if |r=null| */ + integer r_subtype; /* the |subtype| of noad |r| if |r_type| is |fence_noad| */ + integer t; /* the effective |type| of noad |q| during the second pass */ + pointer p, x, y, z; /* temporary registers for list construction */ + integer pen; /* a penalty to be inserted */ + integer s; /* the size of a noad to be deleted */ + scaled max_hl, max_d; /* maximum height and depth of the list translated so far */ + scaled delta; /* offset between subscript and superscript */ + mlist = cur_mlist; + penalties = mlist_penalties; + style = cur_style; /* tuck global parameters away as local variables */ + q = mlist; + r = null; + r_type = op_noad; + r_subtype = 0; + max_hl = 0; + max_d = 0; + x = null; + p = null; + setup_cur_size_and_mu(); + while (q != null) { + /* We use the fact that no character nodes appear in an mlist, hence + the field |type(q)| is always present. */ + + /* One of the things we must do on the first pass is change a |bin_noad| to + an |ord_noad| if the |bin_noad| is not in the context of a binary operator. + The values of |r| and |r_type| make this fairly easy. */ + RESWITCH: + delta = 0; + switch (type(q)) { + case bin_noad: + switch (r_type) { + case bin_noad: + case op_noad: + case rel_noad: + case open_noad: + case punct_noad: + type(q) = ord_noad; + goto RESWITCH; + break; + case fence_noad: + if (r_subtype == left_noad_side) { + type(q) = ord_noad; + goto RESWITCH; + } + } + break; + case rel_noad: + case close_noad: + case punct_noad: + if (r_type == bin_noad) + type(r) = ord_noad; + break; + case fence_noad: + if (subtype(q) != left_noad_side) + if (r_type == bin_noad) + type(r) = ord_noad; + goto DONE_WITH_NOAD; + break; + case fraction_noad: + make_fraction(q); + goto CHECK_DIMENSIONS; + break; + case op_noad: + delta = make_op(q); + if (subtype(q) == limits) + goto CHECK_DIMENSIONS; + break; + case ord_noad: + make_ord(q); + break; + case open_noad: + case inner_noad: + break; + case radical_noad: + if (subtype(q) == 4) + make_under_delimiter(q); + else if (subtype(q) == 5) + make_over_delimiter(q); + else if (subtype(q) == 6) + make_delimiter_under(q); + else if (subtype(q) == 7) + make_delimiter_over(q); + else + make_radical(q); + break; + case over_noad: + make_over(q); + break; + case under_noad: + make_under(q); + break; + case accent_noad: + make_math_accent(q); + break; + case vcenter_noad: + make_vcenter(q); + break; + case style_node: + cur_style = subtype(q); + setup_cur_size_and_mu(); + goto DONE_WITH_NODE; + break; + case choice_node: + switch (cur_style / 2) { + case 0: + choose_mlist(display_mlist); + break; /* |display_style=0| */ + case 1: + choose_mlist(text_mlist); + break; /* |text_style=2| */ + case 2: + choose_mlist(script_mlist); + break; /* |script_style=4| */ + case 3: + choose_mlist(script_script_mlist); + break; /* |script_script_style=6| */ + } /* there are no other cases */ + flush_node_list(display_mlist(q)); + flush_node_list(text_mlist(q)); + flush_node_list(script_mlist(q)); + flush_node_list(script_script_mlist(q)); + type(q) = style_node; + subtype(q) = cur_style; + if (p != null) { + z = vlink(q); + vlink(q) = p; + while (vlink(p) != null) + p = vlink(p); + vlink(p) = z; + } + goto DONE_WITH_NODE; + break; + case ins_node: + case mark_node: + case adjust_node: + case whatsit_node: + case penalty_node: + case disc_node: + goto DONE_WITH_NODE; + break; + case rule_node: + if (height(q) > max_hl) + max_hl = height(q); + if (depth(q) > max_d) + max_d = depth(q); + goto DONE_WITH_NODE; + break; + case glue_node: + /* + 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|. + @:non_script_}{\.{\\nonscript} primitive@> + */ + if (subtype(q) == mu_glue) { + x = glue_ptr(q); + y = math_glue(x, cur_mu); + delete_glue_ref(x); + glue_ptr(q) = y; + subtype(q) = normal; + } else if ((cur_size != text_size) + && (subtype(q) == cond_math_glue)) { + p = vlink(q); + if (p != null) + if ((type(p) == glue_node) || (type(p) == kern_node)) { + vlink(q) = vlink(p); + vlink(p) = null; + flush_node_list(p); + } + } + goto DONE_WITH_NODE; + break; + case kern_node: + math_kern(q, cur_mu); + goto DONE_WITH_NODE; + break; + default: + tconfusion("mlist1"); /* this can't happen mlist1 */ + } + /* When we get to the following part of the program, we have ``fallen through'' + from cases that did not lead to |check_dimensions| or |done_with_noad| or + |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. + @^subscripts@> + @^superscripts@> + */ + switch (type(nucleus(q))) { + case math_char_node: + case math_text_char_node: + fetch(nucleus(q)); + if (char_exists(cur_f, cur_c)) { + delta = char_italic(cur_f, cur_c); + p = new_glyph(cur_f, cur_c); + reset_attributes(p, node_attr(nucleus(q))); + if ((type(nucleus(q)) == math_text_char_node) + && (space(cur_f) != 0)) + delta = 0; /* no italic correction in mid-word of text font */ + if ((subscr(q) == null) && (delta != 0)) { + x = new_kern(delta); + reset_attributes(x, node_attr(nucleus(q))); + vlink(p) = x; + delta = 0; + } + } else { + p = null; + } + break; + case sub_box_node: + p = math_list(nucleus(q)); + break; + case sub_mlist_node: + cur_mlist = math_list(nucleus(q)); + save_style = cur_style; + mlist_penalties = false; + mlist_to_hlist(); /* recursive call */ + cur_style = save_style; + setup_cur_size_and_mu(); + p = hpack(vlink(temp_head), 0, additional); + reset_attributes(p, node_attr(nucleus(q))); + break; + default: + tconfusion("mlist2"); /* this can't happen mlist2 */ + } + assign_new_hlist(q, p); + if ((subscr(q) == null) && (supscr(q) == null)) + goto CHECK_DIMENSIONS; + make_scripts(q, delta); + CHECK_DIMENSIONS: + z = hpack(new_hlist(q), 0, additional); + if (height(z) > max_hl) + max_hl = height(z); + if (depth(z) > max_d) + max_d = depth(z); + list_ptr(z) = null; + flush_node(z); /* only drop the \hbox */ + DONE_WITH_NOAD: + r = q; + r_type = type(r); + if (r_type == fence_noad) { + r_subtype = left_noad_side; + cur_style = style; + setup_cur_size_and_mu(); + } + DONE_WITH_NODE: + q = vlink(q); + } + if (r_type == bin_noad) + type(r) = ord_noad; + /* Make a second pass over the mlist, removing all noads and inserting the + proper spacing and penalties */ + + /* We have now tied up all the loose ends of the first pass of |mlist_to_hlist|. + The second pass simply goes through and hooks everything together with the + proper glue and penalties. It also handles the |fence_noad|s that + might be present, since |max_hl| and |max_d| are now known. Variable |p| points + to a node at the current end of the final hlist. + */ + p = temp_head; + vlink(p) = null; + q = mlist; + r_type = 0; + cur_style = style; + setup_cur_size_and_mu(); + NEXT_NODE: + while (q != null) { + /* If node |q| is a style node, change the style and |goto delete_q|; + otherwise if it is not a noad, put it into the hlist, + advance |q|, and |goto done|; otherwise set |s| to the size + of noad |q|, set |t| to the associated type (|ord_noad.. + inner_noad|), and set |pen| to the associated penalty */ + /* Just before doing the big |case| switch in the second pass, the program + sets up default values so that most of the branches are short. */ + t = ord_noad; + s = noad_size; + pen = inf_penalty; + switch (type(q)) { + case op_noad: + case open_noad: + case close_noad: + case punct_noad: + case inner_noad: + t = type(q); + break; + case bin_noad: + t = bin_noad; + pen = bin_op_penalty; + break; + case rel_noad: + t = rel_noad; + pen = rel_penalty; + break; + case ord_noad: + case vcenter_noad: + case over_noad: + case under_noad: + break; + case radical_noad: + s = radical_noad_size; + break; + case accent_noad: + s = accent_noad_size; + break; + case fraction_noad: + t = inner_noad; + s = fraction_noad_size; + break; + case fence_noad: + t = make_left_right(q, style, max_d, max_hl); + break; + case style_node: + /* Change the current style and |goto delete_q| */ + cur_style = subtype(q); + s = style_node_size; + setup_cur_size_and_mu(); + goto DELETE_Q; + break; + case whatsit_node: + case penalty_node: + case rule_node: + case disc_node: + case adjust_node: + case ins_node: + case mark_node: + case glue_node: + case kern_node: + vlink(p) = q; + p = q; + q = vlink(q); + vlink(p) = null; + goto NEXT_NODE; + break; + default: + tconfusion("mlist3"); /* this can't happen mlist3 */ + } + /* Append inter-element spacing based on |r_type| and |t| */ + if (r_type > 0) { /* not the first noad */ + z = math_spacing_glue(r_type, t, cur_style); + if (z != null) { + reset_attributes(z, node_attr(p)); + vlink(p) = z; + p = z; + } + } + + /* Append any |new_hlist| entries for |q|, and any appropriate penalties */ + /* We insert a penalty node after the hlist entries of noad |q| if |pen| + is not an ``infinite'' penalty, and if the node immediately following |q| + is not a penalty node or a |rel_noad| or absent entirely. */ + + if (new_hlist(q) != null) { + vlink(p) = new_hlist(q); + do { + p = vlink(p); + } while (vlink(p) != null); + } + if (penalties && vlink(q) != null && pen < inf_penalty) { + r_type = type(vlink(q)); + if (r_type != penalty_node && r_type != rel_noad) { + z = new_penalty(pen); + reset_attributes(z, node_attr(q)); + vlink(p) = z; + p = z; + } + } + if (type(q) == fence_noad && subtype(q) == right_noad_side) + t = open_noad; + r_type = t; + 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). + 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)); + free_node(r, get_node_size(type(r), subtype(r))); + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c index 5a7ea253be8..2711f53d25e 100644 --- a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c +++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.c @@ -1,9 +1,29 @@ -/* $Id: postlinebreak.c 1155 2008-04-14 07:53:21Z oneiros $ */ +/* postlinebreak.c + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ #include "luatex-api.h" #include <ptexlib.h> #include "nodes.h" +static const char _svn_version[] = + "$Id: postlinebreak.c 1694 2008-12-28 20:53:16Z oneiros $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/postlinebreak.c $"; + /* 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. @@ -55,7 +75,7 @@ and begin direction instructions at the beginnings of lines. #define next_break prev_break /*new name for |prev_break| after links are reversed */ -#define append_list(a,b) \ +#define append_list(a,b) \ { vlink(cur_list.tail_field)=vlink((a)); cur_list.tail_field = b; } #define left_skip_code 7 /*glue at left of justified lines */ @@ -87,7 +107,8 @@ void ext_post_line_break(boolean d, scaled second_width, scaled second_indent, scaled first_width, - scaled first_indent, halfword best_line) + scaled first_indent, halfword best_line, + halfword pdf_ignored_dimen) { boolean have_directional = true; @@ -151,7 +172,7 @@ void ext_post_line_break(boolean d, halfword tmp = new_dir(dir_dir(q)); halfword nxt = vlink(temp_head); couple_nodes(temp_head, tmp); - couple_nodes(tmp, nxt); + try_couple_nodes(tmp, nxt); /* \break\par */ } if (dir_ptr != null) { flush_node_list(dir_ptr); @@ -192,9 +213,9 @@ void ext_post_line_break(boolean d, assert(vlink(r) == q); /* |r| refers to the node after which the dir nodes should be closed */ } else if (type(r) == disc_node) { - halfword a = alink(r), v; + halfword a = alink(r); + halfword v = vlink(r); assert(a != null); - v = vlink(r); if (v == null) { /* nested disc, let's unfold */ fprintf(stderr, "Nested disc [%d]<-[%d]->null\n", (int) a, (int) r); @@ -203,7 +224,7 @@ void ext_post_line_break(boolean d, while (alink(a) != null) a = alink(a); assert(type(a) == nesting_node); - assert(subtype(a) = no_break_head(0)); /* No_break */ + assert(subtype(a) == no_break_head(0)); /* No_break */ d = a - subtype(a); /* MAGIC subtype is offset of nesting with disc */ assert(type(d) == disc_node); v = vlink(d); @@ -397,13 +418,15 @@ void ext_post_line_break(boolean d, /* Append the new box to the current vertical list, followed by the list of special nodes taken out of the box by the packager; */ - if (pdf_each_line_height != 0) + if (pdf_each_line_height != pdf_ignored_dimen) height(just_box) = pdf_each_line_height; - if (pdf_each_line_depth != 0) + if (pdf_each_line_depth != pdf_ignored_dimen) depth(just_box) = pdf_each_line_depth; - if ((pdf_first_line_height != 0) && (cur_line == cur_list.pg_field + 1)) + if ((pdf_first_line_height != pdf_ignored_dimen) + && (cur_line == cur_list.pg_field + 1)) height(just_box) = pdf_first_line_height; - if ((pdf_last_line_depth != 0) && (cur_line + 1 == best_line)) + if ((pdf_last_line_depth != pdf_ignored_dimen) + && (cur_line + 1 == best_line)) depth(just_box) = pdf_last_line_depth; if (pre_adjust_head != pre_adjust_tail) @@ -496,9 +519,8 @@ void ext_post_line_break(boolean d, q = vlink(r); if (q == cur_break(cur_p) || is_char_node(q)) break; - if (! - ((type(q) == whatsit_node) - && (subtype(q) == local_par_node))) { + if (!((type(q) == whatsit_node) + && (subtype(q) == local_par_node))) { if (non_discardable(q) || (type(q) == kern_node && subtype(q) != explicit)) break; 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..9f2f4e84d36 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/primitive.c @@ -0,0 +1,668 @@ +/* primitive.c + + Copyright 2008-2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#include "luatex-api.h" +#include <ptexlib.h> + +#include "commands.h" +#include "primitive.h" +#include "tokens.h" + +static const char _svn_version[] = + "$Id: primitive.c 2086 2009-03-22 15:32:08Z oneiros $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/primitive.c $"; + +/* as usual, the file starts with a bunch of #defines that mimic pascal @ds */ + +#define level_one 1 +#define flush_string() do { decr(str_ptr); pool_ptr=str_start_macro(str_ptr); } while (0) +#define cur_length (pool_ptr - str_start_macro(str_ptr)) +#define append_char(a) str_pool[pool_ptr++]=(a) + +#define next(a) hash[(a)].lhfield /* link for coalesced lists */ +#define text(a) hash[(a)].rh /* string number for control sequence name */ +#define hash_is_full (hash_used==hash_base) /* test if all positions are occupied */ +#define hash_size 65536 + +#define span_code 1114113 +#define unless_code 32 /* amount added for `\.{\\unless}' prefix */ +#define protected_token 0x1C00001 /* $2^{21}\cdot|end_match|+1$ */ +#define offset_ocp_name 1 +#define ocp_name(A) ocp_tables[(A)][offset_ocp_name] + +#define skip_base get_skip_base() +#define mu_skip_base get_mu_skip_base() +#define glue_base static_glue_base +#define toks_base get_toks_base() +#define count_base get_count_base() +#define int_base static_int_base +#define attribute_base get_attribute_base() +#define scaled_base get_scaled_base() +#define dimen_base get_dimen_base() + + + +/* \primitive support needs a few extra variables and definitions */ + +#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|. + */ + +#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. */ + +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 */ + +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\pct! 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(char *j, pool_pointer l, halfword prime_number) +{ + pool_pointer k; + halfword h = (unsigned char) *j; + for (k = 1; k <= 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 */ + +pointer prim_lookup(str_number s) +{ + integer h; /* hash code */ + pointer p; /* index in |hash| array */ + pool_pointer j, l; + if (s < string_offset) { + p = s; + if ((p < 0) || (get_prim_eq_type(p) == undefined_cs_cmd)) { + p = undefined_primitive; + } + } else { + j = str_start_macro(s); + if (s == str_ptr) + l = cur_length; + else + l = length(s); + h = compute_hash((char *) (str_pool + j), l, prim_prime); + p = h + prim_base; /* we start searching here; note that |0<=h<hash_prime| */ + while (1) { + if (prim_text(p) > 0) + if (length(prim_text(p)) == l) + if (str_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_string("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 */ + +boolean is_primitive(str_number csname) +{ + integer n, m; + m = prim_lookup(csname); + n = string_lookup(makecstring(csname), length(csname)); + return ((n != undefined_cs_cmd) && + (m != undefined_primitive) && + (eq_type(n) == prim_eq_type(m)) && (equiv(n) == prim_equiv(m))); +} + + +/* a few simple accessors */ + +quarterword get_prim_eq_type(integer p) +{ + return prim_eq_type(p); +} + +quarterword get_prim_origin(integer p) +{ + return prim_origin(p); +} + +halfword get_prim_equiv(integer p) +{ + return prim_equiv(p); +} + +str_number get_prim_text(integer p) +{ + return prim_text(p); +} + + +/* 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 *) xcalloc((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. +*/ + +void primitive_def(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. +*/ + +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((idx + 1), sizeof(str_number *)); + if (prim_data[c].names != NULL) { + assert(prim_data[c].subids); + memcpy(new, (prim_data[c].names), + (prim_data[c].subids * sizeof(str_number))); + 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. +*/ + +void +primitive(str_number ss, quarterword c, halfword o, halfword off, + int cmd_origin) +{ + str_number s; /* actual |str_number| used */ + integer prim_val; /* needed to fill |prim_eqtb| */ + assert(o >= off); + if (ss < string_offset) { + if (ss > 127) + tconfusion("prim"); /* should be ASCII */ + append_char(ss); + s = make_string(); + } else { + s = ss; + } + char *thes = makecstring(s); + if (true || cmd_origin == tex_command || cmd_origin == core_command) { + primitive_def(thes, strlen(thes), c, o); + } + prim_val = prim_lookup(s); + prim_origin(prim_val) = cmd_origin; + prim_eq_type(prim_val) = c; + prim_equiv(prim_val) = o; + store_primitive_name(s, c, o, off); +} + + +/* + * Here is a helper that does the actual hash insertion. + */ + +static halfword insert_id(halfword p, unsigned char *j, pool_pointer l) +{ + integer d; + unsigned char *k; + /* This code far from ideal: the existance of |hash_extra| changes + all the potential (short) coalesced lists into a single (long) + one. This will create a slowdown. */ + if (text(p) > 0) { + if (hash_high < hash_extra) { + incr(hash_high); + /* can't use eqtb_top here (perhaps because that is not finalized + yet when called from |primitive|?) */ + next(p) = hash_high + get_eqtb_size(); + p = next(p); + } else { + do { + if (hash_is_full) + overflow_string("hash size", hash_size + hash_extra); + decr(hash_used); + } while (text(hash_used) != 0); /* search for an empty location in |hash| */ + next(p) = hash_used; + p = hash_used; + } + } + check_pool_overflow((pool_ptr + l)); + d = cur_length; + while (pool_ptr > str_start_macro(str_ptr)) { + /* move current string up to make room for another */ + decr(pool_ptr); + str_pool[pool_ptr + l] = str_pool[pool_ptr]; + } + for (k = j; k <= j + l - 1; k++) + append_char(*k); + text(p) = make_string(); + pool_ptr = pool_ptr + d; + 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. +*/ + + +pointer id_lookup(integer j, integer l) +{ /* search the hash table */ + integer h; /* hash code */ + pointer p; /* index in |hash| array */ + + h = compute_hash((char *) (buffer + j), l, hash_prime); + p = h + hash_base; /* we start searching here; note that |0<=h<hash_prime| */ + while (1) { + if (text(p) > 0) + if (length(text(p)) == l) + if (str_eq_buf(text(p), j)) + goto FOUND; + if (next(p) == 0) { + if (no_new_control_sequence) { + p = static_undefined_control_sequence; + } else { + p = insert_id(p, (buffer + j), l); + } + goto FOUND; + } + p = 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. + */ + + +pointer string_lookup(char *s, size_t l) +{ /* search the hash table */ + integer h; /* hash code */ + pointer p; /* index in |hash| array */ + h = compute_hash(s, l, hash_prime); + p = h + hash_base; /* we start searching here; note that |0<=h<hash_prime| */ + while (1) { + if (text(p) > 0) + if (str_eq_cstr(text(p), s, l)) + goto FOUND; + if (next(p) == 0) { + if (no_new_control_sequence) { + p = static_undefined_control_sequence; + } else { + p = insert_id(p, (unsigned char *) s, l); + } + goto FOUND; + } + p = 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. +*/ + +#define chr_cmd(A) do { tprint(A); print(chr_code); } while (0) + +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("\\"); + 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) +{ + integer 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: + 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 + 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 math_comp_cmd: + print_math_comp(chr_code); + break; + case limit_switch_cmd: + print_limit_switch(chr_code); + break; + case math_style_cmd: + print_style(chr_code); + break; + case char_given_cmd: + tprint_esc("char"); + print_hex(chr_code); + break; + case math_given_cmd: + tprint_esc("mathchar"); + show_mathcode_value(mathchar_from_integer(chr_code, tex_mathcode)); + break; + case omath_given_cmd: + tprint_esc("omathchar"); + show_mathcode_value(mathchar_from_integer(chr_code, aleph_mathcode)); + break; + case xmath_given_cmd: + tprint_esc("Umathchar"); + show_mathcode_value(mathchar_from_integer(chr_code, xetex_mathcode)); + break; + case def_family_cmd: + print_size(chr_code); + break; + case set_math_param_cmd: + print_math_param(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 (info(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 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 set_ocp_cmd: + tprint("select ocp "); + slow_print(ocp_name(chr_code)); + break; + case set_ocp_list_cmd: + tprint("select ocp list "); + break; + case assign_glue_cmd: + case assign_mu_glue_cmd: + if (chr_code < skip_base) { + print_skip_param(chr_code - glue_base); + } 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) { + print_param(chr_code - int_base); + } 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) { + print_length_param(chr_code - dimen_base); + } else { + tprint_esc("dimen"); + print_int(chr_code - scaled_base); + } + break; + default: + /* these are most commands, actually */ + prim_cmd_chr(cmd, chr_code); + break; + } +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texdeffont.c b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c new file mode 100644 index 00000000000..5be7d084781 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/tex/texdeffont.c @@ -0,0 +1,189 @@ +/* texdeffont.c + + Copyright 2009 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ + +#include "luatex-api.h" +#include <ptexlib.h> + +#include "tokens.h" +#include "nodes.h" +#include "commands.h" + +static const char _svn_version[] = + "$Id: texdeffont.c 2086 2009-03-22 15:32:08Z oneiros $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/texdeffont.c $"; + +#define text(a) hash[(a)].rh /* string number for control sequence name */ +#define null_cs 1 /* equivalent of \.{\\csname\\endcsname} */ +#define scan_normal_dimen() scan_dimen(false,false,false) + +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 */ + k = 0; + if (s < 0) { + result[k++] = '-'; + s = -s; /* print the sign, if negative */ + } + { + int l = 0; + char dig[8] = { 0 }; + n = s / unity; + /* process the integer part */ + do { + dig[l++] = n % 10; + n = n / 10;; + } while (n > 0); + while (l > 0) { + result[k++] = (dig[--l] + '0'); + } + } + result[k++] = '.'; + s = 10 * (s % unity) + 5; + delta = 10; + do { + if (delta > unity) + s = s + 0100000 - 050000; /* round the last digit */ + result[k++] = '0' + (s / unity); + s = 10 * (s % unity); + delta = delta * 10; + } while (s > delta); + + result[k] = 0; + return (char *) result; +} + +void tex_def_font(small_number a) +{ + pointer u; /* user's font identifier */ + internal_font_number f; /* runs through existing fonts */ + str_number t; /* name for the frozen font identifier */ + int old_setting; /* holds |selector| setting */ + integer offset = 0; + scaled s = -1000; /* stated ``at'' size, or negative of scaled magnification */ + integer natural_dir = -1; /* the natural direction of the font */ + if (job_name == 0) + open_log_file(); /* avoid confusing \.{texput} with the font name */ + get_r_token(); + u = cur_cs; + if (u >= null_cs) + t = text(u); + else + t = maketexstring("FONT"); + if (a >= 4) { + geq_define(u, set_font_cmd, null_font); + } else { + eq_define(u, set_font_cmd, null_font); + } + scan_optional_equals(); + /* @<Get the next non-blank non-call token@>; */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd != left_brace_cmd) { + back_input(); + scan_file_name(); + if (cur_area != get_nullstr() || cur_ext != get_nullstr()) { + /* Have to do some rescue-ing here, fonts only have a name, + no area nor extension */ + old_setting = selector; + selector = new_string; + if (cur_area != get_nullstr()) { + print(cur_area); + } + if (cur_name != get_nullstr()) { + print(cur_name); + } + if (cur_ext != get_nullstr()) { + print(cur_ext); + } + selector = old_setting; + cur_name = make_string(); + cur_ext = get_nullstr(); + cur_area = get_nullstr(); + } + } else { + back_input(); + (void) scan_toks(false, true); + old_setting = selector; + selector = new_string; + token_show(def_ref); + selector = old_setting; + flush_list(def_ref); + /* 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 */ + if (scan_keyword("at")) { + /* @<Put the \(p)(positive) `at' size into |s|@> */ + scan_normal_dimen(); + s = cur_val; + if ((s <= 0) || (s >= 01000000000)) { + char err[256]; + char *errhelp[] = + { "I can only handle fonts at positive sizes that are", + "less than 2048pt, so I've changed what you said to 10pt.", + NULL + }; + snprintf(err, 255, "Improper `at' size (%spt), replaced by 10pt", + scaled_to_string(s)); + tex_error(err, errhelp); + s = 10 * unity; + } + } else if (scan_keyword("scaled")) { + scan_int(); + s = -cur_val; + if ((cur_val <= 0) || (cur_val > 32768)) { + char err[256]; + char *errhelp[] = + { "The magnification ratio must be between 1 and 32768.", + NULL }; + snprintf(err, 255, + "Illegal magnification has been changed to 1000 (%d)", + (int) cur_val); + tex_error(err, errhelp); + s = -1000; + } + } + if (scan_keyword("offset")) { + scan_int(); + offset = cur_val; + if (cur_val < 0) { + char err[256]; + char *errhelp[] = { "The offset must be bigger than 0.", NULL }; + snprintf(err, 255, "Illegal offset has been changed to 0 (%d)", + (int) cur_val); + tex_error(err, errhelp); + offset = 0; + } + } + if (scan_keyword("naturaldir")) { + scan_direction(); + natural_dir = cur_val; + } + name_in_progress = false; + f = read_font_info(u, cur_name, s, natural_dir); + equiv(u) = f; + zeqtb[get_font_id_base() + f] = zeqtb[u]; + text(get_font_id_base() + f) = t; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.c b/Build/source/texk/web2c/luatexdir/tex/texnodes.c index 733437ffe00..8dcdec2e1ad 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texnodes.c +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.c @@ -1,10 +1,75 @@ -/* $Id: texnodes.c 1168 2008-04-15 13:43:34Z taco $ */ +/* texnodes.c + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ #include "luatex-api.h" #include <ptexlib.h> #include "nodes.h" +#include "commands.h" + +#include "tokens.h" +#undef name + +#define nDEBUG + +static const char _svn_version[] = + "$Id: texnodes.c 2029 2009-03-14 19:10:25Z oneiros $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/texnodes.c $"; + +#define append_char(A) str_pool[pool_ptr++]=(A) +#define cur_length (pool_ptr - str_start_macro(str_ptr)) +#define flush_char() pool_ptr-- + +#define stretching 1 +#define shrinking 2 + +#define adjust_pre subtype -#define MAX_CHAIN_SIZE 12 +typedef enum { + pdf_dest_xyz = 0, + pdf_dest_fit, + pdf_dest_fith, + pdf_dest_fitv, + pdf_dest_fitb, + pdf_dest_fitbh, + pdf_dest_fitbv, + pdf_dest_fitr, +} pdf_destination_types; + + +typedef enum { + set_origin = 0, + direct_page, + direct_always, +} ctm_transform_modes; + + +#define obj_aux(A) obj_tab[(A)].int4 +#define obj_data_ptr obj_aux +#define obj_obj_data(A) pdf_mem[obj_data_ptr((A)) + 0] +#define obj_obj_is_stream(A) pdf_mem[obj_data_ptr((A)) + 1] +#define obj_obj_stream_attr(A) pdf_mem[obj_data_ptr((A)) + 2] +#define obj_obj_is_file(A) pdf_mem[obj_data_ptr((A)) + 3] +#define obj_xform_width(A) pdf_mem[obj_data_ptr((A)) + 0] +#define obj_xform_height(A) pdf_mem[obj_data_ptr((A)) + 1] +#define obj_xform_depth(A) pdf_mem[obj_data_ptr((A)) + 2] + + +#define MAX_CHAIN_SIZE 13 volatile memory_word *varmem = NULL; @@ -60,24 +125,33 @@ char *node_fields_glyph[] = { "attr", "char", "font", "lang", "left", "right", "uchyph", "components", "xoffset", "yoffset", NULL }; -char *node_fields_style[] = { NULL }; -char *node_fields_choice[] = { NULL }; -char *node_fields_ord[] = { NULL }; -char *node_fields_op[] = { NULL }; -char *node_fields_bin[] = { NULL }; -char *node_fields_rel[] = { NULL }; -char *node_fields_open[] = { NULL }; -char *node_fields_close[] = { NULL }; -char *node_fields_punct[] = { NULL }; -char *node_fields_inner[] = { NULL }; -char *node_fields_radical[] = { NULL }; -char *node_fields_fraction[] = { NULL }; -char *node_fields_under[] = { NULL }; -char *node_fields_over[] = { NULL }; -char *node_fields_accent[] = { NULL }; -char *node_fields_vcenter[] = { NULL }; -char *node_fields_left[] = { NULL }; -char *node_fields_right[] = { NULL }; +char *node_fields_style[] = { "attr", "style", NULL }; +char *node_fields_choice[] = + { "attr", "display", "text", "script", "scriptscript", NULL }; +char *node_fields_ord[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_op[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_bin[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_rel[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_open[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_close[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_punct[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_inner[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_under[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_over[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_vcenter[] = { "attr", "nucleus", "sub", "sup", NULL }; +char *node_fields_radical[] = + { "attr", "nucleus", "sub", "sup", "left", "degree", NULL }; +char *node_fields_fraction[] = + { "attr", "width", "num", "denom", "left", "right", NULL }; +char *node_fields_accent[] = + { "attr", "nucleus", "sub", "sup", "accent", "bot_accent", NULL }; +char *node_fields_fence[] = { "attr", "delim", NULL }; +char *node_fields_math_char[] = { "attr", "fam", "char", NULL }; +char *node_fields_sub_box[] = { "attr", "list", NULL }; +char *node_fields_sub_mlist[] = { "attr", "list", NULL }; +char *node_fields_math_text_char[] = { "attr", "fam", "char", NULL }; +char *node_fields_delim[] = + { "attr", "small_fam", "small_char", "large_fam", "large_char", NULL }; char *node_fields_inserting[] = { "height", "last_ins_ptr", "best_ins_ptr", NULL }; @@ -133,7 +207,7 @@ char *node_fields_whatsit_pdf_start_thread[] = }; char *node_fields_whatsit_pdf_end_thread[] = { "attr", NULL }; char *node_fields_whatsit_pdf_save_pos[] = { "attr", NULL }; -char *node_fields_whatsit_late_lua[] = { "attr", "reg", "data", NULL }; +char *node_fields_whatsit_late_lua[] = { "attr", "reg", "data", "name", NULL }; char *node_fields_whatsit_close_lua[] = { "attr", "reg", NULL }; char *node_fields_whatsit_pdf_colorstack[] = { "attr", "stack", "cmd", "data", NULL }; @@ -175,18 +249,23 @@ node_info node_data[] = { {over_noad, noad_size, node_fields_over, "over"}, {accent_noad, accent_noad_size, node_fields_accent, "accent"}, {vcenter_noad, noad_size, node_fields_vcenter, "vcenter"}, - {left_noad, noad_size, node_fields_left, "left"}, - {right_noad, noad_size, node_fields_right, "right"}, + {fence_noad, fence_noad_size, node_fields_fence, "fence"}, + {math_char_node, math_kernel_node_size, node_fields_math_char, "math_char"}, + {sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box"}, + {sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist"}, + {math_text_char_node, math_kernel_node_size, node_fields_math_text_char, + "math_text_char"}, + {delim_node, math_shield_node_size, node_fields_delim, "delim"}, {margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern"}, - {glyph_node, glyph_node_size, node_fields_glyph, "glyph"}, /* 33 */ + {glyph_node, glyph_node_size, node_fields_glyph, "glyph"}, {align_record_node, box_node_size, NULL, "align_record"}, {pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file"}, {pseudo_line_node, variable_node_size, NULL, "pseudo_line"}, {inserting_node, page_ins_node_size, node_fields_inserting, "page_insert"}, {split_up_node, page_ins_node_size, node_fields_splitup, "split_insert"}, {expr_node, expr_node_size, NULL, "expr_stack"}, - {nesting_node, nesting_node_size, NULL, "nested_list"}, /* 40 */ + {nesting_node, nesting_node_size, NULL, "nested_list"}, {span_node, span_node_size, NULL, "span"}, {attribute_node, attribute_node_size, node_fields_attribute, "attribute"}, {glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec"}, @@ -197,7 +276,7 @@ node_info node_data[] = { {align_stack_node, align_stack_node_size, NULL, "align_stack"}, {movement_node, movement_node_size, NULL, "movement_stack"}, {if_node, if_node_size, NULL, "if_stack"}, - {unhyphenated_node, active_node_size, NULL, "unhyphenated"}, /* 50 */ + {unhyphenated_node, active_node_size, NULL, "unhyphenated"}, {hyphenated_node, active_node_size, NULL, "hyphenated"}, {delta_node, delta_node_size, NULL, "delta"}, {passive_node, passive_node_size, NULL, "passive"}, @@ -256,7 +335,8 @@ node_info whatsit_node_data[] = { {fake_node, fake_node_size, NULL, fake_node_name}, {fake_node, fake_node_size, NULL, fake_node_name}, {fake_node, fake_node_size, NULL, fake_node_name}, - {late_lua_node, write_node_size, node_fields_whatsit_late_lua, "late_lua"}, + {late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, + "late_lua"}, {close_lua_node, write_node_size, node_fields_whatsit_close_lua, "close_lua"}, {fake_node, fake_node_size, NULL, fake_node_name}, @@ -278,9 +358,6 @@ node_info whatsit_node_data[] = { #define last_whatsit_node user_defined_node -#define get_node_size(i,j) (i!=whatsit_node ? node_data[i].size : whatsit_node_data[j].size) -#define get_node_name(i,j) (i!=whatsit_node ? node_data[i].name : whatsit_node_data[j].name) - halfword new_node(int i, int j) { register int s; @@ -297,10 +374,6 @@ halfword new_node(int i, int j) case glyph_node: init_lang_data(n); break; - case glue_node: - case kern_node: - case glue_spec_node: - break; case hlist_node: case vlist_node: box_dir(n) = -1; @@ -345,11 +418,44 @@ halfword new_node(int i, int j) default: break; } + /* handle synctex extension */ + switch (i) { + case math_node: + synctex_tag_math(n) = cur_input.synctex_tag_field; + synctex_line_math(n) = line; + break; + case glue_node: + synctex_tag_glue(n) = cur_input.synctex_tag_field; + synctex_line_glue(n) = line; + break; + case kern_node: + /* synctex ignores implicit kerns */ + 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; + } + /* take care of attributes */ if (nodetype_has_attributes(i)) { build_attribute_list(n); } type(n) = i; subtype(n) = j; +#ifdef DEBUG + fprintf(stderr, "Alloc-ing %s node %d\n", + get_node_name(type(n), subtype(n)), (int) n); +#endif return n; } @@ -382,7 +488,7 @@ halfword new_glyph_node(void) halfword copy_node_list(halfword p) { - halfword q; /* previous position in new list */ + halfword q = null; /* previous position in new list */ halfword h = null; /* head of the list */ copy_error_seen = 0; while (p != null) { @@ -410,14 +516,29 @@ halfword copy_node(const halfword p) } i = get_node_size(type(p), subtype(p)); r = get_node(i); + (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * i)); + /* handle synctex extension */ + switch (type(p)) { + 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(type(p))) { add_node_attr_ref(node_attr(p)); alink(r) = null; /* needs checking */ } vlink(r) = null; + + switch (type(p)) { case glyph_node: s = copy_node_list(lig_ptr(p)); @@ -479,6 +600,74 @@ halfword copy_node(const halfword p) s = copy_node_list(adjust_ptr(p)); adjust_ptr(r) = s; break; + + case choice_node: + s = copy_node_list(display_mlist(p)); + display_mlist(r) = s; + s = copy_node_list(text_mlist(p)); + text_mlist(r) = s; + s = copy_node_list(script_mlist(p)); + script_mlist(r) = s; + s = copy_node_list(script_script_mlist(p)); + script_script_mlist(r) = s; + break; + case ord_noad: + case op_noad: + case bin_noad: + case rel_noad: + case open_noad: + case close_noad: + case punct_noad: + case inner_noad: + case over_noad: + case under_noad: + case vcenter_noad: + case radical_noad: + case accent_noad: + s = copy_node_list(nucleus(p)); + nucleus(r) = s; + s = copy_node_list(subscr(p)); + subscr(r) = s; + s = copy_node_list(supscr(p)); + supscr(r) = s; + if (type(p) == accent_noad) { + s = copy_node_list(accent_chr(p)); + accent_chr(r) = s; + s = copy_node_list(bot_accent_chr(p)); + bot_accent_chr(r) = s; + } else if (type(p) == radical_noad) { + s = copy_node(left_delimiter(p)); + left_delimiter(r) = s; + s = copy_node_list(degree(p)); + degree(r) = s; + } + break; + case fence_noad: + s = copy_node(delimiter(p)); + delimiter(r) = s; + break; + /* + case style_node: + case delim_node: + case math_char_node: + case math_text_char_node: + break; + */ + case sub_box_node: + case sub_mlist_node: + s = copy_node_list(math_list(p)); + math_list(r) = s; + break; + case fraction_noad: + s = copy_node_list(numerator(p)); + numerator(r) = s; + s = copy_node_list(denominator(p)); + denominator(r) = s; + s = copy_node(left_delimiter(p)); + left_delimiter(r) = s; + s = copy_node(right_delimiter(p)); + right_delimiter(r) = s; + break; case glue_spec_node: glue_ref_count(r) = null; break; @@ -502,6 +691,8 @@ halfword copy_node(const halfword p) add_token_ref(pdf_setmatrix_data(p)); break; case late_lua_node: + if (late_lua_name(p) > 0) + add_token_ref(late_lua_name(p)); add_token_ref(late_lua_data(p)); break; case pdf_annot_node: @@ -531,6 +722,9 @@ halfword copy_node(const halfword p) case 't': add_token_ref(user_node_value(p)); break; + case 's': + /* |add_string_ref(user_node_value(p));| *//* if this was mpost .. */ + break; case 'n': s = copy_node_list(user_node_value(p)); user_node_value(r) = s; @@ -549,7 +743,9 @@ int valid_node(halfword p) { if (p > my_prealloc) { if (p < var_mem_max) { +#ifndef NDEBUG if (varmem_sizes[p] > 0) +#endif return 1; } } else { @@ -560,16 +756,15 @@ int valid_node(halfword p) static void do_free_error(halfword p) { + halfword r; char errstr[255] = { 0 }; - char *errhlp[] = - { -"When I tried to free the node mentioned in the error message, it turned", + char *errhlp[] = { + "When I tried to free the node mentioned in the error message, it turned", "out it was not (or no longer) actually in use.", "Errors such as these are often caused by Lua node list alteration,", "but could also point to a bug in the executable. It should be safe to continue.", NULL }; - halfword r; check_node_mem(); if (free_error_seen) @@ -586,6 +781,7 @@ static void do_free_error(halfword p) get_node_name(type(p), subtype(p)), (int) p); } tex_error(errstr, errhlp); +#ifndef NDEBUG for (r = my_prealloc + 1; r < var_mem_max; r++) { if (vlink(r) == p) { halfword s = r; @@ -646,16 +842,19 @@ static void do_free_error(halfword p) } } } +#endif } int free_error(halfword p) { assert(p > my_prealloc); assert(p < var_mem_max); +#ifndef NDEBUG if (varmem_sizes[p] == 0) { do_free_error(p); return 1; /* double free */ } +#endif return 0; } @@ -663,9 +862,8 @@ int free_error(halfword p) static void do_copy_error(halfword p) { char errstr[255] = { 0 }; - char *errhlp[] = - { -"When I tried to copy the node mentioned in the error message, it turned", + char *errhlp[] = { + "When I tried to copy the node mentioned in the error message, it turned", "out it was not (or no longer) actually in use.", "Errors such as these are often caused by Lua node list alteration,", "but could also point to a bug in the executable. It should be safe to continue.", @@ -692,10 +890,12 @@ int copy_error(halfword p) { assert(p >= 0); assert(p < var_mem_max); +#ifndef NDEBUG if (p > my_prealloc && varmem_sizes[p] == 0) { do_copy_error(p); return 1; /* copy free node */ } +#endif return 0; } @@ -707,6 +907,10 @@ void flush_node(halfword p) if (p == null) /* legal, but no-op */ return; +#ifdef DEBUG + fprintf(stderr, "Free-ing %s node %d\n", get_node_name(type(p), subtype(p)), + (int) p); +#endif if (free_error(p)) return; @@ -769,6 +973,8 @@ void flush_node(halfword p) delete_token_ref(pdf_setmatrix_data(p)); break; case late_lua_node: + if (late_lua_name(p) > 0) + delete_token_ref(late_lua_name(p)); delete_token_ref(late_lua_data(p)); break; case pdf_annot_node: @@ -809,8 +1015,22 @@ void flush_node(halfword p) 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 'd': + break; default: - tconfusion("extuser"); + { + 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; @@ -839,7 +1059,7 @@ void flush_node(halfword p) case adjust_node: flush_node_list(adjust_ptr(p)); break; - case style_node: + case style_node: /* nothing to do */ break; case choice_node: flush_node_list(display_mlist(p)); @@ -855,28 +1075,38 @@ void flush_node(halfword p) case close_noad: case punct_noad: case inner_noad: - case radical_noad: case over_noad: case under_noad: case vcenter_noad: + case radical_noad: case accent_noad: - - /* - * if (math_type(nucleus(p))>=sub_box) - * flush_node_list(vinfo(nucleus(p))); - * if (math_type(supscr(p))>=sub_box) - * flush_node_list(vinfo(supscr(p))); - * if (math_type(subscr(p))>=sub_box) - * flush_node_list(vinfo(subscr(p))); - */ - + flush_node_list(nucleus(p)); + flush_node_list(subscr(p)); + flush_node_list(supscr(p)); + if (type(p) == accent_noad) { + flush_node_list(accent_chr(p)); + flush_node_list(bot_accent_chr(p)); + } else if (type(p) == radical_noad) { + flush_node(left_delimiter(p)); + flush_node_list(degree(p)); + } + break; + case fence_noad: + flush_node(delimiter(p)); break; - case left_noad: - case right_noad: + case delim_node: /* nothing to do */ + case math_char_node: + case math_text_char_node: + break; + case sub_box_node: + case sub_mlist_node: + flush_node_list(math_list(p)); break; case fraction_noad: - flush_node_list(vinfo(numerator(p))); - flush_node_list(vinfo(denominator(p))); + flush_node_list(numerator(p)); + flush_node_list(denominator(p)); + flush_node(left_delimiter(p)); + flush_node(right_delimiter(p)); break; case pseudo_file_node: flush_node_list(pseudo_lines(p)); @@ -929,18 +1159,18 @@ void flush_node_list(halfword pp) static int test_count = 1; -#define dorangetest(a,b,c) do { \ +#define dorangetest(a,b,c) do { \ if (!(b>=0 && b<c)) { \ fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \ (int)a, (int)b, (int)c, __LINE__,test_count); \ - tconfusion("dorangetest"); \ + tconfusion("dorangetest"); \ } } while (0) -#define dotest(a,b,c) do { \ +#define dotest(a,b,c) do { \ if (b!=c) { \ fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \ (int)a, (int)b, (int)c, __LINE__,test_count); \ - tconfusion("dotest"); \ + tconfusion("dotest"); \ } } while (0) #define check_action_ref(a) { dorangetest(p,a,var_mem_max); } @@ -985,6 +1215,8 @@ void check_node(halfword p) check_token_ref(pdf_setmatrix_data(p)); break; case late_lua_node: + if (late_lua_name(p) > 0) + check_token_ref(late_lua_name(p)); check_token_ref(late_lua_data(p)); break; case pdf_annot_node: @@ -1017,6 +1249,9 @@ void check_node(halfword p) case 'n': dorangetest(p, user_node_value(p), var_mem_max); break; + case 's': + case 'd': + break; default: tconfusion("extuser"); break; @@ -1066,15 +1301,11 @@ void check_node(halfword p) dorangetest(p, script_script_mlist(p), var_mem_max); break; case fraction_noad: - dorangetest(p, vinfo(numerator(p)), var_mem_max); - dorangetest(p, vinfo(denominator(p)), var_mem_max); + 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 rule_node: - case kern_node: - case math_node: - case penalty_node: - case mark_node: - case style_node: case ord_noad: case op_noad: case bin_noad: @@ -1083,13 +1314,36 @@ void check_node(halfword p) case close_noad: case punct_noad: case inner_noad: - case radical_noad: case over_noad: case under_noad: case vcenter_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: - case left_noad: - case right_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + dorangetest(p, accent_chr(p), var_mem_max); + dorangetest(p, bot_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 math_node: + case penalty_node: + case mark_node: + case style_node: case attribute_list_node: case attribute_node: case glue_spec_node: @@ -1165,12 +1419,13 @@ void check_node_mem(void) { int i; check_static_node_mem(); - +#ifndef NDEBUG for (i = (my_prealloc + 1); i < var_mem_max; i++) { if (varmem_sizes[i] > 0) { check_node(i); } } +#endif test_count++; } @@ -1214,7 +1469,7 @@ void print_free_chain(int c) halfword p = free_chain[c]; fprintf(stdout, "\nfree chain[%d] =\n ", c); while (p != null) { - fprintf(stdout, "%d,", (int)p); + fprintf(stdout, "%d,", (int) p); p = vlink(p); } fprintf(stdout, "null;\n"); @@ -1265,9 +1520,9 @@ void free_node_chain(halfword q, integer s) } -void init_node_mem(halfword prealloced, halfword t) +void init_node_mem(halfword t) { - my_prealloc = prealloced; + my_prealloc = var_mem_stat_max; assert(whatsit_node_data[user_defined_node].id == user_defined_node); assert(node_data[passive_node].id == passive_node); @@ -1285,10 +1540,112 @@ void init_node_mem(halfword prealloced, halfword t) memset((void *) varmem_sizes, 0, sizeof(char) * t); #endif var_mem_max = t; - rover = prealloced + 1; + rover = var_mem_stat_max + 1; vlink(rover) = rover; node_size(rover) = (t - rover); var_used = 0; + /* initialize static glue specs */ + glue_ref_count(zero_glue) = null + 1; + width(zero_glue) = 0; + type(zero_glue) = glue_spec_node; + vlink(zero_glue) = null; + stretch(zero_glue) = 0; + stretch_order(zero_glue) = normal; + shrink(zero_glue) = 0; + shrink_order(zero_glue) = normal; + glue_ref_count(sfi_glue) = null + 1; + width(sfi_glue) = 0; + type(sfi_glue) = glue_spec_node; + vlink(sfi_glue) = null; + stretch(sfi_glue) = 0; + stretch_order(sfi_glue) = sfi; + shrink(sfi_glue) = 0; + shrink_order(sfi_glue) = normal; + glue_ref_count(fil_glue) = null + 1; + width(fil_glue) = 0; + type(fil_glue) = glue_spec_node; + vlink(fil_glue) = null; + stretch(fil_glue) = unity; + stretch_order(fil_glue) = fil; + shrink(fil_glue) = 0; + shrink_order(fil_glue) = normal; + glue_ref_count(fill_glue) = null + 1; + width(fill_glue) = 0; + type(fill_glue) = glue_spec_node; + vlink(fill_glue) = null; + stretch(fill_glue) = unity; + stretch_order(fill_glue) = fill; + shrink(fill_glue) = 0; + shrink_order(fill_glue) = normal; + glue_ref_count(ss_glue) = null + 1; + width(ss_glue) = 0; + type(ss_glue) = glue_spec_node; + vlink(ss_glue) = null; + stretch(ss_glue) = unity; + stretch_order(ss_glue) = fil; + shrink(ss_glue) = unity; + shrink_order(ss_glue) = fil; + glue_ref_count(fil_neg_glue) = null + 1; + width(fil_neg_glue) = 0; + type(fil_neg_glue) = glue_spec_node; + vlink(fil_neg_glue) = null; + stretch(fil_neg_glue) = -unity; + stretch_order(fil_neg_glue) = fil; + shrink(fil_neg_glue) = 0; + shrink_order(fil_neg_glue) = normal; + /* initialize node list heads */ + vinfo(page_ins_head) = 0; + vlink(page_ins_head) = null; + alink(page_ins_head) = null; + vinfo(contrib_head) = 0; + vlink(contrib_head) = null; + alink(contrib_head) = null; + vinfo(page_head) = 0; + vlink(page_head) = null; + alink(page_head) = null; + vinfo(temp_head) = 0; + vlink(temp_head) = null; + alink(temp_head) = null; + vinfo(hold_head) = 0; + vlink(hold_head) = null; + alink(hold_head) = null; + vinfo(adjust_head) = 0; + vlink(adjust_head) = null; + alink(adjust_head) = null; + vinfo(pre_adjust_head) = 0; + vlink(pre_adjust_head) = null; + alink(pre_adjust_head) = null; + vinfo(active) = 0; + vlink(active) = null; + alink(active) = null; + vinfo(align_head) = 0; + vlink(align_head) = null; + alink(align_head) = null; + vinfo(end_span) = 0; + vlink(end_span) = null; + alink(end_span) = null; + type(begin_point) = glyph_node; + subtype(begin_point) = 0; + vlink(begin_point) = null; + vinfo(begin_point + 1) = null; + alink(begin_point) = null; + font(begin_point) = 0; + character(begin_point) = '.'; + vinfo(begin_point + 3) = 0; + vlink(begin_point + 3) = 0; + vinfo(begin_point + 4) = 0; + vlink(begin_point + 4) = 0; + type(end_point) = glyph_node; + subtype(end_point) = 0; + vlink(end_point) = null; + vinfo(end_point + 1) = null; + alink(end_point) = null; + font(end_point) = 0; + character(end_point) = '.'; + vinfo(end_point + 3) = 0; + vlink(end_point + 3) = 0; + vinfo(end_point + 4) = 0; + vlink(end_point + 4) = 0; } void dump_node_mem(void) @@ -1448,6 +1805,7 @@ char *sprint_node_mem_usage(void) int i, b; char *s, *ss; +#ifndef NDEBUG char msg[256]; int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; @@ -1471,12 +1829,17 @@ char *sprint_node_mem_usage(void) snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], get_node_name((i > last_normal_node ? whatsit_node : i), j)); - ss = concat(s, msg); + ss = xmalloc(strlen(s) + strlen(msg) + 1); + strcpy(ss, s); + strcat(ss, msg); free(s); s = ss; b = 1; } } +#else + s = strdup(""); +#endif return s; } @@ -1484,6 +1847,7 @@ halfword list_node_mem_usage(void) { halfword i, j; halfword p = null, q = null; +#ifndef NDEBUG char *saved_varmem_sizes = xmallocarray(char, var_mem_max); memcpy(saved_varmem_sizes, varmem_sizes, var_mem_max); for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { @@ -1498,17 +1862,17 @@ halfword list_node_mem_usage(void) } } free(saved_varmem_sizes); +#endif return q; } -void print_node_mem_stats(int num, int online) +void print_node_mem_stats(int tracingstats, int tracingonline) { int i, b; halfword j; char msg[256]; char *s; integer free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; - snprintf(msg, 255, "node memory in use: %d words out of %d", (int) (var_used + my_prealloc), (int) var_mem_max); tprint_nl(msg); @@ -1680,7 +2044,7 @@ halfword do_set_attribute(halfword p, int i, int val) vlink(q) = p; return q; } - + q = p; assert(vlink(p) != null); while (vlink(p) != null) { int t = attribute_id(vlink(p)); @@ -1774,11 +2138,15 @@ int unset_attribute(halfword n, int i, int val) t = attribute_id(vlink(p)); if (t > i) return -1; - if (t == i) + if (t == i) { + p = vlink(p); break; + } j++; p = vlink(p); } + if (attribute_id(p) != i) + return -1; /* if we are still here, the attribute exists */ p = node_attr(n); if (attr_list_ref(p) != 1) { @@ -1825,3 +2193,738 @@ int has_attribute(halfword n, int i, int val) } return -1; } + +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_ptr(p) != zero_glue) + 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; + default: + assert(1); + break; + } +} + + +void show_pdftex_whatsit_rule_spec(integer p) +{ + tprint("("); + print_rule_dimen(pdf_height(p)); + print_char('+'); + print_rule_dimen(pdf_depth(p)); + tprint(")x"); + print_rule_dimen(pdf_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. +*/ + + +void print_write_whatsit(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('-'); +} + + +void show_whatsit_node(integer 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 dir_node: + if (dir_dir(p) < 0) { + tprint_esc("enddir"); + print_char(' '); + print_dir(dir_dir(p) + 64); + } else { + tprint_esc("begindir"); + print_char(' '); + print_dir(dir_dir(p)); + } + case local_par_node: + tprint_esc("whatsit"); + 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(pool_ptr); + } + 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(pool_ptr); + } + decr(pool_ptr); + break; + case pdf_literal_node: + tprint_esc("pdfliteral"); + switch (pdf_literal_mode(p)) { + case set_origin: + break; + case direct_page: + tprint(" page"); + break; + case direct_always: + tprint(" direct"); + break; + default: + tconfusion("literal2"); + break; + } + print_mark(pdf_literal_data(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: + tconfusion("pdfcolorstack"); + 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 cancel_boundary_node: + tprint_esc("noboundary"); + break; + case late_lua_node: + tprint_esc("latelua"); + print_int(late_lua_reg(p)); + print_mark(late_lua_data(p)); + break; + case close_lua_node: + tprint_esc("closelua"); + print_int(late_lua_reg(p)); + break; + case pdf_refobj_node: + tprint_esc("pdfrefobj"); + if (obj_obj_is_stream(pdf_obj_objnum(p)) > 0) { + if (obj_obj_stream_attr(pdf_obj_objnum(p)) != null) { + tprint(" attr"); + print_mark(obj_obj_stream_attr(pdf_obj_objnum(p))); + } + tprint(" stream"); + } + if (obj_obj_is_file(pdf_obj_objnum(p)) > 0) + tprint(" file"); + print_mark(obj_obj_data(pdf_obj_objnum(p))); + break; + case pdf_refxform_node: + tprint_esc("pdfrefxform"); + print_char('('); + print_scaled(obj_xform_height(pdf_xform_objnum(p))); + print_char('+'); + print_scaled(obj_xform_depth(pdf_xform_objnum(p))); + tprint(")x"); + print_scaled(obj_xform_width(pdf_xform_objnum(p))); + break; + case pdf_refximage_node: + tprint_esc("pdfrefximage"); + tprint("("); + print_scaled(pdf_height(p)); + print_char('+'); + print_scaled(pdf_depth(p)); + tprint(")x"); + print_scaled(pdf_width(p)); + 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: + pdf_error(maketexstring("displaying"), + maketexstring("unknown action type")); + 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(pdf_height(p)); + print_char('+'); + print_rule_dimen(pdf_depth(p)); + tprint(")x"); + print_rule_dimen(pdf_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; + case pdf_save_pos_node: + tprint_esc("pdfsavepos"); + 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; + default: + tprint("whatsit?"); + 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: */ + +#define node_list_display(A) do { \ + append_char('.'); \ + show_node_list(A); \ + flush_char(); \ +} while (0) + +void show_node_list(integer p) +{ /* prints a node list symbolically */ + integer n; /* the number of items already printed at this level */ + real g; /* a glue ratio, as a floating point number */ + if (cur_length > depth_threshold) { + if (p > null) + tprint(" []"); /* indicate that there's been some truncation */ + return; + } + n = 0; + while (p != null) { + print_ln(); + print_current_string(); /* display the nesting history */ + if (int_par(param_tracing_online_code) < -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), 0); + } + if (glue_shrink(p) != 0) { + tprint(", shrink "); + print_glue(glue_shrink(p), glue_sign(p), 0); + } + } 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), 0); + } else { + print_glue(round(unity * g), glue_order(p), 0); + } + } + + 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|@>; */ + tprint_esc("rule("); + 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), 0); + print_char(','); + print_scaled(depth(p)); + tprint("); float cost "); + print_int(float_cost(p)); + node_list_display(ins_ptr(p)); /* recursive call */ + break; + case whatsit_node: + show_whatsit_node(p); + break; + case glue_node: + /* @<Display glue |p|@>; */ + if (subtype(p) >= a_leaders) { + /* @<Display leaders |p|@>; */ + tprint_esc(""); + if (subtype(p) == c_leaders) + print_char('c'); + else if (subtype(p) == x_leaders) + print_char('x'); + tprint("leaders "); + print_spec(glue_ptr(p), 0); + node_list_display(leader_ptr(p)); /* recursive call */ + } else { + tprint_esc("glue"); + if (subtype(p) != normal) { + print_char('('); + if (subtype(p) < cond_math_glue) + print_skip_param(subtype(p) - 1); + else if (subtype(p) == cond_math_glue) + 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(glue_ptr(p), 0); + else + print_spec(glue_ptr(p), maketexstring("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) == acc_kern) + tprint(" (for 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 (width(p) != 0) { + tprint(", surrounded "); + print_scaled(width(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. */ + tprint_esc("discretionary"); + if (vlink(no_break(p)) != null) { + tprint(" replacing "); + node_list_display(vlink(no_break(p))); + } + node_list_display(vlink(pre_break(p))); /* recursive call */ + append_char('|'); + show_node_list(vlink(post_break(p))); + flush_char(); /* recursive call */ + 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 (adjust_pre(p) != 0) + tprint(" pre "); + node_list_display(adjust_ptr(p)); /* recursive call */ + 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} */ + +pointer actual_box_width(pointer r, scaled base_width) +{ + scaled w; /* calculated |size| */ + pointer p; /* current node when calculating |pre_display_size| */ + pointer q; /* glue specification when calculating |pre_display_size| */ + internal_font_number f; /* font in current |char_node| */ + scaled d; /* increment to |v| */ + scaled v; /* |w| plus possible glue amount */ + w = -max_dimen; + v = shift_amount(r) + base_width; + p = list_ptr(r); + while (p != null) { + if (is_char_node(p)) { + f = font(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 math_node: + d = surround(p); + break; + 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. + */ + q = glue_ptr(p); + d = width(q); + if (glue_sign(r) == stretching) { + if ((glue_order(r) == stretch_order(q)) + && (stretch(q) != 0)) + v = max_dimen; + } else if (glue_sign(r) == shrinking) { + if ((glue_order(r) == shrink_order(q)) + && (shrink(q) != 0)) + v = max_dimen; + } + if (subtype(p) >= a_leaders) + goto found; + break; + case whatsit_node: + if ((subtype(p) == pdf_refxform_node) + || (subtype(p) == pdf_refximage_node)) + d = pdf_width(p); + else + d = 0; + 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; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texpdf.c b/Build/source/texk/web2c/luatexdir/tex/texpdf.c index 000742dd001..c539bbae968 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texpdf.c +++ b/Build/source/texk/web2c/luatexdir/tex/texpdf.c @@ -1,9 +1,29 @@ -/* $Id: texpdf.c 1158 2008-04-14 08:13:06Z oneiros $ */ +/* texpdf.c + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ #include "luatex-api.h" #include <ptexlib.h> #include <ctype.h> +static const char _svn_version[] = + "$Id: texpdf.c 1436 2008-07-17 09:27:08Z taco $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/texpdf.c $"; + #define number_chars 1114112 #define string_offset 2097152 #define str_start_macro(a) str_start[(a) - string_offset] @@ -85,13 +105,13 @@ void pdf_print(str_number s) /* print out a integer to PDF buffer */ -void pdf_print_int(integer n) +void pdf_print_int(longinteger n) { register integer k = 0; /* current digit; we assume that $|n|<10^{23}$ */ if (n < 0) { pdf_out('-'); if (n < -0x7FFFFFFF) { /* need to negate |n| more carefully */ - register integer m; + register longinteger m; k++; m = -1 - n; n = m / 10; diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.c b/Build/source/texk/web2c/luatexdir/tex/textoken.c index 366fa798791..1cfbd56b291 100644 --- a/Build/source/texk/web2c/luatexdir/tex/textoken.c +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.c @@ -1,34 +1,47 @@ -/* $Id: textoken.c 1155 2008-04-14 07:53:21Z oneiros $ */ +/* textoken.c + + Copyright 2006-2008 Taco Hoekwater <taco@luatex.org> + + This file is part of LuaTeX. + + LuaTeX is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your + option) any later version. + + LuaTeX is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU General Public License along + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */ #include "luatex-api.h" #include <ptexlib.h> #include "tokens.h" +#include "commands.h" -/* Integer parameters and other command-related defines. This needs it's own header file! */ +static const char _svn_version[] = + "$Id: textoken.c 2086 2009-03-22 15:32:08Z oneiros $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/tex/textoken.c $"; -#define end_line_char_code 48 /* character placed at the right end of the buffer */ -#define cat_code_table_code 63 -#define tex_int_pars 66 /* total number of \.{\\TeX} + Aleph integer parameters */ -/* this is not what happens in the pascal code! there the values shift from bare numbers to offsets ! */ -#define dir_base tex_int_pars -#define dir_pars 5 -#define pdftex_first_integer_code dir_base+dir_pars /* base for \pdfTeX's integer parameters */ -#define pdf_int_pars pdftex_first_integer_code+27 /*total number of \pdfTeX's integer parameters */ -#define etex_int_base pdf_int_pars /*base for \eTeX's integer parameters */ -#define tracing_nesting_code etex_int_base+4 /*show incomplete groups and ifs within files */ +#define skipping 1 /* |scanner_status| when passing conditional text */ +#define defining 2 /* |scanner_status| when reading a macro definition */ +#define matching 3 /* |scanner_status| when reading macro arguments */ +#define aligning 4 /* |scanner_status| when reading an alignment preamble */ +#define absorbing 5 /* |scanner_status| when reading a balanced text */ -#define int_par(a) zeqtb[static_int_base+a].cint /* an integer parameter */ -#define cat_code_table int_par(cat_code_table_code) -#define tracing_nesting int_par(tracing_nesting_code) -#define end_line_char int_par(end_line_char_code) +#define right_brace_token 0x400000 /* $2^{21}\cdot|right_brace|$ */ -#define every_eof get_every_eof() +#define cat_code_table int_par(param_cat_code_table_code) +#define tracing_nesting int_par(param_tracing_nesting_code) +#define end_line_char int_par(param_end_line_char_code) +#define suppress_outer_error int_par(param_suppress_outer_error_code) -#define number_active_chars 65536+65536 -#define active_base 1 /* beginning of region 1, for active character equivalents */ -#define null_cs active_base+number_active_chars /* equivalent of \.{\\csname\\endcsname} */ +#define every_eof get_every_eof() +#define null_cs 1 /* equivalent of \.{\\csname\\endcsname} */ #define eq_level(a) zeqtb[a].hh.u.B1 #define eq_type(a) zeqtb[a].hh.u.B0 @@ -39,28 +52,43 @@ #define nonstop_mode 1 -/* command codes */ -#define relax 0 -#define out_param 5 -#define max_command 117 /* fetched from C output */ -#define dont_expand 133 /* fetched from C output */ - #define terminal_input (name==0) /* are we reading from the terminal? */ #define special_char 1114113 /* |biggest_char+2| */ #define cur_file input_file[index] /* the current |alpha_file| variable */ #define no_expand_flag special_char /*this characterizes a special variant of |relax| */ +#define detokenized_line() (line_catcode_table==NO_CAT_TABLE) + extern void insert_vj_template(void); -#define do_get_cat_code(a) do { \ - if (local_catcode_table) \ - a=get_cat_code(line_catcode_table,cur_chr); \ - else \ - a=get_cat_code(cat_code_table,cur_chr); \ +#define do_get_cat_code(a) do { \ + if (line_catcode_table!=DEFAULT_CAT_TABLE) \ + a=get_cat_code(line_catcode_table,cur_chr); \ + else \ + a=get_cat_code(cat_code_table,cur_chr); \ } while (0) + +/* string compare */ + +boolean str_eq_cstr(str_number r, char *s, size_t l) +{ + if (l != (size_t) length(r)) + return false; + return (strncmp((const char *) (str_pool + str_start_macro(r)), s, l) == 0); +} + + + +int get_char_cat_code(int cur_chr) +{ + int a; + do_get_cat_code(a); + return a; +} + static void invalid_character_error(void) { char *hlp[] = { "A funny symbol that I can't read has just been input.", @@ -72,14 +100,13 @@ static void invalid_character_error(void) deletions_allowed = true; } -/* no longer done */ - static boolean process_sup_mark(void); /* below */ static int scan_control_sequence(void); /* below */ typedef enum { next_line_ok, next_line_return, - next_line_restart } next_line_retval; + next_line_restart +} next_line_retval; static next_line_retval next_line(void); /* below */ @@ -96,6 +123,8 @@ static void utf_error(void) deletions_allowed = true; } +#define do_buffer_to_unichar(a,b) a = buffer[b] < 0x80 ? buffer[b++] : qbuffer_to_unichar(&b) + static integer qbuffer_to_unichar(integer * k) { register int ch; @@ -131,13 +160,373 @@ static integer qbuffer_to_unichar(integer * k) return (val); } +/* This is a very basic helper */ + +char *u2s(unsigned unic) +{ + char *buf = xmalloc(5); + char *pt = buf; + if (unic < 0x80) + *pt++ = unic; + else if (unic < 0x800) { + *pt++ = 0xc0 | (unic >> 6); + *pt++ = 0x80 | (unic & 0x3f); + } else if (unic >= 0x110000) { + *pt++ = unic - 0x110000; + } else if (unic < 0x10000) { + *pt++ = 0xe0 | (unic >> 12); + *pt++ = 0x80 | ((unic >> 6) & 0x3f); + *pt++ = 0x80 | (unic & 0x3f); + } else { + int u, z, y, x; + unsigned val = unic - 0x10000; + u = ((val & 0xf0000) >> 16) + 1; + z = (val & 0x0f000) >> 12; + y = (val & 0x00fc0) >> 6; + x = val & 0x0003f; + *pt++ = 0xf0 | (u >> 2); + *pt++ = 0x80 | ((u & 3) << 4) | z; + *pt++ = 0x80 | y; + *pt++ = 0x80 | x; + } + *pt = '\0'; + return buf; +} + + +/* + In case you are getting bored, here is a slightly less trivial routine: + Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or + `\.{width}', the |scan_keyword| routine checks to see whether the next + tokens of input match this string. The match must be exact, except that + uppercase letters will match their lowercase counterparts; uppercase + equivalents are determined by subtracting |"a"-"A"|, rather than using the + |uc_code| table, since \TeX\ uses this routine only for its own limited + set of keywords. + + If a match is found, the characters are effectively removed from the input + and |true| is returned. Otherwise |false| is returned, and the input + is left essentially unchanged (except for the fact that some macros + may have been expanded, etc.). + @^inner loop@> +*/ + +boolean scan_keyword(char *s) +{ /* look for a given string */ + pointer p; /* tail of the backup list */ + pointer q; /* new node being added to the token list via |store_new_token| */ + char *k; /* index into |str_pool| */ + if (strlen(s) == 1) { + /* @<Get the next non-blank non-call token@>; */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if ((cur_cs == 0) && ((cur_chr == *s) || (cur_chr == *s - 'a' + 'A'))) { + return true; + } else { + back_input(); + return false; + } + } else { + p = backup_head; + link(p) = null; + k = s; + while (*k) { + get_x_token(); /* recursion is possible here */ + if ((cur_cs == 0) && + ((cur_chr == *k) || (cur_chr == *k - 'a' + 'A'))) { + store_new_token(cur_tok); + k++; + } else if ((cur_cmd != spacer_cmd) || (p != backup_head)) { + if (p != backup_head) { + q = get_avail(); + info(q) = cur_tok; + link(q) = null; + link(p) = q; + begin_token_list(link(backup_head), backed_up); + } else { + back_input(); + } + return false; + } + } + flush_list(link(backup_head)); + } + return true; +} + +/* |scan_direction| has to be defined here because luatangle will output + a character constant when it sees a string literal of length 1 */ + +#define dir_T 0 +#define dir_L 1 +#define dir_B 2 +#define dir_R 3 + +#define scan_single_dir(A) do { \ + if (scan_keyword("T")) A=dir_T; \ + else if (scan_keyword("L")) A=dir_L; \ + else if (scan_keyword("B")) A=dir_B; \ + else if (scan_keyword("R")) A=dir_R; \ + else { \ + tex_error("Bad direction", NULL); \ + cur_val=0; \ + return; \ + } \ + } while (0) + +void scan_direction(void) +{ + integer d1, d2, d3; + get_x_token(); + if (cur_cmd == assign_dir_cmd) { + cur_val = zeqtb[cur_chr].cint; + return; + } else { + back_input(); + } + scan_single_dir(d1); + scan_single_dir(d2); + if (dir_parallel(d1, d2)) { + tex_error("Bad direction", NULL); + cur_val = 0; + return; + } + scan_single_dir(d3); + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + cur_val = d1 * 8 + dir_rearrange[d2] * 4 + d3; +} + + +/* We can not return |undefined_control_sequence| under some conditions + * (inside |shift_case|, for example). This needs thinking. + */ + +halfword active_to_cs(int curchr, int force) +{ + halfword curcs; + char *a, *b; + char *utfbytes = xmalloc(10); + int nncs = no_new_control_sequence; + a = u2s(0xFFFF); + utfbytes = strcpy(utfbytes, a); + if (force) + no_new_control_sequence = false; + if (curchr > 0) { + b = u2s(curchr); + utfbytes = strcat(utfbytes, b); + free(b); + curcs = string_lookup(utfbytes, strlen(utfbytes)); + } else { + utfbytes[3] = '\0'; + curcs = string_lookup(utfbytes, 4); + } + no_new_control_sequence = nncs; + free(a); + free(utfbytes); + return curcs; +} + +/* TODO this function should listen to \.{\\escapechar} */ + +#define is_active_cs(a) (length(a)>3 && \ + (str_pool[str_start_macro(a)] == 0xEF) && \ + (str_pool[str_start_macro(a)+1] == 0xBF) && \ + (str_pool[str_start_macro(a)+2] == 0xBF)) + + +char *cs_to_string(pointer p) +{ /* prints a control sequence */ + char *s; + int k = 0; + static char ret[256] = { 0 }; + if (p == null_cs) { + ret[k++] = '\\'; + s = "csname"; + while (*s) { + ret[k++] = *s++; + } + ret[k++] = '\\'; + s = "endcsname"; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + + } else { + str_number txt = zget_cs_text(p); + s = makecstring(txt); + if (is_active_cs(txt)) { + s = s + 3; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + } else { + ret[k++] = '\\'; + while (*s) { + ret[k++] = *s++; + } + ret[k] = 0; + } + } + return (char *) ret; +} + +/* TODO this is a quick hack, will be solved differently soon */ + +char *cmd_chr_to_string(int cmd, int chr) +{ + char *s; + strnumber str; + int sel = selector; + selector = new_string; + print_cmd_chr(cmd, chr); + str = make_string(); + s = makecstring(str); + selector = sel; + flush_str(str); + return s; +} + +/* Before getting into |get_next|, let's consider the subroutine that + is called when an `\.{\\outer}' control sequence has been scanned or + when the end of a file has been reached. These two cases are distinguished + by |cur_cs|, which is zero at the end of a file. +*/ + +static int frozen_control_sequence = 0; + +#define frozen_cr (frozen_control_sequence+1) /* permanent `\.{\\cr}' */ +#define frozen_fi (frozen_control_sequence+4) /* permanent `\.{\\fi}' */ + +void check_outer_validity(void) +{ + pointer p; /* points to inserted token list */ + pointer q; /* auxiliary pointer */ + if (suppress_outer_error) + return; + if (frozen_control_sequence == 0) { + frozen_control_sequence = get_nullcs() + 1 + get_hash_size(); /* hashbase=nullcs+1 */ + } + if (scanner_status != normal) { + deletions_allowed = false; + /* @<Back up an outer control sequence so that it can be reread@>; */ + /* An outer control sequence that occurs in a \.{\\read} will not be reread, + since the error recovery for \.{\\read} is not very powerful. */ + if (cur_cs != 0) { + if ((state == token_list) || (name < 1) || (name > 17)) { + p = get_avail(); + info(p) = cs_token_flag + cur_cs; + begin_token_list(p, backed_up); /* prepare to read the control sequence again */ + } + cur_cmd = spacer_cmd; + cur_chr = ' '; /* replace it by a space */ + } + if (scanner_status > skipping) { + char *errhlp[] = { "I suspect you have forgotten a `}', causing me", + "to read past where you wanted me to stop.", + "I'll try to recover; but if the error is serious,", + "you'd better type `E' or `X' now and fix your file.", + NULL + }; + char errmsg[256]; + char *startmsg, *scannermsg; + /* @<Tell the user what has run away and try to recover@> */ + runaway(); /* print a definition, argument, or preamble */ + if (cur_cs == 0) { + startmsg = "File ended"; + } else { + cur_cs = 0; + startmsg = "Forbidden control sequence found"; + } + /* @<Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}', + and insert tokens that should lead to recovery@>; */ + /* The recovery procedure can't be fully understood without knowing more + about the \TeX\ routines that should be aborted, but we can sketch the + ideas here: For a runaway definition we will insert a right brace; for a + runaway preamble, we will insert a special \.{\\cr} token and a right + brace; and for a runaway argument, we will set |long_state| to + |outer_call| and insert \.{\\par}. */ + p = get_avail(); + switch (scanner_status) { + case defining: + scannermsg = "definition"; + info(p) = right_brace_token + '}'; + break; + case matching: + scannermsg = "use"; + info(p) = par_token; + long_state = outer_call_cmd; + break; + case aligning: + scannermsg = "preamble"; + info(p) = right_brace_token + '}'; + q = p; + p = get_avail(); + link(p) = q; + info(p) = cs_token_flag + frozen_cr; + align_state = -1000000; + break; + case absorbing: + scannermsg = "text"; + info(p) = right_brace_token + '}'; + break; + default: /* can't happen */ + scannermsg = "unknown"; + break; + } /*there are no other cases */ + begin_token_list(p, inserted); + snprintf(errmsg, 255, "%s while scanning %s of %s", + startmsg, scannermsg, cs_to_string(warning_index)); + tex_error(errmsg, errhlp); + } else { + char errmsg[256]; + char *errhlp_no[] = + { "The file ended while I was skipping conditional text.", + "This kind of error happens when you say `\\if...' and forget", + "the matching `\\fi'. I've inserted a `\\fi'; this might work.", + NULL + }; + char *errhlp_cs[] = + { "A forbidden control sequence occurred in skipped text.", + "This kind of error happens when you say `\\if...' and forget", + "the matching `\\fi'. I've inserted a `\\fi'; this might work.", + NULL + }; + char **errhlp = (char **) errhlp_no; + if (cur_cs != 0) { + errhlp = errhlp_cs; + cur_cs = 0; + } + snprintf(errmsg, 255, + "Incomplete %s; all text was ignored after line %d", + cmd_chr_to_string(if_test_cmd, cur_if), skip_line); + /* @.Incomplete \\if...@> */ + cur_tok = cs_token_flag + frozen_fi; + /* back up one inserted token and call |error| */ + { + OK_to_interrupt = false; + back_input(); + token_type = inserted; + OK_to_interrupt = true; + tex_error(errmsg, errhlp); + } + } + deletions_allowed = true; + } +} + static boolean get_next_file(void) { SWITCH: if (loc <= limit) { /* current line not yet finished */ - cur_chr = qbuffer_to_unichar(&loc); + do_buffer_to_unichar(cur_chr, loc); + RESWITCH: - if (detokenized_line) { + if (detokenized_line()) { cur_cmd = (cur_chr == ' ' ? 10 : 12); } else { do_get_cat_code(cur_cmd); @@ -154,45 +543,49 @@ static boolean get_next_file(void) command code to the state to get a single number that characterizes both. */ switch (state + cur_cmd) { - case mid_line + ignore: - case skip_blanks + ignore: - case new_line + ignore: - case skip_blanks + spacer: - case new_line + spacer: /* @<Cases where character is ignored@> */ + case mid_line + ignore_cmd: + case skip_blanks + ignore_cmd: + case new_line + ignore_cmd: + case skip_blanks + spacer_cmd: + case new_line + spacer_cmd: /* @<Cases where character is ignored@> */ goto SWITCH; break; - case mid_line + escape: - case new_line + escape: - case skip_blanks + escape: /* @<Scan a control sequence ...@>; */ + case mid_line + escape_cmd: + case new_line + escape_cmd: + case skip_blanks + escape_cmd: /* @<Scan a control sequence ...@>; */ state = scan_control_sequence(); + if (cur_cmd >= outer_call_cmd) + check_outer_validity(); break; - case mid_line + active_char: - case new_line + active_char: - case skip_blanks + active_char: /* @<Process an active-character */ - cur_cs = cur_chr + active_base; + case mid_line + active_char_cmd: + case new_line + active_char_cmd: + case skip_blanks + active_char_cmd: /* @<Process an active-character */ + cur_cs = active_to_cs(cur_chr, false); cur_cmd = eq_type(cur_cs); cur_chr = equiv(cur_cs); state = mid_line; + if (cur_cmd >= outer_call_cmd) + check_outer_validity(); break; - case mid_line + sup_mark: - case new_line + sup_mark: - case skip_blanks + sup_mark: /* @<If this |sup_mark| starts */ + case mid_line + sup_mark_cmd: + case new_line + sup_mark_cmd: + case skip_blanks + sup_mark_cmd: /* @<If this |sup_mark| starts */ if (process_sup_mark()) goto RESWITCH; else state = mid_line; break; - case mid_line + invalid_char: - case new_line + invalid_char: - case skip_blanks + invalid_char: /* @<Decry the invalid character and |goto restart|@>; */ + case mid_line + invalid_char_cmd: + case new_line + invalid_char_cmd: + case skip_blanks + invalid_char_cmd: /* @<Decry the invalid character and |goto restart|@>; */ invalid_character_error(); return false; /* because state may be token_list now */ break; - case mid_line + spacer: /* @<Enter |skip_blanks| state, emit a space@>; */ + case mid_line + spacer_cmd: /* @<Enter |skip_blanks| state, emit a space@>; */ state = skip_blanks; cur_chr = ' '; break; - case mid_line + car_ret: /* @<Finish line, emit a space@>; */ + case mid_line + car_ret_cmd: /* @<Finish line, emit a space@>; */ /* When a character of type |spacer| gets through, its character code is changed to $\.{"\ "}=@'40$. This means that the ASCII codes for tab and space, and for the space inserted at the end of a line, will @@ -200,40 +593,42 @@ static boolean get_next_file(void) since such characters are indistinguishable on most computer terminal displays. */ loc = limit + 1; - cur_cmd = spacer; + cur_cmd = spacer_cmd; cur_chr = ' '; break; - case skip_blanks + car_ret: - case mid_line + comment: - case new_line + comment: - case skip_blanks + comment: /* @<Finish line, |goto switch|@>; */ + case skip_blanks + car_ret_cmd: + case mid_line + comment_cmd: + case new_line + comment_cmd: + case skip_blanks + comment_cmd: /* @<Finish line, |goto switch|@>; */ loc = limit + 1; goto SWITCH; break; - case new_line + car_ret: /* @<Finish line, emit a \.{\\par}@>; */ + case new_line + car_ret_cmd: /* @<Finish line, emit a \.{\\par}@>; */ loc = limit + 1; cur_cs = par_loc; cur_cmd = eq_type(cur_cs); cur_chr = equiv(cur_cs); + if (cur_cmd >= outer_call_cmd) + check_outer_validity(); break; - case skip_blanks + left_brace: - case new_line + left_brace: + case skip_blanks + left_brace_cmd: + case new_line + left_brace_cmd: state = mid_line; /* fall through */ - case mid_line + left_brace: + case mid_line + left_brace_cmd: align_state++; break; - case skip_blanks + right_brace: - case new_line + right_brace: + case skip_blanks + right_brace_cmd: + case new_line + right_brace_cmd: state = mid_line; /* fall through */ - case mid_line + right_brace: + case mid_line + right_brace_cmd: align_state--; break; - case mid_line + math_shift: - case mid_line + tab_mark: - case mid_line + mac_param: - case mid_line + sub_mark: - case mid_line + letter: - case mid_line + other_char: + case mid_line + math_shift_cmd: + case mid_line + tab_mark_cmd: + case mid_line + mac_param_cmd: + case mid_line + sub_mark_cmd: + case mid_line + letter_cmd: + case mid_line + other_char_cmd: break; default: /* @@ -282,30 +677,30 @@ static boolean get_next_file(void) #define is_hex(a) ((a>='0'&&a<='9')||(a>='a'&&a<='f')) -#define add_nybble(a) do { \ - if (a<='9') cur_chr=(cur_chr<<4)+a-'0'; \ - else cur_chr=(cur_chr<<4)+a-'a'+10; \ +#define add_nybble(a) do { \ + if (a<='9') cur_chr=(cur_chr<<4)+a-'0'; \ + else cur_chr=(cur_chr<<4)+a-'a'+10; \ } while (0) -#define hex_to_cur_chr do { \ - if (c<='9') cur_chr=c-'0'; \ - else cur_chr=c-'a'+10; \ - add_nybble(cc); \ +#define hex_to_cur_chr do { \ + if (c<='9') cur_chr=c-'0'; \ + else cur_chr=c-'a'+10; \ + add_nybble(cc); \ } while (0) -#define four_hex_to_cur_chr do { \ - hex_to_cur_chr; \ - add_nybble(ccc); add_nybble(cccc); \ +#define four_hex_to_cur_chr do { \ + hex_to_cur_chr; \ + add_nybble(ccc); add_nybble(cccc); \ } while (0) -#define five_hex_to_cur_chr do { \ - four_hex_to_cur_chr; \ - add_nybble(ccccc); \ +#define five_hex_to_cur_chr do { \ + four_hex_to_cur_chr; \ + add_nybble(ccccc); \ } while (0) -#define six_hex_to_cur_chr do { \ - five_hex_to_cur_chr; \ - add_nybble(cccccc); \ +#define six_hex_to_cur_chr do { \ + five_hex_to_cur_chr; \ + add_nybble(cccccc); \ } while (0) @@ -409,22 +804,22 @@ static int scan_control_sequence(void) register int cat; /* |cat_code(cur_chr)|, usually */ while (1) { integer k = loc; - cur_chr = qbuffer_to_unichar(&k); + do_buffer_to_unichar(cur_chr, k); do_get_cat_code(cat); - if (cat != letter || k > limit) { - retval = (cat == spacer ? skip_blanks : mid_line); - if (cat == sup_mark && check_expanded_code(&k)) /* @<If an expanded...@>; */ + if (cat != letter_cmd || k > limit) { + retval = (cat == spacer_cmd ? skip_blanks : mid_line); + if (cat == sup_mark_cmd && check_expanded_code(&k)) /* @<If an expanded...@>; */ continue; } else { retval = skip_blanks; do { - cur_chr = qbuffer_to_unichar(&k); + do_buffer_to_unichar(cur_chr, k); do_get_cat_code(cat); - } while (cat == letter && k <= limit); + } while (cat == letter_cmd && k <= limit); - if (cat == sup_mark && check_expanded_code(&k)) /* @<If an expanded...@>; */ + if (cat == sup_mark_cmd && check_expanded_code(&k)) /* @<If an expanded...@>; */ continue; - if (cat != letter) { + if (cat != letter_cmd) { decr(k); if (cur_chr > 0xFFFF) decr(k); @@ -562,10 +957,7 @@ static boolean check_expanded_code(integer * kk) static next_line_retval next_line(void) { - integer tab; /* a category table */ boolean inhibit_eol = false; /* a way to end a pseudo file without trailing space */ - detokenized_line = false; - local_catcode_table = false; if (name > 17) { /* @<Read next line of file into |buffer|, or |goto restart| if the file has ended@> */ incr(line); @@ -574,6 +966,7 @@ static next_line_retval next_line(void) if (name <= 20) { if (pseudo_input()) { /* not end of file */ firm_up_the_line(); /* this sets |limit| */ + line_catcode_table = DEFAULT_CAT_TABLE; if ((name == 19) && (pseudo_lines(pseudo_files) == null)) inhibit_eol = true; } else if ((every_eof != null) && !eof_seen[index]) { @@ -589,28 +982,20 @@ static next_line_retval next_line(void) if (name == 21) { if (luacstring_input()) { /* not end of strings */ firm_up_the_line(); - if ((luacstring_penultimate()) || (luacstring_simple())) + line_catcode_table = luacstring_cattable(); + line_partial = luacstring_partial(); + if (luacstring_final_line() || line_partial + || line_catcode_table == NO_CAT_TABLE) inhibit_eol = true; - if (!luacstring_simple()) + if (!line_partial) state = new_line; - if (luacstring_detokenized()) { - inhibit_eol = true; - detokenized_line = true; - } else { - if (!luacstring_defaultcattable()) { - tab = luacstring_cattable(); - if (valid_catcode_table(tab)) { - local_catcode_table = true; - line_catcode_table = tab; - } - } - } } else { force_eof = true; } } else { if (lua_input_ln(cur_file, 0, true)) { /* not end of file */ firm_up_the_line(); /* this sets |limit| */ + line_catcode_table = DEFAULT_CAT_TABLE; } else if ((every_eof != null) && (!eof_seen[index])) { limit = first - 1; eof_seen[index] = true; /* fake one empty line */ @@ -635,7 +1020,13 @@ static next_line_retval next_line(void) /* update_terminal(); *//* show user that file has been read */ } force_eof = false; - end_file_reading(); + if (name == 21 || /* lua input */ + name == 19) { /* \scantextokens */ + end_file_reading(); + } else { + end_file_reading(); + check_outer_validity(); + } return next_line_restart; } if (inhibit_eol || end_line_char_inactive) @@ -695,18 +1086,22 @@ static boolean get_next_tokenlist(void) if (t >= cs_token_flag) { /* a control sequence token */ cur_cs = t - cs_token_flag; cur_cmd = eq_type(cur_cs); - if (cur_cmd == dont_expand) { /* @<Get the next token, suppressing expansion@> */ - /* The present point in the program is reached only when the |expand| - routine has inserted a special marker into the input. In this special - case, |info(loc)| is known to be a control sequence token, and |link(loc)=null|. - */ - cur_cs = info(loc) - cs_token_flag; - loc = null; - cur_cmd = eq_type(cur_cs); - if (cur_cmd > max_command) { - cur_cmd = relax; - cur_chr = no_expand_flag; - return true; + if (cur_cmd >= outer_call_cmd) { + if (cur_cmd == dont_expand_cmd) { /* @<Get the next token, suppressing expansion@> */ + /* The present point in the program is reached only when the |expand| + routine has inserted a special marker into the input. In this special + case, |info(loc)| is known to be a control sequence token, and |link(loc)=null|. + */ + cur_cs = info(loc) - cs_token_flag; + loc = null; + cur_cmd = eq_type(cur_cs); + if (cur_cmd > max_command_cmd) { + cur_cmd = relax_cmd; + cur_chr = no_expand_flag; + return true; + } + } else { + check_outer_validity(); } } cur_chr = equiv(cur_cs); @@ -714,13 +1109,13 @@ static boolean get_next_tokenlist(void) cur_cmd = t >> string_offset_bits; /* cur_cmd=t / string_offset; */ cur_chr = t & (string_offset - 1); /* cur_chr=t % string_offset; */ switch (cur_cmd) { - case left_brace: + case left_brace_cmd: align_state++; break; - case right_brace: + case right_brace_cmd: align_state--; break; - case out_param: /* @<Insert macro parameter and |goto restart|@>; */ + case out_param_cmd: /* @<Insert macro parameter and |goto restart|@>; */ begin_token_list(param_stack[param_start + cur_chr - 1], parameter); return false; break; @@ -753,7 +1148,7 @@ void get_next(void) } } /* @<If an alignment entry has just ended, take appropriate action@> */ - if ((cur_cmd == tab_mark || cur_cmd == car_ret) && align_state == 0) { + if ((cur_cmd == tab_mark_cmd || cur_cmd == car_ret_cmd) && align_state == 0) { insert_vj_template(); goto RESTART; } |