diff options
author | Akira Kakuto <kakuto@fuk.kindai.ac.jp> | 2017-02-09 22:44:03 +0000 |
---|---|---|
committer | Akira Kakuto <kakuto@fuk.kindai.ac.jp> | 2017-02-09 22:44:03 +0000 |
commit | f901b04fd7e232afdf1f7b5a3f49846e9f98e399 (patch) | |
tree | 0967ec4ef40a2e9dfa22f46727d0c62cc2770294 /Build/source/texk/web2c/luatexdir/tex | |
parent | 733aac09881d5da8a41e58c774daff3f7ce74fdc (diff) |
luatexdir: sync with the upstream
git-svn-id: svn://tug.org/texlive/trunk@43177 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/luatexdir/tex')
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/commands.w | 1757 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/dumpdata.w | 1052 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/equivalents.h | 3 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/maincontrol.w | 7277 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/texmath.w | 5138 | ||||
-rw-r--r-- | Build/source/texk/web2c/luatexdir/tex/texnodes.w | 7530 |
6 files changed, 11391 insertions, 11366 deletions
diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.w b/Build/source/texk/web2c/luatexdir/tex/commands.w index 04eba945b1c..c267904f0d9 100644 --- a/Build/source/texk/web2c/luatexdir/tex/commands.w +++ b/Build/source/texk/web2c/luatexdir/tex/commands.w @@ -1,878 +1,879 @@ -% commands.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -\def\eTeX{e-\TeX} - -@ @c - - -#include "ptexlib.h" - -@ The symbolic names for glue parameters are put into \TeX's hash table -by using the routine called |primitive|, defined below. Let us enter them -now, so that we don't have to list all those parameter names anywhere else. - -@c -void initialize_commands(void) -{ - - primitive_tex("lineskip", assign_glue_cmd, glue_base + line_skip_code, glue_base); - primitive_tex("baselineskip", assign_glue_cmd, glue_base + baseline_skip_code, glue_base); - primitive_tex("parskip", assign_glue_cmd, glue_base + par_skip_code, glue_base); - primitive_tex("abovedisplayskip", assign_glue_cmd, glue_base + above_display_skip_code, glue_base); - primitive_tex("belowdisplayskip", assign_glue_cmd, glue_base + below_display_skip_code, glue_base); - primitive_tex("abovedisplayshortskip", assign_glue_cmd, glue_base + above_display_short_skip_code, glue_base); - primitive_tex("belowdisplayshortskip", assign_glue_cmd, glue_base + below_display_short_skip_code, glue_base); - primitive_tex("leftskip", assign_glue_cmd, glue_base + left_skip_code, glue_base); - primitive_tex("rightskip", assign_glue_cmd, glue_base + right_skip_code, glue_base); - primitive_tex("topskip", assign_glue_cmd, glue_base + top_skip_code, glue_base); - primitive_tex("splittopskip", assign_glue_cmd, glue_base + split_top_skip_code, glue_base); - primitive_tex("tabskip", assign_glue_cmd, glue_base + tab_skip_code, glue_base); - primitive_tex("spaceskip", assign_glue_cmd, glue_base + space_skip_code, glue_base); - primitive_tex("xspaceskip", assign_glue_cmd, glue_base + xspace_skip_code, glue_base); - primitive_tex("parfillskip", assign_glue_cmd, glue_base + par_fill_skip_code, glue_base); - primitive_tex("thinmuskip", assign_mu_glue_cmd, glue_base + thin_mu_skip_code, glue_base + thin_mu_skip_code); - primitive_tex("medmuskip", assign_mu_glue_cmd, glue_base + med_mu_skip_code, glue_base + thin_mu_skip_code); - primitive_tex("thickmuskip", assign_mu_glue_cmd, glue_base + thick_mu_skip_code, glue_base + thin_mu_skip_code); - primitive_luatex("mathsurroundskip", assign_glue_cmd, glue_base + math_skip_code, glue_base); - primitive_luatex("mathsurroundmode", assign_int_cmd, int_base + math_skip_mode_code, int_base); - primitive_tex("output", assign_toks_cmd, output_routine_loc, local_base); - primitive_tex("everypar", assign_toks_cmd, every_par_loc, local_base); - primitive_tex("everymath", assign_toks_cmd, every_math_loc, local_base); - primitive_tex("everydisplay", assign_toks_cmd, every_display_loc, local_base); - primitive_tex("everyhbox", assign_toks_cmd, every_hbox_loc, local_base); - primitive_tex("everyvbox", assign_toks_cmd, every_vbox_loc, local_base); - primitive_tex("everyjob", assign_toks_cmd, every_job_loc, local_base); - primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base); - primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base); - - /* The integer parameter names must be entered into the hash table */ - - primitive_tex("pretolerance", assign_int_cmd, int_base + pretolerance_code, int_base); - primitive_tex("tolerance", assign_int_cmd, int_base + tolerance_code, int_base); - primitive_tex("linepenalty", assign_int_cmd, int_base + line_penalty_code, int_base); - primitive_tex("hyphenpenalty", assign_int_cmd, int_base + hyphen_penalty_code, int_base); - primitive_tex("exhyphenpenalty", assign_int_cmd, int_base + ex_hyphen_penalty_code, int_base); - primitive_tex("clubpenalty", assign_int_cmd, int_base + club_penalty_code, int_base); - primitive_tex("widowpenalty", assign_int_cmd, int_base + widow_penalty_code, int_base); - primitive_tex("displaywidowpenalty", assign_int_cmd, int_base + display_widow_penalty_code, int_base); - primitive_tex("brokenpenalty", assign_int_cmd, int_base + broken_penalty_code, int_base); - primitive_tex("binoppenalty", assign_int_cmd, int_base + bin_op_penalty_code, int_base); - primitive_tex("relpenalty", assign_int_cmd, int_base + rel_penalty_code, int_base); - primitive_tex("predisplaypenalty", assign_int_cmd, int_base + pre_display_penalty_code, int_base); - primitive_tex("postdisplaypenalty", assign_int_cmd, int_base + post_display_penalty_code, int_base); - primitive_tex("interlinepenalty", assign_int_cmd, int_base + inter_line_penalty_code, int_base); - primitive_tex("doublehyphendemerits", assign_int_cmd, int_base + double_hyphen_demerits_code, int_base); - primitive_tex("finalhyphendemerits", assign_int_cmd, int_base + final_hyphen_demerits_code, int_base); - primitive_tex("adjdemerits", assign_int_cmd, int_base + adj_demerits_code, int_base); - primitive_tex("mag", assign_int_cmd, int_base + mag_code, int_base); - primitive_tex("delimiterfactor", assign_int_cmd, int_base + delimiter_factor_code, int_base); - primitive_tex("looseness", assign_int_cmd, int_base + looseness_code, int_base); - primitive_tex("time", assign_int_cmd, int_base + time_code, int_base); - primitive_tex("day", assign_int_cmd, int_base + day_code, int_base); - primitive_tex("month", assign_int_cmd, int_base + month_code, int_base); - primitive_tex("year", assign_int_cmd, int_base + year_code, int_base); - primitive_tex("showboxbreadth", assign_int_cmd, int_base + show_box_breadth_code, int_base); - primitive_tex("showboxdepth", assign_int_cmd, int_base + show_box_depth_code, int_base); - primitive_tex("hbadness", assign_int_cmd, int_base + hbadness_code, int_base); - primitive_tex("vbadness", assign_int_cmd, int_base + vbadness_code, int_base); - primitive_tex("pausing", assign_int_cmd, int_base + pausing_code, int_base); - primitive_tex("tracingonline", assign_int_cmd, int_base + tracing_online_code, int_base); - primitive_tex("tracingmacros", assign_int_cmd, int_base + tracing_macros_code, int_base); - primitive_tex("tracingstats", assign_int_cmd, int_base + tracing_stats_code, int_base); - primitive_tex("tracingparagraphs", assign_int_cmd, int_base + tracing_paragraphs_code, int_base); - primitive_tex("tracingpages", assign_int_cmd, int_base + tracing_pages_code, int_base); - primitive_tex("tracingoutput", assign_int_cmd, int_base + tracing_output_code, int_base); - primitive_tex("tracinglostchars", assign_int_cmd, int_base + tracing_lost_chars_code, int_base); - primitive_tex("tracingcommands", assign_int_cmd, int_base + tracing_commands_code, int_base); - primitive_tex("tracingrestores", assign_int_cmd, int_base + tracing_restores_code, int_base); - primitive_tex("uchyph", assign_int_cmd, int_base + uc_hyph_code, int_base); - primitive_tex("outputpenalty", assign_int_cmd, int_base + output_penalty_code, int_base); - primitive_tex("maxdeadcycles", assign_int_cmd, int_base + max_dead_cycles_code, int_base); - primitive_tex("hangafter", assign_int_cmd, int_base + hang_after_code, int_base); - primitive_tex("floatingpenalty", assign_int_cmd, int_base + floating_penalty_code, int_base); - primitive_tex("globaldefs", assign_int_cmd, int_base + global_defs_code, int_base); - primitive_tex("fam", assign_int_cmd, int_base + cur_fam_code, int_base); - primitive_tex("escapechar", assign_int_cmd, int_base + escape_char_code, int_base); - primitive_tex("defaulthyphenchar", assign_int_cmd, int_base + default_hyphen_char_code, int_base); - primitive_tex("defaultskewchar", assign_int_cmd, int_base + default_skew_char_code, int_base); - primitive_tex("endlinechar", assign_int_cmd, int_base + end_line_char_code, int_base); - primitive_tex("newlinechar", assign_int_cmd, int_base + new_line_char_code, int_base); - primitive_tex("language", assign_int_cmd, int_base + language_code, int_base); - primitive_tex("setlanguage", assign_int_cmd, int_base + cur_lang_code, int_base); - primitive_tex("firstvalidlanguage", assign_int_cmd, int_base + first_valid_language_code, int_base); - primitive_tex("exhyphenchar", assign_int_cmd, int_base + ex_hyphen_char_code, int_base); - primitive_tex("lefthyphenmin", assign_int_cmd, int_base + left_hyphen_min_code, int_base); - primitive_tex("righthyphenmin", assign_int_cmd, int_base + right_hyphen_min_code, int_base); - primitive_tex("holdinginserts", assign_int_cmd, int_base + holding_inserts_code, int_base); - primitive_tex("errorcontextlines", assign_int_cmd, int_base + error_context_lines_code, int_base); - primitive_luatex("nokerns", assign_int_cmd, int_base + disable_kern_code, int_base); - primitive_luatex("noligs", assign_int_cmd, int_base + disable_lig_code, int_base); - primitive_luatex("nospaces", assign_int_cmd, int_base + disable_space_code, int_base); - primitive_luatex("catcodetable", assign_int_cmd, int_base + cat_code_table_code, int_base); - primitive_luatex("outputbox", assign_int_cmd, int_base + output_box_code, int_base); - primitive_luatex("outputmode", assign_int_cmd, int_base + output_mode_code, int_base); - primitive_luatex("adjustspacing", assign_int_cmd, int_base + adjust_spacing_code, int_base); - primitive_luatex("protrudechars", assign_int_cmd, int_base + protrude_chars_code, int_base); - primitive_luatex("tracingfonts", assign_int_cmd, int_base + tracing_fonts_code, int_base); - primitive_luatex("draftmode", assign_int_cmd, int_base + draft_mode_code, int_base); - primitive_tex("parindent", assign_dimen_cmd, dimen_base + par_indent_code, dimen_base); - primitive_tex("mathsurround", assign_dimen_cmd, dimen_base + math_surround_code, dimen_base); - primitive_tex("lineskiplimit", assign_dimen_cmd, dimen_base + line_skip_limit_code, dimen_base); - primitive_tex("hsize", assign_dimen_cmd, dimen_base + hsize_code, dimen_base); - primitive_tex("vsize", assign_dimen_cmd, dimen_base + vsize_code, dimen_base); - primitive_tex("maxdepth", assign_dimen_cmd, dimen_base + max_depth_code, dimen_base); - primitive_tex("splitmaxdepth", assign_dimen_cmd, dimen_base + split_max_depth_code, dimen_base); - primitive_tex("boxmaxdepth", assign_dimen_cmd, dimen_base + box_max_depth_code, dimen_base); - primitive_tex("hfuzz", assign_dimen_cmd, dimen_base + hfuzz_code, dimen_base); - primitive_tex("vfuzz", assign_dimen_cmd, dimen_base + vfuzz_code, dimen_base); - primitive_tex("delimitershortfall", assign_dimen_cmd, dimen_base + delimiter_shortfall_code, dimen_base); - primitive_tex("nulldelimiterspace", assign_dimen_cmd, dimen_base + null_delimiter_space_code, dimen_base); - primitive_tex("scriptspace", assign_dimen_cmd, dimen_base + script_space_code, dimen_base); - primitive_tex("predisplaysize", assign_dimen_cmd, dimen_base + pre_display_size_code, dimen_base); - primitive_tex("displaywidth", assign_dimen_cmd, dimen_base + display_width_code, dimen_base); - primitive_tex("displayindent", assign_dimen_cmd, dimen_base + display_indent_code, dimen_base); - primitive_tex("overfullrule", assign_dimen_cmd, dimen_base + overfull_rule_code, dimen_base); - primitive_tex("hangindent", assign_dimen_cmd, dimen_base + hang_indent_code, dimen_base); - primitive_tex("hoffset", assign_dimen_cmd, dimen_base + h_offset_code, dimen_base); - primitive_tex("voffset", assign_dimen_cmd, dimen_base + v_offset_code, dimen_base); - primitive_tex("emergencystretch", assign_dimen_cmd, dimen_base + emergency_stretch_code, dimen_base); - primitive_luatex("pagewidth", assign_dimen_cmd, dimen_base + page_width_code, dimen_base); - primitive_luatex("pageheight", assign_dimen_cmd, dimen_base + page_height_code, dimen_base); - primitive_luatex("pxdimen", assign_dimen_cmd, dimen_base + px_dimen_code, dimen_base); - - /* Many of \TeX's primitives need no |equiv|, since they are identifiable - by their |eq_type| alone. These primitives are loaded into the hash table - as follows: */ - - primitive_tex(" ", ex_space_cmd, 0, 0); - primitive_tex("/", ital_corr_cmd, 0, 0); - primitive_tex("accent", accent_cmd, 0, 0); - primitive_tex("advance", advance_cmd, 0, 0); - primitive_tex("afterassignment", after_assignment_cmd, 0, 0); - primitive_tex("aftergroup", after_group_cmd, 0, 0); - primitive_tex("begingroup", begin_group_cmd, 0, 0); - primitive_tex("char", char_num_cmd, 0, 0); - primitive_tex("csname", cs_name_cmd, 0, 0); - primitive_luatex("lastnamedcs", cs_name_cmd, 1, 0); - primitive_luatex("begincsname", cs_name_cmd, 2, 0); - primitive_tex("delimiter", delim_num_cmd, 0, 0); - primitive_luatex("Udelimiter", delim_num_cmd, 1, 0); - primitive_tex("divide", divide_cmd, 0, 0); - primitive_tex("endcsname", end_cs_name_cmd, 0, 0); - primitive_tex("endgroup", end_group_cmd, 0, 0); - cs_text(frozen_end_group) = maketexstring("endgroup"); - eqtb[frozen_end_group] = eqtb[cur_val]; - primitive_tex("expandafter", expand_after_cmd, 0, 0); - primitive_tex("font", def_font_cmd, 0, 0); - primitive_luatex("letterspacefont", letterspace_font_cmd, 0, 0); - primitive_luatex("expandglyphsinfont", normal_cmd, expand_font_code, 0); - primitive_luatex("copyfont", copy_font_cmd, 0, 0); - primitive_luatex("setfontid", set_font_id_cmd, 0, 0); - primitive_tex("fontdimen", assign_font_dimen_cmd, 0, 0); - primitive_tex("halign", halign_cmd, 0, 0); - primitive_tex("hrule", hrule_cmd, 0, 0); - primitive_luatex("nohrule", no_hrule_cmd, 0, 0); - primitive_tex("ignorespaces", ignore_spaces_cmd, 0, 0); - primitive_tex("insert", insert_cmd, 0, 0); - primitive_luatex("leftghost", char_ghost_cmd, 0, 0); - primitive_tex("mark", mark_cmd, 0, 0); - primitive_tex("mathaccent", math_accent_cmd, 0, 0); - primitive_luatex("Umathaccent", math_accent_cmd, 1, 0); - primitive_tex("mathchar", math_char_num_cmd, 0, 0); - primitive_luatex("Umathchar", math_char_num_cmd, 1, 0); - primitive_luatex("Umathcharnum", math_char_num_cmd, 2, 0); - primitive_tex("mathchoice", math_choice_cmd, 0, 0); - primitive_luatex("Ustack", math_choice_cmd, 1, 0); - primitive_tex("multiply", multiply_cmd, 0, 0); - primitive_tex("noalign", no_align_cmd, 0, 0); - primitive_tex("noboundary", boundary_cmd, 0, 0); - primitive_tex("boundary", boundary_cmd, 1, 0); - primitive_tex("protrusionboundary", boundary_cmd, 2, 0); - primitive_tex("wordboundary", boundary_cmd, 3, 0); - primitive_tex("noexpand", no_expand_cmd, 0, 0); - primitive_luatex("primitive", no_expand_cmd, 1, 0); - primitive_tex("nonscript", non_script_cmd, 0, 0); - primitive_tex("omit", omit_cmd, 0, 0); - primitive_tex("parshape", set_tex_shape_cmd, par_shape_loc, par_shape_loc); - primitive_tex("penalty", break_penalty_cmd, 0, 0); - primitive_tex("prevgraf", set_prev_graf_cmd, 0, 0); - primitive_tex("radical", radical_cmd, 0, 0); - primitive_luatex("Uradical", radical_cmd, 1, 0); - primitive_luatex("Uroot", radical_cmd, 2, 0); - primitive_luatex("Uunderdelimiter", radical_cmd, 3, 0); - primitive_luatex("Uoverdelimiter", radical_cmd, 4, 0); - primitive_luatex("Udelimiterunder", radical_cmd, 5, 0); - primitive_luatex("Udelimiterover", radical_cmd, 6, 0); - primitive_luatex("Uhextensible", radical_cmd, 7, 0); - primitive_tex("read", read_to_cs_cmd, 0, 0); - primitive_tex("relax", relax_cmd, too_big_char, too_big_char); - cs_text(frozen_relax) = maketexstring("relax"); - eqtb[frozen_relax] = eqtb[cur_val]; - primitive_luatex("rightghost", char_ghost_cmd, 1, 0); - primitive_tex("setbox", set_box_cmd, 0, 0); - primitive_tex("the", the_cmd, 0, 0); - primitive_luatex("toksapp", combine_toks_cmd, 0, 0); - primitive_luatex("tokspre", combine_toks_cmd, 1, 0); - primitive_luatex("etoksapp", combine_toks_cmd, 2, 0); - primitive_luatex("etokspre", combine_toks_cmd, 3, 0); - primitive_tex("toks", toks_register_cmd, 0, 0); - primitive_tex("vadjust", vadjust_cmd, 0, 0); - primitive_tex("valign", valign_cmd, 0, 0); - primitive_tex("vcenter", vcenter_cmd, 0, 0); - primitive_tex("vrule", vrule_cmd, 0, 0); - primitive_luatex("novrule", no_vrule_cmd, 0, 0); - primitive_tex("par", par_end_cmd, too_big_char, too_big_char); /* cf.\ |scan_file_name| */ - par_loc = cur_val; - par_token = cs_token_flag + par_loc; - @<Create a bunch of primitives@>; - @<Create the math param primitives@>; - @<Create another bunch of primitives@>; -} - - -@ These are in a separate module due to a CWEAVE limitation. - -@<Create a bunch of primitives@>= - - /* - The processing of \.{\\input} involves the |start_input| subroutine, - which will be declared later; the processing of \.{\\endinput} is trivial. - */ - - primitive_tex("input", input_cmd, 0, 0); - primitive_tex("endinput", input_cmd, 1, 0); - primitive_tex("topmark", top_bot_mark_cmd, top_mark_code, 0); - primitive_tex("firstmark", top_bot_mark_cmd, first_mark_code, 0); - primitive_tex("botmark", top_bot_mark_cmd, bot_mark_code, 0); - primitive_tex("splitfirstmark", top_bot_mark_cmd, split_first_mark_code, 0); - primitive_tex("splitbotmark", top_bot_mark_cmd, split_bot_mark_code, 0); - primitive_luatex("clearmarks", mark_cmd, clear_marks_code, 0); - primitive_etex("marks", mark_cmd, marks_code, 0); - primitive_etex("topmarks", top_bot_mark_cmd, top_mark_code + marks_code, 0); - primitive_etex("firstmarks", top_bot_mark_cmd, first_mark_code + marks_code, 0); - primitive_etex("botmarks", top_bot_mark_cmd, bot_mark_code + marks_code, 0); - primitive_etex("splitfirstmarks", top_bot_mark_cmd, split_first_mark_code + marks_code, 0); - primitive_etex("splitbotmarks", top_bot_mark_cmd, split_bot_mark_code + marks_code, 0); - - /* - The hash table is initialized with `\.{\\count}', `\.{\\attribute}', - `\.{\\dimen}', `\.{\\skip}', and `\.{\\muskip}' all having |register| - as their command code; they are distinguished by the |chr_code|, which - is either |int_val|, |attr_val|, |dimen_val|, |glue_val|, or |mu_val|. - */ - - primitive_tex("count", register_cmd, int_val_level, 0); - primitive_luatex("attribute", register_cmd, attr_val_level, 0); - primitive_tex("dimen", register_cmd, dimen_val_level, 0); - primitive_tex("skip", register_cmd, glue_val_level, 0); - primitive_tex("muskip", register_cmd, mu_val_level, 0); - - primitive_tex("spacefactor", set_aux_cmd, hmode, 0); - primitive_tex("prevdepth", set_aux_cmd, vmode, 0); - primitive_tex("deadcycles", set_page_int_cmd, 0, 0); - primitive_tex("insertpenalties", set_page_int_cmd, 1, 0); - primitive_tex("wd", set_box_dimen_cmd, width_offset, 0); - primitive_tex("ht", set_box_dimen_cmd, height_offset, 0); - primitive_tex("dp", set_box_dimen_cmd, depth_offset, 0); - primitive_tex("lastpenalty", last_item_cmd, lastpenalty_code, 0); - primitive_tex("lastkern", last_item_cmd, lastkern_code, 0); - primitive_tex("lastskip", last_item_cmd, lastskip_code, 0); - primitive_tex("inputlineno", last_item_cmd, input_line_no_code, 0); - primitive_tex("badness", last_item_cmd, badness_code, 0); - primitive_luatex("luatexversion", last_item_cmd, luatex_version_code, 0); - primitive_luatex("lastsavedboxresourceindex", last_item_cmd, last_saved_box_resource_index_code, 0); - primitive_luatex("lastsavedimageresourceindex", last_item_cmd, last_saved_image_resource_index_code, 0); - primitive_luatex("lastsavedimageresourcepages", last_item_cmd, last_saved_image_resource_pages_code, 0); - primitive_luatex("lastxpos", last_item_cmd, last_x_pos_code, 0); - primitive_luatex("lastypos", last_item_cmd, last_y_pos_code, 0); - primitive_luatex("randomseed", last_item_cmd, random_seed_code, 0); - - primitive_tex("number", convert_cmd, number_code, 0); - primitive_tex("romannumeral", convert_cmd, roman_numeral_code, 0); - primitive_tex("string", convert_cmd, string_code, 0); - primitive_tex("csstring", convert_cmd, cs_string_code, 0); - primitive_tex("meaning", convert_cmd, meaning_code, 0); - primitive_etex("eTeXVersion", convert_cmd, etex_code, 0); - primitive_tex("fontname", convert_cmd, font_name_code, 0); - primitive_luatex("fontid", convert_cmd, font_id_code, 0); - primitive_luatex("luatexrevision", convert_cmd, luatex_revision_code, 0); - primitive_luatex("luatexdatestamp", convert_cmd, luatex_date_code, 0); - primitive_luatex("luatexbanner", convert_cmd, luatex_banner_code, 0); - primitive_luatex("leftmarginkern", convert_cmd, left_margin_kern_code, 0); - primitive_luatex("rightmarginkern", convert_cmd, right_margin_kern_code, 0); - primitive_luatex("uniformdeviate", convert_cmd, uniform_deviate_code, 0); - primitive_luatex("normaldeviate", convert_cmd, normal_deviate_code, 0); - primitive_core("directlua", convert_cmd, lua_code, 0); - primitive_luatex("luafunction", convert_cmd, lua_function_code, 0); - primitive_luatex("luaescapestring", convert_cmd, lua_escape_string_code, 0); - primitive_luatex("mathstyle", convert_cmd, math_style_code, 0); - primitive_luatex("expanded", convert_cmd, expanded_code, 0); - primitive_tex("jobname", convert_cmd, job_name_code, 0); - primitive_luatex("formatname", convert_cmd, format_name_code, 0); - primitive_luatex("Uchar", convert_cmd, uchar_code, 0); - - primitive_luatex("Umathcharclass", convert_cmd, math_char_class_code, 0); - primitive_luatex("Umathcharfam", convert_cmd, math_char_fam_code, 0); - primitive_luatex("Umathcharslot", convert_cmd, math_char_slot_code, 0); - - primitive_tex("if", if_test_cmd, if_char_code, 0); - primitive_tex("ifcat", if_test_cmd, if_cat_code, 0); - primitive_tex("ifnum", if_test_cmd, if_int_code, 0); - primitive_tex("ifdim", if_test_cmd, if_dim_code, 0); - primitive_tex("ifodd", if_test_cmd, if_odd_code, 0); - primitive_tex("ifvmode", if_test_cmd, if_vmode_code, 0); - primitive_tex("ifhmode", if_test_cmd, if_hmode_code, 0); - primitive_tex("ifmmode", if_test_cmd, if_mmode_code, 0); - primitive_tex("ifinner", if_test_cmd, if_inner_code, 0); - primitive_tex("ifvoid", if_test_cmd, if_void_code, 0); - - primitive_tex("ifhbox", if_test_cmd, if_hbox_code, 0); - primitive_tex("ifvbox", if_test_cmd, if_vbox_code, 0); - primitive_tex("ifx", if_test_cmd, ifx_code, 0); - primitive_tex("ifeof", if_test_cmd, if_eof_code, 0); - primitive_tex("iftrue", if_test_cmd, if_true_code, 0); - primitive_tex("iffalse", if_test_cmd, if_false_code, 0); - primitive_tex("ifcase", if_test_cmd, if_case_code, 0); - primitive_luatex("ifprimitive", if_test_cmd, if_primitive_code, 0); - primitive_tex("fi", fi_or_else_cmd, fi_code, 0); - cs_text(frozen_fi) = maketexstring("fi"); - eqtb[frozen_fi] = eqtb[cur_val]; - primitive_tex("or", fi_or_else_cmd, or_code, 0); - primitive_tex("else", fi_or_else_cmd, else_code, 0); - - /* - \TeX\ always knows at least one font, namely the null font. It has no - characters, and its seven parameters are all equal to zero. - */ - - primitive_tex("nullfont", set_font_cmd, null_font, 0); - cs_text(frozen_null_font) = maketexstring("nullfont"); - eqtb[frozen_null_font] = eqtb[cur_val]; - - primitive_tex("span", tab_mark_cmd, span_code, tab_mark_cmd_code); - primitive_luatex("aligntab", tab_mark_cmd, tab_mark_cmd_code, tab_mark_cmd_code); - primitive_luatex("alignmark", mac_param_cmd, tab_mark_cmd_code, tab_mark_cmd_code); - primitive_tex("cr", car_ret_cmd, cr_code, cr_code); - cs_text(frozen_cr) = maketexstring("cr"); - eqtb[frozen_cr] = eqtb[cur_val]; - primitive_tex("crcr", car_ret_cmd, cr_cr_code, cr_code); - cs_text(frozen_end_template) = maketexstring("endtemplate"); - cs_text(frozen_endv) = maketexstring("endtemplate"); - set_eq_type(frozen_endv, endv_cmd); - set_equiv(frozen_endv, null_list); - set_eq_level(frozen_endv, level_one); - eqtb[frozen_end_template] = eqtb[frozen_endv]; - set_eq_type(frozen_end_template, end_template_cmd); - - primitive_tex("pagegoal", set_page_dimen_cmd, 0, 0); - primitive_tex("pagetotal", set_page_dimen_cmd, 1, 0); - primitive_tex("pagestretch", set_page_dimen_cmd, 2, 0); - primitive_tex("pagefilstretch", set_page_dimen_cmd, 3, 0); - primitive_tex("pagefillstretch", set_page_dimen_cmd, 4, 0); - primitive_tex("pagefilllstretch", set_page_dimen_cmd, 5, 0); - primitive_tex("pageshrink", set_page_dimen_cmd, 6, 0); - primitive_tex("pagedepth", set_page_dimen_cmd, 7, 0); - - /* - Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the - endgame, since both of them have `|stop|' as their command code. - */ - - primitive_tex("end", stop_cmd, 0, 0); - primitive_tex("dump", stop_cmd, 1, 0); - - primitive_tex("hskip", hskip_cmd, skip_code, 0); - primitive_tex("hfil", hskip_cmd, fil_code, 0); - primitive_tex("hfill", hskip_cmd, fill_code, 0); - primitive_tex("hss", hskip_cmd, ss_code, 0); - primitive_tex("hfilneg", hskip_cmd, fil_neg_code, 0); - primitive_tex("vskip", vskip_cmd, skip_code, 0); - primitive_tex("vfil", vskip_cmd, fil_code, 0); - primitive_tex("vfill", vskip_cmd, fill_code, 0); - primitive_tex("vss", vskip_cmd, ss_code, 0); - primitive_tex("vfilneg", vskip_cmd, fil_neg_code, 0); - primitive_tex("mskip", mskip_cmd, mskip_code, 0); - primitive_tex("kern", kern_cmd, explicit_kern, 0); - primitive_tex("mkern", mkern_cmd, mu_glue, 0); - primitive_tex("moveleft", hmove_cmd, 1, 0); - primitive_tex("moveright", hmove_cmd, 0, 0); - primitive_tex("raise", vmove_cmd, 1, 0); - primitive_tex("lower", vmove_cmd, 0, 0); - primitive_tex("box", make_box_cmd, box_code, 0); - primitive_tex("copy", make_box_cmd, copy_code, 0); - primitive_tex("lastbox", make_box_cmd, last_box_code, 0); - primitive_tex("vsplit", make_box_cmd, vsplit_code, 0); - primitive_tex("tpack", make_box_cmd, tpack_code, 0); - primitive_tex("vpack", make_box_cmd, vpack_code, 0); - primitive_tex("hpack", make_box_cmd, hpack_code, 0); - primitive_tex("vtop", make_box_cmd, vtop_code, 0); - primitive_tex("vbox", make_box_cmd, vtop_code + vmode, 0); - primitive_tex("hbox", make_box_cmd, vtop_code + hmode, 0); - primitive_tex("shipout", leader_ship_cmd, a_leaders - 1, 0); /* |ship_out_flag=leader_flag-1| */ - primitive_tex("leaders", leader_ship_cmd, a_leaders, 0); - primitive_tex("cleaders", leader_ship_cmd, c_leaders, 0); - primitive_tex("xleaders", leader_ship_cmd, x_leaders, 0); - primitive_luatex("gleaders", leader_ship_cmd, g_leaders, 0); - primitive_luatex("boxdir", assign_box_dir_cmd, 0, 0); - primitive_tex("indent", start_par_cmd, 1, 0); - primitive_tex("noindent", start_par_cmd, 0, 0); - primitive_luatex("quitvmode", start_par_cmd, 2, 0); - primitive_tex("unpenalty", remove_item_cmd, penalty_node, 0); - primitive_tex("unkern", remove_item_cmd, kern_node, 0); - primitive_tex("unskip", remove_item_cmd, glue_node, 0); - primitive_tex("unhbox", un_hbox_cmd, box_code, 0); - primitive_tex("unhcopy", un_hbox_cmd, copy_code, 0); - primitive_tex("unvbox", un_vbox_cmd, box_code, 0); - primitive_tex("unvcopy", un_vbox_cmd, copy_code, 0); - primitive_tex("-", discretionary_cmd, explicit_disc, 0); - primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0); - primitive_luatex("localleftbox", assign_local_box_cmd, 0, 0); - primitive_luatex("localrightbox", assign_local_box_cmd, 1, 0); - - primitive_luatex("Ustartmath", math_shift_cs_cmd, text_style, 0); - primitive_luatex("Ustopmath", math_shift_cs_cmd, cramped_text_style, 0); - primitive_luatex("Ustartdisplaymath", math_shift_cs_cmd, display_style, 0); - primitive_luatex("Ustopdisplaymath", math_shift_cs_cmd, cramped_display_style, 0); - primitive_tex("eqno", eq_no_cmd, 0, 0); - primitive_tex("leqno", eq_no_cmd, 1, 0); - primitive_tex("mathord", math_comp_cmd, ord_noad_type, 0); - primitive_tex("mathop", math_comp_cmd, op_noad_type_normal, 0); - primitive_tex("mathbin", math_comp_cmd, bin_noad_type, 0); - primitive_tex("mathrel", math_comp_cmd, rel_noad_type, 0); - primitive_tex("mathopen", math_comp_cmd, open_noad_type, 0); - primitive_tex("mathclose", math_comp_cmd, close_noad_type, 0); - primitive_tex("mathpunct", math_comp_cmd, punct_noad_type, 0); - primitive_tex("mathinner", math_comp_cmd, inner_noad_type, 0); - primitive_tex("underline", math_comp_cmd, under_noad_type, 0); - primitive_tex("overline", math_comp_cmd, over_noad_type, 0); - primitive_tex("displaylimits", limit_switch_cmd, op_noad_type_normal, 0); - primitive_tex("limits", limit_switch_cmd, op_noad_type_limits, 0); - primitive_tex("nolimits", limit_switch_cmd, op_noad_type_no_limits, 0); - primitive_tex("displaystyle", math_style_cmd, display_style, 0); - primitive_tex("textstyle", math_style_cmd, text_style, 0); - primitive_tex("scriptstyle", math_style_cmd, script_style, 0); - primitive_tex("scriptscriptstyle", math_style_cmd, script_script_style, 0); - primitive_luatex("crampeddisplaystyle", math_style_cmd, cramped_display_style, 0); - primitive_luatex("crampedtextstyle", math_style_cmd, cramped_text_style, 0); - primitive_luatex("crampedscriptstyle", math_style_cmd, cramped_script_style, 0); - primitive_luatex("crampedscriptscriptstyle", math_style_cmd, cramped_script_script_style, 0); - primitive_luatex("Usuperscript", super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd); - primitive_luatex("Usubscript", super_sub_script_cmd, sub_mark_cmd, sup_mark_cmd); - primitive_tex("above", above_cmd, above_code, 0); - primitive_tex("over", above_cmd, over_code, 0); - primitive_tex("atop", above_cmd, atop_code, 0); - primitive_luatex("Uskewed", above_cmd, skewed_code, 0); - primitive_tex("abovewithdelims", above_cmd, delimited_code + above_code, 0); - primitive_tex("overwithdelims", above_cmd, delimited_code + over_code, 0); - primitive_tex("atopwithdelims", above_cmd, delimited_code + atop_code, 0); - primitive_luatex("Uskewedwithdelims", above_cmd, delimited_code + skewed_code, 0); - primitive_tex("left", left_right_cmd, left_noad_side, 0); - primitive_tex("right", left_right_cmd, right_noad_side, 0); - primitive_tex("middle", left_right_cmd, middle_noad_side, 0); - primitive_tex("Uleft", left_right_cmd, 10+left_noad_side, 0); - primitive_tex("Uright", left_right_cmd, 10+right_noad_side, 0); - primitive_tex("Umiddle", left_right_cmd, 10+middle_noad_side, 0); - primitive_luatex("Uvextensible", left_right_cmd, 10+no_noad_side, 0); - cs_text(frozen_right) = maketexstring("right"); - eqtb[frozen_right] = eqtb[cur_val]; - - primitive_tex("long", prefix_cmd, 1, 0); - primitive_tex("outer", prefix_cmd, 2, 0); - primitive_tex("global", prefix_cmd, 4, 0); - primitive_tex("def", def_cmd, 0, 0); - primitive_tex("gdef", def_cmd, 1, 0); - primitive_tex("edef", def_cmd, 2, 0); - primitive_tex("xdef", def_cmd, 3, 0); - primitive_tex("let", let_cmd, normal, 0); - primitive_tex("futurelet", let_cmd, normal + 1, 0); - primitive_luatex("letcharcode", let_cmd, normal + 2, 0); - primitive_tex("chardef", shorthand_def_cmd, char_def_code, 0); - primitive_tex("mathchardef", shorthand_def_cmd, math_char_def_code, 0); - primitive_luatex("Umathchardef", shorthand_def_cmd, xmath_char_def_code, 0); - primitive_luatex("Umathcharnumdef", shorthand_def_cmd, umath_char_def_code, 0); - primitive_tex("countdef", shorthand_def_cmd, count_def_code, 0); - primitive_luatex("attributedef", shorthand_def_cmd, attribute_def_code, 0); - primitive_tex("dimendef", shorthand_def_cmd, dimen_def_code, 0); - primitive_tex("skipdef", shorthand_def_cmd, skip_def_code, 0); - primitive_tex("muskipdef", shorthand_def_cmd, mu_skip_def_code, 0); - primitive_tex("toksdef", shorthand_def_cmd, toks_def_code, 0); - primitive_tex("catcode", def_char_code_cmd, cat_code_base, cat_code_base); - primitive_tex("mathcode", def_char_code_cmd, math_code_base, cat_code_base); - primitive_tex("lccode", def_char_code_cmd, lc_code_base, cat_code_base); - primitive_tex("uccode", def_char_code_cmd, uc_code_base, cat_code_base); - primitive_tex("sfcode", def_char_code_cmd, sf_code_base, cat_code_base); - primitive_tex("delcode", def_del_code_cmd, del_code_base, del_code_base); - primitive_tex("textfont", def_family_cmd, text_size, 0); - primitive_tex("scriptfont", def_family_cmd, script_size, 0); - primitive_tex("scriptscriptfont", def_family_cmd, script_script_size, 0); - primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0); - primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0); - -@ These are in a separate module due to a CWEAVE limitation. - -@<Create the math param primitives@>= - primitive_luatex("Umathoperatorsize", set_math_param_cmd, math_param_operator_size, 0); - primitive_luatex("Umathoverbarkern", set_math_param_cmd, math_param_overbar_kern, 0); - primitive_luatex("Umathoverbarrule", set_math_param_cmd, math_param_overbar_rule, 0); - primitive_luatex("Umathoverbarvgap", set_math_param_cmd, math_param_overbar_vgap, 0); - primitive_luatex("Umathunderbarkern", set_math_param_cmd, math_param_underbar_kern, 0); - primitive_luatex("Umathunderbarrule", set_math_param_cmd, math_param_underbar_rule, 0); - primitive_luatex("Umathunderbarvgap", set_math_param_cmd, math_param_underbar_vgap, 0); - primitive_luatex("Umathradicalkern", set_math_param_cmd, math_param_radical_kern, 0); - primitive_luatex("Umathradicalrule", set_math_param_cmd, math_param_radical_rule, 0); - primitive_luatex("Umathradicalvgap", set_math_param_cmd, math_param_radical_vgap, 0); - primitive_luatex("Umathradicaldegreebefore", set_math_param_cmd, math_param_radical_degree_before, 0); - primitive_luatex("Umathradicaldegreeafter", set_math_param_cmd, math_param_radical_degree_after, 0); - primitive_luatex("Umathradicaldegreeraise", set_math_param_cmd, math_param_radical_degree_raise, 0); - primitive_luatex("Umathstackvgap", set_math_param_cmd, math_param_stack_vgap, 0); - primitive_luatex("Umathstacknumup", set_math_param_cmd, math_param_stack_num_up, 0); - primitive_luatex("Umathstackdenomdown", set_math_param_cmd, math_param_stack_denom_down, 0); - primitive_luatex("Umathfractionrule", set_math_param_cmd, math_param_fraction_rule, 0); - primitive_luatex("Umathfractionnumvgap", set_math_param_cmd, math_param_fraction_num_vgap, 0); - primitive_luatex("Umathfractionnumup", set_math_param_cmd, math_param_fraction_num_up, 0); - primitive_luatex("Umathfractiondenomvgap", set_math_param_cmd, math_param_fraction_denom_vgap, 0); - primitive_luatex("Umathfractiondenomdown", set_math_param_cmd, math_param_fraction_denom_down, 0); - primitive_luatex("Umathfractiondelsize", set_math_param_cmd, math_param_fraction_del_size, 0); - primitive_luatex("Umathskewedfractionvgap", set_math_param_cmd, math_param_skewed_fraction_vgap, 0); - primitive_luatex("Umathskewedfractionhgap", set_math_param_cmd, math_param_skewed_fraction_hgap, 0); - primitive_luatex("Umathlimitabovevgap", set_math_param_cmd, math_param_limit_above_vgap, 0); - primitive_luatex("Umathlimitabovebgap", set_math_param_cmd, math_param_limit_above_bgap, 0); - primitive_luatex("Umathlimitabovekern", set_math_param_cmd, math_param_limit_above_kern, 0); - primitive_luatex("Umathlimitbelowvgap", set_math_param_cmd, math_param_limit_below_vgap, 0); - primitive_luatex("Umathlimitbelowbgap", set_math_param_cmd, math_param_limit_below_bgap, 0); - primitive_luatex("Umathlimitbelowkern", set_math_param_cmd, math_param_limit_below_kern, 0); - primitive_luatex("Umathnolimitsubfactor", set_math_param_cmd, math_param_nolimit_sub_factor, 0); /* bonus */ - primitive_luatex("Umathnolimitsupfactor", set_math_param_cmd, math_param_nolimit_sup_factor, 0); /* bonus */ - primitive_luatex("Umathunderdelimitervgap", set_math_param_cmd, math_param_under_delimiter_vgap, 0); - primitive_luatex("Umathunderdelimiterbgap", set_math_param_cmd, math_param_under_delimiter_bgap, 0); - primitive_luatex("Umathoverdelimitervgap", set_math_param_cmd, math_param_over_delimiter_vgap, 0); - primitive_luatex("Umathoverdelimiterbgap", set_math_param_cmd, math_param_over_delimiter_bgap, 0); - primitive_luatex("Umathsubshiftdrop", set_math_param_cmd, math_param_sub_shift_drop, 0); - primitive_luatex("Umathsupshiftdrop", set_math_param_cmd, math_param_sup_shift_drop, 0); - primitive_luatex("Umathsubshiftdown", set_math_param_cmd, math_param_sub_shift_down, 0); - primitive_luatex("Umathsubsupshiftdown", set_math_param_cmd, math_param_sub_sup_shift_down, 0); - primitive_luatex("Umathsubtopmax", set_math_param_cmd, math_param_sub_top_max, 0); - primitive_luatex("Umathsupshiftup", set_math_param_cmd, math_param_sup_shift_up, 0); - primitive_luatex("Umathsupbottommin", set_math_param_cmd, math_param_sup_bottom_min, 0); - primitive_luatex("Umathsupsubbottommax", set_math_param_cmd, math_param_sup_sub_bottom_max, 0); - primitive_luatex("Umathsubsupvgap", set_math_param_cmd, math_param_subsup_vgap, 0); - primitive_luatex("Umathspaceafterscript", set_math_param_cmd, math_param_space_after_script, 0); - primitive_luatex("Umathconnectoroverlapmin", set_math_param_cmd, math_param_connector_overlap_min, 0); - primitive_luatex("Umathordordspacing", set_math_param_cmd, math_param_ord_ord_spacing, 0); - primitive_luatex("Umathordopspacing", set_math_param_cmd, math_param_ord_op_spacing, 0); - primitive_luatex("Umathordbinspacing", set_math_param_cmd, math_param_ord_bin_spacing, 0); - primitive_luatex("Umathordrelspacing", set_math_param_cmd, math_param_ord_rel_spacing, 0); - primitive_luatex("Umathordopenspacing", set_math_param_cmd, math_param_ord_open_spacing, 0); - primitive_luatex("Umathordclosespacing", set_math_param_cmd, math_param_ord_close_spacing, 0); - primitive_luatex("Umathordpunctspacing", set_math_param_cmd, math_param_ord_punct_spacing, 0); - primitive_luatex("Umathordinnerspacing", set_math_param_cmd, math_param_ord_inner_spacing, 0); - primitive_luatex("Umathopordspacing", set_math_param_cmd, math_param_op_ord_spacing, 0); - primitive_luatex("Umathopopspacing", set_math_param_cmd, math_param_op_op_spacing, 0); - primitive_luatex("Umathopbinspacing", set_math_param_cmd, math_param_op_bin_spacing, 0); - primitive_luatex("Umathoprelspacing", set_math_param_cmd, math_param_op_rel_spacing, 0); - primitive_luatex("Umathopopenspacing", set_math_param_cmd, math_param_op_open_spacing, 0); - primitive_luatex("Umathopclosespacing", set_math_param_cmd, math_param_op_close_spacing, 0); - primitive_luatex("Umathoppunctspacing", set_math_param_cmd, math_param_op_punct_spacing, 0); - primitive_luatex("Umathopinnerspacing", set_math_param_cmd, math_param_op_inner_spacing, 0); - primitive_luatex("Umathbinordspacing", set_math_param_cmd, math_param_bin_ord_spacing, 0); - primitive_luatex("Umathbinopspacing", set_math_param_cmd, math_param_bin_op_spacing, 0); - primitive_luatex("Umathbinbinspacing", set_math_param_cmd, math_param_bin_bin_spacing, 0); - primitive_luatex("Umathbinrelspacing", set_math_param_cmd, math_param_bin_rel_spacing, 0); - primitive_luatex("Umathbinopenspacing", set_math_param_cmd, math_param_bin_open_spacing, 0); - primitive_luatex("Umathbinclosespacing", set_math_param_cmd, math_param_bin_close_spacing, 0); - primitive_luatex("Umathbinpunctspacing", set_math_param_cmd, math_param_bin_punct_spacing, 0); - primitive_luatex("Umathbininnerspacing", set_math_param_cmd, math_param_bin_inner_spacing, 0); - primitive_luatex("Umathrelordspacing", set_math_param_cmd, math_param_rel_ord_spacing, 0); - primitive_luatex("Umathrelopspacing", set_math_param_cmd, math_param_rel_op_spacing, 0); - primitive_luatex("Umathrelbinspacing", set_math_param_cmd, math_param_rel_bin_spacing, 0); - primitive_luatex("Umathrelrelspacing", set_math_param_cmd, math_param_rel_rel_spacing, 0); - primitive_luatex("Umathrelopenspacing", set_math_param_cmd, math_param_rel_open_spacing, 0); - primitive_luatex("Umathrelclosespacing", set_math_param_cmd, math_param_rel_close_spacing, 0); - primitive_luatex("Umathrelpunctspacing", set_math_param_cmd, math_param_rel_punct_spacing, 0); - primitive_luatex("Umathrelinnerspacing", set_math_param_cmd, math_param_rel_inner_spacing, 0); - primitive_luatex("Umathopenordspacing", set_math_param_cmd, math_param_open_ord_spacing, 0); - primitive_luatex("Umathopenopspacing", set_math_param_cmd, math_param_open_op_spacing, 0); - primitive_luatex("Umathopenbinspacing", set_math_param_cmd, math_param_open_bin_spacing, 0); - primitive_luatex("Umathopenrelspacing", set_math_param_cmd, math_param_open_rel_spacing, 0); - primitive_luatex("Umathopenopenspacing", set_math_param_cmd, math_param_open_open_spacing, 0); - primitive_luatex("Umathopenclosespacing", set_math_param_cmd, math_param_open_close_spacing, 0); - primitive_luatex("Umathopenpunctspacing", set_math_param_cmd, math_param_open_punct_spacing, 0); - primitive_luatex("Umathopeninnerspacing", set_math_param_cmd, math_param_open_inner_spacing, 0); - primitive_luatex("Umathcloseordspacing", set_math_param_cmd, math_param_close_ord_spacing, 0); - primitive_luatex("Umathcloseopspacing", set_math_param_cmd, math_param_close_op_spacing, 0); - primitive_luatex("Umathclosebinspacing", set_math_param_cmd, math_param_close_bin_spacing, 0); - primitive_luatex("Umathcloserelspacing", set_math_param_cmd, math_param_close_rel_spacing, 0); - primitive_luatex("Umathcloseopenspacing", set_math_param_cmd, math_param_close_open_spacing, 0); - primitive_luatex("Umathcloseclosespacing", set_math_param_cmd, math_param_close_close_spacing, 0); - primitive_luatex("Umathclosepunctspacing", set_math_param_cmd, math_param_close_punct_spacing, 0); - primitive_luatex("Umathcloseinnerspacing", set_math_param_cmd, math_param_close_inner_spacing, 0); - primitive_luatex("Umathpunctordspacing", set_math_param_cmd, math_param_punct_ord_spacing, 0); - primitive_luatex("Umathpunctopspacing", set_math_param_cmd, math_param_punct_op_spacing, 0); - primitive_luatex("Umathpunctbinspacing", set_math_param_cmd, math_param_punct_bin_spacing, 0); - primitive_luatex("Umathpunctrelspacing", set_math_param_cmd, math_param_punct_rel_spacing, 0); - primitive_luatex("Umathpunctopenspacing", set_math_param_cmd, math_param_punct_open_spacing, 0); - primitive_luatex("Umathpunctclosespacing", set_math_param_cmd, math_param_punct_close_spacing, 0); - primitive_luatex("Umathpunctpunctspacing", set_math_param_cmd, math_param_punct_punct_spacing, 0); - primitive_luatex("Umathpunctinnerspacing", set_math_param_cmd, math_param_punct_inner_spacing, 0); - primitive_luatex("Umathinnerordspacing", set_math_param_cmd, math_param_inner_ord_spacing, 0); - primitive_luatex("Umathinneropspacing", set_math_param_cmd, math_param_inner_op_spacing, 0); - primitive_luatex("Umathinnerbinspacing", set_math_param_cmd, math_param_inner_bin_spacing, 0); - primitive_luatex("Umathinnerrelspacing", set_math_param_cmd, math_param_inner_rel_spacing, 0); - primitive_luatex("Umathinneropenspacing", set_math_param_cmd, math_param_inner_open_spacing, 0); - primitive_luatex("Umathinnerclosespacing", set_math_param_cmd, math_param_inner_close_spacing, 0); - primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, math_param_inner_punct_spacing, 0); - primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, math_param_inner_inner_spacing, 0); - -@ These are in a separate module due to a CWEAVE limitation. - -@<Create another bunch of primitives@>= - primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base, math_code_base); - primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base, del_code_base); - primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 1, math_code_base); - primitive_luatex("Udelcodenum", extdef_del_code_cmd, del_code_base + 1, del_code_base); - primitive_tex("hyphenation", hyph_data_cmd, 0, 0); - primitive_tex("patterns", hyph_data_cmd, 1, 0); - primitive_luatex("prehyphenchar", hyph_data_cmd, 2, 0); - primitive_luatex("posthyphenchar", hyph_data_cmd, 3, 0); - primitive_luatex("preexhyphenchar", hyph_data_cmd, 4, 0); - primitive_luatex("postexhyphenchar", hyph_data_cmd, 5, 0); - primitive_luatex("hyphenationmin", hyph_data_cmd, 6, 0); - primitive_luatex("hjcode", hyph_data_cmd, 7, 0); - primitive_tex("hyphenchar", assign_font_int_cmd, 0, 0); - primitive_tex("skewchar", assign_font_int_cmd, 1, 0); - primitive_luatex("lpcode", assign_font_int_cmd, lp_code_base, 0); - primitive_luatex("rpcode", assign_font_int_cmd, rp_code_base, 0); - primitive_luatex("efcode", assign_font_int_cmd, ef_code_base, 0); - primitive_luatex("tagcode", assign_font_int_cmd, tag_code, 0); - primitive_luatex("ignoreligaturesinfont", assign_font_int_cmd, no_lig_code, 0); - primitive_tex("batchmode", set_interaction_cmd, batch_mode, 0); - primitive_tex("nonstopmode", set_interaction_cmd, nonstop_mode, 0); - primitive_tex("scrollmode", set_interaction_cmd, scroll_mode, 0); - primitive_tex("errorstopmode", set_interaction_cmd, error_stop_mode, 0); - primitive_tex("openin", in_stream_cmd, 1, 0); - primitive_tex("closein", in_stream_cmd, 0, 0); - primitive_tex("message", message_cmd, 0, 0); - primitive_tex("errmessage", message_cmd, 1, 0); - primitive_tex("lowercase", case_shift_cmd, lc_code_base, lc_code_base); - primitive_tex("uppercase", case_shift_cmd, uc_code_base, lc_code_base); - primitive_tex("show", xray_cmd, show_code, 0); - primitive_tex("showbox", xray_cmd, show_box_code, 0); - primitive_tex("showthe", xray_cmd, show_the_code, 0); - primitive_tex("showlists", xray_cmd, show_lists, 0); - - primitive_tex("openout", extension_cmd, open_code, 0); - primitive_tex("write", extension_cmd, write_code, 0); - write_loc = cur_val; - primitive_tex("closeout", extension_cmd, close_code, 0); - primitive_tex("special", extension_cmd, special_code, 0); - cs_text(frozen_special) = maketexstring("special"); - eqtb[frozen_special] = eqtb[cur_val]; - primitive_tex("immediate", extension_cmd, immediate_code, 0); - primitive_luatex("localinterlinepenalty", assign_int_cmd, int_base + local_inter_line_penalty_code, int_base); - primitive_luatex("localbrokenpenalty", assign_int_cmd, int_base + local_broken_penalty_code, int_base); - primitive_luatex("pagedir", assign_dir_cmd, int_base + page_direction_code, dir_base); - primitive_luatex("bodydir", assign_dir_cmd, int_base + body_direction_code, dir_base); - primitive_luatex("pardir", assign_dir_cmd, int_base + par_direction_code, dir_base); - primitive_luatex("textdir", assign_dir_cmd, int_base + text_direction_code, dir_base); - primitive_luatex("mathdir", assign_dir_cmd, int_base + math_direction_code, dir_base); - primitive_luatex("linedir", assign_dir_cmd, int_base + line_direction_code, dir_base); - primitive_luatex("pageleftoffset", assign_dimen_cmd, dimen_base + page_left_offset_code, dimen_base); - primitive_luatex("pagetopoffset", assign_dimen_cmd, dimen_base + page_top_offset_code, dimen_base); - primitive_luatex("pagerightoffset", assign_dimen_cmd, dimen_base + page_right_offset_code, dimen_base); - primitive_luatex("pagebottomoffset", assign_dimen_cmd, dimen_base + page_bottom_offset_code, dimen_base); - primitive_luatex("saveboxresource", extension_cmd, save_box_resource_code, 0); - primitive_luatex("useboxresource", extension_cmd, use_box_resource_code, 0); - primitive_luatex("saveimageresource", extension_cmd, save_image_resource_code, 0); - primitive_luatex("useimageresource", extension_cmd, use_image_resource_code, 0); - primitive_luatex("savepos", normal_cmd, save_pos_code, 0); - primitive_luatex("savecatcodetable", normal_cmd, save_cat_code_table_code, 0); - primitive_luatex("initcatcodetable", normal_cmd, init_cat_code_table_code, 0); - primitive_luatex("setrandomseed", normal_cmd, set_random_seed_code, 0); - primitive_luatex("latelua", normal_cmd, late_lua_code, 0); - primitive_luatex("insertht", convert_cmd, insert_ht_code, 0); - primitive_luatex("dviextension", extension_cmd, dvi_extension_code, 0); - primitive_luatex("dvifeedback", feedback_cmd, dvi_feedback_code, 0); - primitive_luatex("dvivariable", variable_cmd, dvi_variable_code, 0); - primitive_luatex("pdfextension", extension_cmd, pdf_extension_code, 0); - primitive_luatex("pdffeedback", feedback_cmd, pdf_feedback_code, 0); - primitive_luatex("pdfvariable", variable_cmd, pdf_variable_code, 0); - primitive_luatex("mathoption", option_cmd, math_option_code, 0); - - /* - some of the internal integer parameters are not associated with actual - primitives at all. - */ - - primitive_no("nolocalwhatsits", assign_int_cmd, int_base + no_local_whatsits_code, int_base); - primitive_no("nolocaldirs", assign_int_cmd, int_base + no_local_dirs_code, int_base); - - -@ @c -void initialize_etex_commands(void) -{ - primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0); - primitive_etex("eTeXversion", last_item_cmd, eTeX_version_code, 0); - primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, 0); - primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0); - - /* - First we implement the additional \eTeX\ parameters in the table of equivalents. - */ - - primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base); - primitive_etex("tracingassigns", assign_int_cmd, int_base + tracing_assigns_code, int_base); - primitive_etex("tracinggroups", assign_int_cmd, int_base + tracing_groups_code, int_base); - primitive_etex("tracingifs", assign_int_cmd, int_base + tracing_ifs_code, int_base); - primitive_etex("tracingscantokens", assign_int_cmd, int_base + tracing_scan_tokens_code, int_base); - primitive_etex("tracingnesting", assign_int_cmd, int_base + tracing_nesting_code, int_base); - primitive_etex("predisplaydirection", assign_int_cmd, int_base + pre_display_direction_code, int_base); - primitive_etex("lastlinefit", assign_int_cmd, int_base + last_line_fit_code, int_base); - primitive_etex("savingvdiscards", assign_int_cmd, int_base + saving_vdiscards_code, int_base); - primitive_etex("savinghyphcodes", assign_int_cmd, int_base + saving_hyph_codes_code, int_base); - primitive_luatex("suppressfontnotfounderror", assign_int_cmd, int_base + suppress_fontnotfound_error_code, int_base); - primitive_luatex("suppresslongerror", assign_int_cmd, int_base + suppress_long_error_code, int_base); - primitive_luatex("suppressmathparerror", assign_int_cmd, int_base + suppress_mathpar_error_code, int_base); - primitive_luatex("suppressifcsnameerror", assign_int_cmd, int_base + suppress_ifcsname_error_code, int_base); - primitive_luatex("suppressoutererror", assign_int_cmd, int_base + suppress_outer_error_code, int_base); - primitive_luatex("matheqnogapstep", assign_int_cmd, int_base + math_eqno_gap_step_code, int_base); - primitive_luatex("mathdisplayskipmode", assign_int_cmd, int_base + math_display_skip_mode_code, int_base); - primitive_luatex("mathscriptsmode", assign_int_cmd, int_base + math_scripts_mode_code, int_base); - primitive_luatex("mathnolimitsmode", assign_int_cmd, int_base + math_nolimits_mode_code, int_base); - primitive_luatex("mathrulesmode", assign_int_cmd, int_base + math_rules_mode_code, int_base); - primitive_luatex("mathrulesfam", assign_int_cmd, int_base + math_rules_fam_code, int_base); - primitive_luatex("synctex", assign_int_cmd, int_base + synctex_code, int_base); - - primitive_etex("currentgrouplevel", last_item_cmd, current_group_level_code, 0); - primitive_etex("currentgrouptype", last_item_cmd, current_group_type_code, 0); - - primitive_etex("currentiflevel", last_item_cmd, current_if_level_code, 0); - primitive_etex("currentiftype", last_item_cmd, current_if_type_code, 0); - primitive_etex("currentifbranch", last_item_cmd, current_if_branch_code, 0); - primitive_etex("fontcharwd", last_item_cmd, font_char_wd_code, 0); - primitive_etex("fontcharht", last_item_cmd, font_char_ht_code, 0); - primitive_etex("fontchardp", last_item_cmd, font_char_dp_code, 0); - primitive_etex("fontcharic", last_item_cmd, font_char_ic_code, 0); - - primitive_etex("parshapelength", last_item_cmd, par_shape_length_code, 0); - primitive_etex("parshapeindent", last_item_cmd, par_shape_indent_code, 0); - primitive_etex("parshapedimen", last_item_cmd, par_shape_dimen_code, 0); - - primitive_luatex("shapemode", assign_int_cmd, int_base + shape_mode_code, int_base); - primitive_luatex("hyphenationbounds", assign_int_cmd, int_base + hyphenation_bounds_code, int_base); - - primitive_etex("showgroups", xray_cmd, show_groups, 0); - - /* - The \.{\\showtokens} command displays a token list. - */ - - primitive_etex("showtokens", xray_cmd, show_tokens, 0); - - /* - The \.{\\unexpanded} primitive prevents expansion of tokens much as - the result from \.{\\the} applied to a token variable. The - \.{\\detokenize} primitive converts a token list into a list of - character tokens much as if the token list were written to a file. We - use the fact that the command modifiers for \.{\\unexpanded} and - \.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe} - are even. - */ - - primitive_etex("unexpanded", the_cmd, 1, 0); - primitive_etex("detokenize", the_cmd, show_tokens, 0); - - /* - The \.{\\showifs} command displays all currently active conditionals. - */ - - primitive_etex("showifs", xray_cmd, show_ifs, 0); - - /* - The \.{\\interactionmode} primitive allows to query and set the interaction mode. - */ - - primitive_etex("interactionmode", set_page_int_cmd, 2, 0); - - /* - The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive. - */ - - primitive_etex("scantokens", input_cmd, 2, 0); - primitive_luatex("scantextokens", input_cmd, 3, 0); - - primitive_etex("readline", read_to_cs_cmd, 1, 0); - - primitive_etex("unless", expand_after_cmd, 1, 0); - primitive_etex("ifdefined", if_test_cmd, if_def_code, 0); - primitive_etex("ifcsname", if_test_cmd, if_cs_code, 0); - primitive_etex("iffontchar", if_test_cmd, if_font_char_code, 0); - primitive_luatex("ifincsname", if_test_cmd, if_in_csname_code, 0); - primitive_luatex("ifabsnum", if_test_cmd, if_abs_num_code, 0); - primitive_luatex("ifabsdim", if_test_cmd, if_abs_dim_code, 0); - - /* - The |protected| feature of \eTeX\ defines the \.{\\protected} prefix - command for macro definitions. Such macros are protected against - expansions when lists of expanded tokens are built, e.g., for \.{\\edef} - or during \.{\\write}. - */ - - primitive_etex("protected", prefix_cmd, 8, 0); - - /* - Here are the additional \eTeX\ primitives for expressions. - */ - - primitive_etex("numexpr", last_item_cmd, eTeX_expr - int_val_level + int_val_level, 0); - primitive_etex("dimexpr", last_item_cmd, eTeX_expr - int_val_level + dimen_val_level, 0); - primitive_etex("glueexpr", last_item_cmd, eTeX_expr - int_val_level + glue_val_level, 0); - primitive_etex("muexpr", last_item_cmd, eTeX_expr - int_val_level + mu_val_level, 0); - - primitive_etex("gluestretchorder", last_item_cmd, glue_stretch_order_code, 0); - primitive_etex("glueshrinkorder", last_item_cmd, glue_shrink_order_code, 0); - primitive_etex("gluestretch", last_item_cmd, glue_stretch_code, 0); - primitive_etex("glueshrink", last_item_cmd, glue_shrink_code, 0); - - primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0); - primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0); - - /* - The \.{\\pagediscards} and \.{\\splitdiscards} commands share the - command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are - distinguished by their |chr_code| values |last_box_code| and - |vsplit_code|. These |chr_code| values are larger than |box_code| and - |copy_code|. - */ - - primitive_etex("pagediscards", un_vbox_cmd, last_box_code, 0); - primitive_etex("splitdiscards", un_vbox_cmd, vsplit_code, 0); - - /* - The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties}, - and \.{\\displaywidowpenalties} commands allow to define arrays of - penalty values to be used instead of the corresponding single values. - */ - - primitive_etex("interlinepenalties", set_etex_shape_cmd, inter_line_penalties_loc, etex_pen_base); - primitive_etex("clubpenalties", set_etex_shape_cmd, club_penalties_loc, etex_pen_base); - primitive_etex("widowpenalties", set_etex_shape_cmd, widow_penalties_loc, etex_pen_base); - primitive_etex("displaywidowpenalties", set_etex_shape_cmd, display_widow_penalties_loc, etex_pen_base); - -} +% commands.w
+%
+% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+%
+% This file is part of LuaTeX.
+%
+% LuaTeX is free software; you can redistribute it and/or modify it under
+% the terms of the GNU General Public License as published by the Free
+% Software Foundation; either version 2 of the License, or (at your
+% option) any later version.
+%
+% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+% License for more details.
+%
+% You should have received a copy of the GNU General Public License along
+% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+\def\eTeX{e-\TeX}
+
+@ @c
+
+
+#include "ptexlib.h"
+
+@ The symbolic names for glue parameters are put into \TeX's hash table
+by using the routine called |primitive|, defined below. Let us enter them
+now, so that we don't have to list all those parameter names anywhere else.
+
+@c
+void initialize_commands(void)
+{
+
+ primitive_tex("lineskip", assign_glue_cmd, glue_base + line_skip_code, glue_base);
+ primitive_tex("baselineskip", assign_glue_cmd, glue_base + baseline_skip_code, glue_base);
+ primitive_tex("parskip", assign_glue_cmd, glue_base + par_skip_code, glue_base);
+ primitive_tex("abovedisplayskip", assign_glue_cmd, glue_base + above_display_skip_code, glue_base);
+ primitive_tex("belowdisplayskip", assign_glue_cmd, glue_base + below_display_skip_code, glue_base);
+ primitive_tex("abovedisplayshortskip", assign_glue_cmd, glue_base + above_display_short_skip_code, glue_base);
+ primitive_tex("belowdisplayshortskip", assign_glue_cmd, glue_base + below_display_short_skip_code, glue_base);
+ primitive_tex("leftskip", assign_glue_cmd, glue_base + left_skip_code, glue_base);
+ primitive_tex("rightskip", assign_glue_cmd, glue_base + right_skip_code, glue_base);
+ primitive_tex("topskip", assign_glue_cmd, glue_base + top_skip_code, glue_base);
+ primitive_tex("splittopskip", assign_glue_cmd, glue_base + split_top_skip_code, glue_base);
+ primitive_tex("tabskip", assign_glue_cmd, glue_base + tab_skip_code, glue_base);
+ primitive_tex("spaceskip", assign_glue_cmd, glue_base + space_skip_code, glue_base);
+ primitive_tex("xspaceskip", assign_glue_cmd, glue_base + xspace_skip_code, glue_base);
+ primitive_tex("parfillskip", assign_glue_cmd, glue_base + par_fill_skip_code, glue_base);
+ primitive_tex("thinmuskip", assign_mu_glue_cmd, glue_base + thin_mu_skip_code, glue_base + thin_mu_skip_code);
+ primitive_tex("medmuskip", assign_mu_glue_cmd, glue_base + med_mu_skip_code, glue_base + thin_mu_skip_code);
+ primitive_tex("thickmuskip", assign_mu_glue_cmd, glue_base + thick_mu_skip_code, glue_base + thin_mu_skip_code);
+ primitive_luatex("mathsurroundskip", assign_glue_cmd, glue_base + math_skip_code, glue_base);
+ primitive_luatex("mathsurroundmode", assign_int_cmd, int_base + math_skip_mode_code, int_base);
+ primitive_tex("output", assign_toks_cmd, output_routine_loc, local_base);
+ primitive_tex("everypar", assign_toks_cmd, every_par_loc, local_base);
+ primitive_tex("everymath", assign_toks_cmd, every_math_loc, local_base);
+ primitive_tex("everydisplay", assign_toks_cmd, every_display_loc, local_base);
+ primitive_tex("everyhbox", assign_toks_cmd, every_hbox_loc, local_base);
+ primitive_tex("everyvbox", assign_toks_cmd, every_vbox_loc, local_base);
+ primitive_tex("everyjob", assign_toks_cmd, every_job_loc, local_base);
+ primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base);
+ primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base);
+
+ /* The integer parameter names must be entered into the hash table */
+
+ primitive_tex("pretolerance", assign_int_cmd, int_base + pretolerance_code, int_base);
+ primitive_tex("tolerance", assign_int_cmd, int_base + tolerance_code, int_base);
+ primitive_tex("linepenalty", assign_int_cmd, int_base + line_penalty_code, int_base);
+ primitive_tex("hyphenpenalty", assign_int_cmd, int_base + hyphen_penalty_code, int_base);
+ primitive_tex("exhyphenpenalty", assign_int_cmd, int_base + ex_hyphen_penalty_code, int_base);
+ primitive_tex("clubpenalty", assign_int_cmd, int_base + club_penalty_code, int_base);
+ primitive_tex("widowpenalty", assign_int_cmd, int_base + widow_penalty_code, int_base);
+ primitive_tex("displaywidowpenalty", assign_int_cmd, int_base + display_widow_penalty_code, int_base);
+ primitive_tex("brokenpenalty", assign_int_cmd, int_base + broken_penalty_code, int_base);
+ primitive_tex("binoppenalty", assign_int_cmd, int_base + bin_op_penalty_code, int_base);
+ primitive_tex("relpenalty", assign_int_cmd, int_base + rel_penalty_code, int_base);
+ primitive_tex("predisplaypenalty", assign_int_cmd, int_base + pre_display_penalty_code, int_base);
+ primitive_tex("postdisplaypenalty", assign_int_cmd, int_base + post_display_penalty_code, int_base);
+ primitive_tex("interlinepenalty", assign_int_cmd, int_base + inter_line_penalty_code, int_base);
+ primitive_tex("doublehyphendemerits", assign_int_cmd, int_base + double_hyphen_demerits_code, int_base);
+ primitive_tex("finalhyphendemerits", assign_int_cmd, int_base + final_hyphen_demerits_code, int_base);
+ primitive_tex("adjdemerits", assign_int_cmd, int_base + adj_demerits_code, int_base);
+ primitive_tex("mag", assign_int_cmd, int_base + mag_code, int_base);
+ primitive_tex("delimiterfactor", assign_int_cmd, int_base + delimiter_factor_code, int_base);
+ primitive_tex("looseness", assign_int_cmd, int_base + looseness_code, int_base);
+ primitive_tex("time", assign_int_cmd, int_base + time_code, int_base);
+ primitive_tex("day", assign_int_cmd, int_base + day_code, int_base);
+ primitive_tex("month", assign_int_cmd, int_base + month_code, int_base);
+ primitive_tex("year", assign_int_cmd, int_base + year_code, int_base);
+ primitive_tex("showboxbreadth", assign_int_cmd, int_base + show_box_breadth_code, int_base);
+ primitive_tex("showboxdepth", assign_int_cmd, int_base + show_box_depth_code, int_base);
+ primitive_tex("hbadness", assign_int_cmd, int_base + hbadness_code, int_base);
+ primitive_tex("vbadness", assign_int_cmd, int_base + vbadness_code, int_base);
+ primitive_tex("pausing", assign_int_cmd, int_base + pausing_code, int_base);
+ primitive_tex("tracingonline", assign_int_cmd, int_base + tracing_online_code, int_base);
+ primitive_tex("tracingmacros", assign_int_cmd, int_base + tracing_macros_code, int_base);
+ primitive_tex("tracingstats", assign_int_cmd, int_base + tracing_stats_code, int_base);
+ primitive_tex("tracingparagraphs", assign_int_cmd, int_base + tracing_paragraphs_code, int_base);
+ primitive_tex("tracingpages", assign_int_cmd, int_base + tracing_pages_code, int_base);
+ primitive_tex("tracingoutput", assign_int_cmd, int_base + tracing_output_code, int_base);
+ primitive_tex("tracinglostchars", assign_int_cmd, int_base + tracing_lost_chars_code, int_base);
+ primitive_tex("tracingcommands", assign_int_cmd, int_base + tracing_commands_code, int_base);
+ primitive_tex("tracingrestores", assign_int_cmd, int_base + tracing_restores_code, int_base);
+ primitive_tex("uchyph", assign_int_cmd, int_base + uc_hyph_code, int_base);
+ primitive_tex("outputpenalty", assign_int_cmd, int_base + output_penalty_code, int_base);
+ primitive_tex("maxdeadcycles", assign_int_cmd, int_base + max_dead_cycles_code, int_base);
+ primitive_tex("hangafter", assign_int_cmd, int_base + hang_after_code, int_base);
+ primitive_tex("floatingpenalty", assign_int_cmd, int_base + floating_penalty_code, int_base);
+ primitive_tex("globaldefs", assign_int_cmd, int_base + global_defs_code, int_base);
+ primitive_tex("fam", assign_int_cmd, int_base + cur_fam_code, int_base);
+ primitive_tex("escapechar", assign_int_cmd, int_base + escape_char_code, int_base);
+ primitive_tex("defaulthyphenchar", assign_int_cmd, int_base + default_hyphen_char_code, int_base);
+ primitive_tex("defaultskewchar", assign_int_cmd, int_base + default_skew_char_code, int_base);
+ primitive_tex("endlinechar", assign_int_cmd, int_base + end_line_char_code, int_base);
+ primitive_tex("newlinechar", assign_int_cmd, int_base + new_line_char_code, int_base);
+ primitive_tex("language", assign_int_cmd, int_base + language_code, int_base);
+ primitive_tex("setlanguage", assign_int_cmd, int_base + cur_lang_code, int_base);
+ primitive_tex("firstvalidlanguage", assign_int_cmd, int_base + first_valid_language_code, int_base);
+ primitive_tex("exhyphenchar", assign_int_cmd, int_base + ex_hyphen_char_code, int_base);
+ primitive_tex("lefthyphenmin", assign_int_cmd, int_base + left_hyphen_min_code, int_base);
+ primitive_tex("righthyphenmin", assign_int_cmd, int_base + right_hyphen_min_code, int_base);
+ primitive_tex("holdinginserts", assign_int_cmd, int_base + holding_inserts_code, int_base);
+ primitive_tex("errorcontextlines", assign_int_cmd, int_base + error_context_lines_code, int_base);
+ primitive_luatex("nokerns", assign_int_cmd, int_base + disable_kern_code, int_base);
+ primitive_luatex("noligs", assign_int_cmd, int_base + disable_lig_code, int_base);
+ primitive_luatex("nospaces", assign_int_cmd, int_base + disable_space_code, int_base);
+ primitive_luatex("catcodetable", assign_int_cmd, int_base + cat_code_table_code, int_base);
+ primitive_luatex("outputbox", assign_int_cmd, int_base + output_box_code, int_base);
+ primitive_luatex("outputmode", assign_int_cmd, int_base + output_mode_code, int_base);
+ primitive_luatex("adjustspacing", assign_int_cmd, int_base + adjust_spacing_code, int_base);
+ primitive_luatex("protrudechars", assign_int_cmd, int_base + protrude_chars_code, int_base);
+ primitive_luatex("tracingfonts", assign_int_cmd, int_base + tracing_fonts_code, int_base);
+ primitive_luatex("draftmode", assign_int_cmd, int_base + draft_mode_code, int_base);
+ primitive_tex("parindent", assign_dimen_cmd, dimen_base + par_indent_code, dimen_base);
+ primitive_tex("mathsurround", assign_dimen_cmd, dimen_base + math_surround_code, dimen_base);
+ primitive_tex("lineskiplimit", assign_dimen_cmd, dimen_base + line_skip_limit_code, dimen_base);
+ primitive_tex("hsize", assign_dimen_cmd, dimen_base + hsize_code, dimen_base);
+ primitive_tex("vsize", assign_dimen_cmd, dimen_base + vsize_code, dimen_base);
+ primitive_tex("maxdepth", assign_dimen_cmd, dimen_base + max_depth_code, dimen_base);
+ primitive_tex("splitmaxdepth", assign_dimen_cmd, dimen_base + split_max_depth_code, dimen_base);
+ primitive_tex("boxmaxdepth", assign_dimen_cmd, dimen_base + box_max_depth_code, dimen_base);
+ primitive_tex("hfuzz", assign_dimen_cmd, dimen_base + hfuzz_code, dimen_base);
+ primitive_tex("vfuzz", assign_dimen_cmd, dimen_base + vfuzz_code, dimen_base);
+ primitive_tex("delimitershortfall", assign_dimen_cmd, dimen_base + delimiter_shortfall_code, dimen_base);
+ primitive_tex("nulldelimiterspace", assign_dimen_cmd, dimen_base + null_delimiter_space_code, dimen_base);
+ primitive_tex("scriptspace", assign_dimen_cmd, dimen_base + script_space_code, dimen_base);
+ primitive_tex("predisplaysize", assign_dimen_cmd, dimen_base + pre_display_size_code, dimen_base);
+ primitive_tex("displaywidth", assign_dimen_cmd, dimen_base + display_width_code, dimen_base);
+ primitive_tex("displayindent", assign_dimen_cmd, dimen_base + display_indent_code, dimen_base);
+ primitive_tex("overfullrule", assign_dimen_cmd, dimen_base + overfull_rule_code, dimen_base);
+ primitive_tex("hangindent", assign_dimen_cmd, dimen_base + hang_indent_code, dimen_base);
+ primitive_tex("hoffset", assign_dimen_cmd, dimen_base + h_offset_code, dimen_base);
+ primitive_tex("voffset", assign_dimen_cmd, dimen_base + v_offset_code, dimen_base);
+ primitive_tex("emergencystretch", assign_dimen_cmd, dimen_base + emergency_stretch_code, dimen_base);
+ primitive_luatex("pagewidth", assign_dimen_cmd, dimen_base + page_width_code, dimen_base);
+ primitive_luatex("pageheight", assign_dimen_cmd, dimen_base + page_height_code, dimen_base);
+ primitive_luatex("pxdimen", assign_dimen_cmd, dimen_base + px_dimen_code, dimen_base);
+ primitive_luatex("predisplaygapfactor", assign_int_cmd, int_base + math_pre_display_gap_factor_code, int_base);
+
+ /* Many of \TeX's primitives need no |equiv|, since they are identifiable
+ by their |eq_type| alone. These primitives are loaded into the hash table
+ as follows: */
+
+ primitive_tex(" ", ex_space_cmd, 0, 0);
+ primitive_tex("/", ital_corr_cmd, 0, 0);
+ primitive_tex("accent", accent_cmd, 0, 0);
+ primitive_tex("advance", advance_cmd, 0, 0);
+ primitive_tex("afterassignment", after_assignment_cmd, 0, 0);
+ primitive_tex("aftergroup", after_group_cmd, 0, 0);
+ primitive_tex("begingroup", begin_group_cmd, 0, 0);
+ primitive_tex("char", char_num_cmd, 0, 0);
+ primitive_tex("csname", cs_name_cmd, 0, 0);
+ primitive_luatex("lastnamedcs", cs_name_cmd, 1, 0);
+ primitive_luatex("begincsname", cs_name_cmd, 2, 0);
+ primitive_tex("delimiter", delim_num_cmd, 0, 0);
+ primitive_luatex("Udelimiter", delim_num_cmd, 1, 0);
+ primitive_tex("divide", divide_cmd, 0, 0);
+ primitive_tex("endcsname", end_cs_name_cmd, 0, 0);
+ primitive_tex("endgroup", end_group_cmd, 0, 0);
+ cs_text(frozen_end_group) = maketexstring("endgroup");
+ eqtb[frozen_end_group] = eqtb[cur_val];
+ primitive_tex("expandafter", expand_after_cmd, 0, 0);
+ primitive_tex("font", def_font_cmd, 0, 0);
+ primitive_luatex("letterspacefont", letterspace_font_cmd, 0, 0);
+ primitive_luatex("expandglyphsinfont", normal_cmd, expand_font_code, 0);
+ primitive_luatex("copyfont", copy_font_cmd, 0, 0);
+ primitive_luatex("setfontid", set_font_id_cmd, 0, 0);
+ primitive_tex("fontdimen", assign_font_dimen_cmd, 0, 0);
+ primitive_tex("halign", halign_cmd, 0, 0);
+ primitive_tex("hrule", hrule_cmd, 0, 0);
+ primitive_luatex("nohrule", no_hrule_cmd, 0, 0);
+ primitive_tex("ignorespaces", ignore_spaces_cmd, 0, 0);
+ primitive_tex("insert", insert_cmd, 0, 0);
+ primitive_luatex("leftghost", char_ghost_cmd, 0, 0);
+ primitive_tex("mark", mark_cmd, 0, 0);
+ primitive_tex("mathaccent", math_accent_cmd, 0, 0);
+ primitive_luatex("Umathaccent", math_accent_cmd, 1, 0);
+ primitive_tex("mathchar", math_char_num_cmd, 0, 0);
+ primitive_luatex("Umathchar", math_char_num_cmd, 1, 0);
+ primitive_luatex("Umathcharnum", math_char_num_cmd, 2, 0);
+ primitive_tex("mathchoice", math_choice_cmd, 0, 0);
+ primitive_luatex("Ustack", math_choice_cmd, 1, 0);
+ primitive_tex("multiply", multiply_cmd, 0, 0);
+ primitive_tex("noalign", no_align_cmd, 0, 0);
+ primitive_tex("noboundary", boundary_cmd, 0, 0);
+ primitive_tex("boundary", boundary_cmd, 1, 0);
+ primitive_tex("protrusionboundary", boundary_cmd, 2, 0);
+ primitive_tex("wordboundary", boundary_cmd, 3, 0);
+ primitive_tex("noexpand", no_expand_cmd, 0, 0);
+ primitive_luatex("primitive", no_expand_cmd, 1, 0);
+ primitive_tex("nonscript", non_script_cmd, 0, 0);
+ primitive_tex("omit", omit_cmd, 0, 0);
+ primitive_tex("parshape", set_tex_shape_cmd, par_shape_loc, par_shape_loc);
+ primitive_tex("penalty", break_penalty_cmd, 0, 0);
+ primitive_tex("prevgraf", set_prev_graf_cmd, 0, 0);
+ primitive_tex("radical", radical_cmd, 0, 0);
+ primitive_luatex("Uradical", radical_cmd, 1, 0);
+ primitive_luatex("Uroot", radical_cmd, 2, 0);
+ primitive_luatex("Uunderdelimiter", radical_cmd, 3, 0);
+ primitive_luatex("Uoverdelimiter", radical_cmd, 4, 0);
+ primitive_luatex("Udelimiterunder", radical_cmd, 5, 0);
+ primitive_luatex("Udelimiterover", radical_cmd, 6, 0);
+ primitive_luatex("Uhextensible", radical_cmd, 7, 0);
+ primitive_tex("read", read_to_cs_cmd, 0, 0);
+ primitive_tex("relax", relax_cmd, too_big_char, too_big_char);
+ cs_text(frozen_relax) = maketexstring("relax");
+ eqtb[frozen_relax] = eqtb[cur_val];
+ primitive_luatex("rightghost", char_ghost_cmd, 1, 0);
+ primitive_tex("setbox", set_box_cmd, 0, 0);
+ primitive_tex("the", the_cmd, 0, 0);
+ primitive_luatex("toksapp", combine_toks_cmd, 0, 0);
+ primitive_luatex("tokspre", combine_toks_cmd, 1, 0);
+ primitive_luatex("etoksapp", combine_toks_cmd, 2, 0);
+ primitive_luatex("etokspre", combine_toks_cmd, 3, 0);
+ primitive_tex("toks", toks_register_cmd, 0, 0);
+ primitive_tex("vadjust", vadjust_cmd, 0, 0);
+ primitive_tex("valign", valign_cmd, 0, 0);
+ primitive_tex("vcenter", vcenter_cmd, 0, 0);
+ primitive_tex("vrule", vrule_cmd, 0, 0);
+ primitive_luatex("novrule", no_vrule_cmd, 0, 0);
+ primitive_tex("par", par_end_cmd, too_big_char, too_big_char); /* cf.\ |scan_file_name| */
+ par_loc = cur_val;
+ par_token = cs_token_flag + par_loc;
+ @<Create a bunch of primitives@>;
+ @<Create the math param primitives@>;
+ @<Create another bunch of primitives@>;
+}
+
+
+@ These are in a separate module due to a CWEAVE limitation.
+
+@<Create a bunch of primitives@>=
+
+ /*
+ The processing of \.{\\input} involves the |start_input| subroutine,
+ which will be declared later; the processing of \.{\\endinput} is trivial.
+ */
+
+ primitive_tex("input", input_cmd, 0, 0);
+ primitive_tex("endinput", input_cmd, 1, 0);
+ primitive_tex("topmark", top_bot_mark_cmd, top_mark_code, 0);
+ primitive_tex("firstmark", top_bot_mark_cmd, first_mark_code, 0);
+ primitive_tex("botmark", top_bot_mark_cmd, bot_mark_code, 0);
+ primitive_tex("splitfirstmark", top_bot_mark_cmd, split_first_mark_code, 0);
+ primitive_tex("splitbotmark", top_bot_mark_cmd, split_bot_mark_code, 0);
+ primitive_luatex("clearmarks", mark_cmd, clear_marks_code, 0);
+ primitive_etex("marks", mark_cmd, marks_code, 0);
+ primitive_etex("topmarks", top_bot_mark_cmd, top_mark_code + marks_code, 0);
+ primitive_etex("firstmarks", top_bot_mark_cmd, first_mark_code + marks_code, 0);
+ primitive_etex("botmarks", top_bot_mark_cmd, bot_mark_code + marks_code, 0);
+ primitive_etex("splitfirstmarks", top_bot_mark_cmd, split_first_mark_code + marks_code, 0);
+ primitive_etex("splitbotmarks", top_bot_mark_cmd, split_bot_mark_code + marks_code, 0);
+
+ /*
+ The hash table is initialized with `\.{\\count}', `\.{\\attribute}',
+ `\.{\\dimen}', `\.{\\skip}', and `\.{\\muskip}' all having |register|
+ as their command code; they are distinguished by the |chr_code|, which
+ is either |int_val|, |attr_val|, |dimen_val|, |glue_val|, or |mu_val|.
+ */
+
+ primitive_tex("count", register_cmd, int_val_level, 0);
+ primitive_luatex("attribute", register_cmd, attr_val_level, 0);
+ primitive_tex("dimen", register_cmd, dimen_val_level, 0);
+ primitive_tex("skip", register_cmd, glue_val_level, 0);
+ primitive_tex("muskip", register_cmd, mu_val_level, 0);
+
+ primitive_tex("spacefactor", set_aux_cmd, hmode, 0);
+ primitive_tex("prevdepth", set_aux_cmd, vmode, 0);
+ primitive_tex("deadcycles", set_page_int_cmd, 0, 0);
+ primitive_tex("insertpenalties", set_page_int_cmd, 1, 0);
+ primitive_tex("wd", set_box_dimen_cmd, width_offset, 0);
+ primitive_tex("ht", set_box_dimen_cmd, height_offset, 0);
+ primitive_tex("dp", set_box_dimen_cmd, depth_offset, 0);
+ primitive_tex("lastpenalty", last_item_cmd, lastpenalty_code, 0);
+ primitive_tex("lastkern", last_item_cmd, lastkern_code, 0);
+ primitive_tex("lastskip", last_item_cmd, lastskip_code, 0);
+ primitive_tex("inputlineno", last_item_cmd, input_line_no_code, 0);
+ primitive_tex("badness", last_item_cmd, badness_code, 0);
+ primitive_luatex("luatexversion", last_item_cmd, luatex_version_code, 0);
+ primitive_luatex("lastsavedboxresourceindex", last_item_cmd, last_saved_box_resource_index_code, 0);
+ primitive_luatex("lastsavedimageresourceindex", last_item_cmd, last_saved_image_resource_index_code, 0);
+ primitive_luatex("lastsavedimageresourcepages", last_item_cmd, last_saved_image_resource_pages_code, 0);
+ primitive_luatex("lastxpos", last_item_cmd, last_x_pos_code, 0);
+ primitive_luatex("lastypos", last_item_cmd, last_y_pos_code, 0);
+ primitive_luatex("randomseed", last_item_cmd, random_seed_code, 0);
+
+ primitive_tex("number", convert_cmd, number_code, 0);
+ primitive_tex("romannumeral", convert_cmd, roman_numeral_code, 0);
+ primitive_tex("string", convert_cmd, string_code, 0);
+ primitive_tex("csstring", convert_cmd, cs_string_code, 0);
+ primitive_tex("meaning", convert_cmd, meaning_code, 0);
+ primitive_etex("eTeXVersion", convert_cmd, etex_code, 0);
+ primitive_tex("fontname", convert_cmd, font_name_code, 0);
+ primitive_luatex("fontid", convert_cmd, font_id_code, 0);
+ primitive_luatex("luatexrevision", convert_cmd, luatex_revision_code, 0);
+ primitive_luatex("luatexdatestamp", convert_cmd, luatex_date_code, 0);
+ primitive_luatex("luatexbanner", convert_cmd, luatex_banner_code, 0);
+ primitive_luatex("leftmarginkern", convert_cmd, left_margin_kern_code, 0);
+ primitive_luatex("rightmarginkern", convert_cmd, right_margin_kern_code, 0);
+ primitive_luatex("uniformdeviate", convert_cmd, uniform_deviate_code, 0);
+ primitive_luatex("normaldeviate", convert_cmd, normal_deviate_code, 0);
+ primitive_core("directlua", convert_cmd, lua_code, 0);
+ primitive_luatex("luafunction", convert_cmd, lua_function_code, 0);
+ primitive_luatex("luaescapestring", convert_cmd, lua_escape_string_code, 0);
+ primitive_luatex("mathstyle", convert_cmd, math_style_code, 0);
+ primitive_luatex("expanded", convert_cmd, expanded_code, 0);
+ primitive_tex("jobname", convert_cmd, job_name_code, 0);
+ primitive_luatex("formatname", convert_cmd, format_name_code, 0);
+ primitive_luatex("Uchar", convert_cmd, uchar_code, 0);
+
+ primitive_luatex("Umathcharclass", convert_cmd, math_char_class_code, 0);
+ primitive_luatex("Umathcharfam", convert_cmd, math_char_fam_code, 0);
+ primitive_luatex("Umathcharslot", convert_cmd, math_char_slot_code, 0);
+
+ primitive_tex("if", if_test_cmd, if_char_code, 0);
+ primitive_tex("ifcat", if_test_cmd, if_cat_code, 0);
+ primitive_tex("ifnum", if_test_cmd, if_int_code, 0);
+ primitive_tex("ifdim", if_test_cmd, if_dim_code, 0);
+ primitive_tex("ifodd", if_test_cmd, if_odd_code, 0);
+ primitive_tex("ifvmode", if_test_cmd, if_vmode_code, 0);
+ primitive_tex("ifhmode", if_test_cmd, if_hmode_code, 0);
+ primitive_tex("ifmmode", if_test_cmd, if_mmode_code, 0);
+ primitive_tex("ifinner", if_test_cmd, if_inner_code, 0);
+ primitive_tex("ifvoid", if_test_cmd, if_void_code, 0);
+
+ primitive_tex("ifhbox", if_test_cmd, if_hbox_code, 0);
+ primitive_tex("ifvbox", if_test_cmd, if_vbox_code, 0);
+ primitive_tex("ifx", if_test_cmd, ifx_code, 0);
+ primitive_tex("ifeof", if_test_cmd, if_eof_code, 0);
+ primitive_tex("iftrue", if_test_cmd, if_true_code, 0);
+ primitive_tex("iffalse", if_test_cmd, if_false_code, 0);
+ primitive_tex("ifcase", if_test_cmd, if_case_code, 0);
+ primitive_luatex("ifprimitive", if_test_cmd, if_primitive_code, 0);
+ primitive_tex("fi", fi_or_else_cmd, fi_code, 0);
+ cs_text(frozen_fi) = maketexstring("fi");
+ eqtb[frozen_fi] = eqtb[cur_val];
+ primitive_tex("or", fi_or_else_cmd, or_code, 0);
+ primitive_tex("else", fi_or_else_cmd, else_code, 0);
+
+ /*
+ \TeX\ always knows at least one font, namely the null font. It has no
+ characters, and its seven parameters are all equal to zero.
+ */
+
+ primitive_tex("nullfont", set_font_cmd, null_font, 0);
+ cs_text(frozen_null_font) = maketexstring("nullfont");
+ eqtb[frozen_null_font] = eqtb[cur_val];
+
+ primitive_tex("span", tab_mark_cmd, span_code, tab_mark_cmd_code);
+ primitive_luatex("aligntab", tab_mark_cmd, tab_mark_cmd_code, tab_mark_cmd_code);
+ primitive_luatex("alignmark", mac_param_cmd, tab_mark_cmd_code, tab_mark_cmd_code);
+ primitive_tex("cr", car_ret_cmd, cr_code, cr_code);
+ cs_text(frozen_cr) = maketexstring("cr");
+ eqtb[frozen_cr] = eqtb[cur_val];
+ primitive_tex("crcr", car_ret_cmd, cr_cr_code, cr_code);
+ cs_text(frozen_end_template) = maketexstring("endtemplate");
+ cs_text(frozen_endv) = maketexstring("endtemplate");
+ set_eq_type(frozen_endv, endv_cmd);
+ set_equiv(frozen_endv, null_list);
+ set_eq_level(frozen_endv, level_one);
+ eqtb[frozen_end_template] = eqtb[frozen_endv];
+ set_eq_type(frozen_end_template, end_template_cmd);
+
+ primitive_tex("pagegoal", set_page_dimen_cmd, 0, 0);
+ primitive_tex("pagetotal", set_page_dimen_cmd, 1, 0);
+ primitive_tex("pagestretch", set_page_dimen_cmd, 2, 0);
+ primitive_tex("pagefilstretch", set_page_dimen_cmd, 3, 0);
+ primitive_tex("pagefillstretch", set_page_dimen_cmd, 4, 0);
+ primitive_tex("pagefilllstretch", set_page_dimen_cmd, 5, 0);
+ primitive_tex("pageshrink", set_page_dimen_cmd, 6, 0);
+ primitive_tex("pagedepth", set_page_dimen_cmd, 7, 0);
+
+ /*
+ Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the
+ endgame, since both of them have `|stop|' as their command code.
+ */
+
+ primitive_tex("end", stop_cmd, 0, 0);
+ primitive_tex("dump", stop_cmd, 1, 0);
+
+ primitive_tex("hskip", hskip_cmd, skip_code, 0);
+ primitive_tex("hfil", hskip_cmd, fil_code, 0);
+ primitive_tex("hfill", hskip_cmd, fill_code, 0);
+ primitive_tex("hss", hskip_cmd, ss_code, 0);
+ primitive_tex("hfilneg", hskip_cmd, fil_neg_code, 0);
+ primitive_tex("vskip", vskip_cmd, skip_code, 0);
+ primitive_tex("vfil", vskip_cmd, fil_code, 0);
+ primitive_tex("vfill", vskip_cmd, fill_code, 0);
+ primitive_tex("vss", vskip_cmd, ss_code, 0);
+ primitive_tex("vfilneg", vskip_cmd, fil_neg_code, 0);
+ primitive_tex("mskip", mskip_cmd, mskip_code, 0);
+ primitive_tex("kern", kern_cmd, explicit_kern, 0);
+ primitive_tex("mkern", mkern_cmd, mu_glue, 0);
+ primitive_tex("moveleft", hmove_cmd, 1, 0);
+ primitive_tex("moveright", hmove_cmd, 0, 0);
+ primitive_tex("raise", vmove_cmd, 1, 0);
+ primitive_tex("lower", vmove_cmd, 0, 0);
+ primitive_tex("box", make_box_cmd, box_code, 0);
+ primitive_tex("copy", make_box_cmd, copy_code, 0);
+ primitive_tex("lastbox", make_box_cmd, last_box_code, 0);
+ primitive_tex("vsplit", make_box_cmd, vsplit_code, 0);
+ primitive_tex("tpack", make_box_cmd, tpack_code, 0);
+ primitive_tex("vpack", make_box_cmd, vpack_code, 0);
+ primitive_tex("hpack", make_box_cmd, hpack_code, 0);
+ primitive_tex("vtop", make_box_cmd, vtop_code, 0);
+ primitive_tex("vbox", make_box_cmd, vtop_code + vmode, 0);
+ primitive_tex("hbox", make_box_cmd, vtop_code + hmode, 0);
+ primitive_tex("shipout", leader_ship_cmd, a_leaders - 1, 0); /* |ship_out_flag=leader_flag-1| */
+ primitive_tex("leaders", leader_ship_cmd, a_leaders, 0);
+ primitive_tex("cleaders", leader_ship_cmd, c_leaders, 0);
+ primitive_tex("xleaders", leader_ship_cmd, x_leaders, 0);
+ primitive_luatex("gleaders", leader_ship_cmd, g_leaders, 0);
+ primitive_luatex("boxdir", assign_box_dir_cmd, 0, 0);
+ primitive_tex("indent", start_par_cmd, 1, 0);
+ primitive_tex("noindent", start_par_cmd, 0, 0);
+ primitive_luatex("quitvmode", start_par_cmd, 2, 0);
+ primitive_tex("unpenalty", remove_item_cmd, penalty_node, 0);
+ primitive_tex("unkern", remove_item_cmd, kern_node, 0);
+ primitive_tex("unskip", remove_item_cmd, glue_node, 0);
+ primitive_tex("unhbox", un_hbox_cmd, box_code, 0);
+ primitive_tex("unhcopy", un_hbox_cmd, copy_code, 0);
+ primitive_tex("unvbox", un_vbox_cmd, box_code, 0);
+ primitive_tex("unvcopy", un_vbox_cmd, copy_code, 0);
+ primitive_tex("-", discretionary_cmd, explicit_disc, 0);
+ primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0);
+ primitive_luatex("localleftbox", assign_local_box_cmd, 0, 0);
+ primitive_luatex("localrightbox", assign_local_box_cmd, 1, 0);
+
+ primitive_luatex("Ustartmath", math_shift_cs_cmd, text_style, 0);
+ primitive_luatex("Ustopmath", math_shift_cs_cmd, cramped_text_style, 0);
+ primitive_luatex("Ustartdisplaymath", math_shift_cs_cmd, display_style, 0);
+ primitive_luatex("Ustopdisplaymath", math_shift_cs_cmd, cramped_display_style, 0);
+ primitive_tex("eqno", eq_no_cmd, 0, 0);
+ primitive_tex("leqno", eq_no_cmd, 1, 0);
+ primitive_tex("mathord", math_comp_cmd, ord_noad_type, 0);
+ primitive_tex("mathop", math_comp_cmd, op_noad_type_normal, 0);
+ primitive_tex("mathbin", math_comp_cmd, bin_noad_type, 0);
+ primitive_tex("mathrel", math_comp_cmd, rel_noad_type, 0);
+ primitive_tex("mathopen", math_comp_cmd, open_noad_type, 0);
+ primitive_tex("mathclose", math_comp_cmd, close_noad_type, 0);
+ primitive_tex("mathpunct", math_comp_cmd, punct_noad_type, 0);
+ primitive_tex("mathinner", math_comp_cmd, inner_noad_type, 0);
+ primitive_tex("underline", math_comp_cmd, under_noad_type, 0);
+ primitive_tex("overline", math_comp_cmd, over_noad_type, 0);
+ primitive_tex("displaylimits", limit_switch_cmd, op_noad_type_normal, 0);
+ primitive_tex("limits", limit_switch_cmd, op_noad_type_limits, 0);
+ primitive_tex("nolimits", limit_switch_cmd, op_noad_type_no_limits, 0);
+ primitive_tex("displaystyle", math_style_cmd, display_style, 0);
+ primitive_tex("textstyle", math_style_cmd, text_style, 0);
+ primitive_tex("scriptstyle", math_style_cmd, script_style, 0);
+ primitive_tex("scriptscriptstyle", math_style_cmd, script_script_style, 0);
+ primitive_luatex("crampeddisplaystyle", math_style_cmd, cramped_display_style, 0);
+ primitive_luatex("crampedtextstyle", math_style_cmd, cramped_text_style, 0);
+ primitive_luatex("crampedscriptstyle", math_style_cmd, cramped_script_style, 0);
+ primitive_luatex("crampedscriptscriptstyle", math_style_cmd, cramped_script_script_style, 0);
+ primitive_luatex("Usuperscript", super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd);
+ primitive_luatex("Usubscript", super_sub_script_cmd, sub_mark_cmd, sup_mark_cmd);
+ primitive_tex("above", above_cmd, above_code, 0);
+ primitive_tex("over", above_cmd, over_code, 0);
+ primitive_tex("atop", above_cmd, atop_code, 0);
+ primitive_luatex("Uskewed", above_cmd, skewed_code, 0);
+ primitive_tex("abovewithdelims", above_cmd, delimited_code + above_code, 0);
+ primitive_tex("overwithdelims", above_cmd, delimited_code + over_code, 0);
+ primitive_tex("atopwithdelims", above_cmd, delimited_code + atop_code, 0);
+ primitive_luatex("Uskewedwithdelims", above_cmd, delimited_code + skewed_code, 0);
+ primitive_tex("left", left_right_cmd, left_noad_side, 0);
+ primitive_tex("right", left_right_cmd, right_noad_side, 0);
+ primitive_tex("middle", left_right_cmd, middle_noad_side, 0);
+ primitive_tex("Uleft", left_right_cmd, 10+left_noad_side, 0);
+ primitive_tex("Uright", left_right_cmd, 10+right_noad_side, 0);
+ primitive_tex("Umiddle", left_right_cmd, 10+middle_noad_side, 0);
+ primitive_luatex("Uvextensible", left_right_cmd, 10+no_noad_side, 0);
+ cs_text(frozen_right) = maketexstring("right");
+ eqtb[frozen_right] = eqtb[cur_val];
+
+ primitive_tex("long", prefix_cmd, 1, 0);
+ primitive_tex("outer", prefix_cmd, 2, 0);
+ primitive_tex("global", prefix_cmd, 4, 0);
+ primitive_tex("def", def_cmd, 0, 0);
+ primitive_tex("gdef", def_cmd, 1, 0);
+ primitive_tex("edef", def_cmd, 2, 0);
+ primitive_tex("xdef", def_cmd, 3, 0);
+ primitive_tex("let", let_cmd, normal, 0);
+ primitive_tex("futurelet", let_cmd, normal + 1, 0);
+ primitive_luatex("letcharcode", let_cmd, normal + 2, 0);
+ primitive_tex("chardef", shorthand_def_cmd, char_def_code, 0);
+ primitive_tex("mathchardef", shorthand_def_cmd, math_char_def_code, 0);
+ primitive_luatex("Umathchardef", shorthand_def_cmd, xmath_char_def_code, 0);
+ primitive_luatex("Umathcharnumdef", shorthand_def_cmd, umath_char_def_code, 0);
+ primitive_tex("countdef", shorthand_def_cmd, count_def_code, 0);
+ primitive_luatex("attributedef", shorthand_def_cmd, attribute_def_code, 0);
+ primitive_tex("dimendef", shorthand_def_cmd, dimen_def_code, 0);
+ primitive_tex("skipdef", shorthand_def_cmd, skip_def_code, 0);
+ primitive_tex("muskipdef", shorthand_def_cmd, mu_skip_def_code, 0);
+ primitive_tex("toksdef", shorthand_def_cmd, toks_def_code, 0);
+ primitive_tex("catcode", def_char_code_cmd, cat_code_base, cat_code_base);
+ primitive_tex("mathcode", def_char_code_cmd, math_code_base, cat_code_base);
+ primitive_tex("lccode", def_char_code_cmd, lc_code_base, cat_code_base);
+ primitive_tex("uccode", def_char_code_cmd, uc_code_base, cat_code_base);
+ primitive_tex("sfcode", def_char_code_cmd, sf_code_base, cat_code_base);
+ primitive_tex("delcode", def_del_code_cmd, del_code_base, del_code_base);
+ primitive_tex("textfont", def_family_cmd, text_size, 0);
+ primitive_tex("scriptfont", def_family_cmd, script_size, 0);
+ primitive_tex("scriptscriptfont", def_family_cmd, script_script_size, 0);
+ primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0);
+ primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0);
+
+@ These are in a separate module due to a CWEAVE limitation.
+
+@<Create the math param primitives@>=
+ primitive_luatex("Umathoperatorsize", set_math_param_cmd, math_param_operator_size, 0);
+ primitive_luatex("Umathoverbarkern", set_math_param_cmd, math_param_overbar_kern, 0);
+ primitive_luatex("Umathoverbarrule", set_math_param_cmd, math_param_overbar_rule, 0);
+ primitive_luatex("Umathoverbarvgap", set_math_param_cmd, math_param_overbar_vgap, 0);
+ primitive_luatex("Umathunderbarkern", set_math_param_cmd, math_param_underbar_kern, 0);
+ primitive_luatex("Umathunderbarrule", set_math_param_cmd, math_param_underbar_rule, 0);
+ primitive_luatex("Umathunderbarvgap", set_math_param_cmd, math_param_underbar_vgap, 0);
+ primitive_luatex("Umathradicalkern", set_math_param_cmd, math_param_radical_kern, 0);
+ primitive_luatex("Umathradicalrule", set_math_param_cmd, math_param_radical_rule, 0);
+ primitive_luatex("Umathradicalvgap", set_math_param_cmd, math_param_radical_vgap, 0);
+ primitive_luatex("Umathradicaldegreebefore", set_math_param_cmd, math_param_radical_degree_before, 0);
+ primitive_luatex("Umathradicaldegreeafter", set_math_param_cmd, math_param_radical_degree_after, 0);
+ primitive_luatex("Umathradicaldegreeraise", set_math_param_cmd, math_param_radical_degree_raise, 0);
+ primitive_luatex("Umathstackvgap", set_math_param_cmd, math_param_stack_vgap, 0);
+ primitive_luatex("Umathstacknumup", set_math_param_cmd, math_param_stack_num_up, 0);
+ primitive_luatex("Umathstackdenomdown", set_math_param_cmd, math_param_stack_denom_down, 0);
+ primitive_luatex("Umathfractionrule", set_math_param_cmd, math_param_fraction_rule, 0);
+ primitive_luatex("Umathfractionnumvgap", set_math_param_cmd, math_param_fraction_num_vgap, 0);
+ primitive_luatex("Umathfractionnumup", set_math_param_cmd, math_param_fraction_num_up, 0);
+ primitive_luatex("Umathfractiondenomvgap", set_math_param_cmd, math_param_fraction_denom_vgap, 0);
+ primitive_luatex("Umathfractiondenomdown", set_math_param_cmd, math_param_fraction_denom_down, 0);
+ primitive_luatex("Umathfractiondelsize", set_math_param_cmd, math_param_fraction_del_size, 0);
+ primitive_luatex("Umathskewedfractionvgap", set_math_param_cmd, math_param_skewed_fraction_vgap, 0);
+ primitive_luatex("Umathskewedfractionhgap", set_math_param_cmd, math_param_skewed_fraction_hgap, 0);
+ primitive_luatex("Umathlimitabovevgap", set_math_param_cmd, math_param_limit_above_vgap, 0);
+ primitive_luatex("Umathlimitabovebgap", set_math_param_cmd, math_param_limit_above_bgap, 0);
+ primitive_luatex("Umathlimitabovekern", set_math_param_cmd, math_param_limit_above_kern, 0);
+ primitive_luatex("Umathlimitbelowvgap", set_math_param_cmd, math_param_limit_below_vgap, 0);
+ primitive_luatex("Umathlimitbelowbgap", set_math_param_cmd, math_param_limit_below_bgap, 0);
+ primitive_luatex("Umathlimitbelowkern", set_math_param_cmd, math_param_limit_below_kern, 0);
+ primitive_luatex("Umathnolimitsubfactor", set_math_param_cmd, math_param_nolimit_sub_factor, 0); /* bonus */
+ primitive_luatex("Umathnolimitsupfactor", set_math_param_cmd, math_param_nolimit_sup_factor, 0); /* bonus */
+ primitive_luatex("Umathunderdelimitervgap", set_math_param_cmd, math_param_under_delimiter_vgap, 0);
+ primitive_luatex("Umathunderdelimiterbgap", set_math_param_cmd, math_param_under_delimiter_bgap, 0);
+ primitive_luatex("Umathoverdelimitervgap", set_math_param_cmd, math_param_over_delimiter_vgap, 0);
+ primitive_luatex("Umathoverdelimiterbgap", set_math_param_cmd, math_param_over_delimiter_bgap, 0);
+ primitive_luatex("Umathsubshiftdrop", set_math_param_cmd, math_param_sub_shift_drop, 0);
+ primitive_luatex("Umathsupshiftdrop", set_math_param_cmd, math_param_sup_shift_drop, 0);
+ primitive_luatex("Umathsubshiftdown", set_math_param_cmd, math_param_sub_shift_down, 0);
+ primitive_luatex("Umathsubsupshiftdown", set_math_param_cmd, math_param_sub_sup_shift_down, 0);
+ primitive_luatex("Umathsubtopmax", set_math_param_cmd, math_param_sub_top_max, 0);
+ primitive_luatex("Umathsupshiftup", set_math_param_cmd, math_param_sup_shift_up, 0);
+ primitive_luatex("Umathsupbottommin", set_math_param_cmd, math_param_sup_bottom_min, 0);
+ primitive_luatex("Umathsupsubbottommax", set_math_param_cmd, math_param_sup_sub_bottom_max, 0);
+ primitive_luatex("Umathsubsupvgap", set_math_param_cmd, math_param_subsup_vgap, 0);
+ primitive_luatex("Umathspaceafterscript", set_math_param_cmd, math_param_space_after_script, 0);
+ primitive_luatex("Umathconnectoroverlapmin", set_math_param_cmd, math_param_connector_overlap_min, 0);
+ primitive_luatex("Umathordordspacing", set_math_param_cmd, math_param_ord_ord_spacing, 0);
+ primitive_luatex("Umathordopspacing", set_math_param_cmd, math_param_ord_op_spacing, 0);
+ primitive_luatex("Umathordbinspacing", set_math_param_cmd, math_param_ord_bin_spacing, 0);
+ primitive_luatex("Umathordrelspacing", set_math_param_cmd, math_param_ord_rel_spacing, 0);
+ primitive_luatex("Umathordopenspacing", set_math_param_cmd, math_param_ord_open_spacing, 0);
+ primitive_luatex("Umathordclosespacing", set_math_param_cmd, math_param_ord_close_spacing, 0);
+ primitive_luatex("Umathordpunctspacing", set_math_param_cmd, math_param_ord_punct_spacing, 0);
+ primitive_luatex("Umathordinnerspacing", set_math_param_cmd, math_param_ord_inner_spacing, 0);
+ primitive_luatex("Umathopordspacing", set_math_param_cmd, math_param_op_ord_spacing, 0);
+ primitive_luatex("Umathopopspacing", set_math_param_cmd, math_param_op_op_spacing, 0);
+ primitive_luatex("Umathopbinspacing", set_math_param_cmd, math_param_op_bin_spacing, 0);
+ primitive_luatex("Umathoprelspacing", set_math_param_cmd, math_param_op_rel_spacing, 0);
+ primitive_luatex("Umathopopenspacing", set_math_param_cmd, math_param_op_open_spacing, 0);
+ primitive_luatex("Umathopclosespacing", set_math_param_cmd, math_param_op_close_spacing, 0);
+ primitive_luatex("Umathoppunctspacing", set_math_param_cmd, math_param_op_punct_spacing, 0);
+ primitive_luatex("Umathopinnerspacing", set_math_param_cmd, math_param_op_inner_spacing, 0);
+ primitive_luatex("Umathbinordspacing", set_math_param_cmd, math_param_bin_ord_spacing, 0);
+ primitive_luatex("Umathbinopspacing", set_math_param_cmd, math_param_bin_op_spacing, 0);
+ primitive_luatex("Umathbinbinspacing", set_math_param_cmd, math_param_bin_bin_spacing, 0);
+ primitive_luatex("Umathbinrelspacing", set_math_param_cmd, math_param_bin_rel_spacing, 0);
+ primitive_luatex("Umathbinopenspacing", set_math_param_cmd, math_param_bin_open_spacing, 0);
+ primitive_luatex("Umathbinclosespacing", set_math_param_cmd, math_param_bin_close_spacing, 0);
+ primitive_luatex("Umathbinpunctspacing", set_math_param_cmd, math_param_bin_punct_spacing, 0);
+ primitive_luatex("Umathbininnerspacing", set_math_param_cmd, math_param_bin_inner_spacing, 0);
+ primitive_luatex("Umathrelordspacing", set_math_param_cmd, math_param_rel_ord_spacing, 0);
+ primitive_luatex("Umathrelopspacing", set_math_param_cmd, math_param_rel_op_spacing, 0);
+ primitive_luatex("Umathrelbinspacing", set_math_param_cmd, math_param_rel_bin_spacing, 0);
+ primitive_luatex("Umathrelrelspacing", set_math_param_cmd, math_param_rel_rel_spacing, 0);
+ primitive_luatex("Umathrelopenspacing", set_math_param_cmd, math_param_rel_open_spacing, 0);
+ primitive_luatex("Umathrelclosespacing", set_math_param_cmd, math_param_rel_close_spacing, 0);
+ primitive_luatex("Umathrelpunctspacing", set_math_param_cmd, math_param_rel_punct_spacing, 0);
+ primitive_luatex("Umathrelinnerspacing", set_math_param_cmd, math_param_rel_inner_spacing, 0);
+ primitive_luatex("Umathopenordspacing", set_math_param_cmd, math_param_open_ord_spacing, 0);
+ primitive_luatex("Umathopenopspacing", set_math_param_cmd, math_param_open_op_spacing, 0);
+ primitive_luatex("Umathopenbinspacing", set_math_param_cmd, math_param_open_bin_spacing, 0);
+ primitive_luatex("Umathopenrelspacing", set_math_param_cmd, math_param_open_rel_spacing, 0);
+ primitive_luatex("Umathopenopenspacing", set_math_param_cmd, math_param_open_open_spacing, 0);
+ primitive_luatex("Umathopenclosespacing", set_math_param_cmd, math_param_open_close_spacing, 0);
+ primitive_luatex("Umathopenpunctspacing", set_math_param_cmd, math_param_open_punct_spacing, 0);
+ primitive_luatex("Umathopeninnerspacing", set_math_param_cmd, math_param_open_inner_spacing, 0);
+ primitive_luatex("Umathcloseordspacing", set_math_param_cmd, math_param_close_ord_spacing, 0);
+ primitive_luatex("Umathcloseopspacing", set_math_param_cmd, math_param_close_op_spacing, 0);
+ primitive_luatex("Umathclosebinspacing", set_math_param_cmd, math_param_close_bin_spacing, 0);
+ primitive_luatex("Umathcloserelspacing", set_math_param_cmd, math_param_close_rel_spacing, 0);
+ primitive_luatex("Umathcloseopenspacing", set_math_param_cmd, math_param_close_open_spacing, 0);
+ primitive_luatex("Umathcloseclosespacing", set_math_param_cmd, math_param_close_close_spacing, 0);
+ primitive_luatex("Umathclosepunctspacing", set_math_param_cmd, math_param_close_punct_spacing, 0);
+ primitive_luatex("Umathcloseinnerspacing", set_math_param_cmd, math_param_close_inner_spacing, 0);
+ primitive_luatex("Umathpunctordspacing", set_math_param_cmd, math_param_punct_ord_spacing, 0);
+ primitive_luatex("Umathpunctopspacing", set_math_param_cmd, math_param_punct_op_spacing, 0);
+ primitive_luatex("Umathpunctbinspacing", set_math_param_cmd, math_param_punct_bin_spacing, 0);
+ primitive_luatex("Umathpunctrelspacing", set_math_param_cmd, math_param_punct_rel_spacing, 0);
+ primitive_luatex("Umathpunctopenspacing", set_math_param_cmd, math_param_punct_open_spacing, 0);
+ primitive_luatex("Umathpunctclosespacing", set_math_param_cmd, math_param_punct_close_spacing, 0);
+ primitive_luatex("Umathpunctpunctspacing", set_math_param_cmd, math_param_punct_punct_spacing, 0);
+ primitive_luatex("Umathpunctinnerspacing", set_math_param_cmd, math_param_punct_inner_spacing, 0);
+ primitive_luatex("Umathinnerordspacing", set_math_param_cmd, math_param_inner_ord_spacing, 0);
+ primitive_luatex("Umathinneropspacing", set_math_param_cmd, math_param_inner_op_spacing, 0);
+ primitive_luatex("Umathinnerbinspacing", set_math_param_cmd, math_param_inner_bin_spacing, 0);
+ primitive_luatex("Umathinnerrelspacing", set_math_param_cmd, math_param_inner_rel_spacing, 0);
+ primitive_luatex("Umathinneropenspacing", set_math_param_cmd, math_param_inner_open_spacing, 0);
+ primitive_luatex("Umathinnerclosespacing", set_math_param_cmd, math_param_inner_close_spacing, 0);
+ primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, math_param_inner_punct_spacing, 0);
+ primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, math_param_inner_inner_spacing, 0);
+
+@ These are in a separate module due to a CWEAVE limitation.
+
+@<Create another bunch of primitives@>=
+ primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base, math_code_base);
+ primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base, del_code_base);
+ primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 1, math_code_base);
+ primitive_luatex("Udelcodenum", extdef_del_code_cmd, del_code_base + 1, del_code_base);
+ primitive_tex("hyphenation", hyph_data_cmd, 0, 0);
+ primitive_tex("patterns", hyph_data_cmd, 1, 0);
+ primitive_luatex("prehyphenchar", hyph_data_cmd, 2, 0);
+ primitive_luatex("posthyphenchar", hyph_data_cmd, 3, 0);
+ primitive_luatex("preexhyphenchar", hyph_data_cmd, 4, 0);
+ primitive_luatex("postexhyphenchar", hyph_data_cmd, 5, 0);
+ primitive_luatex("hyphenationmin", hyph_data_cmd, 6, 0);
+ primitive_luatex("hjcode", hyph_data_cmd, 7, 0);
+ primitive_tex("hyphenchar", assign_font_int_cmd, 0, 0);
+ primitive_tex("skewchar", assign_font_int_cmd, 1, 0);
+ primitive_luatex("lpcode", assign_font_int_cmd, lp_code_base, 0);
+ primitive_luatex("rpcode", assign_font_int_cmd, rp_code_base, 0);
+ primitive_luatex("efcode", assign_font_int_cmd, ef_code_base, 0);
+ primitive_luatex("tagcode", assign_font_int_cmd, tag_code, 0);
+ primitive_luatex("ignoreligaturesinfont", assign_font_int_cmd, no_lig_code, 0);
+ primitive_tex("batchmode", set_interaction_cmd, batch_mode, 0);
+ primitive_tex("nonstopmode", set_interaction_cmd, nonstop_mode, 0);
+ primitive_tex("scrollmode", set_interaction_cmd, scroll_mode, 0);
+ primitive_tex("errorstopmode", set_interaction_cmd, error_stop_mode, 0);
+ primitive_tex("openin", in_stream_cmd, 1, 0);
+ primitive_tex("closein", in_stream_cmd, 0, 0);
+ primitive_tex("message", message_cmd, 0, 0);
+ primitive_tex("errmessage", message_cmd, 1, 0);
+ primitive_tex("lowercase", case_shift_cmd, lc_code_base, lc_code_base);
+ primitive_tex("uppercase", case_shift_cmd, uc_code_base, lc_code_base);
+ primitive_tex("show", xray_cmd, show_code, 0);
+ primitive_tex("showbox", xray_cmd, show_box_code, 0);
+ primitive_tex("showthe", xray_cmd, show_the_code, 0);
+ primitive_tex("showlists", xray_cmd, show_lists, 0);
+
+ primitive_tex("openout", extension_cmd, open_code, 0);
+ primitive_tex("write", extension_cmd, write_code, 0);
+ write_loc = cur_val;
+ primitive_tex("closeout", extension_cmd, close_code, 0);
+ primitive_tex("special", extension_cmd, special_code, 0);
+ cs_text(frozen_special) = maketexstring("special");
+ eqtb[frozen_special] = eqtb[cur_val];
+ primitive_tex("immediate", extension_cmd, immediate_code, 0);
+ primitive_luatex("localinterlinepenalty", assign_int_cmd, int_base + local_inter_line_penalty_code, int_base);
+ primitive_luatex("localbrokenpenalty", assign_int_cmd, int_base + local_broken_penalty_code, int_base);
+ primitive_luatex("pagedir", assign_dir_cmd, int_base + page_direction_code, dir_base);
+ primitive_luatex("bodydir", assign_dir_cmd, int_base + body_direction_code, dir_base);
+ primitive_luatex("pardir", assign_dir_cmd, int_base + par_direction_code, dir_base);
+ primitive_luatex("textdir", assign_dir_cmd, int_base + text_direction_code, dir_base);
+ primitive_luatex("mathdir", assign_dir_cmd, int_base + math_direction_code, dir_base);
+ primitive_luatex("linedir", assign_dir_cmd, int_base + line_direction_code, dir_base);
+ primitive_luatex("pageleftoffset", assign_dimen_cmd, dimen_base + page_left_offset_code, dimen_base);
+ primitive_luatex("pagetopoffset", assign_dimen_cmd, dimen_base + page_top_offset_code, dimen_base);
+ primitive_luatex("pagerightoffset", assign_dimen_cmd, dimen_base + page_right_offset_code, dimen_base);
+ primitive_luatex("pagebottomoffset", assign_dimen_cmd, dimen_base + page_bottom_offset_code, dimen_base);
+ primitive_luatex("saveboxresource", extension_cmd, save_box_resource_code, 0);
+ primitive_luatex("useboxresource", extension_cmd, use_box_resource_code, 0);
+ primitive_luatex("saveimageresource", extension_cmd, save_image_resource_code, 0);
+ primitive_luatex("useimageresource", extension_cmd, use_image_resource_code, 0);
+ primitive_luatex("savepos", normal_cmd, save_pos_code, 0);
+ primitive_luatex("savecatcodetable", normal_cmd, save_cat_code_table_code, 0);
+ primitive_luatex("initcatcodetable", normal_cmd, init_cat_code_table_code, 0);
+ primitive_luatex("setrandomseed", normal_cmd, set_random_seed_code, 0);
+ primitive_luatex("latelua", normal_cmd, late_lua_code, 0);
+ primitive_luatex("insertht", convert_cmd, insert_ht_code, 0);
+ primitive_luatex("dviextension", extension_cmd, dvi_extension_code, 0);
+ primitive_luatex("dvifeedback", feedback_cmd, dvi_feedback_code, 0);
+ primitive_luatex("dvivariable", variable_cmd, dvi_variable_code, 0);
+ primitive_luatex("pdfextension", extension_cmd, pdf_extension_code, 0);
+ primitive_luatex("pdffeedback", feedback_cmd, pdf_feedback_code, 0);
+ primitive_luatex("pdfvariable", variable_cmd, pdf_variable_code, 0);
+ primitive_luatex("mathoption", option_cmd, math_option_code, 0);
+
+ /*
+ some of the internal integer parameters are not associated with actual
+ primitives at all.
+ */
+
+ primitive_no("nolocalwhatsits", assign_int_cmd, int_base + no_local_whatsits_code, int_base);
+ primitive_no("nolocaldirs", assign_int_cmd, int_base + no_local_dirs_code, int_base);
+
+
+@ @c
+void initialize_etex_commands(void)
+{
+ primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0);
+ primitive_etex("eTeXversion", last_item_cmd, eTeX_version_code, 0);
+ primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, 0);
+ primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0);
+
+ /*
+ First we implement the additional \eTeX\ parameters in the table of equivalents.
+ */
+
+ primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base);
+ primitive_etex("tracingassigns", assign_int_cmd, int_base + tracing_assigns_code, int_base);
+ primitive_etex("tracinggroups", assign_int_cmd, int_base + tracing_groups_code, int_base);
+ primitive_etex("tracingifs", assign_int_cmd, int_base + tracing_ifs_code, int_base);
+ primitive_etex("tracingscantokens", assign_int_cmd, int_base + tracing_scan_tokens_code, int_base);
+ primitive_etex("tracingnesting", assign_int_cmd, int_base + tracing_nesting_code, int_base);
+ primitive_etex("predisplaydirection", assign_int_cmd, int_base + pre_display_direction_code, int_base);
+ primitive_etex("lastlinefit", assign_int_cmd, int_base + last_line_fit_code, int_base);
+ primitive_etex("savingvdiscards", assign_int_cmd, int_base + saving_vdiscards_code, int_base);
+ primitive_etex("savinghyphcodes", assign_int_cmd, int_base + saving_hyph_codes_code, int_base);
+ primitive_luatex("suppressfontnotfounderror", assign_int_cmd, int_base + suppress_fontnotfound_error_code, int_base);
+ primitive_luatex("suppresslongerror", assign_int_cmd, int_base + suppress_long_error_code, int_base);
+ primitive_luatex("suppressmathparerror", assign_int_cmd, int_base + suppress_mathpar_error_code, int_base);
+ primitive_luatex("suppressifcsnameerror", assign_int_cmd, int_base + suppress_ifcsname_error_code, int_base);
+ primitive_luatex("suppressoutererror", assign_int_cmd, int_base + suppress_outer_error_code, int_base);
+ primitive_luatex("matheqnogapstep", assign_int_cmd, int_base + math_eqno_gap_step_code, int_base);
+ primitive_luatex("mathdisplayskipmode", assign_int_cmd, int_base + math_display_skip_mode_code, int_base);
+ primitive_luatex("mathscriptsmode", assign_int_cmd, int_base + math_scripts_mode_code, int_base);
+ primitive_luatex("mathnolimitsmode", assign_int_cmd, int_base + math_nolimits_mode_code, int_base);
+ primitive_luatex("mathrulesmode", assign_int_cmd, int_base + math_rules_mode_code, int_base);
+ primitive_luatex("mathrulesfam", assign_int_cmd, int_base + math_rules_fam_code, int_base);
+ primitive_luatex("synctex", assign_int_cmd, int_base + synctex_code, int_base);
+
+ primitive_etex("currentgrouplevel", last_item_cmd, current_group_level_code, 0);
+ primitive_etex("currentgrouptype", last_item_cmd, current_group_type_code, 0);
+
+ primitive_etex("currentiflevel", last_item_cmd, current_if_level_code, 0);
+ primitive_etex("currentiftype", last_item_cmd, current_if_type_code, 0);
+ primitive_etex("currentifbranch", last_item_cmd, current_if_branch_code, 0);
+ primitive_etex("fontcharwd", last_item_cmd, font_char_wd_code, 0);
+ primitive_etex("fontcharht", last_item_cmd, font_char_ht_code, 0);
+ primitive_etex("fontchardp", last_item_cmd, font_char_dp_code, 0);
+ primitive_etex("fontcharic", last_item_cmd, font_char_ic_code, 0);
+
+ primitive_etex("parshapelength", last_item_cmd, par_shape_length_code, 0);
+ primitive_etex("parshapeindent", last_item_cmd, par_shape_indent_code, 0);
+ primitive_etex("parshapedimen", last_item_cmd, par_shape_dimen_code, 0);
+
+ primitive_luatex("shapemode", assign_int_cmd, int_base + shape_mode_code, int_base);
+ primitive_luatex("hyphenationbounds", assign_int_cmd, int_base + hyphenation_bounds_code, int_base);
+
+ primitive_etex("showgroups", xray_cmd, show_groups, 0);
+
+ /*
+ The \.{\\showtokens} command displays a token list.
+ */
+
+ primitive_etex("showtokens", xray_cmd, show_tokens, 0);
+
+ /*
+ The \.{\\unexpanded} primitive prevents expansion of tokens much as
+ the result from \.{\\the} applied to a token variable. The
+ \.{\\detokenize} primitive converts a token list into a list of
+ character tokens much as if the token list were written to a file. We
+ use the fact that the command modifiers for \.{\\unexpanded} and
+ \.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe}
+ are even.
+ */
+
+ primitive_etex("unexpanded", the_cmd, 1, 0);
+ primitive_etex("detokenize", the_cmd, show_tokens, 0);
+
+ /*
+ The \.{\\showifs} command displays all currently active conditionals.
+ */
+
+ primitive_etex("showifs", xray_cmd, show_ifs, 0);
+
+ /*
+ The \.{\\interactionmode} primitive allows to query and set the interaction mode.
+ */
+
+ primitive_etex("interactionmode", set_page_int_cmd, 2, 0);
+
+ /*
+ The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive.
+ */
+
+ primitive_etex("scantokens", input_cmd, 2, 0);
+ primitive_luatex("scantextokens", input_cmd, 3, 0);
+
+ primitive_etex("readline", read_to_cs_cmd, 1, 0);
+
+ primitive_etex("unless", expand_after_cmd, 1, 0);
+ primitive_etex("ifdefined", if_test_cmd, if_def_code, 0);
+ primitive_etex("ifcsname", if_test_cmd, if_cs_code, 0);
+ primitive_etex("iffontchar", if_test_cmd, if_font_char_code, 0);
+ primitive_luatex("ifincsname", if_test_cmd, if_in_csname_code, 0);
+ primitive_luatex("ifabsnum", if_test_cmd, if_abs_num_code, 0);
+ primitive_luatex("ifabsdim", if_test_cmd, if_abs_dim_code, 0);
+
+ /*
+ The |protected| feature of \eTeX\ defines the \.{\\protected} prefix
+ command for macro definitions. Such macros are protected against
+ expansions when lists of expanded tokens are built, e.g., for \.{\\edef}
+ or during \.{\\write}.
+ */
+
+ primitive_etex("protected", prefix_cmd, 8, 0);
+
+ /*
+ Here are the additional \eTeX\ primitives for expressions.
+ */
+
+ primitive_etex("numexpr", last_item_cmd, eTeX_expr - int_val_level + int_val_level, 0);
+ primitive_etex("dimexpr", last_item_cmd, eTeX_expr - int_val_level + dimen_val_level, 0);
+ primitive_etex("glueexpr", last_item_cmd, eTeX_expr - int_val_level + glue_val_level, 0);
+ primitive_etex("muexpr", last_item_cmd, eTeX_expr - int_val_level + mu_val_level, 0);
+
+ primitive_etex("gluestretchorder", last_item_cmd, glue_stretch_order_code, 0);
+ primitive_etex("glueshrinkorder", last_item_cmd, glue_shrink_order_code, 0);
+ primitive_etex("gluestretch", last_item_cmd, glue_stretch_code, 0);
+ primitive_etex("glueshrink", last_item_cmd, glue_shrink_code, 0);
+
+ primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0);
+ primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0);
+
+ /*
+ The \.{\\pagediscards} and \.{\\splitdiscards} commands share the
+ command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are
+ distinguished by their |chr_code| values |last_box_code| and
+ |vsplit_code|. These |chr_code| values are larger than |box_code| and
+ |copy_code|.
+ */
+
+ primitive_etex("pagediscards", un_vbox_cmd, last_box_code, 0);
+ primitive_etex("splitdiscards", un_vbox_cmd, vsplit_code, 0);
+
+ /*
+ The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties},
+ and \.{\\displaywidowpenalties} commands allow to define arrays of
+ penalty values to be used instead of the corresponding single values.
+ */
+
+ primitive_etex("interlinepenalties", set_etex_shape_cmd, inter_line_penalties_loc, etex_pen_base);
+ primitive_etex("clubpenalties", set_etex_shape_cmd, club_penalties_loc, etex_pen_base);
+ primitive_etex("widowpenalties", set_etex_shape_cmd, widow_penalties_loc, etex_pen_base);
+ primitive_etex("displaywidowpenalties", set_etex_shape_cmd, display_widow_penalties_loc, etex_pen_base);
+
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w b/Build/source/texk/web2c/luatexdir/tex/dumpdata.w index dd0cf3b5aba..8f1261fb426 100644 --- a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w +++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.w @@ -1,526 +1,526 @@ -% dumpdata.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - -#include "ptexlib.h" - -/* we start with 907: the sum of the values of the bytes of "don knuth" */ - -#define FORMAT_ID (907+24) -#if ((FORMAT_ID>=0) && (FORMAT_ID<=256)) -#error Wrong value for FORMAT_ID. -#endif - - -@ After \.{INITEX} has seen a collection of fonts and macros, it -can write all the necessary information on an auxiliary file so -that production versions of \TeX\ are able to initialize their -memory at high speed. The present section of the program takes -care of such output and input. We shall consider simultaneously -the processes of storing and restoring, -so that the inverse relation between them is clear. -@.INITEX@> - -The global variable |format_ident| is a string that is printed right -after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this -string says simply `\.{(INITEX)}'; for other versions of \TeX\ it says, -for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year, -month, and day that the format file was created. We have |format_ident=0| -before \TeX's tables are loaded. |FORMAT_ID| is a new field of type int -suitable for the identification of a format: values between 0 and 256 -(included) can not be used because in the previous format they are used -for the length of the name of the engine. -@c -str_number format_ident; -str_number format_name; /* principal file name */ - - -@ Format files consist of |memory_word| items, and we use the following -macros to dump words of different types: - -@c -FILE *fmt_file; /* for input or output of format information */ - -@ @c -void store_fmt_file(void) -{ - int j, k, l; /* all-purpose indices */ - halfword p; /* all-purpose pointer */ - int x; /* something to dump */ - char *format_engine; - int callback_id; /* |pre_dump| callback */ - char *fmtname = NULL; - /* If dumping is not allowed, abort */ - /* The user is not allowed to dump a format file unless |save_ptr=0|. - This condition implies that |cur_level=level_one|, hence - the |xeq_level| array is constant and it need not be dumped. */ - if (save_ptr != 0) { - print_err("You can't dump inside a group"); - help1("`{...\\dump}' is a no-no."); - succumb(); - } - - /* Create the |format_ident|, open the format file, and inform the user - that dumping has begun */ - callback_id = callback_defined(pre_dump_callback); - if (callback_id > 0) { - (void) run_callback(callback_id, "->"); - } - selector = new_string; - tprint(" (format="); - print(job_name); - print_char(' '); - print_int(year_par); - print_char('.'); - print_int(month_par); - print_char('.'); - print_int(day_par); - print_char(')'); - str_room(2); - format_ident = make_string(); - print(job_name); - format_name = make_string(); - if (interaction == batch_mode) - selector = log_only; - else - selector = term_and_log; - - fmtname = pack_job_name(format_extension); - while (!zopen_w_output(&fmt_file, fmtname, FOPEN_WBIN_MODE)) { - fmtname = prompt_file_name("format file name", format_extension); - } - tprint_nl("Beginning to dump on file "); - tprint(fmtname); - free(fmtname); - tprint_nl(""); - print(format_ident); - - /* Dump constants for consistency check */ - /* The next few sections of the program should make it clear how we use the - dump/undump macros. */ - - dump_int(0x57325458); /* Web2C \TeX's magic constant: "W2TX" */ - dump_int(FORMAT_ID); - - /* Align engine to 4 bytes with one or more trailing NUL */ - x = (int) strlen(engine_name); - format_engine = xmalloc((unsigned) (x + 4)); - strcpy(format_engine, engine_name); - for (k = x; k <= x + 3; k++) - format_engine[k] = 0; - x = x + 4 - (x % 4); - dump_int(x); - dump_things(format_engine[0], x); - xfree(format_engine); - dump_int(0x57325458); /* TODO HM, what checksum would make sense? */ - dump_int(max_halfword); - dump_int(hash_high); - dump_int(eqtb_size); - dump_int(hash_prime); - - /* Dump the string pool */ - k = dump_string_pool(); - print_ln(); - print_int(k); - tprint(" strings using "); - print_int((longinteger) pool_size); - tprint(" bytes"); - - /* Dump the dynamic memory */ - /* By sorting the list of available spaces in the variable-size portion of - |mem|, we are usually able to get by without having to dump very much - of the dynamic memory. - - We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid - information even when it has not been gathering statistics. - */ - dump_node_mem(); - dump_int(temp_token_head); - dump_int(hold_token_head); - dump_int(omit_template); - dump_int(null_list); - dump_int(backup_head); - dump_int(garbage); - x = (int) fix_mem_min; - dump_int(x); - x = (int) fix_mem_max; - dump_int(x); - x = (int) fix_mem_end; - dump_int(x); - dump_int(avail); - dyn_used = (int) fix_mem_end + 1; - dump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); - x = x + (int) (fix_mem_end + 1 - fix_mem_min); - p = avail; - while (p != null) { - decr(dyn_used); - p = token_link(p); - } - dump_int(dyn_used); - print_ln(); - print_int(x); - tprint(" memory locations dumped; current usage is "); - print_int(var_used); - print_char('&'); - print_int(dyn_used); - - /* Dump the table of equivalents */ - /* Dump regions 1 to 4 of |eqtb| */ - /*The table of equivalents usually contains repeated information, so we dump it - in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the - format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra - copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. - */ - k = null_cs; - do { - j = k; - while (j < int_base - 1) { - if ((equiv(j) == equiv(j + 1)) && (eq_type(j) == eq_type(j + 1)) && - (eq_level(j) == eq_level(j + 1))) - goto FOUND1; - incr(j); - } - l = int_base; - goto DONE1; /* |j=int_base-1| */ - FOUND1: - incr(j); - l = j; - while (j < int_base - 1) { - if ((equiv(j) != equiv(j + 1)) || (eq_type(j) != eq_type(j + 1)) || - (eq_level(j) != eq_level(j + 1))) - goto DONE1; - incr(j); - } - DONE1: - dump_int(l - k); - dump_things(eqtb[k], l - k); - k = j + 1; - dump_int(k - l); - } while (k != int_base); - - /* Dump regions 5 and 6 of |eqtb| */ - do { - j = k; - while (j < eqtb_size) { - if (eqtb[j].cint == eqtb[j + 1].cint) - goto FOUND2; - incr(j); - } - l = eqtb_size + 1; - goto DONE2; /* |j=eqtb_size| */ - FOUND2: - incr(j); - l = j; - while (j < eqtb_size) { - if (eqtb[j].cint != eqtb[j + 1].cint) - goto DONE2; - incr(j); - } - DONE2: - dump_int(l - k); - dump_things(eqtb[k], l - k); - k = j + 1; - dump_int(k - l); - } while (k <= eqtb_size); - if (hash_high > 0) - dump_things(eqtb[eqtb_size + 1], hash_high); /* dump |hash_extra| part */ - - dump_int(par_loc); - dump_int(write_loc); - dump_math_codes(); - dump_text_codes(); - /* Dump the hash table */ - /* A different scheme is used to compress the hash table, since its lower - region is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output - two words, |p| and |hash[p]|. The hash table is, of course, densely packed - for |p>=hash_used|, so the remaining entries are output in a~block. - */ - dump_primitives(); - dump_int(hash_used); - cs_count = frozen_control_sequence - 1 - hash_used + hash_high; - for (p = hash_base; p <= hash_used; p++) { - if (cs_text(p) != 0) { - dump_int(p); - dump_hh(hash[p]); - incr(cs_count); - } - } - dump_things(hash[hash_used + 1], - undefined_control_sequence - 1 - hash_used); - if (hash_high > 0) - dump_things(hash[eqtb_size + 1], hash_high); - dump_int(cs_count); - print_ln(); - print_int(cs_count); - tprint(" multiletter control sequences"); - - /* Dump the font information */ - dump_int(max_font_id()); - for (k = 0; k <= max_font_id(); k++) { - /* Dump the array info for internal font number |k| */ - dump_font(k); - tprint_nl("\\font"); - print_esc(font_id_text(k)); - print_char('='); - tprint_file_name((unsigned char *) font_name(k), - (unsigned char *) font_area(k), NULL); - if (font_size(k) != font_dsize(k)) { - tprint(" at "); - print_scaled(font_size(k)); - tprint("pt"); - } - } - print_ln(); - print_int(max_font_id()); - tprint(" preloaded font"); - if (max_font_id() != 1) - print_char('s'); - dump_math_data(); - - /* Dump the hyphenation tables */ - dump_language_data(); - - /* Dump a couple more things and the closing check word */ - dump_int(interaction); - dump_int(format_ident); - dump_int(format_name); - dump_int(69069); - /* We have already printed a lot of statistics, so we set |tracing_stats:=0| - to prevent them from appearing again. */ - tracing_stats_par = 0; - - /* Dump the lua bytecodes */ - dump_luac_registers(); - - /* Close the format file */ - zwclose(fmt_file); -} - -@ Corresponding to the procedure that dumps a format file, we have a function -that reads one in. The function returns |false| if the dumped format is -incompatible with the present \TeX\ table sizes, etc. - -@c -#define too_small(A) do { \ - wake_up_terminal(); \ - wterm_cr(); \ - fprintf(term_out,"---! Must increase the %s",(A)); \ - goto BAD_FMT; \ - } while (0) - -@ The inverse macros are slightly more complicated, since we need to check -the range of the values we are reading in. We say `|undump(a)(b)(x)|' to -read an integer value |x| that is supposed to be in the range |a<=x<=b|. - -@c -#define undump(A,B,C) do { \ - undump_int(x); \ - if (x<(A) || x>(B)) goto BAD_FMT; \ - else (C) = x; \ - } while (0) - - -#define format_debug(A,B) do { \ - if (debug_format_file) { \ - fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \ - } \ - } while (0) - -#define undump_size(A,B,C,D) do { \ - undump_int(x); \ - if (x<(A)) goto BAD_FMT; \ - if (x>(B)) too_small(C); \ - else format_debug (C,x); \ - (D) = x; \ - } while (0) - - -@ @c -boolean load_fmt_file(const char *fmtname) -{ - int j, k; /* all-purpose indices */ - halfword p; /* all-purpose pointer */ - int x; /* something undumped */ - char *format_engine; - /* Undump constants for consistency check */ - if (ini_version) { - libcfree(hash); - libcfree(eqtb); - libcfree(fixmem); - libcfree(varmem); - } - undump_int(x); - format_debug("format magic number", x); - if (x != 0x57325458) - goto BAD_FMT; /* not a format file */ - - undump_int(x); - format_debug("format id", x); - if (x != FORMAT_ID) - goto BAD_FMT; /* FORMAT_ID mismatch */ - - undump_int(x); - format_debug("engine name size", x); - if ((x < 0) || (x > 256)) - goto BAD_FMT; /* corrupted format file */ - - format_engine = xmalloc((unsigned) x); - undump_things(format_engine[0], x); - format_engine[x - 1] = 0; /* force string termination, just in case */ - if (strcmp(engine_name, format_engine)) { - wake_up_terminal(); - wterm_cr(); - fprintf(term_out, "---! %s was written by %s", fmtname, format_engine); - xfree(format_engine); - goto BAD_FMT; - } - xfree(format_engine); - undump_int(x); - format_debug("string pool checksum", x); - if (x != 0x57325458) { /* todo: @@\$ *//* check that strings are the same */ - wake_up_terminal(); - wterm_cr(); - fprintf(term_out, "---! %s was written by a different version", - fmtname); - goto BAD_FMT; - } - undump_int(x); - if (x != max_halfword) - goto BAD_FMT; /* check |max_halfword| */ - undump_int(hash_high); - if ((hash_high < 0) || (hash_high > sup_hash_extra)) - goto BAD_FMT; - if (hash_extra < hash_high) - hash_extra = hash_high; - eqtb_top = eqtb_size + hash_extra; - if (hash_extra == 0) - hash_top = undefined_control_sequence; - else - hash_top = eqtb_top; - hash = xmallocarray(two_halves, (unsigned) (1 + hash_top)); - memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); - eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); - set_eq_type(undefined_control_sequence, undefined_cs_cmd); - set_equiv(undefined_control_sequence, null); - set_eq_level(undefined_control_sequence, level_zero); - for (x = eqtb_size + 1; x <= eqtb_top; x++) - eqtb[x] = eqtb[undefined_control_sequence]; - undump_int(x); - if (x != eqtb_size) - goto BAD_FMT; - undump_int(x); - if (x != hash_prime) - goto BAD_FMT; - - /* Undump the string pool */ - str_ptr = undump_string_pool(); - /* Undump the dynamic memory */ - undump_node_mem(); - undump_int(temp_token_head); - undump_int(hold_token_head); - undump_int(omit_template); - undump_int(null_list); - undump_int(backup_head); - undump_int(garbage); - undump_int(fix_mem_min); - undump_int(fix_mem_max); - fixmem = xmallocarray(smemory_word, fix_mem_max + 1); - memset(voidcast(fixmem), 0, (fix_mem_max + 1) * sizeof(smemory_word)); - undump_int(fix_mem_end); - undump_int(avail); - undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); - undump_int(dyn_used); - - /* Undump the table of equivalents */ - /* Undump regions 1 to 6 of |eqtb| */ - k = null_cs; - do { - undump_int(x); - if ((x < 1) || (k + x > eqtb_size + 1)) - goto BAD_FMT; - undump_things(eqtb[k], x); - k = k + x; - undump_int(x); - if ((x < 0) || (k + x > eqtb_size + 1)) - goto BAD_FMT; - for (j = k; j <= k + x - 1; j++) - eqtb[j] = eqtb[k - 1]; - k = k + x; - } while (k <= eqtb_size); - if (hash_high > 0) /* undump |hash_extra| part */ - undump_things(eqtb[eqtb_size + 1], hash_high); - - undump(hash_base, hash_top, par_loc); - par_token = cs_token_flag + par_loc; - undump(hash_base, hash_top, write_loc); - undump_math_codes(); - undump_text_codes(); - /* Undump the hash table */ - undump_primitives(); - undump(hash_base, frozen_control_sequence, hash_used); - p = hash_base - 1; - do { - undump(p + 1, hash_used, p); - undump_hh(hash[p]); - } while (p != hash_used); - undump_things(hash[hash_used + 1], - undefined_control_sequence - 1 - hash_used); - if (debug_format_file) - print_csnames(hash_base, undefined_control_sequence - 1); - if (hash_high > 0) { - undump_things(hash[eqtb_size + 1], hash_high); - if (debug_format_file) - print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1)); - } - undump_int(cs_count); - - /* Undump the font information */ - undump_int(x); - set_max_font_id(x); - for (k = 0; k <= max_font_id(); k++) { - /* Undump the array info for internal font number |k| */ - undump_font(k); - } - undump_math_data(); - - /* Undump the hyphenation tables */ - undump_language_data(); - - /* Undump a couple more things and the closing check word */ - undump(batch_mode, error_stop_mode, interaction); - if (interactionoption != unspecified_mode) - interaction = interactionoption; - undump(0, str_ptr, format_ident); - undump(0, str_ptr, format_name); - undump_int(x); - if (x != 69069) - goto BAD_FMT; - - /* Undump the lua bytecodes */ - undump_luac_registers(); - - prev_depth_par = ignore_depth; - return true; /* it worked! */ - BAD_FMT: - wake_up_terminal(); - wterm_cr(); - fprintf(term_out, "(Fatal format file error; I'm stymied)"); - return false; -} +% dumpdata.w
+%
+% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+%
+% This file is part of LuaTeX.
+%
+% LuaTeX is free software; you can redistribute it and/or modify it under
+% the terms of the GNU General Public License as published by the Free
+% Software Foundation; either version 2 of the License, or (at your
+% option) any later version.
+%
+% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+% License for more details.
+%
+% You should have received a copy of the GNU General Public License along
+% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+@ @c
+
+#include "ptexlib.h"
+
+/* we start with 907: the sum of the values of the bytes of "don knuth" */
+
+#define FORMAT_ID (907+25)
+#if ((FORMAT_ID>=0) && (FORMAT_ID<=256))
+#error Wrong value for FORMAT_ID.
+#endif
+
+
+@ After \.{INITEX} has seen a collection of fonts and macros, it
+can write all the necessary information on an auxiliary file so
+that production versions of \TeX\ are able to initialize their
+memory at high speed. The present section of the program takes
+care of such output and input. We shall consider simultaneously
+the processes of storing and restoring,
+so that the inverse relation between them is clear.
+@.INITEX@>
+
+The global variable |format_ident| is a string that is printed right
+after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this
+string says simply `\.{(INITEX)}'; for other versions of \TeX\ it says,
+for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year,
+month, and day that the format file was created. We have |format_ident=0|
+before \TeX's tables are loaded. |FORMAT_ID| is a new field of type int
+suitable for the identification of a format: values between 0 and 256
+(included) can not be used because in the previous format they are used
+for the length of the name of the engine.
+@c
+str_number format_ident;
+str_number format_name; /* principal file name */
+
+
+@ Format files consist of |memory_word| items, and we use the following
+macros to dump words of different types:
+
+@c
+FILE *fmt_file; /* for input or output of format information */
+
+@ @c
+void store_fmt_file(void)
+{
+ int j, k, l; /* all-purpose indices */
+ halfword p; /* all-purpose pointer */
+ int x; /* something to dump */
+ char *format_engine;
+ int callback_id; /* |pre_dump| callback */
+ char *fmtname = NULL;
+ /* If dumping is not allowed, abort */
+ /* The user is not allowed to dump a format file unless |save_ptr=0|.
+ This condition implies that |cur_level=level_one|, hence
+ the |xeq_level| array is constant and it need not be dumped. */
+ if (save_ptr != 0) {
+ print_err("You can't dump inside a group");
+ help1("`{...\\dump}' is a no-no.");
+ succumb();
+ }
+
+ /* Create the |format_ident|, open the format file, and inform the user
+ that dumping has begun */
+ callback_id = callback_defined(pre_dump_callback);
+ if (callback_id > 0) {
+ (void) run_callback(callback_id, "->");
+ }
+ selector = new_string;
+ tprint(" (format=");
+ print(job_name);
+ print_char(' ');
+ print_int(year_par);
+ print_char('.');
+ print_int(month_par);
+ print_char('.');
+ print_int(day_par);
+ print_char(')');
+ str_room(2);
+ format_ident = make_string();
+ print(job_name);
+ format_name = make_string();
+ if (interaction == batch_mode)
+ selector = log_only;
+ else
+ selector = term_and_log;
+
+ fmtname = pack_job_name(format_extension);
+ while (!zopen_w_output(&fmt_file, fmtname, FOPEN_WBIN_MODE)) {
+ fmtname = prompt_file_name("format file name", format_extension);
+ }
+ tprint_nl("Beginning to dump on file ");
+ tprint(fmtname);
+ free(fmtname);
+ tprint_nl("");
+ print(format_ident);
+
+ /* Dump constants for consistency check */
+ /* The next few sections of the program should make it clear how we use the
+ dump/undump macros. */
+
+ dump_int(0x57325458); /* Web2C \TeX's magic constant: "W2TX" */
+ dump_int(FORMAT_ID);
+
+ /* Align engine to 4 bytes with one or more trailing NUL */
+ x = (int) strlen(engine_name);
+ format_engine = xmalloc((unsigned) (x + 4));
+ strcpy(format_engine, engine_name);
+ for (k = x; k <= x + 3; k++)
+ format_engine[k] = 0;
+ x = x + 4 - (x % 4);
+ dump_int(x);
+ dump_things(format_engine[0], x);
+ xfree(format_engine);
+ dump_int(0x57325458); /* TODO HM, what checksum would make sense? */
+ dump_int(max_halfword);
+ dump_int(hash_high);
+ dump_int(eqtb_size);
+ dump_int(hash_prime);
+
+ /* Dump the string pool */
+ k = dump_string_pool();
+ print_ln();
+ print_int(k);
+ tprint(" strings using ");
+ print_int((longinteger) pool_size);
+ tprint(" bytes");
+
+ /* Dump the dynamic memory */
+ /* By sorting the list of available spaces in the variable-size portion of
+ |mem|, we are usually able to get by without having to dump very much
+ of the dynamic memory.
+
+ We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid
+ information even when it has not been gathering statistics.
+ */
+ dump_node_mem();
+ dump_int(temp_token_head);
+ dump_int(hold_token_head);
+ dump_int(omit_template);
+ dump_int(null_list);
+ dump_int(backup_head);
+ dump_int(garbage);
+ x = (int) fix_mem_min;
+ dump_int(x);
+ x = (int) fix_mem_max;
+ dump_int(x);
+ x = (int) fix_mem_end;
+ dump_int(x);
+ dump_int(avail);
+ dyn_used = (int) fix_mem_end + 1;
+ dump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1);
+ x = x + (int) (fix_mem_end + 1 - fix_mem_min);
+ p = avail;
+ while (p != null) {
+ decr(dyn_used);
+ p = token_link(p);
+ }
+ dump_int(dyn_used);
+ print_ln();
+ print_int(x);
+ tprint(" memory locations dumped; current usage is ");
+ print_int(var_used);
+ print_char('&');
+ print_int(dyn_used);
+
+ /* Dump the table of equivalents */
+ /* Dump regions 1 to 4 of |eqtb| */
+ /*The table of equivalents usually contains repeated information, so we dump it
+ in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the
+ format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra
+ copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$.
+ */
+ k = null_cs;
+ do {
+ j = k;
+ while (j < int_base - 1) {
+ if ((equiv(j) == equiv(j + 1)) && (eq_type(j) == eq_type(j + 1)) &&
+ (eq_level(j) == eq_level(j + 1)))
+ goto FOUND1;
+ incr(j);
+ }
+ l = int_base;
+ goto DONE1; /* |j=int_base-1| */
+ FOUND1:
+ incr(j);
+ l = j;
+ while (j < int_base - 1) {
+ if ((equiv(j) != equiv(j + 1)) || (eq_type(j) != eq_type(j + 1)) ||
+ (eq_level(j) != eq_level(j + 1)))
+ goto DONE1;
+ incr(j);
+ }
+ DONE1:
+ dump_int(l - k);
+ dump_things(eqtb[k], l - k);
+ k = j + 1;
+ dump_int(k - l);
+ } while (k != int_base);
+
+ /* Dump regions 5 and 6 of |eqtb| */
+ do {
+ j = k;
+ while (j < eqtb_size) {
+ if (eqtb[j].cint == eqtb[j + 1].cint)
+ goto FOUND2;
+ incr(j);
+ }
+ l = eqtb_size + 1;
+ goto DONE2; /* |j=eqtb_size| */
+ FOUND2:
+ incr(j);
+ l = j;
+ while (j < eqtb_size) {
+ if (eqtb[j].cint != eqtb[j + 1].cint)
+ goto DONE2;
+ incr(j);
+ }
+ DONE2:
+ dump_int(l - k);
+ dump_things(eqtb[k], l - k);
+ k = j + 1;
+ dump_int(k - l);
+ } while (k <= eqtb_size);
+ if (hash_high > 0)
+ dump_things(eqtb[eqtb_size + 1], hash_high); /* dump |hash_extra| part */
+
+ dump_int(par_loc);
+ dump_int(write_loc);
+ dump_math_codes();
+ dump_text_codes();
+ /* Dump the hash table */
+ /* A different scheme is used to compress the hash table, since its lower
+ region is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output
+ two words, |p| and |hash[p]|. The hash table is, of course, densely packed
+ for |p>=hash_used|, so the remaining entries are output in a~block.
+ */
+ dump_primitives();
+ dump_int(hash_used);
+ cs_count = frozen_control_sequence - 1 - hash_used + hash_high;
+ for (p = hash_base; p <= hash_used; p++) {
+ if (cs_text(p) != 0) {
+ dump_int(p);
+ dump_hh(hash[p]);
+ incr(cs_count);
+ }
+ }
+ dump_things(hash[hash_used + 1],
+ undefined_control_sequence - 1 - hash_used);
+ if (hash_high > 0)
+ dump_things(hash[eqtb_size + 1], hash_high);
+ dump_int(cs_count);
+ print_ln();
+ print_int(cs_count);
+ tprint(" multiletter control sequences");
+
+ /* Dump the font information */
+ dump_int(max_font_id());
+ for (k = 0; k <= max_font_id(); k++) {
+ /* Dump the array info for internal font number |k| */
+ dump_font(k);
+ tprint_nl("\\font");
+ print_esc(font_id_text(k));
+ print_char('=');
+ tprint_file_name((unsigned char *) font_name(k),
+ (unsigned char *) font_area(k), NULL);
+ if (font_size(k) != font_dsize(k)) {
+ tprint(" at ");
+ print_scaled(font_size(k));
+ tprint("pt");
+ }
+ }
+ print_ln();
+ print_int(max_font_id());
+ tprint(" preloaded font");
+ if (max_font_id() != 1)
+ print_char('s');
+ dump_math_data();
+
+ /* Dump the hyphenation tables */
+ dump_language_data();
+
+ /* Dump a couple more things and the closing check word */
+ dump_int(interaction);
+ dump_int(format_ident);
+ dump_int(format_name);
+ dump_int(69069);
+ /* We have already printed a lot of statistics, so we set |tracing_stats:=0|
+ to prevent them from appearing again. */
+ tracing_stats_par = 0;
+
+ /* Dump the lua bytecodes */
+ dump_luac_registers();
+
+ /* Close the format file */
+ zwclose(fmt_file);
+}
+
+@ Corresponding to the procedure that dumps a format file, we have a function
+that reads one in. The function returns |false| if the dumped format is
+incompatible with the present \TeX\ table sizes, etc.
+
+@c
+#define too_small(A) do { \
+ wake_up_terminal(); \
+ wterm_cr(); \
+ fprintf(term_out,"---! Must increase the %s",(A)); \
+ goto BAD_FMT; \
+ } while (0)
+
+@ The inverse macros are slightly more complicated, since we need to check
+the range of the values we are reading in. We say `|undump(a)(b)(x)|' to
+read an integer value |x| that is supposed to be in the range |a<=x<=b|.
+
+@c
+#define undump(A,B,C) do { \
+ undump_int(x); \
+ if (x<(A) || x>(B)) goto BAD_FMT; \
+ else (C) = x; \
+ } while (0)
+
+
+#define format_debug(A,B) do { \
+ if (debug_format_file) { \
+ fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \
+ } \
+ } while (0)
+
+#define undump_size(A,B,C,D) do { \
+ undump_int(x); \
+ if (x<(A)) goto BAD_FMT; \
+ if (x>(B)) too_small(C); \
+ else format_debug (C,x); \
+ (D) = x; \
+ } while (0)
+
+
+@ @c
+boolean load_fmt_file(const char *fmtname)
+{
+ int j, k; /* all-purpose indices */
+ halfword p; /* all-purpose pointer */
+ int x; /* something undumped */
+ char *format_engine;
+ /* Undump constants for consistency check */
+ if (ini_version) {
+ libcfree(hash);
+ libcfree(eqtb);
+ libcfree(fixmem);
+ libcfree(varmem);
+ }
+ undump_int(x);
+ format_debug("format magic number", x);
+ if (x != 0x57325458)
+ goto BAD_FMT; /* not a format file */
+
+ undump_int(x);
+ format_debug("format id", x);
+ if (x != FORMAT_ID)
+ goto BAD_FMT; /* FORMAT_ID mismatch */
+
+ undump_int(x);
+ format_debug("engine name size", x);
+ if ((x < 0) || (x > 256))
+ goto BAD_FMT; /* corrupted format file */
+
+ format_engine = xmalloc((unsigned) x);
+ undump_things(format_engine[0], x);
+ format_engine[x - 1] = 0; /* force string termination, just in case */
+ if (strcmp(engine_name, format_engine)) {
+ wake_up_terminal();
+ wterm_cr();
+ fprintf(term_out, "---! %s was written by %s", fmtname, format_engine);
+ xfree(format_engine);
+ goto BAD_FMT;
+ }
+ xfree(format_engine);
+ undump_int(x);
+ format_debug("string pool checksum", x);
+ if (x != 0x57325458) { /* todo: @@\$ *//* check that strings are the same */
+ wake_up_terminal();
+ wterm_cr();
+ fprintf(term_out, "---! %s was written by a different version",
+ fmtname);
+ goto BAD_FMT;
+ }
+ undump_int(x);
+ if (x != max_halfword)
+ goto BAD_FMT; /* check |max_halfword| */
+ undump_int(hash_high);
+ if ((hash_high < 0) || (hash_high > sup_hash_extra))
+ goto BAD_FMT;
+ if (hash_extra < hash_high)
+ hash_extra = hash_high;
+ eqtb_top = eqtb_size + hash_extra;
+ if (hash_extra == 0)
+ hash_top = undefined_control_sequence;
+ else
+ hash_top = eqtb_top;
+ hash = xmallocarray(two_halves, (unsigned) (1 + hash_top));
+ memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1));
+ eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1));
+ set_eq_type(undefined_control_sequence, undefined_cs_cmd);
+ set_equiv(undefined_control_sequence, null);
+ set_eq_level(undefined_control_sequence, level_zero);
+ for (x = eqtb_size + 1; x <= eqtb_top; x++)
+ eqtb[x] = eqtb[undefined_control_sequence];
+ undump_int(x);
+ if (x != eqtb_size)
+ goto BAD_FMT;
+ undump_int(x);
+ if (x != hash_prime)
+ goto BAD_FMT;
+
+ /* Undump the string pool */
+ str_ptr = undump_string_pool();
+ /* Undump the dynamic memory */
+ undump_node_mem();
+ undump_int(temp_token_head);
+ undump_int(hold_token_head);
+ undump_int(omit_template);
+ undump_int(null_list);
+ undump_int(backup_head);
+ undump_int(garbage);
+ undump_int(fix_mem_min);
+ undump_int(fix_mem_max);
+ fixmem = xmallocarray(smemory_word, fix_mem_max + 1);
+ memset(voidcast(fixmem), 0, (fix_mem_max + 1) * sizeof(smemory_word));
+ undump_int(fix_mem_end);
+ undump_int(avail);
+ undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1);
+ undump_int(dyn_used);
+
+ /* Undump the table of equivalents */
+ /* Undump regions 1 to 6 of |eqtb| */
+ k = null_cs;
+ do {
+ undump_int(x);
+ if ((x < 1) || (k + x > eqtb_size + 1))
+ goto BAD_FMT;
+ undump_things(eqtb[k], x);
+ k = k + x;
+ undump_int(x);
+ if ((x < 0) || (k + x > eqtb_size + 1))
+ goto BAD_FMT;
+ for (j = k; j <= k + x - 1; j++)
+ eqtb[j] = eqtb[k - 1];
+ k = k + x;
+ } while (k <= eqtb_size);
+ if (hash_high > 0) /* undump |hash_extra| part */
+ undump_things(eqtb[eqtb_size + 1], hash_high);
+
+ undump(hash_base, hash_top, par_loc);
+ par_token = cs_token_flag + par_loc;
+ undump(hash_base, hash_top, write_loc);
+ undump_math_codes();
+ undump_text_codes();
+ /* Undump the hash table */
+ undump_primitives();
+ undump(hash_base, frozen_control_sequence, hash_used);
+ p = hash_base - 1;
+ do {
+ undump(p + 1, hash_used, p);
+ undump_hh(hash[p]);
+ } while (p != hash_used);
+ undump_things(hash[hash_used + 1],
+ undefined_control_sequence - 1 - hash_used);
+ if (debug_format_file)
+ print_csnames(hash_base, undefined_control_sequence - 1);
+ if (hash_high > 0) {
+ undump_things(hash[eqtb_size + 1], hash_high);
+ if (debug_format_file)
+ print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1));
+ }
+ undump_int(cs_count);
+
+ /* Undump the font information */
+ undump_int(x);
+ set_max_font_id(x);
+ for (k = 0; k <= max_font_id(); k++) {
+ /* Undump the array info for internal font number |k| */
+ undump_font(k);
+ }
+ undump_math_data();
+
+ /* Undump the hyphenation tables */
+ undump_language_data();
+
+ /* Undump a couple more things and the closing check word */
+ undump(batch_mode, error_stop_mode, interaction);
+ if (interactionoption != unspecified_mode)
+ interaction = interactionoption;
+ undump(0, str_ptr, format_ident);
+ undump(0, str_ptr, format_name);
+ undump_int(x);
+ if (x != 69069)
+ goto BAD_FMT;
+
+ /* Undump the lua bytecodes */
+ undump_luac_registers();
+
+ prev_depth_par = ignore_depth;
+ return true; /* it worked! */
+ BAD_FMT:
+ wake_up_terminal();
+ wterm_cr();
+ fprintf(term_out, "(Fatal format file error; I'm stymied)");
+ return false;
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.h b/Build/source/texk/web2c/luatexdir/tex/equivalents.h index d12ae5b29f5..80f8108393f 100644 --- a/Build/source/texk/web2c/luatexdir/tex/equivalents.h +++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.h @@ -286,6 +286,7 @@ the |number_regs| \.{\\dimen} registers. # define first_valid_language_code 95 # define hyphenation_bounds_code 96 # define math_skip_mode_code 97 +# define math_pre_display_gap_factor_code 98 # define math_option_code (math_skip_mode_code+1) @@ -742,6 +743,8 @@ extern halfword last_cs_name; #define math_use_old_fraction_scaling_par mathoption_int_par(c_mathoption_use_old_fraction_scaling_code) #define math_umathcode_meaning_par mathoption_int_par(c_mathoption_umathcode_meaning_code) +#define math_pre_display_gap_factor_par int_par(math_pre_display_gap_factor_code) + #define time_par int_par(time_code) #define day_par int_par(day_code) #define month_par int_par(month_code) diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w b/Build/source/texk/web2c/luatexdir/tex/maincontrol.w index cbfd333bbb7..45e9bb46934 100644 --- a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w +++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.w @@ -1,3638 +1,3639 @@ -% maincontrol.w -% -% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - -#include "ptexlib.h" -#include "lua/luatex-api.h" - -@ @c -#define mode mode_par -#define tail tail_par -#define head head_par -#define dir_save dirs_par - -@ We come now to the |main_control| routine, which contains the master -switch that causes all the various pieces of \TeX\ to do their things, -in the right order. - -In a sense, this is the grand climax of the program: It applies all the -tools that we have worked so hard to construct. In another sense, this is -the messiest part of the program: It necessarily refers to other pieces -of code all over the place, so that a person can't fully understand what is -going on without paging back and forth to be reminded of conventions that -are defined elsewhere. We are now at the hub of the web, the central nervous -system that touches most of the other parts and ties them together. -@^brain@> - -The structure of |main_control| itself is quite simple. There's a label -called |big_switch|, at which point the next token of input is fetched -using |get_x_token|. Then the program branches at high speed into one of -about 100 possible directions, based on the value of the current -mode and the newly fetched command code; the sum |abs(mode)+cur_cmd| -indicates what to do next. For example, the case `|vmode+letter|' arises -when a letter occurs in vertical mode (or internal vertical mode); this -case leads to instructions that initialize a new paragraph and enter -horizontal mode. - -The big |case| statement that contains this multiway switch has been labeled -|reswitch|, so that the program can |goto reswitch| when the next token -has already been fetched. Most of the cases are quite short; they call -an ``action procedure'' that does the work for that case, and then they -either |goto reswitch| or they ``fall through'' to the end of the |case| -statement, which returns control back to |big_switch|. Thus, |main_control| -is not an extremely large procedure, in spite of the multiplicity of things -it must do; it is small enough to be handled by PASCAL compilers that put -severe restrictions on procedure size. -@!@^action procedure@> - -One case is singled out for special treatment, because it accounts for most -of \TeX's activities in typical applications. The process of reading simple -text and converting it into |char_node| records, while looking for ligatures -and kerns, is part of \TeX's ``inner loop''; the whole program runs -efficiently when its inner loop is fast, so this part has been written -with particular care. - -We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we -set it equal to |sf_code(cur_chr)|, except that it should never change -from a value less than 1000 to a value exceeding 1000. The most common -case is |sf_code(cur_chr)=1000|, so we want that case to be fast. - -@c -void adjust_space_factor(void) -{ - halfword s = get_sf_code(cur_chr); - if (s == 1000) { - space_factor_par = 1000; - } else if (s < 1000) { - if (s > 0) - space_factor_par = s; - } else if (space_factor_par < 1000) { - space_factor_par = 1000; - } else { - space_factor_par = s; - } -} - -@ From Knuth: ``Having |font_glue| allocated for each text font saves -both time and memory.'' That may be true, but it also punches through -the API wall for fonts, so I removed that -- Taco. But a bit of caching -is very welcome, which is why I need to have the next two globals: - -@ To handle the execution state of |main_control|'s eternal loop, -an extra global variable is used, along with a macro to define -its values. - -@c -#define goto_next 0 -#define goto_skip_token 1 -#define goto_return 2 - -static int main_control_state; - -@* Main control helpers. - -Here are all the functions that are called from |main_control| that -are not already defined elsewhere. For the moment, this list simply -in the order that the appear in |init_main_control|, below. - -@ -@c -static void run_char_num (void) { - scan_char_num(); - cur_chr = cur_val; - adjust_space_factor(); - tail_append(new_char(cur_font_par, cur_chr)); -} - -static void run_char (void) { - adjust_space_factor(); - tail_append(new_char(cur_font_par, cur_chr)); -} - -@ -The occurrence of blank spaces is almost part of \TeX's inner loop, -since we usually encounter about one space for every five non-blank characters. -Therefore |main_control| gives second-highest priority to ordinary spaces. - -When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will -see to it later that the corresponding glue specification is precisely -|zero_glue|, not merely a pointer to some specification that happens -to be full of zeroes. Therefore it is simple to test whether a glue parameter -is zero or~not. - -@c -static void run_app_space (void) { - halfword p; /* was a global temp_ptr */ - int method = disable_space_par ; - if (method == 1) { - /* don't inject anything, not even zero skip */ - } else if (method == 2) { - p = new_glue(zero_glue); - couple_nodes(tail,p); - tail = p; - } else if ((abs(mode) + cur_cmd == hmode + spacer_cmd) && (!(space_factor_par == 1000))) { - app_space(); - } else { - /* Append a normal inter-word space to the current list */ - if (glue_is_zero(space_skip_par)) { - /* Find the glue specification for text spaces in the current font */ - p = new_glue(zero_glue); - width(p) = space(cur_font_par); - stretch(p) = space_stretch(cur_font_par); - shrink(p) = space_shrink(cur_font_par); - - } else { - p = new_param_glue(space_skip_code); - } - /* so from now we have a subtype with spaces: */ - subtype(p) = space_skip_code + 1 ; - couple_nodes(tail,p); - tail = p; - } -} - -@ Append a |boundary_node| -@c -static void run_boundary (void) { - halfword n ; - n = new_node(boundary_node,cur_chr); - if ((cur_chr == 1) || (cur_chr == 2) ) { - /* user boundary or protrusion boundary */ - scan_int(); - boundary_value(n) = cur_val; - } - couple_nodes(tail, n); - tail = n; -} - -@ @c -static void run_char_ghost (void) { - int t; - t = cur_chr; - get_x_token(); - if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd) - || (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) { - halfword p = new_glyph(get_cur_font(), cur_chr); - if (t == 0) { - set_is_leftghost(p); - } else { - set_is_rightghost(p); - } - tail_append(p); - } -} - -@ @c -static void run_relax (void) { - return; -} - -@ |ignore_spaces| is a special case: after it has acted, |get_x_token| has already -fetched the next token from the input, so that operation in |main_control| -should be skipped. - -@c -static void run_ignore_spaces (void) { - if (cur_chr == 0) { - /* Get the next non-blank non-call... */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - main_control_state = goto_skip_token; - } else { - int t = scanner_status; - scanner_status = normal; - get_next(); - scanner_status = t; - cur_cs = prim_lookup(cs_text(cur_cs)); - if (cur_cs != undefined_primitive) { - cur_cmd = get_prim_eq_type(cur_cs); - cur_chr = get_prim_equiv(cur_cs); - cur_tok = (cur_cmd * STRING_OFFSET) + cur_chr; - main_control_state = goto_skip_token; - } - } -} - -@ |stop| is the second special case. We want |main_control| to return to its caller -if there is nothing left to do. - -@c -static void run_stop (void) { - if (its_all_over()) - main_control_state= goto_return; /* this is the only way out */ -} - -@ @c -static void run_non_math_math (void) { - back_input(); - new_graf(true); -} - -@ @c -static void run_math_char_num (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - if (cur_chr == 0) - mval = scan_mathchar(tex_mathcode); - else if (cur_chr == 1) - mval = scan_mathchar(umath_mathcode); - else - mval = scan_mathchar(umathnum_mathcode); - math_char_in_text(mval); -} - -@ @c -static void run_math_given (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - mval = mathchar_from_integer(cur_chr, tex_mathcode); - math_char_in_text(mval); -} - -static void run_xmath_given (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - mval = mathchar_from_integer(cur_chr, umath_mathcode); - math_char_in_text(mval); -} - -@ The most important parts of |main_control| are concerned with \TeX's -chief mission of box-making. We need to control the activities that put -entries on vlists and hlists, as well as the activities that convert -those lists into boxes. All of the necessary machinery has already been -developed; it remains for us to ``push the buttons'' at the right times. - -As an introduction to these routines, let's consider one of the simplest -cases: What happens when `\.{\\hrule}' occurs in vertical mode, or -`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control| -is short, since the |scan_rule_spec| routine already does most of what is -required; thus, there is no need for a special action procedure. - -Note that baselineskip calculations are disabled after a rule in vertical -mode, by setting |prev_depth:=ignore_depth|. - -@c -static void run_rule (void) { - tail_append(scan_rule_spec()); - if (abs(mode) == vmode) - prev_depth_par = ignore_depth; - else if (abs(mode) == hmode) - space_factor_par = 1000; -} - -@ -Many of the actions related to box-making are triggered by the appearance -of braces in the input. For example, when the user says `\.{\\hbox} -\.{to} \.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode, -the information about the box size (100pt, |exactly|) is put onto |save_stack| -with a level boundary word just above it, and |cur_group:=adjusted_hbox_group|; -\TeX\ enters restricted horizontal mode to process the hlist. The right -brace eventually causes |save_stack| to be restored to its former state, -at which time the information about the box size (100pt, |exactly|) is -available once again; a box is packaged and we leave restricted horizontal -mode, appending the new box to the current list of the enclosing mode -(in this case to the current list of vertical mode), followed by any -vertical adjustments that were removed from the box by |hpack|. - -The next few sections of the program are therefore concerned with the -treatment of left and right curly braces. - -If a left brace occurs in the middle of a page or paragraph, it simply -introduces a new level of grouping, and the matching right brace will not have -such a drastic effect. Such grouping affects neither the mode nor the -current list. - -@c -static void run_left_brace (void) { - new_save_level(simple_group); - eq_word_define(int_base + no_local_whatsits_code, 0); - eq_word_define(int_base + no_local_dirs_code, 0); -} - -static void run_begin_group (void) { - new_save_level(semi_simple_group); - eq_word_define(int_base + no_local_whatsits_code, 0); - eq_word_define(int_base + no_local_dirs_code, 0); -} - -static void run_end_group (void) { - if (cur_group == semi_simple_group) { - fixup_directions(); - } else { - off_save(); - } -} - -@ Constructions that require a box are started by calling |scan_box| with -a specified context code. The |scan_box| routine verifies -that a |make_box| command comes next and then it calls |begin_box|. - -@c -static void run_move (void) { - int t = cur_chr; - scan_normal_dimen(); - if (t == 0) - scan_box(cur_val); - else - scan_box(-cur_val); -} - -@ @c -static void run_leader_ship (void) { - scan_box(leader_flag - a_leaders + cur_chr); -} - -@ @c -static void run_make_box (void) { - begin_box(0); -} - -@ @c -static void run_box_dir (void) { - scan_register_num(); - cur_box = box(cur_val); - scan_optional_equals(); - scan_direction(); - if (cur_box != null) - box_dir(cur_box) = cur_val; -} - -@ There is a really small patch to add a new primitive called -\.{\\quitvmode}. In vertical modes, it is identical to \.{\\indent}, -but in horizontal and math modes it is really a no-op (as opposed to -\.{\\indent}, which executes the |indent_in_hmode| procedure). - -A paragraph begins when horizontal-mode material occurs in vertical mode, -or when the paragraph is explicitly started by `\.{\\quitvmode}', -`\.{\\indent}' or `\.{\\noindent}'. - -@c -static void run_start_par_vmode (void) { - new_graf((cur_chr > 0)); -} - -@ @c -static void run_start_par (void) { - if (cur_chr != 2) - indent_in_hmode(); -} - -@ @c -static void run_new_graf (void) { - back_input(); - new_graf(true); -} - -@ A paragraph ends when a |par_end| command is sensed, or when we are in -horizontal mode when reaching the right brace of vertical-mode routines -like \.{\\vbox}, \.{\\insert}, or \.{\\output}. - -@c -static void run_par_end_vmode (void) { - normal_paragraph(); - if (mode > 0) { - checked_page_filter(vmode_par); - build_page(); - } -} - -@ @c -static void run_par_end_hmode (void) { - if (align_state < 0) - off_save(); /* this tries to recover from an alignment that didn't end properly */ - end_graf(bottom_level); /* this takes us to the enclosing mode, if |mode>0| */ - if (mode == vmode) { - checked_page_filter(hmode_par); - build_page(); - } -} - -@ @c -static void append_italic_correction_mmode (void) { - tail_append(new_kern(0)); /* what subtype to use */ -} - -@ @c -static void run_local_box (void) { - append_local_box(cur_chr); -} - -@ @c -static void run_halign_mmode (void) { - if (privileged()) { - if (cur_group == math_shift_group) - init_align(); - else - off_save(); - } -} - -@ @c -static void run_eq_no (void) { - if (privileged()) { - if (cur_group == math_shift_group) - start_eq_no(); - else - off_save(); - } -} - -@ @c -static void run_letter_mmode (void) { - set_math_char(get_math_code(cur_chr)); -} - -@ @c -static void run_char_num_mmode (void) { - scan_char_num(); - cur_chr = cur_val; - set_math_char(get_math_code(cur_chr)); -} - -@ @c -static void run_math_char_num_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - if (cur_chr == 0) - mval = scan_mathchar(tex_mathcode); - else if (cur_chr == 1) - mval = scan_mathchar(umath_mathcode); - else - mval = scan_mathchar(umathnum_mathcode); - set_math_char(mval); -} - -@ @c -static void run_math_given_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - mval = mathchar_from_integer(cur_chr, tex_mathcode); - set_math_char(mval); -} - -static void run_xmath_given_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - mval = mathchar_from_integer(cur_chr, umath_mathcode); - set_math_char(mval); -} - -@ @c -static void run_delim_num (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - if (cur_chr == 0) - mval = scan_delimiter_as_mathchar(tex_mathcode); - else - mval = scan_delimiter_as_mathchar(umath_mathcode); - set_math_char(mval); - -} - -@ @c -static void run_vcenter (void) { - scan_spec(vcenter_group); - normal_paragraph(); - push_nest(); - mode = -vmode; - prev_depth_par = ignore_depth; - if (every_vbox_par != null) - begin_token_list(every_vbox_par, every_vbox_text); -} - -@ @c -static void run_math_style (void) { - tail_append(new_style((small_number) cur_chr)); -} - -@ @c -static void run_non_script (void) { - tail_append(new_glue(zero_glue)); - subtype(tail) = cond_math_glue; -} - -@ @c -static void run_math_choice (void) { - if (cur_chr == 0) - append_choices(); - else - setup_math_style(); -} - -@ @c -static void run_math_shift (void) { - if (cur_group == math_shift_group) - after_math(); - else - off_save(); -} - -@ @c -static void run_after_assignment (void) { - get_token(); - after_token = cur_tok; -} - -@ @c -static void run_after_group (void) { - get_token(); - save_for_after(cur_tok); -} - -@ @c -static void run_extension (void) { - do_extension(0); -} - -static void run_normal (void) { -{ - switch (cur_chr) { - case save_pos_code: - new_whatsit(save_pos_node); - break; - case save_cat_code_table_code: - scan_int(); - if ((cur_val < 0) || (cur_val > 0x7FFF)) { - print_err("Invalid \\catcode table"); - help1("All \\catcode table ids must be between 0 and 0x7FFF"); - error(); - } else { - if (cur_val == cat_code_table_par) { - print_err("Invalid \\catcode table"); - help1("You cannot overwrite the current \\catcode table"); - error(); - } else { - copy_cat_codes(cat_code_table_par, cur_val); - } - } - break; - case init_cat_code_table_code: - scan_int(); - if ((cur_val < 0) || (cur_val > 0x7FFF)) { - print_err("Invalid \\catcode table"); - help1("All \\catcode table ids must be between 0 and 0x7FFF"); - error(); - } else { - if (cur_val == cat_code_table_par) { - print_err("Invalid \\catcode table"); - help1("You cannot overwrite the current \\catcode table"); - error(); - } else { - initex_cat_codes(cur_val); - } - } - break; - case set_random_seed_code: - /* Negative random seed values are silently converted to positive ones */ - scan_int(); - if (cur_val < 0) - negate(cur_val); - random_seed = cur_val; - init_randoms(random_seed); - break; - case late_lua_code: - new_whatsit(late_lua_node); /* type == normal */ - late_lua_name(tail) = scan_lua_state(); - (void) scan_toks(false, false); - late_lua_data(tail) = def_ref; - break; - case expand_font_code: - read_expand_font(); - break; - default: - confusion("int1"); - break; - } - } -} - -/* - this is experimental and not used for production, only for testing and writing - macros (some options stay) - -*/ - -#define mathoption_set_int(A) \ - scan_int(); \ - word_define(mathoption_int_base+A, cur_val); - -static void run_option(void) { - int a = 0 ; - switch (cur_chr) { - case math_option_code: - if (scan_keyword("old")) { - mathoption_set_int(c_mathoption_old_code); - } else if (scan_keyword("noitaliccompensation")) { - mathoption_set_int(c_mathoption_no_italic_compensation_code); - } else if (scan_keyword("nocharitalic")) { - mathoption_set_int(c_mathoption_no_char_italic_code); - } else if (scan_keyword("useoldfractionscaling")) { - mathoption_set_int(c_mathoption_use_old_fraction_scaling_code); - } else if (scan_keyword("umathcodemeaning")) { - mathoption_set_int(c_mathoption_umathcode_meaning_code); - } else { - normal_warning("mathoption","unknown key"); - } - break; - default: - /* harmless */ - break; - } -} - -@ For mode-independent commands, the following macro is useful. - -Also, there is a list of cases where the user has probably gotten into or out of math -mode by mistake. \TeX\ will insert a dollar sign and rescan the current token, and -it makes sense ot have a macro for that as well. - -@c -#define any_mode(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; jump_table[mmode+(A)]=B -#define non_math(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; - - -@ The |main_control| uses a jump table, and |init_main_control| sets that table up. -@c -typedef void (*main_control_function) (void); -main_control_function *jump_table; - -static void init_main_control (void) { - jump_table = xmalloc((mmode+max_command_cmd+1) * sizeof(main_control_function)) ; - - jump_table[hmode + char_num_cmd] = run_char_num; - jump_table[hmode + letter_cmd] = run_char; - jump_table[hmode + other_char_cmd] = run_char; - jump_table[hmode + char_given_cmd] = run_char; - jump_table[hmode + spacer_cmd] = run_app_space; - jump_table[hmode + ex_space_cmd] = run_app_space; - jump_table[mmode + ex_space_cmd] = run_app_space; - jump_table[hmode + boundary_cmd] = run_boundary; - jump_table[hmode + char_ghost_cmd] = run_char_ghost; - jump_table[mmode + char_ghost_cmd] = run_char_ghost; - any_mode(relax_cmd, run_relax); - jump_table[vmode + spacer_cmd] = run_relax; - jump_table[mmode + spacer_cmd] = run_relax; - jump_table[mmode + boundary_cmd] = run_relax; - any_mode(ignore_spaces_cmd,run_ignore_spaces); - jump_table[vmode + stop_cmd] = run_stop; - jump_table[vmode + math_char_num_cmd] = run_non_math_math; - jump_table[vmode + math_given_cmd] = run_non_math_math; - jump_table[vmode + xmath_given_cmd] = run_non_math_math; - jump_table[hmode + math_char_num_cmd] = run_math_char_num; - jump_table[hmode + math_given_cmd] = run_math_given; - jump_table[hmode + xmath_given_cmd] = run_xmath_given; - - jump_table[vmode + vmove_cmd] = report_illegal_case; - jump_table[hmode + hmove_cmd] = report_illegal_case; - jump_table[mmode + hmove_cmd] = report_illegal_case; - any_mode(last_item_cmd, report_illegal_case); - jump_table[vmode + vadjust_cmd] = report_illegal_case; - jump_table[vmode + ital_corr_cmd] = report_illegal_case; - non_math(eq_no_cmd,report_illegal_case); - any_mode(mac_param_cmd,report_illegal_case); - - non_math(sup_mark_cmd, insert_dollar_sign); - non_math(sub_mark_cmd, insert_dollar_sign); - non_math(super_sub_script_cmd, insert_dollar_sign); - non_math(math_comp_cmd, insert_dollar_sign); - non_math(delim_num_cmd, insert_dollar_sign); - non_math(left_right_cmd, insert_dollar_sign); - non_math(above_cmd, insert_dollar_sign); - non_math(radical_cmd, insert_dollar_sign); - non_math(math_style_cmd, insert_dollar_sign); - non_math(math_choice_cmd, insert_dollar_sign); - non_math(vcenter_cmd, insert_dollar_sign); - non_math(non_script_cmd, insert_dollar_sign); - non_math(mkern_cmd, insert_dollar_sign); - non_math(limit_switch_cmd, insert_dollar_sign); - non_math(mskip_cmd, insert_dollar_sign); - non_math(math_accent_cmd, insert_dollar_sign); - jump_table[mmode + endv_cmd] = insert_dollar_sign; - jump_table[mmode + par_end_cmd] = insert_dollar_sign_par_end; - jump_table[mmode + stop_cmd] = insert_dollar_sign; - jump_table[mmode + vskip_cmd] = insert_dollar_sign; - jump_table[mmode + un_vbox_cmd] = insert_dollar_sign; - jump_table[mmode + valign_cmd] = insert_dollar_sign; - jump_table[mmode + hrule_cmd] = insert_dollar_sign; - jump_table[mmode + no_hrule_cmd] = insert_dollar_sign; - jump_table[vmode + hrule_cmd] = run_rule; - jump_table[vmode + no_hrule_cmd] = run_rule; - jump_table[hmode + vrule_cmd] = run_rule; - jump_table[hmode + no_vrule_cmd] = run_rule; - jump_table[mmode + vrule_cmd] = run_rule; - jump_table[mmode + no_vrule_cmd] = run_rule; - jump_table[vmode + vskip_cmd] = append_glue; - jump_table[hmode + hskip_cmd] = append_glue; - jump_table[mmode + hskip_cmd] = append_glue; - jump_table[mmode + mskip_cmd] = append_glue; - any_mode(kern_cmd, append_kern); - jump_table[mmode + mkern_cmd] = append_kern; - non_math(left_brace_cmd, run_left_brace); - any_mode(begin_group_cmd,run_begin_group); - any_mode(end_group_cmd, run_end_group); - any_mode(right_brace_cmd, handle_right_brace); - jump_table[vmode + hmove_cmd] = run_move; - jump_table[hmode + vmove_cmd] = run_move; - jump_table[mmode + vmove_cmd] = run_move; - any_mode(leader_ship_cmd, run_leader_ship); - any_mode(make_box_cmd, run_make_box); - any_mode(assign_box_dir_cmd, run_box_dir); - jump_table[vmode + start_par_cmd] = run_start_par_vmode; - jump_table[hmode + start_par_cmd] = run_start_par; - jump_table[mmode + start_par_cmd] = run_start_par; - jump_table[vmode + letter_cmd] = run_new_graf; - jump_table[vmode + other_char_cmd] = run_new_graf; - jump_table[vmode + char_num_cmd] = run_new_graf; - jump_table[vmode + char_given_cmd] = run_new_graf; - jump_table[vmode + char_ghost_cmd] = run_new_graf; - jump_table[vmode + math_shift_cmd] = run_new_graf; - jump_table[vmode + math_shift_cs_cmd] = run_new_graf; - jump_table[vmode + un_hbox_cmd] = run_new_graf; - jump_table[vmode + vrule_cmd] = run_new_graf; - jump_table[vmode + no_vrule_cmd] = run_new_graf; - jump_table[vmode + accent_cmd] = run_new_graf; - jump_table[vmode + discretionary_cmd] = run_new_graf; - jump_table[vmode + hskip_cmd] = run_new_graf; - jump_table[vmode + valign_cmd] = run_new_graf; - jump_table[vmode + ex_space_cmd] = run_new_graf; - jump_table[vmode + boundary_cmd] = run_new_graf; - jump_table[vmode + par_end_cmd] = run_par_end_vmode; - jump_table[hmode + par_end_cmd] = run_par_end_hmode; - jump_table[hmode + stop_cmd] = head_for_vmode; - jump_table[hmode + vskip_cmd] = head_for_vmode; - jump_table[hmode + hrule_cmd] = head_for_vmode; - jump_table[hmode + no_hrule_cmd] = head_for_vmode; - jump_table[hmode + un_vbox_cmd] = head_for_vmode; - jump_table[hmode + halign_cmd] = head_for_vmode; - any_mode(insert_cmd,begin_insert_or_adjust); - jump_table[hmode + vadjust_cmd] = begin_insert_or_adjust; - jump_table[mmode + vadjust_cmd] = begin_insert_or_adjust; - any_mode(mark_cmd, handle_mark); - any_mode(break_penalty_cmd, append_penalty); - any_mode(remove_item_cmd, delete_last); - jump_table[vmode + un_vbox_cmd] = unpackage; - jump_table[hmode + un_hbox_cmd] = unpackage; - jump_table[mmode + un_hbox_cmd] = unpackage; - jump_table[hmode + ital_corr_cmd] = append_italic_correction; - jump_table[mmode + ital_corr_cmd] = append_italic_correction_mmode; - jump_table[hmode + discretionary_cmd] = append_discretionary; - jump_table[mmode + discretionary_cmd] = append_discretionary; - any_mode(assign_local_box_cmd, run_local_box); - jump_table[hmode + accent_cmd] = make_accent; - any_mode(car_ret_cmd,align_error); - any_mode(tab_mark_cmd,align_error); - any_mode(no_align_cmd,no_align_error); - any_mode(omit_cmd, omit_error); - jump_table[vmode + halign_cmd] = init_align; - jump_table[hmode + valign_cmd] = init_align; - jump_table[mmode + halign_cmd] = run_halign_mmode; - jump_table[vmode + endv_cmd] = do_endv; - jump_table[hmode + endv_cmd] = do_endv; - any_mode(end_cs_name_cmd, cs_error); - jump_table[hmode + math_shift_cmd] = init_math; - jump_table[hmode + math_shift_cs_cmd] = init_math; - jump_table[mmode + eq_no_cmd] = run_eq_no; - jump_table[mmode + left_brace_cmd] = math_left_brace; - jump_table[mmode + letter_cmd] = run_letter_mmode; - jump_table[mmode + other_char_cmd] = run_letter_mmode; - jump_table[mmode + char_given_cmd] = run_letter_mmode; - jump_table[mmode + char_num_cmd] = run_char_num_mmode; - jump_table[mmode + math_char_num_cmd] = run_math_char_num_mmode; - jump_table[mmode + math_given_cmd] = run_math_given_mmode; - jump_table[mmode + xmath_given_cmd] = run_xmath_given_mmode; - jump_table[mmode + delim_num_cmd] = run_delim_num; - jump_table[mmode + math_comp_cmd] = math_math_comp; - jump_table[mmode + limit_switch_cmd] = math_limit_switch; - jump_table[mmode + radical_cmd] = math_radical; - jump_table[mmode + accent_cmd] = math_ac; - jump_table[mmode + math_accent_cmd] = math_ac; - jump_table[mmode + vcenter_cmd] = run_vcenter; - jump_table[mmode + math_style_cmd] = run_math_style; - jump_table[mmode + non_script_cmd] = run_non_script; - jump_table[mmode + math_choice_cmd] = run_math_choice; - jump_table[mmode + above_cmd] = math_fraction; - jump_table[mmode + sub_mark_cmd] = sub_sup; - jump_table[mmode + sup_mark_cmd] = sub_sup; - jump_table[mmode + super_sub_script_cmd] = sub_sup; - jump_table[mmode + left_right_cmd] = math_left_right; - jump_table[mmode + math_shift_cmd] = run_math_shift; - jump_table[mmode + math_shift_cs_cmd] = run_math_shift; - any_mode(toks_register_cmd, prefixed_command); - any_mode(assign_toks_cmd, prefixed_command); - any_mode(assign_int_cmd, prefixed_command); - any_mode(assign_attr_cmd, prefixed_command); - any_mode(assign_dir_cmd, prefixed_command); - any_mode(assign_dimen_cmd, prefixed_command); - any_mode(assign_glue_cmd, prefixed_command); - any_mode(assign_mu_glue_cmd, prefixed_command); - any_mode(assign_font_dimen_cmd, prefixed_command); - any_mode(assign_font_int_cmd, prefixed_command); - any_mode(set_aux_cmd, prefixed_command); - any_mode(set_prev_graf_cmd, prefixed_command); - any_mode(set_page_dimen_cmd, prefixed_command); - any_mode(set_page_int_cmd, prefixed_command); - any_mode(set_box_dimen_cmd, prefixed_command); - any_mode(set_tex_shape_cmd, prefixed_command); - any_mode(set_etex_shape_cmd, prefixed_command); - any_mode(def_char_code_cmd, prefixed_command); - any_mode(def_del_code_cmd, prefixed_command); - any_mode(extdef_math_code_cmd, prefixed_command); - any_mode(extdef_del_code_cmd, prefixed_command); - any_mode(def_family_cmd, prefixed_command); - any_mode(set_math_param_cmd, prefixed_command); - any_mode(set_font_cmd, prefixed_command); - any_mode(def_font_cmd, prefixed_command); - any_mode(letterspace_font_cmd, prefixed_command); - any_mode(copy_font_cmd, prefixed_command); - any_mode(set_font_id_cmd, prefixed_command); - any_mode(register_cmd, prefixed_command); - any_mode(advance_cmd, prefixed_command); - any_mode(multiply_cmd, prefixed_command); - any_mode(divide_cmd, prefixed_command); - any_mode(prefix_cmd, prefixed_command); - any_mode(let_cmd, prefixed_command); - any_mode(shorthand_def_cmd, prefixed_command); - any_mode(read_to_cs_cmd, prefixed_command); - any_mode(def_cmd, prefixed_command); - any_mode(set_box_cmd, prefixed_command); - any_mode(hyph_data_cmd, prefixed_command); - any_mode(set_interaction_cmd, prefixed_command); - any_mode(after_assignment_cmd,run_after_assignment); - any_mode(after_group_cmd,run_after_group); - any_mode(in_stream_cmd,open_or_close_in); - any_mode(message_cmd,issue_message); - any_mode(case_shift_cmd, shift_case); - any_mode(xray_cmd, show_whatever); - any_mode(normal_cmd, run_normal); - any_mode(extension_cmd, run_extension); - any_mode(option_cmd, run_option); -} - -@ And here is |main_control| itself. It is quite short nowadays. - -@c -void main_control(void) -{ - main_control_state = goto_next; - init_main_control () ; - - if (equiv(every_job_loc) != null) - begin_token_list(equiv(every_job_loc), every_job_text); - - while (1) { - if (main_control_state == goto_skip_token) - main_control_state = goto_next; /* reset */ - else - get_x_token(); - - /* Give diagnostic information, if requested */ - /* When a new token has just been fetched at |big_switch|, we have an - ideal place to monitor \TeX's activity. */ - if (interrupt != 0 && OK_to_interrupt) { - back_input(); - check_interrupt(); - continue; - } - if (tracing_commands_par > 0) - show_cur_cmd_chr(); - - (jump_table[(abs(mode) + cur_cmd)])(); /* run the command */ - - if (main_control_state == goto_return) { - return; - } - } - return; /* not reached */ -} - -@ @c -void app_space(void) -{ /* handle spaces when |space_factor<>1000| */ - halfword q; /* glue node */ - if ((space_factor_par >= 2000) && (! glue_is_zero(xspace_skip_par))) { - q = new_param_glue(xspace_skip_code); - /* so from now we have a subtype with spaces: */ - subtype(q) = xspace_skip_code + 1; - } else { - if (!glue_is_zero(space_skip_par)) { - q = new_glue(space_skip_par); - } else { - q = new_glue(zero_glue); - width(q) = space(cur_font_par); - stretch(q) = space_stretch(cur_font_par); - shrink(q) = space_shrink(cur_font_par); - } - /* Modify the glue specification in |q| according to the space factor */ - if (space_factor_par >= 2000) - width(q) = width(q) + extra_space(cur_font_par); - stretch(q) = xn_over_d(stretch(q), space_factor_par, 1000); - shrink(q) = xn_over_d(shrink(q), 1000, space_factor_par); - - /* so from now we have a subtype with spaces: */ - subtype(q) = space_skip_code + 1; - } - couple_nodes(tail, q); - tail = q; -} - -@ @c -void insert_dollar_sign(void) -{ - back_input(); - cur_tok = math_shift_token + '$'; - print_err("Missing $ inserted"); - help2("I've inserted a begin-math/end-math symbol since I think", - "you left one out. Proceed, with fingers crossed."); - ins_error(); -} - -@ We can silently ignore \.{\\par}s in a math formula. - -@c -void insert_dollar_sign_par_end(void) -{ - if (!suppress_mathpar_error_par) { - insert_dollar_sign() ; - } -} - -@ The `|you_cant|' procedure prints a line saying that the current command -is illegal in the current mode; it identifies these things symbolically. - -@c -void you_cant(void) -{ - print_err("You can't use `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - print_in_mode(mode); -} - -@ -When erroneous situations arise, \TeX\ usually issues an error message -specific to the particular error. For example, `\.{\\noalign}' should -not appear in any mode, since it is recognized by the |align_peek| routine -in all of its legitimate appearances; a special error message is given -when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate -error message is simply that the user is not allowed to do what he or she -has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode, -and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated -here and in the other sections referred to under `See also \dots.' - -@c -void report_illegal_case(void) -{ - you_cant(); - help4("Sorry, but I'm not programmed to handle this case;", - "I'll just pretend that you didn''t ask for it.", - "If you're in the wrong mode, you might be able to", - "return to the right one by typing `I}' or `I$' or `I\\par'."); - error(); -} - -@ Some operations are allowed only in privileged modes, i.e., in cases -that |mode>0|. The |privileged| function is used to detect violations -of this rule; it issues an error message and returns |false| if the -current |mode| is negative. - -@c -boolean privileged(void) -{ - if (mode > 0) { - return true; - } else { - report_illegal_case(); - return false; - } -} - -@ We don't want to leave |main_control| immediately when a |stop| command -is sensed, because it may be necessary to invoke an \.{\\output} routine -several times before things really grind to a halt. (The output routine -might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.) -Therefore |its_all_over| is |true| only when the current page -and contribution list are empty, and when the last output was not a -``dead cycle.'' - -@c -boolean its_all_over(void) -{ /* do this when \.{\\end} or \.{\\dump} occurs */ - if (privileged()) { - if ((page_head == page_tail) && (head == tail) && (dead_cycles == 0)) { - return true; - } - back_input(); /* we will try to end again after ejecting residual material */ - tail_append(new_null_box()); - width(tail) = hsize_par; - tail_append(new_glue(fill_glue)); - tail_append(new_penalty(-010000000000)); - normal_page_filter(end); - build_page(); /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */ - } - return false; -} - - -@ The |hskip| and |vskip| command codes are used for control sequences -like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}. -The difference is in the value of |cur_chr|. - -All the work relating to glue creation has been relegated to the -following subroutine. It does not call |build_page|, because it is -used in at least one place where that would be a mistake. - -@c -void append_glue(void) -{ - int s = cur_chr; - switch (s) { - case fil_code: - cur_val = new_glue(fil_glue); - break; - case fill_code: - cur_val = new_glue(fill_glue); - break; - case ss_code: - cur_val = new_glue(ss_glue); - break; - case fil_neg_code: - cur_val = new_glue(fil_neg_glue); - break; - case skip_code: - scan_glue(glue_val_level); - break; - case mskip_code: - scan_glue(mu_val_level); - break; - } - /* now |cur_val| points to the glue specification */ - tail_append(new_glue(cur_val)); - flush_node(cur_val); - if (s > skip_code) { - subtype(tail) = mu_glue; - } -} - -@ @c -void append_kern(void) -{ - int s; /* |subtype| of the kern node */ - s = cur_chr; - scan_dimen((s == mu_glue), false, false); - tail_append(new_kern(cur_val)); - subtype(tail) = (quarterword) s; -} - -@ We have to deal with errors in which braces and such things are not -properly nested. Sometimes the user makes an error of commission by -inserting an extra symbol, but sometimes the user makes an error of omission. -\TeX\ can't always tell one from the other, so it makes a guess and tries -to avoid getting into a loop. - -The |off_save| routine is called when the current group code is wrong. It tries -to insert something into the user's input that will help clean off -the top level. - -@c -void off_save(void) -{ - halfword p, q; /* inserted token */ - if (cur_group == bottom_level) { - /* Drop current token and complain that it was unmatched */ - print_err("Extra "); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - help1("Things are pretty mixed up, but I think the worst is over."); - error(); - - } else { - back_input(); - p = get_avail(); - set_token_link(temp_token_head, p); - print_err("Missing "); - /* Prepare to insert a token that matches |cur_group|, and print what it is */ - /* At this point, |link(temp_token_head)=p|, a pointer to an empty one-word node. */ - switch (cur_group) { - case semi_simple_group: - set_token_info(p, cs_token_flag + frozen_end_group); - tprint_esc("endgroup"); - break; - case math_shift_group: - set_token_info(p, math_shift_token + '$'); - print_char('$'); - break; - case math_left_group: - set_token_info(p, cs_token_flag + frozen_right); - q = get_avail(); - set_token_link(p, q); - p = token_link(p); - set_token_info(p, other_token + '.'); - tprint_esc("right."); - break; - default: - set_token_info(p, right_brace_token + '}'); - print_char('}'); - break; - } - tprint(" inserted"); - ins_list(token_link(temp_token_head)); - help5("I've inserted something that you may have forgotten.", - "(See the <inserted text> above.)", - "With luck, this will get me unwedged. But if you", - "really didn't forget anything, try typing `2' now; then", - "my insertion and my current dilemma will both disappear."); - error(); - } -} - -@ The routine for a |right_brace| character branches into many subcases, -since a variety of things may happen, depending on |cur_group|. Some -types of groups are not supposed to be ended by a right brace; error -messages are given in hopes of pinpointing the problem. Most branches -of this routine will be filled in later, when we are ready to understand -them; meanwhile, we must prepare ourselves to deal with such errors. - -@c -void handle_right_brace(void) -{ - halfword p, q; /* for short-term use */ - scaled d; /* holds |split_max_depth| in |insert_group| */ - int f; /* holds |floating_penalty| in |insert_group| */ - p = null; - switch (cur_group) { - case simple_group: - fixup_directions(); - break; - case bottom_level: - print_err("Too many }'s"); - help2("You've closed more groups than you opened.", - "Such booboos are generally harmless, so keep going."); - error(); - break; - case semi_simple_group: - case math_shift_group: - case math_left_group: - extra_right_brace(); - break; - case hbox_group: - /* When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or - \.{\\vtop} construction, the |package| routine comes into action. We might - also have to finish a paragraph that hasn't ended. */ - package(0); - break; - case adjusted_hbox_group: - adjust_tail = adjust_head; - pre_adjust_tail = pre_adjust_head; - package(0); - break; - case vbox_group: - end_graf(vbox_group); - package(0); - break; - case vtop_group: - end_graf(vtop_group); - package(vtop_code); - break; - case insert_group: - end_graf(insert_group); - q = new_glue(split_top_skip_par); - d = split_max_depth_par; - f = floating_penalty_par; - unsave(); - save_ptr--; - /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */ - p = vpack(vlink(head), 0, additional, -1); - pop_nest(); - if (saved_type(0) == saved_insert) { - tail_append(new_node(ins_node, saved_value(0))); - height(tail) = height(p) + depth(p); - ins_ptr(tail) = list_ptr(p); - split_top_ptr(tail) = q; - depth(tail) = d; - float_cost(tail) = f; - } else if (saved_type(0) == saved_adjust) { - tail_append(new_node(adjust_node, saved_value(0))); - adjust_ptr(tail) = list_ptr(p); - flush_node(q); - } else { - confusion("insert_group"); - } - list_ptr(p) = null; - flush_node(p); - if (nest_ptr == 0) { - checked_page_filter(insert); - build_page(); - } - break; - case output_group: - /* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */ - if (dir_level(text_dir_ptr) == cur_level) { - /* DIR: Remove from |text_dir_ptr| */ - halfword text_dir_tmp = vlink(text_dir_ptr); - flush_node(text_dir_ptr); - text_dir_ptr = text_dir_tmp; - } - resume_after_output(); - break; - case disc_group: - build_discretionary(); - break; - case local_box_group: - build_local_box(); - break; - case align_group: - back_input(); - cur_tok = cs_token_flag + frozen_cr; - print_err("Missing \\cr inserted"); - help1("I'm guessing that you meant to end an alignment here."); - ins_error(); - break; - case no_align_group: - end_graf(no_align_group); - unsave(); - align_peek(); - break; - case vcenter_group: - end_graf(vcenter_group); - finish_vcenter(); - break; - case math_choice_group: - build_choices(); - break; - case math_group: - close_math_group(p); - break; - default: - confusion("rightbrace"); - break; - } -} - -@ @c -void extra_right_brace(void) -{ - print_err("Extra }, or forgotten "); - switch (cur_group) { - case semi_simple_group: - tprint_esc("endgroup"); - break; - case math_shift_group: - print_char('$'); - break; - case math_left_group: - tprint_esc("right"); - break; - } - help5("I've deleted a group-closing symbol because it seems to be", - "spurious, as in `$x}$'. But perhaps the } is legitimate and", - "you forgot something else, as in `\\hbox{$x}'. In such cases", - "the way to recover is to insert both the forgotten and the", - "deleted material, e.g., by typing `I$}'."); - error(); - incr(align_state); -} - -@ Here is where we clear the parameters that are supposed to revert to their -default values after every paragraph and when internal vertical mode is entered. - -@c -void normal_paragraph(void) -{ - if (looseness_par != 0) - eq_word_define(int_base + looseness_code, 0); - if (hang_indent_par != 0) - eq_word_define(dimen_base + hang_indent_code, 0); - if (hang_after_par != 1) - eq_word_define(int_base + hang_after_code, 1); - if (par_shape_par_ptr != null) - eq_define(par_shape_loc, shape_ref_cmd, null); - if (inter_line_penalties_par_ptr != null) - eq_define(inter_line_penalties_loc, shape_ref_cmd, null); - if (shape_mode_par > 0) - eq_word_define(dimen_base + shape_mode_code, 0); -} - -@ The global variable |cur_box| will point to a newly-made box. If the box -is void, we will have |cur_box=null|. Otherwise we will have -|type(cur_box)=hlist_node| or |vlist_node| or |rule_node|; the |rule_node| -case can occur only with leaders. - -@c -halfword cur_box; /* box to be placed into its context */ - -@ The |box_end| procedure does the right thing with |cur_box|, if -|box_context| represents the context as explained above. - -@c -void box_end(int box_context) -{ - if (box_context < box_flag) { - /* Append box |cur_box| to the current list, shifted by |box_context| */ - /* - The global variable |adjust_tail| will be non-null if and only if the - current box might include adjustments that should be appended to the - current vertical list. - */ - if (cur_box != null) { - shift_amount(cur_box) = box_context; - if (abs(mode) == vmode) { - if (pre_adjust_tail != null) { - if (pre_adjust_head != pre_adjust_tail) - append_list(pre_adjust_head, pre_adjust_tail); - pre_adjust_tail = null; - } - append_to_vlist(cur_box,lua_key_index(box)); - if (adjust_tail != null) { - if (adjust_head != adjust_tail) - append_list(adjust_head, adjust_tail); - adjust_tail = null; - } - if (mode > 0) { - checked_page_filter(box); - build_page(); - } - } else { - if (abs(mode) == hmode) - space_factor_par = 1000; - else - cur_box = new_sub_box(cur_box); - couple_nodes(tail, cur_box); - tail = cur_box; - } - } - } else if (box_context < ship_out_flag) { - /* Store |cur_box| in a box register */ - if (box_context < global_box_flag) - eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box); - else - geq_define(box_base + box_context - global_box_flag, box_ref_cmd, cur_box); - } else if (cur_box != null) { - if (box_context > ship_out_flag) { - /* Append a new leader node that uses |cur_box| */ - /* Get the next non-blank non-relax... */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - if (((cur_cmd == hskip_cmd) && (abs(mode) != vmode)) || - ((cur_cmd == vskip_cmd) && (abs(mode) == vmode))) { - append_glue(); - subtype(tail) = (quarterword) (box_context - (leader_flag - a_leaders)); - leader_ptr(tail) = cur_box; - } else { - print_err("Leaders not followed by proper glue"); - help3 - ("You should say `\\leaders <box or rule><hskip or vskip>'.", - "I found the <box or rule>, but there's no suitable", - "<hskip or vskip>, so I'm ignoring these leaders."); - back_error(); - flush_node_list(cur_box); - } - } else { - ship_out(static_pdf, cur_box, SHIPPING_PAGE); - } - } -} - -@ the next input should specify a box or perhaps a rule - -@c -void scan_box(int box_context) -{ - /* Get the next non-blank non-relax... */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - if (cur_cmd == make_box_cmd) { - begin_box(box_context); - } else if ((box_context >= leader_flag) && - ((cur_cmd == hrule_cmd) || (cur_cmd == vrule_cmd) || - (cur_cmd == no_hrule_cmd) || (cur_cmd == no_vrule_cmd))) { - cur_box = scan_rule_spec(); - box_end(box_context); - } else { - print_err("A <box> was supposed to be here"); - help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or", - "something like that. So you might find something missing in", - "your output. But keep trying; you can fix this later."); - back_error(); - } -} - -@ @c -void new_graf(boolean indented) -{ - halfword p, q, dir_graf_tmp; - halfword dir_rover; - prev_graf_par = 0; - if ((mode == vmode) || (head != tail)) { - tail_append(new_param_glue(par_skip_code)); - } - push_nest(); - mode = hmode; - space_factor_par = 1000; - /* LOCAL: Add local paragraph node */ - tail_append(make_local_par_node(new_graf_par_code)); - if (indented) { - p = new_null_box(); - box_dir(p) = par_direction_par; - width(p) = par_indent_par; - subtype(p) = indent_list; - q = tail; - tail_append(p); - } else { - q = tail; - } - dir_rover = text_dir_ptr; - while (dir_rover != null) { - if ((vlink(dir_rover) != null) || (dir_dir(dir_rover) != par_direction_par)) { - dir_graf_tmp = new_dir(dir_dir(dir_rover)); - try_couple_nodes(dir_graf_tmp,vlink(q)); - couple_nodes(q,dir_graf_tmp); - } - dir_rover = vlink(dir_rover); - } - q = head; - while (vlink(q) != null) - q = vlink(q); - tail = q; - if (every_par_par != null) - begin_token_list(every_par_par, every_par_text); - if (nest_ptr == 1) { - checked_page_filter(new_graf); - build_page(); /* put |par_skip| glue on current page */ - } -} - -@ @c -void indent_in_hmode(void) -{ - halfword p; - if (cur_chr > 0) { /* \.{\\indent} */ - p = new_null_box(); - width(p) = par_indent_par; - if (abs(mode) == hmode) - space_factor_par = 1000; - else - p = new_sub_box(p); - tail_append(p); - } -} - -@ @c -void head_for_vmode(void) -{ - if (mode < 0) { - if ((cur_cmd != hrule_cmd) && (cur_cmd != no_hrule_cmd)) { - off_save(); - } else { - print_err("You can't use `\\hrule' here except with leaders"); - help2("To put a horizontal rule in an hbox or an alignment,", - "you should use \\leaders or \\hrulefill (see The TeXbook)."); - error(); - } - } else { - back_input(); - cur_tok = par_token; - back_input(); - token_type = inserted; - } -} - -@ TODO (BUG?): |dir_save| would have been set by |line_break| by means -of |post_line_break|, but this is not done right now, as it introduces -pretty heavy memory leaks. This means the current code is probably -wrong in some way that relates to in-paragraph displays. - -@c -void end_graf(int line_break_context) -{ - if (mode == hmode) { - if ((head == tail) || (vlink(head) == tail)) { - if (vlink(head) == tail) - flush_node(vlink(head)); - pop_nest(); /* null paragraphs are ignored, all contain a |local_paragraph| node */ - } else { - line_break(false, line_break_context); - } - if (dir_save != null) { - flush_node_list(dir_save); - dir_save = null; - } - normal_paragraph(); - error_count = 0; - } -} - -@ @c -void begin_insert_or_adjust(void) -{ - if (cur_cmd != vadjust_cmd) { - scan_register_num(); - if (cur_val == output_box_par) { - print_err("You can't \\insert"); - print_int(output_box_par); - help1("I'm changing to \\insert0; box \\outputbox is special."); - error(); - cur_val = 0; - } - set_saved_record(0, saved_insert, 0, cur_val); - } else if (scan_keyword("pre")) { - set_saved_record(0, saved_adjust, 0, 1); - } else { - set_saved_record(0, saved_adjust, 0, 0); - } - save_ptr++; - new_save_level(insert_group); - scan_left_brace(); - normal_paragraph(); - push_nest(); - mode = -vmode; - prev_depth_par = ignore_depth; -} - -@ I (TH)'ve renamed the |make_mark| procedure to this, because if the -current chr code is 1, then the actual command was \.{\\clearmarks}, -which does not generate a mark node but instead destroys the current -mark tokenlists. - -@c -void handle_mark(void) -{ - halfword p; /* new node */ - halfword c; /* the mark class */ - if (cur_chr == clear_marks_code) { - scan_mark_num(); - c = cur_val; - delete_top_mark(c); - delete_bot_mark(c); - delete_first_mark(c); - delete_split_first_mark(c); - delete_split_bot_mark(c); - } else { - if (cur_chr == 0) { - c = 0; - } else { - scan_mark_num(); - c = cur_val; - if (c > biggest_used_mark) - biggest_used_mark = c; - } - p = scan_toks(false, true); - p = new_node(mark_node, 0); /* the |subtype| is not used */ - mark_class(p) = c; - mark_ptr(p) = def_ref; - couple_nodes(tail, p); - tail = p; - } -} - -@ @c -void append_penalty(void) -{ - scan_int(); - tail_append(new_penalty(cur_val)); - if (mode == vmode) { - checked_page_filter(penalty); - build_page(); - } -} - -@ When |delete_last| is called, |cur_chr| is the |type| of node that -will be deleted, if present. - -The |remove_item| command removes a penalty, kern, or glue node if it -appears at the tail of the current list, using a brute-force linear scan. -Like \.{\\lastbox}, this command is not allowed in vertical mode (except -internal vertical mode), since the current list in vertical mode is sent -to the page builder. But if we happen to be able to implement it in -vertical mode, we do. - -@c -void delete_last(void) -{ - halfword p, q; /* run through the current list */ - if ((mode == vmode) && (tail == head)) { - /* Apologize for inability to do the operation now, - unless \.{\\unskip} follows non-glue */ - if ((cur_chr != glue_node) || (last_glue != max_halfword)) { - you_cant(); - if (cur_chr == kern_node) { - help2 - ("Sorry...I usually can't take things from the current page.", - "Try `I\\kern-\\lastkern' instead."); - } else if (cur_chr != glue_node) { - help2 - ("Sorry...I usually can't take things from the current page.", - "Perhaps you can make the output routine do it."); - } else { - help2 - ("Sorry...I usually can't take things from the current page.", - "Try `I\\vskip-\\lastskip' instead."); - } - error(); - } - } else { - /* todo: clean this up */ - if (!is_char_node(tail)) { - if (type(tail) == cur_chr) { - q = head; - do { - p = q; - if (!is_char_node(q)) { - if (type(q) == disc_node) { - if (p == tail) - return; - } - } - q = vlink(p); - } while (q != tail); - vlink(p) = null; - flush_node_list(tail); - tail = p; - } - } - } -} - -@ @c -void unpackage(void) -{ - halfword p; /* the box */ - halfword r; /* to remove marginal kern nodes */ - int c; /* should we copy? */ - halfword s; /* for varmem assignment */ - if (cur_chr > copy_code) { - /* Handle saved items and |goto done| */ - try_couple_nodes(tail, disc_ptr[cur_chr]); - disc_ptr[cur_chr] = null; - goto DONE; - } - c = cur_chr; - scan_register_num(); - p = box(cur_val); - if (p == null) - return; - if ((abs(mode) == mmode) - || ((abs(mode) == vmode) && (type(p) != vlist_node)) - || ((abs(mode) == hmode) && (type(p) != hlist_node))) { - print_err("Incompatible list can't be unboxed"); - help3("Sorry, Pandora. (You sneaky devil.)", - "I refuse to unbox an \\hbox in vertical mode or vice versa.", - "And I can't open any boxes in math mode."); - error(); - return; - } - if (c == copy_code) { - s = copy_node_list(list_ptr(p)); - try_couple_nodes(tail,s); - } else { - try_couple_nodes(tail,list_ptr(p)); - box(cur_val) = null; - list_ptr(p) = null; - flush_node(p); - } - DONE: - while (vlink(tail) != null) { - r = vlink(tail); - if (!is_char_node(r) && (type(r) == margin_kern_node)) { - try_couple_nodes(tail,vlink(r)); - flush_node(r); - } - tail = vlink(tail); - } -} - -@ -Italic corrections are converted to kern nodes when the |ital_corr| command -follows a character. In math mode the same effect is achieved by appending -a kern of zero here, since italic corrections are supplied later. - -@c -void append_italic_correction(void) -{ - halfword p; /* |char_node| at the tail of the current list */ - internal_font_number f; /* the font in the |char_node| */ - if (tail != head) { - if (is_char_node(tail)) - p = tail; - else - return; - f = font(p); - tail_append(new_kern(char_italic(f, character(p)))); - subtype(tail) = italic_kern; - } -} - -@ @c -void append_local_box(int kind) -{ - incr(save_ptr); - set_saved_record(-1, saved_boxtype, 0, kind); - new_save_level(local_box_group); - scan_left_brace(); - push_nest(); - mode = -hmode; - space_factor_par = 1000; -} - -@ Discretionary nodes are easy in the common case `\.{\\-}', but in the -general case we must process three braces full of items. - -The space factor does not change when we append a discretionary node, -but it starts out as 1000 in the subsidiary lists. - -@c -void append_discretionary(void) -{ - int c; - tail_append(new_disc()); - subtype(tail) = (quarterword) cur_chr; - if (cur_chr == explicit_disc) { - /* \- */ - c = get_pre_hyphen_char(cur_lang_par); - if (c != 0) { - vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c); - alink(vlink(pre_break(tail))) = pre_break(tail); - tlink(pre_break(tail)) = vlink(pre_break(tail)); - } - c = get_post_hyphen_char(cur_lang_par); - if (c != 0) { - vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c); - alink(vlink(post_break(tail))) = post_break(tail); - tlink(post_break(tail)) = vlink(post_break(tail)); - } - disc_penalty(tail) = ex_hyphen_penalty_par; - } else { - /* \discretionary */ - if (scan_keyword("penalty")) { - scan_int(); - disc_penalty(tail) = cur_val; - } - incr(save_ptr); - set_saved_record(-1, saved_disc, 0, 0); - new_save_level(disc_group); - scan_left_brace(); - push_nest(); - mode = -hmode; - space_factor_par = 1000; - /* already preset: disc_penalty(tail) = hyphen_penalty_par; */ - } -} - -@ The test for |p != null| ensures that empty \.{\\localleftbox} and - \.{\\localrightbox} commands are not applied. - -@c -void build_local_box(void) -{ - halfword p; - int kind; - unsave(); - assert(saved_type(-1) == saved_boxtype); - kind = saved_value(-1); - decr(save_ptr); - p = vlink(head); - pop_nest(); - if (p != null) - p = hpack(p, 0, additional, -1); - if (kind == 0) - eq_define(local_left_box_base, box_ref_cmd, p); - else - eq_define(local_right_box_base, box_ref_cmd, p); - if (abs(mode) == hmode) { - /* LOCAL: Add local paragraph node */ - tail_append(make_local_par_node(local_box_par_code)); - } - eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); -} - -@ The three discretionary lists are constructed somewhat as if they were -hboxes. A~subroutine called |build_discretionary| handles the transitions. -(This is sort of fun.) - -@c -void build_discretionary(void) -{ - halfword p, q; /* for link manipulation */ - int n; /* length of discretionary list */ - unsave(); - /* Prune the current list, if necessary, until it contains only - |char_node|, |kern_node|, |hlist_node|, |vlist_node| and - |rule_node| items; set |n| to the length of the list, - and set |q| to the lists tail */ - /* During this loop, |p=vlink(q)| and there are |n| items preceding |p|. */ - q = head; - p = vlink(q); - n = 0; - while (p != null) { - if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) { - print_err("Improper discretionary list"); - help1("Discretionary lists must contain only boxes and kerns."); - error(); - begin_diagnostic(); - tprint_nl("The following discretionary sublist has been deleted:"); - show_box(p); - end_diagnostic(true); - flush_node_list(p); - vlink(q) = null; - break; - } - alink(p) = q; - q = p; - p = vlink(q); - incr(n); - } - - p = vlink(head); - pop_nest(); - assert(saved_type(-1) == saved_disc); - switch (saved_value(-1)) { - case 0: - if (n > 0) { - vlink(pre_break(tail)) = p; - alink(p) = pre_break(tail); - tlink(pre_break(tail)) = q; - } - break; - case 1: - if (n > 0) { - vlink(post_break(tail)) = p; - alink(p) = post_break(tail); - tlink(post_break(tail)) = q; - } - break; - case 2: - /* Attach list |p| to the current list, and record its length; - then finish up and |return| */ - if ((n > 0) && (abs(mode) == mmode)) { - print_err("Illegal math \\discretionary"); - help2("Sorry: The third part of a discretionary break must be", - "empty, in math formulas. I had to delete your third part."); - flush_node_list(p); - error(); - } else { - if (n > 0) { - vlink(no_break(tail)) = p; - alink(p) = no_break(tail); - tlink(no_break(tail)) = q; - } - } - decr(save_ptr); - return; - break; - } /* there are no other cases */ - set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1)); - new_save_level(disc_group); - scan_left_brace(); - push_nest(); - mode = -hmode; - space_factor_par = 1000; -} - -@ The positioning of accents is straightforward but tedious. Given an accent -of width |a|, designed for characters of height |x| and slant |s|; -and given a character of width |w|, height |h|, and slant |t|: We will shift -the accent down by |x-h|, and we will insert kern nodes that have the effect of -centering the accent over the character and shifting the accent to the -right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is -absent from the font, we will simply use the other, without shifting. - -@c -void make_accent(void) -{ - double s, t; /* amount of slant */ - halfword p, q, r; /* character, box, and kern nodes */ - internal_font_number f; /* relevant font */ - scaled a, h, x, w, delta; /* heights and widths, as explained above */ - scan_char_num(); - f = equiv(cur_font_loc); - p = new_glyph(f, cur_val); - if (p != null) { - x = x_height(f); - s = float_cast(slant(f)) / float_constant(65536); /* real division */ - a = glyph_width(p); - do_assignments(); - /* Create a character node |q| for the next character, - but set |q:=null| if problems arise */ - q = null; - f = equiv(cur_font_loc); - if ((cur_cmd == letter_cmd) || - (cur_cmd == other_char_cmd) || (cur_cmd == char_given_cmd)) { - q = new_glyph(f, cur_chr); - } else if (cur_cmd == char_num_cmd) { - scan_char_num(); - q = new_glyph(f, cur_val); - } else { - back_input(); - } - - if (q != null) { - /* Append the accent with appropriate kerns, then set |p:=q| */ - /* The kern nodes appended here must be distinguished from other kerns, lest - they be wiped away by the hyphenation algorithm or by a previous line break. - - The two kerns are computed with (machine-dependent) |real| arithmetic, but - their sum is machine-independent; the net effect is machine-independent, - because the user cannot remove these nodes nor access them via \.{\\lastkern}. - */ - t = float_cast(slant(f)) / float_constant(65536); /* real division */ - w = glyph_width(q); - h = glyph_height(q); - if (h != x) { /* the accent must be shifted up or down */ - p = hpack(p, 0, additional, -1); - shift_amount(p) = x - h; - } - delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); /* real multiplication */ - r = new_kern(delta); - subtype(r) = accent_kern; - couple_nodes(tail, r); - couple_nodes(r, p); - tail = new_kern(-a - delta); - subtype(tail) = accent_kern; - couple_nodes(p, tail); - p = q; - - } - couple_nodes(tail, p); - tail = p; - space_factor_par = 1000; - } -} - -@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner -into |main_control|, it might be that the user has foolishly inserted -one of them into something that has nothing to do with alignment. But it is -far more likely that a left brace or right brace has been omitted, since -|get_next| takes actions appropriate to alignment only when `\.{\\cr}' -or `\.{\\span}' or tab marks occur with |align_state=0|. The following -program attempts to make an appropriate recovery. - -@c -void align_error(void) -{ - if (abs(align_state) > 2) { - /* Express consternation over the fact that no alignment is in progress */ - print_err("Misplaced "); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - if (cur_tok == tab_token + '&') { - help6("I can't figure out why you would want to use a tab mark", - "here. If you just want an ampersand, the remedy is", - "simple: Just type `I\\&' now. But if some right brace", - "up above has ended a previous alignment prematurely,", - "you're probably due for more error messages, and you", - "might try typing `S' now just to see what is salvageable."); - } else { - help5("I can't figure out why you would want to use a tab mark", - "or \\cr or \\span just now. If something like a right brace", - "up above has ended a previous alignment prematurely,", - "you're probably due for more error messages, and you", - "might try typing `S' now just to see what is salvageable."); - } - error(); - - } else { - back_input(); - if (align_state < 0) { - print_err("Missing { inserted"); - incr(align_state); - cur_tok = left_brace_token + '{'; - } else { - print_err("Missing } inserted"); - decr(align_state); - cur_tok = right_brace_token + '}'; - } - help3("I've put in what seems to be necessary to fix", - "the current column of the current alignment.", - "Try to go on, since this might almost work."); - ins_error(); - } -} - -@ The help messages here contain a little white lie, since \.{\\noalign} -and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'. - -@c -void no_align_error(void) -{ - print_err("Misplaced \\noalign"); - help2("I expect to see \\noalign only after the \\cr of", - "an alignment. Proceed, and I'll ignore this case."); - error(); -} - -void omit_error(void) -{ - print_err("Misplaced \\omit"); - help2("I expect to see \\omit only after tab marks or the \\cr of", - "an alignment. Proceed, and I'll ignore this case."); - error(); -} - -@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}. -Let's take a look at what happens when they are used correctly. - -An |align_group| code is supposed to remain on the |save_stack| -during an entire alignment, until |fin_align| removes it. - -A devious user might force an |endv| command to occur just about anywhere; -we must defeat such hacks. - -@c -void do_endv(void) -{ - base_ptr = input_ptr; - input_stack[base_ptr] = cur_input; - while ((input_stack[base_ptr].index_field != v_template) && - (input_stack[base_ptr].loc_field == null) && - (input_stack[base_ptr].state_field == token_list)) - decr(base_ptr); - if ((input_stack[base_ptr].index_field != v_template) || - (input_stack[base_ptr].loc_field != null) || - (input_stack[base_ptr].state_field != token_list)) - fatal_error("(interwoven alignment preambles are not allowed)"); - /*.interwoven alignment preambles... */ - if (cur_group == align_group) { - end_graf(align_group); - if (fin_col()) - fin_row(); - } else { - off_save(); - } -} - -@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|. - -@c -void cs_error(void) -{ - print_err("Extra \\endcsname"); - help1("I'm ignoring this, since I wasn't doing a \\csname."); - error(); -} - -@ - Assignments to values in |eqtb| can be global or local. Furthermore, a - control sequence can be defined to be `\.{\\long}', `\.{\\protected}', - or `\.{\\outer}', and it might or might not be expanded. The prefixes - `\.{\\global}', `\.{\\long}', `\.{\\protected}', - and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric - codes, making it possible to accumulate the union of all specified prefixes - by adding the corresponding codes. (PASCAL's |set| operations could also - have been used.) - - Every prefix, and every command code that might or might not be prefixed, - calls the action procedure |prefixed_command|. This routine accumulates - a sequence of prefixes until coming to a non-prefix, then it carries out - the command. - -@ If the user says, e.g., `\.{\\global\\global}', the redundancy is -silently accepted. - - -@ The different types of code values have different legal ranges; the -following program is careful to check each case properly. - -@c -#define check_def_code(A) do { \ - if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \ - print_err("Invalid code ("); \ - print_int(cur_val); \ - if (p<(A)) \ - tprint("), should be in the range 0.."); \ - else \ - tprint("), should be at most "); \ - print_int(n); \ - help1("I'm going to use 0 instead of that illegal code value."); \ - error(); \ - cur_val=0; \ - } \ -} while (0) - -@ @c -/* -halfword swap_hang_indent(halfword indentation, halfword shape_mode) { - if (shape_mode == 1 || shape_mode == 3 || shape_mode == -1 || shape_mode == -3) { - return negate(indentation); - } else { - return indentation; - } -} - -halfword swap_parshape_indent(halfword indentation, halfword width, halfword shape_mode) { - if (shape_mode == 2 || shape_mode == 3 || shape_mode == -2 || shape_mode == -3) { - return hsize_par - width - indentation; - } else { - return indentation; - } -} - -*/ - -void prefixed_command(void) -{ - int a; /* accumulated prefix codes so far */ - internal_font_number f; /* identifies a font */ - halfword j; /* index into a \.{\\parshape} specification */ - halfword p, q; /* for temporary short-term use */ - int n; /* ditto */ - boolean e, check_glue; /* should a definition be expanded? or was \.{\\let} not done? */ - mathcodeval mval; /* for handling of \.{\\mathchardef}s */ - a = 0; - while (cur_cmd == prefix_cmd) { - if (!odd(a / cur_chr)) - a = a + cur_chr; - /* Get the next non-blank non-relax... */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - - if (cur_cmd <= max_non_prefixed_command) { - /* Discard erroneous prefixes and |return| */ - print_err("You can't use a prefix with `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - print_char('\''); - help2 - ("I'll pretend you didn't say \\long or \\outer or \\global or", - "\\protected."); - back_error(); - return; - } - if (tracing_commands_par > 2) - show_cur_cmd_chr(); - } - /* Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant */ - if (a >= 8) { - j = protected_token; - a = a - 8; - } else { - j = 0; - } - if ((cur_cmd != def_cmd) && ((a % 4 != 0) || (j != 0))) { - print_err("You can't use `\\long' or `\\outer' or `\\protected' with `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - print_char('\''); - help1("I'll pretend you didn't say \\long or \\outer or \\protected here."); - error(); - } - /* Adjust for the setting of \.{\\globaldefs} */ - if (global_defs_par != 0) { - if (global_defs_par < 0) { - if (is_global(a)) - a = a - 4; - } else { - if (!is_global(a)) - a = a + 4; - } - } - switch (cur_cmd) { - case set_font_cmd: - /* Here's an example of the way many of the following routines operate. - (Unfortunately, they aren't all as simple as this.) */ - define(cur_font_loc, data_cmd, cur_chr); - break; - case def_cmd: - /* When a |def| command has been scanned, - |cur_chr| is odd if the definition is supposed to be global, and - |cur_chr>=2| if the definition is supposed to be expanded. */ - - if (odd(cur_chr) && !is_global(a) && (global_defs_par >= 0)) - a = a + 4; - e = (cur_chr >= 2); - get_r_token(); - p = cur_cs; - q = scan_toks(true, e); - if (j != 0) { - q = get_avail(); - set_token_info(q, j); - set_token_link(q, token_link(def_ref)); - set_token_link(def_ref, q); - } - define(p, call_cmd + (a % 4), def_ref); - break; - case let_cmd: - n = cur_chr; - if (n == normal) { - get_r_token(); - p = cur_cs; - do { - get_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '=') { - get_token(); - if (cur_cmd == spacer_cmd) - get_token(); - } - } else if (n == normal + 1) { - /* futurelet */ - get_r_token(); - p = cur_cs; - get_token(); - q = cur_tok; - get_token(); - back_input(); - cur_tok = q; - /* look ahead, then back up */ - /* note that |back_input| doesn't affect |cur_cmd|, |cur_chr| */ - back_input(); - } else { - /* letcharcode */ - scan_int(); - if (cur_val > 0) { - cur_cs = active_to_cs(cur_val, true); - set_token_info(cur_cs, cur_cs + cs_token_flag); - p = cur_cs; - do { - get_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '=') { - get_token(); - if (cur_cmd == spacer_cmd) - get_token(); - } - } else { - p = null; - tex_error("invalid number for \\letcharcode",NULL); - } - } - if (cur_cmd >= call_cmd) - add_token_ref(cur_chr); - define(p, cur_cmd, cur_chr); - break; - case shorthand_def_cmd: - /* We temporarily define |p| to be |relax|, so that an occurrence of |p| - while scanning the definition will simply stop the scanning instead of - producing an ``undefined control sequence'' error or expanding the - previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'. - */ - n = cur_chr; - get_r_token(); - p = cur_cs; - define(p, relax_cmd, too_big_char); - scan_optional_equals(); - switch (n) { - case char_def_code: - scan_char_num(); - define(p, char_given_cmd, cur_val); - break; - case math_char_def_code: - mval = scan_mathchar(tex_mathcode); - if (math_umathcode_meaning_par == 1) { - cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; - define(p, xmath_given_cmd, cur_val); - } else { - cur_val = (mval.class_value * 16 + mval.family_value) * 256 + mval.character_value; - define(p, math_given_cmd, cur_val); - } - break; - case xmath_char_def_code: - mval = scan_mathchar(umath_mathcode); - cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; - define(p, xmath_given_cmd, cur_val); - break; - case umath_char_def_code: - mval = scan_mathchar(umathnum_mathcode); - cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; - define(p, xmath_given_cmd, cur_val); - break; - default: - scan_register_num(); - switch (n) { - case count_def_code: - define(p, assign_int_cmd, count_base + cur_val); - break; - case attribute_def_code: - define(p, assign_attr_cmd, attribute_base + cur_val); - break; - case dimen_def_code: - define(p, assign_dimen_cmd, scaled_base + cur_val); - break; - case skip_def_code: - define(p, assign_glue_cmd, skip_base + cur_val); - break; - case mu_skip_def_code: - define(p, assign_mu_glue_cmd, mu_skip_base + cur_val); - break; - case toks_def_code: - define(p, assign_toks_cmd, toks_base + cur_val); - break; - default: - confusion("shorthand_def"); - break; - } - break; - } - break; - case read_to_cs_cmd: - j = cur_chr; - scan_int(); - n = cur_val; - if (!scan_keyword("to")) { - print_err("Missing `to' inserted"); - help2("You should have said `\\read<number> to \\cs'.", - "I'm going to look for the \\cs now."); - error(); - } - get_r_token(); - p = cur_cs; - read_toks(n, p, j); - define(p, call_cmd, cur_val); - break; - case toks_register_cmd: - case assign_toks_cmd: - /* The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive - their values in the following way. (For safety's sake, we place an - enclosing pair of braces around an \.{\\output} list.) */ - q = cur_cs; - if (cur_cmd == toks_register_cmd) { - scan_register_num(); - p = toks_base + cur_val; - } else { - p = cur_chr; /* |p=every_par_loc| or |output_routine_loc| or \dots */ - } - scan_optional_equals(); - /* Get the next non-blank non-relax non-call token */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - - if (cur_cmd != left_brace_cmd) { - /* If the right-hand side is a token parameter - or token register, finish the assignment and |goto done| */ - if (cur_cmd == toks_register_cmd) { - scan_register_num(); - cur_cmd = assign_toks_cmd; - cur_chr = toks_base + cur_val; - } - if (cur_cmd == assign_toks_cmd) { - q = equiv(cur_chr); - if (q == null) { - define(p, undefined_cs_cmd, null); - } else { - add_token_ref(q); - define(p, call_cmd, q); - } - goto DONE; - } - } - back_input(); - cur_cs = q; - q = scan_toks(false, false); - if (token_link(def_ref) == null) { /* empty list: revert to the default */ - define(p, undefined_cs_cmd, null); - free_avail(def_ref); - } else { - if (p == output_routine_loc) { /* enclose in curlies */ - p = get_avail(); - set_token_link(q, p); - p = output_routine_loc; - q = token_link(q); - set_token_info(q, right_brace_token + '}'); - q = get_avail(); - set_token_info(q, left_brace_token + '{'); - set_token_link(q, token_link(def_ref)); - set_token_link(def_ref, q); - } - define(p, call_cmd, def_ref); - } - break; - case assign_int_cmd: - /* Similar routines are used to assign values to the numeric parameters. */ - p = cur_chr; - scan_optional_equals(); - scan_int(); - assign_internal_value(a, p, cur_val); - break; - case assign_attr_cmd: - p = cur_chr; - scan_optional_equals(); - scan_int(); - if ((p - attribute_base) > max_used_attr) - max_used_attr = (p - attribute_base); - attr_list_cache = cache_disabled; - word_define(p, cur_val); - break; - case assign_dir_cmd: - /* DIR: Assign direction codes */ - scan_direction(); - switch (cur_chr) { - case int_base + page_direction_code: - eq_word_define(int_base + page_direction_code, cur_val); - break; - case int_base + body_direction_code: - eq_word_define(int_base + body_direction_code, cur_val); - break; - case int_base + par_direction_code: - eq_word_define(int_base + par_direction_code, cur_val); - break; - case int_base + math_direction_code: - eq_word_define(int_base + math_direction_code, cur_val); - break; - case int_base + text_direction_code: - case int_base + line_direction_code: - /* - pre version 0.97 this was a commented section because various tests hint that this - is unnecessary and sometimes even produces weird results, like: - - (\hbox{\textdir TRT ABC\textdir TLT DEF})) - - becomes - - (DEFCBA) - - in the output when we use - - tail_append(new_dir(text_direction_par) - - but when we append the reverse of the current it goes better - - */ - check_glue = (cur_chr == (int_base + line_direction_code)); - if (check_glue) { - cur_chr = int_base + text_direction_code ; - } - if (abs(mode) == hmode) { - if (no_local_dirs_par > 0) { - /* tail is non zero but we test anyway */ - if (check_glue && (tail != null && type(tail) == glue_node)) { - halfword prev = alink(tail); - halfword dirn = new_dir(text_direction_par - dir_swap); - couple_nodes(prev,dirn); - couple_nodes(dirn,tail); - } else { - tail_append(new_dir(text_direction_par - dir_swap)); - } - } else { - /* what is the use of nolocaldirs .. maybe we should get rid of it */ - } - update_text_dir_ptr(cur_val); - tail_append(new_dir(cur_val)); - dir_level(tail) = cur_level; - } else { - update_text_dir_ptr(cur_val); - } - /* original: - - // if ((no_local_dirs_par > 0) && (abs(mode) == hmode)) { - // // tail_append(new_dir(text_direction_par) // kind of wrong - // tail_append(new_dir(text_direction_par - dir_swap)); // better - // } - - update_text_dir_ptr(cur_val); - if (abs(mode) == hmode) { - tail_append(new_dir(cur_val)); - dir_level(tail) = cur_level; - } - */ - eq_word_define(int_base + text_direction_code, cur_val); - eq_word_define(int_base + no_local_dirs_code, no_local_dirs_par + 1); - break; - } - break; - case assign_dimen_cmd: - p = cur_chr; - scan_optional_equals(); - scan_normal_dimen(); - assign_internal_value(a, p, cur_val); - break; - case assign_glue_cmd: - case assign_mu_glue_cmd: - p = cur_chr; - n = cur_cmd; - scan_optional_equals(); - if (n == assign_mu_glue_cmd) - scan_glue(mu_val_level); - else - scan_glue(glue_val_level); - define(p, glue_ref_cmd, cur_val); - break; - case def_char_code_cmd: - case def_del_code_cmd: - /* Let |n| be the largest legal code value, based on |cur_chr| */ - if (cur_chr == cat_code_base) - n = max_char_code; - else if (cur_chr == sf_code_base) - n = 077777; - else - n = biggest_char; - - p = cur_chr; - if (cur_chr == math_code_base) { - if (is_global(a)) - cur_val1 = level_one; - else - cur_val1 = cur_level; - scan_extdef_math_code(cur_val1, tex_mathcode); - } else if (cur_chr == lc_code_base) { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - check_def_code(lc_code_base); - define_lc_code(p, cur_val); - } else if (cur_chr == uc_code_base) { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - check_def_code(uc_code_base); - define_uc_code(p, cur_val); - } else if (cur_chr == sf_code_base) { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - check_def_code(sf_code_base); - define_sf_code(p, cur_val); - } else if (cur_chr == cat_code_base) { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - check_def_code(cat_code_base); - define_cat_code(p, cur_val); - } else if (cur_chr == del_code_base) { - if (is_global(a)) - cur_val1 = level_one; - else - cur_val1 = cur_level; - scan_extdef_del_code(cur_val1, tex_mathcode); - } - break; - case extdef_math_code_cmd: - case extdef_del_code_cmd: - if (is_global(a)) - cur_val1 = level_one; - else - cur_val1 = cur_level; - if (cur_chr == math_code_base) - scan_extdef_math_code(cur_val1, umath_mathcode); - else if (cur_chr == math_code_base + 1) - scan_extdef_math_code(cur_val1, umathnum_mathcode); - else if (cur_chr == del_code_base) - scan_extdef_del_code(cur_val1, umath_mathcode); - else if (cur_chr == del_code_base + 1) - scan_extdef_del_code(cur_val1, umathnum_mathcode); - break; - case def_family_cmd: - p = cur_chr; - scan_math_family_int(); - cur_val1 = cur_val; - scan_optional_equals(); - scan_font_ident(); - define_fam_fnt(cur_val1, p, cur_val); - break; - case set_math_param_cmd: - p = cur_chr; - get_token(); - if (cur_cmd != math_style_cmd) { - print_err("Missing math style, treated as \\displaystyle"); - help1 - ("A style should have been here; I inserted `\\displaystyle'."); - cur_val1 = display_style; - back_error(); - } else { - cur_val1 = cur_chr; - } - scan_optional_equals(); - if (p < math_param_first_mu_glue) { - if (p == math_param_radical_degree_raise) - scan_int(); - else - scan_dimen(false, false, false); - } else { - scan_glue(mu_val_level); - if (cur_val == thin_mu_skip_par) - cur_val = thin_mu_skip_code; - else if (cur_val == med_mu_skip_par) - cur_val = med_mu_skip_code; - else if (cur_val == thick_mu_skip_par) - cur_val = thick_mu_skip_code; - } - define_math_param(p, cur_val1, cur_val); - break; - case register_cmd: - case advance_cmd: - case multiply_cmd: - case divide_cmd: - do_register_command(a); - break; - case set_box_cmd: - /* The processing of boxes is somewhat different, because we may need - to scan and create an entire box before we actually change the value - of the old one. */ - scan_register_num(); - if (is_global(a)) - n = global_box_flag + cur_val; - else - n = box_flag + cur_val; - scan_optional_equals(); - if (set_box_allowed) { - scan_box(n); - } else { - print_err("Improper \\setbox"); - help2("Sorry, \\setbox is not allowed after \\halign in a display,", - "or between \\accent and an accented character."); - error(); - } - break; - case set_aux_cmd: - /* The |space_factor| or |prev_depth| settings are changed when a |set_aux| - command is sensed. Similarly, |prev_graf| is changed in the presence of - |set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of - |set_page_int|. These definitions are always global. */ - alter_aux(); - break; - case set_prev_graf_cmd: - alter_prev_graf(); - break; - case set_page_dimen_cmd: - alter_page_so_far(); - break; - case set_page_int_cmd: - alter_integer(); - break; - case set_box_dimen_cmd: - /* When some dimension of a box register is changed, the change isn't exactly - global; but \TeX\ does not look at the \.{\\global} switch. */ - alter_box_dimen(); - break; - case set_tex_shape_cmd: - q = cur_chr; - scan_optional_equals(); - scan_int(); - n = cur_val; - if (n <= 0) { - p = null; - } else { - p = new_node(shape_node, 2 * (n + 1) + 1); - vinfo(p + 1) = n; - for (j = 1; j <= n; j++) { - scan_normal_dimen(); - varmem[p + 2 * j].cint = cur_val; /* indentation */ - scan_normal_dimen(); - varmem[p + 2 * j + 1].cint = cur_val; /* width */ - } - } - define(q, shape_ref_cmd, p); - break; - case set_etex_shape_cmd: - q = cur_chr; - scan_optional_equals(); - scan_int(); - n = cur_val; - if (n <= 0) { - p = null; - } else { - n = (cur_val / 2) + 1; - p = new_node(shape_node, 2 * n + 1 + 1); - vinfo(p + 1) = n; - n = cur_val; - varmem[p + 2].cint = n; /* number of penalties */ - for (j = p + 3; j <= p + n + 2; j++) { - scan_int(); - varmem[j].cint = cur_val; /* penalty values */ - } - if (!odd(n)) - varmem[p + n + 3].cint = 0; /* unused */ - } - define(q, shape_ref_cmd, p); - break; - case hyph_data_cmd: - /* All of \TeX's parameters are kept in |eqtb| except the font information, - the interaction mode, and the hyphenation tables; these are strictly global. - */ - switch (cur_chr) { - case 0: - new_hyph_exceptions(); - break; - case 1: - new_patterns(); - break; - case 2: - new_pre_hyphen_char(); - break; - case 3: - new_post_hyphen_char(); - break; - case 4: - new_pre_exhyphen_char(); - break; - case 5: - new_post_exhyphen_char(); - break; - case 6: - new_hyphenation_min(); - break; - case 7: - new_hj_code(); - break; - } - break; - case assign_font_dimen_cmd: - set_font_dimen(); - break; - case assign_font_int_cmd: - n = cur_chr; - scan_font_ident(); - f = cur_val; - if (n == no_lig_code) { - set_no_ligatures(f); - } else if (n < lp_code_base) { - scan_optional_equals(); - scan_int(); - if (n == 0) - set_hyphen_char(f, cur_val); - else - set_skew_char(f, cur_val); - } else { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - switch (n) { - case lp_code_base: - set_lp_code(f, p, cur_val); - break; - case rp_code_base: - set_rp_code(f, p, cur_val); - break; - case ef_code_base: - set_ef_code(f, p, cur_val); - break; - case tag_code: - set_tag_code(f, p, cur_val); - break; - } - } - break; - case def_font_cmd: - /* Here is where the information for a new font gets loaded. */ - tex_def_font((small_number) a); - break; - case letterspace_font_cmd: - new_letterspaced_font((small_number) a); - break; - case copy_font_cmd: - make_font_copy((small_number) a); - break; - case set_font_id_cmd: - scan_int(); - if (is_valid_font(cur_val)) - zset_cur_font(cur_val); - break ; - case set_interaction_cmd: - new_interaction(); - break; - default: - confusion("prefix"); - break; - } /* end of Assignments cases */ - DONE: - /* Insert a token saved by \.{\\afterassignment}, if any */ - if (after_token != 0) { - cur_tok = after_token; - back_input(); - after_token = 0; - } -} - -@ @c -void fixup_directions(void) -{ - int temp_no_whatsits = no_local_whatsits_par; - int temp_no_dirs = no_local_dirs_par; - int temporary_dir = text_direction_par; - if (dir_level(text_dir_ptr) == cur_level) { - /* DIR: Remove from |text_dir_ptr| */ - halfword text_dir_tmp = vlink(text_dir_ptr); - flush_node(text_dir_ptr); - text_dir_ptr = text_dir_tmp; - } - unsave(); - if (abs(mode) == hmode) { - if (temp_no_dirs != 0) { - /* DIR: Add local dir node */ - tail_append(new_dir(text_direction_par)); - dir_dir(tail) = temporary_dir - dir_swap; - } - if (temp_no_whatsits != 0) { - /* LOCAL: Add local paragraph node */ - tail_append(make_local_par_node(hmode_par_par_code)); - } - } -} - -@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or -something similar, the |get_r_token| routine will substitute a special -control sequence for a token that is not redefinable. - -@c -void get_r_token(void) -{ - RESTART: - do { - get_token(); - } while (cur_tok == space_token); - if ((cur_cs == 0) || (cur_cs > eqtb_top) || - ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) { - print_err("Missing control sequence inserted"); - help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.", - "I've inserted an inaccessible control sequence so that your", - "definition will be completed without mixing me up too badly.", - "You can recover graciously from this error, if you're", - "careful; see exercise 27.2 in The TeXbook."); - if (cur_cs == 0) - back_input(); - cur_tok = cs_token_flag + frozen_protection; - ins_error(); - goto RESTART; - } -} - -@ @c -void assign_internal_value(int a, halfword p, int val) -{ - halfword n; - if ((p >= int_base) && (p < attribute_base)) { - switch ((p - int_base)) { - case cat_code_table_code: - if (valid_catcode_table(val)) { - if (val != cat_code_table_par) - word_define(p, val); - } else { - print_err("Invalid \\catcode table"); - help2 - ("You can only switch to a \\catcode table that is initialized", - "using \\savecatcodetable or \\initcatcodetable, or to table 0"); - error(); - } - break; - case output_box_code: - if ((val > 65535) | (val < 0)) { - print_err("Invalid \\outputbox"); - help1 - ("The value for \\outputbox has to be between 0 and 65535."); - error(); - } else { - word_define(p, val); - } - break; - case new_line_char_code: - if (val > 127) { - print_err("Invalid \\newlinechar"); - help2 - ("The value for \\newlinechar has to be no higher than 127.", - "Your invalid assignment will be ignored."); - error(); - } else { - word_define(p, val); - } - break; - case end_line_char_code: - if (val > 127) { - print_err("Invalid \\endlinechar"); - help2 - ("The value for \\endlinechar has to be no higher than 127.", - "Your invalid assignment will be ignored."); - error(); - } else { - word_define(p, val); - } - break; - case language_code: - if (val < 0) { - word_define(int_base + cur_lang_code, -1); - word_define(p, -1); - } else if (val > 16383) { - print_err("Invalid \\language"); - help2 - ("The absolute value for \\language has to be no higher than 16383.", - "Your invalid assignment will be ignored."); - error(); - } else { - word_define(int_base + cur_lang_code, val); - word_define(p, val); - } - break; - default: - word_define(p, val); - break; - } - /* If we are defining subparagraph penalty levels while we are - in hmode, then we put out a whatsit immediately, otherwise - we leave it alone. This mechanism might not be sufficiently - powerful, and some other algorithm, searching down the stack, - might be necessary. Good first step. */ - if ((abs(mode) == hmode) && - ((p == (int_base + local_inter_line_penalty_code)) || - (p == (int_base + local_broken_penalty_code)))) { - /* LOCAL: Add local paragraph node */ - tail_append(make_local_par_node(penalty_par_code)); - eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); - } - } else if ((p >= dimen_base) && (p <= eqtb_size)) { - if (p == (dimen_base + page_left_offset_code)) { - n = val - one_true_inch; - word_define(dimen_base + h_offset_code, n); - } else if (p == (dimen_base + h_offset_code)) { - n = val + one_true_inch; - word_define(dimen_base + page_left_offset_code, n); - } else if (p == (dimen_base + page_top_offset_code)) { - n = val - one_true_inch; - word_define(dimen_base + v_offset_code, n); - } else if (p == (dimen_base + v_offset_code)) { - n = val + one_true_inch; - word_define(dimen_base + page_top_offset_code, n); - } - word_define(p, val); - } else if ((p >= local_base) && (p < toks_base)) { /* internal locals */ - define(p, call_cmd, val); - } else { - confusion("assign internal value"); - } -} - -@ We use the fact that |register<advance<multiply<divide| - -Compute the register location |l| and its type |p|; but |return| if invalid -Here we use the fact that the consecutive codes |int_val..mu_val| and -|assign_int..assign_mu_glue| correspond to each other nicely. - -@c -void do_register_command(int a) -{ - int p; - halfword q = cur_cmd; - halfword l = 0; - if (q != register_cmd) { - get_x_token(); - if ((cur_cmd >= assign_int_cmd) && (cur_cmd <= assign_mu_glue_cmd)) { - l = cur_chr; - p = cur_cmd - assign_int_cmd; - goto FOUND; - } - if (cur_cmd != register_cmd) { - print_err("You can't use `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - tprint("' after "); - print_cmd_chr((quarterword) q, 0); - help1("I'm forgetting what you said and not changing anything."); - error(); - return; - } - } - p = cur_chr; - scan_register_num(); - if (p == int_val_level) - l = cur_val + count_base; - else if (p == attr_val_level) - l = cur_val + attribute_base; - else if (p == dimen_val_level) - l = cur_val + scaled_base; - else if (p == glue_val_level) - l = cur_val + skip_base; - else if (p == mu_val_level) - l = cur_val + mu_skip_base; - FOUND: - if (q == register_cmd) { - scan_optional_equals(); - } else if (scan_keyword("by")) { - /* optional `\.{by}' */ - } - arith_error = false; - if (q < multiply_cmd) { - /* Compute result of |register| or |advance|, put it in |cur_val| */ - if (p < glue_val_level) { - if ((p == int_val_level) || (p == attr_val_level)) - scan_int(); - else - scan_normal_dimen(); - if (q == advance_cmd) - cur_val = cur_val + eqtb[l].cint; - } else { - /* we can probably save a copy */ - scan_glue(p); - if (q == advance_cmd) { - /* Compute the sum of two glue specs */ - halfword r = equiv(l); - q = new_spec(cur_val); - flush_node(cur_val); - width(q) = width(q) + width(r); - if (stretch(q) == 0) { - stretch_order(q) = normal; - } - if (stretch_order(q) == stretch_order(r)) { - stretch(q) = stretch(q) + stretch(r); - } else if ((stretch_order(q) < stretch_order(r)) && (stretch(r) != 0)) { - stretch(q) = stretch(r); - stretch_order(q) = stretch_order(r); - } - if (shrink(q) == 0) { - shrink_order(q) = normal; - } - if (shrink_order(q) == shrink_order(r)) { - shrink(q) = shrink(q) + shrink(r); - } else if ((shrink_order(q) < shrink_order(r)) && (shrink(r) != 0)) { - shrink(q) = shrink(r); - shrink_order(q) = shrink_order(r); - } - cur_val = q; - } - } - } else { - /* Compute result of |multiply| or |divide|, put it in |cur_val| */ - scan_int(); - if (p < glue_val_level) { - if (q == multiply_cmd) { - if ((p == int_val_level) || (p == attr_val_level)) { - cur_val = mult_integers(eqtb[l].cint, cur_val); - } else { - cur_val = nx_plus_y(eqtb[l].cint, cur_val, 0); - } - } else { - cur_val = x_over_n(eqtb[l].cint, cur_val); - } - } else { - halfword s = equiv(l); - halfword r = new_spec(s); - if (q == multiply_cmd) { - width(r) = nx_plus_y(width(s), cur_val, 0); - stretch(r) = nx_plus_y(stretch(s), cur_val, 0); - shrink(r) = nx_plus_y(shrink(s), cur_val, 0); - } else { - width(r) = x_over_n(width(s), cur_val); - stretch(r) = x_over_n(stretch(s), cur_val); - shrink(r) = x_over_n(shrink(s), cur_val); - } - cur_val = r; - } - } - if (arith_error) { - print_err("Arithmetic overflow"); - help2("I can't carry out that multiplication or division,", - "since the result is out of range."); - if (p >= glue_val_level) - flush_node(cur_val); - error(); - return; - } - if (p < glue_val_level) { - if (p == attr_val_level) { - if ((l - attribute_base) > max_used_attr) - max_used_attr = (l - attribute_base); - attr_list_cache = cache_disabled; - } - if ((p == int_val_level) || (p == dimen_val_level)) - assign_internal_value(a, l, cur_val); - else - word_define(l, cur_val); - } else { - define(l, glue_ref_cmd, cur_val); - } -} - -@ @c -void alter_aux(void) -{ - halfword c; /* |hmode| or |vmode| */ - if (cur_chr != abs(mode)) { - report_illegal_case(); - } else { - c = cur_chr; - scan_optional_equals(); - if (c == vmode) { - scan_normal_dimen(); - prev_depth_par = cur_val; - } else { - scan_int(); - if ((cur_val <= 0) || (cur_val > 32767)) { - print_err("Bad space factor"); - help1("I allow only values in the range 1..32767 here."); - int_error(cur_val); - } else { - space_factor_par = cur_val; - } - } - } -} - -@ @c -void alter_prev_graf(void) -{ - int p; /* index into |nest| */ - p = nest_ptr; - while (abs(nest[p].mode_field) != vmode) - decr(p); - scan_optional_equals(); - scan_int(); - if (cur_val < 0) { - print_err("Bad \\prevgraf"); - help1("I allow only nonnegative values here."); - int_error(cur_val); - } else { - nest[p].pg_field = cur_val; - } -} - -@ @c -void alter_page_so_far(void) -{ - int c; /* index into |page_so_far| */ - c = cur_chr; - scan_optional_equals(); - scan_normal_dimen(); - page_so_far[c] = cur_val; -} - -@ @c -void alter_integer(void) -{ - int c; /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */ - c = cur_chr; - scan_optional_equals(); - scan_int(); - if (c == 0) { - dead_cycles = cur_val; - } else if (c == 2) { - if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) { - print_err("Bad interaction mode"); - help2("Modes are 0=batch, 1=nonstop, 2=scroll, and", - "3=errorstop. Proceed, and I'll ignore this case."); - int_error(cur_val); - } else { - cur_chr = cur_val; - new_interaction(); - } - } else { - insert_penalties = cur_val; - } -} - -@ @c -void alter_box_dimen(void) -{ - int c; /* |width_offset| or |height_offset| or |depth_offset| */ - int b; /* box number */ - c = cur_chr; - scan_register_num(); - b = cur_val; - scan_optional_equals(); - scan_normal_dimen(); - if (box(b) != null) - varmem[box(b) + c].cint = cur_val; -} - -@ @c -void new_interaction(void) -{ - print_ln(); - interaction = cur_chr; - if (interaction == batch_mode) - kpse_make_tex_discard_errors = 1; - else - kpse_make_tex_discard_errors = 0; - fixup_selector(log_opened_global); -} - -@ The \.{\\afterassignment} command puts a token into the global -variable |after_token|. This global variable is examined just after -every assignment has been performed. - -@c -halfword after_token; /* zero, or a saved token */ - -@ Here is a procedure that might be called `Get the next non-blank non-relax -non-call non-assignment token'. - -@c -void do_assignments(void) -{ - while (true) { - /* Get the next non-blank non-relax... */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - if (cur_cmd <= max_non_prefixed_command) - return; - set_box_allowed = false; - prefixed_command(); - set_box_allowed = true; - } -} - -@ @c -void open_or_close_in(void) -{ - int c; /* 1 for \.{\\openin}, 0 for \.{\\closein} */ - int n; /* stream number */ - char *fn; - c = cur_chr; - scan_four_bit_int(); - n = cur_val; - if (read_open[n] != closed) { - lua_a_close_in(read_file[n], (n + 1)); - read_open[n] = closed; - } - if (c != 0) { - scan_optional_equals(); - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - back_input(); - if (cur_cmd != left_brace_cmd) { - scan_file_name(); /* set |cur_name| to desired file name */ - if (cur_ext == get_nullstr()) - cur_ext = maketexstring(".tex"); - } else { - scan_file_name_toks(); - } - fn = pack_file_name(cur_name, cur_area, cur_ext); - if (lua_a_open_in(&(read_file[n]), fn, (n + 1))) { - read_open[n] = just_open; - } - } -} - -@ @c -boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */ - -void issue_message(void) -{ - int old_setting; /* holds |selector| setting */ - int c; /* identifies \.{\\message} and \.{\\errmessage} */ - str_number s; /* the message */ - c = cur_chr; - (void) scan_toks(false, true); - old_setting = selector; - selector = new_string; - token_show(def_ref); - selector = old_setting; - flush_list(def_ref); - str_room(1); - s = make_string(); - if (c == 0) { - /* Print string |s| on the terminal */ - if (term_offset + (int) str_length(s) > max_print_line - 2) - print_ln(); - else if ((term_offset > 0) || (file_offset > 0)) - print_char(' '); - print(s); - update_terminal(); - - } else { - /* Print string |s| as an error message */ - /* If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined - \.{\\errhelp}, we don't want to give a long help message each time. So we - give a verbose explanation only once. */ - print_err(""); - print(s); - if (err_help_par != null) { - use_err_help = true; - } else if (long_help_seen) { - help1("(That was another \\errmessage.)"); - } else { - if (interaction < error_stop_mode) - long_help_seen = true; - help4("This error message was generated by an \\errmessage", - "command, so I can't give any explicit help.", - "Pretend that you're Hercule Poirot: Examine all clues,", - "and deduce the truth by order and method."); - } - error(); - use_err_help = false; - - } - flush_str(s); -} - -@ The |error| routine calls on |give_err_help| if help is requested from -the |err_help| parameter. - -@c -void give_err_help(void) -{ - token_show(err_help_par); -} - -@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by -building a token list and then changing the cases of the letters in it. - -@c -void shift_case(void) -{ - halfword b; /* |lc_code_base| or |uc_code_base| */ - halfword p; /* runs through the token list */ - halfword t; /* token */ - halfword c; /* character code */ - halfword i; /* inbetween */ - b = cur_chr; - p = scan_toks(false, false); - p = token_link(def_ref); - while (p != null) { - /* Change the case of the token in |p|, if a change is appropriate */ - /* - When the case of a |chr_code| changes, we don't change the |cmd|. - We also change active characters. - */ - t = token_info(p); - if (t < cs_token_flag) { - c = t % STRING_OFFSET; - if (b == uc_code_base) - i = get_uc_code(c); - else - i = get_lc_code(c); - if (i != 0) - set_token_info(p, t - c + i); - } else if (is_active_cs(cs_text(t - cs_token_flag))) { - c = active_cs_value(cs_text(t - cs_token_flag)); - if (b == uc_code_base) - i = get_uc_code(c); - else - i = get_lc_code(c); - if (i != 0) - set_token_info(p, active_to_cs(i, true) + cs_token_flag); - } - p = token_link(p); - } - back_list(token_link(def_ref)); - free_avail(def_ref); /* omit reference count */ -} - -@ We come finally to the last pieces missing from |main_control|, namely the -`\.{\\show}' commands that are useful when debugging. - -@c -void show_whatever(void) -{ - halfword p; /* tail of a token list to show */ - int t; /* type of conditional being shown */ - int m; /* upper bound on |fi_or_else| codes */ - int l; /* line where that conditional began */ - int n; /* level of \.{\\if...\\fi} nesting */ - switch (cur_chr) { - case show_lists: - begin_diagnostic(); - show_activities(); - break; - case show_box_code: - /* Show the current contents of a box */ - scan_register_num(); - begin_diagnostic(); - tprint_nl("> \\box"); - print_int(cur_val); - print_char('='); - if (box(cur_val) == null) - tprint("void"); - else - show_box(box(cur_val)); - break; - case show_code: - /* Show the current meaning of a token, then |goto common_ending| */ - get_token(); - if (interaction == error_stop_mode) - wake_up_terminal(); - tprint_nl("> "); - if (cur_cs != 0) { - sprint_cs(cur_cs); - print_char('='); - } - print_meaning(); - goto COMMON_ENDING; - break; - /* Cases for |show_whatever| */ - case show_groups: - begin_diagnostic(); - show_save_groups(); - break; - case show_ifs: - begin_diagnostic(); - tprint_nl(""); - print_ln(); - if (cond_ptr == null) { - tprint_nl("### "); - tprint("no active conditionals"); - } else { - p = cond_ptr; - n = 0; - do { - incr(n); - p = vlink(p); - } while (p != null); - p = cond_ptr; - t = cur_if; - l = if_line; - m = if_limit; - do { - tprint_nl("### level "); - print_int(n); - tprint(": "); - print_cmd_chr(if_test_cmd, t); - if (m == fi_code) - tprint_esc("else"); - print_if_line(l); - decr(n); - t = if_limit_subtype(p); - l = if_line_field(p); - m = if_limit_type(p); - p = vlink(p); - } while (p != null); - } - break; - default: - /* Show the current value of some parameter or register, - then |goto common_ending| */ - p = the_toks(); - if (interaction == error_stop_mode) - wake_up_terminal(); - tprint_nl("> "); - token_show(temp_token_head); - flush_list(token_link(temp_token_head)); - goto COMMON_ENDING; - break; - } - /* Complete a potentially long \.{\\show} command */ - end_diagnostic(true); - print_err("OK"); - if (selector == term_and_log) { - if (tracing_online_par <= 0) { - selector = term_only; - tprint(" (see the transcript file)"); - selector = term_and_log; - } - } - COMMON_ENDING: - if (interaction < error_stop_mode) { - help0(); - decr(error_count); - } else if (tracing_online_par > 0) { - help3("This isn't an error message; I'm just \\showing something.", - "Type `I\\show...' to show more (e.g., \\show\\cs,", - "\\showthe\\count10, \\showbox255, \\showlists)."); - } else { - help5("This isn't an error message; I'm just \\showing something.", - "Type `I\\show...' to show more (e.g., \\show\\cs,", - "\\showthe\\count10, \\showbox255, \\showlists).", - "And type `I\\tracingonline=1\\show...' to show boxes and", - "lists on your terminal as well as in the transcript file."); - } - error(); -} - -@ @c -void initialize(void) -{ /* this procedure gets things started properly */ - int k; /* index into |mem|, |eqtb|, etc. */ - /* Initialize whatever \TeX\ might access */ - /* Set initial values of key variables */ - initialize_errors(); - initialize_arithmetic(); - max_used_attr = -1; - attr_list_cache = cache_disabled; - initialize_nesting(); - - /* Start a new current page */ - page_contents = empty; - page_tail = page_head; -#if 0 - vlink(page_head) = null; -#endif - last_glue = max_halfword; - last_penalty = 0; - last_kern = 0; - last_node_type = -1; - page_depth = 0; - page_max_depth = 0; - - initialize_equivalents(); - no_new_control_sequence = true; /* new identifiers are usually forbidden */ - init_primitives(); - - mag_set = 0; - initialize_marks(); - initialize_read(); - - static_pdf = init_pdf_struct(static_pdf); /* should be init_backend() */ - - format_ident = 0; - format_name = get_nullstr(); - initialize_directions(); - initialize_write_files(); - seconds_and_micros(epochseconds, microseconds); - initialize_start_time(static_pdf); - - edit_name_start = 0; - stop_at_space = true; - - if (ini_version) { - /* Initialize table entries (done by \.{INITEX} only) */ - - init_node_mem(500); - initialize_tokens(); - /* Initialize the special list heads and constant nodes */ - initialize_alignments(); - initialize_buildpage(); - - initialize_active(); - - set_eq_type(undefined_control_sequence, undefined_cs_cmd); - set_equiv(undefined_control_sequence, null); - set_eq_level(undefined_control_sequence, level_zero); - for (k = null_cs; k <= (eqtb_top - 1); k++) - eqtb[k] = eqtb[undefined_control_sequence]; - set_equiv(glue_base, zero_glue); - set_eq_level(glue_base, level_one); - set_eq_type(glue_base, glue_ref_cmd); - for (k = glue_base + 1; k <= local_base - 1; k++) { - eqtb[k] = eqtb[glue_base]; - } - par_shape_par_ptr = null; - set_eq_type(par_shape_loc, shape_ref_cmd); - set_eq_level(par_shape_loc, level_one); - for (k = etex_pen_base; k <= (etex_pens - 1); k++) - eqtb[k] = eqtb[par_shape_loc]; - for (k = output_routine_loc; k <= toks_base + biggest_reg; k++) - eqtb[k] = eqtb[undefined_control_sequence]; - box(0) = null; - set_eq_type(box_base, box_ref_cmd); - set_eq_level(box_base, level_one); - for (k = box_base + 1; k <= (box_base + biggest_reg); k++) - eqtb[k] = eqtb[box_base]; - cur_font_par = null_font; - set_eq_type(cur_font_loc, data_cmd); - set_eq_level(cur_font_loc, level_one); - set_equiv(cat_code_base, 0); - set_eq_type(cat_code_base, data_cmd); - set_eq_level(cat_code_base, level_one); - eqtb[internal_math_param_base] = eqtb[cat_code_base]; - eqtb[lc_code_base] = eqtb[cat_code_base]; - eqtb[uc_code_base] = eqtb[cat_code_base]; - eqtb[sf_code_base] = eqtb[cat_code_base]; - eqtb[math_code_base] = eqtb[cat_code_base]; - cat_code_table_par = 0; - initialize_math_codes(); - initialize_text_codes(); - initex_cat_codes(0); - for (k = '0'; k <= '9'; k++) - set_math_code(k, math_use_current_family_code, 0, k, level_one); - for (k = 'A'; k <= 'Z'; k++) { - set_math_code(k, math_use_current_family_code, 1, k, level_one); - set_math_code((k + 32), math_use_current_family_code, 1, (k + 32), level_one); - set_lc_code(k, k + 32, level_one); - set_lc_code(k + 32, k + 32, level_one); - set_uc_code(k, k, level_one); - set_uc_code(k + 32, k, level_one); - set_sf_code(k, 999, level_one); - } - for (k = int_base; k <= attribute_base - 1; k++) - eqtb[k].cint = 0; - for (k = attribute_base; k <= del_code_base - 1; k++) - eqtb[k].cint = UNUSED_ATTRIBUTE; - mag_par = 1000; - tolerance_par = 10000; - hang_after_par = 1; - max_dead_cycles_par = 25; - escape_char_par = '\\'; - end_line_char_par = carriage_return; - set_del_code('.', 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */ - ex_hyphen_char_par = '-'; - output_box_par = 255; - for (k = dimen_base; k <= eqtb_size; k++) - eqtb[k].cint = 0; - page_left_offset_par = one_inch; - page_top_offset_par = one_inch; - page_right_offset_par = one_inch; - page_bottom_offset_par = one_inch; - ini_init_primitives(); - hash_used = frozen_control_sequence; /* nothing is used */ - hash_high = 0; - cs_count = 0; - set_eq_type(frozen_dont_expand, dont_expand_cmd); - cs_text(frozen_dont_expand) = maketexstring("notexpanded:"); - set_eq_type(frozen_primitive, ignore_spaces_cmd); - set_equiv(frozen_primitive, 1); - set_eq_level(frozen_primitive, level_one); - cs_text(frozen_primitive) = maketexstring("primitive"); - create_null_font(); - font_bytes = 0; - px_dimen_par = one_bp; - math_eqno_gap_step_par = 1000 ; - cs_text(frozen_protection) = maketexstring("inaccessible"); - format_ident = maketexstring(" (INITEX)"); - cs_text(end_write) = maketexstring("endwrite"); - set_eq_level(end_write, level_one); - set_eq_type(end_write, outer_call_cmd); - set_equiv(end_write, null); - - } - synctexoffset = int_base + synctex_code; - -} +% maincontrol.w
+%
+% Copyright 2009-2010 Taco Hoekwater <taco@@luatex.org>
+%
+% This file is part of LuaTeX.
+%
+% LuaTeX is free software; you can redistribute it and/or modify it under
+% the terms of the GNU General Public License as published by the Free
+% Software Foundation; either version 2 of the License, or (at your
+% option) any later version.
+%
+% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+% License for more details.
+%
+% You should have received a copy of the GNU General Public License along
+% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+@ @c
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+@ @c
+#define mode mode_par
+#define tail tail_par
+#define head head_par
+#define dir_save dirs_par
+
+@ We come now to the |main_control| routine, which contains the master
+switch that causes all the various pieces of \TeX\ to do their things,
+in the right order.
+
+In a sense, this is the grand climax of the program: It applies all the
+tools that we have worked so hard to construct. In another sense, this is
+the messiest part of the program: It necessarily refers to other pieces
+of code all over the place, so that a person can't fully understand what is
+going on without paging back and forth to be reminded of conventions that
+are defined elsewhere. We are now at the hub of the web, the central nervous
+system that touches most of the other parts and ties them together.
+@^brain@>
+
+The structure of |main_control| itself is quite simple. There's a label
+called |big_switch|, at which point the next token of input is fetched
+using |get_x_token|. Then the program branches at high speed into one of
+about 100 possible directions, based on the value of the current
+mode and the newly fetched command code; the sum |abs(mode)+cur_cmd|
+indicates what to do next. For example, the case `|vmode+letter|' arises
+when a letter occurs in vertical mode (or internal vertical mode); this
+case leads to instructions that initialize a new paragraph and enter
+horizontal mode.
+
+The big |case| statement that contains this multiway switch has been labeled
+|reswitch|, so that the program can |goto reswitch| when the next token
+has already been fetched. Most of the cases are quite short; they call
+an ``action procedure'' that does the work for that case, and then they
+either |goto reswitch| or they ``fall through'' to the end of the |case|
+statement, which returns control back to |big_switch|. Thus, |main_control|
+is not an extremely large procedure, in spite of the multiplicity of things
+it must do; it is small enough to be handled by PASCAL compilers that put
+severe restrictions on procedure size.
+@!@^action procedure@>
+
+One case is singled out for special treatment, because it accounts for most
+of \TeX's activities in typical applications. The process of reading simple
+text and converting it into |char_node| records, while looking for ligatures
+and kerns, is part of \TeX's ``inner loop''; the whole program runs
+efficiently when its inner loop is fast, so this part has been written
+with particular care.
+
+We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we
+set it equal to |sf_code(cur_chr)|, except that it should never change
+from a value less than 1000 to a value exceeding 1000. The most common
+case is |sf_code(cur_chr)=1000|, so we want that case to be fast.
+
+@c
+void adjust_space_factor(void)
+{
+ halfword s = get_sf_code(cur_chr);
+ if (s == 1000) {
+ space_factor_par = 1000;
+ } else if (s < 1000) {
+ if (s > 0)
+ space_factor_par = s;
+ } else if (space_factor_par < 1000) {
+ space_factor_par = 1000;
+ } else {
+ space_factor_par = s;
+ }
+}
+
+@ From Knuth: ``Having |font_glue| allocated for each text font saves
+both time and memory.'' That may be true, but it also punches through
+the API wall for fonts, so I removed that -- Taco. But a bit of caching
+is very welcome, which is why I need to have the next two globals:
+
+@ To handle the execution state of |main_control|'s eternal loop,
+an extra global variable is used, along with a macro to define
+its values.
+
+@c
+#define goto_next 0
+#define goto_skip_token 1
+#define goto_return 2
+
+static int main_control_state;
+
+@* Main control helpers.
+
+Here are all the functions that are called from |main_control| that
+are not already defined elsewhere. For the moment, this list simply
+in the order that the appear in |init_main_control|, below.
+
+@
+@c
+static void run_char_num (void) {
+ scan_char_num();
+ cur_chr = cur_val;
+ adjust_space_factor();
+ tail_append(new_char(cur_font_par, cur_chr));
+}
+
+static void run_char (void) {
+ adjust_space_factor();
+ tail_append(new_char(cur_font_par, cur_chr));
+}
+
+@
+The occurrence of blank spaces is almost part of \TeX's inner loop,
+since we usually encounter about one space for every five non-blank characters.
+Therefore |main_control| gives second-highest priority to ordinary spaces.
+
+When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will
+see to it later that the corresponding glue specification is precisely
+|zero_glue|, not merely a pointer to some specification that happens
+to be full of zeroes. Therefore it is simple to test whether a glue parameter
+is zero or~not.
+
+@c
+static void run_app_space (void) {
+ halfword p; /* was a global temp_ptr */
+ int method = disable_space_par ;
+ if (method == 1) {
+ /* don't inject anything, not even zero skip */
+ } else if (method == 2) {
+ p = new_glue(zero_glue);
+ couple_nodes(tail,p);
+ tail = p;
+ } else if ((abs(mode) + cur_cmd == hmode + spacer_cmd) && (!(space_factor_par == 1000))) {
+ app_space();
+ } else {
+ /* Append a normal inter-word space to the current list */
+ if (glue_is_zero(space_skip_par)) {
+ /* Find the glue specification for text spaces in the current font */
+ p = new_glue(zero_glue);
+ width(p) = space(cur_font_par);
+ stretch(p) = space_stretch(cur_font_par);
+ shrink(p) = space_shrink(cur_font_par);
+
+ } else {
+ p = new_param_glue(space_skip_code);
+ }
+ /* so from now we have a subtype with spaces: */
+ subtype(p) = space_skip_code + 1 ;
+ couple_nodes(tail,p);
+ tail = p;
+ }
+}
+
+@ Append a |boundary_node|
+@c
+static void run_boundary (void) {
+ halfword n ;
+ n = new_node(boundary_node,cur_chr);
+ if ((cur_chr == 1) || (cur_chr == 2) ) {
+ /* user boundary or protrusion boundary */
+ scan_int();
+ boundary_value(n) = cur_val;
+ }
+ couple_nodes(tail, n);
+ tail = n;
+}
+
+@ @c
+static void run_char_ghost (void) {
+ int t;
+ t = cur_chr;
+ get_x_token();
+ if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd)
+ || (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) {
+ halfword p = new_glyph(get_cur_font(), cur_chr);
+ if (t == 0) {
+ set_is_leftghost(p);
+ } else {
+ set_is_rightghost(p);
+ }
+ tail_append(p);
+ }
+}
+
+@ @c
+static void run_relax (void) {
+ return;
+}
+
+@ |ignore_spaces| is a special case: after it has acted, |get_x_token| has already
+fetched the next token from the input, so that operation in |main_control|
+should be skipped.
+
+@c
+static void run_ignore_spaces (void) {
+ if (cur_chr == 0) {
+ /* Get the next non-blank non-call... */
+ do {
+ get_x_token();
+ } while (cur_cmd == spacer_cmd);
+ main_control_state = goto_skip_token;
+ } else {
+ int t = scanner_status;
+ scanner_status = normal;
+ get_next();
+ scanner_status = t;
+ cur_cs = prim_lookup(cs_text(cur_cs));
+ if (cur_cs != undefined_primitive) {
+ cur_cmd = get_prim_eq_type(cur_cs);
+ cur_chr = get_prim_equiv(cur_cs);
+ cur_tok = (cur_cmd * STRING_OFFSET) + cur_chr;
+ main_control_state = goto_skip_token;
+ }
+ }
+}
+
+@ |stop| is the second special case. We want |main_control| to return to its caller
+if there is nothing left to do.
+
+@c
+static void run_stop (void) {
+ if (its_all_over())
+ main_control_state= goto_return; /* this is the only way out */
+}
+
+@ @c
+static void run_non_math_math (void) {
+ back_input();
+ new_graf(true);
+}
+
+@ @c
+static void run_math_char_num (void) {
+ mathcodeval mval; /* to build up an argument to |set_math_char| */
+ if (cur_chr == 0)
+ mval = scan_mathchar(tex_mathcode);
+ else if (cur_chr == 1)
+ mval = scan_mathchar(umath_mathcode);
+ else
+ mval = scan_mathchar(umathnum_mathcode);
+ math_char_in_text(mval);
+}
+
+@ @c
+static void run_math_given (void) {
+ mathcodeval mval; /* to build up an argument to |set_math_char| */
+ mval = mathchar_from_integer(cur_chr, tex_mathcode);
+ math_char_in_text(mval);
+}
+
+static void run_xmath_given (void) {
+ mathcodeval mval; /* to build up an argument to |set_math_char| */
+ mval = mathchar_from_integer(cur_chr, umath_mathcode);
+ math_char_in_text(mval);
+}
+
+@ The most important parts of |main_control| are concerned with \TeX's
+chief mission of box-making. We need to control the activities that put
+entries on vlists and hlists, as well as the activities that convert
+those lists into boxes. All of the necessary machinery has already been
+developed; it remains for us to ``push the buttons'' at the right times.
+
+As an introduction to these routines, let's consider one of the simplest
+cases: What happens when `\.{\\hrule}' occurs in vertical mode, or
+`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control|
+is short, since the |scan_rule_spec| routine already does most of what is
+required; thus, there is no need for a special action procedure.
+
+Note that baselineskip calculations are disabled after a rule in vertical
+mode, by setting |prev_depth:=ignore_depth|.
+
+@c
+static void run_rule (void) {
+ tail_append(scan_rule_spec());
+ if (abs(mode) == vmode)
+ prev_depth_par = ignore_depth;
+ else if (abs(mode) == hmode)
+ space_factor_par = 1000;
+}
+
+@
+Many of the actions related to box-making are triggered by the appearance
+of braces in the input. For example, when the user says `\.{\\hbox}
+\.{to} \.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode,
+the information about the box size (100pt, |exactly|) is put onto |save_stack|
+with a level boundary word just above it, and |cur_group:=adjusted_hbox_group|;
+\TeX\ enters restricted horizontal mode to process the hlist. The right
+brace eventually causes |save_stack| to be restored to its former state,
+at which time the information about the box size (100pt, |exactly|) is
+available once again; a box is packaged and we leave restricted horizontal
+mode, appending the new box to the current list of the enclosing mode
+(in this case to the current list of vertical mode), followed by any
+vertical adjustments that were removed from the box by |hpack|.
+
+The next few sections of the program are therefore concerned with the
+treatment of left and right curly braces.
+
+If a left brace occurs in the middle of a page or paragraph, it simply
+introduces a new level of grouping, and the matching right brace will not have
+such a drastic effect. Such grouping affects neither the mode nor the
+current list.
+
+@c
+static void run_left_brace (void) {
+ new_save_level(simple_group);
+ eq_word_define(int_base + no_local_whatsits_code, 0);
+ eq_word_define(int_base + no_local_dirs_code, 0);
+}
+
+static void run_begin_group (void) {
+ new_save_level(semi_simple_group);
+ eq_word_define(int_base + no_local_whatsits_code, 0);
+ eq_word_define(int_base + no_local_dirs_code, 0);
+}
+
+static void run_end_group (void) {
+ if (cur_group == semi_simple_group) {
+ fixup_directions();
+ } else {
+ off_save();
+ }
+}
+
+@ Constructions that require a box are started by calling |scan_box| with
+a specified context code. The |scan_box| routine verifies
+that a |make_box| command comes next and then it calls |begin_box|.
+
+@c
+static void run_move (void) {
+ int t = cur_chr;
+ scan_normal_dimen();
+ if (t == 0)
+ scan_box(cur_val);
+ else
+ scan_box(-cur_val);
+}
+
+@ @c
+static void run_leader_ship (void) {
+ scan_box(leader_flag - a_leaders + cur_chr);
+}
+
+@ @c
+static void run_make_box (void) {
+ begin_box(0);
+}
+
+@ @c
+static void run_box_dir (void) {
+ scan_register_num();
+ cur_box = box(cur_val);
+ scan_optional_equals();
+ scan_direction();
+ if (cur_box != null)
+ box_dir(cur_box) = cur_val;
+}
+
+@ There is a really small patch to add a new primitive called
+\.{\\quitvmode}. In vertical modes, it is identical to \.{\\indent},
+but in horizontal and math modes it is really a no-op (as opposed to
+\.{\\indent}, which executes the |indent_in_hmode| procedure).
+
+A paragraph begins when horizontal-mode material occurs in vertical mode,
+or when the paragraph is explicitly started by `\.{\\quitvmode}',
+`\.{\\indent}' or `\.{\\noindent}'.
+
+@c
+static void run_start_par_vmode (void) {
+ new_graf((cur_chr > 0));
+}
+
+@ @c
+static void run_start_par (void) {
+ if (cur_chr != 2)
+ indent_in_hmode();
+}
+
+@ @c
+static void run_new_graf (void) {
+ back_input();
+ new_graf(true);
+}
+
+@ A paragraph ends when a |par_end| command is sensed, or when we are in
+horizontal mode when reaching the right brace of vertical-mode routines
+like \.{\\vbox}, \.{\\insert}, or \.{\\output}.
+
+@c
+static void run_par_end_vmode (void) {
+ normal_paragraph();
+ if (mode > 0) {
+ checked_page_filter(vmode_par);
+ build_page();
+ }
+}
+
+@ @c
+static void run_par_end_hmode (void) {
+ if (align_state < 0)
+ off_save(); /* this tries to recover from an alignment that didn't end properly */
+ end_graf(bottom_level); /* this takes us to the enclosing mode, if |mode>0| */
+ if (mode == vmode) {
+ checked_page_filter(hmode_par);
+ build_page();
+ }
+}
+
+@ @c
+static void append_italic_correction_mmode (void) {
+ tail_append(new_kern(0)); /* what subtype to use */
+}
+
+@ @c
+static void run_local_box (void) {
+ append_local_box(cur_chr);
+}
+
+@ @c
+static void run_halign_mmode (void) {
+ if (privileged()) {
+ if (cur_group == math_shift_group)
+ init_align();
+ else
+ off_save();
+ }
+}
+
+@ @c
+static void run_eq_no (void) {
+ if (privileged()) {
+ if (cur_group == math_shift_group)
+ start_eq_no();
+ else
+ off_save();
+ }
+}
+
+@ @c
+static void run_letter_mmode (void) {
+ set_math_char(get_math_code(cur_chr));
+}
+
+@ @c
+static void run_char_num_mmode (void) {
+ scan_char_num();
+ cur_chr = cur_val;
+ set_math_char(get_math_code(cur_chr));
+}
+
+@ @c
+static void run_math_char_num_mmode (void) {
+ mathcodeval mval; /* to build up an argument to |set_math_char| */
+ if (cur_chr == 0)
+ mval = scan_mathchar(tex_mathcode);
+ else if (cur_chr == 1)
+ mval = scan_mathchar(umath_mathcode);
+ else
+ mval = scan_mathchar(umathnum_mathcode);
+ set_math_char(mval);
+}
+
+@ @c
+static void run_math_given_mmode (void) {
+ mathcodeval mval; /* to build up an argument to |set_math_char| */
+ mval = mathchar_from_integer(cur_chr, tex_mathcode);
+ set_math_char(mval);
+}
+
+static void run_xmath_given_mmode (void) {
+ mathcodeval mval; /* to build up an argument to |set_math_char| */
+ mval = mathchar_from_integer(cur_chr, umath_mathcode);
+ set_math_char(mval);
+}
+
+@ @c
+static void run_delim_num (void) {
+ mathcodeval mval; /* to build up an argument to |set_math_char| */
+ if (cur_chr == 0)
+ mval = scan_delimiter_as_mathchar(tex_mathcode);
+ else
+ mval = scan_delimiter_as_mathchar(umath_mathcode);
+ set_math_char(mval);
+
+}
+
+@ @c
+static void run_vcenter (void) {
+ scan_spec(vcenter_group);
+ normal_paragraph();
+ push_nest();
+ mode = -vmode;
+ prev_depth_par = ignore_depth;
+ if (every_vbox_par != null)
+ begin_token_list(every_vbox_par, every_vbox_text);
+}
+
+@ @c
+static void run_math_style (void) {
+ tail_append(new_style((small_number) cur_chr));
+}
+
+@ @c
+static void run_non_script (void) {
+ tail_append(new_glue(zero_glue));
+ subtype(tail) = cond_math_glue;
+}
+
+@ @c
+static void run_math_choice (void) {
+ if (cur_chr == 0)
+ append_choices();
+ else
+ setup_math_style();
+}
+
+@ @c
+static void run_math_shift (void) {
+ if (cur_group == math_shift_group)
+ after_math();
+ else
+ off_save();
+}
+
+@ @c
+static void run_after_assignment (void) {
+ get_token();
+ after_token = cur_tok;
+}
+
+@ @c
+static void run_after_group (void) {
+ get_token();
+ save_for_after(cur_tok);
+}
+
+@ @c
+static void run_extension (void) {
+ do_extension(0);
+}
+
+static void run_normal (void) {
+{
+ switch (cur_chr) {
+ case save_pos_code:
+ new_whatsit(save_pos_node);
+ break;
+ case save_cat_code_table_code:
+ scan_int();
+ if ((cur_val < 0) || (cur_val > 0x7FFF)) {
+ print_err("Invalid \\catcode table");
+ help1("All \\catcode table ids must be between 0 and 0x7FFF");
+ error();
+ } else {
+ if (cur_val == cat_code_table_par) {
+ print_err("Invalid \\catcode table");
+ help1("You cannot overwrite the current \\catcode table");
+ error();
+ } else {
+ copy_cat_codes(cat_code_table_par, cur_val);
+ }
+ }
+ break;
+ case init_cat_code_table_code:
+ scan_int();
+ if ((cur_val < 0) || (cur_val > 0x7FFF)) {
+ print_err("Invalid \\catcode table");
+ help1("All \\catcode table ids must be between 0 and 0x7FFF");
+ error();
+ } else {
+ if (cur_val == cat_code_table_par) {
+ print_err("Invalid \\catcode table");
+ help1("You cannot overwrite the current \\catcode table");
+ error();
+ } else {
+ initex_cat_codes(cur_val);
+ }
+ }
+ break;
+ case set_random_seed_code:
+ /* Negative random seed values are silently converted to positive ones */
+ scan_int();
+ if (cur_val < 0)
+ negate(cur_val);
+ random_seed = cur_val;
+ init_randoms(random_seed);
+ break;
+ case late_lua_code:
+ new_whatsit(late_lua_node); /* type == normal */
+ late_lua_name(tail) = scan_lua_state();
+ (void) scan_toks(false, false);
+ late_lua_data(tail) = def_ref;
+ break;
+ case expand_font_code:
+ read_expand_font();
+ break;
+ default:
+ confusion("int1");
+ break;
+ }
+ }
+}
+
+/*
+ this is experimental and not used for production, only for testing and writing
+ macros (some options stay)
+
+*/
+
+#define mathoption_set_int(A) \
+ scan_int(); \
+ word_define(mathoption_int_base+A, cur_val);
+
+static void run_option(void) {
+ int a = 0 ;
+ switch (cur_chr) {
+ case math_option_code:
+ if (scan_keyword("old")) {
+ mathoption_set_int(c_mathoption_old_code);
+ } else if (scan_keyword("noitaliccompensation")) {
+ mathoption_set_int(c_mathoption_no_italic_compensation_code);
+ } else if (scan_keyword("nocharitalic")) {
+ mathoption_set_int(c_mathoption_no_char_italic_code);
+ } else if (scan_keyword("useoldfractionscaling")) {
+ mathoption_set_int(c_mathoption_use_old_fraction_scaling_code);
+ } else if (scan_keyword("umathcodemeaning")) {
+ mathoption_set_int(c_mathoption_umathcode_meaning_code);
+ } else {
+ normal_warning("mathoption","unknown key");
+ }
+ break;
+ default:
+ /* harmless */
+ break;
+ }
+}
+
+@ For mode-independent commands, the following macro is useful.
+
+Also, there is a list of cases where the user has probably gotten into or out of math
+mode by mistake. \TeX\ will insert a dollar sign and rescan the current token, and
+it makes sense ot have a macro for that as well.
+
+@c
+#define any_mode(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; jump_table[mmode+(A)]=B
+#define non_math(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B;
+
+
+@ The |main_control| uses a jump table, and |init_main_control| sets that table up.
+@c
+typedef void (*main_control_function) (void);
+main_control_function *jump_table;
+
+static void init_main_control (void) {
+ jump_table = xmalloc((mmode+max_command_cmd+1) * sizeof(main_control_function)) ;
+
+ jump_table[hmode + char_num_cmd] = run_char_num;
+ jump_table[hmode + letter_cmd] = run_char;
+ jump_table[hmode + other_char_cmd] = run_char;
+ jump_table[hmode + char_given_cmd] = run_char;
+ jump_table[hmode + spacer_cmd] = run_app_space;
+ jump_table[hmode + ex_space_cmd] = run_app_space;
+ jump_table[mmode + ex_space_cmd] = run_app_space;
+ jump_table[hmode + boundary_cmd] = run_boundary;
+ jump_table[hmode + char_ghost_cmd] = run_char_ghost;
+ jump_table[mmode + char_ghost_cmd] = run_char_ghost;
+ any_mode(relax_cmd, run_relax);
+ jump_table[vmode + spacer_cmd] = run_relax;
+ jump_table[mmode + spacer_cmd] = run_relax;
+ jump_table[mmode + boundary_cmd] = run_relax;
+ any_mode(ignore_spaces_cmd,run_ignore_spaces);
+ jump_table[vmode + stop_cmd] = run_stop;
+ jump_table[vmode + math_char_num_cmd] = run_non_math_math;
+ jump_table[vmode + math_given_cmd] = run_non_math_math;
+ jump_table[vmode + xmath_given_cmd] = run_non_math_math;
+ jump_table[hmode + math_char_num_cmd] = run_math_char_num;
+ jump_table[hmode + math_given_cmd] = run_math_given;
+ jump_table[hmode + xmath_given_cmd] = run_xmath_given;
+
+ jump_table[vmode + vmove_cmd] = report_illegal_case;
+ jump_table[hmode + hmove_cmd] = report_illegal_case;
+ jump_table[mmode + hmove_cmd] = report_illegal_case;
+ any_mode(last_item_cmd, report_illegal_case);
+ jump_table[vmode + vadjust_cmd] = report_illegal_case;
+ jump_table[vmode + ital_corr_cmd] = report_illegal_case;
+ non_math(eq_no_cmd,report_illegal_case);
+ any_mode(mac_param_cmd,report_illegal_case);
+
+ non_math(sup_mark_cmd, insert_dollar_sign);
+ non_math(sub_mark_cmd, insert_dollar_sign);
+ non_math(super_sub_script_cmd, insert_dollar_sign);
+ non_math(math_comp_cmd, insert_dollar_sign);
+ non_math(delim_num_cmd, insert_dollar_sign);
+ non_math(left_right_cmd, insert_dollar_sign);
+ non_math(above_cmd, insert_dollar_sign);
+ non_math(radical_cmd, insert_dollar_sign);
+ non_math(math_style_cmd, insert_dollar_sign);
+ non_math(math_choice_cmd, insert_dollar_sign);
+ non_math(vcenter_cmd, insert_dollar_sign);
+ non_math(non_script_cmd, insert_dollar_sign);
+ non_math(mkern_cmd, insert_dollar_sign);
+ non_math(limit_switch_cmd, insert_dollar_sign);
+ non_math(mskip_cmd, insert_dollar_sign);
+ non_math(math_accent_cmd, insert_dollar_sign);
+ jump_table[mmode + endv_cmd] = insert_dollar_sign;
+ jump_table[mmode + par_end_cmd] = insert_dollar_sign_par_end;
+ jump_table[mmode + stop_cmd] = insert_dollar_sign;
+ jump_table[mmode + vskip_cmd] = insert_dollar_sign;
+ jump_table[mmode + un_vbox_cmd] = insert_dollar_sign;
+ jump_table[mmode + valign_cmd] = insert_dollar_sign;
+ jump_table[mmode + hrule_cmd] = insert_dollar_sign;
+ jump_table[mmode + no_hrule_cmd] = insert_dollar_sign;
+ jump_table[vmode + hrule_cmd] = run_rule;
+ jump_table[vmode + no_hrule_cmd] = run_rule;
+ jump_table[hmode + vrule_cmd] = run_rule;
+ jump_table[hmode + no_vrule_cmd] = run_rule;
+ jump_table[mmode + vrule_cmd] = run_rule;
+ jump_table[mmode + no_vrule_cmd] = run_rule;
+ jump_table[vmode + vskip_cmd] = append_glue;
+ jump_table[hmode + hskip_cmd] = append_glue;
+ jump_table[mmode + hskip_cmd] = append_glue;
+ jump_table[mmode + mskip_cmd] = append_glue;
+ any_mode(kern_cmd, append_kern);
+ jump_table[mmode + mkern_cmd] = append_kern;
+ non_math(left_brace_cmd, run_left_brace);
+ any_mode(begin_group_cmd,run_begin_group);
+ any_mode(end_group_cmd, run_end_group);
+ any_mode(right_brace_cmd, handle_right_brace);
+ jump_table[vmode + hmove_cmd] = run_move;
+ jump_table[hmode + vmove_cmd] = run_move;
+ jump_table[mmode + vmove_cmd] = run_move;
+ any_mode(leader_ship_cmd, run_leader_ship);
+ any_mode(make_box_cmd, run_make_box);
+ any_mode(assign_box_dir_cmd, run_box_dir);
+ jump_table[vmode + start_par_cmd] = run_start_par_vmode;
+ jump_table[hmode + start_par_cmd] = run_start_par;
+ jump_table[mmode + start_par_cmd] = run_start_par;
+ jump_table[vmode + letter_cmd] = run_new_graf;
+ jump_table[vmode + other_char_cmd] = run_new_graf;
+ jump_table[vmode + char_num_cmd] = run_new_graf;
+ jump_table[vmode + char_given_cmd] = run_new_graf;
+ jump_table[vmode + char_ghost_cmd] = run_new_graf;
+ jump_table[vmode + math_shift_cmd] = run_new_graf;
+ jump_table[vmode + math_shift_cs_cmd] = run_new_graf;
+ jump_table[vmode + un_hbox_cmd] = run_new_graf;
+ jump_table[vmode + vrule_cmd] = run_new_graf;
+ jump_table[vmode + no_vrule_cmd] = run_new_graf;
+ jump_table[vmode + accent_cmd] = run_new_graf;
+ jump_table[vmode + discretionary_cmd] = run_new_graf;
+ jump_table[vmode + hskip_cmd] = run_new_graf;
+ jump_table[vmode + valign_cmd] = run_new_graf;
+ jump_table[vmode + ex_space_cmd] = run_new_graf;
+ jump_table[vmode + boundary_cmd] = run_new_graf;
+ jump_table[vmode + par_end_cmd] = run_par_end_vmode;
+ jump_table[hmode + par_end_cmd] = run_par_end_hmode;
+ jump_table[hmode + stop_cmd] = head_for_vmode;
+ jump_table[hmode + vskip_cmd] = head_for_vmode;
+ jump_table[hmode + hrule_cmd] = head_for_vmode;
+ jump_table[hmode + no_hrule_cmd] = head_for_vmode;
+ jump_table[hmode + un_vbox_cmd] = head_for_vmode;
+ jump_table[hmode + halign_cmd] = head_for_vmode;
+ any_mode(insert_cmd,begin_insert_or_adjust);
+ jump_table[hmode + vadjust_cmd] = begin_insert_or_adjust;
+ jump_table[mmode + vadjust_cmd] = begin_insert_or_adjust;
+ any_mode(mark_cmd, handle_mark);
+ any_mode(break_penalty_cmd, append_penalty);
+ any_mode(remove_item_cmd, delete_last);
+ jump_table[vmode + un_vbox_cmd] = unpackage;
+ jump_table[hmode + un_hbox_cmd] = unpackage;
+ jump_table[mmode + un_hbox_cmd] = unpackage;
+ jump_table[hmode + ital_corr_cmd] = append_italic_correction;
+ jump_table[mmode + ital_corr_cmd] = append_italic_correction_mmode;
+ jump_table[hmode + discretionary_cmd] = append_discretionary;
+ jump_table[mmode + discretionary_cmd] = append_discretionary;
+ any_mode(assign_local_box_cmd, run_local_box);
+ jump_table[hmode + accent_cmd] = make_accent;
+ any_mode(car_ret_cmd,align_error);
+ any_mode(tab_mark_cmd,align_error);
+ any_mode(no_align_cmd,no_align_error);
+ any_mode(omit_cmd, omit_error);
+ jump_table[vmode + halign_cmd] = init_align;
+ jump_table[hmode + valign_cmd] = init_align;
+ jump_table[mmode + halign_cmd] = run_halign_mmode;
+ jump_table[vmode + endv_cmd] = do_endv;
+ jump_table[hmode + endv_cmd] = do_endv;
+ any_mode(end_cs_name_cmd, cs_error);
+ jump_table[hmode + math_shift_cmd] = init_math;
+ jump_table[hmode + math_shift_cs_cmd] = init_math;
+ jump_table[mmode + eq_no_cmd] = run_eq_no;
+ jump_table[mmode + left_brace_cmd] = math_left_brace;
+ jump_table[mmode + letter_cmd] = run_letter_mmode;
+ jump_table[mmode + other_char_cmd] = run_letter_mmode;
+ jump_table[mmode + char_given_cmd] = run_letter_mmode;
+ jump_table[mmode + char_num_cmd] = run_char_num_mmode;
+ jump_table[mmode + math_char_num_cmd] = run_math_char_num_mmode;
+ jump_table[mmode + math_given_cmd] = run_math_given_mmode;
+ jump_table[mmode + xmath_given_cmd] = run_xmath_given_mmode;
+ jump_table[mmode + delim_num_cmd] = run_delim_num;
+ jump_table[mmode + math_comp_cmd] = math_math_comp;
+ jump_table[mmode + limit_switch_cmd] = math_limit_switch;
+ jump_table[mmode + radical_cmd] = math_radical;
+ jump_table[mmode + accent_cmd] = math_ac;
+ jump_table[mmode + math_accent_cmd] = math_ac;
+ jump_table[mmode + vcenter_cmd] = run_vcenter;
+ jump_table[mmode + math_style_cmd] = run_math_style;
+ jump_table[mmode + non_script_cmd] = run_non_script;
+ jump_table[mmode + math_choice_cmd] = run_math_choice;
+ jump_table[mmode + above_cmd] = math_fraction;
+ jump_table[mmode + sub_mark_cmd] = sub_sup;
+ jump_table[mmode + sup_mark_cmd] = sub_sup;
+ jump_table[mmode + super_sub_script_cmd] = sub_sup;
+ jump_table[mmode + left_right_cmd] = math_left_right;
+ jump_table[mmode + math_shift_cmd] = run_math_shift;
+ jump_table[mmode + math_shift_cs_cmd] = run_math_shift;
+ any_mode(toks_register_cmd, prefixed_command);
+ any_mode(assign_toks_cmd, prefixed_command);
+ any_mode(assign_int_cmd, prefixed_command);
+ any_mode(assign_attr_cmd, prefixed_command);
+ any_mode(assign_dir_cmd, prefixed_command);
+ any_mode(assign_dimen_cmd, prefixed_command);
+ any_mode(assign_glue_cmd, prefixed_command);
+ any_mode(assign_mu_glue_cmd, prefixed_command);
+ any_mode(assign_font_dimen_cmd, prefixed_command);
+ any_mode(assign_font_int_cmd, prefixed_command);
+ any_mode(set_aux_cmd, prefixed_command);
+ any_mode(set_prev_graf_cmd, prefixed_command);
+ any_mode(set_page_dimen_cmd, prefixed_command);
+ any_mode(set_page_int_cmd, prefixed_command);
+ any_mode(set_box_dimen_cmd, prefixed_command);
+ any_mode(set_tex_shape_cmd, prefixed_command);
+ any_mode(set_etex_shape_cmd, prefixed_command);
+ any_mode(def_char_code_cmd, prefixed_command);
+ any_mode(def_del_code_cmd, prefixed_command);
+ any_mode(extdef_math_code_cmd, prefixed_command);
+ any_mode(extdef_del_code_cmd, prefixed_command);
+ any_mode(def_family_cmd, prefixed_command);
+ any_mode(set_math_param_cmd, prefixed_command);
+ any_mode(set_font_cmd, prefixed_command);
+ any_mode(def_font_cmd, prefixed_command);
+ any_mode(letterspace_font_cmd, prefixed_command);
+ any_mode(copy_font_cmd, prefixed_command);
+ any_mode(set_font_id_cmd, prefixed_command);
+ any_mode(register_cmd, prefixed_command);
+ any_mode(advance_cmd, prefixed_command);
+ any_mode(multiply_cmd, prefixed_command);
+ any_mode(divide_cmd, prefixed_command);
+ any_mode(prefix_cmd, prefixed_command);
+ any_mode(let_cmd, prefixed_command);
+ any_mode(shorthand_def_cmd, prefixed_command);
+ any_mode(read_to_cs_cmd, prefixed_command);
+ any_mode(def_cmd, prefixed_command);
+ any_mode(set_box_cmd, prefixed_command);
+ any_mode(hyph_data_cmd, prefixed_command);
+ any_mode(set_interaction_cmd, prefixed_command);
+ any_mode(after_assignment_cmd,run_after_assignment);
+ any_mode(after_group_cmd,run_after_group);
+ any_mode(in_stream_cmd,open_or_close_in);
+ any_mode(message_cmd,issue_message);
+ any_mode(case_shift_cmd, shift_case);
+ any_mode(xray_cmd, show_whatever);
+ any_mode(normal_cmd, run_normal);
+ any_mode(extension_cmd, run_extension);
+ any_mode(option_cmd, run_option);
+}
+
+@ And here is |main_control| itself. It is quite short nowadays.
+
+@c
+void main_control(void)
+{
+ main_control_state = goto_next;
+ init_main_control () ;
+
+ if (equiv(every_job_loc) != null)
+ begin_token_list(equiv(every_job_loc), every_job_text);
+
+ while (1) {
+ if (main_control_state == goto_skip_token)
+ main_control_state = goto_next; /* reset */
+ else
+ get_x_token();
+
+ /* Give diagnostic information, if requested */
+ /* When a new token has just been fetched at |big_switch|, we have an
+ ideal place to monitor \TeX's activity. */
+ if (interrupt != 0 && OK_to_interrupt) {
+ back_input();
+ check_interrupt();
+ continue;
+ }
+ if (tracing_commands_par > 0)
+ show_cur_cmd_chr();
+
+ (jump_table[(abs(mode) + cur_cmd)])(); /* run the command */
+
+ if (main_control_state == goto_return) {
+ return;
+ }
+ }
+ return; /* not reached */
+}
+
+@ @c
+void app_space(void)
+{ /* handle spaces when |space_factor<>1000| */
+ halfword q; /* glue node */
+ if ((space_factor_par >= 2000) && (! glue_is_zero(xspace_skip_par))) {
+ q = new_param_glue(xspace_skip_code);
+ /* so from now we have a subtype with spaces: */
+ subtype(q) = xspace_skip_code + 1;
+ } else {
+ if (!glue_is_zero(space_skip_par)) {
+ q = new_glue(space_skip_par);
+ } else {
+ q = new_glue(zero_glue);
+ width(q) = space(cur_font_par);
+ stretch(q) = space_stretch(cur_font_par);
+ shrink(q) = space_shrink(cur_font_par);
+ }
+ /* Modify the glue specification in |q| according to the space factor */
+ if (space_factor_par >= 2000)
+ width(q) = width(q) + extra_space(cur_font_par);
+ stretch(q) = xn_over_d(stretch(q), space_factor_par, 1000);
+ shrink(q) = xn_over_d(shrink(q), 1000, space_factor_par);
+
+ /* so from now we have a subtype with spaces: */
+ subtype(q) = space_skip_code + 1;
+ }
+ couple_nodes(tail, q);
+ tail = q;
+}
+
+@ @c
+void insert_dollar_sign(void)
+{
+ back_input();
+ cur_tok = math_shift_token + '$';
+ print_err("Missing $ inserted");
+ help2("I've inserted a begin-math/end-math symbol since I think",
+ "you left one out. Proceed, with fingers crossed.");
+ ins_error();
+}
+
+@ We can silently ignore \.{\\par}s in a math formula.
+
+@c
+void insert_dollar_sign_par_end(void)
+{
+ if (!suppress_mathpar_error_par) {
+ insert_dollar_sign() ;
+ }
+}
+
+@ The `|you_cant|' procedure prints a line saying that the current command
+is illegal in the current mode; it identifies these things symbolically.
+
+@c
+void you_cant(void)
+{
+ print_err("You can't use `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ print_in_mode(mode);
+}
+
+@
+When erroneous situations arise, \TeX\ usually issues an error message
+specific to the particular error. For example, `\.{\\noalign}' should
+not appear in any mode, since it is recognized by the |align_peek| routine
+in all of its legitimate appearances; a special error message is given
+when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate
+error message is simply that the user is not allowed to do what he or she
+has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode,
+and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated
+here and in the other sections referred to under `See also \dots.'
+
+@c
+void report_illegal_case(void)
+{
+ you_cant();
+ help4("Sorry, but I'm not programmed to handle this case;",
+ "I'll just pretend that you didn''t ask for it.",
+ "If you're in the wrong mode, you might be able to",
+ "return to the right one by typing `I}' or `I$' or `I\\par'.");
+ error();
+}
+
+@ Some operations are allowed only in privileged modes, i.e., in cases
+that |mode>0|. The |privileged| function is used to detect violations
+of this rule; it issues an error message and returns |false| if the
+current |mode| is negative.
+
+@c
+boolean privileged(void)
+{
+ if (mode > 0) {
+ return true;
+ } else {
+ report_illegal_case();
+ return false;
+ }
+}
+
+@ We don't want to leave |main_control| immediately when a |stop| command
+is sensed, because it may be necessary to invoke an \.{\\output} routine
+several times before things really grind to a halt. (The output routine
+might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.)
+Therefore |its_all_over| is |true| only when the current page
+and contribution list are empty, and when the last output was not a
+``dead cycle.''
+
+@c
+boolean its_all_over(void)
+{ /* do this when \.{\\end} or \.{\\dump} occurs */
+ if (privileged()) {
+ if ((page_head == page_tail) && (head == tail) && (dead_cycles == 0)) {
+ return true;
+ }
+ back_input(); /* we will try to end again after ejecting residual material */
+ tail_append(new_null_box());
+ width(tail) = hsize_par;
+ tail_append(new_glue(fill_glue));
+ tail_append(new_penalty(-010000000000));
+ normal_page_filter(end);
+ build_page(); /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */
+ }
+ return false;
+}
+
+
+@ The |hskip| and |vskip| command codes are used for control sequences
+like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}.
+The difference is in the value of |cur_chr|.
+
+All the work relating to glue creation has been relegated to the
+following subroutine. It does not call |build_page|, because it is
+used in at least one place where that would be a mistake.
+
+@c
+void append_glue(void)
+{
+ int s = cur_chr;
+ switch (s) {
+ case fil_code:
+ cur_val = new_glue(fil_glue);
+ break;
+ case fill_code:
+ cur_val = new_glue(fill_glue);
+ break;
+ case ss_code:
+ cur_val = new_glue(ss_glue);
+ break;
+ case fil_neg_code:
+ cur_val = new_glue(fil_neg_glue);
+ break;
+ case skip_code:
+ scan_glue(glue_val_level);
+ break;
+ case mskip_code:
+ scan_glue(mu_val_level);
+ break;
+ }
+ /* now |cur_val| points to the glue specification */
+ tail_append(new_glue(cur_val));
+ flush_node(cur_val);
+ if (s > skip_code) {
+ subtype(tail) = mu_glue;
+ }
+}
+
+@ @c
+void append_kern(void)
+{
+ int s; /* |subtype| of the kern node */
+ s = cur_chr;
+ scan_dimen((s == mu_glue), false, false);
+ tail_append(new_kern(cur_val));
+ subtype(tail) = (quarterword) s;
+}
+
+@ We have to deal with errors in which braces and such things are not
+properly nested. Sometimes the user makes an error of commission by
+inserting an extra symbol, but sometimes the user makes an error of omission.
+\TeX\ can't always tell one from the other, so it makes a guess and tries
+to avoid getting into a loop.
+
+The |off_save| routine is called when the current group code is wrong. It tries
+to insert something into the user's input that will help clean off
+the top level.
+
+@c
+void off_save(void)
+{
+ halfword p, q; /* inserted token */
+ if (cur_group == bottom_level) {
+ /* Drop current token and complain that it was unmatched */
+ print_err("Extra ");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ help1("Things are pretty mixed up, but I think the worst is over.");
+ error();
+
+ } else {
+ back_input();
+ p = get_avail();
+ set_token_link(temp_token_head, p);
+ print_err("Missing ");
+ /* Prepare to insert a token that matches |cur_group|, and print what it is */
+ /* At this point, |link(temp_token_head)=p|, a pointer to an empty one-word node. */
+ switch (cur_group) {
+ case semi_simple_group:
+ set_token_info(p, cs_token_flag + frozen_end_group);
+ tprint_esc("endgroup");
+ break;
+ case math_shift_group:
+ set_token_info(p, math_shift_token + '$');
+ print_char('$');
+ break;
+ case math_left_group:
+ set_token_info(p, cs_token_flag + frozen_right);
+ q = get_avail();
+ set_token_link(p, q);
+ p = token_link(p);
+ set_token_info(p, other_token + '.');
+ tprint_esc("right.");
+ break;
+ default:
+ set_token_info(p, right_brace_token + '}');
+ print_char('}');
+ break;
+ }
+ tprint(" inserted");
+ ins_list(token_link(temp_token_head));
+ help5("I've inserted something that you may have forgotten.",
+ "(See the <inserted text> above.)",
+ "With luck, this will get me unwedged. But if you",
+ "really didn't forget anything, try typing `2' now; then",
+ "my insertion and my current dilemma will both disappear.");
+ error();
+ }
+}
+
+@ The routine for a |right_brace| character branches into many subcases,
+since a variety of things may happen, depending on |cur_group|. Some
+types of groups are not supposed to be ended by a right brace; error
+messages are given in hopes of pinpointing the problem. Most branches
+of this routine will be filled in later, when we are ready to understand
+them; meanwhile, we must prepare ourselves to deal with such errors.
+
+@c
+void handle_right_brace(void)
+{
+ halfword p, q; /* for short-term use */
+ scaled d; /* holds |split_max_depth| in |insert_group| */
+ int f; /* holds |floating_penalty| in |insert_group| */
+ p = null;
+ switch (cur_group) {
+ case simple_group:
+ fixup_directions();
+ break;
+ case bottom_level:
+ print_err("Too many }'s");
+ help2("You've closed more groups than you opened.",
+ "Such booboos are generally harmless, so keep going.");
+ error();
+ break;
+ case semi_simple_group:
+ case math_shift_group:
+ case math_left_group:
+ extra_right_brace();
+ break;
+ case hbox_group:
+ /* When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or
+ \.{\\vtop} construction, the |package| routine comes into action. We might
+ also have to finish a paragraph that hasn't ended. */
+ package(0);
+ break;
+ case adjusted_hbox_group:
+ adjust_tail = adjust_head;
+ pre_adjust_tail = pre_adjust_head;
+ package(0);
+ break;
+ case vbox_group:
+ end_graf(vbox_group);
+ package(0);
+ break;
+ case vtop_group:
+ end_graf(vtop_group);
+ package(vtop_code);
+ break;
+ case insert_group:
+ end_graf(insert_group);
+ q = new_glue(split_top_skip_par);
+ d = split_max_depth_par;
+ f = floating_penalty_par;
+ unsave();
+ save_ptr--;
+ /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */
+ p = vpack(vlink(head), 0, additional, -1);
+ pop_nest();
+ if (saved_type(0) == saved_insert) {
+ tail_append(new_node(ins_node, saved_value(0)));
+ height(tail) = height(p) + depth(p);
+ ins_ptr(tail) = list_ptr(p);
+ split_top_ptr(tail) = q;
+ depth(tail) = d;
+ float_cost(tail) = f;
+ } else if (saved_type(0) == saved_adjust) {
+ tail_append(new_node(adjust_node, saved_value(0)));
+ adjust_ptr(tail) = list_ptr(p);
+ flush_node(q);
+ } else {
+ confusion("insert_group");
+ }
+ list_ptr(p) = null;
+ flush_node(p);
+ if (nest_ptr == 0) {
+ checked_page_filter(insert);
+ build_page();
+ }
+ break;
+ case output_group:
+ /* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */
+ if (dir_level(text_dir_ptr) == cur_level) {
+ /* DIR: Remove from |text_dir_ptr| */
+ halfword text_dir_tmp = vlink(text_dir_ptr);
+ flush_node(text_dir_ptr);
+ text_dir_ptr = text_dir_tmp;
+ }
+ resume_after_output();
+ break;
+ case disc_group:
+ build_discretionary();
+ break;
+ case local_box_group:
+ build_local_box();
+ break;
+ case align_group:
+ back_input();
+ cur_tok = cs_token_flag + frozen_cr;
+ print_err("Missing \\cr inserted");
+ help1("I'm guessing that you meant to end an alignment here.");
+ ins_error();
+ break;
+ case no_align_group:
+ end_graf(no_align_group);
+ unsave();
+ align_peek();
+ break;
+ case vcenter_group:
+ end_graf(vcenter_group);
+ finish_vcenter();
+ break;
+ case math_choice_group:
+ build_choices();
+ break;
+ case math_group:
+ close_math_group(p);
+ break;
+ default:
+ confusion("rightbrace");
+ break;
+ }
+}
+
+@ @c
+void extra_right_brace(void)
+{
+ print_err("Extra }, or forgotten ");
+ switch (cur_group) {
+ case semi_simple_group:
+ tprint_esc("endgroup");
+ break;
+ case math_shift_group:
+ print_char('$');
+ break;
+ case math_left_group:
+ tprint_esc("right");
+ break;
+ }
+ help5("I've deleted a group-closing symbol because it seems to be",
+ "spurious, as in `$x}$'. But perhaps the } is legitimate and",
+ "you forgot something else, as in `\\hbox{$x}'. In such cases",
+ "the way to recover is to insert both the forgotten and the",
+ "deleted material, e.g., by typing `I$}'.");
+ error();
+ incr(align_state);
+}
+
+@ Here is where we clear the parameters that are supposed to revert to their
+default values after every paragraph and when internal vertical mode is entered.
+
+@c
+void normal_paragraph(void)
+{
+ if (looseness_par != 0)
+ eq_word_define(int_base + looseness_code, 0);
+ if (hang_indent_par != 0)
+ eq_word_define(dimen_base + hang_indent_code, 0);
+ if (hang_after_par != 1)
+ eq_word_define(int_base + hang_after_code, 1);
+ if (par_shape_par_ptr != null)
+ eq_define(par_shape_loc, shape_ref_cmd, null);
+ if (inter_line_penalties_par_ptr != null)
+ eq_define(inter_line_penalties_loc, shape_ref_cmd, null);
+ if (shape_mode_par > 0)
+ eq_word_define(dimen_base + shape_mode_code, 0);
+}
+
+@ The global variable |cur_box| will point to a newly-made box. If the box
+is void, we will have |cur_box=null|. Otherwise we will have
+|type(cur_box)=hlist_node| or |vlist_node| or |rule_node|; the |rule_node|
+case can occur only with leaders.
+
+@c
+halfword cur_box; /* box to be placed into its context */
+
+@ The |box_end| procedure does the right thing with |cur_box|, if
+|box_context| represents the context as explained above.
+
+@c
+void box_end(int box_context)
+{
+ if (box_context < box_flag) {
+ /* Append box |cur_box| to the current list, shifted by |box_context| */
+ /*
+ The global variable |adjust_tail| will be non-null if and only if the
+ current box might include adjustments that should be appended to the
+ current vertical list.
+ */
+ if (cur_box != null) {
+ shift_amount(cur_box) = box_context;
+ if (abs(mode) == vmode) {
+ if (pre_adjust_tail != null) {
+ if (pre_adjust_head != pre_adjust_tail)
+ append_list(pre_adjust_head, pre_adjust_tail);
+ pre_adjust_tail = null;
+ }
+ append_to_vlist(cur_box,lua_key_index(box));
+ if (adjust_tail != null) {
+ if (adjust_head != adjust_tail)
+ append_list(adjust_head, adjust_tail);
+ adjust_tail = null;
+ }
+ if (mode > 0) {
+ checked_page_filter(box);
+ build_page();
+ }
+ } else {
+ if (abs(mode) == hmode)
+ space_factor_par = 1000;
+ else
+ cur_box = new_sub_box(cur_box);
+ couple_nodes(tail, cur_box);
+ tail = cur_box;
+ }
+ }
+ } else if (box_context < ship_out_flag) {
+ /* Store |cur_box| in a box register */
+ if (box_context < global_box_flag)
+ eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box);
+ else
+ geq_define(box_base + box_context - global_box_flag, box_ref_cmd, cur_box);
+ } else if (cur_box != null) {
+ if (box_context > ship_out_flag) {
+ /* Append a new leader node that uses |cur_box| */
+ /* Get the next non-blank non-relax... */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (((cur_cmd == hskip_cmd) && (abs(mode) != vmode)) ||
+ ((cur_cmd == vskip_cmd) && (abs(mode) == vmode))) {
+ append_glue();
+ subtype(tail) = (quarterword) (box_context - (leader_flag - a_leaders));
+ leader_ptr(tail) = cur_box;
+ } else {
+ print_err("Leaders not followed by proper glue");
+ help3
+ ("You should say `\\leaders <box or rule><hskip or vskip>'.",
+ "I found the <box or rule>, but there's no suitable",
+ "<hskip or vskip>, so I'm ignoring these leaders.");
+ back_error();
+ flush_node_list(cur_box);
+ }
+ } else {
+ ship_out(static_pdf, cur_box, SHIPPING_PAGE);
+ }
+ }
+}
+
+@ the next input should specify a box or perhaps a rule
+
+@c
+void scan_box(int box_context)
+{
+ /* Get the next non-blank non-relax... */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (cur_cmd == make_box_cmd) {
+ begin_box(box_context);
+ } else if ((box_context >= leader_flag) &&
+ ((cur_cmd == hrule_cmd) || (cur_cmd == vrule_cmd) ||
+ (cur_cmd == no_hrule_cmd) || (cur_cmd == no_vrule_cmd))) {
+ cur_box = scan_rule_spec();
+ box_end(box_context);
+ } else {
+ print_err("A <box> was supposed to be here");
+ help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or",
+ "something like that. So you might find something missing in",
+ "your output. But keep trying; you can fix this later.");
+ back_error();
+ }
+}
+
+@ @c
+void new_graf(boolean indented)
+{
+ halfword p, q, dir_graf_tmp;
+ halfword dir_rover;
+ prev_graf_par = 0;
+ if ((mode == vmode) || (head != tail)) {
+ tail_append(new_param_glue(par_skip_code));
+ }
+ push_nest();
+ mode = hmode;
+ space_factor_par = 1000;
+ /* LOCAL: Add local paragraph node */
+ tail_append(make_local_par_node(new_graf_par_code));
+ if (indented) {
+ p = new_null_box();
+ box_dir(p) = par_direction_par;
+ width(p) = par_indent_par;
+ subtype(p) = indent_list;
+ q = tail;
+ tail_append(p);
+ } else {
+ q = tail;
+ }
+ dir_rover = text_dir_ptr;
+ while (dir_rover != null) {
+ if ((vlink(dir_rover) != null) || (dir_dir(dir_rover) != par_direction_par)) {
+ dir_graf_tmp = new_dir(dir_dir(dir_rover));
+ try_couple_nodes(dir_graf_tmp,vlink(q));
+ couple_nodes(q,dir_graf_tmp);
+ }
+ dir_rover = vlink(dir_rover);
+ }
+ q = head;
+ while (vlink(q) != null)
+ q = vlink(q);
+ tail = q;
+ if (every_par_par != null)
+ begin_token_list(every_par_par, every_par_text);
+ if (nest_ptr == 1) {
+ checked_page_filter(new_graf);
+ build_page(); /* put |par_skip| glue on current page */
+ }
+}
+
+@ @c
+void indent_in_hmode(void)
+{
+ halfword p;
+ if (cur_chr > 0) { /* \.{\\indent} */
+ p = new_null_box();
+ width(p) = par_indent_par;
+ if (abs(mode) == hmode)
+ space_factor_par = 1000;
+ else
+ p = new_sub_box(p);
+ tail_append(p);
+ }
+}
+
+@ @c
+void head_for_vmode(void)
+{
+ if (mode < 0) {
+ if ((cur_cmd != hrule_cmd) && (cur_cmd != no_hrule_cmd)) {
+ off_save();
+ } else {
+ print_err("You can't use `\\hrule' here except with leaders");
+ help2("To put a horizontal rule in an hbox or an alignment,",
+ "you should use \\leaders or \\hrulefill (see The TeXbook).");
+ error();
+ }
+ } else {
+ back_input();
+ cur_tok = par_token;
+ back_input();
+ token_type = inserted;
+ }
+}
+
+@ TODO (BUG?): |dir_save| would have been set by |line_break| by means
+of |post_line_break|, but this is not done right now, as it introduces
+pretty heavy memory leaks. This means the current code is probably
+wrong in some way that relates to in-paragraph displays.
+
+@c
+void end_graf(int line_break_context)
+{
+ if (mode == hmode) {
+ if ((head == tail) || (vlink(head) == tail)) {
+ if (vlink(head) == tail)
+ flush_node(vlink(head));
+ pop_nest(); /* null paragraphs are ignored, all contain a |local_paragraph| node */
+ } else {
+ line_break(false, line_break_context);
+ }
+ if (dir_save != null) {
+ flush_node_list(dir_save);
+ dir_save = null;
+ }
+ normal_paragraph();
+ error_count = 0;
+ }
+}
+
+@ @c
+void begin_insert_or_adjust(void)
+{
+ if (cur_cmd != vadjust_cmd) {
+ scan_register_num();
+ if (cur_val == output_box_par) {
+ print_err("You can't \\insert");
+ print_int(output_box_par);
+ help1("I'm changing to \\insert0; box \\outputbox is special.");
+ error();
+ cur_val = 0;
+ }
+ set_saved_record(0, saved_insert, 0, cur_val);
+ } else if (scan_keyword("pre")) {
+ set_saved_record(0, saved_adjust, 0, 1);
+ } else {
+ set_saved_record(0, saved_adjust, 0, 0);
+ }
+ save_ptr++;
+ new_save_level(insert_group);
+ scan_left_brace();
+ normal_paragraph();
+ push_nest();
+ mode = -vmode;
+ prev_depth_par = ignore_depth;
+}
+
+@ I (TH)'ve renamed the |make_mark| procedure to this, because if the
+current chr code is 1, then the actual command was \.{\\clearmarks},
+which does not generate a mark node but instead destroys the current
+mark tokenlists.
+
+@c
+void handle_mark(void)
+{
+ halfword p; /* new node */
+ halfword c; /* the mark class */
+ if (cur_chr == clear_marks_code) {
+ scan_mark_num();
+ c = cur_val;
+ delete_top_mark(c);
+ delete_bot_mark(c);
+ delete_first_mark(c);
+ delete_split_first_mark(c);
+ delete_split_bot_mark(c);
+ } else {
+ if (cur_chr == 0) {
+ c = 0;
+ } else {
+ scan_mark_num();
+ c = cur_val;
+ if (c > biggest_used_mark)
+ biggest_used_mark = c;
+ }
+ p = scan_toks(false, true);
+ p = new_node(mark_node, 0); /* the |subtype| is not used */
+ mark_class(p) = c;
+ mark_ptr(p) = def_ref;
+ couple_nodes(tail, p);
+ tail = p;
+ }
+}
+
+@ @c
+void append_penalty(void)
+{
+ scan_int();
+ tail_append(new_penalty(cur_val));
+ if (mode == vmode) {
+ checked_page_filter(penalty);
+ build_page();
+ }
+}
+
+@ When |delete_last| is called, |cur_chr| is the |type| of node that
+will be deleted, if present.
+
+The |remove_item| command removes a penalty, kern, or glue node if it
+appears at the tail of the current list, using a brute-force linear scan.
+Like \.{\\lastbox}, this command is not allowed in vertical mode (except
+internal vertical mode), since the current list in vertical mode is sent
+to the page builder. But if we happen to be able to implement it in
+vertical mode, we do.
+
+@c
+void delete_last(void)
+{
+ halfword p, q; /* run through the current list */
+ if ((mode == vmode) && (tail == head)) {
+ /* Apologize for inability to do the operation now,
+ unless \.{\\unskip} follows non-glue */
+ if ((cur_chr != glue_node) || (last_glue != max_halfword)) {
+ you_cant();
+ if (cur_chr == kern_node) {
+ help2
+ ("Sorry...I usually can't take things from the current page.",
+ "Try `I\\kern-\\lastkern' instead.");
+ } else if (cur_chr != glue_node) {
+ help2
+ ("Sorry...I usually can't take things from the current page.",
+ "Perhaps you can make the output routine do it.");
+ } else {
+ help2
+ ("Sorry...I usually can't take things from the current page.",
+ "Try `I\\vskip-\\lastskip' instead.");
+ }
+ error();
+ }
+ } else {
+ /* todo: clean this up */
+ if (!is_char_node(tail)) {
+ if (type(tail) == cur_chr) {
+ q = head;
+ do {
+ p = q;
+ if (!is_char_node(q)) {
+ if (type(q) == disc_node) {
+ if (p == tail)
+ return;
+ }
+ }
+ q = vlink(p);
+ } while (q != tail);
+ vlink(p) = null;
+ flush_node_list(tail);
+ tail = p;
+ }
+ }
+ }
+}
+
+@ @c
+void unpackage(void)
+{
+ halfword p; /* the box */
+ halfword r; /* to remove marginal kern nodes */
+ int c; /* should we copy? */
+ halfword s; /* for varmem assignment */
+ if (cur_chr > copy_code) {
+ /* Handle saved items and |goto done| */
+ try_couple_nodes(tail, disc_ptr[cur_chr]);
+ disc_ptr[cur_chr] = null;
+ goto DONE;
+ }
+ c = cur_chr;
+ scan_register_num();
+ p = box(cur_val);
+ if (p == null)
+ return;
+ if ((abs(mode) == mmode)
+ || ((abs(mode) == vmode) && (type(p) != vlist_node))
+ || ((abs(mode) == hmode) && (type(p) != hlist_node))) {
+ print_err("Incompatible list can't be unboxed");
+ help3("Sorry, Pandora. (You sneaky devil.)",
+ "I refuse to unbox an \\hbox in vertical mode or vice versa.",
+ "And I can't open any boxes in math mode.");
+ error();
+ return;
+ }
+ if (c == copy_code) {
+ s = copy_node_list(list_ptr(p));
+ try_couple_nodes(tail,s);
+ } else {
+ try_couple_nodes(tail,list_ptr(p));
+ box(cur_val) = null;
+ list_ptr(p) = null;
+ flush_node(p);
+ }
+ DONE:
+ while (vlink(tail) != null) {
+ r = vlink(tail);
+ if (!is_char_node(r) && (type(r) == margin_kern_node)) {
+ try_couple_nodes(tail,vlink(r));
+ flush_node(r);
+ }
+ tail = vlink(tail);
+ }
+}
+
+@
+Italic corrections are converted to kern nodes when the |ital_corr| command
+follows a character. In math mode the same effect is achieved by appending
+a kern of zero here, since italic corrections are supplied later.
+
+@c
+void append_italic_correction(void)
+{
+ halfword p; /* |char_node| at the tail of the current list */
+ internal_font_number f; /* the font in the |char_node| */
+ if (tail != head) {
+ if (is_char_node(tail))
+ p = tail;
+ else
+ return;
+ f = font(p);
+ tail_append(new_kern(char_italic(f, character(p))));
+ subtype(tail) = italic_kern;
+ }
+}
+
+@ @c
+void append_local_box(int kind)
+{
+ incr(save_ptr);
+ set_saved_record(-1, saved_boxtype, 0, kind);
+ new_save_level(local_box_group);
+ scan_left_brace();
+ push_nest();
+ mode = -hmode;
+ space_factor_par = 1000;
+}
+
+@ Discretionary nodes are easy in the common case `\.{\\-}', but in the
+general case we must process three braces full of items.
+
+The space factor does not change when we append a discretionary node,
+but it starts out as 1000 in the subsidiary lists.
+
+@c
+void append_discretionary(void)
+{
+ int c;
+ tail_append(new_disc());
+ subtype(tail) = (quarterword) cur_chr;
+ if (cur_chr == explicit_disc) {
+ /* \- */
+ c = get_pre_hyphen_char(cur_lang_par);
+ if (c != 0) {
+ vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c);
+ alink(vlink(pre_break(tail))) = pre_break(tail);
+ tlink(pre_break(tail)) = vlink(pre_break(tail));
+ }
+ c = get_post_hyphen_char(cur_lang_par);
+ if (c != 0) {
+ vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c);
+ alink(vlink(post_break(tail))) = post_break(tail);
+ tlink(post_break(tail)) = vlink(post_break(tail));
+ }
+ disc_penalty(tail) = ex_hyphen_penalty_par;
+ } else {
+ /* \discretionary */
+ if (scan_keyword("penalty")) {
+ scan_int();
+ disc_penalty(tail) = cur_val;
+ }
+ incr(save_ptr);
+ set_saved_record(-1, saved_disc, 0, 0);
+ new_save_level(disc_group);
+ scan_left_brace();
+ push_nest();
+ mode = -hmode;
+ space_factor_par = 1000;
+ /* already preset: disc_penalty(tail) = hyphen_penalty_par; */
+ }
+}
+
+@ The test for |p != null| ensures that empty \.{\\localleftbox} and
+ \.{\\localrightbox} commands are not applied.
+
+@c
+void build_local_box(void)
+{
+ halfword p;
+ int kind;
+ unsave();
+ assert(saved_type(-1) == saved_boxtype);
+ kind = saved_value(-1);
+ decr(save_ptr);
+ p = vlink(head);
+ pop_nest();
+ if (p != null)
+ p = hpack(p, 0, additional, -1);
+ if (kind == 0)
+ eq_define(local_left_box_base, box_ref_cmd, p);
+ else
+ eq_define(local_right_box_base, box_ref_cmd, p);
+ if (abs(mode) == hmode) {
+ /* LOCAL: Add local paragraph node */
+ tail_append(make_local_par_node(local_box_par_code));
+ }
+ eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1);
+}
+
+@ The three discretionary lists are constructed somewhat as if they were
+hboxes. A~subroutine called |build_discretionary| handles the transitions.
+(This is sort of fun.)
+
+@c
+void build_discretionary(void)
+{
+ halfword p, q; /* for link manipulation */
+ int n; /* length of discretionary list */
+ unsave();
+ /* Prune the current list, if necessary, until it contains only
+ |char_node|, |kern_node|, |hlist_node|, |vlist_node| and
+ |rule_node| items; set |n| to the length of the list,
+ and set |q| to the lists tail */
+ /* During this loop, |p=vlink(q)| and there are |n| items preceding |p|. */
+ q = head;
+ p = vlink(q);
+ n = 0;
+ while (p != null) {
+ if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) {
+ print_err("Improper discretionary list");
+ help1("Discretionary lists must contain only boxes and kerns.");
+ error();
+ begin_diagnostic();
+ tprint_nl("The following discretionary sublist has been deleted:");
+ show_box(p);
+ end_diagnostic(true);
+ flush_node_list(p);
+ vlink(q) = null;
+ break;
+ }
+ alink(p) = q;
+ q = p;
+ p = vlink(q);
+ incr(n);
+ }
+
+ p = vlink(head);
+ pop_nest();
+ assert(saved_type(-1) == saved_disc);
+ switch (saved_value(-1)) {
+ case 0:
+ if (n > 0) {
+ vlink(pre_break(tail)) = p;
+ alink(p) = pre_break(tail);
+ tlink(pre_break(tail)) = q;
+ }
+ break;
+ case 1:
+ if (n > 0) {
+ vlink(post_break(tail)) = p;
+ alink(p) = post_break(tail);
+ tlink(post_break(tail)) = q;
+ }
+ break;
+ case 2:
+ /* Attach list |p| to the current list, and record its length;
+ then finish up and |return| */
+ if ((n > 0) && (abs(mode) == mmode)) {
+ print_err("Illegal math \\discretionary");
+ help2("Sorry: The third part of a discretionary break must be",
+ "empty, in math formulas. I had to delete your third part.");
+ flush_node_list(p);
+ error();
+ } else {
+ if (n > 0) {
+ vlink(no_break(tail)) = p;
+ alink(p) = no_break(tail);
+ tlink(no_break(tail)) = q;
+ }
+ }
+ decr(save_ptr);
+ return;
+ break;
+ } /* there are no other cases */
+ set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1));
+ new_save_level(disc_group);
+ scan_left_brace();
+ push_nest();
+ mode = -hmode;
+ space_factor_par = 1000;
+}
+
+@ The positioning of accents is straightforward but tedious. Given an accent
+of width |a|, designed for characters of height |x| and slant |s|;
+and given a character of width |w|, height |h|, and slant |t|: We will shift
+the accent down by |x-h|, and we will insert kern nodes that have the effect of
+centering the accent over the character and shifting the accent to the
+right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is
+absent from the font, we will simply use the other, without shifting.
+
+@c
+void make_accent(void)
+{
+ double s, t; /* amount of slant */
+ halfword p, q, r; /* character, box, and kern nodes */
+ internal_font_number f; /* relevant font */
+ scaled a, h, x, w, delta; /* heights and widths, as explained above */
+ scan_char_num();
+ f = equiv(cur_font_loc);
+ p = new_glyph(f, cur_val);
+ if (p != null) {
+ x = x_height(f);
+ s = float_cast(slant(f)) / float_constant(65536); /* real division */
+ a = glyph_width(p);
+ do_assignments();
+ /* Create a character node |q| for the next character,
+ but set |q:=null| if problems arise */
+ q = null;
+ f = equiv(cur_font_loc);
+ if ((cur_cmd == letter_cmd) ||
+ (cur_cmd == other_char_cmd) || (cur_cmd == char_given_cmd)) {
+ q = new_glyph(f, cur_chr);
+ } else if (cur_cmd == char_num_cmd) {
+ scan_char_num();
+ q = new_glyph(f, cur_val);
+ } else {
+ back_input();
+ }
+
+ if (q != null) {
+ /* Append the accent with appropriate kerns, then set |p:=q| */
+ /* The kern nodes appended here must be distinguished from other kerns, lest
+ they be wiped away by the hyphenation algorithm or by a previous line break.
+
+ The two kerns are computed with (machine-dependent) |real| arithmetic, but
+ their sum is machine-independent; the net effect is machine-independent,
+ because the user cannot remove these nodes nor access them via \.{\\lastkern}.
+ */
+ t = float_cast(slant(f)) / float_constant(65536); /* real division */
+ w = glyph_width(q);
+ h = glyph_height(q);
+ if (h != x) { /* the accent must be shifted up or down */
+ p = hpack(p, 0, additional, -1);
+ shift_amount(p) = x - h;
+ }
+ delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); /* real multiplication */
+ r = new_kern(delta);
+ subtype(r) = accent_kern;
+ couple_nodes(tail, r);
+ couple_nodes(r, p);
+ tail = new_kern(-a - delta);
+ subtype(tail) = accent_kern;
+ couple_nodes(p, tail);
+ p = q;
+
+ }
+ couple_nodes(tail, p);
+ tail = p;
+ space_factor_par = 1000;
+ }
+}
+
+@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner
+into |main_control|, it might be that the user has foolishly inserted
+one of them into something that has nothing to do with alignment. But it is
+far more likely that a left brace or right brace has been omitted, since
+|get_next| takes actions appropriate to alignment only when `\.{\\cr}'
+or `\.{\\span}' or tab marks occur with |align_state=0|. The following
+program attempts to make an appropriate recovery.
+
+@c
+void align_error(void)
+{
+ if (abs(align_state) > 2) {
+ /* Express consternation over the fact that no alignment is in progress */
+ print_err("Misplaced ");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ if (cur_tok == tab_token + '&') {
+ help6("I can't figure out why you would want to use a tab mark",
+ "here. If you just want an ampersand, the remedy is",
+ "simple: Just type `I\\&' now. But if some right brace",
+ "up above has ended a previous alignment prematurely,",
+ "you're probably due for more error messages, and you",
+ "might try typing `S' now just to see what is salvageable.");
+ } else {
+ help5("I can't figure out why you would want to use a tab mark",
+ "or \\cr or \\span just now. If something like a right brace",
+ "up above has ended a previous alignment prematurely,",
+ "you're probably due for more error messages, and you",
+ "might try typing `S' now just to see what is salvageable.");
+ }
+ error();
+
+ } else {
+ back_input();
+ if (align_state < 0) {
+ print_err("Missing { inserted");
+ incr(align_state);
+ cur_tok = left_brace_token + '{';
+ } else {
+ print_err("Missing } inserted");
+ decr(align_state);
+ cur_tok = right_brace_token + '}';
+ }
+ help3("I've put in what seems to be necessary to fix",
+ "the current column of the current alignment.",
+ "Try to go on, since this might almost work.");
+ ins_error();
+ }
+}
+
+@ The help messages here contain a little white lie, since \.{\\noalign}
+and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
+
+@c
+void no_align_error(void)
+{
+ print_err("Misplaced \\noalign");
+ help2("I expect to see \\noalign only after the \\cr of",
+ "an alignment. Proceed, and I'll ignore this case.");
+ error();
+}
+
+void omit_error(void)
+{
+ print_err("Misplaced \\omit");
+ help2("I expect to see \\omit only after tab marks or the \\cr of",
+ "an alignment. Proceed, and I'll ignore this case.");
+ error();
+}
+
+@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}.
+Let's take a look at what happens when they are used correctly.
+
+An |align_group| code is supposed to remain on the |save_stack|
+during an entire alignment, until |fin_align| removes it.
+
+A devious user might force an |endv| command to occur just about anywhere;
+we must defeat such hacks.
+
+@c
+void do_endv(void)
+{
+ base_ptr = input_ptr;
+ input_stack[base_ptr] = cur_input;
+ while ((input_stack[base_ptr].index_field != v_template) &&
+ (input_stack[base_ptr].loc_field == null) &&
+ (input_stack[base_ptr].state_field == token_list))
+ decr(base_ptr);
+ if ((input_stack[base_ptr].index_field != v_template) ||
+ (input_stack[base_ptr].loc_field != null) ||
+ (input_stack[base_ptr].state_field != token_list))
+ fatal_error("(interwoven alignment preambles are not allowed)");
+ /*.interwoven alignment preambles... */
+ if (cur_group == align_group) {
+ end_graf(align_group);
+ if (fin_col())
+ fin_row();
+ } else {
+ off_save();
+ }
+}
+
+@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
+
+@c
+void cs_error(void)
+{
+ print_err("Extra \\endcsname");
+ help1("I'm ignoring this, since I wasn't doing a \\csname.");
+ error();
+}
+
+@
+ Assignments to values in |eqtb| can be global or local. Furthermore, a
+ control sequence can be defined to be `\.{\\long}', `\.{\\protected}',
+ or `\.{\\outer}', and it might or might not be expanded. The prefixes
+ `\.{\\global}', `\.{\\long}', `\.{\\protected}',
+ and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric
+ codes, making it possible to accumulate the union of all specified prefixes
+ by adding the corresponding codes. (PASCAL's |set| operations could also
+ have been used.)
+
+ Every prefix, and every command code that might or might not be prefixed,
+ calls the action procedure |prefixed_command|. This routine accumulates
+ a sequence of prefixes until coming to a non-prefix, then it carries out
+ the command.
+
+@ If the user says, e.g., `\.{\\global\\global}', the redundancy is
+silently accepted.
+
+
+@ The different types of code values have different legal ranges; the
+following program is careful to check each case properly.
+
+@c
+#define check_def_code(A) do { \
+ if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \
+ print_err("Invalid code ("); \
+ print_int(cur_val); \
+ if (p<(A)) \
+ tprint("), should be in the range 0.."); \
+ else \
+ tprint("), should be at most "); \
+ print_int(n); \
+ help1("I'm going to use 0 instead of that illegal code value."); \
+ error(); \
+ cur_val=0; \
+ } \
+} while (0)
+
+@ @c
+/*
+halfword swap_hang_indent(halfword indentation, halfword shape_mode) {
+ if (shape_mode == 1 || shape_mode == 3 || shape_mode == -1 || shape_mode == -3) {
+ return negate(indentation);
+ } else {
+ return indentation;
+ }
+}
+
+halfword swap_parshape_indent(halfword indentation, halfword width, halfword shape_mode) {
+ if (shape_mode == 2 || shape_mode == 3 || shape_mode == -2 || shape_mode == -3) {
+ return hsize_par - width - indentation;
+ } else {
+ return indentation;
+ }
+}
+
+*/
+
+void prefixed_command(void)
+{
+ int a; /* accumulated prefix codes so far */
+ internal_font_number f; /* identifies a font */
+ halfword j; /* index into a \.{\\parshape} specification */
+ halfword p, q; /* for temporary short-term use */
+ int n; /* ditto */
+ boolean e, check_glue; /* should a definition be expanded? or was \.{\\let} not done? */
+ mathcodeval mval; /* for handling of \.{\\mathchardef}s */
+ a = 0;
+ while (cur_cmd == prefix_cmd) {
+ if (!odd(a / cur_chr))
+ a = a + cur_chr;
+ /* Get the next non-blank non-relax... */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+ if (cur_cmd <= max_non_prefixed_command) {
+ /* Discard erroneous prefixes and |return| */
+ print_err("You can't use a prefix with `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ print_char('\'');
+ help2
+ ("I'll pretend you didn't say \\long or \\outer or \\global or",
+ "\\protected.");
+ back_error();
+ return;
+ }
+ if (tracing_commands_par > 2)
+ show_cur_cmd_chr();
+ }
+ /* Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant */
+ if (a >= 8) {
+ j = protected_token;
+ a = a - 8;
+ } else {
+ j = 0;
+ }
+ if ((cur_cmd != def_cmd) && ((a % 4 != 0) || (j != 0))) {
+ print_err("You can't use `\\long' or `\\outer' or `\\protected' with `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ print_char('\'');
+ help1("I'll pretend you didn't say \\long or \\outer or \\protected here.");
+ error();
+ }
+ /* Adjust for the setting of \.{\\globaldefs} */
+ if (global_defs_par != 0) {
+ if (global_defs_par < 0) {
+ if (is_global(a))
+ a = a - 4;
+ } else {
+ if (!is_global(a))
+ a = a + 4;
+ }
+ }
+ switch (cur_cmd) {
+ case set_font_cmd:
+ /* Here's an example of the way many of the following routines operate.
+ (Unfortunately, they aren't all as simple as this.) */
+ define(cur_font_loc, data_cmd, cur_chr);
+ break;
+ case def_cmd:
+ /* When a |def| command has been scanned,
+ |cur_chr| is odd if the definition is supposed to be global, and
+ |cur_chr>=2| if the definition is supposed to be expanded. */
+
+ if (odd(cur_chr) && !is_global(a) && (global_defs_par >= 0))
+ a = a + 4;
+ e = (cur_chr >= 2);
+ get_r_token();
+ p = cur_cs;
+ q = scan_toks(true, e);
+ if (j != 0) {
+ q = get_avail();
+ set_token_info(q, j);
+ set_token_link(q, token_link(def_ref));
+ set_token_link(def_ref, q);
+ }
+ define(p, call_cmd + (a % 4), def_ref);
+ break;
+ case let_cmd:
+ n = cur_chr;
+ if (n == normal) {
+ get_r_token();
+ p = cur_cs;
+ do {
+ get_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_tok == other_token + '=') {
+ get_token();
+ if (cur_cmd == spacer_cmd)
+ get_token();
+ }
+ } else if (n == normal + 1) {
+ /* futurelet */
+ get_r_token();
+ p = cur_cs;
+ get_token();
+ q = cur_tok;
+ get_token();
+ back_input();
+ cur_tok = q;
+ /* look ahead, then back up */
+ /* note that |back_input| doesn't affect |cur_cmd|, |cur_chr| */
+ back_input();
+ } else {
+ /* letcharcode */
+ scan_int();
+ if (cur_val > 0) {
+ cur_cs = active_to_cs(cur_val, true);
+ set_token_info(cur_cs, cur_cs + cs_token_flag);
+ p = cur_cs;
+ do {
+ get_token();
+ } while (cur_cmd == spacer_cmd);
+ if (cur_tok == other_token + '=') {
+ get_token();
+ if (cur_cmd == spacer_cmd)
+ get_token();
+ }
+ } else {
+ p = null;
+ tex_error("invalid number for \\letcharcode",NULL);
+ }
+ }
+ if (cur_cmd >= call_cmd)
+ add_token_ref(cur_chr);
+ define(p, cur_cmd, cur_chr);
+ break;
+ case shorthand_def_cmd:
+ /* We temporarily define |p| to be |relax|, so that an occurrence of |p|
+ while scanning the definition will simply stop the scanning instead of
+ producing an ``undefined control sequence'' error or expanding the
+ previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'.
+ */
+ n = cur_chr;
+ get_r_token();
+ p = cur_cs;
+ define(p, relax_cmd, too_big_char);
+ scan_optional_equals();
+ switch (n) {
+ case char_def_code:
+ scan_char_num();
+ define(p, char_given_cmd, cur_val);
+ break;
+ case math_char_def_code:
+ mval = scan_mathchar(tex_mathcode);
+ if (math_umathcode_meaning_par == 1) {
+ cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value;
+ define(p, xmath_given_cmd, cur_val);
+ } else {
+ cur_val = (mval.class_value * 16 + mval.family_value) * 256 + mval.character_value;
+ define(p, math_given_cmd, cur_val);
+ }
+ break;
+ case xmath_char_def_code:
+ mval = scan_mathchar(umath_mathcode);
+ cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value;
+ define(p, xmath_given_cmd, cur_val);
+ break;
+ case umath_char_def_code:
+ mval = scan_mathchar(umathnum_mathcode);
+ cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value;
+ define(p, xmath_given_cmd, cur_val);
+ break;
+ default:
+ scan_register_num();
+ switch (n) {
+ case count_def_code:
+ define(p, assign_int_cmd, count_base + cur_val);
+ break;
+ case attribute_def_code:
+ define(p, assign_attr_cmd, attribute_base + cur_val);
+ break;
+ case dimen_def_code:
+ define(p, assign_dimen_cmd, scaled_base + cur_val);
+ break;
+ case skip_def_code:
+ define(p, assign_glue_cmd, skip_base + cur_val);
+ break;
+ case mu_skip_def_code:
+ define(p, assign_mu_glue_cmd, mu_skip_base + cur_val);
+ break;
+ case toks_def_code:
+ define(p, assign_toks_cmd, toks_base + cur_val);
+ break;
+ default:
+ confusion("shorthand_def");
+ break;
+ }
+ break;
+ }
+ break;
+ case read_to_cs_cmd:
+ j = cur_chr;
+ scan_int();
+ n = cur_val;
+ if (!scan_keyword("to")) {
+ print_err("Missing `to' inserted");
+ help2("You should have said `\\read<number> to \\cs'.",
+ "I'm going to look for the \\cs now.");
+ error();
+ }
+ get_r_token();
+ p = cur_cs;
+ read_toks(n, p, j);
+ define(p, call_cmd, cur_val);
+ break;
+ case toks_register_cmd:
+ case assign_toks_cmd:
+ /* The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive
+ their values in the following way. (For safety's sake, we place an
+ enclosing pair of braces around an \.{\\output} list.) */
+ q = cur_cs;
+ if (cur_cmd == toks_register_cmd) {
+ scan_register_num();
+ p = toks_base + cur_val;
+ } else {
+ p = cur_chr; /* |p=every_par_loc| or |output_routine_loc| or \dots */
+ }
+ scan_optional_equals();
+ /* Get the next non-blank non-relax non-call token */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+
+ if (cur_cmd != left_brace_cmd) {
+ /* If the right-hand side is a token parameter
+ or token register, finish the assignment and |goto done| */
+ if (cur_cmd == toks_register_cmd) {
+ scan_register_num();
+ cur_cmd = assign_toks_cmd;
+ cur_chr = toks_base + cur_val;
+ }
+ if (cur_cmd == assign_toks_cmd) {
+ q = equiv(cur_chr);
+ if (q == null) {
+ define(p, undefined_cs_cmd, null);
+ } else {
+ add_token_ref(q);
+ define(p, call_cmd, q);
+ }
+ goto DONE;
+ }
+ }
+ back_input();
+ cur_cs = q;
+ q = scan_toks(false, false);
+ if (token_link(def_ref) == null) { /* empty list: revert to the default */
+ define(p, undefined_cs_cmd, null);
+ free_avail(def_ref);
+ } else {
+ if (p == output_routine_loc) { /* enclose in curlies */
+ p = get_avail();
+ set_token_link(q, p);
+ p = output_routine_loc;
+ q = token_link(q);
+ set_token_info(q, right_brace_token + '}');
+ q = get_avail();
+ set_token_info(q, left_brace_token + '{');
+ set_token_link(q, token_link(def_ref));
+ set_token_link(def_ref, q);
+ }
+ define(p, call_cmd, def_ref);
+ }
+ break;
+ case assign_int_cmd:
+ /* Similar routines are used to assign values to the numeric parameters. */
+ p = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ assign_internal_value(a, p, cur_val);
+ break;
+ case assign_attr_cmd:
+ p = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ if ((p - attribute_base) > max_used_attr)
+ max_used_attr = (p - attribute_base);
+ attr_list_cache = cache_disabled;
+ word_define(p, cur_val);
+ break;
+ case assign_dir_cmd:
+ /* DIR: Assign direction codes */
+ scan_direction();
+ switch (cur_chr) {
+ case int_base + page_direction_code:
+ eq_word_define(int_base + page_direction_code, cur_val);
+ break;
+ case int_base + body_direction_code:
+ eq_word_define(int_base + body_direction_code, cur_val);
+ break;
+ case int_base + par_direction_code:
+ eq_word_define(int_base + par_direction_code, cur_val);
+ break;
+ case int_base + math_direction_code:
+ eq_word_define(int_base + math_direction_code, cur_val);
+ break;
+ case int_base + text_direction_code:
+ case int_base + line_direction_code:
+ /*
+ pre version 0.97 this was a commented section because various tests hint that this
+ is unnecessary and sometimes even produces weird results, like:
+
+ (\hbox{\textdir TRT ABC\textdir TLT DEF}))
+
+ becomes
+
+ (DEFCBA)
+
+ in the output when we use
+
+ tail_append(new_dir(text_direction_par)
+
+ but when we append the reverse of the current it goes better
+
+ */
+ check_glue = (cur_chr == (int_base + line_direction_code));
+ if (check_glue) {
+ cur_chr = int_base + text_direction_code ;
+ }
+ if (abs(mode) == hmode) {
+ if (no_local_dirs_par > 0) {
+ /* tail is non zero but we test anyway */
+ if (check_glue && (tail != null && type(tail) == glue_node)) {
+ halfword prev = alink(tail);
+ halfword dirn = new_dir(text_direction_par - dir_swap);
+ couple_nodes(prev,dirn);
+ couple_nodes(dirn,tail);
+ } else {
+ tail_append(new_dir(text_direction_par - dir_swap));
+ }
+ } else {
+ /* what is the use of nolocaldirs .. maybe we should get rid of it */
+ }
+ update_text_dir_ptr(cur_val);
+ tail_append(new_dir(cur_val));
+ dir_level(tail) = cur_level;
+ } else {
+ update_text_dir_ptr(cur_val);
+ }
+ /* original:
+
+ // if ((no_local_dirs_par > 0) && (abs(mode) == hmode)) {
+ // // tail_append(new_dir(text_direction_par) // kind of wrong
+ // tail_append(new_dir(text_direction_par - dir_swap)); // better
+ // }
+
+ update_text_dir_ptr(cur_val);
+ if (abs(mode) == hmode) {
+ tail_append(new_dir(cur_val));
+ dir_level(tail) = cur_level;
+ }
+ */
+ eq_word_define(int_base + text_direction_code, cur_val);
+ eq_word_define(int_base + no_local_dirs_code, no_local_dirs_par + 1);
+ break;
+ }
+ break;
+ case assign_dimen_cmd:
+ p = cur_chr;
+ scan_optional_equals();
+ scan_normal_dimen();
+ assign_internal_value(a, p, cur_val);
+ break;
+ case assign_glue_cmd:
+ case assign_mu_glue_cmd:
+ p = cur_chr;
+ n = cur_cmd;
+ scan_optional_equals();
+ if (n == assign_mu_glue_cmd)
+ scan_glue(mu_val_level);
+ else
+ scan_glue(glue_val_level);
+ define(p, glue_ref_cmd, cur_val);
+ break;
+ case def_char_code_cmd:
+ case def_del_code_cmd:
+ /* Let |n| be the largest legal code value, based on |cur_chr| */
+ if (cur_chr == cat_code_base)
+ n = max_char_code;
+ else if (cur_chr == sf_code_base)
+ n = 077777;
+ else
+ n = biggest_char;
+
+ p = cur_chr;
+ if (cur_chr == math_code_base) {
+ if (is_global(a))
+ cur_val1 = level_one;
+ else
+ cur_val1 = cur_level;
+ scan_extdef_math_code(cur_val1, tex_mathcode);
+ } else if (cur_chr == lc_code_base) {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ check_def_code(lc_code_base);
+ define_lc_code(p, cur_val);
+ } else if (cur_chr == uc_code_base) {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ check_def_code(uc_code_base);
+ define_uc_code(p, cur_val);
+ } else if (cur_chr == sf_code_base) {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ check_def_code(sf_code_base);
+ define_sf_code(p, cur_val);
+ } else if (cur_chr == cat_code_base) {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ check_def_code(cat_code_base);
+ define_cat_code(p, cur_val);
+ } else if (cur_chr == del_code_base) {
+ if (is_global(a))
+ cur_val1 = level_one;
+ else
+ cur_val1 = cur_level;
+ scan_extdef_del_code(cur_val1, tex_mathcode);
+ }
+ break;
+ case extdef_math_code_cmd:
+ case extdef_del_code_cmd:
+ if (is_global(a))
+ cur_val1 = level_one;
+ else
+ cur_val1 = cur_level;
+ if (cur_chr == math_code_base)
+ scan_extdef_math_code(cur_val1, umath_mathcode);
+ else if (cur_chr == math_code_base + 1)
+ scan_extdef_math_code(cur_val1, umathnum_mathcode);
+ else if (cur_chr == del_code_base)
+ scan_extdef_del_code(cur_val1, umath_mathcode);
+ else if (cur_chr == del_code_base + 1)
+ scan_extdef_del_code(cur_val1, umathnum_mathcode);
+ break;
+ case def_family_cmd:
+ p = cur_chr;
+ scan_math_family_int();
+ cur_val1 = cur_val;
+ scan_optional_equals();
+ scan_font_ident();
+ define_fam_fnt(cur_val1, p, cur_val);
+ break;
+ case set_math_param_cmd:
+ p = cur_chr;
+ get_token();
+ if (cur_cmd != math_style_cmd) {
+ print_err("Missing math style, treated as \\displaystyle");
+ help1
+ ("A style should have been here; I inserted `\\displaystyle'.");
+ cur_val1 = display_style;
+ back_error();
+ } else {
+ cur_val1 = cur_chr;
+ }
+ scan_optional_equals();
+ if (p < math_param_first_mu_glue) {
+ if (p == math_param_radical_degree_raise)
+ scan_int();
+ else
+ scan_dimen(false, false, false);
+ } else {
+ scan_glue(mu_val_level);
+ if (cur_val == thin_mu_skip_par)
+ cur_val = thin_mu_skip_code;
+ else if (cur_val == med_mu_skip_par)
+ cur_val = med_mu_skip_code;
+ else if (cur_val == thick_mu_skip_par)
+ cur_val = thick_mu_skip_code;
+ }
+ define_math_param(p, cur_val1, cur_val);
+ break;
+ case register_cmd:
+ case advance_cmd:
+ case multiply_cmd:
+ case divide_cmd:
+ do_register_command(a);
+ break;
+ case set_box_cmd:
+ /* The processing of boxes is somewhat different, because we may need
+ to scan and create an entire box before we actually change the value
+ of the old one. */
+ scan_register_num();
+ if (is_global(a))
+ n = global_box_flag + cur_val;
+ else
+ n = box_flag + cur_val;
+ scan_optional_equals();
+ if (set_box_allowed) {
+ scan_box(n);
+ } else {
+ print_err("Improper \\setbox");
+ help2("Sorry, \\setbox is not allowed after \\halign in a display,",
+ "or between \\accent and an accented character.");
+ error();
+ }
+ break;
+ case set_aux_cmd:
+ /* The |space_factor| or |prev_depth| settings are changed when a |set_aux|
+ command is sensed. Similarly, |prev_graf| is changed in the presence of
+ |set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of
+ |set_page_int|. These definitions are always global. */
+ alter_aux();
+ break;
+ case set_prev_graf_cmd:
+ alter_prev_graf();
+ break;
+ case set_page_dimen_cmd:
+ alter_page_so_far();
+ break;
+ case set_page_int_cmd:
+ alter_integer();
+ break;
+ case set_box_dimen_cmd:
+ /* When some dimension of a box register is changed, the change isn't exactly
+ global; but \TeX\ does not look at the \.{\\global} switch. */
+ alter_box_dimen();
+ break;
+ case set_tex_shape_cmd:
+ q = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ n = cur_val;
+ if (n <= 0) {
+ p = null;
+ } else {
+ p = new_node(shape_node, 2 * (n + 1) + 1);
+ vinfo(p + 1) = n;
+ for (j = 1; j <= n; j++) {
+ scan_normal_dimen();
+ varmem[p + 2 * j].cint = cur_val; /* indentation */
+ scan_normal_dimen();
+ varmem[p + 2 * j + 1].cint = cur_val; /* width */
+ }
+ }
+ define(q, shape_ref_cmd, p);
+ break;
+ case set_etex_shape_cmd:
+ q = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ n = cur_val;
+ if (n <= 0) {
+ p = null;
+ } else {
+ n = (cur_val / 2) + 1;
+ p = new_node(shape_node, 2 * n + 1 + 1);
+ vinfo(p + 1) = n;
+ n = cur_val;
+ varmem[p + 2].cint = n; /* number of penalties */
+ for (j = p + 3; j <= p + n + 2; j++) {
+ scan_int();
+ varmem[j].cint = cur_val; /* penalty values */
+ }
+ if (!odd(n))
+ varmem[p + n + 3].cint = 0; /* unused */
+ }
+ define(q, shape_ref_cmd, p);
+ break;
+ case hyph_data_cmd:
+ /* All of \TeX's parameters are kept in |eqtb| except the font information,
+ the interaction mode, and the hyphenation tables; these are strictly global.
+ */
+ switch (cur_chr) {
+ case 0:
+ new_hyph_exceptions();
+ break;
+ case 1:
+ new_patterns();
+ break;
+ case 2:
+ new_pre_hyphen_char();
+ break;
+ case 3:
+ new_post_hyphen_char();
+ break;
+ case 4:
+ new_pre_exhyphen_char();
+ break;
+ case 5:
+ new_post_exhyphen_char();
+ break;
+ case 6:
+ new_hyphenation_min();
+ break;
+ case 7:
+ new_hj_code();
+ break;
+ }
+ break;
+ case assign_font_dimen_cmd:
+ set_font_dimen();
+ break;
+ case assign_font_int_cmd:
+ n = cur_chr;
+ scan_font_ident();
+ f = cur_val;
+ if (n == no_lig_code) {
+ set_no_ligatures(f);
+ } else if (n < lp_code_base) {
+ scan_optional_equals();
+ scan_int();
+ if (n == 0)
+ set_hyphen_char(f, cur_val);
+ else
+ set_skew_char(f, cur_val);
+ } else {
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ scan_int();
+ switch (n) {
+ case lp_code_base:
+ set_lp_code(f, p, cur_val);
+ break;
+ case rp_code_base:
+ set_rp_code(f, p, cur_val);
+ break;
+ case ef_code_base:
+ set_ef_code(f, p, cur_val);
+ break;
+ case tag_code:
+ set_tag_code(f, p, cur_val);
+ break;
+ }
+ }
+ break;
+ case def_font_cmd:
+ /* Here is where the information for a new font gets loaded. */
+ tex_def_font((small_number) a);
+ break;
+ case letterspace_font_cmd:
+ new_letterspaced_font((small_number) a);
+ break;
+ case copy_font_cmd:
+ make_font_copy((small_number) a);
+ break;
+ case set_font_id_cmd:
+ scan_int();
+ if (is_valid_font(cur_val))
+ zset_cur_font(cur_val);
+ break ;
+ case set_interaction_cmd:
+ new_interaction();
+ break;
+ default:
+ confusion("prefix");
+ break;
+ } /* end of Assignments cases */
+ DONE:
+ /* Insert a token saved by \.{\\afterassignment}, if any */
+ if (after_token != 0) {
+ cur_tok = after_token;
+ back_input();
+ after_token = 0;
+ }
+}
+
+@ @c
+void fixup_directions(void)
+{
+ int temp_no_whatsits = no_local_whatsits_par;
+ int temp_no_dirs = no_local_dirs_par;
+ int temporary_dir = text_direction_par;
+ if (dir_level(text_dir_ptr) == cur_level) {
+ /* DIR: Remove from |text_dir_ptr| */
+ halfword text_dir_tmp = vlink(text_dir_ptr);
+ flush_node(text_dir_ptr);
+ text_dir_ptr = text_dir_tmp;
+ }
+ unsave();
+ if (abs(mode) == hmode) {
+ if (temp_no_dirs != 0) {
+ /* DIR: Add local dir node */
+ tail_append(new_dir(text_direction_par));
+ dir_dir(tail) = temporary_dir - dir_swap;
+ }
+ if (temp_no_whatsits != 0) {
+ /* LOCAL: Add local paragraph node */
+ tail_append(make_local_par_node(hmode_par_par_code));
+ }
+ }
+}
+
+@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or
+something similar, the |get_r_token| routine will substitute a special
+control sequence for a token that is not redefinable.
+
+@c
+void get_r_token(void)
+{
+ RESTART:
+ do {
+ get_token();
+ } while (cur_tok == space_token);
+ if ((cur_cs == 0) || (cur_cs > eqtb_top) ||
+ ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) {
+ print_err("Missing control sequence inserted");
+ help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.",
+ "I've inserted an inaccessible control sequence so that your",
+ "definition will be completed without mixing me up too badly.",
+ "You can recover graciously from this error, if you're",
+ "careful; see exercise 27.2 in The TeXbook.");
+ if (cur_cs == 0)
+ back_input();
+ cur_tok = cs_token_flag + frozen_protection;
+ ins_error();
+ goto RESTART;
+ }
+}
+
+@ @c
+void assign_internal_value(int a, halfword p, int val)
+{
+ halfword n;
+ if ((p >= int_base) && (p < attribute_base)) {
+ switch ((p - int_base)) {
+ case cat_code_table_code:
+ if (valid_catcode_table(val)) {
+ if (val != cat_code_table_par)
+ word_define(p, val);
+ } else {
+ print_err("Invalid \\catcode table");
+ help2
+ ("You can only switch to a \\catcode table that is initialized",
+ "using \\savecatcodetable or \\initcatcodetable, or to table 0");
+ error();
+ }
+ break;
+ case output_box_code:
+ if ((val > 65535) | (val < 0)) {
+ print_err("Invalid \\outputbox");
+ help1
+ ("The value for \\outputbox has to be between 0 and 65535.");
+ error();
+ } else {
+ word_define(p, val);
+ }
+ break;
+ case new_line_char_code:
+ if (val > 127) {
+ print_err("Invalid \\newlinechar");
+ help2
+ ("The value for \\newlinechar has to be no higher than 127.",
+ "Your invalid assignment will be ignored.");
+ error();
+ } else {
+ word_define(p, val);
+ }
+ break;
+ case end_line_char_code:
+ if (val > 127) {
+ print_err("Invalid \\endlinechar");
+ help2
+ ("The value for \\endlinechar has to be no higher than 127.",
+ "Your invalid assignment will be ignored.");
+ error();
+ } else {
+ word_define(p, val);
+ }
+ break;
+ case language_code:
+ if (val < 0) {
+ word_define(int_base + cur_lang_code, -1);
+ word_define(p, -1);
+ } else if (val > 16383) {
+ print_err("Invalid \\language");
+ help2
+ ("The absolute value for \\language has to be no higher than 16383.",
+ "Your invalid assignment will be ignored.");
+ error();
+ } else {
+ word_define(int_base + cur_lang_code, val);
+ word_define(p, val);
+ }
+ break;
+ default:
+ word_define(p, val);
+ break;
+ }
+ /* If we are defining subparagraph penalty levels while we are
+ in hmode, then we put out a whatsit immediately, otherwise
+ we leave it alone. This mechanism might not be sufficiently
+ powerful, and some other algorithm, searching down the stack,
+ might be necessary. Good first step. */
+ if ((abs(mode) == hmode) &&
+ ((p == (int_base + local_inter_line_penalty_code)) ||
+ (p == (int_base + local_broken_penalty_code)))) {
+ /* LOCAL: Add local paragraph node */
+ tail_append(make_local_par_node(penalty_par_code));
+ eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1);
+ }
+ } else if ((p >= dimen_base) && (p <= eqtb_size)) {
+ if (p == (dimen_base + page_left_offset_code)) {
+ n = val - one_true_inch;
+ word_define(dimen_base + h_offset_code, n);
+ } else if (p == (dimen_base + h_offset_code)) {
+ n = val + one_true_inch;
+ word_define(dimen_base + page_left_offset_code, n);
+ } else if (p == (dimen_base + page_top_offset_code)) {
+ n = val - one_true_inch;
+ word_define(dimen_base + v_offset_code, n);
+ } else if (p == (dimen_base + v_offset_code)) {
+ n = val + one_true_inch;
+ word_define(dimen_base + page_top_offset_code, n);
+ }
+ word_define(p, val);
+ } else if ((p >= local_base) && (p < toks_base)) { /* internal locals */
+ define(p, call_cmd, val);
+ } else {
+ confusion("assign internal value");
+ }
+}
+
+@ We use the fact that |register<advance<multiply<divide|
+
+Compute the register location |l| and its type |p|; but |return| if invalid
+Here we use the fact that the consecutive codes |int_val..mu_val| and
+|assign_int..assign_mu_glue| correspond to each other nicely.
+
+@c
+void do_register_command(int a)
+{
+ int p;
+ halfword q = cur_cmd;
+ halfword l = 0;
+ if (q != register_cmd) {
+ get_x_token();
+ if ((cur_cmd >= assign_int_cmd) && (cur_cmd <= assign_mu_glue_cmd)) {
+ l = cur_chr;
+ p = cur_cmd - assign_int_cmd;
+ goto FOUND;
+ }
+ if (cur_cmd != register_cmd) {
+ print_err("You can't use `");
+ print_cmd_chr((quarterword) cur_cmd, cur_chr);
+ tprint("' after ");
+ print_cmd_chr((quarterword) q, 0);
+ help1("I'm forgetting what you said and not changing anything.");
+ error();
+ return;
+ }
+ }
+ p = cur_chr;
+ scan_register_num();
+ if (p == int_val_level)
+ l = cur_val + count_base;
+ else if (p == attr_val_level)
+ l = cur_val + attribute_base;
+ else if (p == dimen_val_level)
+ l = cur_val + scaled_base;
+ else if (p == glue_val_level)
+ l = cur_val + skip_base;
+ else if (p == mu_val_level)
+ l = cur_val + mu_skip_base;
+ FOUND:
+ if (q == register_cmd) {
+ scan_optional_equals();
+ } else if (scan_keyword("by")) {
+ /* optional `\.{by}' */
+ }
+ arith_error = false;
+ if (q < multiply_cmd) {
+ /* Compute result of |register| or |advance|, put it in |cur_val| */
+ if (p < glue_val_level) {
+ if ((p == int_val_level) || (p == attr_val_level))
+ scan_int();
+ else
+ scan_normal_dimen();
+ if (q == advance_cmd)
+ cur_val = cur_val + eqtb[l].cint;
+ } else {
+ /* we can probably save a copy */
+ scan_glue(p);
+ if (q == advance_cmd) {
+ /* Compute the sum of two glue specs */
+ halfword r = equiv(l);
+ q = new_spec(cur_val);
+ flush_node(cur_val);
+ width(q) = width(q) + width(r);
+ if (stretch(q) == 0) {
+ stretch_order(q) = normal;
+ }
+ if (stretch_order(q) == stretch_order(r)) {
+ stretch(q) = stretch(q) + stretch(r);
+ } else if ((stretch_order(q) < stretch_order(r)) && (stretch(r) != 0)) {
+ stretch(q) = stretch(r);
+ stretch_order(q) = stretch_order(r);
+ }
+ if (shrink(q) == 0) {
+ shrink_order(q) = normal;
+ }
+ if (shrink_order(q) == shrink_order(r)) {
+ shrink(q) = shrink(q) + shrink(r);
+ } else if ((shrink_order(q) < shrink_order(r)) && (shrink(r) != 0)) {
+ shrink(q) = shrink(r);
+ shrink_order(q) = shrink_order(r);
+ }
+ cur_val = q;
+ }
+ }
+ } else {
+ /* Compute result of |multiply| or |divide|, put it in |cur_val| */
+ scan_int();
+ if (p < glue_val_level) {
+ if (q == multiply_cmd) {
+ if ((p == int_val_level) || (p == attr_val_level)) {
+ cur_val = mult_integers(eqtb[l].cint, cur_val);
+ } else {
+ cur_val = nx_plus_y(eqtb[l].cint, cur_val, 0);
+ }
+ } else {
+ cur_val = x_over_n(eqtb[l].cint, cur_val);
+ }
+ } else {
+ halfword s = equiv(l);
+ halfword r = new_spec(s);
+ if (q == multiply_cmd) {
+ width(r) = nx_plus_y(width(s), cur_val, 0);
+ stretch(r) = nx_plus_y(stretch(s), cur_val, 0);
+ shrink(r) = nx_plus_y(shrink(s), cur_val, 0);
+ } else {
+ width(r) = x_over_n(width(s), cur_val);
+ stretch(r) = x_over_n(stretch(s), cur_val);
+ shrink(r) = x_over_n(shrink(s), cur_val);
+ }
+ cur_val = r;
+ }
+ }
+ if (arith_error) {
+ print_err("Arithmetic overflow");
+ help2("I can't carry out that multiplication or division,",
+ "since the result is out of range.");
+ if (p >= glue_val_level)
+ flush_node(cur_val);
+ error();
+ return;
+ }
+ if (p < glue_val_level) {
+ if (p == attr_val_level) {
+ if ((l - attribute_base) > max_used_attr)
+ max_used_attr = (l - attribute_base);
+ attr_list_cache = cache_disabled;
+ }
+ if ((p == int_val_level) || (p == dimen_val_level))
+ assign_internal_value(a, l, cur_val);
+ else
+ word_define(l, cur_val);
+ } else {
+ define(l, glue_ref_cmd, cur_val);
+ }
+}
+
+@ @c
+void alter_aux(void)
+{
+ halfword c; /* |hmode| or |vmode| */
+ if (cur_chr != abs(mode)) {
+ report_illegal_case();
+ } else {
+ c = cur_chr;
+ scan_optional_equals();
+ if (c == vmode) {
+ scan_normal_dimen();
+ prev_depth_par = cur_val;
+ } else {
+ scan_int();
+ if ((cur_val <= 0) || (cur_val > 32767)) {
+ print_err("Bad space factor");
+ help1("I allow only values in the range 1..32767 here.");
+ int_error(cur_val);
+ } else {
+ space_factor_par = cur_val;
+ }
+ }
+ }
+}
+
+@ @c
+void alter_prev_graf(void)
+{
+ int p; /* index into |nest| */
+ p = nest_ptr;
+ while (abs(nest[p].mode_field) != vmode)
+ decr(p);
+ scan_optional_equals();
+ scan_int();
+ if (cur_val < 0) {
+ print_err("Bad \\prevgraf");
+ help1("I allow only nonnegative values here.");
+ int_error(cur_val);
+ } else {
+ nest[p].pg_field = cur_val;
+ }
+}
+
+@ @c
+void alter_page_so_far(void)
+{
+ int c; /* index into |page_so_far| */
+ c = cur_chr;
+ scan_optional_equals();
+ scan_normal_dimen();
+ page_so_far[c] = cur_val;
+}
+
+@ @c
+void alter_integer(void)
+{
+ int c; /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */
+ c = cur_chr;
+ scan_optional_equals();
+ scan_int();
+ if (c == 0) {
+ dead_cycles = cur_val;
+ } else if (c == 2) {
+ if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) {
+ print_err("Bad interaction mode");
+ help2("Modes are 0=batch, 1=nonstop, 2=scroll, and",
+ "3=errorstop. Proceed, and I'll ignore this case.");
+ int_error(cur_val);
+ } else {
+ cur_chr = cur_val;
+ new_interaction();
+ }
+ } else {
+ insert_penalties = cur_val;
+ }
+}
+
+@ @c
+void alter_box_dimen(void)
+{
+ int c; /* |width_offset| or |height_offset| or |depth_offset| */
+ int b; /* box number */
+ c = cur_chr;
+ scan_register_num();
+ b = cur_val;
+ scan_optional_equals();
+ scan_normal_dimen();
+ if (box(b) != null)
+ varmem[box(b) + c].cint = cur_val;
+}
+
+@ @c
+void new_interaction(void)
+{
+ print_ln();
+ interaction = cur_chr;
+ if (interaction == batch_mode)
+ kpse_make_tex_discard_errors = 1;
+ else
+ kpse_make_tex_discard_errors = 0;
+ fixup_selector(log_opened_global);
+}
+
+@ The \.{\\afterassignment} command puts a token into the global
+variable |after_token|. This global variable is examined just after
+every assignment has been performed.
+
+@c
+halfword after_token; /* zero, or a saved token */
+
+@ Here is a procedure that might be called `Get the next non-blank non-relax
+non-call non-assignment token'.
+
+@c
+void do_assignments(void)
+{
+ while (true) {
+ /* Get the next non-blank non-relax... */
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ if (cur_cmd <= max_non_prefixed_command)
+ return;
+ set_box_allowed = false;
+ prefixed_command();
+ set_box_allowed = true;
+ }
+}
+
+@ @c
+void open_or_close_in(void)
+{
+ int c; /* 1 for \.{\\openin}, 0 for \.{\\closein} */
+ int n; /* stream number */
+ char *fn;
+ c = cur_chr;
+ scan_four_bit_int();
+ n = cur_val;
+ if (read_open[n] != closed) {
+ lua_a_close_in(read_file[n], (n + 1));
+ read_open[n] = closed;
+ }
+ if (c != 0) {
+ scan_optional_equals();
+ do {
+ get_x_token();
+ } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd));
+ back_input();
+ if (cur_cmd != left_brace_cmd) {
+ scan_file_name(); /* set |cur_name| to desired file name */
+ if (cur_ext == get_nullstr())
+ cur_ext = maketexstring(".tex");
+ } else {
+ scan_file_name_toks();
+ }
+ fn = pack_file_name(cur_name, cur_area, cur_ext);
+ if (lua_a_open_in(&(read_file[n]), fn, (n + 1))) {
+ read_open[n] = just_open;
+ }
+ }
+}
+
+@ @c
+boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */
+
+void issue_message(void)
+{
+ int old_setting; /* holds |selector| setting */
+ int c; /* identifies \.{\\message} and \.{\\errmessage} */
+ str_number s; /* the message */
+ c = cur_chr;
+ (void) scan_toks(false, true);
+ old_setting = selector;
+ selector = new_string;
+ token_show(def_ref);
+ selector = old_setting;
+ flush_list(def_ref);
+ str_room(1);
+ s = make_string();
+ if (c == 0) {
+ /* Print string |s| on the terminal */
+ if (term_offset + (int) str_length(s) > max_print_line - 2)
+ print_ln();
+ else if ((term_offset > 0) || (file_offset > 0))
+ print_char(' ');
+ print(s);
+ update_terminal();
+
+ } else {
+ /* Print string |s| as an error message */
+ /* If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined
+ \.{\\errhelp}, we don't want to give a long help message each time. So we
+ give a verbose explanation only once. */
+ print_err("");
+ print(s);
+ if (err_help_par != null) {
+ use_err_help = true;
+ } else if (long_help_seen) {
+ help1("(That was another \\errmessage.)");
+ } else {
+ if (interaction < error_stop_mode)
+ long_help_seen = true;
+ help4("This error message was generated by an \\errmessage",
+ "command, so I can't give any explicit help.",
+ "Pretend that you're Hercule Poirot: Examine all clues,",
+ "and deduce the truth by order and method.");
+ }
+ error();
+ use_err_help = false;
+
+ }
+ flush_str(s);
+}
+
+@ The |error| routine calls on |give_err_help| if help is requested from
+the |err_help| parameter.
+
+@c
+void give_err_help(void)
+{
+ token_show(err_help_par);
+}
+
+@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by
+building a token list and then changing the cases of the letters in it.
+
+@c
+void shift_case(void)
+{
+ halfword b; /* |lc_code_base| or |uc_code_base| */
+ halfword p; /* runs through the token list */
+ halfword t; /* token */
+ halfword c; /* character code */
+ halfword i; /* inbetween */
+ b = cur_chr;
+ p = scan_toks(false, false);
+ p = token_link(def_ref);
+ while (p != null) {
+ /* Change the case of the token in |p|, if a change is appropriate */
+ /*
+ When the case of a |chr_code| changes, we don't change the |cmd|.
+ We also change active characters.
+ */
+ t = token_info(p);
+ if (t < cs_token_flag) {
+ c = t % STRING_OFFSET;
+ if (b == uc_code_base)
+ i = get_uc_code(c);
+ else
+ i = get_lc_code(c);
+ if (i != 0)
+ set_token_info(p, t - c + i);
+ } else if (is_active_cs(cs_text(t - cs_token_flag))) {
+ c = active_cs_value(cs_text(t - cs_token_flag));
+ if (b == uc_code_base)
+ i = get_uc_code(c);
+ else
+ i = get_lc_code(c);
+ if (i != 0)
+ set_token_info(p, active_to_cs(i, true) + cs_token_flag);
+ }
+ p = token_link(p);
+ }
+ back_list(token_link(def_ref));
+ free_avail(def_ref); /* omit reference count */
+}
+
+@ We come finally to the last pieces missing from |main_control|, namely the
+`\.{\\show}' commands that are useful when debugging.
+
+@c
+void show_whatever(void)
+{
+ halfword p; /* tail of a token list to show */
+ int t; /* type of conditional being shown */
+ int m; /* upper bound on |fi_or_else| codes */
+ int l; /* line where that conditional began */
+ int n; /* level of \.{\\if...\\fi} nesting */
+ switch (cur_chr) {
+ case show_lists:
+ begin_diagnostic();
+ show_activities();
+ break;
+ case show_box_code:
+ /* Show the current contents of a box */
+ scan_register_num();
+ begin_diagnostic();
+ tprint_nl("> \\box");
+ print_int(cur_val);
+ print_char('=');
+ if (box(cur_val) == null)
+ tprint("void");
+ else
+ show_box(box(cur_val));
+ break;
+ case show_code:
+ /* Show the current meaning of a token, then |goto common_ending| */
+ get_token();
+ if (interaction == error_stop_mode)
+ wake_up_terminal();
+ tprint_nl("> ");
+ if (cur_cs != 0) {
+ sprint_cs(cur_cs);
+ print_char('=');
+ }
+ print_meaning();
+ goto COMMON_ENDING;
+ break;
+ /* Cases for |show_whatever| */
+ case show_groups:
+ begin_diagnostic();
+ show_save_groups();
+ break;
+ case show_ifs:
+ begin_diagnostic();
+ tprint_nl("");
+ print_ln();
+ if (cond_ptr == null) {
+ tprint_nl("### ");
+ tprint("no active conditionals");
+ } else {
+ p = cond_ptr;
+ n = 0;
+ do {
+ incr(n);
+ p = vlink(p);
+ } while (p != null);
+ p = cond_ptr;
+ t = cur_if;
+ l = if_line;
+ m = if_limit;
+ do {
+ tprint_nl("### level ");
+ print_int(n);
+ tprint(": ");
+ print_cmd_chr(if_test_cmd, t);
+ if (m == fi_code)
+ tprint_esc("else");
+ print_if_line(l);
+ decr(n);
+ t = if_limit_subtype(p);
+ l = if_line_field(p);
+ m = if_limit_type(p);
+ p = vlink(p);
+ } while (p != null);
+ }
+ break;
+ default:
+ /* Show the current value of some parameter or register,
+ then |goto common_ending| */
+ p = the_toks();
+ if (interaction == error_stop_mode)
+ wake_up_terminal();
+ tprint_nl("> ");
+ token_show(temp_token_head);
+ flush_list(token_link(temp_token_head));
+ goto COMMON_ENDING;
+ break;
+ }
+ /* Complete a potentially long \.{\\show} command */
+ end_diagnostic(true);
+ print_err("OK");
+ if (selector == term_and_log) {
+ if (tracing_online_par <= 0) {
+ selector = term_only;
+ tprint(" (see the transcript file)");
+ selector = term_and_log;
+ }
+ }
+ COMMON_ENDING:
+ if (interaction < error_stop_mode) {
+ help0();
+ decr(error_count);
+ } else if (tracing_online_par > 0) {
+ help3("This isn't an error message; I'm just \\showing something.",
+ "Type `I\\show...' to show more (e.g., \\show\\cs,",
+ "\\showthe\\count10, \\showbox255, \\showlists).");
+ } else {
+ help5("This isn't an error message; I'm just \\showing something.",
+ "Type `I\\show...' to show more (e.g., \\show\\cs,",
+ "\\showthe\\count10, \\showbox255, \\showlists).",
+ "And type `I\\tracingonline=1\\show...' to show boxes and",
+ "lists on your terminal as well as in the transcript file.");
+ }
+ error();
+}
+
+@ @c
+void initialize(void)
+{ /* this procedure gets things started properly */
+ int k; /* index into |mem|, |eqtb|, etc. */
+ /* Initialize whatever \TeX\ might access */
+ /* Set initial values of key variables */
+ initialize_errors();
+ initialize_arithmetic();
+ max_used_attr = -1;
+ attr_list_cache = cache_disabled;
+ initialize_nesting();
+
+ /* Start a new current page */
+ page_contents = empty;
+ page_tail = page_head;
+#if 0
+ vlink(page_head) = null;
+#endif
+ last_glue = max_halfword;
+ last_penalty = 0;
+ last_kern = 0;
+ last_node_type = -1;
+ page_depth = 0;
+ page_max_depth = 0;
+
+ initialize_equivalents();
+ no_new_control_sequence = true; /* new identifiers are usually forbidden */
+ init_primitives();
+
+ mag_set = 0;
+ initialize_marks();
+ initialize_read();
+
+ static_pdf = init_pdf_struct(static_pdf); /* should be init_backend() */
+
+ format_ident = 0;
+ format_name = get_nullstr();
+ initialize_directions();
+ initialize_write_files();
+ seconds_and_micros(epochseconds, microseconds);
+ initialize_start_time(static_pdf);
+
+ edit_name_start = 0;
+ stop_at_space = true;
+
+ if (ini_version) {
+ /* Initialize table entries (done by \.{INITEX} only) */
+
+ init_node_mem(500);
+ initialize_tokens();
+ /* Initialize the special list heads and constant nodes */
+ initialize_alignments();
+ initialize_buildpage();
+
+ initialize_active();
+
+ set_eq_type(undefined_control_sequence, undefined_cs_cmd);
+ set_equiv(undefined_control_sequence, null);
+ set_eq_level(undefined_control_sequence, level_zero);
+ for (k = null_cs; k <= (eqtb_top - 1); k++)
+ eqtb[k] = eqtb[undefined_control_sequence];
+ set_equiv(glue_base, zero_glue);
+ set_eq_level(glue_base, level_one);
+ set_eq_type(glue_base, glue_ref_cmd);
+ for (k = glue_base + 1; k <= local_base - 1; k++) {
+ eqtb[k] = eqtb[glue_base];
+ }
+ par_shape_par_ptr = null;
+ set_eq_type(par_shape_loc, shape_ref_cmd);
+ set_eq_level(par_shape_loc, level_one);
+ for (k = etex_pen_base; k <= (etex_pens - 1); k++)
+ eqtb[k] = eqtb[par_shape_loc];
+ for (k = output_routine_loc; k <= toks_base + biggest_reg; k++)
+ eqtb[k] = eqtb[undefined_control_sequence];
+ box(0) = null;
+ set_eq_type(box_base, box_ref_cmd);
+ set_eq_level(box_base, level_one);
+ for (k = box_base + 1; k <= (box_base + biggest_reg); k++)
+ eqtb[k] = eqtb[box_base];
+ cur_font_par = null_font;
+ set_eq_type(cur_font_loc, data_cmd);
+ set_eq_level(cur_font_loc, level_one);
+ set_equiv(cat_code_base, 0);
+ set_eq_type(cat_code_base, data_cmd);
+ set_eq_level(cat_code_base, level_one);
+ eqtb[internal_math_param_base] = eqtb[cat_code_base];
+ eqtb[lc_code_base] = eqtb[cat_code_base];
+ eqtb[uc_code_base] = eqtb[cat_code_base];
+ eqtb[sf_code_base] = eqtb[cat_code_base];
+ eqtb[math_code_base] = eqtb[cat_code_base];
+ cat_code_table_par = 0;
+ initialize_math_codes();
+ initialize_text_codes();
+ initex_cat_codes(0);
+ for (k = '0'; k <= '9'; k++)
+ set_math_code(k, math_use_current_family_code, 0, k, level_one);
+ for (k = 'A'; k <= 'Z'; k++) {
+ set_math_code(k, math_use_current_family_code, 1, k, level_one);
+ set_math_code((k + 32), math_use_current_family_code, 1, (k + 32), level_one);
+ set_lc_code(k, k + 32, level_one);
+ set_lc_code(k + 32, k + 32, level_one);
+ set_uc_code(k, k, level_one);
+ set_uc_code(k + 32, k, level_one);
+ set_sf_code(k, 999, level_one);
+ }
+ for (k = int_base; k <= attribute_base - 1; k++)
+ eqtb[k].cint = 0;
+ for (k = attribute_base; k <= del_code_base - 1; k++)
+ eqtb[k].cint = UNUSED_ATTRIBUTE;
+ mag_par = 1000;
+ tolerance_par = 10000;
+ hang_after_par = 1;
+ max_dead_cycles_par = 25;
+ math_pre_display_gap_factor_par = 2000;
+ escape_char_par = '\\';
+ end_line_char_par = carriage_return;
+ set_del_code('.', 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */
+ ex_hyphen_char_par = '-';
+ output_box_par = 255;
+ for (k = dimen_base; k <= eqtb_size; k++)
+ eqtb[k].cint = 0;
+ page_left_offset_par = one_inch;
+ page_top_offset_par = one_inch;
+ page_right_offset_par = one_inch;
+ page_bottom_offset_par = one_inch;
+ ini_init_primitives();
+ hash_used = frozen_control_sequence; /* nothing is used */
+ hash_high = 0;
+ cs_count = 0;
+ set_eq_type(frozen_dont_expand, dont_expand_cmd);
+ cs_text(frozen_dont_expand) = maketexstring("notexpanded:");
+ set_eq_type(frozen_primitive, ignore_spaces_cmd);
+ set_equiv(frozen_primitive, 1);
+ set_eq_level(frozen_primitive, level_one);
+ cs_text(frozen_primitive) = maketexstring("primitive");
+ create_null_font();
+ font_bytes = 0;
+ px_dimen_par = one_bp;
+ math_eqno_gap_step_par = 1000 ;
+ cs_text(frozen_protection) = maketexstring("inaccessible");
+ format_ident = maketexstring(" (INITEX)");
+ cs_text(end_write) = maketexstring("endwrite");
+ set_eq_level(end_write, level_one);
+ set_eq_type(end_write, outer_call_cmd);
+ set_equiv(end_write, null);
+
+ }
+ synctexoffset = int_base + synctex_code;
+
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.w b/Build/source/texk/web2c/luatexdir/tex/texmath.w index 16911bc4a6c..769fbe8dd35 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texmath.w +++ b/Build/source/texk/web2c/luatexdir/tex/texmath.w @@ -1,2569 +1,2569 @@ -% texmath.w -% -% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c -#include "ptexlib.h" - -@ @c -#define mode mode_par -#define tail tail_par -#define head head_par -#define dir_save dirs_par - -/* - - \mathdisplayskipmode - - tex normally always inserts before and only after when larger than zero - - 0 = normal tex - 1 = always - 2 = non-zero - 3 = ignore - -*/ - -@ TODO: not sure if this is the right order -@c -#define back_error(A,B) do { \ - OK_to_interrupt=false; \ - back_input(); \ - OK_to_interrupt=true; \ - tex_error(A,B); \ - } while (0) - -@ @c -int scan_math(pointer, int); -int scan_math_style(pointer, int); -pointer fin_mlist(pointer); - -@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an -{\sl mlist}, which is essentially a tree structure representing that -formula. An mlist is a linear sequence of items, but we can regard it as -a tree structure because mlists can appear within mlists. For example, many -of the entries can be subscripted or superscripted, and such ``scripts'' -are mlists in their own right. - -An entire formula is parsed into such a tree before any of the actual -typesetting is done, because the current style of type is usually not -known until the formula has been fully scanned. For example, when the -formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell -that `\.{a+b}' will be in script size until `\.{\\over}' has appeared. - -During the scanning process, each element of the mlist being built is -classified as a relation, a binary operator, an open parenthesis, etc., -or as a construct like `\.{\\sqrt}' that must be built up. This classification -appears in the mlist data structure. - -After a formula has been fully scanned, the mlist is converted to an hlist -so that it can be incorporated into the surrounding text. This conversion is -controlled by a recursive procedure that decides all of the appropriate -styles by a ``top-down'' process starting at the outermost level and working -in towards the subformulas. The formula is ultimately pasted together using -combinations of horizontal and vertical boxes, with glue and penalty nodes -inserted as necessary. - -An mlist is represented internally as a linked list consisting chiefly -of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat -similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are -allowed to appear in mlists together with the noads; \TeX\ tells the difference -by means of the |type| field, since a noad's |type| is always greater than -that of a node. An mlist does not contain character nodes, hlist nodes, vlist -nodes, math nodes or unset nodes; in particular, each mlist item appears in the -variable-size part of |mem|, so the |type| field is always present. - -Each noad is five or more words long. The first word contains the -|type| and |subtype| and |link| fields that are already so familiar to -us; the second contains the attribute list pointer, and the third, -fourth an fifth words are called the noad's |nucleus|, |subscr|, and -|supscr| fields. (This use of a combined attribute list is temporary. -Eventually, each of fields need their own list) - -Consider, for example, the simple formula `\.{\$x\^2\$}', which would be -parsed into an mlist containing a single element called an |ord_noad|. -The |nucleus| of this noad is a representation of `\.x', the |subscr| is -empty, and the |supscr| is a representation of `\.2'. - -The |nucleus|, |subscr|, and |supscr| fields are further broken into -subfields. If |p| points to a noad, and if |q| is one of its principal -fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the -corresponding attribute of noad |p| is not present). Otherwise, there are -several possibilities for the subfields, depending on the |type| of |q|. - -\yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of -the sixteen font families, and |character(q)| is the number of a character -within a font of that family, as in a character node. - -\yskip\hang|type(q)=math_text_char_node| is similar, but the character is -unsubscripted and unsuperscripted and it is followed immediately by another -character from the same font. (This |type| setting appears only -briefly during the processing; it is used to suppress unwanted italic -corrections.) - -\yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box -node (either an |hlist_node| or a |vlist_node|) that should be used as the -value of the field. The |shift_amount| in the subsidiary box node is the -amount by which that box will be shifted downward. - -\yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to -an mlist; the mlist must be converted to an hlist in order to obtain -the value of this field. - -\yskip\noindent In the latter case, we might have |math_list(q)=null|. This -is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}' -and `\.{\$P\$}' produce different results (the former will not have the -``italic correction'' added to the width of |P|, but the ``script skip'' -will be added). - -@c -static void unsave_math(void) -{ - unsave(); - decr(save_ptr); - flush_node_list(text_dir_ptr); - assert(saved_type(0) == saved_textdir); - text_dir_ptr = saved_value(0); -} - -@ Sometimes it is necessary to destroy an mlist. The following -subroutine empties the current list, assuming that |abs(mode)=mmode|. - -@c -void flush_math(void) -{ - flush_node_list(vlink(head)); - flush_node_list(incompleat_noad_par); - vlink(head) = null; - tail = head; - incompleat_noad_par = null; -} - -@ Before we can do anything in math mode, we need fonts. - -@c -#define MATHFONTSTACK 8 -#define MATHFONTDEFAULT 0 /* == nullfont */ - -static sa_tree math_fam_head = NULL; - -@ @c -int fam_fnt(int fam_id, int size_id) -{ - int n = fam_id + (256 * size_id); - return (int) get_sa_item(math_fam_head, n).int_value; -} - -void def_fam_fnt(int fam_id, int size_id, int f, int lvl) -{ - int n = fam_id + (256 * size_id); - sa_tree_item sa_value = { 0 }; - sa_value.int_value = f; - set_sa_item(math_fam_head, n, sa_value, lvl); - fixup_math_parameters(fam_id, size_id, f, lvl); - if (tracing_assigns_par > 1) { - begin_diagnostic(); - tprint("{assigning"); - print_char(' '); - print_cmd_chr(def_family_cmd, size_id); - print_int(fam_id); - print_char('='); - print_font_identifier(fam_fnt(fam_id, size_id)); - print_char('}'); - end_diagnostic(false); - } -} - -@ @c -static void unsave_math_fam_data(int gl) -{ - sa_stack_item st; - if (math_fam_head->stack == NULL) - return; - while (math_fam_head->stack_ptr > 0 && - abs(math_fam_head->stack[math_fam_head->stack_ptr].level) - >= (int) gl) { - st = math_fam_head->stack[math_fam_head->stack_ptr]; - if (st.level > 0) { - rawset_sa_item(math_fam_head, st.code, st.value); - /* now do a trace message, if requested */ - if (tracing_restores_par > 1) { - int size_id = st.code / 256; - int fam_id = st.code % 256; - begin_diagnostic(); - tprint("{restoring"); - print_char(' '); - print_cmd_chr(def_family_cmd, size_id); - print_int(fam_id); - print_char('='); - print_font_identifier(fam_fnt(fam_id, size_id)); - print_char('}'); - end_diagnostic(false); - } - } - (math_fam_head->stack_ptr)--; - } -} - -@ and parameters - -@c -#define MATHPARAMSTACK 8 -#define MATHPARAMDEFAULT undefined_math_parameter - -static sa_tree math_param_head = NULL; - -@ @c -void def_math_param(int param_id, int style_id, scaled value, int lvl) -{ - int n = param_id + (256 * style_id); - sa_tree_item sa_value = { 0 }; - sa_value.int_value = (int) value; - set_sa_item(math_param_head, n, sa_value, lvl); - if (tracing_assigns_par > 1) { - begin_diagnostic(); - tprint("{assigning"); - print_char(' '); - print_cmd_chr(set_math_param_cmd, param_id); - print_cmd_chr(math_style_cmd, style_id); - print_char('='); - print_int(value); - print_char('}'); - end_diagnostic(false); - } -} - -scaled get_math_param(int param_id, int style_id) -{ - int n = param_id + (256 * style_id); - return (scaled) get_sa_item(math_param_head, n).int_value; -} - -@ @c -static void unsave_math_param_data(int gl) -{ - sa_stack_item st; - if (math_param_head->stack == NULL) - return; - while (math_param_head->stack_ptr > 0 && - abs(math_param_head->stack[math_param_head->stack_ptr].level) - >= (int) gl) { - st = math_param_head->stack[math_param_head->stack_ptr]; - if (st.level > 0) { - rawset_sa_item(math_param_head, st.code, st.value); - /* now do a trace message, if requested */ - if (tracing_restores_par > 1) { - int param_id = st.code % 256; - int style_id = st.code / 256; - begin_diagnostic(); - tprint("{restoring"); - print_char(' '); - print_cmd_chr(set_math_param_cmd, param_id); - print_cmd_chr(math_style_cmd, style_id); - print_char('='); - print_int(get_math_param(param_id, style_id)); - print_char('}'); - end_diagnostic(false); - } - } - (math_param_head->stack_ptr)--; - } -} - -@ saving and unsaving of both - -@c -void unsave_math_data(int gl) -{ - unsave_math_fam_data(gl); - unsave_math_param_data(gl); -} - -@ Dumping and undumping -@c -void dump_math_data(void) -{ - sa_tree_item sa_value = { 0 }; - if (math_fam_head == NULL) { - sa_value.int_value = MATHFONTDEFAULT; - math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); - } - dump_sa_tree(math_fam_head, "mathfonts"); - if (math_param_head == NULL) { - sa_value.int_value = MATHPARAMDEFAULT; - math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); - } - dump_sa_tree(math_param_head, "mathparameters"); -} - -void undump_math_data(void) -{ - math_fam_head = undump_sa_tree("mathfonts"); - math_param_head = undump_sa_tree("mathparameters"); -} - -@ @c -void initialize_math(void) -{ - sa_tree_item sa_value = { 0 }; - if (math_fam_head == NULL) { - sa_value.int_value = MATHFONTDEFAULT; - math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); - } - if (math_param_head == NULL) { - sa_value.int_value = MATHPARAMDEFAULT; - math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); - initialize_math_spacing(); - } - return; -} - -@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, -Clo, Pun, or Inn, for purposes of spacing and line breaking. An -|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, -|punct_noad|, or |inner_noad| is used to represent portions of the various -types. For example, an `\.=' sign in a formula leads to the creation of a -|rel_noad| whose |nucleus| field is a representation of an equals sign -(usually |fam=0|, |character=075|). A formula preceded by \.{\\mathrel} -also results in a |rel_noad|. When a |rel_noad| is followed by an -|op_noad|, say, and possibly separated by one or more ordinary nodes (not -noads), \TeX\ will insert a penalty node (with the current |rel_penalty|) -just after the formula that corresponds to the |rel_noad|, unless there -already was a penalty immediately following; and a ``thick space'' will be -inserted just before the formula that corresponds to the |op_noad|. - -A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually -has a |subtype=normal|. The only exception is that an |op_noad| might -have |subtype=limits| or |no_limits|, if the normal positioning of -limits has been overridden for this operator. - -A |radical_noad| also has a |left_delimiter| field, which usually -represents a square root sign. - -A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|. - -Delimiter fields have four subfields -called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields -represent variable-size delimiters by giving the ``small'' and ``large'' -starting characters, as explained in Chapter~17 of {\sl The \TeX book}. -@:TeXbook}{\sl The \TeX book@> - -A |fraction_noad| is actually quite different from all other noads. -It has |thickness|, |denominator|, and |numerator| fields instead of -|nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value -that tells how thick to make a fraction rule; however, the special -value |default_code| is used to stand for the -|default_rule_thickness| of the current size. The |numerator| and -|denominator| point to mlists that define a fraction; we always have -$$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The -|left_delimiter| and |right_delimiter| fields specify delimiters that will -be placed at the left and right of the fraction. In this way, a -|fraction_noad| is able to represent all of \TeX's operators \.{\\over}, -\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and - \.{\\abovewithdelims}. - -@ The |new_noad| function creates an |ord_noad| that is completely null - -@c -pointer new_noad(void) -{ - pointer p; - p = new_node(simple_noad, ord_noad_type); - /* all noad fields are zero after this */ - return p; -} - -@ @c -pointer new_sub_box(pointer curbox) -{ - pointer p, q; - p = new_noad(); - q = new_node(sub_box_node, 0); - nucleus(p) = q; - math_list(nucleus(p)) = curbox; - return p; -} - -@ A few more kinds of noads will complete the set: An |under_noad| has its -nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places -an accent over its nucleus; the accent character appears as -|math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad| -centers its nucleus vertically with respect to the axis of the formula; -in such noads we always have |type(nucleus(p))=sub_box|. - -And finally, we have the |fence_noad| type, to implement -\TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}. -The |nucleus| of such noads is -replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces -a |fence_noad| such that |delimiter(p)| holds the family and character -codes for all left parentheses. A |fence_noad| of subtype |left_noad_side| -never appears in an mlist except as the first element, and a |fence_noad| -with subtype |right_noad_side| never appears in an mlist -except as the last element; furthermore, we either have both a |left_noad_side| -and a |right_noad_side|, or neither one is present. - -@ Math formulas can also contain instructions like \.{\\textstyle} that -override \TeX's normal style rules. A |style_node| is inserted into the -data structure to record such instructions; it is three words long, so it -is considered a node instead of a noad. The |subtype| is either |display_style| -or |text_style| or |script_style| or |script_script_style|. The -second and third words of a |style_node| are not used, but they are -present because a |choice_node| is converted to a |style_node|. - -\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles -|display_style|, \dots, |script_script_style|, and adds~1 to get the -``cramped'' versions of these styles. This gives a numerical order that -is backwards from the convention of Appendix~G in {\sl The \TeX book\/}; -i.e., a smaller style has a larger numerical value. -@:TeXbook}{\sl The \TeX book@> - -@c -const char *math_style_names[] = { - "display", "crampeddisplay", - "text", "crampedtext", - "script", "crampedscript", - "scriptscript", "crampedscriptscript", - NULL -}; - -const char *math_param_names[] = { - "quad", "axis", "operatorsize", - "overbarkern", "overbarrule", "overbarvgap", - "underbarkern", "underbarrule", "underbarvgap", - "radicalkern", "radicalrule", "radicalvgap", - "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise", - "stackvgap", "stacknumup", "stackdenomdown", - "fractionrule", "fractionnumvgap", "fractionnumup", - "fractiondenomvgap", "fractiondenomdown", "fractiondelsize", - "limitabovevgap", "limitabovebgap", "limitabovekern", - "limitbelowvgap", "limitbelowbgap", "limitbelowkern", - "nolimitsubfactor", "nolimitsupfactor", /* bonus */ - "underdelimitervgap", "underdelimiterbgap", - "overdelimitervgap", "overdelimiterbgap", - "subshiftdrop", "supshiftdrop", "subshiftdown", - "subsupshiftdown", "subtopmax", "supshiftup", - "supbottommin", "supsubbottommax", "subsupvgap", - "spaceafterscript", "connectoroverlapmin", - "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing", - "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing", - "opordspacing", "opopspacing", "opbinspacing", "oprelspacing", - "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing", - "binordspacing", "binopspacing", "binbinspacing", "binrelspacing", - "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing", - "relordspacing", "relopspacing", "relbinspacing", "relrelspacing", - "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing", - "openordspacing", "openopspacing", "openbinspacing", "openrelspacing", - "openopenspacing", "openclosespacing", "openpunctspacing", - "openinnerspacing", - "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing", - "closeopenspacing", "closeclosespacing", "closepunctspacing", - "closeinnerspacing", - "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing", - "punctopenspacing", "punctclosespacing", "punctpunctspacing", - "punctinnerspacing", - "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing", - "inneropenspacing", "innerclosespacing", "innerpunctspacing", - "innerinnerspacing", - NULL -}; - -@ @c -pointer new_style(small_number s) -{ /* create a style node */ - m_style = s; - return new_node(style_node, s); -} - -@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which -has special subfields |display_mlist|, |text_mlist|, |script_mlist|, -and |script_script_mlist| pointing to the mlists for each style. - -@c -static pointer new_choice(void) -{ /* create a choice node */ - return new_node(choice_node, 0); /* the |subtype| is not used */ -} - -@ Let's consider now the previously unwritten part of |show_node_list| -that displays the things that can only be present in mlists; this -program illustrates how to access the data structures just defined. - -In the context of the following program, |p| points to a node or noad that -should be displayed, and the current string contains the ``recursion history'' -that leads to this point. The recursion history consists of a dot for each -outer level in which |p| is subsidiary to some node, or in which |p| is -subsidiary to the |nucleus| field of some noad; the dot is replaced by -`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr| -or |supscr| or |denominator| or |numerator| fields of noads. For example, -the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for -|x| in the (ridiculous) formula -`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'. - -@c -void display_normal_noad(pointer p); /* forward */ -void display_fence_noad(pointer p); /* forward */ -void display_fraction_noad(pointer p); /* forward */ - -void show_math_node(pointer p) -{ - switch (type(p)) { - case style_node: - print_cmd_chr(math_style_cmd, subtype(p)); - break; - case choice_node: - tprint_esc("mathchoice"); - append_char('D'); - show_node_list(display_mlist(p)); - flush_char(); - append_char('T'); - show_node_list(text_mlist(p)); - flush_char(); - append_char('S'); - show_node_list(script_mlist(p)); - flush_char(); - append_char('s'); - show_node_list(script_script_mlist(p)); - flush_char(); - break; - case simple_noad: - case radical_noad: - case accent_noad: - display_normal_noad(p); - break; - case fence_noad: - display_fence_noad(p); - break; - case fraction_noad: - display_fraction_noad(p); - break; - default: - tprint("Unknown node type!"); - break; - } -} - -@ Here are some simple routines used in the display of noads. - -@c -static void print_fam_and_char(pointer p) -{ /* prints family and character */ - tprint_esc("fam"); - print_int(math_fam(p)); - print_char(' '); - print(math_character(p)); -} - -@ @c -static void print_delimiter(pointer p) -{ - int a; - if (delimiteroptionset(p)) { - tprint(" [ "); - if (delimiteraxis(p)) - tprint("axis "); - if (delimiternoaxis(p)) - tprint("noaxis "); - if (delimiterexact(p)) - tprint("exact "); - tprint("]"); - } - if (delimiterheight(p)) { - tprint("height="); - print_scaled(delimiterheight(p)); - tprint(" "); - } - if (delimiterdepth(p)) { - tprint("depth="); - print_scaled(delimiterdepth(p)); - tprint(" "); - } - if (delimiterclass(p)) { - tprint("class="); - print_int(delimiterclass(p)); - tprint(" "); - } - if (small_fam(p) < 0) { - 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)); - } -} - -@ 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@> - -@c -static void print_subsidiary_data(pointer p, ASCII_code c) -{ /* display a noad field */ - if ((int) cur_length >= depth_threshold) { - if (p != null) - tprint(" []"); - } else { - append_char(c); /* include |c| in the recursion history */ - 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 */ - } -} - -@ @c -void display_normal_noad(pointer p) -{ - switch (type(p)) { - case simple_noad: - switch (subtype(p)) { - case ord_noad_type: - tprint_esc("mathord"); - break; - case op_noad_type_normal: - case op_noad_type_limits: - case op_noad_type_no_limits: - tprint_esc("mathop"); - if (subtype(p) == op_noad_type_limits) - tprint_esc("limits"); - else if (subtype(p) == op_noad_type_no_limits) - tprint_esc("nolimits"); - break; - case bin_noad_type: - tprint_esc("mathbin"); - break; - case rel_noad_type: - tprint_esc("mathrel"); - break; - case open_noad_type: - tprint_esc("mathopen"); - break; - case close_noad_type: - tprint_esc("mathclose"); - break; - case punct_noad_type: - tprint_esc("mathpunct"); - break; - case inner_noad_type: - tprint_esc("mathinner"); - break; - case over_noad_type: - tprint_esc("overline"); - break; - case under_noad_type: - tprint_esc("underline"); - break; - case vcenter_noad_type: - tprint_esc("vcenter"); - break; - default: - tprint("<unknown noad type!>"); - break; - } - break; - case radical_noad: - if (subtype(p) == 6) - tprint_esc("Udelimiterover"); - else if (subtype(p) == 5) - tprint_esc("Udelimiterunder"); - else if (subtype(p) == 4) - tprint_esc("Uoverdelimiter"); - else if (subtype(p) == 3) - tprint_esc("Uunderdelimiter"); - else if (subtype(p) == 2) - tprint_esc("Uroot"); - else - tprint_esc("radical"); - print_delimiter(left_delimiter(p)); - if (degree(p) != null) { - print_subsidiary_data(degree(p), '/'); - } - if (radicalwidth(p)) { - tprint("width="); - print_scaled(radicalwidth(p)); - tprint(" "); - } - if (radicaloptionset(p)) { - tprint(" [ "); - if (radicalexact(p)) - tprint("exact "); - if (radicalleft(p)) - tprint("left "); - if (radicalmiddle(p)) - tprint("middle "); - if (radicalright(p)) - tprint("right "); - tprint("]"); - } - break; - case accent_noad: - if (top_accent_chr(p) != null) { - if (bot_accent_chr(p) != null) { - tprint_esc("Umathaccent both"); - } else { - tprint_esc("Umathaccent"); - } - } else if (bot_accent_chr(p) != null) { - tprint_esc("Umathaccent bottom"); - } else { - tprint_esc("Umathaccent overlay"); - } - if (accentfraction(p)) { - tprint(" fraction="); - print_int(accentfraction(p)); - tprint(" "); - } - switch (subtype(p)) { - case 0: - if (top_accent_chr(p) != null) { - if (bot_accent_chr(p) != null) { - print_fam_and_char(top_accent_chr(p)); - print_fam_and_char(bot_accent_chr(p)); - } else { - print_fam_and_char(top_accent_chr(p)); - } - } else if (bot_accent_chr(p) != null) { - print_fam_and_char(bot_accent_chr(p)); - } else { - print_fam_and_char(overlay_accent_chr(p)); - } - break; - case 1: - if (top_accent_chr(p) != null) { - tprint(" fixed "); - print_fam_and_char(top_accent_chr(p)); - if (bot_accent_chr(p) != null) { - print_fam_and_char(bot_accent_chr(p)); - } - } else { - confusion("display_accent_noad"); - } - break; - case 2: - if (bot_accent_chr(p) != null) { - if (top_accent_chr(p) != null) { - print_fam_and_char(top_accent_chr(p)); - } - tprint(" fixed "); - print_fam_and_char(bot_accent_chr(p)); - } else{ - confusion("display_accent_noad"); - } - break; - case 3: - if (top_accent_chr(p) != null && bot_accent_chr(p) != null) { - tprint(" fixed "); - print_fam_and_char(top_accent_chr(p)); - tprint(" fixed "); - print_fam_and_char(bot_accent_chr(p)); - } else { - confusion("display_accent_noad"); - } - break; - } - break; - } - print_subsidiary_data(nucleus(p), '.'); - print_subsidiary_data(supscr(p), '^'); - print_subsidiary_data(subscr(p), '_'); -} - -@ @c -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)); -} - -@ @c -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), '/'); -} - -@ 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|. - -@c -static void new_save_level_math(group_code c) -{ - set_saved_record(0, saved_textdir, 0, text_dir_ptr); - text_dir_ptr = new_dir(math_direction_par); - incr(save_ptr); - new_save_level(c); - eq_word_define(int_base + body_direction_code, math_direction_par); - eq_word_define(int_base + par_direction_code, math_direction_par); - eq_word_define(int_base + text_direction_code, math_direction_par); -} - -@ @c -static void push_math(group_code c, int mstyle) -{ - if (math_direction_par != text_direction_par) - dir_math_save = true; - push_nest(); - mode = -mmode; - incompleat_noad_par = null; - m_style = mstyle; - new_save_level_math(c); -} - -@ @c -static void enter_ordinary_math(void) -{ - push_math(math_shift_group, text_style); - eq_word_define(int_base + cur_fam_code, -1); - if (every_math_par != null) - begin_token_list(every_math_par, every_math_text); -} - -@ @c -void enter_display_math(void); - -@ We get into math mode from horizontal mode when a `\.\$' (i.e., a -|math_shift| character) is scanned. We must check to see whether this -`\.\$' is immediately followed by another, in case display math mode is -called for. - -@c -void init_math(void) -{ - if (cur_cmd == math_shift_cmd) { - 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(); - } - } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) { - enter_display_math(); - } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) { - enter_ordinary_math(); - } else { - you_cant(); - } -} - -@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or -`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; -the value of |cur_chr| is placed onto |save_stack| for safe keeping. - -@ When \TeX\ is in display math mode, |cur_group=math_shift_group|, -so it is not necessary for the |start_eq_no| procedure to test for -this condition. - -@c -void start_eq_no(void) -{ - set_saved_record(0, saved_eqno, 0, cur_chr); - 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. - -@c -void math_left_brace(void) -{ - pointer q; - tail_append(new_noad()); - q = new_node(math_char_node, 0); - nucleus(tail) = q; - back_input(); - (void) scan_math(nucleus(tail), m_style); -} - -@ If the inline directions of \.{\\pardir} and \.{\\mathdir} are -opposite, then this function will return true. Discovering that fact -is somewhat odd because it needs traversal of the |save_stack|. -The occurance of displayed equations is weird enough that this is -probably still better than having yet another field in the |input_stack| -structures. - -None of this makes much sense if the inline direction of either one of -\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current -math machinery is ill suited anyway so I do not bother to test that. - -@c -static boolean math_and_text_reversed_p(void) -{ - int i = save_ptr - 1; - while (save_type(i) != level_boundary) - i--; - while (i < save_ptr) { - if (save_type(i) == restore_old_value && - save_value(i) == int_base + par_direction_code) { - if (textdir_opposite(math_direction_par, save_value(i - 1))) - return true; - } - i++; - } - return false; -} - -@ When we enter display math mode, we need to call |line_break| to -process the partial paragraph that has just been interrupted by the -display. Then we can set the proper values of |display_width| and -|display_indent| and |pre_display_size|. - -@c -void enter_display_math(void) -{ - scaled w; /* new or partial |pre_display_size| */ - scaled l; /* new |display_width| */ - scaled s; /* new |display_indent| */ - pointer p; - int n; /* scope of paragraph shape specification */ - if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ - (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */ - type(tail) == local_par_node && vlink(tail) == null)) { - if (vlink(head) == tail) { - /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if - there is another display immediately following, we have to get rid - of that node */ - flush_node(tail); - } - pop_nest(); - w = -max_dimen; - } else { - line_break(true, math_shift_group); - w = actual_box_width(just_box, (2 * quad(get_cur_font()))); - } - /* now we are in vertical mode, working on the list that will contain the display */ - /* A displayed equation is considered to be three lines long, so we - calculate the length and offset of line number |prev_graf+2|. */ - if (par_shape_par_ptr == null) { - if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) { - halfword used_hang_indent = swap_hang_indent(hang_indent_par); - l = hsize_par - abs(used_hang_indent); - if (used_hang_indent > 0) - s = used_hang_indent; - else - s = 0; - } else { - l = hsize_par; - s = 0; - } - } else { - n = vinfo(par_shape_par_ptr + 1); - if (prev_graf_par + 2 >= n) - p = par_shape_par_ptr + 2 * n + 1; - else - p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1; - s = varmem[(p - 1)].cint; - l = varmem[p].cint; - s = swap_parshape_indent(s,l); - } - - push_math(math_shift_group, display_style); - mode = mmode; - eq_word_define(int_base + cur_fam_code, -1); - eq_word_define(dimen_base + pre_display_size_code, w); - eq_word_define(dimen_base + display_width_code, l); - eq_word_define(dimen_base + display_indent_code, s); - eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0)); - if (every_display_par != null) - begin_token_list(every_display_par, every_display_text); - if (nest_ptr == 1) { - checked_page_filter(before_display); - build_page(); - } -} - -@ The next routine parses all variations of a delimiter code. The |extcode| - tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the - |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.). - (the class is passed on for conversion to \.{\\mathchar}). - -@c -static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) -{ - const char *hlp[] = { - "I'm going to use 0 instead of that illegal code value.", - NULL - }; - delcodeval d; - int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; - if (extcode == tex_mathcode) { /* \.{\\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 == umath_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 == umathnum_mathcode) { /* \.{\\Udelcodenum} */ - /* "FF<21bits> */ - /* the largest numeric value is $2^29-1$, but - the top of bit 21 can't be used as it contains invalid USV's - */ - if (doclass) { /* such a primitive doesn't exist */ - confusion("umathnum_mathcode"); - } - scan_int(); - msfam = (cur_val / 0x200000); - mschr = cur_val & 0x1FFFFF; - if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) { - tex_error("Invalid delimiter code", hlp); - msfam = 0; - mschr = 0; - } - mlfam = 0; - mlchr = 0; - } else { - /* something's gone wrong */ - confusion("unknown_extcode"); - } - d.class_value = mcls; - d.small_family_value = msfam; - d.small_character_value = mschr; - d.large_family_value = mlfam; - d.large_character_value = mlchr; - return d; -} - -@ @c -void scan_extdef_del_code(int level, int extcode) -{ - delcodeval d; - int p; - scan_char_num(); - p = cur_val; - scan_optional_equals(); - d = do_scan_extdef_del_code(extcode, false); - set_del_code(p, d.small_family_value, d.small_character_value, - d.large_family_value, d.large_character_value, - (quarterword) (level)); -} - -@ @c -mathcodeval scan_mathchar(int extcode) -{ - char errstr[255] = { 0 }; - const char *hlp[] = { - "I'm going to use 0 instead of that illegal code value.", - NULL - }; - mathcodeval d; - int mcls = 0, mfam = 0, mchr = 0; - if (extcode == tex_mathcode) { /* \.{\\mathcode} */ - /* "TFCC */ - scan_int(); - if (cur_val > 0x8000) { - /* - tex_error("Invalid math code", hlp); - cur_val = 0; - */ - /* needed for latex: fallback to umathnum_mathcode */ - mfam = (cur_val / 0x200000) & 0x7FF; - mcls = mfam % 0x08; - mfam = mfam / 0x08; - mchr = cur_val & 0x1FFFFF; - if (mchr > 0x10FFFF) { - tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp); - mcls = 0; - mfam = 0; - mchr = 0; - } - } else { - if (cur_val < 0) { - snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val); - tex_error(errstr, hlp); - cur_val = 0; - } - mcls = (cur_val / 0x1000); - mfam = ((cur_val % 0x1000) / 0x100); - mchr = (cur_val % 0x100); - } - } else if (extcode == umath_mathcode) { - /* <0-0x7> <0-0xFF> <0-0x10FFFF> */ - scan_int(); - mcls = cur_val; - scan_int(); - mfam = cur_val; - scan_char_num(); - mchr = cur_val; - if (mcls < 0 || mcls > 7 || mfam > 255) { - tex_error("Invalid math code", hlp); - mchr = 0; - mfam = 0; - mcls = 0; - } - } else if (extcode == umathnum_mathcode) { - /* "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 */ - confusion("unknown_extcode"); - } - d.class_value = mcls; - d.family_value = mfam; - d.character_value = mchr; - return d; -} - -@ @c -void scan_extdef_math_code(int level, int extcode) -{ - mathcodeval d; - int p; - scan_char_num(); - p = cur_val; - scan_optional_equals(); - d = scan_mathchar(extcode); - set_math_code(p, d.class_value, - d.family_value, d.character_value, (quarterword) (level)); -} - -@ this reads in a delcode when actually a mathcode is needed -@c -mathcodeval scan_delimiter_as_mathchar(int extcode) -{ - delcodeval dval; - mathcodeval mval; - dval = do_scan_extdef_del_code(extcode, true); - mval.class_value = dval.class_value; - mval.family_value = dval.small_family_value; - mval.character_value = dval.small_character_value; - return mval; -} - -@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad -are broken down into subfields called |type| and either |math_list| or -|(math_fam,math_character)|. The job of |scan_math| is to figure out -what to place in one of these principal fields; it looks at the -subformula that comes next in the input, and places an encoding of -that subformula into a given word of |mem|. - -@c -#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) - -int scan_math_style(pointer p, int mstyle) -{ - get_next_nb_nr(); - back_input(); - scan_left_brace(); - set_saved_record(0, saved_math, 0, p); - incr(save_ptr); - push_math(math_group, mstyle); - return 1; -} - -int scan_math(pointer p, int mstyle) -{ - /* label restart,reswitch,exit; */ - mathcodeval mval = { 0, 0, 0 }; - assert(p != null); - RESTART: - get_next_nb_nr(); - RESWITCH: - switch (cur_cmd) { - case letter_cmd: - case other_char_cmd: - case char_given_cmd: - mval = get_math_code(cur_chr); - if (mval.class_value == 8) { - /* An active character that is an |outer_call| is allowed here */ - cur_cs = active_to_cs(cur_chr, true); - cur_cmd = eq_type(cur_cs); - cur_chr = equiv(cur_cs); - x_token(); - back_input(); - goto RESTART; - } - break; - case char_num_cmd: - scan_char_num(); - cur_chr = cur_val; - cur_cmd = char_given_cmd; - goto RESWITCH; - break; - case math_char_num_cmd: - if (cur_chr == 0) - mval = scan_mathchar(tex_mathcode); - else if (cur_chr == 1) - mval = scan_mathchar(umath_mathcode); - else if (cur_chr == 2) - mval = scan_mathchar(umathnum_mathcode); - else - confusion("scan_math"); - break; - case math_given_cmd: - mval = mathchar_from_integer(cur_chr, tex_mathcode); - break; - case xmath_given_cmd: - mval = mathchar_from_integer(cur_chr, umath_mathcode); - break; - case delim_num_cmd: - if (cur_chr == 0) - mval = scan_delimiter_as_mathchar(tex_mathcode); - else if (cur_chr == 1) - mval = scan_delimiter_as_mathchar(umath_mathcode); - else - confusion("scan_math"); - break; - default: - /* The pointer |p| is placed on |save_stack| while a complex subformula - is being scanned. */ - back_input(); - scan_left_brace(); - set_saved_record(0, saved_math, 0, p); - incr(save_ptr); - push_math(math_group, mstyle); - return 1; - } - type(p) = math_char_node; - math_character(p) = mval.character_value; - if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range) - math_fam(p) = cur_fam_par; - else - math_fam(p) = mval.family_value; - return 0; -} - -@ The |set_math_char| procedure creates a new noad appropriate to a given -math code, and appends it to the current mlist. However, if the math code -is sufficiently large, the |cur_chr| is treated as an active character and -nothing is appended. - -@c -void set_math_char(mathcodeval mval) -{ - pointer p; /* the new noad */ - 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 == math_use_current_family_code) { - if (cur_fam_par_in_range) - math_fam(nucleus(p)) = cur_fam_par; - subtype(p) = ord_noad_type; - } else { - switch (mval.class_value) { - /* *INDENT-OFF* */ - case 0: subtype(p) = ord_noad_type; break; - case 1: subtype(p) = op_noad_type_normal; break; - case 2: subtype(p) = bin_noad_type; break; - case 3: subtype(p) = rel_noad_type; break; - case 4: subtype(p) = open_noad_type; break; - case 5: subtype(p) = close_noad_type; break; - case 6: subtype(p) = punct_noad_type; break; - /* *INDENT-ON* */ - } - } - 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. - -@c -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; - } -} - -@ @c -void math_math_comp(void) -{ - pointer q; - tail_append(new_noad()); - subtype(tail) = (quarterword) cur_chr; - q = new_node(math_char_node, 0); - nucleus(tail) = q; - if (cur_chr == over_noad_type) - (void) scan_math(nucleus(tail), cramped_style(m_style)); - else - (void) scan_math(nucleus(tail), m_style); -} - -@ @c -void math_limit_switch(void) -{ - const char *hlp[] = { - "I'm ignoring this misplaced \\limits or \\nolimits command.", - NULL - }; - if (head != tail) { - if (type(tail) == simple_noad && - (subtype(tail) == op_noad_type_normal || - subtype(tail) == op_noad_type_limits || - subtype(tail) == op_noad_type_no_limits)) { - subtype(tail) = (quarterword) cur_chr; - return; - } - } - tex_error("Limit controls must follow a math operator", hlp); -} - -@ Delimiter fields of noads are filled in by the |scan_delimiter| routine. -The first parameter of this procedure is the |mem| address where the -delimiter is to be placed; the second tells if this delimiter follows -\.{\\radical} or not. - -@c -static void scan_delimiter(pointer p, int r) -{ - delcodeval dval = { 0, 0, 0, 0, 0 }; - if (r == tex_mathcode) { /* \.{\\radical} */ - dval = do_scan_extdef_del_code(tex_mathcode, true); - } else if (r == umath_mathcode) { /* \.{\\Uradical} */ - dval = do_scan_extdef_del_code(umath_mathcode, false); - } else if (r == no_mathcode) { - get_next_nb_nr(); - switch (cur_cmd) { - case letter_cmd: - case other_char_cmd: - dval = get_del_code(cur_chr); - break; - case delim_num_cmd: - if (cur_chr == 0) /* \.{\\delimiter} */ - dval = do_scan_extdef_del_code(tex_mathcode, true); - else if (cur_chr == 1) /* \.{\\Udelimiter} */ - dval = do_scan_extdef_del_code(umath_mathcode, true); - else - confusion("scan_delimiter1"); - break; - default: - dval.small_family_value = -1; - break; - } - } else { - confusion("scan_delimiter2"); - } - if (p == null) - return; - if (dval.small_family_value < 0) { - const char *hlp[] = { - "I was expecting to see something like `(' or `\\{' or", - "`\\}' here. If you typed, e.g., `{' instead of `\\{', you", - "should probably delete the `{' by typing `1' now, so that", - "braces don't get unbalanced. Otherwise just proceed", - "Acceptable delimiters are characters whose \\delcode is", - "nonnegative, or you can use `\\delimiter <delimiter code>'.", - NULL - }; - back_error("Missing delimiter (. inserted)", hlp); - small_fam(p) = 0; - small_char(p) = 0; - large_fam(p) = 0; - large_char(p) = 0; - } else { - small_fam(p) = dval.small_family_value; - small_char(p) = dval.small_character_value; - large_fam(p) = dval.large_family_value; - large_char(p) = dval.large_character_value; - } - return; -} - -@ @c -void math_radical(void) -{ - halfword q; - int chr_code = cur_chr; - halfword options = 0; - tail_append(new_node(radical_noad, chr_code)); - q = new_node(delim_node, 0); - left_delimiter(tail) = q; - while (1) { - if (scan_keyword("width")) { - scan_dimen(false,false,false); - radicalwidth(tail) = cur_val ; - } else if (scan_keyword("left")) { - options = options | noad_option_left ; - } else if (scan_keyword("middle")) { - options = options | noad_option_middle ; - } else if (scan_keyword("right")) { - options = options | noad_option_right ; - } else { - break; - } - } - radicaloptions(tail) = options; - if (chr_code == 0) /* \.{\\radical} */ - scan_delimiter(left_delimiter(tail), tex_mathcode); - else if (chr_code == 1) /* \.{\\Uradical} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 2) /* \.{\\Uroot} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 3) /* \.{\\Uunderdelimiter} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 4) /* \.{\\Uoverdelimiter} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 5) /* \.{\\Udelimiterunder} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 6) /* \.{\\Udelimiterover} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 7) /* \.{\\Uhextensible} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else - confusion("math_radical"); - if (chr_code == 7) { - q = new_node(sub_box_node, 0); /* type will change */ - nucleus(tail) = q; - return; - } else if (chr_code == 2) { - /* the trick with the |vlink(q)| is used by |scan_math| - to decide whether it needs to go on */ - q = new_node(math_char_node, 0); - vlink(q) = tail; - degree(tail) = q; - if (!scan_math(degree(tail), sup_sup_style(m_style))) { - vlink(degree(tail)) = null; - q = new_node(math_char_node, 0); - nucleus(tail) = q; - (void) scan_math(nucleus(tail), cramped_style(m_style)); - } - } else { - q = new_node(math_char_node, 0); - nucleus(tail) = q; - (void) scan_math(nucleus(tail), cramped_style(m_style)); - } -} - -@ @c -void math_ac(void) -{ - halfword q; - mathcodeval t = { 0, 0, 0 }; - mathcodeval b = { 0, 0, 0 }; - mathcodeval o = { 0, 0, 0 }; - if (cur_cmd == accent_cmd) { - const char *hlp[] = { - "I'm changing \\accent to \\mathaccent here; wish me luck.", - "(Accents are not the same in formulas as they are in text.)", - NULL - }; - tex_error("Please use \\mathaccent for accents in math mode", hlp); - } - tail_append(new_node(accent_noad, 0)); - if (cur_chr == 0) { /* \.{\\mathaccent} */ - t = scan_mathchar(tex_mathcode); - } else if (cur_chr == 1) { /* \.{\\Umathaccent} */ - if (scan_keyword("fixed")) { - /* top */ - subtype(tail) = 1; - t = scan_mathchar(umath_mathcode); - } else if (scan_keyword("both")) { - /* top bottom */ - if (scan_keyword("fixed")) { - subtype(tail) = 1; - } - t = scan_mathchar(umath_mathcode); - if (scan_keyword("fixed")) { - subtype(tail) += 2; - } - b = scan_mathchar(umath_mathcode); - } else if (scan_keyword("bottom")) { - /* bottom */ - if (scan_keyword("fixed")) { - subtype(tail) = 2; - } - b = scan_mathchar(umath_mathcode); - } else if (scan_keyword("top")) { - /* top */ - if (scan_keyword("fixed")) { - subtype(tail) = 1; - } - t = scan_mathchar(umath_mathcode); - } else if (scan_keyword("overlay")) { - /* overlay */ - if (scan_keyword("fixed")) { - subtype(tail) = 1; - } - o = scan_mathchar(umath_mathcode); - } else { - /* top */ - t = scan_mathchar(umath_mathcode); - } - if (scan_keyword("fraction")) { - scan_int(); - accentfraction(tail) = cur_val; - } - } else { - confusion("mathaccent"); - } - if (!(t.character_value == 0 && t.family_value == 0)) { - q = new_node(math_char_node, 0); - top_accent_chr(tail) = q; - math_character(top_accent_chr(tail)) = t.character_value; - if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range) - math_fam(top_accent_chr(tail)) = cur_fam_par; - else - math_fam(top_accent_chr(tail)) = t.family_value; - } - if (!(b.character_value == 0 && b.family_value == 0)) { - q = new_node(math_char_node, 0); - bot_accent_chr(tail) = q; - math_character(bot_accent_chr(tail)) = b.character_value; - if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range) - math_fam(bot_accent_chr(tail)) = cur_fam_par; - else - math_fam(bot_accent_chr(tail)) = b.family_value; - } - if (!(o.character_value == 0 && o.family_value == 0)) { - q = new_node(math_char_node, 0); - overlay_accent_chr(tail) = q; - math_character(overlay_accent_chr(tail)) = o.character_value; - if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range) - math_fam(overlay_accent_chr(tail)) = cur_fam_par; - else - math_fam(overlay_accent_chr(tail)) = o.family_value; - } - q = new_node(math_char_node, 0); - nucleus(tail) = q; - (void) scan_math(nucleus(tail), cramped_style(m_style)); -} - -@ @c -pointer math_vcenter_group(pointer p) -{ - pointer q, r; - q = new_noad(); - subtype(q) = vcenter_noad_type; - r = new_node(sub_box_node, 0); - nucleus(q) = r; - math_list(nucleus(q)) = p; - return q; -} - -@ The routine that scans the four mlists of a \.{\\mathchoice} is very -much like the routine that builds discretionary nodes. - -@c -void append_choices(void) -{ - tail_append(new_choice()); - incr(save_ptr); - set_saved_record(-1, saved_choices, 0, 0); - push_math(math_choice_group, display_style); - scan_left_brace(); -} - -@ @c -void build_choices(void) -{ - pointer p; /* the current mlist */ - int prev_style; - prev_style = m_style; - unsave_math(); - p = fin_mlist(null); - assert(saved_type(-1) == saved_choices); - switch (saved_value(-1)) { - case 0: - display_mlist(tail) = p; - break; - case 1: - text_mlist(tail) = p; - break; - case 2: - script_mlist(tail) = p; - break; - case 3: - script_script_mlist(tail) = p; - decr(save_ptr); - return; - break; - } /* there are no other cases */ - set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1)); - push_math(math_choice_group, (prev_style + 2)); - scan_left_brace(); -} - -@ Subscripts and superscripts are attached to the previous nucleus by the -action procedure called |sub_sup|. - -@c -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 || cur_chr == sup_mark_cmd) { /* |super_sub_script| */ - if (supscr(tail) != null) { - const char *hlp[] = { - "I treat `x^1^2' essentially like `x^1{}^2'.", NULL - }; - 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), sup_style(m_style)); - } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) { - if (subscr(tail) != null) { - const char *hlp[] = { - "I treat `x_1_2' essentially like `x_1{}_2'.", NULL - }; - tail_append(new_noad()); - q = new_node(sub_mlist_node, 0); - nucleus(tail) = q; - tex_error("Double subscript", hlp); - } - q = new_node(math_char_node, 0); - subscr(tail) = q; - (void) scan_math(subscr(tail), sub_style(m_style)); - } -} - -@ An operation like `\.{\\over}' causes the current mlist to go into a -state of suspended animation: |incompleat_noad| points to a |fraction_noad| -that contains the mlist-so-far as its numerator, while the denominator -is yet to come. Finally when the mlist is finished, the denominator will -go into the incompleat fraction noad, and that noad will become the -whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' -delimiters. - -@c -void math_fraction(void) -{ - halfword c; /* the type of generalized fraction we are scanning */ - pointer q; - halfword options = 0; - halfword temp_value; - c = cur_chr; - if (incompleat_noad_par != null) { - const char *hlp[] = { - "I'm ignoring this fraction specification, since I don't", - "know whether a construction like `x \\over y \\over z'", - "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.", - NULL - }; - if (c >= delimited_code) { - scan_delimiter(null, no_mathcode); - scan_delimiter(null, no_mathcode); - } - if ((c % delimited_code) == above_code) - scan_normal_dimen(); - tex_error("Ambiguous; you need another { and }", hlp); - } else { - incompleat_noad_par = new_node(fraction_noad, 0); - temp_value = new_node(sub_mlist_node, 0); - numerator(incompleat_noad_par) = temp_value; - math_list(numerator(incompleat_noad_par)) = vlink(head); - vlink(head) = null; - tail = head; - m_style = cramped_style(m_style); - - if ((c % delimited_code) == skewed_code) { - q = new_node(delim_node, 0); - middle_delimiter(incompleat_noad_par) = q; - scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode); - } - if (c >= delimited_code) { - q = new_node(delim_node, 0); - left_delimiter(incompleat_noad_par) = q; - q = new_node(delim_node, 0); - right_delimiter(incompleat_noad_par) = q; - scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode); - scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode); - } - switch (c % delimited_code) { - case above_code: - while (1) { - if (scan_keyword("exact")) { - options = options | noad_option_exact ; - } else { - break; - } - } - fractionoptions(incompleat_noad_par) = options; - scan_normal_dimen(); - thickness(incompleat_noad_par) = cur_val; - break; - case over_code: - thickness(incompleat_noad_par) = default_code; - break; - case atop_code: - thickness(incompleat_noad_par) = 0; - break; - case skewed_code: - while (1) { - if (scan_keyword("exact")) { - options = options | noad_option_exact ; - } else if (scan_keyword("noaxis")) { - options = options | noad_option_no_axis ; - } else { - break; - } - } - fractionoptions(incompleat_noad_par) = options; - thickness(incompleat_noad_par) = 0; - break; - } - } -} - -@ At the end of a math formula or subformula, the |fin_mlist| routine is -called upon to return a pointer to the newly completed mlist, and to -pop the nest back to the enclosing semantic level. The parameter to -|fin_mlist|, if not null, points to a |fence_noad| that ends the -current mlist; this |fence_noad| has not yet been appended. - -@c -pointer fin_mlist(pointer p) -{ - pointer q; /* the mlist to return */ - if (incompleat_noad_par != null) { - if (denominator(incompleat_noad_par) != null) { - type(denominator(incompleat_noad_par)) = sub_mlist_node; - } else { - q = new_node(sub_mlist_node, 0); - denominator(incompleat_noad_par) = q; - } - math_list(denominator(incompleat_noad_par)) = vlink(head); - if (p == null) { - q = incompleat_noad_par; - } else { - q = math_list(numerator(incompleat_noad_par)); - if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) - || (delim_par == null)) - confusion("right"); /* this can't happen */ - math_list(numerator(incompleat_noad_par)) = vlink(delim_par); - vlink(delim_par) = incompleat_noad_par; - vlink(incompleat_noad_par) = p; - } - } else { - vlink(tail) = p; - q = vlink(head); - } - pop_nest(); - return q; -} - -@ Now at last we're ready to see what happens when a right brace occurs -in a math formula. Two special cases are simplified here: Braces are effectively -removed when they surround a single Ord without sub/superscripts, or when they -surround an accent that is the nucleus of an Ord atom. - -@c -void close_math_group(pointer p) -{ - int old_style = m_style; - unsave_math(); - - decr(save_ptr); - assert(saved_type(0) == saved_math); - type(saved_value(0)) = sub_mlist_node; - p = fin_mlist(null); - math_list(saved_value(0)) = p; - if (p != null) { - if (vlink(p) == null) { - if (type(p) == simple_noad && subtype(p) == ord_noad_type) { - if (subscr(p) == null && supscr(p) == null) { - type(saved_value(0)) = type(nucleus(p)); - if (type(nucleus(p)) == math_char_node) { - math_fam(saved_value(0)) = math_fam(nucleus(p)); - math_character(saved_value(0)) = - math_character(nucleus(p)); - } else { - math_list(saved_value(0)) = math_list(nucleus(p)); - math_list(nucleus(p)) = null; - } - delete_attribute_ref(node_attr(saved_value(0))); - node_attr(saved_value(0)) = node_attr(nucleus(p)); - node_attr(nucleus(p)) = null; - flush_node(p); - } - } else if (type(p) == accent_noad) { - if (saved_value(0) == nucleus(tail)) { - if (type(tail) == simple_noad - && subtype(tail) == ord_noad_type) { - pointer q = head; - while (vlink(q) != tail) - q = vlink(q); - vlink(q) = p; - nucleus(tail) = null; - subscr(tail) = null; - supscr(tail) = null; - delete_attribute_ref(node_attr(p)); - node_attr(p) = node_attr(tail); - node_attr(tail) = null; - flush_node(tail); - tail = p; - } - } - } - } - } - if (vlink(saved_value(0)) > 0) { - pointer q; - q = new_node(math_char_node, 0); - nucleus(vlink(saved_value(0))) = q; - vlink(saved_value(0)) = null; - saved_value(0) = q; - (void) scan_math(saved_value(0), old_style); - /* 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}. - -@c -void math_left_right(void) -{ - halfword t; /* |left_noad_side| .. |right_noad_side| */ - pointer p; /* new noad */ - pointer q; /* resulting mlist */ - pointer r; /* temporary */ - halfword ht = 0; - halfword dp = 0; - halfword options = 0; - halfword type = -1 ; - t = cur_chr; - - if (t > 10) { - /* we have \Uleft \Uright \Umiddle */ - t = t - 10; - while (1) { - if (scan_keyword("height")) { - scan_dimen(false,false,false); - ht = cur_val ; - } else if (scan_keyword("depth")) { - scan_dimen(false,false,false); - dp = cur_val ; - } else if (scan_keyword("axis")) { - options = options | noad_option_axis ; - } else if (scan_keyword("noaxis")) { - options = options | noad_option_no_axis ; - } else if (scan_keyword("exact")) { - options = options | noad_option_exact ; - } else if (scan_keyword("class")) { - scan_int(); - type = cur_val ; - } else { - break; - } - } - } - - if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) { - if (cur_group == math_shift_group) { - scan_delimiter(null, no_mathcode); - if (t == middle_noad_side) { - const char *hlp[] = { - "I'm ignoring a \\middle that had no matching \\left.", - NULL - }; - tex_error("Extra \\middle", hlp); - } else { - const char *hlp[] = { - "I'm ignoring a \\right that had no matching \\left.", - NULL - }; - tex_error("Extra \\right", hlp); - } - } else { - off_save(); - } - } else { - p = new_noad(); - type(p) = fence_noad; - subtype(p) = (quarterword) t; - r = new_node(delim_node, 0); - delimiter(p) = r; - - delimiterheight(p) = ht; - delimiterdepth(p) = dp; - delimiteroptions(p) = options; - delimiterclass(p) = type; - delimiteritalic(p) = 0; - - scan_delimiter(delimiter(p), no_mathcode); - - if (t == no_noad_side) { - tail_append(new_noad()); - subtype(tail) = inner_noad_type; - r = new_node(sub_mlist_node, 0); - nucleus(tail) = r; - math_list(nucleus(tail)) = p; - return ; - } - - if (t == left_noad_side) { - q = p; - } else { - q = fin_mlist(p); - unsave_math(); - } - if (t != right_noad_side) { - push_math(math_left_group, m_style); - vlink(head) = q; - tail = p; - delim_par = p; - } else { - tail_append(new_noad()); - subtype(tail) = inner_noad_type; - r = new_node(sub_mlist_node, 0); - nucleus(tail) = r; - math_list(nucleus(tail)) = q; - } - } -} - -@ \TeX\ gets to the following part of the program when -the first `\.\$' ending a display has been scanned. - -@c -static void check_second_math_shift(void) -{ - get_x_token(); - if (cur_cmd != math_shift_cmd) { - const char *hlp[] = { - "The `$' that I just saw supposedly matches a previous `$$'.", - "So I shall assume that you typed `$$' both times.", - NULL - }; - back_error("Display math should end with $$", hlp); - } -} - -static void check_display_math_end(void) -{ - if (cur_chr != cramped_display_style) { - const char *hlp[] = { - "I shall assume that you typed that.", - NULL - }; - tex_error("Display math should end with \\Ustopdisplaymath", hlp); - } -} - -static void check_inline_math_end(void) -{ - if (cur_chr != cramped_text_style) { - const char *hlp[] = { - "I shall assume that you typed that.", - NULL - }; - tex_error("Inline math should end with \\Ustopmath", hlp); - } -} - -@ @c -static void resume_after_display(void) -{ - if (cur_group != math_shift_group) - confusion("display"); - unsave_math(); - prev_graf_par = prev_graf_par + 3; - push_nest(); - mode = hmode; - space_factor_par = 1000; - /* this needs to be intercepted in the display math start ! */ - tail_append(make_local_par_node(penalty_par_code)); - get_x_token(); - if (cur_cmd != spacer_cmd) - back_input(); - if (nest_ptr == 1) { - normal_page_filter(after_display); - build_page(); - } -} - -@ The fussiest part of math mode processing occurs when a displayed formula is -being centered and placed with an optional equation number. - -At this time we are in vertical mode (or internal vertical mode). - - |p| points to the mlist for the formula. - |a| is either |null| or it points to a box containing the equation number. - |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box). - -@c -#define inject_display_skip_before(g) \ - switch (display_skip_mode_par) { \ - case 0 : /* normal tex */ \ - tail_append(new_param_glue(g)); \ - break;\ - case 1 : /* always */ \ - tail_append(new_param_glue(g)); \ - break; \ - case 2 : /* non-zero */ \ - if (g != 0 && ! glue_is_zero(glue_par(g))) \ - tail_append(new_param_glue(g)); \ - break; \ - case 3: /* ignore */ \ - break; \ - } - -#define inject_display_skip_after(g) \ - switch (display_skip_mode_par) { \ - case 0 : /* normal tex */ \ - if (g != 0 && glue_is_positive(glue_par(g))) \ - tail_append(new_param_glue(g)); \ - break; \ - case 1 : /* always */ \ - tail_append(new_param_glue(g)); \ - break; \ - case 2 : /* non-zero */ \ - if (g != 0 && ! glue_is_zero(glue_par(g))) \ - tail_append(new_param_glue(g)); \ - break; \ - case 3: /* ignore */ \ - break; \ - } - -static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) -{ - pointer eq_box; /* box containing the equation */ - scaled eq_w; /* width of the equation */ - scaled line_w; /* width of the line */ - scaled eqno_w; /* width of equation number */ - scaled eqno_w2; /* width of equation number plus space to separate from equation */ - scaled line_s; /* move the line right this much */ - scaled d; /* displacement of equation in the line */ - small_number g1, g2; /* glue parameter codes for before and after */ - pointer r,s; /* kern nodes used to position the display */ - pointer t; /* tail of adjustment list */ - pointer pre_t; /* tail of pre-adjustment list */ - boolean swap_dir; /* true if the math and surrounding text dirs are opposed */ - scaled eqno_width; - swap_dir = (pre_display_direction_par < 0 ? true : false ); - if (eqno_box != null && swap_dir) - l = !l; - adjust_tail = adjust_head; - pre_adjust_tail = pre_adjust_head; - eq_box = hpack(p, 0, additional, -1); - subtype(eq_box) = equation_list; /* new */ - build_attribute_list(eq_box); - p = list_ptr(eq_box); - t = adjust_tail; - adjust_tail = null; - pre_t = pre_adjust_tail; - pre_adjust_tail = null; - eq_w = width(eq_box); - line_w = display_width_par; - line_s = display_indent_par; - if (eqno_box == null) { - eqno_w = 0; - eqno_width = 0; - eqno_w2 = 0; - } else { - eqno_w = width(eqno_box); - eqno_width = eqno_w; - eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000); - subtype(eqno_box) = equation_number_list; /* new */ - /* build_attribute_list(eqno_box); */ /* probably already set */ - } - if (eq_w + eqno_w2 > line_w) { - /* The user can force the equation number to go on a separate line - by causing its width to be zero. */ - if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w) - || (total_shrink[sfi] != 0) - || (total_shrink[fil] != 0) - || (total_shrink[fill] != 0) - || (total_shrink[filll] != 0))) { - list_ptr(eq_box) = null; - flush_node(eq_box); - eq_box = hpack(p, line_w - eqno_w2, exactly, -1); - subtype(eq_box) = equation_list; /* new */ - build_attribute_list(eq_box); - } else { - eqno_w = 0; - if (eq_w > line_w) { - list_ptr(eq_box) = null; - flush_node(eq_box); - eq_box = hpack(p, line_w, exactly, -1); - subtype(eq_box) = equation_list; /* new */ - build_attribute_list(eq_box); - } - } - eq_w = width(eq_box); - } - /* We try first to center the display without regard to the existence of - the equation number. If that would make it too close (where ``too close'' - means that the space between display and equation number is less than the - width of the equation number), we either center it in the remaining space - or move it as far from the equation number as possible. The latter alternative - is taken only if the display begins with glue, since we assume that the - user put glue there to control the spacing precisely. - */ - d = half(line_w - eq_w); - if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */ - d = half(line_w - eq_w - eqno_w); - if (p != null) - if (!is_char_node(p)) - if (type(p) == glue_node) - d = 0; - } - - tail_append(new_penalty(pre_display_penalty_par)); - if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */ - g1 = above_display_skip_code; - g2 = below_display_skip_code; - } else { - g1 = above_display_short_skip_code; - g2 = below_display_short_skip_code; - } - - /* If the equation number is set on a line by itself, either before or - after the formula, we append an infinite penalty so that no page break will - separate the display from its number; and we use the same size and - displacement for all three potential lines of the display, even though - `\.{\\parshape}' may specify them differently. - */ - /* \.{\\leqno} on a forced single line due to |width=0| */ - /* it follows that |type(a)=hlist_node| */ - - if (eqno_box && l && (eqno_w == 0)) { - /* if (math_direction_par==dir_TLT) { */ - shift_amount(eqno_box) = 0; - /* } else { */ - /* } */ - append_to_vlist(eqno_box,lua_key_index(equation_number)); - tail_append(new_penalty(inf_penalty)); - } else { - inject_display_skip_before(g1); - } - - if (eqno_w != 0) { - r = new_kern(line_w - eq_w - eqno_w - d); - if (l) { - if (swap_dir) { - if (math_direction_par==dir_TLT) { - /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 1\n"); -#endif - s = new_kern(width(r) + eqno_w); - try_couple_nodes(eqno_box,r); - try_couple_nodes(r,eq_box); - try_couple_nodes(eq_box,s); - } else { - /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 2\n"); -#endif - try_couple_nodes(eqno_box,r); - try_couple_nodes(r,eq_box); - } - } else { - if (math_direction_par==dir_TLT) { - /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 3\n"); -#endif - s = new_kern(width(r) + eqno_w); - } else { - /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 4\n"); -#endif - s = new_kern(width(r)); - } - try_couple_nodes(eqno_box,r); - try_couple_nodes(r,eq_box); - try_couple_nodes(eq_box,s); - } - eq_box = eqno_box; - } else { - if (swap_dir) { - if (math_direction_par==dir_TLT) { - /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 5\n"); -#endif - } else { - /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 6\n"); -#endif - } - try_couple_nodes(eq_box,r); - try_couple_nodes(r,eqno_box); - } else { - if (math_direction_par==dir_TLT) { - /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 7\n"); -#endif - s = new_kern(d); - } else { - /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 8\n"); -#endif - s = new_kern(width(r) + eqno_w); - } - try_couple_nodes(s,eq_box); - try_couple_nodes(eq_box,r); - try_couple_nodes(r,eqno_box); - eq_box = s; - } - } - eq_box = hpack(eq_box, 0, additional, -1); - subtype(eq_box) = equation_list; /* new */ - build_attribute_list(eq_box); - shift_amount(eq_box) = line_s; - } else { - shift_amount(eq_box) = line_s + d; - } -/* check for prev: */ - append_to_vlist(eq_box,lua_key_index(equation)); - - if ((eqno_box != null) && (eqno_w == 0) && !l) { - tail_append(new_penalty(inf_penalty)); - /* if (math_direction_par==dir_TLT) { */ - shift_amount(eqno_box) = line_s + line_w - eqno_width ; - /* } else { */ - /* } */ - append_to_vlist(eqno_box,lua_key_index(equation_number)); - g2 = 0; - } - if (t != adjust_head) { /* migrating material comes after equation number */ - vlink(tail) = vlink(adjust_head); - /* needs testing */ - alink(adjust_tail) = alink(tail); - tail = t; - } - if (pre_t != pre_adjust_head) { - vlink(tail) = vlink(pre_adjust_head); - /* needs testing */ - alink(pre_adjust_tail) = alink(tail); - tail = pre_t; - } - tail_append(new_penalty(post_display_penalty_par)); - inject_display_skip_after(g2); - resume_after_display(); -} - -@ @c -void after_math(void) -{ - int m; /* |mmode| or |-mmode| */ - pointer p; /* the formula */ - pointer a = null; /* box containing equation number */ - boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ - m = mode; - p = fin_mlist(null); /* this pops the nest */ - if (cur_cmd == math_shift_cs_cmd && - (cur_chr == text_style || cur_chr == display_style)) { - you_cant(); - } - if (mode == -m) { /* end of equation number */ - if (cur_cmd == math_shift_cmd) { - check_second_math_shift(); - } else { - check_display_math_end(); - } - run_mlist_to_hlist(p, false, text_style); - a = hpack(vlink(temp_head), 0, additional, -1); - build_attribute_list(a); - unsave_math(); - decr(save_ptr); /* now |cur_group=math_shift_group| */ - assert(saved_type(0) == saved_eqno); - if (saved_value(0) == 1) - l = true; - m = mode; - p = fin_mlist(null); - - } - if (m < 0) { - /* The |unsave| is done after everything else here; hence an appearance of - `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these - particular \.\$'s. This is consistent with the conventions of - `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the - space above that display. - */ - if (cur_cmd == math_shift_cs_cmd) { - check_inline_math_end(); - } - tail_append(new_math(math_surround_par, before)); - /* begin mathskip code */ - switch (math_skip_mode) { - case 0 : - /* obey mathsurround when zero glue */ - if (! glue_is_zero(math_skip_par)) { - copy_glue_values(tail,math_skip_par); - surround(tail) = 0; - } - break ; - case 1 : - /* always left */ - case 3 : - /* always both */ - case 6 : - /* only when skip */ - copy_glue_values(tail,math_skip_par); - surround(tail) = 0; - break ; - case 2 : - /* only right */ - surround(tail) = 0; - break ; - case 4 : - /* ignore, obey marthsurround */ - break ; - case 5: - /* all spacing disabled */ - surround(tail) = 0; - break ; - } - /* end mathskip code */ - if (dir_math_save) { - tail_append(new_dir(math_direction_par)); - } - run_mlist_to_hlist(p, (mode > 0), text_style); - vlink(tail) = vlink(temp_head); - while (vlink(tail) != null) { - tail = vlink(tail); - } - if (dir_math_save) { - tail_append(new_dir(math_direction_par - dir_swap)); - } - dir_math_save = false; - tail_append(new_math(math_surround_par, after)); - /* begin mathskip code */ - switch (math_skip_mode) { - case 0 : - /* obey mathsurround when zero glue */ - if (! glue_is_zero(math_skip_par)) { - copy_glue_values(tail,math_skip_par); - surround(tail) = 0; - } - break ; - case 2 : - /* always right */ - case 3 : - /* always both */ - case 6 : - /* only when skip */ - copy_glue_values(tail,math_skip_par); - surround(tail) = 0; - break ; - case 1 : - /* only left */ - surround(tail) = 0; - break ; - case 4 : - /* ignore, obey marthsurround */ - break ; - case 5: - /* all spacing disabled */ - surround(tail) = 0; - break ; - } - /* end mathskip code */ - space_factor_par = 1000; - unsave_math(); - } else { - if (a == null) { - if (cur_cmd == math_shift_cmd) { - check_second_math_shift(); - } else { - check_display_math_end(); - } - } - run_mlist_to_hlist(p, false, display_style); - finish_displayed_math(l, a, vlink(temp_head)); - } -} - -@ When \.{\\halign} appears in a display, the alignment routines operate -essentially as they do in vertical mode. Then the following program is -activated, with |p| and |q| pointing to the beginning and end of the -resulting list, and with |aux_save| holding the |prev_depth| value. - -@c -void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) -{ - do_assignments(); - if (cur_cmd == math_shift_cmd) { - check_second_math_shift(); - } else { - check_display_math_end(); - } - pop_nest(); - tail_append(new_penalty(pre_display_penalty_par)); - inject_display_skip_before(above_display_skip_code); - vlink(tail) = p; - if (p != null) - tail = q; - tail_append(new_penalty(post_display_penalty_par)); - inject_display_skip_after(below_display_skip_code); - cur_list.prev_depth_field = saved_prevdepth; - resume_after_display(); -} - -@ Interface to \.{\\Umath} and \.{\\mathstyle} - -@c -void setup_math_style(void) -{ - pointer q; - tail_append(new_noad()); - q = new_node(math_char_node, 0); - nucleus(tail) = q; - (void) scan_math_style(nucleus(tail), num_style(m_style)); -} - -@ @c -void print_math_style(void) -{ - if (abs(mode) == mmode) - print_int(m_style); - else - print_int(-1); -} +% texmath.w
+%
+% Copyright 2008-2010 Taco Hoekwater <taco@@luatex.org>
+%
+% This file is part of LuaTeX.
+%
+% LuaTeX is free software; you can redistribute it and/or modify it under
+% the terms of the GNU General Public License as published by the Free
+% Software Foundation; either version 2 of the License, or (at your
+% option) any later version.
+%
+% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+% License for more details.
+%
+% You should have received a copy of the GNU General Public License along
+% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+@ @c
+#include "ptexlib.h"
+
+@ @c
+#define mode mode_par
+#define tail tail_par
+#define head head_par
+#define dir_save dirs_par
+
+/*
+
+ \mathdisplayskipmode
+
+ tex normally always inserts before and only after when larger than zero
+
+ 0 = normal tex
+ 1 = always
+ 2 = non-zero
+ 3 = ignore
+
+*/
+
+@ TODO: not sure if this is the right order
+@c
+#define back_error(A,B) do { \
+ OK_to_interrupt=false; \
+ back_input(); \
+ OK_to_interrupt=true; \
+ tex_error(A,B); \
+ } while (0)
+
+@ @c
+int scan_math(pointer, int);
+int scan_math_style(pointer, int);
+pointer fin_mlist(pointer);
+
+@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an
+{\sl mlist}, which is essentially a tree structure representing that
+formula. An mlist is a linear sequence of items, but we can regard it as
+a tree structure because mlists can appear within mlists. For example, many
+of the entries can be subscripted or superscripted, and such ``scripts''
+are mlists in their own right.
+
+An entire formula is parsed into such a tree before any of the actual
+typesetting is done, because the current style of type is usually not
+known until the formula has been fully scanned. For example, when the
+formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell
+that `\.{a+b}' will be in script size until `\.{\\over}' has appeared.
+
+During the scanning process, each element of the mlist being built is
+classified as a relation, a binary operator, an open parenthesis, etc.,
+or as a construct like `\.{\\sqrt}' that must be built up. This classification
+appears in the mlist data structure.
+
+After a formula has been fully scanned, the mlist is converted to an hlist
+so that it can be incorporated into the surrounding text. This conversion is
+controlled by a recursive procedure that decides all of the appropriate
+styles by a ``top-down'' process starting at the outermost level and working
+in towards the subformulas. The formula is ultimately pasted together using
+combinations of horizontal and vertical boxes, with glue and penalty nodes
+inserted as necessary.
+
+An mlist is represented internally as a linked list consisting chiefly
+of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat
+similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are
+allowed to appear in mlists together with the noads; \TeX\ tells the difference
+by means of the |type| field, since a noad's |type| is always greater than
+that of a node. An mlist does not contain character nodes, hlist nodes, vlist
+nodes, math nodes or unset nodes; in particular, each mlist item appears in the
+variable-size part of |mem|, so the |type| field is always present.
+
+Each noad is five or more words long. The first word contains the
+|type| and |subtype| and |link| fields that are already so familiar to
+us; the second contains the attribute list pointer, and the third,
+fourth an fifth words are called the noad's |nucleus|, |subscr|, and
+|supscr| fields. (This use of a combined attribute list is temporary.
+Eventually, each of fields need their own list)
+
+Consider, for example, the simple formula `\.{\$x\^2\$}', which would be
+parsed into an mlist containing a single element called an |ord_noad|.
+The |nucleus| of this noad is a representation of `\.x', the |subscr| is
+empty, and the |supscr| is a representation of `\.2'.
+
+The |nucleus|, |subscr|, and |supscr| fields are further broken into
+subfields. If |p| points to a noad, and if |q| is one of its principal
+fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the
+corresponding attribute of noad |p| is not present). Otherwise, there are
+several possibilities for the subfields, depending on the |type| of |q|.
+
+\yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of
+the sixteen font families, and |character(q)| is the number of a character
+within a font of that family, as in a character node.
+
+\yskip\hang|type(q)=math_text_char_node| is similar, but the character is
+unsubscripted and unsuperscripted and it is followed immediately by another
+character from the same font. (This |type| setting appears only
+briefly during the processing; it is used to suppress unwanted italic
+corrections.)
+
+\yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box
+node (either an |hlist_node| or a |vlist_node|) that should be used as the
+value of the field. The |shift_amount| in the subsidiary box node is the
+amount by which that box will be shifted downward.
+
+\yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to
+an mlist; the mlist must be converted to an hlist in order to obtain
+the value of this field.
+
+\yskip\noindent In the latter case, we might have |math_list(q)=null|. This
+is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}'
+and `\.{\$P\$}' produce different results (the former will not have the
+``italic correction'' added to the width of |P|, but the ``script skip''
+will be added).
+
+@c
+static void unsave_math(void)
+{
+ unsave();
+ decr(save_ptr);
+ flush_node_list(text_dir_ptr);
+ assert(saved_type(0) == saved_textdir);
+ text_dir_ptr = saved_value(0);
+}
+
+@ Sometimes it is necessary to destroy an mlist. The following
+subroutine empties the current list, assuming that |abs(mode)=mmode|.
+
+@c
+void flush_math(void)
+{
+ flush_node_list(vlink(head));
+ flush_node_list(incompleat_noad_par);
+ vlink(head) = null;
+ tail = head;
+ incompleat_noad_par = null;
+}
+
+@ Before we can do anything in math mode, we need fonts.
+
+@c
+#define MATHFONTSTACK 8
+#define MATHFONTDEFAULT 0 /* == nullfont */
+
+static sa_tree math_fam_head = NULL;
+
+@ @c
+int fam_fnt(int fam_id, int size_id)
+{
+ int n = fam_id + (256 * size_id);
+ return (int) get_sa_item(math_fam_head, n).int_value;
+}
+
+void def_fam_fnt(int fam_id, int size_id, int f, int lvl)
+{
+ int n = fam_id + (256 * size_id);
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = f;
+ set_sa_item(math_fam_head, n, sa_value, lvl);
+ fixup_math_parameters(fam_id, size_id, f, lvl);
+ if (tracing_assigns_par > 1) {
+ begin_diagnostic();
+ tprint("{assigning");
+ print_char(' ');
+ print_cmd_chr(def_family_cmd, size_id);
+ print_int(fam_id);
+ print_char('=');
+ print_font_identifier(fam_fnt(fam_id, size_id));
+ print_char('}');
+ end_diagnostic(false);
+ }
+}
+
+@ @c
+static void unsave_math_fam_data(int gl)
+{
+ sa_stack_item st;
+ if (math_fam_head->stack == NULL)
+ return;
+ while (math_fam_head->stack_ptr > 0 &&
+ abs(math_fam_head->stack[math_fam_head->stack_ptr].level)
+ >= (int) gl) {
+ st = math_fam_head->stack[math_fam_head->stack_ptr];
+ if (st.level > 0) {
+ rawset_sa_item(math_fam_head, st.code, st.value);
+ /* now do a trace message, if requested */
+ if (tracing_restores_par > 1) {
+ int size_id = st.code / 256;
+ int fam_id = st.code % 256;
+ begin_diagnostic();
+ tprint("{restoring");
+ print_char(' ');
+ print_cmd_chr(def_family_cmd, size_id);
+ print_int(fam_id);
+ print_char('=');
+ print_font_identifier(fam_fnt(fam_id, size_id));
+ print_char('}');
+ end_diagnostic(false);
+ }
+ }
+ (math_fam_head->stack_ptr)--;
+ }
+}
+
+@ and parameters
+
+@c
+#define MATHPARAMSTACK 8
+#define MATHPARAMDEFAULT undefined_math_parameter
+
+static sa_tree math_param_head = NULL;
+
+@ @c
+void def_math_param(int param_id, int style_id, scaled value, int lvl)
+{
+ int n = param_id + (256 * style_id);
+ sa_tree_item sa_value = { 0 };
+ sa_value.int_value = (int) value;
+ set_sa_item(math_param_head, n, sa_value, lvl);
+ if (tracing_assigns_par > 1) {
+ begin_diagnostic();
+ tprint("{assigning");
+ print_char(' ');
+ print_cmd_chr(set_math_param_cmd, param_id);
+ print_cmd_chr(math_style_cmd, style_id);
+ print_char('=');
+ print_int(value);
+ print_char('}');
+ end_diagnostic(false);
+ }
+}
+
+scaled get_math_param(int param_id, int style_id)
+{
+ int n = param_id + (256 * style_id);
+ return (scaled) get_sa_item(math_param_head, n).int_value;
+}
+
+@ @c
+static void unsave_math_param_data(int gl)
+{
+ sa_stack_item st;
+ if (math_param_head->stack == NULL)
+ return;
+ while (math_param_head->stack_ptr > 0 &&
+ abs(math_param_head->stack[math_param_head->stack_ptr].level)
+ >= (int) gl) {
+ st = math_param_head->stack[math_param_head->stack_ptr];
+ if (st.level > 0) {
+ rawset_sa_item(math_param_head, st.code, st.value);
+ /* now do a trace message, if requested */
+ if (tracing_restores_par > 1) {
+ int param_id = st.code % 256;
+ int style_id = st.code / 256;
+ begin_diagnostic();
+ tprint("{restoring");
+ print_char(' ');
+ print_cmd_chr(set_math_param_cmd, param_id);
+ print_cmd_chr(math_style_cmd, style_id);
+ print_char('=');
+ print_int(get_math_param(param_id, style_id));
+ print_char('}');
+ end_diagnostic(false);
+ }
+ }
+ (math_param_head->stack_ptr)--;
+ }
+}
+
+@ saving and unsaving of both
+
+@c
+void unsave_math_data(int gl)
+{
+ unsave_math_fam_data(gl);
+ unsave_math_param_data(gl);
+}
+
+@ Dumping and undumping
+@c
+void dump_math_data(void)
+{
+ sa_tree_item sa_value = { 0 };
+ if (math_fam_head == NULL) {
+ sa_value.int_value = MATHFONTDEFAULT;
+ math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value);
+ }
+ dump_sa_tree(math_fam_head, "mathfonts");
+ if (math_param_head == NULL) {
+ sa_value.int_value = MATHPARAMDEFAULT;
+ math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value);
+ }
+ dump_sa_tree(math_param_head, "mathparameters");
+}
+
+void undump_math_data(void)
+{
+ math_fam_head = undump_sa_tree("mathfonts");
+ math_param_head = undump_sa_tree("mathparameters");
+}
+
+@ @c
+void initialize_math(void)
+{
+ sa_tree_item sa_value = { 0 };
+ if (math_fam_head == NULL) {
+ sa_value.int_value = MATHFONTDEFAULT;
+ math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value);
+ }
+ if (math_param_head == NULL) {
+ sa_value.int_value = MATHPARAMDEFAULT;
+ math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value);
+ initialize_math_spacing();
+ }
+ return;
+}
+
+@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope,
+Clo, Pun, or Inn, for purposes of spacing and line breaking. An
+|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|,
+|punct_noad|, or |inner_noad| is used to represent portions of the various
+types. For example, an `\.=' sign in a formula leads to the creation of a
+|rel_noad| whose |nucleus| field is a representation of an equals sign
+(usually |fam=0|, |character=075|). A formula preceded by \.{\\mathrel}
+also results in a |rel_noad|. When a |rel_noad| is followed by an
+|op_noad|, say, and possibly separated by one or more ordinary nodes (not
+noads), \TeX\ will insert a penalty node (with the current |rel_penalty|)
+just after the formula that corresponds to the |rel_noad|, unless there
+already was a penalty immediately following; and a ``thick space'' will be
+inserted just before the formula that corresponds to the |op_noad|.
+
+A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually
+has a |subtype=normal|. The only exception is that an |op_noad| might
+have |subtype=limits| or |no_limits|, if the normal positioning of
+limits has been overridden for this operator.
+
+A |radical_noad| also has a |left_delimiter| field, which usually
+represents a square root sign.
+
+A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|.
+
+Delimiter fields have four subfields
+called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields
+represent variable-size delimiters by giving the ``small'' and ``large''
+starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
+@:TeXbook}{\sl The \TeX book@>
+
+A |fraction_noad| is actually quite different from all other noads.
+It has |thickness|, |denominator|, and |numerator| fields instead of
+|nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value
+that tells how thick to make a fraction rule; however, the special
+value |default_code| is used to stand for the
+|default_rule_thickness| of the current size. The |numerator| and
+|denominator| point to mlists that define a fraction; we always have
+$$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The
+|left_delimiter| and |right_delimiter| fields specify delimiters that will
+be placed at the left and right of the fraction. In this way, a
+|fraction_noad| is able to represent all of \TeX's operators \.{\\over},
+\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and
+ \.{\\abovewithdelims}.
+
+@ The |new_noad| function creates an |ord_noad| that is completely null
+
+@c
+pointer new_noad(void)
+{
+ pointer p;
+ p = new_node(simple_noad, ord_noad_type);
+ /* all noad fields are zero after this */
+ return p;
+}
+
+@ @c
+pointer new_sub_box(pointer curbox)
+{
+ pointer p, q;
+ p = new_noad();
+ q = new_node(sub_box_node, 0);
+ nucleus(p) = q;
+ math_list(nucleus(p)) = curbox;
+ return p;
+}
+
+@ A few more kinds of noads will complete the set: An |under_noad| has its
+nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
+an accent over its nucleus; the accent character appears as
+|math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad|
+centers its nucleus vertically with respect to the axis of the formula;
+in such noads we always have |type(nucleus(p))=sub_box|.
+
+And finally, we have the |fence_noad| type, to implement
+\TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}.
+The |nucleus| of such noads is
+replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces
+a |fence_noad| such that |delimiter(p)| holds the family and character
+codes for all left parentheses. A |fence_noad| of subtype |left_noad_side|
+never appears in an mlist except as the first element, and a |fence_noad|
+with subtype |right_noad_side| never appears in an mlist
+except as the last element; furthermore, we either have both a |left_noad_side|
+and a |right_noad_side|, or neither one is present.
+
+@ Math formulas can also contain instructions like \.{\\textstyle} that
+override \TeX's normal style rules. A |style_node| is inserted into the
+data structure to record such instructions; it is three words long, so it
+is considered a node instead of a noad. The |subtype| is either |display_style|
+or |text_style| or |script_style| or |script_script_style|. The
+second and third words of a |style_node| are not used, but they are
+present because a |choice_node| is converted to a |style_node|.
+
+\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles
+|display_style|, \dots, |script_script_style|, and adds~1 to get the
+``cramped'' versions of these styles. This gives a numerical order that
+is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
+i.e., a smaller style has a larger numerical value.
+@:TeXbook}{\sl The \TeX book@>
+
+@c
+const char *math_style_names[] = {
+ "display", "crampeddisplay",
+ "text", "crampedtext",
+ "script", "crampedscript",
+ "scriptscript", "crampedscriptscript",
+ NULL
+};
+
+const char *math_param_names[] = {
+ "quad", "axis", "operatorsize",
+ "overbarkern", "overbarrule", "overbarvgap",
+ "underbarkern", "underbarrule", "underbarvgap",
+ "radicalkern", "radicalrule", "radicalvgap",
+ "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise",
+ "stackvgap", "stacknumup", "stackdenomdown",
+ "fractionrule", "fractionnumvgap", "fractionnumup",
+ "fractiondenomvgap", "fractiondenomdown", "fractiondelsize",
+ "limitabovevgap", "limitabovebgap", "limitabovekern",
+ "limitbelowvgap", "limitbelowbgap", "limitbelowkern",
+ "nolimitsubfactor", "nolimitsupfactor", /* bonus */
+ "underdelimitervgap", "underdelimiterbgap",
+ "overdelimitervgap", "overdelimiterbgap",
+ "subshiftdrop", "supshiftdrop", "subshiftdown",
+ "subsupshiftdown", "subtopmax", "supshiftup",
+ "supbottommin", "supsubbottommax", "subsupvgap",
+ "spaceafterscript", "connectoroverlapmin",
+ "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing",
+ "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing",
+ "opordspacing", "opopspacing", "opbinspacing", "oprelspacing",
+ "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing",
+ "binordspacing", "binopspacing", "binbinspacing", "binrelspacing",
+ "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing",
+ "relordspacing", "relopspacing", "relbinspacing", "relrelspacing",
+ "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing",
+ "openordspacing", "openopspacing", "openbinspacing", "openrelspacing",
+ "openopenspacing", "openclosespacing", "openpunctspacing",
+ "openinnerspacing",
+ "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing",
+ "closeopenspacing", "closeclosespacing", "closepunctspacing",
+ "closeinnerspacing",
+ "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing",
+ "punctopenspacing", "punctclosespacing", "punctpunctspacing",
+ "punctinnerspacing",
+ "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing",
+ "inneropenspacing", "innerclosespacing", "innerpunctspacing",
+ "innerinnerspacing",
+ NULL
+};
+
+@ @c
+pointer new_style(small_number s)
+{ /* create a style node */
+ m_style = s;
+ return new_node(style_node, s);
+}
+
+@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which
+has special subfields |display_mlist|, |text_mlist|, |script_mlist|,
+and |script_script_mlist| pointing to the mlists for each style.
+
+@c
+static pointer new_choice(void)
+{ /* create a choice node */
+ return new_node(choice_node, 0); /* the |subtype| is not used */
+}
+
+@ Let's consider now the previously unwritten part of |show_node_list|
+that displays the things that can only be present in mlists; this
+program illustrates how to access the data structures just defined.
+
+In the context of the following program, |p| points to a node or noad that
+should be displayed, and the current string contains the ``recursion history''
+that leads to this point. The recursion history consists of a dot for each
+outer level in which |p| is subsidiary to some node, or in which |p| is
+subsidiary to the |nucleus| field of some noad; the dot is replaced by
+`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr|
+or |supscr| or |denominator| or |numerator| fields of noads. For example,
+the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for
+|x| in the (ridiculous) formula
+`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'.
+
+@c
+void display_normal_noad(pointer p); /* forward */
+void display_fence_noad(pointer p); /* forward */
+void display_fraction_noad(pointer p); /* forward */
+
+void show_math_node(pointer p)
+{
+ switch (type(p)) {
+ case style_node:
+ print_cmd_chr(math_style_cmd, subtype(p));
+ break;
+ case choice_node:
+ tprint_esc("mathchoice");
+ append_char('D');
+ show_node_list(display_mlist(p));
+ flush_char();
+ append_char('T');
+ show_node_list(text_mlist(p));
+ flush_char();
+ append_char('S');
+ show_node_list(script_mlist(p));
+ flush_char();
+ append_char('s');
+ show_node_list(script_script_mlist(p));
+ flush_char();
+ break;
+ case simple_noad:
+ case radical_noad:
+ case accent_noad:
+ display_normal_noad(p);
+ break;
+ case fence_noad:
+ display_fence_noad(p);
+ break;
+ case fraction_noad:
+ display_fraction_noad(p);
+ break;
+ default:
+ tprint("Unknown node type!");
+ break;
+ }
+}
+
+@ Here are some simple routines used in the display of noads.
+
+@c
+static void print_fam_and_char(pointer p)
+{ /* prints family and character */
+ tprint_esc("fam");
+ print_int(math_fam(p));
+ print_char(' ');
+ print(math_character(p));
+}
+
+@ @c
+static void print_delimiter(pointer p)
+{
+ int a;
+ if (delimiteroptionset(p)) {
+ tprint(" [ ");
+ if (delimiteraxis(p))
+ tprint("axis ");
+ if (delimiternoaxis(p))
+ tprint("noaxis ");
+ if (delimiterexact(p))
+ tprint("exact ");
+ tprint("]");
+ }
+ if (delimiterheight(p)) {
+ tprint("height=");
+ print_scaled(delimiterheight(p));
+ tprint(" ");
+ }
+ if (delimiterdepth(p)) {
+ tprint("depth=");
+ print_scaled(delimiterdepth(p));
+ tprint(" ");
+ }
+ if (delimiterclass(p)) {
+ tprint("class=");
+ print_int(delimiterclass(p));
+ tprint(" ");
+ }
+ if (small_fam(p) < 0) {
+ 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));
+ }
+}
+
+@ 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@>
+
+@c
+static void print_subsidiary_data(pointer p, ASCII_code c)
+{ /* display a noad field */
+ if ((int) cur_length >= depth_threshold) {
+ if (p != null)
+ tprint(" []");
+ } else {
+ append_char(c); /* include |c| in the recursion history */
+ 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 */
+ }
+}
+
+@ @c
+void display_normal_noad(pointer p)
+{
+ switch (type(p)) {
+ case simple_noad:
+ switch (subtype(p)) {
+ case ord_noad_type:
+ tprint_esc("mathord");
+ break;
+ case op_noad_type_normal:
+ case op_noad_type_limits:
+ case op_noad_type_no_limits:
+ tprint_esc("mathop");
+ if (subtype(p) == op_noad_type_limits)
+ tprint_esc("limits");
+ else if (subtype(p) == op_noad_type_no_limits)
+ tprint_esc("nolimits");
+ break;
+ case bin_noad_type:
+ tprint_esc("mathbin");
+ break;
+ case rel_noad_type:
+ tprint_esc("mathrel");
+ break;
+ case open_noad_type:
+ tprint_esc("mathopen");
+ break;
+ case close_noad_type:
+ tprint_esc("mathclose");
+ break;
+ case punct_noad_type:
+ tprint_esc("mathpunct");
+ break;
+ case inner_noad_type:
+ tprint_esc("mathinner");
+ break;
+ case over_noad_type:
+ tprint_esc("overline");
+ break;
+ case under_noad_type:
+ tprint_esc("underline");
+ break;
+ case vcenter_noad_type:
+ tprint_esc("vcenter");
+ break;
+ default:
+ tprint("<unknown noad type!>");
+ break;
+ }
+ break;
+ case radical_noad:
+ if (subtype(p) == 6)
+ tprint_esc("Udelimiterover");
+ else if (subtype(p) == 5)
+ tprint_esc("Udelimiterunder");
+ else if (subtype(p) == 4)
+ tprint_esc("Uoverdelimiter");
+ else if (subtype(p) == 3)
+ tprint_esc("Uunderdelimiter");
+ else if (subtype(p) == 2)
+ tprint_esc("Uroot");
+ else
+ tprint_esc("radical");
+ print_delimiter(left_delimiter(p));
+ if (degree(p) != null) {
+ print_subsidiary_data(degree(p), '/');
+ }
+ if (radicalwidth(p)) {
+ tprint("width=");
+ print_scaled(radicalwidth(p));
+ tprint(" ");
+ }
+ if (radicaloptionset(p)) {
+ tprint(" [ ");
+ if (radicalexact(p))
+ tprint("exact ");
+ if (radicalleft(p))
+ tprint("left ");
+ if (radicalmiddle(p))
+ tprint("middle ");
+ if (radicalright(p))
+ tprint("right ");
+ tprint("]");
+ }
+ break;
+ case accent_noad:
+ if (top_accent_chr(p) != null) {
+ if (bot_accent_chr(p) != null) {
+ tprint_esc("Umathaccent both");
+ } else {
+ tprint_esc("Umathaccent");
+ }
+ } else if (bot_accent_chr(p) != null) {
+ tprint_esc("Umathaccent bottom");
+ } else {
+ tprint_esc("Umathaccent overlay");
+ }
+ if (accentfraction(p)) {
+ tprint(" fraction=");
+ print_int(accentfraction(p));
+ tprint(" ");
+ }
+ switch (subtype(p)) {
+ case 0:
+ if (top_accent_chr(p) != null) {
+ if (bot_accent_chr(p) != null) {
+ print_fam_and_char(top_accent_chr(p));
+ print_fam_and_char(bot_accent_chr(p));
+ } else {
+ print_fam_and_char(top_accent_chr(p));
+ }
+ } else if (bot_accent_chr(p) != null) {
+ print_fam_and_char(bot_accent_chr(p));
+ } else {
+ print_fam_and_char(overlay_accent_chr(p));
+ }
+ break;
+ case 1:
+ if (top_accent_chr(p) != null) {
+ tprint(" fixed ");
+ print_fam_and_char(top_accent_chr(p));
+ if (bot_accent_chr(p) != null) {
+ print_fam_and_char(bot_accent_chr(p));
+ }
+ } else {
+ confusion("display_accent_noad");
+ }
+ break;
+ case 2:
+ if (bot_accent_chr(p) != null) {
+ if (top_accent_chr(p) != null) {
+ print_fam_and_char(top_accent_chr(p));
+ }
+ tprint(" fixed ");
+ print_fam_and_char(bot_accent_chr(p));
+ } else{
+ confusion("display_accent_noad");
+ }
+ break;
+ case 3:
+ if (top_accent_chr(p) != null && bot_accent_chr(p) != null) {
+ tprint(" fixed ");
+ print_fam_and_char(top_accent_chr(p));
+ tprint(" fixed ");
+ print_fam_and_char(bot_accent_chr(p));
+ } else {
+ confusion("display_accent_noad");
+ }
+ break;
+ }
+ break;
+ }
+ print_subsidiary_data(nucleus(p), '.');
+ print_subsidiary_data(supscr(p), '^');
+ print_subsidiary_data(subscr(p), '_');
+}
+
+@ @c
+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));
+}
+
+@ @c
+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), '/');
+}
+
+@ 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|.
+
+@c
+static void new_save_level_math(group_code c)
+{
+ set_saved_record(0, saved_textdir, 0, text_dir_ptr);
+ text_dir_ptr = new_dir(math_direction_par);
+ incr(save_ptr);
+ new_save_level(c);
+ eq_word_define(int_base + body_direction_code, math_direction_par);
+ eq_word_define(int_base + par_direction_code, math_direction_par);
+ eq_word_define(int_base + text_direction_code, math_direction_par);
+}
+
+@ @c
+static void push_math(group_code c, int mstyle)
+{
+ if (math_direction_par != text_direction_par)
+ dir_math_save = true;
+ push_nest();
+ mode = -mmode;
+ incompleat_noad_par = null;
+ m_style = mstyle;
+ new_save_level_math(c);
+}
+
+@ @c
+static void enter_ordinary_math(void)
+{
+ push_math(math_shift_group, text_style);
+ eq_word_define(int_base + cur_fam_code, -1);
+ if (every_math_par != null)
+ begin_token_list(every_math_par, every_math_text);
+}
+
+@ @c
+void enter_display_math(void);
+
+@ We get into math mode from horizontal mode when a `\.\$' (i.e., a
+|math_shift| character) is scanned. We must check to see whether this
+`\.\$' is immediately followed by another, in case display math mode is
+called for.
+
+@c
+void init_math(void)
+{
+ if (cur_cmd == math_shift_cmd) {
+ 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();
+ }
+ } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) {
+ enter_display_math();
+ } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) {
+ enter_ordinary_math();
+ } else {
+ you_cant();
+ }
+}
+
+@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or
+`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively;
+the value of |cur_chr| is placed onto |save_stack| for safe keeping.
+
+@ When \TeX\ is in display math mode, |cur_group=math_shift_group|,
+so it is not necessary for the |start_eq_no| procedure to test for
+this condition.
+
+@c
+void start_eq_no(void)
+{
+ set_saved_record(0, saved_eqno, 0, cur_chr);
+ 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.
+
+@c
+void math_left_brace(void)
+{
+ pointer q;
+ tail_append(new_noad());
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ back_input();
+ (void) scan_math(nucleus(tail), m_style);
+}
+
+@ If the inline directions of \.{\\pardir} and \.{\\mathdir} are
+opposite, then this function will return true. Discovering that fact
+is somewhat odd because it needs traversal of the |save_stack|.
+The occurance of displayed equations is weird enough that this is
+probably still better than having yet another field in the |input_stack|
+structures.
+
+None of this makes much sense if the inline direction of either one of
+\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current
+math machinery is ill suited anyway so I do not bother to test that.
+
+@c
+static boolean math_and_text_reversed_p(void)
+{
+ int i = save_ptr - 1;
+ while (save_type(i) != level_boundary)
+ i--;
+ while (i < save_ptr) {
+ if (save_type(i) == restore_old_value &&
+ save_value(i) == int_base + par_direction_code) {
+ if (textdir_opposite(math_direction_par, save_value(i - 1)))
+ return true;
+ }
+ i++;
+ }
+ return false;
+}
+
+@ When we enter display math mode, we need to call |line_break| to
+process the partial paragraph that has just been interrupted by the
+display. Then we can set the proper values of |display_width| and
+|display_indent| and |pre_display_size|.
+
+@c
+void enter_display_math(void)
+{
+ scaled w; /* new or partial |pre_display_size| */
+ scaled l; /* new |display_width| */
+ scaled s; /* new |display_indent| */
+ pointer p;
+ int n; /* scope of paragraph shape specification */
+ if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */
+ (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */
+ type(tail) == local_par_node && vlink(tail) == null)) {
+ if (vlink(head) == tail) {
+ /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if
+ there is another display immediately following, we have to get rid
+ of that node */
+ flush_node(tail);
+ }
+ pop_nest();
+ w = -max_dimen;
+ } else {
+ line_break(true, math_shift_group);
+ w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par);
+ }
+ /* now we are in vertical mode, working on the list that will contain the display */
+ /* A displayed equation is considered to be three lines long, so we
+ calculate the length and offset of line number |prev_graf+2|. */
+ if (par_shape_par_ptr == null) {
+ if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) {
+ halfword used_hang_indent = swap_hang_indent(hang_indent_par);
+ l = hsize_par - abs(used_hang_indent);
+ if (used_hang_indent > 0)
+ s = used_hang_indent;
+ else
+ s = 0;
+ } else {
+ l = hsize_par;
+ s = 0;
+ }
+ } else {
+ n = vinfo(par_shape_par_ptr + 1);
+ if (prev_graf_par + 2 >= n)
+ p = par_shape_par_ptr + 2 * n + 1;
+ else
+ p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1;
+ s = varmem[(p - 1)].cint;
+ l = varmem[p].cint;
+ s = swap_parshape_indent(s,l);
+ }
+
+ push_math(math_shift_group, display_style);
+ mode = mmode;
+ eq_word_define(int_base + cur_fam_code, -1);
+ eq_word_define(dimen_base + pre_display_size_code, w);
+ eq_word_define(dimen_base + display_width_code, l);
+ eq_word_define(dimen_base + display_indent_code, s);
+ eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0));
+ if (every_display_par != null)
+ begin_token_list(every_display_par, every_display_text);
+ if (nest_ptr == 1) {
+ checked_page_filter(before_display);
+ build_page();
+ }
+}
+
+@ The next routine parses all variations of a delimiter code. The |extcode|
+ tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the
+ |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.).
+ (the class is passed on for conversion to \.{\\mathchar}).
+
+@c
+static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass)
+{
+ const char *hlp[] = {
+ "I'm going to use 0 instead of that illegal code value.",
+ NULL
+ };
+ delcodeval d;
+ int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0;
+ if (extcode == tex_mathcode) { /* \.{\\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 == umath_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 == umathnum_mathcode) { /* \.{\\Udelcodenum} */
+ /* "FF<21bits> */
+ /* the largest numeric value is $2^29-1$, but
+ the top of bit 21 can't be used as it contains invalid USV's
+ */
+ if (doclass) { /* such a primitive doesn't exist */
+ confusion("umathnum_mathcode");
+ }
+ scan_int();
+ msfam = (cur_val / 0x200000);
+ mschr = cur_val & 0x1FFFFF;
+ if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) {
+ tex_error("Invalid delimiter code", hlp);
+ msfam = 0;
+ mschr = 0;
+ }
+ mlfam = 0;
+ mlchr = 0;
+ } else {
+ /* something's gone wrong */
+ confusion("unknown_extcode");
+ }
+ d.class_value = mcls;
+ d.small_family_value = msfam;
+ d.small_character_value = mschr;
+ d.large_family_value = mlfam;
+ d.large_character_value = mlchr;
+ return d;
+}
+
+@ @c
+void scan_extdef_del_code(int level, int extcode)
+{
+ delcodeval d;
+ int p;
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ d = do_scan_extdef_del_code(extcode, false);
+ set_del_code(p, d.small_family_value, d.small_character_value,
+ d.large_family_value, d.large_character_value,
+ (quarterword) (level));
+}
+
+@ @c
+mathcodeval scan_mathchar(int extcode)
+{
+ char errstr[255] = { 0 };
+ const char *hlp[] = {
+ "I'm going to use 0 instead of that illegal code value.",
+ NULL
+ };
+ mathcodeval d;
+ int mcls = 0, mfam = 0, mchr = 0;
+ if (extcode == tex_mathcode) { /* \.{\\mathcode} */
+ /* "TFCC */
+ scan_int();
+ if (cur_val > 0x8000) {
+ /*
+ tex_error("Invalid math code", hlp);
+ cur_val = 0;
+ */
+ /* needed for latex: fallback to umathnum_mathcode */
+ mfam = (cur_val / 0x200000) & 0x7FF;
+ mcls = mfam % 0x08;
+ mfam = mfam / 0x08;
+ mchr = cur_val & 0x1FFFFF;
+ if (mchr > 0x10FFFF) {
+ tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp);
+ mcls = 0;
+ mfam = 0;
+ mchr = 0;
+ }
+ } else {
+ if (cur_val < 0) {
+ snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val);
+ tex_error(errstr, hlp);
+ cur_val = 0;
+ }
+ mcls = (cur_val / 0x1000);
+ mfam = ((cur_val % 0x1000) / 0x100);
+ mchr = (cur_val % 0x100);
+ }
+ } else if (extcode == umath_mathcode) {
+ /* <0-0x7> <0-0xFF> <0-0x10FFFF> */
+ scan_int();
+ mcls = cur_val;
+ scan_int();
+ mfam = cur_val;
+ scan_char_num();
+ mchr = cur_val;
+ if (mcls < 0 || mcls > 7 || mfam > 255) {
+ tex_error("Invalid math code", hlp);
+ mchr = 0;
+ mfam = 0;
+ mcls = 0;
+ }
+ } else if (extcode == umathnum_mathcode) {
+ /* "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 */
+ confusion("unknown_extcode");
+ }
+ d.class_value = mcls;
+ d.family_value = mfam;
+ d.character_value = mchr;
+ return d;
+}
+
+@ @c
+void scan_extdef_math_code(int level, int extcode)
+{
+ mathcodeval d;
+ int p;
+ scan_char_num();
+ p = cur_val;
+ scan_optional_equals();
+ d = scan_mathchar(extcode);
+ set_math_code(p, d.class_value,
+ d.family_value, d.character_value, (quarterword) (level));
+}
+
+@ this reads in a delcode when actually a mathcode is needed
+@c
+mathcodeval scan_delimiter_as_mathchar(int extcode)
+{
+ delcodeval dval;
+ mathcodeval mval;
+ dval = do_scan_extdef_del_code(extcode, true);
+ mval.class_value = dval.class_value;
+ mval.family_value = dval.small_family_value;
+ mval.character_value = dval.small_character_value;
+ return mval;
+}
+
+@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad
+are broken down into subfields called |type| and either |math_list| or
+|(math_fam,math_character)|. The job of |scan_math| is to figure out
+what to place in one of these principal fields; it looks at the
+subformula that comes next in the input, and places an encoding of
+that subformula into a given word of |mem|.
+
+@c
+#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd)
+
+int scan_math_style(pointer p, int mstyle)
+{
+ get_next_nb_nr();
+ back_input();
+ scan_left_brace();
+ set_saved_record(0, saved_math, 0, p);
+ incr(save_ptr);
+ push_math(math_group, mstyle);
+ return 1;
+}
+
+int scan_math(pointer p, int mstyle)
+{
+ /* label restart,reswitch,exit; */
+ mathcodeval mval = { 0, 0, 0 };
+ assert(p != null);
+ RESTART:
+ get_next_nb_nr();
+ RESWITCH:
+ switch (cur_cmd) {
+ case letter_cmd:
+ case other_char_cmd:
+ case char_given_cmd:
+ mval = get_math_code(cur_chr);
+ if (mval.class_value == 8) {
+ /* An active character that is an |outer_call| is allowed here */
+ cur_cs = active_to_cs(cur_chr, true);
+ cur_cmd = eq_type(cur_cs);
+ cur_chr = equiv(cur_cs);
+ x_token();
+ back_input();
+ goto RESTART;
+ }
+ break;
+ case char_num_cmd:
+ scan_char_num();
+ cur_chr = cur_val;
+ cur_cmd = char_given_cmd;
+ goto RESWITCH;
+ break;
+ case math_char_num_cmd:
+ if (cur_chr == 0)
+ mval = scan_mathchar(tex_mathcode);
+ else if (cur_chr == 1)
+ mval = scan_mathchar(umath_mathcode);
+ else if (cur_chr == 2)
+ mval = scan_mathchar(umathnum_mathcode);
+ else
+ confusion("scan_math");
+ break;
+ case math_given_cmd:
+ mval = mathchar_from_integer(cur_chr, tex_mathcode);
+ break;
+ case xmath_given_cmd:
+ mval = mathchar_from_integer(cur_chr, umath_mathcode);
+ break;
+ case delim_num_cmd:
+ if (cur_chr == 0)
+ mval = scan_delimiter_as_mathchar(tex_mathcode);
+ else if (cur_chr == 1)
+ mval = scan_delimiter_as_mathchar(umath_mathcode);
+ else
+ confusion("scan_math");
+ break;
+ default:
+ /* The pointer |p| is placed on |save_stack| while a complex subformula
+ is being scanned. */
+ back_input();
+ scan_left_brace();
+ set_saved_record(0, saved_math, 0, p);
+ incr(save_ptr);
+ push_math(math_group, mstyle);
+ return 1;
+ }
+ type(p) = math_char_node;
+ math_character(p) = mval.character_value;
+ if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range)
+ math_fam(p) = cur_fam_par;
+ else
+ math_fam(p) = mval.family_value;
+ return 0;
+}
+
+@ The |set_math_char| procedure creates a new noad appropriate to a given
+math code, and appends it to the current mlist. However, if the math code
+is sufficiently large, the |cur_chr| is treated as an active character and
+nothing is appended.
+
+@c
+void set_math_char(mathcodeval mval)
+{
+ pointer p; /* the new noad */
+ 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 == math_use_current_family_code) {
+ if (cur_fam_par_in_range)
+ math_fam(nucleus(p)) = cur_fam_par;
+ subtype(p) = ord_noad_type;
+ } else {
+ switch (mval.class_value) {
+ /* *INDENT-OFF* */
+ case 0: subtype(p) = ord_noad_type; break;
+ case 1: subtype(p) = op_noad_type_normal; break;
+ case 2: subtype(p) = bin_noad_type; break;
+ case 3: subtype(p) = rel_noad_type; break;
+ case 4: subtype(p) = open_noad_type; break;
+ case 5: subtype(p) = close_noad_type; break;
+ case 6: subtype(p) = punct_noad_type; break;
+ /* *INDENT-ON* */
+ }
+ }
+ 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.
+
+@c
+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;
+ }
+}
+
+@ @c
+void math_math_comp(void)
+{
+ pointer q;
+ tail_append(new_noad());
+ subtype(tail) = (quarterword) cur_chr;
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ if (cur_chr == over_noad_type)
+ (void) scan_math(nucleus(tail), cramped_style(m_style));
+ else
+ (void) scan_math(nucleus(tail), m_style);
+}
+
+@ @c
+void math_limit_switch(void)
+{
+ const char *hlp[] = {
+ "I'm ignoring this misplaced \\limits or \\nolimits command.",
+ NULL
+ };
+ if (head != tail) {
+ if (type(tail) == simple_noad &&
+ (subtype(tail) == op_noad_type_normal ||
+ subtype(tail) == op_noad_type_limits ||
+ subtype(tail) == op_noad_type_no_limits)) {
+ subtype(tail) = (quarterword) cur_chr;
+ return;
+ }
+ }
+ tex_error("Limit controls must follow a math operator", hlp);
+}
+
+@ Delimiter fields of noads are filled in by the |scan_delimiter| routine.
+The first parameter of this procedure is the |mem| address where the
+delimiter is to be placed; the second tells if this delimiter follows
+\.{\\radical} or not.
+
+@c
+static void scan_delimiter(pointer p, int r)
+{
+ delcodeval dval = { 0, 0, 0, 0, 0 };
+ if (r == tex_mathcode) { /* \.{\\radical} */
+ dval = do_scan_extdef_del_code(tex_mathcode, true);
+ } else if (r == umath_mathcode) { /* \.{\\Uradical} */
+ dval = do_scan_extdef_del_code(umath_mathcode, false);
+ } else if (r == no_mathcode) {
+ get_next_nb_nr();
+ switch (cur_cmd) {
+ case letter_cmd:
+ case other_char_cmd:
+ dval = get_del_code(cur_chr);
+ break;
+ case delim_num_cmd:
+ if (cur_chr == 0) /* \.{\\delimiter} */
+ dval = do_scan_extdef_del_code(tex_mathcode, true);
+ else if (cur_chr == 1) /* \.{\\Udelimiter} */
+ dval = do_scan_extdef_del_code(umath_mathcode, true);
+ else
+ confusion("scan_delimiter1");
+ break;
+ default:
+ dval.small_family_value = -1;
+ break;
+ }
+ } else {
+ confusion("scan_delimiter2");
+ }
+ if (p == null)
+ return;
+ if (dval.small_family_value < 0) {
+ const char *hlp[] = {
+ "I was expecting to see something like `(' or `\\{' or",
+ "`\\}' here. If you typed, e.g., `{' instead of `\\{', you",
+ "should probably delete the `{' by typing `1' now, so that",
+ "braces don't get unbalanced. Otherwise just proceed",
+ "Acceptable delimiters are characters whose \\delcode is",
+ "nonnegative, or you can use `\\delimiter <delimiter code>'.",
+ NULL
+ };
+ back_error("Missing delimiter (. inserted)", hlp);
+ small_fam(p) = 0;
+ small_char(p) = 0;
+ large_fam(p) = 0;
+ large_char(p) = 0;
+ } else {
+ small_fam(p) = dval.small_family_value;
+ small_char(p) = dval.small_character_value;
+ large_fam(p) = dval.large_family_value;
+ large_char(p) = dval.large_character_value;
+ }
+ return;
+}
+
+@ @c
+void math_radical(void)
+{
+ halfword q;
+ int chr_code = cur_chr;
+ halfword options = 0;
+ tail_append(new_node(radical_noad, chr_code));
+ q = new_node(delim_node, 0);
+ left_delimiter(tail) = q;
+ while (1) {
+ if (scan_keyword("width")) {
+ scan_dimen(false,false,false);
+ radicalwidth(tail) = cur_val ;
+ } else if (scan_keyword("left")) {
+ options = options | noad_option_left ;
+ } else if (scan_keyword("middle")) {
+ options = options | noad_option_middle ;
+ } else if (scan_keyword("right")) {
+ options = options | noad_option_right ;
+ } else {
+ break;
+ }
+ }
+ radicaloptions(tail) = options;
+ if (chr_code == 0) /* \.{\\radical} */
+ scan_delimiter(left_delimiter(tail), tex_mathcode);
+ else if (chr_code == 1) /* \.{\\Uradical} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 2) /* \.{\\Uroot} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 3) /* \.{\\Uunderdelimiter} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 4) /* \.{\\Uoverdelimiter} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 5) /* \.{\\Udelimiterunder} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 6) /* \.{\\Udelimiterover} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else if (chr_code == 7) /* \.{\\Uhextensible} */
+ scan_delimiter(left_delimiter(tail), umath_mathcode);
+ else
+ confusion("math_radical");
+ if (chr_code == 7) {
+ q = new_node(sub_box_node, 0); /* type will change */
+ nucleus(tail) = q;
+ return;
+ } else if (chr_code == 2) {
+ /* the trick with the |vlink(q)| is used by |scan_math|
+ to decide whether it needs to go on */
+ q = new_node(math_char_node, 0);
+ vlink(q) = tail;
+ degree(tail) = q;
+ if (!scan_math(degree(tail), sup_sup_style(m_style))) {
+ vlink(degree(tail)) = null;
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ (void) scan_math(nucleus(tail), cramped_style(m_style));
+ }
+ } else {
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ (void) scan_math(nucleus(tail), cramped_style(m_style));
+ }
+}
+
+@ @c
+void math_ac(void)
+{
+ halfword q;
+ mathcodeval t = { 0, 0, 0 };
+ mathcodeval b = { 0, 0, 0 };
+ mathcodeval o = { 0, 0, 0 };
+ if (cur_cmd == accent_cmd) {
+ const char *hlp[] = {
+ "I'm changing \\accent to \\mathaccent here; wish me luck.",
+ "(Accents are not the same in formulas as they are in text.)",
+ NULL
+ };
+ tex_error("Please use \\mathaccent for accents in math mode", hlp);
+ }
+ tail_append(new_node(accent_noad, 0));
+ if (cur_chr == 0) { /* \.{\\mathaccent} */
+ t = scan_mathchar(tex_mathcode);
+ } else if (cur_chr == 1) { /* \.{\\Umathaccent} */
+ if (scan_keyword("fixed")) {
+ /* top */
+ subtype(tail) = 1;
+ t = scan_mathchar(umath_mathcode);
+ } else if (scan_keyword("both")) {
+ /* top bottom */
+ if (scan_keyword("fixed")) {
+ subtype(tail) = 1;
+ }
+ t = scan_mathchar(umath_mathcode);
+ if (scan_keyword("fixed")) {
+ subtype(tail) += 2;
+ }
+ b = scan_mathchar(umath_mathcode);
+ } else if (scan_keyword("bottom")) {
+ /* bottom */
+ if (scan_keyword("fixed")) {
+ subtype(tail) = 2;
+ }
+ b = scan_mathchar(umath_mathcode);
+ } else if (scan_keyword("top")) {
+ /* top */
+ if (scan_keyword("fixed")) {
+ subtype(tail) = 1;
+ }
+ t = scan_mathchar(umath_mathcode);
+ } else if (scan_keyword("overlay")) {
+ /* overlay */
+ if (scan_keyword("fixed")) {
+ subtype(tail) = 1;
+ }
+ o = scan_mathchar(umath_mathcode);
+ } else {
+ /* top */
+ t = scan_mathchar(umath_mathcode);
+ }
+ if (scan_keyword("fraction")) {
+ scan_int();
+ accentfraction(tail) = cur_val;
+ }
+ } else {
+ confusion("mathaccent");
+ }
+ if (!(t.character_value == 0 && t.family_value == 0)) {
+ q = new_node(math_char_node, 0);
+ top_accent_chr(tail) = q;
+ math_character(top_accent_chr(tail)) = t.character_value;
+ if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range)
+ math_fam(top_accent_chr(tail)) = cur_fam_par;
+ else
+ math_fam(top_accent_chr(tail)) = t.family_value;
+ }
+ if (!(b.character_value == 0 && b.family_value == 0)) {
+ q = new_node(math_char_node, 0);
+ bot_accent_chr(tail) = q;
+ math_character(bot_accent_chr(tail)) = b.character_value;
+ if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range)
+ math_fam(bot_accent_chr(tail)) = cur_fam_par;
+ else
+ math_fam(bot_accent_chr(tail)) = b.family_value;
+ }
+ if (!(o.character_value == 0 && o.family_value == 0)) {
+ q = new_node(math_char_node, 0);
+ overlay_accent_chr(tail) = q;
+ math_character(overlay_accent_chr(tail)) = o.character_value;
+ if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range)
+ math_fam(overlay_accent_chr(tail)) = cur_fam_par;
+ else
+ math_fam(overlay_accent_chr(tail)) = o.family_value;
+ }
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ (void) scan_math(nucleus(tail), cramped_style(m_style));
+}
+
+@ @c
+pointer math_vcenter_group(pointer p)
+{
+ pointer q, r;
+ q = new_noad();
+ subtype(q) = vcenter_noad_type;
+ r = new_node(sub_box_node, 0);
+ nucleus(q) = r;
+ math_list(nucleus(q)) = p;
+ return q;
+}
+
+@ The routine that scans the four mlists of a \.{\\mathchoice} is very
+much like the routine that builds discretionary nodes.
+
+@c
+void append_choices(void)
+{
+ tail_append(new_choice());
+ incr(save_ptr);
+ set_saved_record(-1, saved_choices, 0, 0);
+ push_math(math_choice_group, display_style);
+ scan_left_brace();
+}
+
+@ @c
+void build_choices(void)
+{
+ pointer p; /* the current mlist */
+ int prev_style;
+ prev_style = m_style;
+ unsave_math();
+ p = fin_mlist(null);
+ assert(saved_type(-1) == saved_choices);
+ switch (saved_value(-1)) {
+ case 0:
+ display_mlist(tail) = p;
+ break;
+ case 1:
+ text_mlist(tail) = p;
+ break;
+ case 2:
+ script_mlist(tail) = p;
+ break;
+ case 3:
+ script_script_mlist(tail) = p;
+ decr(save_ptr);
+ return;
+ break;
+ } /* there are no other cases */
+ set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1));
+ push_math(math_choice_group, (prev_style + 2));
+ scan_left_brace();
+}
+
+@ Subscripts and superscripts are attached to the previous nucleus by the
+action procedure called |sub_sup|.
+
+@c
+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 || cur_chr == sup_mark_cmd) { /* |super_sub_script| */
+ if (supscr(tail) != null) {
+ const char *hlp[] = {
+ "I treat `x^1^2' essentially like `x^1{}^2'.", NULL
+ };
+ 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), sup_style(m_style));
+ } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) {
+ if (subscr(tail) != null) {
+ const char *hlp[] = {
+ "I treat `x_1_2' essentially like `x_1{}_2'.", NULL
+ };
+ tail_append(new_noad());
+ q = new_node(sub_mlist_node, 0);
+ nucleus(tail) = q;
+ tex_error("Double subscript", hlp);
+ }
+ q = new_node(math_char_node, 0);
+ subscr(tail) = q;
+ (void) scan_math(subscr(tail), sub_style(m_style));
+ }
+}
+
+@ An operation like `\.{\\over}' causes the current mlist to go into a
+state of suspended animation: |incompleat_noad| points to a |fraction_noad|
+that contains the mlist-so-far as its numerator, while the denominator
+is yet to come. Finally when the mlist is finished, the denominator will
+go into the incompleat fraction noad, and that noad will become the
+whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}'
+delimiters.
+
+@c
+void math_fraction(void)
+{
+ halfword c; /* the type of generalized fraction we are scanning */
+ pointer q;
+ halfword options = 0;
+ halfword temp_value;
+ c = cur_chr;
+ if (incompleat_noad_par != null) {
+ const char *hlp[] = {
+ "I'm ignoring this fraction specification, since I don't",
+ "know whether a construction like `x \\over y \\over z'",
+ "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.",
+ NULL
+ };
+ if (c >= delimited_code) {
+ scan_delimiter(null, no_mathcode);
+ scan_delimiter(null, no_mathcode);
+ }
+ if ((c % delimited_code) == above_code)
+ scan_normal_dimen();
+ tex_error("Ambiguous; you need another { and }", hlp);
+ } else {
+ incompleat_noad_par = new_node(fraction_noad, 0);
+ temp_value = new_node(sub_mlist_node, 0);
+ numerator(incompleat_noad_par) = temp_value;
+ math_list(numerator(incompleat_noad_par)) = vlink(head);
+ vlink(head) = null;
+ tail = head;
+ m_style = cramped_style(m_style);
+
+ if ((c % delimited_code) == skewed_code) {
+ q = new_node(delim_node, 0);
+ middle_delimiter(incompleat_noad_par) = q;
+ scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode);
+ }
+ if (c >= delimited_code) {
+ q = new_node(delim_node, 0);
+ left_delimiter(incompleat_noad_par) = q;
+ q = new_node(delim_node, 0);
+ right_delimiter(incompleat_noad_par) = q;
+ scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode);
+ scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode);
+ }
+ switch (c % delimited_code) {
+ case above_code:
+ while (1) {
+ if (scan_keyword("exact")) {
+ options = options | noad_option_exact ;
+ } else {
+ break;
+ }
+ }
+ fractionoptions(incompleat_noad_par) = options;
+ scan_normal_dimen();
+ thickness(incompleat_noad_par) = cur_val;
+ break;
+ case over_code:
+ thickness(incompleat_noad_par) = default_code;
+ break;
+ case atop_code:
+ thickness(incompleat_noad_par) = 0;
+ break;
+ case skewed_code:
+ while (1) {
+ if (scan_keyword("exact")) {
+ options = options | noad_option_exact ;
+ } else if (scan_keyword("noaxis")) {
+ options = options | noad_option_no_axis ;
+ } else {
+ break;
+ }
+ }
+ fractionoptions(incompleat_noad_par) = options;
+ thickness(incompleat_noad_par) = 0;
+ break;
+ }
+ }
+}
+
+@ At the end of a math formula or subformula, the |fin_mlist| routine is
+called upon to return a pointer to the newly completed mlist, and to
+pop the nest back to the enclosing semantic level. The parameter to
+|fin_mlist|, if not null, points to a |fence_noad| that ends the
+current mlist; this |fence_noad| has not yet been appended.
+
+@c
+pointer fin_mlist(pointer p)
+{
+ pointer q; /* the mlist to return */
+ if (incompleat_noad_par != null) {
+ if (denominator(incompleat_noad_par) != null) {
+ type(denominator(incompleat_noad_par)) = sub_mlist_node;
+ } else {
+ q = new_node(sub_mlist_node, 0);
+ denominator(incompleat_noad_par) = q;
+ }
+ math_list(denominator(incompleat_noad_par)) = vlink(head);
+ if (p == null) {
+ q = incompleat_noad_par;
+ } else {
+ q = math_list(numerator(incompleat_noad_par));
+ if ((type(q) != fence_noad) || (subtype(q) != left_noad_side)
+ || (delim_par == null))
+ confusion("right"); /* this can't happen */
+ math_list(numerator(incompleat_noad_par)) = vlink(delim_par);
+ vlink(delim_par) = incompleat_noad_par;
+ vlink(incompleat_noad_par) = p;
+ }
+ } else {
+ vlink(tail) = p;
+ q = vlink(head);
+ }
+ pop_nest();
+ return q;
+}
+
+@ Now at last we're ready to see what happens when a right brace occurs
+in a math formula. Two special cases are simplified here: Braces are effectively
+removed when they surround a single Ord without sub/superscripts, or when they
+surround an accent that is the nucleus of an Ord atom.
+
+@c
+void close_math_group(pointer p)
+{
+ int old_style = m_style;
+ unsave_math();
+
+ decr(save_ptr);
+ assert(saved_type(0) == saved_math);
+ type(saved_value(0)) = sub_mlist_node;
+ p = fin_mlist(null);
+ math_list(saved_value(0)) = p;
+ if (p != null) {
+ if (vlink(p) == null) {
+ if (type(p) == simple_noad && subtype(p) == ord_noad_type) {
+ if (subscr(p) == null && supscr(p) == null) {
+ type(saved_value(0)) = type(nucleus(p));
+ if (type(nucleus(p)) == math_char_node) {
+ math_fam(saved_value(0)) = math_fam(nucleus(p));
+ math_character(saved_value(0)) =
+ math_character(nucleus(p));
+ } else {
+ math_list(saved_value(0)) = math_list(nucleus(p));
+ math_list(nucleus(p)) = null;
+ }
+ delete_attribute_ref(node_attr(saved_value(0)));
+ node_attr(saved_value(0)) = node_attr(nucleus(p));
+ node_attr(nucleus(p)) = null;
+ flush_node(p);
+ }
+ } else if (type(p) == accent_noad) {
+ if (saved_value(0) == nucleus(tail)) {
+ if (type(tail) == simple_noad
+ && subtype(tail) == ord_noad_type) {
+ pointer q = head;
+ while (vlink(q) != tail)
+ q = vlink(q);
+ vlink(q) = p;
+ nucleus(tail) = null;
+ subscr(tail) = null;
+ supscr(tail) = null;
+ delete_attribute_ref(node_attr(p));
+ node_attr(p) = node_attr(tail);
+ node_attr(tail) = null;
+ flush_node(tail);
+ tail = p;
+ }
+ }
+ }
+ }
+ }
+ if (vlink(saved_value(0)) > 0) {
+ pointer q;
+ q = new_node(math_char_node, 0);
+ nucleus(vlink(saved_value(0))) = q;
+ vlink(saved_value(0)) = null;
+ saved_value(0) = q;
+ (void) scan_math(saved_value(0), old_style);
+ /* 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}.
+
+@c
+void math_left_right(void)
+{
+ halfword t; /* |left_noad_side| .. |right_noad_side| */
+ pointer p; /* new noad */
+ pointer q; /* resulting mlist */
+ pointer r; /* temporary */
+ halfword ht = 0;
+ halfword dp = 0;
+ halfword options = 0;
+ halfword type = -1 ;
+ t = cur_chr;
+
+ if (t > 10) {
+ /* we have \Uleft \Uright \Umiddle */
+ t = t - 10;
+ while (1) {
+ if (scan_keyword("height")) {
+ scan_dimen(false,false,false);
+ ht = cur_val ;
+ } else if (scan_keyword("depth")) {
+ scan_dimen(false,false,false);
+ dp = cur_val ;
+ } else if (scan_keyword("axis")) {
+ options = options | noad_option_axis ;
+ } else if (scan_keyword("noaxis")) {
+ options = options | noad_option_no_axis ;
+ } else if (scan_keyword("exact")) {
+ options = options | noad_option_exact ;
+ } else if (scan_keyword("class")) {
+ scan_int();
+ type = cur_val ;
+ } else {
+ break;
+ }
+ }
+ }
+
+ if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) {
+ if (cur_group == math_shift_group) {
+ scan_delimiter(null, no_mathcode);
+ if (t == middle_noad_side) {
+ const char *hlp[] = {
+ "I'm ignoring a \\middle that had no matching \\left.",
+ NULL
+ };
+ tex_error("Extra \\middle", hlp);
+ } else {
+ const char *hlp[] = {
+ "I'm ignoring a \\right that had no matching \\left.",
+ NULL
+ };
+ tex_error("Extra \\right", hlp);
+ }
+ } else {
+ off_save();
+ }
+ } else {
+ p = new_noad();
+ type(p) = fence_noad;
+ subtype(p) = (quarterword) t;
+ r = new_node(delim_node, 0);
+ delimiter(p) = r;
+
+ delimiterheight(p) = ht;
+ delimiterdepth(p) = dp;
+ delimiteroptions(p) = options;
+ delimiterclass(p) = type;
+ delimiteritalic(p) = 0;
+
+ scan_delimiter(delimiter(p), no_mathcode);
+
+ if (t == no_noad_side) {
+ tail_append(new_noad());
+ subtype(tail) = inner_noad_type;
+ r = new_node(sub_mlist_node, 0);
+ nucleus(tail) = r;
+ math_list(nucleus(tail)) = p;
+ return ;
+ }
+
+ if (t == left_noad_side) {
+ q = p;
+ } else {
+ q = fin_mlist(p);
+ unsave_math();
+ }
+ if (t != right_noad_side) {
+ push_math(math_left_group, m_style);
+ vlink(head) = q;
+ tail = p;
+ delim_par = p;
+ } else {
+ tail_append(new_noad());
+ subtype(tail) = inner_noad_type;
+ r = new_node(sub_mlist_node, 0);
+ nucleus(tail) = r;
+ math_list(nucleus(tail)) = q;
+ }
+ }
+}
+
+@ \TeX\ gets to the following part of the program when
+the first `\.\$' ending a display has been scanned.
+
+@c
+static void check_second_math_shift(void)
+{
+ get_x_token();
+ if (cur_cmd != math_shift_cmd) {
+ const char *hlp[] = {
+ "The `$' that I just saw supposedly matches a previous `$$'.",
+ "So I shall assume that you typed `$$' both times.",
+ NULL
+ };
+ back_error("Display math should end with $$", hlp);
+ }
+}
+
+static void check_display_math_end(void)
+{
+ if (cur_chr != cramped_display_style) {
+ const char *hlp[] = {
+ "I shall assume that you typed that.",
+ NULL
+ };
+ tex_error("Display math should end with \\Ustopdisplaymath", hlp);
+ }
+}
+
+static void check_inline_math_end(void)
+{
+ if (cur_chr != cramped_text_style) {
+ const char *hlp[] = {
+ "I shall assume that you typed that.",
+ NULL
+ };
+ tex_error("Inline math should end with \\Ustopmath", hlp);
+ }
+}
+
+@ @c
+static void resume_after_display(void)
+{
+ if (cur_group != math_shift_group)
+ confusion("display");
+ unsave_math();
+ prev_graf_par = prev_graf_par + 3;
+ push_nest();
+ mode = hmode;
+ space_factor_par = 1000;
+ /* this needs to be intercepted in the display math start ! */
+ tail_append(make_local_par_node(penalty_par_code));
+ get_x_token();
+ if (cur_cmd != spacer_cmd)
+ back_input();
+ if (nest_ptr == 1) {
+ normal_page_filter(after_display);
+ build_page();
+ }
+}
+
+@ The fussiest part of math mode processing occurs when a displayed formula is
+being centered and placed with an optional equation number.
+
+At this time we are in vertical mode (or internal vertical mode).
+
+ |p| points to the mlist for the formula.
+ |a| is either |null| or it points to a box containing the equation number.
+ |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box).
+
+@c
+#define inject_display_skip_before(g) \
+ switch (display_skip_mode_par) { \
+ case 0 : /* normal tex */ \
+ tail_append(new_param_glue(g)); \
+ break;\
+ case 1 : /* always */ \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 2 : /* non-zero */ \
+ if (g != 0 && ! glue_is_zero(glue_par(g))) \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 3: /* ignore */ \
+ break; \
+ }
+
+#define inject_display_skip_after(g) \
+ switch (display_skip_mode_par) { \
+ case 0 : /* normal tex */ \
+ if (g != 0 && glue_is_positive(glue_par(g))) \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 1 : /* always */ \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 2 : /* non-zero */ \
+ if (g != 0 && ! glue_is_zero(glue_par(g))) \
+ tail_append(new_param_glue(g)); \
+ break; \
+ case 3: /* ignore */ \
+ break; \
+ }
+
+static void finish_displayed_math(boolean l, pointer eqno_box, pointer p)
+{
+ pointer eq_box; /* box containing the equation */
+ scaled eq_w; /* width of the equation */
+ scaled line_w; /* width of the line */
+ scaled eqno_w; /* width of equation number */
+ scaled eqno_w2; /* width of equation number plus space to separate from equation */
+ scaled line_s; /* move the line right this much */
+ scaled d; /* displacement of equation in the line */
+ small_number g1, g2; /* glue parameter codes for before and after */
+ pointer r,s; /* kern nodes used to position the display */
+ pointer t; /* tail of adjustment list */
+ pointer pre_t; /* tail of pre-adjustment list */
+ boolean swap_dir; /* true if the math and surrounding text dirs are opposed */
+ scaled eqno_width;
+ swap_dir = (pre_display_direction_par < 0 ? true : false );
+ if (eqno_box != null && swap_dir)
+ l = !l;
+ adjust_tail = adjust_head;
+ pre_adjust_tail = pre_adjust_head;
+ eq_box = hpack(p, 0, additional, -1);
+ subtype(eq_box) = equation_list; /* new */
+ build_attribute_list(eq_box);
+ p = list_ptr(eq_box);
+ t = adjust_tail;
+ adjust_tail = null;
+ pre_t = pre_adjust_tail;
+ pre_adjust_tail = null;
+ eq_w = width(eq_box);
+ line_w = display_width_par;
+ line_s = display_indent_par;
+ if (eqno_box == null) {
+ eqno_w = 0;
+ eqno_width = 0;
+ eqno_w2 = 0;
+ } else {
+ eqno_w = width(eqno_box);
+ eqno_width = eqno_w;
+ eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000);
+ subtype(eqno_box) = equation_number_list; /* new */
+ /* build_attribute_list(eqno_box); */ /* probably already set */
+ }
+ if (eq_w + eqno_w2 > line_w) {
+ /* The user can force the equation number to go on a separate line
+ by causing its width to be zero. */
+ if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w)
+ || (total_shrink[sfi] != 0)
+ || (total_shrink[fil] != 0)
+ || (total_shrink[fill] != 0)
+ || (total_shrink[filll] != 0))) {
+ list_ptr(eq_box) = null;
+ flush_node(eq_box);
+ eq_box = hpack(p, line_w - eqno_w2, exactly, -1);
+ subtype(eq_box) = equation_list; /* new */
+ build_attribute_list(eq_box);
+ } else {
+ eqno_w = 0;
+ if (eq_w > line_w) {
+ list_ptr(eq_box) = null;
+ flush_node(eq_box);
+ eq_box = hpack(p, line_w, exactly, -1);
+ subtype(eq_box) = equation_list; /* new */
+ build_attribute_list(eq_box);
+ }
+ }
+ eq_w = width(eq_box);
+ }
+ /* We try first to center the display without regard to the existence of
+ the equation number. If that would make it too close (where ``too close''
+ means that the space between display and equation number is less than the
+ width of the equation number), we either center it in the remaining space
+ or move it as far from the equation number as possible. The latter alternative
+ is taken only if the display begins with glue, since we assume that the
+ user put glue there to control the spacing precisely.
+ */
+ d = half(line_w - eq_w);
+ if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */
+ d = half(line_w - eq_w - eqno_w);
+ if (p != null)
+ if (!is_char_node(p))
+ if (type(p) == glue_node)
+ d = 0;
+ }
+
+ tail_append(new_penalty(pre_display_penalty_par));
+ if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */
+ g1 = above_display_skip_code;
+ g2 = below_display_skip_code;
+ } else {
+ g1 = above_display_short_skip_code;
+ g2 = below_display_short_skip_code;
+ }
+
+ /* If the equation number is set on a line by itself, either before or
+ after the formula, we append an infinite penalty so that no page break will
+ separate the display from its number; and we use the same size and
+ displacement for all three potential lines of the display, even though
+ `\.{\\parshape}' may specify them differently.
+ */
+ /* \.{\\leqno} on a forced single line due to |width=0| */
+ /* it follows that |type(a)=hlist_node| */
+
+ if (eqno_box && l && (eqno_w == 0)) {
+ /* if (math_direction_par==dir_TLT) { */
+ shift_amount(eqno_box) = 0;
+ /* } else { */
+ /* } */
+ append_to_vlist(eqno_box,lua_key_index(equation_number));
+ tail_append(new_penalty(inf_penalty));
+ } else {
+ inject_display_skip_before(g1);
+ }
+
+ if (eqno_w != 0) {
+ r = new_kern(line_w - eq_w - eqno_w - d);
+ if (l) {
+ if (swap_dir) {
+ if (math_direction_par==dir_TLT) {
+ /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */
+#ifdef DEBUG
+ fprintf(stderr, "\nDEBUG: CASE 1\n");
+#endif
+ s = new_kern(width(r) + eqno_w);
+ try_couple_nodes(eqno_box,r);
+ try_couple_nodes(r,eq_box);
+ try_couple_nodes(eq_box,s);
+ } else {
+ /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */
+#ifdef DEBUG
+ fprintf(stderr, "\nDEBUG: CASE 2\n");
+#endif
+ try_couple_nodes(eqno_box,r);
+ try_couple_nodes(r,eq_box);
+ }
+ } else {
+ if (math_direction_par==dir_TLT) {
+ /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */
+#ifdef DEBUG
+ fprintf(stderr, "\nDEBUG: CASE 3\n");
+#endif
+ s = new_kern(width(r) + eqno_w);
+ } else {
+ /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */
+#ifdef DEBUG
+ fprintf(stderr, "\nDEBUG: CASE 4\n");
+#endif
+ s = new_kern(width(r));
+ }
+ try_couple_nodes(eqno_box,r);
+ try_couple_nodes(r,eq_box);
+ try_couple_nodes(eq_box,s);
+ }
+ eq_box = eqno_box;
+ } else {
+ if (swap_dir) {
+ if (math_direction_par==dir_TLT) {
+ /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */
+#ifdef DEBUG
+ fprintf(stderr, "\nDEBUG: CASE 5\n");
+#endif
+ } else {
+ /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */
+#ifdef DEBUG
+ fprintf(stderr, "\nDEBUG: CASE 6\n");
+#endif
+ }
+ try_couple_nodes(eq_box,r);
+ try_couple_nodes(r,eqno_box);
+ } else {
+ if (math_direction_par==dir_TLT) {
+ /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */
+#ifdef DEBUG
+ fprintf(stderr, "\nDEBUG: CASE 7\n");
+#endif
+ s = new_kern(d);
+ } else {
+ /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */
+#ifdef DEBUG
+ fprintf(stderr, "\nDEBUG: CASE 8\n");
+#endif
+ s = new_kern(width(r) + eqno_w);
+ }
+ try_couple_nodes(s,eq_box);
+ try_couple_nodes(eq_box,r);
+ try_couple_nodes(r,eqno_box);
+ eq_box = s;
+ }
+ }
+ eq_box = hpack(eq_box, 0, additional, -1);
+ subtype(eq_box) = equation_list; /* new */
+ build_attribute_list(eq_box);
+ shift_amount(eq_box) = line_s;
+ } else {
+ shift_amount(eq_box) = line_s + d;
+ }
+/* check for prev: */
+ append_to_vlist(eq_box,lua_key_index(equation));
+
+ if ((eqno_box != null) && (eqno_w == 0) && !l) {
+ tail_append(new_penalty(inf_penalty));
+ /* if (math_direction_par==dir_TLT) { */
+ shift_amount(eqno_box) = line_s + line_w - eqno_width ;
+ /* } else { */
+ /* } */
+ append_to_vlist(eqno_box,lua_key_index(equation_number));
+ g2 = 0;
+ }
+ if (t != adjust_head) { /* migrating material comes after equation number */
+ vlink(tail) = vlink(adjust_head);
+ /* needs testing */
+ alink(adjust_tail) = alink(tail);
+ tail = t;
+ }
+ if (pre_t != pre_adjust_head) {
+ vlink(tail) = vlink(pre_adjust_head);
+ /* needs testing */
+ alink(pre_adjust_tail) = alink(tail);
+ tail = pre_t;
+ }
+ tail_append(new_penalty(post_display_penalty_par));
+ inject_display_skip_after(g2);
+ resume_after_display();
+}
+
+@ @c
+void after_math(void)
+{
+ int m; /* |mmode| or |-mmode| */
+ pointer p; /* the formula */
+ pointer a = null; /* box containing equation number */
+ boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */
+ m = mode;
+ p = fin_mlist(null); /* this pops the nest */
+ if (cur_cmd == math_shift_cs_cmd &&
+ (cur_chr == text_style || cur_chr == display_style)) {
+ you_cant();
+ }
+ if (mode == -m) { /* end of equation number */
+ if (cur_cmd == math_shift_cmd) {
+ check_second_math_shift();
+ } else {
+ check_display_math_end();
+ }
+ run_mlist_to_hlist(p, false, text_style);
+ a = hpack(vlink(temp_head), 0, additional, -1);
+ build_attribute_list(a);
+ unsave_math();
+ decr(save_ptr); /* now |cur_group=math_shift_group| */
+ assert(saved_type(0) == saved_eqno);
+ if (saved_value(0) == 1)
+ l = true;
+ m = mode;
+ p = fin_mlist(null);
+
+ }
+ if (m < 0) {
+ /* The |unsave| is done after everything else here; hence an appearance of
+ `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these
+ particular \.\$'s. This is consistent with the conventions of
+ `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the
+ space above that display.
+ */
+ if (cur_cmd == math_shift_cs_cmd) {
+ check_inline_math_end();
+ }
+ tail_append(new_math(math_surround_par, before));
+ /* begin mathskip code */
+ switch (math_skip_mode) {
+ case 0 :
+ /* obey mathsurround when zero glue */
+ if (! glue_is_zero(math_skip_par)) {
+ copy_glue_values(tail,math_skip_par);
+ surround(tail) = 0;
+ }
+ break ;
+ case 1 :
+ /* always left */
+ case 3 :
+ /* always both */
+ case 6 :
+ /* only when skip */
+ copy_glue_values(tail,math_skip_par);
+ surround(tail) = 0;
+ break ;
+ case 2 :
+ /* only right */
+ surround(tail) = 0;
+ break ;
+ case 4 :
+ /* ignore, obey marthsurround */
+ break ;
+ case 5:
+ /* all spacing disabled */
+ surround(tail) = 0;
+ break ;
+ }
+ /* end mathskip code */
+ if (dir_math_save) {
+ tail_append(new_dir(math_direction_par));
+ }
+ run_mlist_to_hlist(p, (mode > 0), text_style);
+ vlink(tail) = vlink(temp_head);
+ while (vlink(tail) != null) {
+ tail = vlink(tail);
+ }
+ if (dir_math_save) {
+ tail_append(new_dir(math_direction_par - dir_swap));
+ }
+ dir_math_save = false;
+ tail_append(new_math(math_surround_par, after));
+ /* begin mathskip code */
+ switch (math_skip_mode) {
+ case 0 :
+ /* obey mathsurround when zero glue */
+ if (! glue_is_zero(math_skip_par)) {
+ copy_glue_values(tail,math_skip_par);
+ surround(tail) = 0;
+ }
+ break ;
+ case 2 :
+ /* always right */
+ case 3 :
+ /* always both */
+ case 6 :
+ /* only when skip */
+ copy_glue_values(tail,math_skip_par);
+ surround(tail) = 0;
+ break ;
+ case 1 :
+ /* only left */
+ surround(tail) = 0;
+ break ;
+ case 4 :
+ /* ignore, obey marthsurround */
+ break ;
+ case 5:
+ /* all spacing disabled */
+ surround(tail) = 0;
+ break ;
+ }
+ /* end mathskip code */
+ space_factor_par = 1000;
+ unsave_math();
+ } else {
+ if (a == null) {
+ if (cur_cmd == math_shift_cmd) {
+ check_second_math_shift();
+ } else {
+ check_display_math_end();
+ }
+ }
+ run_mlist_to_hlist(p, false, display_style);
+ finish_displayed_math(l, a, vlink(temp_head));
+ }
+}
+
+@ When \.{\\halign} appears in a display, the alignment routines operate
+essentially as they do in vertical mode. Then the following program is
+activated, with |p| and |q| pointing to the beginning and end of the
+resulting list, and with |aux_save| holding the |prev_depth| value.
+
+@c
+void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth)
+{
+ do_assignments();
+ if (cur_cmd == math_shift_cmd) {
+ check_second_math_shift();
+ } else {
+ check_display_math_end();
+ }
+ pop_nest();
+ tail_append(new_penalty(pre_display_penalty_par));
+ inject_display_skip_before(above_display_skip_code);
+ vlink(tail) = p;
+ if (p != null)
+ tail = q;
+ tail_append(new_penalty(post_display_penalty_par));
+ inject_display_skip_after(below_display_skip_code);
+ cur_list.prev_depth_field = saved_prevdepth;
+ resume_after_display();
+}
+
+@ Interface to \.{\\Umath} and \.{\\mathstyle}
+
+@c
+void setup_math_style(void)
+{
+ pointer q;
+ tail_append(new_noad());
+ q = new_node(math_char_node, 0);
+ nucleus(tail) = q;
+ (void) scan_math_style(nucleus(tail), num_style(m_style));
+}
+
+@ @c
+void print_math_style(void)
+{
+ if (abs(mode) == mmode)
+ print_int(m_style);
+ else
+ print_int(-1);
+}
diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.w b/Build/source/texk/web2c/luatexdir/tex/texnodes.w index 17be906c8e6..9ba54f07432 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texnodes.w +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.w @@ -1,3755 +1,3775 @@ -% texnodes.w -% -% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. - -@ @c - -#include "ptexlib.h" -#include "lua/luatex-api.h" - -/* we can consider less mode sizes: 2 4 6 8 */ - -@ -This module started out using NDEBUG to trigger checking invalid node usage, -something that is needed because users can mess up nodes in Lua. At some point -that code was always enabled so it is now always on but still can be recognized -as additional code. And as the performance hit is close to zero so disabling -makes no sense, not even to make it configureable. There is a little more memory -used but that is neglectable compared to other memory usage. - -@c -#define MAX_CHAIN_SIZE 13 /* why not a bit larger */ -#define CHECK_NODE_USAGE 1 /* this triggers checking */ - -memory_word *volatile varmem = NULL; - -#ifdef CHECK_NODE_USAGE - char *varmem_sizes = NULL; -#endif - -halfword var_mem_max = 0; -halfword rover = 0; - -halfword free_chain[MAX_CHAIN_SIZE] = { null }; - -static int my_prealloc = 0; - -int fix_node_lists = 1; /* used in font and lang */ - -halfword slow_get_node(int s); /* defined below */ - -#define fake_node 100 -#define fake_node_size 2 -#define fake_node_name "fake" - -#define variable_node_size 2 - -/* core nodes */ - -const char *node_fields_list[] = { - "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign", - "glue_set", "head", NULL -}; -const char *node_fields_rule[] = { - "attr", "width", "depth", "height", "dir", "index", NULL -}; -const char *node_fields_insert[] = { - "attr", "cost", "depth", "height", "spec", "head", NULL -}; -const char *node_fields_mark[] = { - "attr", "class", "mark", NULL -}; -const char *node_fields_adjust[] = { - "attr", "head", NULL -}; -const char *node_fields_disc[] = { - "attr", "pre", "post", "replace", "penalty", NULL -}; -const char *node_fields_math[] = { - "attr", "surround", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_glue[] = { - "attr", "leader", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_kern[] = { - "attr", "kern", "expansion_factor", NULL -}; -const char *node_fields_penalty[] = { - "attr", "penalty", NULL -}; -const char *node_fields_unset[] = { - "attr", "width", "depth", "height", "dir", "shrink", "glue_order", - "glue_sign", "stretch", "span", "head", NULL -}; -const char *node_fields_margin_kern[] = { - "attr", "width", "glyph", NULL -}; -const char *node_fields_glyph[] = { - "attr", "char", "font", "lang", "left", "right", "uchyph", "components", - "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL -}; -const char *node_fields_inserting[] = { - "height", "last_ins_ptr", "best_ins_ptr", - "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_splitup[] = { - "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL -}; -const char *node_fields_attribute[] = { - "number", "value", NULL -}; -const char *node_fields_glue_spec[] = { - "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_attribute_list[] = { - NULL -}; -const char *node_fields_local_par[] = { - "attr", "pen_inter", "pen_broken", "dir", "box_left", "box_left_width", - "box_right", "box_right_width", NULL -}; -const char *node_fields_dir[] = { - "attr", "dir", "level", NULL -}; -const char *node_fields_boundary[] = { - "attr", "value", NULL -}; - -/* math nodes */ - -const char *node_fields_noad[] = { - "attr", "nucleus", "sub", "sup", NULL -}; - -const char *node_fields_style[] = { - "attr", "style", NULL -}; -const char *node_fields_choice[] = { - "attr", "display", "text", "script", "scriptscript", NULL -}; -const char *node_fields_radical[] = { - "attr", "nucleus", "sub", "sup", "left", "degree", "width", "options", NULL -}; -const char *node_fields_fraction[] = { - "attr", "width", "num", "denom", "left", "right", "middle", "options", NULL -}; -const char *node_fields_accent[] = { - "attr", "nucleus", "sub", "sup", "accent", "bot_accent", "top_accent", - "overlay_accent", "fraction", NULL -}; -const char *node_fields_fence[] = { - "attr", "delim", "italic", "height", "depth", "options", "class", NULL -}; -const char *node_fields_math_char[] = { - "attr", "fam", "char", NULL -}; -const char *node_fields_sub_box[] = { - "attr", "head", NULL -}; -const char *node_fields_sub_mlist[] = { - "attr", "head", NULL -}; -const char *node_fields_math_text_char[] = { - "attr", "fam", "char", NULL -}; -const char *node_fields_delim[] = { - "attr", "small_fam", "small_char", "large_fam", "large_char", NULL -}; - -/* whatsit nodes */ - -const char *node_fields_whatsit_open[] = { - "attr", "stream", "name", "area", "ext", NULL -}; -const char *node_fields_whatsit_write[] = { - "attr", "stream", "data", NULL -}; -const char *node_fields_whatsit_close[] = { - "attr", "stream", NULL -}; -const char *node_fields_whatsit_special[] = { - "attr", "data", NULL -}; -const char *node_fields_whatsit_save_pos[] = { - "attr", NULL -}; -const char *node_fields_whatsit_late_lua[] = { - "attr", "reg", "data", "name", "string", NULL -}; -const char *node_fields_whatsit_user_defined[] = { - "attr", "user_id", "type", "value", NULL -}; - -/* pdf backend whatsit nodes */ - -const char *node_fields_whatsit_pdf_literal[] = { - "attr", "mode", "data", NULL -}; -const char *node_fields_whatsit_pdf_refobj[] = { - "attr", "objnum", NULL -}; -const char *node_fields_whatsit_pdf_annot[] = { - "attr", "width", "depth", "height", "objnum", "data", NULL -}; -const char *node_fields_whatsit_pdf_start_link[] = { - "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL -}; -const char *node_fields_whatsit_pdf_end_link[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_dest[] = { - "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type", - "xyz_zoom", "objnum", NULL -}; -const char *node_fields_whatsit_pdf_action[] = { - "action_type", "named_id", "action_id", "file", "new_window", "data", NULL -}; -const char *node_fields_whatsit_pdf_thread[] = { - "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL -}; -const char *node_fields_whatsit_pdf_start_thread[] = { - "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL -}; -const char *node_fields_whatsit_pdf_end_thread[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_colorstack[] = { - "attr", "stack", "cmd", "data", NULL -}; -const char *node_fields_whatsit_pdf_setmatrix[] = { - "attr", "data", NULL -}; -const char *node_fields_whatsit_pdf_save[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_restore[] = { - "attr", NULL -}; - -/* subtypes */ - -const char *node_subtypes_glue[] = { - "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip", - "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip", - "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip", - "mathskip", "thinmuskip", "medmuskip", "thickmuskip", NULL -}; -const char *node_subtypes_mathglue[] = { /* 98+ */ - "conditionalmathskip", "muglue", NULL -}; -const char *node_subtypes_leader[] = { /* 100+ */ - "leaders", "cleaders", "xleaders", "gleaders", NULL -}; -const char *node_subtypes_fill[] = { - "stretch", "fi", "fil", "fill", "filll", NULL -}; -const char *node_subtypes_boundary[] = { - "cancel", "user", "protrusion", "word", NULL -}; -const char *node_subtypes_penalty[] = { - "userpenalty", NULL -}; -const char *node_subtypes_kern[] = { - "fontkern", "userkern", "accentkern", "italiccorrection", NULL -}; -const char *node_subtypes_rule[] = { - "normal", "box", "image", "empty", "user", "over", "under", "fraction", "radical", NULL -}; -const char *node_subtypes_glyph[] = { - "character", "glyph", "ligature", "ghost", "left", "right", NULL -}; -const char *node_subtypes_disc[] = { - "discretionary", "explicit", "automatic", "regular", "first", "second", NULL -}; -const char *node_subtypes_marginkern[] = { - "left", "right", NULL -}; -const char *node_subtypes_list[] = { - "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL -}; -const char *node_subtypes_adjust[] = { - "normal", "pre", NULL -}; -const char *node_subtypes_math[] = { - "beginmath", "endmath", NULL -}; -const char *node_subtypes_noad[] = { - "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close", - "punct", "inner", "under", "over", "vcenter", NULL -}; -const char *node_subtypes_radical[] = { - "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder", - "udelimiterover", NULL -}; -const char *node_subtypes_accent[] = { - "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL, -}; -const char *node_subtypes_fence[] = { - "unset", "left", "middle", "right", NULL -}; - -node_info node_data[] = { /* the last entry in a row is the etex number */ - { hlist_node, box_node_size, node_fields_list, "hlist", 1 }, - { vlist_node, box_node_size, node_fields_list, "vlist", 2 }, - { rule_node, rule_node_size, node_fields_rule, "rule", 3 }, - { ins_node, ins_node_size, node_fields_insert, "ins", 4 }, - { mark_node, mark_node_size, node_fields_mark, "mark", 5 }, - { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 }, - { boundary_node, boundary_node_size, node_fields_boundary, "boundary", -1 }, - { disc_node, disc_node_size, node_fields_disc, "disc", 8 }, - { whatsit_node, -1, NULL, "whatsit", 9 }, - { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 }, - { dir_node, dir_node_size, node_fields_dir, "dir", -1 }, - { math_node, math_node_size, node_fields_math, "math", 10 }, - { glue_node, glue_node_size, node_fields_glue, "glue", 11 }, - { kern_node, kern_node_size, node_fields_kern, "kern", 12 }, - { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 }, - { unset_node, box_node_size, node_fields_unset, "unset", 14 }, - { style_node, style_node_size, node_fields_style, "style", 15 }, - { choice_node, style_node_size, node_fields_choice, "choice", 15 }, - { simple_noad, noad_size, node_fields_noad, "noad", 15 }, - { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 }, - { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 }, - { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 }, - { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 }, - { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 }, - { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 }, - { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 }, - { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 }, - { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 }, - { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 }, - { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 }, - { align_record_node, box_node_size, NULL, "align_record", -1 }, - { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 }, - { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 }, - { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 }, - { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 }, - { expr_node, expr_node_size, NULL, "expr_stack", -1 }, - { nesting_node, nesting_node_size, NULL, "nested_list", -1 }, - { span_node, span_node_size, NULL, "span", -1 }, - { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 }, - { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 }, - { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 }, - { temp_node, temp_node_size, NULL, "temp", -1 }, - { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 }, - { movement_node, movement_node_size, NULL, "movement_stack", -1 }, - { if_node, if_node_size, NULL, "if_stack", -1 }, - { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 }, - { hyphenated_node, active_node_size, NULL, "hyphenated", -1 }, - { delta_node, delta_node_size, NULL, "delta", -1 }, - { passive_node, passive_node_size, NULL, "passive", -1 }, - { shape_node, variable_node_size, NULL, "shape", -1 }, - { -1, -1, NULL, NULL, -1 }, -}; - -const char *node_subtypes_pdf_destination[] = { - "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL -}; -const char *node_subtypes_pdf_literal[] = { - "origin", "page", "direct", NULL -}; - -node_info whatsit_node_data[] = { - { open_node, open_node_size, node_fields_whatsit_open, "open", -1 }, - { write_node, write_node_size, node_fields_whatsit_write, "write", -1 }, - { close_node, close_node_size, node_fields_whatsit_close, "close", -1 }, - { special_node, special_node_size, node_fields_whatsit_special, "special", -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 }, - { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 }, - { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - /* here starts the dvi backend section, todo: a separate list */ - /* nothing for dvi */ - /* here starts the pdf backend section, todo: a separate list */ - { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 }, - { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 }, - { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 }, - { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 }, - { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 }, - { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 }, - { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 }, - { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 }, - { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 }, - { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 }, - { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 }, - { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 }, - { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 }, - { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 }, - { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 }, - { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 }, - /* done */ - { -1, -1, NULL, NULL, -1 }, -}; - -#define last_whatsit_node pdf_restore_node - -@ -When we copy a node list, there are several possibilities: we do the same as a new node, -we copy the entry to the table in properties (a reference), we do a deep copy of a table -in the properties, we create a new table and give it the original one as a metatable. -After some experiments (that also included timing) with these scenarios I decided that a -deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable -variant were both ok, although the second ons is slower. The most important aspect to keep -in mind is that references to other nodes in properties no longer can be valid for that -copy. We could use two tables (one unique and one shared) or metatables but that only -complicates matters. - -When defining a new node, we could already allocate a table but it is rather easy to do -that at the lua end e.g. using a metatable __index method. That way it is under macro -package control. - -When deleting a node, we could keep the slot (e.g. setting it to false) but it could make -memory consumption raise unneeded when we have temporary large node lists and after that -only small lists. - -So, in the end this is what we ended up with. For the record, I also experimented with the -following: - -- copy attributes to the properties so that we have fast access at the lua end: in the end - the overhead is not compensated by speed and convenience, in fact, attributes are not - that slow when it comes to accessing them - -- a bitset in the node but again the gain compared to attributes is neglectable and it also - demands a pretty string agreement over what bit represents what, and this is unlikely to - succeed in the tex community (I could use it for font handling, which is cross package, - but decided that it doesn't pay off - -In case one wonders why properties make sense then, well, it is not so much speed that we -gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and -this mechanism makes sure that properties are cleaned up when a node is freed. Also, the -advantage of a more or less global properties table is that we stay at the lua end. An -alternative is to store a reference in the node itself but that is complicated by the fact -that the register has some limitations (no numeric keys) and we also don't want to mess with -it too much. - -@c -int lua_properties_level = 0 ; /* can be private */ -int lua_properties_enabled = 0 ; -int lua_properties_use_metatable = 0 ; - -@ -We keep track of nesting so that we don't oveflow the stack, and, what is more -important, don't keep resolving the registry index. - -@c -#define lua_properties_push do { \ - if (lua_properties_enabled) { \ - lua_properties_level = lua_properties_level + 1 ; \ - if (lua_properties_level == 1) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - } \ - } \ -} while(0) - -#define lua_properties_pop do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 1) \ - lua_pop(Luas,1); \ - lua_properties_level = lua_properties_level - 1 ; \ - } \ -} while(0) - -/* No setting is needed: */ - -#define lua_properties_set(target) do { \ -} while(0) - -/* Resetting boils down to nilling. */ - -#define lua_properties_reset(target) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_pushnil(Luas); \ - lua_rawseti(Luas,-2,target); \ - lua_pop(Luas,1); \ - } else { \ - lua_pushnil(Luas); \ - lua_rawseti(Luas,-2,target); \ - } \ - } \ -} while(0) - -/* - For a moment I considered supporting all kind of data types but in practice - that makes no sense. So we stick to a cheap shallow copy with as option a - metatable. Btw, a deep copy would look like this: - - static void copy_lua_table(lua_State* L, int index) { - lua_newtable(L); - lua_pushnil(L); - while(lua_next(L, index-1) != 0) { - lua_pushvalue(L, -2); - lua_insert(L, -2); - if (lua_type(L,-1)==LUA_TTABLE) - copy_lua_table(L,-1); - lua_settable(L, -4); - } - lua_pop(L,1); - } - - #define lua_properties_copy(target, source) do { \ - if (lua_properties_enabled) { \ - lua_pushinteger(Luas,source); \ - lua_rawget(Luas,-2); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - copy_lua_table(Luas,-1); \ - lua_pushinteger(Luas,target); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas,-3); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } while(0) - -*/ - -/* isn't there a faster way to metatable? */ - -/* - -#define lua_properties_copy(target,source) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setfield(Luas,-2,"__index"); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - lua_pop(Luas,1); \ - } else { \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setfield(Luas,-2,"__index"); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } \ -} while(0) - -*/ - -/* - A simple testrun on many pages of dumb text shows 1% gain (of course it depends - on how properties are used but some other tests confirm it). -*/ - -#define lua_properties_copy(target,source) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_push_string_by_name(Luas,__index); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas, -3); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - lua_pop(Luas,1); \ - } else { \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_push_string_by_name(Luas,__index); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas, -3); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } \ -} while(0) - -/* Here end the property handlers. */ - -@ @c -int valid_node(halfword p) -{ - if (p > my_prealloc && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - if (varmem_sizes[p] > 0) { - return 1; - } -#else - return 1; -#endif - } - return 0; -} - -@ @c -static int test_count = 1; - -#define dorangetest(a,b,c) do { \ - if (!(b>=0 && b<c)) { \ - fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \ - (int)a, (int)b, (int)c, __LINE__,test_count); \ - confusion("node range test failed"); \ - } } while (0) - -#define dotest(a,b,c) do { \ - if (b!=c) { \ - fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \ - (int)a, (int)b, (int)c, __LINE__,test_count); \ - confusion("node test failed"); \ - } } while (0) - -#define check_action_ref(a) { dorangetest(p,a,var_mem_max); } -#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); } -#define check_token_ref(a) { \ - if (type(p) == whatsit_node) { \ - formatted_error("nodes","fuzzy token cleanup in whatsit node with id %i and subtype %i",type(p),subtype(p)); \ - } else { \ - formatted_error("nodes","fuzzy token cleanup in node with id %i",type(p)); \ - } \ -} - -#ifdef CHECK_NODE_USAGE - -static void check_static_node_mem(void) -{ - dotest(zero_glue, width(zero_glue), 0); - dotest(zero_glue, type(zero_glue), glue_spec_node); - dotest(zero_glue, vlink(zero_glue), null); - dotest(zero_glue, stretch(zero_glue), 0); - dotest(zero_glue, stretch_order(zero_glue), normal); - dotest(zero_glue, shrink(zero_glue), 0); - dotest(zero_glue, shrink_order(zero_glue), normal); - - dotest(sfi_glue, width(sfi_glue), 0); - dotest(sfi_glue, type(sfi_glue), glue_spec_node); - dotest(sfi_glue, vlink(sfi_glue), null); - dotest(sfi_glue, stretch(sfi_glue), 0); - dotest(sfi_glue, stretch_order(sfi_glue), sfi); - dotest(sfi_glue, shrink(sfi_glue), 0); - dotest(sfi_glue, shrink_order(sfi_glue), normal); - - dotest(fil_glue, width(fil_glue), 0); - dotest(fil_glue, type(fil_glue), glue_spec_node); - dotest(fil_glue, vlink(fil_glue), null); - dotest(fil_glue, stretch(fil_glue), unity); - dotest(fil_glue, stretch_order(fil_glue), fil); - dotest(fil_glue, shrink(fil_glue), 0); - dotest(fil_glue, shrink_order(fil_glue), normal); - - dotest(fill_glue, width(fill_glue), 0); - dotest(fill_glue, type(fill_glue), glue_spec_node); - dotest(fill_glue, vlink(fill_glue), null); - dotest(fill_glue, stretch(fill_glue), unity); - dotest(fill_glue, stretch_order(fill_glue), fill); - dotest(fill_glue, shrink(fill_glue), 0); - dotest(fill_glue, shrink_order(fill_glue), normal); - - dotest(ss_glue, width(ss_glue), 0); - dotest(ss_glue, type(ss_glue), glue_spec_node); - dotest(ss_glue, vlink(ss_glue), null); - dotest(ss_glue, stretch(ss_glue), unity); - dotest(ss_glue, stretch_order(ss_glue), fil); - dotest(ss_glue, shrink(ss_glue), unity); - dotest(ss_glue, shrink_order(ss_glue), fil); - - dotest(fil_neg_glue, width(fil_neg_glue), 0); - dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node); - dotest(fil_neg_glue, vlink(fil_neg_glue), null); - dotest(fil_neg_glue, stretch(fil_neg_glue), -unity); - dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil); - dotest(fil_neg_glue, shrink(fil_neg_glue), 0); - dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal); -} - -static void node_mem_dump(halfword p) -{ - halfword r; - for (r = my_prealloc + 1; r < var_mem_max; r++) { - if (vlink(r) == p) { - halfword s = r; - while (s > my_prealloc && varmem_sizes[s] == 0) { - s--; - } - if (s != null - && s != my_prealloc - && s != var_mem_max - && (r - s) < get_node_size(type(s), subtype(s)) - && alink(s) != p) { - if (type(s) == disc_node) { - fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", - get_node_name(type(s), subtype(s)), (int) s, - (int) vlink(s), (int) alink(s)); - fprintf(stdout, "pre_break(%d,%d,%d), ", - (int) vlink_pre_break(s), (int) tlink(pre_break(s)), - (int) alink(pre_break(s))); - fprintf(stdout, "post_break(%d,%d,%d), ", - (int) vlink_post_break(s), - (int) tlink(post_break(s)), - (int) alink(post_break(s))); - fprintf(stdout, "no_break(%d,%d,%d)", - (int) vlink_no_break(s), (int) tlink(no_break(s)), - (int) alink(no_break(s))); - fprintf(stdout, "\n"); - } else { - if (vlink(s) == p - || (type(s) == glyph_node && lig_ptr (s) == p) - || (type(s) == vlist_node && list_ptr(s) == p) - || (type(s) == hlist_node && list_ptr(s) == p) - || (type(s) == unset_node && list_ptr(s) == p) - || (type(s) == ins_node && ins_ptr (s) == p) - ) { - fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", - get_node_name(type(s), subtype(s)), (int) s, - (int) vlink(s), (int) alink(s)); - if (type(s) == glyph_node) { - fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); - } else if (type(s) == vlist_node || type(s) == hlist_node) { - fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); - } - fprintf(stdout, "\n"); - } else { - if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) { - fprintf(stdout, " pointed to from %s node %d\n", - get_node_name(type(s), subtype(s)), (int) s); - } - } - } - } - } - } -} - -#endif - -static int free_error(halfword p) -{ - if (p > my_prealloc && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - int i; - if (varmem_sizes[p] == 0) { - check_static_node_mem(); - for (i = (my_prealloc + 1); i < var_mem_max; i++) { - if (varmem_sizes[i] > 0) { - check_node(i); - } - } - test_count++; - if (type(p) == glyph_node) { - formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p); - } else { - formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); - } - node_mem_dump(p); - return 1; - } -#endif - } else { - formatted_error("nodes", "attempt to free an impossible node %d", (int) p); - return 1; - } - return 0; -} - -@ @c -static int copy_error(halfword p) -{ - if (p >= 0 && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - if (p > my_prealloc && varmem_sizes[p] == 0) { - if (type(p) == glyph_node) { - formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p); - } else { - formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); - } - return 1; - } -#endif - } else { - formatted_error("nodes", "attempt to copy an impossible node %d", (int) p); - return 1; - } - return 0; -} - -@ @c -halfword new_node(int i, int j) -{ - int s = get_node_size(i, j); - halfword n = get_node(s); - /* - It should be possible to do this memset at |free_node()|. - - Both type() and subtype() will be set below, and vlink() is - set to null by |get_node()|, so we can do we clearing one - word less than |s| - */ - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1))); - switch (i) { - case glyph_node: - init_lang_data(n); - break; - case hlist_node: - case vlist_node: - box_dir(n) = -1; - break; - case disc_node: - pre_break(n) = pre_break_head(n); - type(pre_break(n)) = nesting_node; - subtype(pre_break(n)) = pre_break_head(0); - post_break(n) = post_break_head(n); - type(post_break(n)) = nesting_node; - subtype(post_break(n)) = post_break_head(0); - no_break(n) = no_break_head(n); - type(no_break(n)) = nesting_node; - subtype(no_break(n)) = no_break_head(0); - break; - case rule_node: - depth(n) = null_flag; - height(n) = null_flag; - width(n) = null_flag; - rule_dir(n) = -1; - rule_index(n) = 0; - rule_transform(n) = 0; - break; - case whatsit_node: - if (j == open_node) { - open_name(n) = get_nullstr(); - open_area(n) = open_name(n); - open_ext(n) = open_name(n); - } - break; - case unset_node: - width(n) = null_flag; - break; - case pseudo_line_node: - case shape_node: - /* this is a trick that makes |pseudo_files| slightly slower, - but the overall allocation faster then an explicit test - at the top of |new_node()|. - */ - if (j>0) { - free_node(n, variable_node_size); - n = slow_get_node(j); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1))); - } - break; - default: - break; - } - if (synctex_par) { - /* 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: - 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); - /* lua_properties_set */ - } - type(n) = (quarterword) i; - subtype(n) = (quarterword) j; - return n; -} - -halfword raw_glyph_node(void) -{ - register halfword n = get_node(glyph_node_size); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); - type(n) = glyph_node; - subtype(n) = 0; - return n; -} - -halfword new_glyph_node(void) -{ - register halfword n = get_node(glyph_node_size); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); - type(n) = glyph_node; - subtype(n) = 0; - build_attribute_list(n); - /* lua_properties_set */ - return n; -} - -@ makes a duplicate of the node list that starts at |p| and returns a -pointer to the new list - -@c -halfword do_copy_node_list(halfword p, halfword end) -{ - halfword q = null; /* previous position in new list */ - halfword h = null; /* head of the list */ - register halfword s ; - lua_properties_push; /* saves stack and time */ - while (p != end) { - s = copy_node(p); - if (h == null) { - h = s; - } else { - couple_nodes(q, s); - } - q = s; - p = vlink(p); - } - lua_properties_pop; /* saves stack and time */ - return h; -} - -halfword copy_node_list(halfword p) -{ - return do_copy_node_list(p, null); -} - -#define copy_sub_list(target,source) do { \ - if (source != null) { \ - s = do_copy_node_list(source, null); \ - target = s; \ - } else { \ - target = null; \ - } \ - } while (0) - -#define copy_sub_node(target,source) do { \ - if (source != null) { \ - s = copy_node(source); \ - target = s ; \ - } else { \ - target = null; \ - } \ -} while (0) - -@ make a dupe of a single node - -@c -static void copy_node_wrapup_core(halfword p, halfword r) -{ - halfword s ; - switch (subtype(p)) { - case write_node: - case special_node: - add_token_ref(write_tokens(p)); - break; - case late_lua_node: - copy_late_lua(r, p); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - add_node_attr_ref(user_node_value(p)); - break; - case 'l': - copy_user_lua(r, p); - break; - case 'n': - s = copy_node_list(user_node_value(p)); - user_node_value(r) = s; - break; - case 's': - /* |add_string_ref(user_node_value(p));| */ - break; - case 't': - add_token_ref(user_node_value(p)); - break; - } - break; - default: - break ; - } -} - -void copy_node_wrapup_dvi(halfword p, halfword r) -{ -} - -void copy_node_wrapup_pdf(halfword p, halfword r) -{ - switch(subtype(p)) { - case pdf_literal_node: - copy_pdf_literal(r, p); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - add_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - add_token_ref(pdf_setmatrix_data(p)); - break; - case pdf_annot_node: - add_token_ref(pdf_annot_data(p)); - break; - case pdf_start_link_node: - if (pdf_link_attr(r) != null) - add_token_ref(pdf_link_attr(r)); - add_action_ref(pdf_link_action(r)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - add_token_ref(pdf_dest_id(p)); - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - add_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - add_token_ref(pdf_thread_attr(p)); - break; - default: - break; - } -} - -halfword copy_node(const halfword p) -{ - halfword r; /* current node being fabricated for new list */ - halfword w; /* whatsit subtype */ - halfword t; /* type of node */ - register halfword s; /* a helper variable for copying into variable mem */ - register int i; - if (copy_error(p)) { - r = new_node(temp_node, 0); - return r; - } - t = type(p); - i = get_node_size(t,subtype(p)); - r = get_node(i); - - (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i)); - - /* possible speedup: */ - /* - if t == glue_spec) { - return r; - } - */ - - if (synctex_par) { - /* handle synctex extension */ - switch (t) { - case math_node: - synctex_tag_math(r) = cur_input.synctex_tag_field; - synctex_line_math(r) = line; - break; - case kern_node: - synctex_tag_kern(r) = cur_input.synctex_tag_field; - synctex_line_kern(r) = line; - break; - } - } - if (nodetype_has_attributes(t)) { - add_node_attr_ref(node_attr(p)); - alink(r) = null; - lua_properties_copy(r,p); - } - vlink(r) = null; - - switch (t) { - case glyph_node: - copy_sub_list(lig_ptr(r),lig_ptr(p)) ; - break; - case glue_node: - copy_sub_list(leader_ptr(r),leader_ptr(p)) ; - break; - case hlist_node: - case vlist_node: - case unset_node: - copy_sub_list(list_ptr(r),list_ptr(p)) ; - break; - case disc_node: - pre_break(r) = pre_break_head(r); - if (vlink_pre_break(p) != null) { - s = copy_node_list(vlink_pre_break(p)); - alink(s) = pre_break(r); - tlink_pre_break(r) = tail_of_list(s); - vlink_pre_break(r) = s; - } else { - assert(tlink(pre_break(r)) == null); - } - post_break(r) = post_break_head(r); - if (vlink_post_break(p) != null) { - s = copy_node_list(vlink_post_break(p)); - alink(s) = post_break(r); - tlink_post_break(r) = tail_of_list(s); - vlink_post_break(r) = s; - } else { - assert(tlink_post_break(r) == null); - } - no_break(r) = no_break_head(r); - if (vlink(no_break(p)) != null) { - s = copy_node_list(vlink_no_break(p)); - alink(s) = no_break(r); - tlink_no_break(r) = tail_of_list(s); - vlink_no_break(r) = s; - } else { - assert(tlink_no_break(r) == null); - } - break; - case math_node: - break; - case ins_node: - copy_sub_list(ins_ptr(r),ins_ptr(p)) ; - break; - case margin_kern_node: - copy_sub_node(margin_char(r),margin_char(p)); - break; - case mark_node: - add_token_ref(mark_ptr(p)); - break; - case adjust_node: - copy_sub_list(adjust_ptr(r),adjust_ptr(p)); - break; - case choice_node: - copy_sub_list(display_mlist(r),display_mlist(p)) ; - copy_sub_list(text_mlist(r),text_mlist(p)) ; - copy_sub_list(script_mlist(r),script_mlist(p)) ; - copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ; - break; - case simple_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - break; - case radical_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - copy_sub_node(left_delimiter(r),left_delimiter(p)) ; - copy_sub_list(degree(r),degree(p)) ; - break; - case accent_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ; - copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ; - copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ; - break; - case fence_noad: - copy_sub_node(delimiter(r),delimiter(p)) ; - break; - case sub_box_node: - case sub_mlist_node: - copy_sub_list(math_list(r),math_list(p)) ; - break; - case fraction_noad: - copy_sub_list(numerator(r),numerator(p)) ; - copy_sub_list(denominator(r),denominator(p)) ; - copy_sub_node(left_delimiter(r),left_delimiter(p)) ; - copy_sub_node(right_delimiter(r),right_delimiter(p)) ; - break; - case glue_spec_node: - case dir_node: - case local_par_node: - case boundary_node: - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - copy_node_wrapup_pdf(p,r); - } else if (w >= backend_first_dvi_whatsit) { - copy_node_wrapup_dvi(p,r); - } else { - copy_node_wrapup_core(p,r); - } - break; - } - return r; -} - -/* x */ - -#define free_sub_list(source) if (source != null) flush_node_list(source); -#define free_sub_node(source) if (source != null) flush_node(source); - -@ @c - -static void flush_node_wrapup_core(halfword p) -{ - switch (subtype(p)) { - case open_node: - case write_node: - case close_node: - case save_pos_node: - break; - case special_node: - delete_token_ref(write_tokens(p)); - break; - case late_lua_node: - free_late_lua(p); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - delete_attribute_ref(user_node_value(p)); - break; - case 'd': - break; - case 'l': - free_user_lua(user_node_value(p)); - break; - case 'n': - flush_node_list(user_node_value(p)); - break; - case 's': - /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */ - break; - case 't': - delete_token_ref(user_node_value(p)); - break; - default: - { - const char *hlp[] = { - "The type of the value in a user defined whatsit node should be one", - "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),", - "or 't' (tokenlist). Yours has an unknown type, and therefore I don't", - "know how to free the node's value. A memory leak may result.", - NULL - }; - tex_error("Unidentified user defined whatsit", hlp); - } - break; - } - break; - } -} - -void flush_node_wrapup_dvi(halfword p) -{ -} - -void flush_node_wrapup_pdf(halfword p) -{ - switch(subtype(p)) { - case pdf_save_node: - case pdf_restore_node: - case pdf_refobj_node: - case pdf_end_link_node: - case pdf_end_thread_node: - break; - case pdf_literal_node: - free_pdf_literal(p); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - delete_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - delete_token_ref(pdf_setmatrix_data(p)); - break; - case pdf_annot_node: - delete_token_ref(pdf_annot_data(p)); - break; - case pdf_link_data_node: - break; - case pdf_start_link_node: - if (pdf_link_attr(p) != null) - delete_token_ref(pdf_link_attr(p)); - delete_action_ref(pdf_link_action(p)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - delete_token_ref(pdf_dest_id(p)); - break; - case pdf_action_node: - if (pdf_action_type(p) == pdf_action_user) { - delete_token_ref(pdf_action_tokens(p)); - } else { - if (pdf_action_file(p) != null) - delete_token_ref(pdf_action_file(p)); - if (pdf_action_type(p) == pdf_action_page) - delete_token_ref(pdf_action_tokens(p)); - else if (pdf_action_named_id(p) > 0) - delete_token_ref(pdf_action_id(p)); - } - break; - case pdf_thread_data_node: - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - delete_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - delete_token_ref(pdf_thread_attr(p)); - break; - } -} - -void flush_node(halfword p) -{ - halfword w; - if (p == null) /* legal, but no-op */ - return; - if (free_error(p)) - return; - switch (type(p)) { - case glyph_node: - free_sub_list(lig_ptr(p)); - break; - case glue_node: - free_sub_list(leader_ptr(p)); - break; - case hlist_node: - case vlist_node: - case unset_node: - free_sub_list(list_ptr(p)); - break; - case disc_node: - /* watch the start at temp node hack */ - free_sub_list(vlink(pre_break(p))); - free_sub_list(vlink(post_break(p))); - free_sub_list(vlink(no_break(p))); - break; - case rule_node: - case kern_node: - case penalty_node: - case math_node: - break; - case glue_spec_node: - /* this allows free-ing of lua-allocated glue specs */ -//if (valid_node(p)) { -// free_node(p, subtype(p)); -//} -// return ; - break ; - case dir_node: - case local_par_node: - case boundary_node: - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - flush_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - flush_node_wrapup_dvi(p); - } else { - flush_node_wrapup_core(p); - } - break; - case ins_node: - flush_node_list(ins_ptr(p)); - break; - case margin_kern_node: - flush_node(margin_char(p)); - break; - case mark_node: - delete_token_ref(mark_ptr(p)); - break; - case adjust_node: - flush_node_list(adjust_ptr(p)); - break; - case style_node: /* nothing to do */ - break; - case choice_node: - free_sub_list(display_mlist(p)); - free_sub_list(text_mlist(p)); - free_sub_list(script_mlist(p)); - free_sub_list(script_script_mlist(p)); - break; - case simple_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - break; - case radical_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - free_sub_node(left_delimiter(p)); - free_sub_list(degree(p)); - break; - case accent_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - free_sub_list(top_accent_chr(p)); - free_sub_list(bot_accent_chr(p)); - free_sub_list(overlay_accent_chr(p)); - break; - case fence_noad: - free_sub_list(delimiter(p)); - break; - case delim_node: /* nothing to do */ - case math_char_node: - case math_text_char_node: - break; - case sub_box_node: - case sub_mlist_node: - free_sub_list(math_list(p)); - break; - case fraction_noad: - free_sub_list(numerator(p)); - free_sub_list(denominator(p)); - free_sub_node(left_delimiter(p)); - free_sub_node(right_delimiter(p)); - break; - case pseudo_file_node: - free_sub_list(pseudo_lines(p)); - break; - case pseudo_line_node: - case shape_node: - free_node(p, subtype(p)); - return; - break; - case align_stack_node: - case span_node: - case movement_node: - case if_node: - case nesting_node: - case unhyphenated_node: - case hyphenated_node: - case delta_node: - case passive_node: - case inserting_node: - case split_up_node: - case expr_node: - case attribute_node: - case attribute_list_node: - case temp_node: - break; - default: - formatted_error("nodes","flushing weird node type %d", type(p)); - return; - } - if (nodetype_has_attributes(type(p))) { - delete_attribute_ref(node_attr(p)); - lua_properties_reset(p); - } - free_node(p, get_node_size(type(p), subtype(p))); - return; -} - -@ @c -void flush_node_list(halfword pp) -{ /* erase list of nodes starting at |p| */ - register halfword p = pp; - if (p == null) /* legal, but no-op */ - return; - if (free_error(p)) - return; - lua_properties_push; /* saves stack and time */ - while (p != null) { - register halfword q = vlink(p); - flush_node(p); - p = q; - } - lua_properties_pop; /* saves stack and time */ -} - -@ @c -static void check_node_wrapup_core(halfword p) -{ - switch (subtype(p)) { - /* frontend code */ - case special_node: - check_token_ref(write_tokens(p)); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - check_attribute_ref(user_node_value(p)); - break; - case 't': - check_token_ref(user_node_value(p)); - break; - case 'n': - dorangetest(p, user_node_value(p), var_mem_max); - break; - case 's': - case 'd': - break; - default: - confusion("unknown user node type"); - break; - } - break; - case open_node: - case write_node: - case close_node: - case save_pos_node: - break; - } -} - -void check_node_wrapup_dvi(halfword p) -{ -} - -void check_node_wrapup_pdf(halfword p) -{ - switch (subtype(p)) { - case pdf_literal_node: - if (pdf_literal_type(p) == normal) - check_token_ref(pdf_literal_data(p)); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - check_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - check_token_ref(pdf_setmatrix_data(p)); - break; - case late_lua_node: - if (late_lua_name(p) > 0) - check_token_ref(late_lua_name(p)); - if (late_lua_type(p) == normal) - check_token_ref(late_lua_data(p)); - break; - case pdf_annot_node: - check_token_ref(pdf_annot_data(p)); - break; - case pdf_start_link_node: - if (pdf_link_attr(p) != null) - check_token_ref(pdf_link_attr(p)); - check_action_ref(pdf_link_action(p)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - check_token_ref(pdf_dest_id(p)); - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - check_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - check_token_ref(pdf_thread_attr(p)); - break; - case pdf_save_node: - case pdf_restore_node: - case pdf_refobj_node: - case pdf_end_link_node: - case pdf_end_thread_node: - break; - default: - confusion("wrapup pdf nodes"); - break; - } -} - -void check_node(halfword p) -{ - halfword w ; - switch (type(p)) { - case glyph_node: - dorangetest(p, lig_ptr(p), var_mem_max); - break; - case glue_node: - dorangetest(p, leader_ptr(p), var_mem_max); - break; - case hlist_node: - case vlist_node: - case unset_node: - case align_record_node: - dorangetest(p, list_ptr(p), var_mem_max); - break; - case ins_node: - dorangetest(p, ins_ptr(p), var_mem_max); - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - check_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - check_node_wrapup_dvi(p); - } else { - check_node_wrapup_core(p); - } - break; - case margin_kern_node: - check_node(margin_char(p)); - break; - case math_node: - break; - case disc_node: - dorangetest(p, vlink(pre_break(p)), var_mem_max); - dorangetest(p, vlink(post_break(p)), var_mem_max); - dorangetest(p, vlink(no_break(p)), var_mem_max); - break; - case adjust_node: - dorangetest(p, adjust_ptr(p), var_mem_max); - break; - case pseudo_file_node: - dorangetest(p, pseudo_lines(p), var_mem_max); - break; - case pseudo_line_node: - case shape_node: - break; - case choice_node: - dorangetest(p, display_mlist(p), var_mem_max); - dorangetest(p, text_mlist(p), var_mem_max); - dorangetest(p, script_mlist(p), var_mem_max); - dorangetest(p, script_script_mlist(p), var_mem_max); - break; - case fraction_noad: - dorangetest(p, numerator(p), var_mem_max); - dorangetest(p, denominator(p), var_mem_max); - dorangetest(p, left_delimiter(p), var_mem_max); - dorangetest(p, right_delimiter(p), var_mem_max); - break; - case simple_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - break; - case radical_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - dorangetest(p, degree(p), var_mem_max); - dorangetest(p, left_delimiter(p), var_mem_max); - break; - case accent_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - dorangetest(p, top_accent_chr(p), var_mem_max); - dorangetest(p, bot_accent_chr(p), var_mem_max); - dorangetest(p, overlay_accent_chr(p), var_mem_max); - break; - case fence_noad: - dorangetest(p, delimiter(p), var_mem_max); - break; - /* - case rule_node: - case kern_node: - case penalty_node: - case mark_node: - case style_node: - case attribute_list_node: - case attribute_node: - case glue_spec_node: - case temp_node: - case align_stack_node: - case movement_node: - case if_node: - case nesting_node: - case span_node: - case unhyphenated_node: - case hyphenated_node: - case delta_node: - case passive_node: - case expr_node: - case dir_node: - case boundary_node: - case local_par_node: - break; - default: - fprintf(stdout, "check_node: type is %d\n", type(p)); - */ - } -} - -@ @c -void fix_node_list(halfword head) -{ - halfword p, q; - if (head == null) - return; - p = head; - q = vlink(p); - while (q != null) { - alink(q) = p; - p = q; - q = vlink(p); - } -} - -@ @c -halfword get_node(int s) -{ - register halfword r; - - if (s < MAX_CHAIN_SIZE) { - r = free_chain[s]; - if (r != null) { - free_chain[s] = vlink(r); -#ifdef CHECK_NODE_USAGE - varmem_sizes[r] = (char) s; -#endif - vlink(r) = null; - var_used += s; /* maintain usage statistics */ - return r; - } - /* this is the end of the 'inner loop' */ - return slow_get_node(s); - } else { - normal_error("nodes","there is a problem in getting a node, case 1"); - return null; - } -} - -@ @c -void free_node(halfword p, int s) -{ - if (p <= my_prealloc) { - formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p)); - return; - } -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - if (s < MAX_CHAIN_SIZE) { - vlink(p) = free_chain[s]; - free_chain[s] = p; - } else { - /* todo ? it is perhaps possible to merge this node with an existing rover */ - node_size(p) = s; - vlink(p) = rover; - while (vlink(rover) != vlink(p)) { - rover = vlink(rover); - } - vlink(rover) = p; - } - /* maintain statistics */ - var_used -= s; -} - -@ @c -static void free_node_chain(halfword q, int s) -{ - register halfword p = q; - while (vlink(p) != null) { -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - var_used -= s; - p = vlink(p); - } - var_used -= s; -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - vlink(p) = free_chain[s]; - free_chain[s] = q; -} - -@ At the start of the node memory area we reserve some special nodes, -for instance frequently used glue specifications. We could as well just -use new_glue here but for the moment we stick to the traditional approach. - -@c -#define initialize_glue(n,wi,st,sh,sto,sho) \ - vlink(n) = null; \ - type(n) = glue_spec_node; \ - width(n) = wi; \ - stretch(n) = st; \ - shrink(n) = sh; \ - stretch_order(n) = sto; \ - shrink_order(n) = sho; - -#define initialize_whatever(n,t) \ - vinfo(n) = 0; \ - type(n) = t; \ - vlink(n) = null; \ - alink(n) = null; - -#define initialize_point(n) \ - type(n) = glyph_node; \ - subtype(n) = 0; \ - vlink(n) = null; \ - vinfo(n + 1) = null; \ - alink(n) = null; \ - font(n) = 0; \ - character(n) = '.'; \ - vinfo(n + 3) = 0; \ - vlink(n + 3) = 0; \ - vinfo(n + 4) = 0; \ - vlink(n + 4) = 0; - -void init_node_mem(int t) -{ - my_prealloc = var_mem_stat_max; - - varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t); - if (varmem == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word)); -#ifdef CHECK_NODE_USAGE - varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t); - if (varmem_sizes == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t); -#endif - var_mem_max = t; - rover = var_mem_stat_max + 1; - vlink(rover) = rover; - node_size(rover) = (t - rover); - var_used = 0; - - /* initialize static glue specs */ - - initialize_glue(zero_glue,0,0,0,0,0); - initialize_glue(sfi_glue,0,0,0,sfi,0); - initialize_glue(fil_glue,0,unity,0,fil,0); - initialize_glue(fill_glue,0,unity,0,fill,0); - initialize_glue(ss_glue,0,unity,unity,fil,fil); - initialize_glue(fil_neg_glue,0,-unity,0,fil,0); - - /* initialize node list heads */ - - initialize_whatever(page_ins_head,temp_node); - initialize_whatever(contrib_head,temp_node); - initialize_whatever(page_head,temp_node); - initialize_whatever(temp_head,temp_node); - initialize_whatever(hold_head,temp_node); - initialize_whatever(adjust_head,temp_node); - initialize_whatever(pre_adjust_head,temp_node); - initialize_whatever(align_head,temp_node); - - initialize_whatever(active,unhyphenated_node); - initialize_whatever(end_span,span_node); - - initialize_point(begin_point); - initialize_point(end_point); -} - -@ @c -void dump_node_mem(void) -{ - dump_int(var_mem_max); - dump_int(rover); - dump_things(varmem[0], var_mem_max); -#ifdef CHECK_NODE_USAGE - dump_things(varmem_sizes[0], var_mem_max); -#endif - dump_things(free_chain[0], MAX_CHAIN_SIZE); - dump_int(var_used); - dump_int(my_prealloc); -} - -@ it makes sense to enlarge the varmem array immediately -@c - -void undump_node_mem(void) -{ - int x; - undump_int(x); - undump_int(rover); - var_mem_max = (x < 100000 ? 100000 : x); - varmem = xmallocarray(memory_word, (unsigned) var_mem_max); - undump_things(varmem[0], x); -#ifdef CHECK_NODE_USAGE - varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); - memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char)); - undump_things(varmem_sizes[0], x); -#endif - undump_things(free_chain[0], MAX_CHAIN_SIZE); - undump_int(var_used); - undump_int(my_prealloc); - if (var_mem_max > x) { - /* todo ? it is perhaps possible to merge the new node with an existing rover */ - vlink(x) = rover; - node_size(x) = (var_mem_max - x); - while (vlink(rover) != vlink(x)) { - rover = vlink(rover); - } - vlink(rover) = x; - } -} - -@ @c -halfword slow_get_node(int s) -{ - register int t; - - RETRY: - t = node_size(rover); - if (vlink(rover) < var_mem_max && vlink(rover) != 0) { - if (t > s) { - /* allocating from the bottom helps decrease page faults */ - register halfword r = rover; - rover += s; - vlink(rover) = vlink(r); - node_size(rover) = node_size(r) - s; - if (vlink(rover) != r) { /* list is longer than one */ - halfword q = r; - while (vlink(q) != r) { - q = vlink(q); - } - vlink(q) += s; - } else { - vlink(rover) += s; - } - if (vlink(rover) < var_mem_max) { -#ifdef CHECK_NODE_USAGE - varmem_sizes[r] = (char) (s > 127 ? 127 : s); -#endif - vlink(r) = null; - var_used += s; /* maintain usage statistics */ - return r; /* this is the only exit */ - } else { - normal_error("nodes","there is a problem in getting a node, case 2"); - return null; - } - } else { - /* attempt to keep the free list small */ - int x; - if (vlink(rover) != rover) { - if (t < MAX_CHAIN_SIZE) { - halfword l = vlink(rover); - vlink(rover) = free_chain[t]; - free_chain[t] = rover; - rover = l; - while (vlink(l) != free_chain[t]) { - l = vlink(l); - } - vlink(l) = rover; - goto RETRY; - } else { - halfword l = rover; - while (vlink(rover) != l) { - if (node_size(rover) > s) { - goto RETRY; - } - rover = vlink(rover); - } - } - } - /* if we are still here, it was apparently impossible to get a match */ - x = (var_mem_max >> 2) + s; - varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x)); - if (varmem == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word)); -#ifdef CHECK_NODE_USAGE - varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x)); - if (varmem_sizes == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char)); -#endif - /* todo ? it is perhaps possible to merge the new memory with an existing rover */ - vlink(var_mem_max) = rover; - node_size(var_mem_max) = x; - while (vlink(rover) != vlink(var_mem_max)) { - rover = vlink(rover); - } - vlink(rover) = var_mem_max; - rover = var_mem_max; - var_mem_max += x; - goto RETRY; - } - } else { - normal_error("nodes","there is a problem in getting a node, case 3"); - return null; - } -} - -@ @c -char *sprint_node_mem_usage(void) -{ - char *s; -#ifdef CHECK_NODE_USAGE - char *ss; - int i; - int b = 0; - char msg[256]; - int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; - s = strdup(""); - for (i = (var_mem_max - 1); i > my_prealloc; i--) { - if (varmem_sizes[i] > 0) { - if (type(i) > last_normal_node + last_whatsit_node) { - node_counts[last_normal_node + last_whatsit_node + 1]++; - } else if (type(i) == whatsit_node) { - node_counts[(subtype(i) + last_normal_node + 1)]++; - } else { - node_counts[type(i)]++; - } - } - } - for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) { - if (node_counts[i] > 0) { - int j = - (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0); - snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], - get_node_name((i > last_normal_node ? whatsit_node : i), j)); - ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1)); - strcpy(ss, s); - strcat(ss, msg); - free(s); - s = ss; - b = 1; - } - } -#else - s = strdup(""); -#endif - return s; -} - -@ @c -halfword list_node_mem_usage(void) -{ - halfword q = null; -#ifdef CHECK_NODE_USAGE - halfword p = null; - halfword i, j; - char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); - memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max); - for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { - if (saved_varmem_sizes[i] > 0) { - j = copy_node(i); - if (p == null) { - q = j; - } else { - vlink(p) = j; - } - p = j; - } - } - free(saved_varmem_sizes); -#endif - return q; -} - -@ @c -void print_node_mem_stats(void) -{ - int i, b; - halfword j; - char msg[256]; - char *s; - int free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; - snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc)); - tprint_nl(msg); - s = sprint_node_mem_usage(); - tprint_nl(" "); - tprint(s); - free(s); - tprint(" nodes"); - tprint_nl(" avail lists: "); - b = 0; - for (i = 1; i < MAX_CHAIN_SIZE; i++) { - for (j = free_chain[i]; j != null; j = vlink(j)) - free_chain_counts[i]++; - if (free_chain_counts[i] > 0) { - snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]); - tprint(msg); - b = 1; - } - } - /* newline, if needed */ - print_nlp(); -} - -/* this belongs in the web but i couldn't find the correct syntactic place */ - -halfword new_span_node(halfword n, int s, scaled w) -{ - halfword p = new_node(span_node, 0); - span_link(p) = n; - span_span(p) = s; - width(p) = w; - return p; -} - -@* Attribute stuff. - -@c -static halfword new_attribute_node(unsigned int i, int v) -{ - register halfword r = get_node(attribute_node_size); - type(r) = attribute_node; - attribute_id(r) = (halfword) i; - attribute_value(r) = v; - /* not used but nicer in print */ - subtype(r) = 0; - return r; -} - -@ @c -halfword copy_attribute_list(halfword n) -{ - halfword q = get_node(attribute_node_size); - register halfword p = q; - type(p) = attribute_list_node; - attr_list_ref(p) = 0; - n = vlink(n); - while (n != null) { - register halfword r = get_node(attribute_node_size); - /* the link will be fixed automatically in the next loop */ - (void) memcpy((void *) (varmem + r), (void *) (varmem + n), - (sizeof(memory_word) * attribute_node_size)); - vlink(p) = r; - p = r; - n = vlink(n); - } - return q; -} - -@ @c -void update_attribute_cache(void) -{ - halfword p; - register int i; - attr_list_cache = get_node(attribute_node_size); - type(attr_list_cache) = attribute_list_node; - attr_list_ref(attr_list_cache) = 0; - p = attr_list_cache; - for (i = 0; i <= max_used_attr; i++) { - register int v = attribute(i); - if (v > UNUSED_ATTRIBUTE) { - register halfword r = new_attribute_node((unsigned) i, v); - vlink(p) = r; - p = r; - } - } - if (vlink(attr_list_cache) == null) { - free_node(attr_list_cache, attribute_node_size); - attr_list_cache = null; - } - return; -} - -@ @c -void build_attribute_list(halfword b) -{ - if (max_used_attr >= 0) { - if (attr_list_cache == cache_disabled|| attr_list_cache == null) { - update_attribute_cache(); - if (attr_list_cache == null) - return; - } - attr_list_ref(attr_list_cache)++; - node_attr(b) = attr_list_cache; - } -} - -@ @c -halfword current_attribute_list(void) -{ - if (max_used_attr >= 0) { - if (attr_list_cache == cache_disabled) { - update_attribute_cache(); - } - return attr_list_cache ; - } - return null ; -} - - -@ @c -void reassign_attribute(halfword n, halfword new) -{ - halfword old; - old = node_attr(n); - if (new == null) { - /* there is nothing to assign but we need to check for an old value */ - if (old != null) - delete_attribute_ref(old); /* also nulls attr field of n */ - } else if (old == null) { - /* nothing is assigned so we just do that now */ - assign_attribute_ref(n,new); - } else if (old != new) { - /* something is assigned so we need to clean up and assign then */ - delete_attribute_ref(old); - assign_attribute_ref(n,new); - } - /* else: same value so there is no need to assign and change the refcount */ - node_attr(n) = new ; -} - -@ @c -void delete_attribute_ref(halfword b) -{ - if (b != null) { - if (type(b) == attribute_list_node){ - attr_list_ref(b)--; - if (attr_list_ref(b) == 0) { - if (b == attr_list_cache) - attr_list_cache = cache_disabled; - free_node_chain(b, attribute_node_size); - } - /* maintain sanity */ - if (attr_list_ref(b) < 0) { - attr_list_ref(b) = 0; - } - } else { - normal_error("nodes","trying to delete an attribute reference of a non attribute node"); - } - } -} - -void reset_node_properties(halfword b) -{ - if (b != null) { - lua_properties_reset(b); - } -} - -@ |p| is an attr list head, or zero -@c -halfword do_set_attribute(halfword p, int i, int val) -{ - register halfword q; - register int j = 0; - if (p == null) { /* add a new head \& node */ - q = get_node(attribute_node_size); - type(q) = attribute_list_node; - attr_list_ref(q) = 1; - p = new_attribute_node((unsigned) i, val); - vlink(q) = p; - return q; - } - q = p; - if (vlink(p) != null) { - while (vlink(p) != null) { - int t = attribute_id(vlink(p)); - if (t == i && attribute_value(vlink(p)) == val) - return q; /* no need to do anything */ - if (t >= i) - break; - j++; - p = vlink(p); - } - - p = q; - while (j-- > 0) - p = vlink(p); - if (attribute_id(vlink(p)) == i) { - attribute_value(vlink(p)) = val; - } else { /* add a new node */ - halfword r = new_attribute_node((unsigned) i, val); - vlink(r) = vlink(p); - vlink(p) = r; - } - return q; - } else { - normal_error("nodes","trying to set an attribute fails, case 1"); - return null ; - } -} - -@ @c -void set_attribute(halfword n, int i, int val) -{ - register halfword p; - register int j = 0; - /* not all nodes can have an attribute list */ - if (!nodetype_has_attributes(type(n))) - return; - /* if we have no list, we create one and quit */ - p = node_attr(n); - if (p == null) { /* add a new head \& node */ - p = get_node(attribute_node_size); - type(p) = attribute_list_node; - attr_list_ref(p) = 1; - node_attr(n) = p; - p = new_attribute_node((unsigned) i, val); - vlink(node_attr(n)) = p; - return; - } - /* we check if we have this attribute already and quit if the value stays the same */ - if (vlink(p) != null) { - while (vlink(p) != null) { - int t = attribute_id(vlink(p)); - if (t == i && attribute_value(vlink(p)) == val) - return; - if (t >= i) - break; - j++; - p = vlink(p); - } - /* j has now the position (if found) .. we assume a sorted list ! */ - p = node_attr(n); - - if (attr_list_ref(p) == 0 ) { - /* the list is invalid i.e. freed already */ - formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n); - /* the still dangling list gets ref count 1 */ - attr_list_ref(p) = 1; - } else if (attr_list_ref(p) == 1) { - /* this can really happen HH-LS */ - if (p == attr_list_cache) { - /* we can invalidate the cache setting */ - /* attr_list_cache = cache_disabled */ - /* or save the list, as done below */ - p = copy_attribute_list(p); - node_attr(n) = p; - /* the copied list gets ref count 1 */ - attr_list_ref(p) = 1; - } - } else { - /* the list is used multiple times so we make a copy */ - p = copy_attribute_list(p); - /* we decrement the ref count or the original */ - delete_attribute_ref(node_attr(n)); - node_attr(n) = p; - /* the copied list gets ref count 1 */ - attr_list_ref(p) = 1; - } - - - /* we go to position j in the list */ - while (j-- > 0) - p = vlink(p); - /* if we have a hit we just set the value otherwise we add a new node */ - if (attribute_id(vlink(p)) == i) { - attribute_value(vlink(p)) = val; - } else { /* add a new node */ - halfword r = new_attribute_node((unsigned) i, val); - vlink(r) = vlink(p); - vlink(p) = r; - } - } else { - normal_error("nodes","trying to set an attribute fails, case 2"); - } -} - -@ @c -int unset_attribute(halfword n, int i, int val) -{ - register halfword p; - register int t; - register int j = 0; - - if (!nodetype_has_attributes(type(n))) - return null; - p = node_attr(n); - if (p == null) - return UNUSED_ATTRIBUTE; - if (attr_list_ref(p) == 0) { - formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n); - return UNUSED_ATTRIBUTE; - } - if (vlink(p) != null) { - while (vlink(p) != null) { - t = attribute_id(vlink(p)); - if (t > i) - return UNUSED_ATTRIBUTE; - if (t == i) { - p = vlink(p); - break; - } - j++; - p = vlink(p); - } - if (attribute_id(p) != i) - return UNUSED_ATTRIBUTE; - /* if we are still here, the attribute exists */ - p = node_attr(n); - if (attr_list_ref(p) > 1 || p == attr_list_cache) { - halfword q = copy_attribute_list(p); - if (attr_list_ref(p) > 1) { - delete_attribute_ref(node_attr(n)); - } - attr_list_ref(q) = 1; - node_attr(n) = q; - } - p = vlink(node_attr(n)); - while (j-- > 0) - p = vlink(p); - t = attribute_value(p); - if (val == UNUSED_ATTRIBUTE || t == val) { - attribute_value(p) = UNUSED_ATTRIBUTE; - } - return t; - } else { - normal_error("nodes","trying to unset an attribute fails"); - return null; - } -} - -@ @c -int has_attribute(halfword n, int i, int val) -{ - register halfword p; - if (!nodetype_has_attributes(type(n))) - return UNUSED_ATTRIBUTE; - p = node_attr(n); - if (p == null || vlink(p) == null) - return UNUSED_ATTRIBUTE; - p = vlink(p); - while (p != null) { - if (attribute_id(p) == i) { - int ret = attribute_value(p); - if (val == UNUSED_ATTRIBUTE || val == ret) - return ret; - return UNUSED_ATTRIBUTE; - } else if (attribute_id(p) > i) { - return UNUSED_ATTRIBUTE; - } - p = vlink(p); - } - return UNUSED_ATTRIBUTE; -} - -@ @c -void print_short_node_contents(halfword p) -{ - switch (type(p)) { - case hlist_node: - case vlist_node: - case ins_node: - case whatsit_node: - case mark_node: - case adjust_node: - case unset_node: - print_char('['); - print_char(']'); - break; - case rule_node: - print_char('|'); - break; - case glue_node: - if (! glue_is_zero(p)) - print_char(' '); - break; - case math_node: - print_char('$'); - break; - case disc_node: - short_display(vlink(pre_break(p))); - short_display(vlink(post_break(p))); - break; - } -} - -@ @c -static void show_pdftex_whatsit_rule_spec(int p) -{ - tprint("("); - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); -} - -@ Each new type of node that appears in our data structure must be capable -of being displayed, copied, destroyed, and so on. The routines that we -need for write-oriented whatsits are somewhat like those for mark nodes; -other extensions might, of course, involve more subtlety here. - -@c -static void print_write_whatsit(const char *s, pointer p) -{ - tprint_esc(s); - if (write_stream(p) < 16) - print_int(write_stream(p)); - else if (write_stream(p) == 16) - print_char('*'); - else - print_char('-'); -} - -@ @c -static void show_node_wrapup_core(int p) -{ - switch (subtype(p)) { - case open_node: - print_write_whatsit("openout", p); - print_char('='); - print_file_name(open_name(p), open_area(p), open_ext(p)); - break; - case write_node: - print_write_whatsit("write", p); - print_mark(write_tokens(p)); - break; - case close_node: - print_write_whatsit("closeout", p); - break; - case special_node: - tprint_esc("special"); - print_mark(write_tokens(p)); - break; - case late_lua_node: - show_late_lua(p); - break; - case save_pos_node: - tprint_esc("savepos"); - break; - case user_defined_node: - tprint_esc("whatsit"); - print_int(user_node_id(p)); - print_char('='); - switch (user_node_type(p)) { - case 'a': - tprint("<>"); - break; - case 'n': - tprint("["); - show_node_list(user_node_value(p)); - tprint("]"); - break; - case 's': - print_char('"'); - print(user_node_value(p)); - print_char('"'); - break; - case 't': - print_mark(user_node_value(p)); - break; - default: /* only 'd' */ - print_int(user_node_value(p)); - break; - } - break; - } -} - -void show_node_wrapup_dvi(int p) -{ -} - -void show_node_wrapup_pdf(int p) -{ - switch (subtype(p)) { - case pdf_literal_node: - show_pdf_literal(p); - break; - case pdf_colorstack_node: - tprint_esc("pdfcolorstack "); - print_int(pdf_colorstack_stack(p)); - switch (pdf_colorstack_cmd(p)) { - case colorstack_set: - tprint(" set "); - break; - case colorstack_push: - tprint(" push "); - break; - case colorstack_pop: - tprint(" pop"); - break; - case colorstack_current: - tprint(" current"); - break; - default: - confusion("colorstack"); - break; - } - if (pdf_colorstack_cmd(p) <= colorstack_data) - print_mark(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - tprint_esc("pdfsetmatrix"); - print_mark(pdf_setmatrix_data(p)); - break; - case pdf_save_node: - tprint_esc("pdfsave"); - break; - case pdf_restore_node: - tprint_esc("pdfrestore"); - break; - case pdf_refobj_node: - tprint_esc("pdfrefobj"); - if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) { - if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { - tprint(" attr"); - lua_rawgeti(Luas, LUA_REGISTRYINDEX, - obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p))); - print_char(' '); - tprint((const char *) lua_tostring(Luas, -1)); - lua_pop(Luas, 1); - } - tprint(" stream"); - } - if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p))) - tprint(" file"); - if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { - lua_rawgeti(Luas, LUA_REGISTRYINDEX, - obj_obj_data(static_pdf, pdf_obj_objnum(p))); - print_char(' '); - tprint((const char *) lua_tostring(Luas, -1)); - lua_pop(Luas, 1); - } - break; - case pdf_annot_node: - tprint_esc("pdfannot"); - show_pdftex_whatsit_rule_spec(p); - print_mark(pdf_annot_data(p)); - break; - case pdf_start_link_node: - tprint_esc("pdfstartlink"); - show_pdftex_whatsit_rule_spec(p); - if (pdf_link_attr(p) != null) { - tprint(" attr"); - print_mark(pdf_link_attr(p)); - } - tprint(" action"); - if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) { - tprint(" user"); - print_mark(pdf_action_tokens(pdf_link_action(p))); - return; - } - if (pdf_action_file(pdf_link_action(p)) != null) { - tprint(" file"); - print_mark(pdf_action_file(pdf_link_action(p))); - } - switch (pdf_action_type(pdf_link_action(p))) { - case pdf_action_goto: - if (pdf_action_named_id(pdf_link_action(p)) > 0) { - tprint(" goto name"); - print_mark(pdf_action_id(pdf_link_action(p))); - } else { - tprint(" goto num"); - print_int(pdf_action_id(pdf_link_action(p))); - } - break; - case pdf_action_page: - tprint(" page"); - print_int(pdf_action_id(pdf_link_action(p))); - print_mark(pdf_action_tokens(pdf_link_action(p))); - break; - case pdf_action_thread: - if (pdf_action_named_id(pdf_link_action(p)) > 0) { - tprint(" thread name"); - print_mark(pdf_action_id(pdf_link_action(p))); - } else { - tprint(" thread num"); - print_int(pdf_action_id(pdf_link_action(p))); - } - break; - default: - normal_error("pdf backend", "unknown action type for link"); - break; - } - break; - case pdf_end_link_node: - tprint_esc("pdfendlink"); - break; - case pdf_dest_node: - tprint_esc("pdfdest"); - if (pdf_dest_named_id(p) > 0) { - tprint(" name"); - print_mark(pdf_dest_id(p)); - } else { - tprint(" num"); - print_int(pdf_dest_id(p)); - } - print_char(' '); - switch (pdf_dest_type(p)) { - case pdf_dest_xyz: - tprint("xyz"); - if (pdf_dest_xyz_zoom(p) != null) { - tprint(" zoom"); - print_int(pdf_dest_xyz_zoom(p)); - } - break; - case pdf_dest_fitbh: - tprint("fitbh"); - break; - case pdf_dest_fitbv: - tprint("fitbv"); - break; - case pdf_dest_fitb: - tprint("fitb"); - break; - case pdf_dest_fith: - tprint("fith"); - break; - case pdf_dest_fitv: - tprint("fitv"); - break; - case pdf_dest_fitr: - tprint("fitr"); - show_pdftex_whatsit_rule_spec(p); - break; - case pdf_dest_fit: - tprint("fit"); - break; - default: - tprint("unknown!"); - break; - } - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (subtype(p) == pdf_thread_node) - tprint_esc("pdfthread"); - else - tprint_esc("pdfstartthread"); - tprint("("); - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); - if (pdf_thread_attr(p) != null) { - tprint(" attr"); - print_mark(pdf_thread_attr(p)); - } - if (pdf_thread_named_id(p) > 0) { - tprint(" name"); - print_mark(pdf_thread_id(p)); - } else { - tprint(" num"); - print_int(pdf_thread_id(p)); - } - break; - case pdf_end_thread_node: - tprint_esc("pdfendthread"); - break; - default: - break; - } -} - -@ Now we are ready for |show_node_list| itself. This procedure has been - written to be ``extra robust'' in the sense that it should not crash or get - into a loop even if the data structures have been messed up by bugs in - the rest of the program. You can safely call its parent routine - |show_box(p)| for arbitrary values of |p| when you are debugging \TeX. - However, in the presence of bad data, the procedure may - fetch a |memory_word| whose variant is different from the way it was stored; - for example, it might try to read |mem[p].hh| when |mem[p]| - contains a scaled integer, if |p| is a pointer that has been - clobbered or chosen at random. - - -@ |str_room| need not be checked; see |show_box| - -@ Recursive calls on |show_node_list| therefore use the following pattern: -@c -#define node_list_display(A) do { \ - append_char('.'); \ - show_node_list(A); \ - flush_char(); \ -} while (0) - -#define node_list_display_x(A,B) do { \ - if ((B) != null) { \ - append_char('.'); \ - append_char(A); \ - append_char(' '); \ - show_node_list(B); \ - flush_char(); \ - flush_char(); \ - flush_char(); \ - } \ -} while (0) - -/* prints a node list symbolically */ - -void show_node_list(int p) -{ - int n = 0; /* the number of items already printed at this level */ - halfword w; - real g; /* a glue ratio, as a floating point number */ - if ((int) cur_length > depth_threshold) { - if (p > null) - tprint(" []"); /* indicate that there's been some truncation */ - return; - } - while (p != null) { - print_ln(); - print_current_string(); /* display the nesting history */ - if (tracing_online_par < -2) - print_int(p); - incr(n); - if (n > breadth_max) { /* time to stop */ - tprint("etc."); - return; - } - /* Display node |p| */ - if (is_char_node(p)) { - print_font_and_char(p); - if (is_ligature(p)) { - /* Display ligature |p|; */ - tprint(" (ligature "); - if (is_leftboundary(p)) - print_char('|'); - font_in_short_display = font(p); - short_display(lig_ptr(p)); - if (is_rightboundary(p)) - print_char('|'); - print_char(')'); - } - } else { - switch (type(p)) { - case hlist_node: - case vlist_node: - case unset_node: - /* Display box |p|; */ - if (type(p) == hlist_node) - tprint_esc("h"); - else if (type(p) == vlist_node) - tprint_esc("v"); - else - tprint_esc("unset"); - tprint("box("); - print_scaled(height(p)); - print_char('+'); - print_scaled(depth(p)); - tprint(")x"); - print_scaled(width(p)); - if (type(p) == unset_node) { - /* Display special fields of the unset node |p|; */ - if (span_count(p) != min_quarterword) { - tprint(" ("); - print_int(span_count(p) + 1); - tprint(" columns)"); - } - if (glue_stretch(p) != 0) { - tprint(", stretch "); - print_glue(glue_stretch(p), glue_order(p), NULL); - } - if (glue_shrink(p) != 0) { - tprint(", shrink "); - print_glue(glue_shrink(p), glue_sign(p), NULL); - } - } else { - /* Display the value of |glue_set(p)| */ - /* The code will have to change in this place if |glue_ratio| is - a structured type instead of an ordinary |real|. Note that this routine - should avoid arithmetic errors even if the |glue_set| field holds an - arbitrary random value. The following code assumes that a properly - formed nonzero |real| number has absolute value $2^{20}$ or more when - it is regarded as an integer; this precaution was adequate to prevent - floating point underflow on the author's computer. - */ - - g = (real) (glue_set(p)); - if ((g != 0.0) && (glue_sign(p) != normal)) { - tprint(", glue set "); - if (glue_sign(p) == shrinking) - tprint("- "); - if (g > 20000.0 || g < -20000.0) { - if (g > 0.0) - print_char('>'); - else - tprint("< -"); - print_glue(20000 * unity, glue_order(p), NULL); - } else { - print_glue(round(unity * g), glue_order(p), NULL); - } - } - - if (shift_amount(p) != 0) { - tprint(", shifted "); - print_scaled(shift_amount(p)); - } - tprint(", direction "); - print_dir(box_dir(p)); - } - node_list_display(list_ptr(p)); /* recursive call */ - break; - case rule_node: - /* Display rule |p|; */ - if (subtype(p) == normal_rule) { - tprint_esc("rule("); - } else if (subtype(p) == empty_rule) { - tprint_esc("norule("); - } else if (subtype(p) == user_rule) { - tprint_esc("userrule("); - } else if (subtype(p) == box_rule) { - tprint_esc("box("); - } else if (subtype(p) == image_rule) { - tprint_esc("image("); - } - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); - break; - case ins_node: - /* Display insertion |p|; */ - tprint_esc("insert"); - print_int(subtype(p)); - tprint(", natural size "); - print_scaled(height(p)); - tprint("; split("); - print_spec(split_top_ptr(p), NULL); - print_char(','); - print_scaled(depth(p)); - tprint("); float cost "); - print_int(float_cost(p)); - node_list_display(ins_ptr(p)); /* recursive call */ - break; - case dir_node: - if (dir_dir(p) < 0) { - tprint_esc("enddir"); - print_char(' '); - print_dir(dir_dir(p) + dir_swap); - } else { - tprint_esc("begindir"); - print_char(' '); - print_dir(dir_dir(p)); - } - break; - case local_par_node: - tprint_esc("localpar"); - append_char('.'); - print_ln(); - print_current_string(); - tprint_esc("localinterlinepenalty"); - print_char('='); - print_int(local_pen_inter(p)); - print_ln(); - print_current_string(); - tprint_esc("localbrokenpenalty"); - print_char('='); - print_int(local_pen_broken(p)); - print_ln(); - print_current_string(); - tprint_esc("localleftbox"); - if (local_box_left(p) == null) { - tprint("=null"); - } else { - append_char('.'); - show_node_list(local_box_left(p)); - decr(cur_length); - } - print_ln(); - print_current_string(); - tprint_esc("localrightbox"); - if (local_box_right(p) == null) { - tprint("=null"); - } else { - append_char('.'); - show_node_list(local_box_right(p)); - decr(cur_length); - } - decr(cur_length); - break; - case boundary_node: - if (subtype(p)==0) { - tprint_esc("noboundary"); - } else { - switch (subtype(p)) { - case 1: - tprint_esc("boundary"); - break; - case 2: - tprint_esc("protrusionboundary"); - break; - case 3: - tprint_esc("wordboundary"); - break; - default: - tprint_esc("boundary"); - print_char(':'); - print_int(subtype(p)); - break; - } - print_char('='); - print_int(boundary_value(p)); - } - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - show_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - show_node_wrapup_dvi(p); - } else { - show_node_wrapup_core(p); - } - break; - case glue_node: - /* Display glue |p|; */ - if (subtype(p) >= a_leaders) { - /* Display leaders |p|; */ - tprint_esc(""); - switch (subtype(p)) { - case a_leaders: - break; - case c_leaders: - print_char('c'); - break; - case x_leaders: - print_char('x'); - break; - case g_leaders: - print_char('g'); - break; - default: - normal_warning("nodes","weird glue leader subtype ignored"); - } - tprint("leaders "); - print_spec(p, NULL); - node_list_display(leader_ptr(p)); /* recursive call */ - } else { - tprint_esc("glue"); - if (subtype(p) != normal) { - print_char('('); - if ((subtype(p) - 1) < thin_mu_skip_code) { - print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1)); - } else if (subtype(p) < cond_math_glue) { - print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1)); - } else if (subtype(p) == cond_math_glue) { - tprint_esc("nonscript"); - } else { - tprint_esc("mskip"); - } - print_char(')'); - } - if (subtype(p) != cond_math_glue) { - print_char(' '); - if (subtype(p) < cond_math_glue) - print_spec(p, NULL); - else - print_spec(p, "mu"); - } - } - break; - case margin_kern_node: - tprint_esc("kern"); - print_scaled(width(p)); - if (subtype(p) == left_side) - tprint(" (left margin)"); - else - tprint(" (right margin)"); - break; - case kern_node: - /* Display kern |p|; */ - /* An ``explicit'' kern value is indicated implicitly by an explicit space. */ - if (subtype(p) != mu_glue) { - tprint_esc("kern"); - if (subtype(p) != normal) - print_char(' '); - print_scaled(width(p)); - if (subtype(p) == accent_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 (!glue_is_zero(p)) { - tprint(", glued "); - print_spec(p, NULL); - } else if (surround(p) != 0) { - tprint(", surrounded "); - print_scaled(surround(p)); - } - break; - case penalty_node: - /* Display penalty |p|; */ - tprint_esc("penalty "); - print_int(penalty(p)); - break; - case disc_node: - /* Display discretionary |p|; */ - /* The |post_break| list of a discretionary node is indicated by a prefixed - `\.{\char'174}' instead of the `\..' before the |pre_break| list. */ - /* We're not compatible anyway so ... - tprint_esc("discretionary"); - print_int(disc_penalty(p)); - print_char('|'); - if (vlink(no_break(p)) != null) { - tprint(" replacing "); - node_list_display(vlink(no_break(p))); - } - node_list_display(vlink(pre_break(p))); - append_char('|'); - show_node_list(vlink(post_break(p))); - flush_char(); - */ - tprint_esc("discretionary"); - tprint(" (penalty "); - print_int(disc_penalty(p)); - print_char(')'); - node_list_display_x('<',vlink(pre_break(p))); - node_list_display_x('>',vlink(post_break(p))); - node_list_display_x('=',vlink(no_break(p))); - break; - case mark_node: - /* Display mark |p|; */ - tprint_esc("mark"); - if (mark_class(p) != 0) { - print_char('s'); - print_int(mark_class(p)); - } - print_mark(mark_ptr(p)); - break; - case adjust_node: - /* Display adjustment |p|; */ - tprint_esc("vadjust"); - if (subtype(p) != 0) - tprint(" pre "); - node_list_display(adjust_ptr(p)); /* recursive call */ - break; - case glue_spec_node: - tprint("<glue_spec "); - print_spec(p, NULL); - tprint(">"); - break; - default: - show_math_node(p); - break; - } - } - p = vlink(p); - } -} - -@ This routine finds the 'base' width of a horizontal box, using the same logic - that \TeX82 used for \.{\\predisplaywidth} */ - -@c -pointer actual_box_width(pointer r, scaled base_width) -{ - scaled d; /* increment to |v| */ - scaled w = -max_dimen; /* calculated |size| */ - scaled v = shift_amount(r) + base_width; /* |w| plus possible glue amount */ - pointer p = list_ptr(r); /* current node when calculating |pre_display_size| */ - while (p != null) { - if (is_char_node(p)) { - d = glyph_width(p); - goto FOUND; - } - switch (type(p)) { - case hlist_node: - case vlist_node: - case rule_node: - d = width(p); - goto FOUND; - break; - case margin_kern_node: - d = width(p); - break; - case kern_node: - d = width(p); - break; - case math_node: - /* begin mathskip code */ - if (glue_is_zero(p)) { - d = surround(p); - break; - } else { - /* fall through */ - } - /* end mathskip code */ - case glue_node: - /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set| - values, since such values are subject to system-dependent rounding. - System-dependent numbers are not allowed to infiltrate parameters like - |pre_display_size|, since \TeX82 is supposed to make the same decisions on all - machines. - */ - d = width(p); - if (glue_sign(r) == stretching) { - if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0)) - v = max_dimen; - } else if (glue_sign(r) == shrinking) { - if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0)) - v = max_dimen; - } - if (subtype(p) >= a_leaders) - goto FOUND; - break; - default: - d = 0; - break; - } - if (v < max_dimen) - v = v + d; - goto NOT_FOUND; - FOUND: - if (v < max_dimen) { - v = v + d; - w = v; - } else { - w = max_dimen; - break; - } - NOT_FOUND: - p = vlink(p); - } - return w; -} - -@ @c -halfword tail_of_list(halfword p) -{ - halfword q = p; - while (vlink(q) != null) - q = vlink(q); - return q; -} - - - -@ @c -int var_used; - -@ Attribute lists need two extra globals to increase processing efficiency. -|max_used_attr| limits the test loop that checks for set attributes, and -|attr_list_cache| contains a pointer to an already created attribute list. It is -set to the special value |cache_disabled| when the current value can no longer be -trusted: after an assignment to an attribute register, and after a group has -ended. - -@c -int max_used_attr; /* maximum assigned attribute id */ -halfword attr_list_cache; - -@ From the computer's standpoint, \TeX's chief mission is to create -horizontal and vertical lists. We shall now investigate how the elements -of these lists are represented internally as nodes in the dynamic memory. - -A horizontal or vertical list is linked together by |link| fields in -the first word of each node. Individual nodes represent boxes, glue, -penalties, or special things like discretionary hyphens; because of this -variety, some nodes are longer than others, and we must distinguish different -kinds of nodes. We do this by putting a `|type|' field in the first word, -together with the link and an optional `|subtype|'. - -@ Character nodes appear only in horizontal lists, never in vertical lists. - -An |hlist_node| stands for a box that was made from a horizontal list. -Each |hlist_node| is seven words long, and contains the following fields -(in addition to the mandatory |type| and |link|, which we shall not -mention explicitly when discussing the other node types): The |height| and -|width| and |depth| are scaled integers denoting the dimensions of the -box. There is also a |shift_amount| field, a scaled integer indicating -how much this box should be lowered (if it appears in a horizontal list), -or how much it should be moved to the right (if it appears in a vertical -list). There is a |list_ptr| field, which points to the beginning of the -list from which this box was fabricated; if |list_ptr| is |null|, the box -is empty. Finally, there are three fields that represent the setting of -the glue: |glue_set(p)| is a word of type |glue_ratio| that represents -the proportionality constant for glue setting; |glue_sign(p)| is -|stretching| or |shrinking| or |normal| depending on whether or not the -glue should stretch or shrink or remain rigid; and |glue_order(p)| -specifies the order of infinity to which glue setting applies (|normal|, -|sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used. - -@ The |new_null_box| function returns a pointer to an |hlist_node| in -which all subfields have the values corresponding to `\.{\\hbox\{\}}'. -The |subtype| field is set to |min_quarterword|, since that's the desired -|span_count| value if this |hlist_node| is changed to an |unset_node|. - -@c -halfword new_null_box(void) -{ /* creates a new box node */ - halfword p = new_node(hlist_node, min_quarterword); - box_dir(p) = text_direction_par; - return p; -} - -@ A |vlist_node| is like an |hlist_node| in all respects except that it -contains a vertical list. - -@ A |rule_node| stands for a solid black rectangle; it has |width|, -|depth|, and |height| fields just as in an |hlist_node|. However, if -any of these dimensions is $-2^{30}$, the actual value will be determined -by running the rule up to the boundary of the innermost enclosing box. -This is called a ``running dimension.'' The |width| is never running in -an hlist; the |height| and |depth| are never running in a~vlist. - -@ A new rule node is delivered by the |new_rule| function. It -makes all the dimensions ``running,'' so you have to change the -ones that are not allowed to run. - -@c -halfword new_rule(int s) -{ - halfword p = new_node(rule_node,s); - return p; -} - -@ Insertions are represented by |ins_node| records, where the |subtype| -indicates the corresponding box number. For example, `\.{\\insert 250}' -leads to an |ins_node| whose |subtype| is |250+min_quarterword|. -The |height| field of an |ins_node| is slightly misnamed; it actually holds -the natural height plus depth of the vertical list being inserted. -The |depth| field holds the |split_max_depth| to be used in case this -insertion is split, and the |split_top_ptr| points to the corresponding -|split_top_skip|. The |float_cost| field holds the |floating_penalty| that -will be used if this insertion floats to a subsequent page after a -split insertion of the same class. There is one more field, the -|ins_ptr|, which points to the beginning of the vlist for the insertion. - -@ A |mark_node| has a |mark_ptr| field that points to the reference count -of a token list that contains the user's \.{\\mark} text. -In addition there is a |mark_class| field that contains the mark class. - -@ An |adjust_node|, which occurs only in horizontal lists, -specifies material that will be moved out into the surrounding -vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}' -operation. The |adjust_ptr| field points to the vlist containing this -material. - -@ A |glyph_node|, which occurs only in horizontal lists, specifies a -glyph in a particular font, along with its attribute list. Older -versions of \TeX\ could use token memory for characters, because the -font,char combination would fit in a single word (both values were -required to be strictly less than $2^{16}$). In LuaTeX, room is -needed for characters that are larger than that, as well as a pointer -to a potential attribute list, and the two displacement values. - -In turn, that made the node so large that it made sense to merge -ligature glyphs as well, as that requires only one extra pointer. A -few extra classes of glyph nodes will be introduced later. The -unification of all those types makes it easier to manipulate lists of -glyphs. The subtype differentiates various glyph kinds. - -First, here is a function that returns a pointer to a glyph node for a given -glyph in a given font. If that glyph doesn't exist, |null| is returned -instead. Nodes of this subtype are directly created only for accents -and their base (through |make_accent|), and math nucleus items (in the -conversion from |mlist| to |hlist|). - -@c -halfword new_glyph(int f, int c) -{ - halfword p = null; /* the new node */ - if ((f == 0) || (char_exists(f, c))) { - p = new_glyph_node(); - set_to_glyph(p); - font(p) = f; - character(p) = c; - } - return p; -} - -@ A subset of the glyphs nodes represent ligatures: characters -fabricated from the interaction of two or more actual characters. The -characters that generated the ligature have not been forgotten, since -they are needed for diagnostic messages; the |lig_ptr| field points to -a linked list of character nodes for all original characters that have -been deleted. (This list might be empty if the characters that -generated the ligature were retained in other nodes.) - -The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if -the original source of the ligature included implicit left and/or -right boundaries. These nodes are created by the C function |new_ligkern|. - -A third general type of glyphs could be called a character, as it -only appears in lists that are not yet processed by the ligaturing and -kerning steps of the program. - -|main_control| inserts these, and they are later converted to -|subtype_normal| by |new_ligkern|. - -@c -quarterword norm_min(int h) -{ - if (h <= 0) - return 1; - else if (h >= 255) - return 255; - else - return (quarterword) h; -} - -halfword new_char(int f, int c) -{ - halfword p; /* the new node */ - p = new_glyph_node(); - set_to_character(p); - font(p) = f; - character(p) = c; - lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par); - return p; -} - -@ Left and right ghost glyph nodes are the result of \.{\\leftghost} -and \.{\\rightghost}, respectively. They are going to be removed by -|new_ligkern|, at the end of which they are no longer needed. - -@ Here are a few handy helpers used by the list output routines. - -@c -scaled glyph_width(halfword p) -{ - scaled w = char_width(font(p), character(p)); - return w; -} - -scaled glyph_height(halfword p) -{ - scaled w = char_height(font(p), character(p)) + y_displace(p); - if (w < 0) - w = 0; - return w; -} - -scaled glyph_depth(halfword p) -{ - scaled w = char_depth(font(p), character(p)); - if (y_displace(p) > 0) - w = w - y_displace(p); - if (w < 0) - w = 0; - return w; -} - -@ A |disc_node|, which occurs only in horizontal lists, specifies a -``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text -that starts at |pre_break(p)| will precede the break, the text that starts at -|post_break(p)| will follow the break, and text that appears in -|no_break(p)| nodes will be ignored. For example, an ordinary -discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with -|pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|, -and |no_break=null|. - -{TODO: Knuth said: All three of the discretionary texts must be lists -that consist entirely of character, kern, box and rule nodes.} - -If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this -break. Otherwise the |hyphen_penalty| will be charged. The texts will -actually be substituted into the list by the line-breaking algorithm if it -decides to make the break, and the discretionary node will disappear at -that time; thus, the output routine sees only discretionaries that were -not chosen. - -@c -halfword new_disc(void) -{ /* creates an empty |disc_node| */ - halfword p = new_node(disc_node, 0); - disc_penalty(p) = hyphen_penalty_par; - return p; -} - -@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The -|subtype| field in its first word says what `\\{whatsit}' it is, and -implicitly determines the node size (which must be 2 or more) and the -format of the remaining words. When a |whatsit_node| is encountered -in a list, special actions are invoked; knowledgeable people who are -careful not to mess up the rest of \TeX\ are able to make \TeX\ do new -things by adding code at the end of the program. For example, there -might be a `\TeX nicolor' extension to specify different colors of ink, -@^extensions to \TeX@> -and the whatsit node might contain the desired parameters. - -The present implementation of \TeX\ treats the features associated with -`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to -illustrate how such routines might be coded. We shall defer further -discussion of extensions until the end of this program. - -@ A |math_node|, which occurs only in horizontal lists, appears before and -after mathematical formulas. The |subtype| field is |before| before the -formula and |after| after it. There is a |surround| field, which represents -the amount of surrounding space inserted by \.{\\mathsurround}. - -@c -halfword new_math(scaled w, int s) -{ - halfword p = new_node(math_node, s); - surround(p) = w; - return p; -} - -@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, -|rule_node|, |ins_node|, |mark_node|, |adjust_node|, -|disc_node|, |whatsit_node|, and |math_node| are at the low end of the -type codes, by permitting a break at glue in a list if and only if the -|type| of the previous node is less than |math_node|. Furthermore, a -node is discarded after a break if its type is |math_node| or~more. - -@ A |glue_node| represents glue in a list. However, it is really only -a pointer to a separate glue specification, since \TeX\ makes use of the -fact that many essentially identical nodes of glue are usually present. -If |p| points to a |glue_node|, |glue_ptr(p)| points to -another packet of words that specify the stretch and shrink components, etc. - -Glue nodes also serve to represent leaders; the |subtype| is used to -distinguish between ordinary glue (which is called |normal|) and the three -kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|). -The |leader_ptr| field points to a rule node or to a box node containing the -leaders; it is set to |null| in ordinary glue nodes. - -Many kinds of glue are computed from \TeX's ``skip'' parameters, and -it is helpful to know which parameter has led to a particular glue node. -Therefore the |subtype| is set to indicate the source of glue, whenever -it originated as a parameter. We will be defining symbolic names for the -parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|, -etc.); it suffices for now to say that the |subtype| of parametric glue -will be the same as the parameter number, plus~one. - -@ In math formulas there are two more possibilities for the |subtype| in a -glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu} -instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}' -feature that cancels the glue node immediately following if it appears -in a subscript. - -@ A glue specification has a halfword reference count in its first word, -@^reference counts@> -representing |null| plus the number of glue nodes that point to it (less one). -Note that the reference count appears in the same position as -the |link| field in list nodes; this is the field that is initialized -to |null| when a node is allocated, and it is also the field that is flagged -by |empty_flag| in empty nodes. - -Glue specifications also contain three |scaled| fields, for the |width|, -|stretch|, and |shrink| dimensions. Finally, there are two one-byte -fields called |stretch_order| and |shrink_order|; these contain the -orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|) -corresponding to the stretch and shrink values. - -@ Here is a function that returns a pointer to a copy of a glue spec. -The reference count in the copy is |null|, because there is assumed -to be exactly one reference to the new specification. - -@c -halfword new_spec(halfword q) /* safeguard for copying a glue node */ -{ - if (q == null) { - return copy_node(zero_glue); - } else if (type(q) == glue_spec_node) { - return copy_node(q); - } else if (type(q) == glue_node) { - halfword p = copy_node(zero_glue); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; - } else { - /* alternatively we can issue a warning */ - return copy_node(zero_glue); - } -} - -@ And here's a function that creates a glue node for a given parameter -identified by its code number; for example, -|new_param_glue(line_skip_code)| returns a pointer to a glue node for the -current \.{\\lineskip}. - -@c -halfword new_param_glue(int n) -{ - halfword p = new_node(glue_node, n + 1); - halfword q = glue_par(n); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; -} - -@ Glue nodes that are more or less anonymous are created by |new_glue|, -whose argument points to a glue specification. - -@c -halfword new_glue(halfword q) -{ - halfword p = new_node(glue_node, normal); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; -} - -@ Still another subroutine is needed: This one is sort of a combination -of |new_param_glue| and |new_glue|. It creates a glue node for one of -the current glue parameters, but it makes a fresh copy of the glue -specification, since that specification will probably be subject to change, -while the parameter will stay put. - -/* - The global variable |temp_ptr| is set to the address of the new spec. -*/ - -@c -halfword new_skip_param(int n) -{ - halfword p = new_node(glue_node, n + 1); - halfword q = glue_par(n); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; -} - -@ A |kern_node| has a |width| field to specify a (normally negative) -amount of spacing. This spacing correction appears in horizontal lists -between letters like A and V when the font designer said that it looks -better to move them closer together or further apart. A kern node can -also appear in a vertical list, when its `|width|' denotes additional -spacing in the vertical direction. The |subtype| is either |normal| (for -kerns inserted from font information or math mode calculations) or |explicit| -(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern| -(for kerns inserted from non-math accents) or |mu_glue| (for kerns -inserted from \.{\\mkern} specifications in math formulas). - -@ The |new_kern| function creates a kern node having a given width. - -@c -halfword new_kern(scaled w) -{ - halfword p = new_node(kern_node, normal); - width(p) = w; - return p; -} - -@ A |penalty_node| specifies the penalty associated with line or page -breaking, in its |penalty| field. This field is a fullword integer, but -the full range of integer values is not used: Any penalty |>=10000| is -treated as infinity, and no break will be allowed for such high values. -Similarly, any penalty |<=-10000| is treated as negative infinity, and a -break will be forced. - -@ Anyone who has been reading the last few sections of the program will -be able to guess what comes next. - -@c -halfword new_penalty(int m) -{ - halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */ - penalty(p) = m; - return p; -} - -@ You might think that we have introduced enough node types by now. Well, -almost, but there is one more: An |unset_node| has nearly the same format -as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} -or \.{\\valign} that are not yet in their final form, since the box -dimensions are their ``natural'' sizes before any glue adjustment has been -made. The |glue_set| word is not present; instead, we have a |glue_stretch| -field, which contains the total stretch of order |glue_order| that is -present in the hlist or vlist being boxed. -Similarly, the |shift_amount| field is replaced by a |glue_shrink| field, -containing the total shrink of order |glue_sign| that is present. -The |subtype| field is called |span_count|; an unset box typically -contains the data for |qo(span_count)+1| columns. -Unset nodes will be changed to box nodes when alignment is completed. - -In fact, there are still more types coming. When we get to math formula -processing we will see that a |style_node| has |type=14|; and a number -of larger type codes will also be defined, for use in math mode only. - -Warning: If any changes are made to these data structure layouts, such as -changing any of the node sizes or even reordering the words of nodes, -the |copy_node_list| procedure and the memory initialization code -below may have to be changed. Such potentially dangerous parts of the -program are listed in the index under `data structure assumptions'. -@!@^data structure assumptions@> -However, other references to the nodes are made symbolically in terms of -the \.{WEB} macro definitions above, so that format changes will leave -\TeX's other algorithms intact. -@^system dependencies@> - -@ This function creates a |local_paragraph| node - -@c - -halfword make_local_par_node(int mode) -{ - int callback_id; - halfword q; - halfword p = new_node(local_par_node,0); - local_pen_inter(p) = local_inter_line_penalty_par; - local_pen_broken(p) = local_broken_penalty_par; - if (local_left_box_par != null) { - q = copy_node_list(local_left_box_par); - local_box_left(p) = q; - local_box_left_width(p) = width(local_left_box_par); - } - if (local_right_box_par != null) { - q = copy_node_list(local_right_box_par); - local_box_right(p) = q; - local_box_right_width(p) = width(local_right_box_par); - } - local_par_dir(p) = par_direction_par; - /* callback with node passed */ - callback_id = callback_defined(insert_local_par_callback); - if (callback_id > 0) { - int sfix = lua_gettop(Luas); - if (!get_callback(Luas, callback_id)) { - lua_settop(Luas, sfix); - return p; - } - nodelist_to_lua(Luas, p); - lua_push_local_par_mode(Luas,mode) - if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */ - char errmsg[256]; /* temp hack ... we will have a formatted error */ - snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1)); - errmsg[255]='\0'; - lua_settop(Luas, sfix); - normal_error("insert_local_par",errmsg); /* to be done */ - return p; - } - lua_settop(Luas, sfix); - } - /* done */ - return p; -} +% texnodes.w
+%
+% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org>
+%
+% This file is part of LuaTeX.
+%
+% LuaTeX is free software; you can redistribute it and/or modify it under
+% the terms of the GNU General Public License as published by the Free
+% Software Foundation; either version 2 of the License, or (at your
+% option) any later version.
+%
+% LuaTeX is distributed in the hope that it will be useful, but WITHOUT
+% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+% License for more details.
+%
+% You should have received a copy of the GNU General Public License along
+% with LuaTeX; if not, see <http://www.gnu.org/licenses/>.
+
+@ @c
+
+#include "ptexlib.h"
+#include "lua/luatex-api.h"
+
+/* we can consider less mode sizes: 2 4 6 8 */
+
+@
+This module started out using NDEBUG to trigger checking invalid node usage,
+something that is needed because users can mess up nodes in Lua. At some point
+that code was always enabled so it is now always on but still can be recognized
+as additional code. And as the performance hit is close to zero so disabling
+makes no sense, not even to make it configureable. There is a little more memory
+used but that is neglectable compared to other memory usage.
+
+@c
+#define MAX_CHAIN_SIZE 13 /* why not a bit larger */
+#define CHECK_NODE_USAGE 1 /* this triggers checking */
+
+memory_word *volatile varmem = NULL;
+
+#ifdef CHECK_NODE_USAGE
+ char *varmem_sizes = NULL;
+#endif
+
+halfword var_mem_max = 0;
+halfword rover = 0;
+
+halfword free_chain[MAX_CHAIN_SIZE] = { null };
+
+static int my_prealloc = 0;
+
+int fix_node_lists = 1; /* used in font and lang */
+
+halfword slow_get_node(int s); /* defined below */
+
+#define fake_node 100
+#define fake_node_size 2
+#define fake_node_name "fake"
+
+#define variable_node_size 2
+
+/* core nodes */
+
+const char *node_fields_list[] = {
+ "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign",
+ "glue_set", "head", NULL
+};
+const char *node_fields_rule[] = {
+ "attr", "width", "depth", "height", "dir", "index", NULL
+};
+const char *node_fields_insert[] = {
+ "attr", "cost", "depth", "height", "spec", "head", NULL
+};
+const char *node_fields_mark[] = {
+ "attr", "class", "mark", NULL
+};
+const char *node_fields_adjust[] = {
+ "attr", "head", NULL
+};
+const char *node_fields_disc[] = {
+ "attr", "pre", "post", "replace", "penalty", NULL
+};
+const char *node_fields_math[] = {
+ "attr", "surround", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
+};
+const char *node_fields_glue[] = {
+ "attr", "leader", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
+};
+const char *node_fields_kern[] = {
+ "attr", "kern", "expansion_factor", NULL
+};
+const char *node_fields_penalty[] = {
+ "attr", "penalty", NULL
+};
+const char *node_fields_unset[] = {
+ "attr", "width", "depth", "height", "dir", "shrink", "glue_order",
+ "glue_sign", "stretch", "span", "head", NULL
+};
+const char *node_fields_margin_kern[] = {
+ "attr", "width", "glyph", NULL
+};
+const char *node_fields_glyph[] = {
+ "attr", "char", "font", "lang", "left", "right", "uchyph", "components",
+ "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL
+};
+const char *node_fields_inserting[] = {
+ "height", "last_ins_ptr", "best_ins_ptr",
+ "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
+};
+const char *node_fields_splitup[] = {
+ "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL
+};
+const char *node_fields_attribute[] = {
+ "number", "value", NULL
+};
+const char *node_fields_glue_spec[] = {
+ "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL
+};
+const char *node_fields_attribute_list[] = {
+ NULL
+};
+const char *node_fields_local_par[] = {
+ "attr", "pen_inter", "pen_broken", "dir", "box_left", "box_left_width",
+ "box_right", "box_right_width", NULL
+};
+const char *node_fields_dir[] = {
+ "attr", "dir", "level", NULL
+};
+const char *node_fields_boundary[] = {
+ "attr", "value", NULL
+};
+
+/* math nodes */
+
+const char *node_fields_noad[] = {
+ "attr", "nucleus", "sub", "sup", NULL
+};
+
+const char *node_fields_style[] = {
+ "attr", "style", NULL
+};
+const char *node_fields_choice[] = {
+ "attr", "display", "text", "script", "scriptscript", NULL
+};
+const char *node_fields_radical[] = {
+ "attr", "nucleus", "sub", "sup", "left", "degree", "width", "options", NULL
+};
+const char *node_fields_fraction[] = {
+ "attr", "width", "num", "denom", "left", "right", "middle", "options", NULL
+};
+const char *node_fields_accent[] = {
+ "attr", "nucleus", "sub", "sup", "accent", "bot_accent", "top_accent",
+ "overlay_accent", "fraction", NULL
+};
+const char *node_fields_fence[] = {
+ "attr", "delim", "italic", "height", "depth", "options", "class", NULL
+};
+const char *node_fields_math_char[] = {
+ "attr", "fam", "char", NULL
+};
+const char *node_fields_sub_box[] = {
+ "attr", "head", NULL
+};
+const char *node_fields_sub_mlist[] = {
+ "attr", "head", NULL
+};
+const char *node_fields_math_text_char[] = {
+ "attr", "fam", "char", NULL
+};
+const char *node_fields_delim[] = {
+ "attr", "small_fam", "small_char", "large_fam", "large_char", NULL
+};
+
+/* whatsit nodes */
+
+const char *node_fields_whatsit_open[] = {
+ "attr", "stream", "name", "area", "ext", NULL
+};
+const char *node_fields_whatsit_write[] = {
+ "attr", "stream", "data", NULL
+};
+const char *node_fields_whatsit_close[] = {
+ "attr", "stream", NULL
+};
+const char *node_fields_whatsit_special[] = {
+ "attr", "data", NULL
+};
+const char *node_fields_whatsit_save_pos[] = {
+ "attr", NULL
+};
+const char *node_fields_whatsit_late_lua[] = {
+ "attr", "reg", "data", "name", "string", NULL
+};
+const char *node_fields_whatsit_user_defined[] = {
+ "attr", "user_id", "type", "value", NULL
+};
+
+/* pdf backend whatsit nodes */
+
+const char *node_fields_whatsit_pdf_literal[] = {
+ "attr", "mode", "data", NULL
+};
+const char *node_fields_whatsit_pdf_refobj[] = {
+ "attr", "objnum", NULL
+};
+const char *node_fields_whatsit_pdf_annot[] = {
+ "attr", "width", "depth", "height", "objnum", "data", NULL
+};
+const char *node_fields_whatsit_pdf_start_link[] = {
+ "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL
+};
+const char *node_fields_whatsit_pdf_end_link[] = {
+ "attr", NULL
+};
+const char *node_fields_whatsit_pdf_dest[] = {
+ "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type",
+ "xyz_zoom", "objnum", NULL
+};
+const char *node_fields_whatsit_pdf_action[] = {
+ "action_type", "named_id", "action_id", "file", "new_window", "data", NULL
+};
+const char *node_fields_whatsit_pdf_thread[] = {
+ "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
+};
+const char *node_fields_whatsit_pdf_start_thread[] = {
+ "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
+};
+const char *node_fields_whatsit_pdf_end_thread[] = {
+ "attr", NULL
+};
+const char *node_fields_whatsit_pdf_colorstack[] = {
+ "attr", "stack", "cmd", "data", NULL
+};
+const char *node_fields_whatsit_pdf_setmatrix[] = {
+ "attr", "data", NULL
+};
+const char *node_fields_whatsit_pdf_save[] = {
+ "attr", NULL
+};
+const char *node_fields_whatsit_pdf_restore[] = {
+ "attr", NULL
+};
+
+/* subtypes */
+
+const char *node_subtypes_glue[] = {
+ "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip",
+ "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip",
+ "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip",
+ "mathskip", "thinmuskip", "medmuskip", "thickmuskip", NULL
+};
+const char *node_subtypes_mathglue[] = { /* 98+ */
+ "conditionalmathskip", "muglue", NULL
+};
+const char *node_subtypes_leader[] = { /* 100+ */
+ "leaders", "cleaders", "xleaders", "gleaders", NULL
+};
+const char *node_subtypes_fill[] = {
+ "stretch", "fi", "fil", "fill", "filll", NULL
+};
+const char *node_subtypes_boundary[] = {
+ "cancel", "user", "protrusion", "word", NULL
+};
+const char *node_subtypes_penalty[] = {
+ "userpenalty", NULL
+};
+const char *node_subtypes_kern[] = {
+ "fontkern", "userkern", "accentkern", "italiccorrection", NULL
+};
+const char *node_subtypes_rule[] = {
+ "normal", "box", "image", "empty", "user", "over", "under", "fraction", "radical", NULL
+};
+const char *node_subtypes_glyph[] = {
+ "character", "glyph", "ligature", "ghost", "left", "right", NULL
+};
+const char *node_subtypes_disc[] = {
+ "discretionary", "explicit", "automatic", "regular", "first", "second", NULL
+};
+const char *node_subtypes_marginkern[] = {
+ "left", "right", NULL
+};
+const char *node_subtypes_list[] = {
+ "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL
+};
+const char *node_subtypes_adjust[] = {
+ "normal", "pre", NULL
+};
+const char *node_subtypes_math[] = {
+ "beginmath", "endmath", NULL
+};
+const char *node_subtypes_noad[] = {
+ "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close",
+ "punct", "inner", "under", "over", "vcenter", NULL
+};
+const char *node_subtypes_radical[] = {
+ "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder",
+ "udelimiterover", NULL
+};
+const char *node_subtypes_accent[] = {
+ "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL,
+};
+const char *node_subtypes_fence[] = {
+ "unset", "left", "middle", "right", NULL
+};
+
+node_info node_data[] = { /* the last entry in a row is the etex number */
+ { hlist_node, box_node_size, node_fields_list, "hlist", 1 },
+ { vlist_node, box_node_size, node_fields_list, "vlist", 2 },
+ { rule_node, rule_node_size, node_fields_rule, "rule", 3 },
+ { ins_node, ins_node_size, node_fields_insert, "ins", 4 },
+ { mark_node, mark_node_size, node_fields_mark, "mark", 5 },
+ { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 },
+ { boundary_node, boundary_node_size, node_fields_boundary, "boundary", -1 },
+ { disc_node, disc_node_size, node_fields_disc, "disc", 8 },
+ { whatsit_node, -1, NULL, "whatsit", 9 },
+ { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 },
+ { dir_node, dir_node_size, node_fields_dir, "dir", -1 },
+ { math_node, math_node_size, node_fields_math, "math", 10 },
+ { glue_node, glue_node_size, node_fields_glue, "glue", 11 },
+ { kern_node, kern_node_size, node_fields_kern, "kern", 12 },
+ { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 },
+ { unset_node, box_node_size, node_fields_unset, "unset", 14 },
+ { style_node, style_node_size, node_fields_style, "style", 15 },
+ { choice_node, style_node_size, node_fields_choice, "choice", 15 },
+ { simple_noad, noad_size, node_fields_noad, "noad", 15 },
+ { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 },
+ { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 },
+ { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 },
+ { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 },
+ { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 },
+ { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 },
+ { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 },
+ { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 },
+ { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 },
+ { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 },
+ { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 },
+ { align_record_node, box_node_size, NULL, "align_record", -1 },
+ { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 },
+ { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 },
+ { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 },
+ { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 },
+ { expr_node, expr_node_size, NULL, "expr_stack", -1 },
+ { nesting_node, nesting_node_size, NULL, "nested_list", -1 },
+ { span_node, span_node_size, NULL, "span", -1 },
+ { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 },
+ { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 },
+ { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 },
+ { temp_node, temp_node_size, NULL, "temp", -1 },
+ { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 },
+ { movement_node, movement_node_size, NULL, "movement_stack", -1 },
+ { if_node, if_node_size, NULL, "if_stack", -1 },
+ { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 },
+ { hyphenated_node, active_node_size, NULL, "hyphenated", -1 },
+ { delta_node, delta_node_size, NULL, "delta", -1 },
+ { passive_node, passive_node_size, NULL, "passive", -1 },
+ { shape_node, variable_node_size, NULL, "shape", -1 },
+ { -1, -1, NULL, NULL, -1 },
+};
+
+const char *node_subtypes_pdf_destination[] = {
+ "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL
+};
+const char *node_subtypes_pdf_literal[] = {
+ "origin", "page", "direct", NULL
+};
+
+node_info whatsit_node_data[] = {
+ { open_node, open_node_size, node_fields_whatsit_open, "open", -1 },
+ { write_node, write_node_size, node_fields_whatsit_write, "write", -1 },
+ { close_node, close_node_size, node_fields_whatsit_close, "close", -1 },
+ { special_node, special_node_size, node_fields_whatsit_special, "special", -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 },
+ { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 },
+ { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ { fake_node, fake_node_size, NULL, fake_node_name, -1 },
+ /* here starts the dvi backend section, todo: a separate list */
+ /* nothing for dvi */
+ /* here starts the pdf backend section, todo: a separate list */
+ { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 },
+ { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 },
+ { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 },
+ { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 },
+ { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 },
+ { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 },
+ { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 },
+ { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 },
+ { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 },
+ { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 },
+ { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 },
+ { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 },
+ { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 },
+ { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 },
+ { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 },
+ { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 },
+ /* done */
+ { -1, -1, NULL, NULL, -1 },
+};
+
+#define last_whatsit_node pdf_restore_node
+
+@
+When we copy a node list, there are several possibilities: we do the same as a new node,
+we copy the entry to the table in properties (a reference), we do a deep copy of a table
+in the properties, we create a new table and give it the original one as a metatable.
+After some experiments (that also included timing) with these scenarios I decided that a
+deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable
+variant were both ok, although the second ons is slower. The most important aspect to keep
+in mind is that references to other nodes in properties no longer can be valid for that
+copy. We could use two tables (one unique and one shared) or metatables but that only
+complicates matters.
+
+When defining a new node, we could already allocate a table but it is rather easy to do
+that at the lua end e.g. using a metatable __index method. That way it is under macro
+package control.
+
+When deleting a node, we could keep the slot (e.g. setting it to false) but it could make
+memory consumption raise unneeded when we have temporary large node lists and after that
+only small lists.
+
+So, in the end this is what we ended up with. For the record, I also experimented with the
+following:
+
+- copy attributes to the properties so that we have fast access at the lua end: in the end
+ the overhead is not compensated by speed and convenience, in fact, attributes are not
+ that slow when it comes to accessing them
+
+- a bitset in the node but again the gain compared to attributes is neglectable and it also
+ demands a pretty string agreement over what bit represents what, and this is unlikely to
+ succeed in the tex community (I could use it for font handling, which is cross package,
+ but decided that it doesn't pay off
+
+In case one wonders why properties make sense then, well, it is not so much speed that we
+gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and
+this mechanism makes sure that properties are cleaned up when a node is freed. Also, the
+advantage of a more or less global properties table is that we stay at the lua end. An
+alternative is to store a reference in the node itself but that is complicated by the fact
+that the register has some limitations (no numeric keys) and we also don't want to mess with
+it too much.
+
+@c
+int lua_properties_level = 0 ; /* can be private */
+int lua_properties_enabled = 0 ;
+int lua_properties_use_metatable = 0 ;
+
+@
+We keep track of nesting so that we don't oveflow the stack, and, what is more
+important, don't keep resolving the registry index.
+
+@c
+#define lua_properties_push do { \
+ if (lua_properties_enabled) { \
+ lua_properties_level = lua_properties_level + 1 ; \
+ if (lua_properties_level == 1) { \
+ lua_get_metatablelua_l(Luas,node_properties); \
+ } \
+ } \
+} while(0)
+
+#define lua_properties_pop do { \
+ if (lua_properties_enabled) { \
+ if (lua_properties_level == 1) \
+ lua_pop(Luas,1); \
+ lua_properties_level = lua_properties_level - 1 ; \
+ } \
+} while(0)
+
+/* No setting is needed: */
+
+#define lua_properties_set(target) do { \
+} while(0)
+
+/* Resetting boils down to nilling. */
+
+#define lua_properties_reset(target) do { \
+ if (lua_properties_enabled) { \
+ if (lua_properties_level == 0) { \
+ lua_get_metatablelua_l(Luas,node_properties); \
+ lua_pushnil(Luas); \
+ lua_rawseti(Luas,-2,target); \
+ lua_pop(Luas,1); \
+ } else { \
+ lua_pushnil(Luas); \
+ lua_rawseti(Luas,-2,target); \
+ } \
+ } \
+} while(0)
+
+/*
+ For a moment I considered supporting all kind of data types but in practice
+ that makes no sense. So we stick to a cheap shallow copy with as option a
+ metatable. Btw, a deep copy would look like this:
+
+ static void copy_lua_table(lua_State* L, int index) {
+ lua_newtable(L);
+ lua_pushnil(L);
+ while(lua_next(L, index-1) != 0) {
+ lua_pushvalue(L, -2);
+ lua_insert(L, -2);
+ if (lua_type(L,-1)==LUA_TTABLE)
+ copy_lua_table(L,-1);
+ lua_settable(L, -4);
+ }
+ lua_pop(L,1);
+ }
+
+ #define lua_properties_copy(target, source) do { \
+ if (lua_properties_enabled) { \
+ lua_pushinteger(Luas,source); \
+ lua_rawget(Luas,-2); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ copy_lua_table(Luas,-1); \
+ lua_pushinteger(Luas,target); \
+ lua_insert(Luas,-2); \
+ lua_rawset(Luas,-3); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ } \
+ } while(0)
+
+*/
+
+/* isn't there a faster way to metatable? */
+
+/*
+
+#define lua_properties_copy(target,source) do { \
+ if (lua_properties_enabled) { \
+ if (lua_properties_level == 0) { \
+ lua_get_metatablelua_l(Luas,node_properties); \
+ lua_rawgeti(Luas,-1,source); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ if (lua_properties_use_metatable) { \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setfield(Luas,-2,"__index"); \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setmetatable(Luas,-2); \
+ } \
+ lua_rawseti(Luas,-2,target); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ lua_pop(Luas,1); \
+ } else { \
+ lua_rawgeti(Luas,-1,source); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ if (lua_properties_use_metatable) { \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setfield(Luas,-2,"__index"); \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setmetatable(Luas,-2); \
+ } \
+ lua_rawseti(Luas,-2,target); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ } \
+ } \
+} while(0)
+
+*/
+
+/*
+ A simple testrun on many pages of dumb text shows 1% gain (of course it depends
+ on how properties are used but some other tests confirm it).
+*/
+
+#define lua_properties_copy(target,source) do { \
+ if (lua_properties_enabled) { \
+ if (lua_properties_level == 0) { \
+ lua_get_metatablelua_l(Luas,node_properties); \
+ lua_rawgeti(Luas,-1,source); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ if (lua_properties_use_metatable) { \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_push_string_by_name(Luas,__index); \
+ lua_insert(Luas,-2); \
+ lua_rawset(Luas, -3); \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setmetatable(Luas,-2); \
+ } \
+ lua_rawseti(Luas,-2,target); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ lua_pop(Luas,1); \
+ } else { \
+ lua_rawgeti(Luas,-1,source); \
+ if (lua_type(Luas,-1)==LUA_TTABLE) { \
+ if (lua_properties_use_metatable) { \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_push_string_by_name(Luas,__index); \
+ lua_insert(Luas,-2); \
+ lua_rawset(Luas, -3); \
+ lua_newtable(Luas); \
+ lua_insert(Luas,-2); \
+ lua_setmetatable(Luas,-2); \
+ } \
+ lua_rawseti(Luas,-2,target); \
+ } else { \
+ lua_pop(Luas,1); \
+ } \
+ } \
+ } \
+} while(0)
+
+/* Here end the property handlers. */
+
+@ @c
+int valid_node(halfword p)
+{
+ if (p > my_prealloc && p < var_mem_max) {
+#ifdef CHECK_NODE_USAGE
+ if (varmem_sizes[p] > 0) {
+ return 1;
+ }
+#else
+ return 1;
+#endif
+ }
+ return 0;
+}
+
+@ @c
+static int test_count = 1;
+
+#define dorangetest(a,b,c) do { \
+ if (!(b>=0 && b<c)) { \
+ fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
+ (int)a, (int)b, (int)c, __LINE__,test_count); \
+ confusion("node range test failed"); \
+ } } while (0)
+
+#define dotest(a,b,c) do { \
+ if (b!=c) { \
+ fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
+ (int)a, (int)b, (int)c, __LINE__,test_count); \
+ confusion("node test failed"); \
+ } } while (0)
+
+#define check_action_ref(a) { dorangetest(p,a,var_mem_max); }
+#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); }
+#define check_token_ref(a) { \
+ if (type(p) == whatsit_node) { \
+ formatted_error("nodes","fuzzy token cleanup in whatsit node with id %i and subtype %i",type(p),subtype(p)); \
+ } else { \
+ formatted_error("nodes","fuzzy token cleanup in node with id %i",type(p)); \
+ } \
+}
+
+#ifdef CHECK_NODE_USAGE
+
+static void check_static_node_mem(void)
+{
+ dotest(zero_glue, width(zero_glue), 0);
+ dotest(zero_glue, type(zero_glue), glue_spec_node);
+ dotest(zero_glue, vlink(zero_glue), null);
+ dotest(zero_glue, stretch(zero_glue), 0);
+ dotest(zero_glue, stretch_order(zero_glue), normal);
+ dotest(zero_glue, shrink(zero_glue), 0);
+ dotest(zero_glue, shrink_order(zero_glue), normal);
+
+ dotest(sfi_glue, width(sfi_glue), 0);
+ dotest(sfi_glue, type(sfi_glue), glue_spec_node);
+ dotest(sfi_glue, vlink(sfi_glue), null);
+ dotest(sfi_glue, stretch(sfi_glue), 0);
+ dotest(sfi_glue, stretch_order(sfi_glue), sfi);
+ dotest(sfi_glue, shrink(sfi_glue), 0);
+ dotest(sfi_glue, shrink_order(sfi_glue), normal);
+
+ dotest(fil_glue, width(fil_glue), 0);
+ dotest(fil_glue, type(fil_glue), glue_spec_node);
+ dotest(fil_glue, vlink(fil_glue), null);
+ dotest(fil_glue, stretch(fil_glue), unity);
+ dotest(fil_glue, stretch_order(fil_glue), fil);
+ dotest(fil_glue, shrink(fil_glue), 0);
+ dotest(fil_glue, shrink_order(fil_glue), normal);
+
+ dotest(fill_glue, width(fill_glue), 0);
+ dotest(fill_glue, type(fill_glue), glue_spec_node);
+ dotest(fill_glue, vlink(fill_glue), null);
+ dotest(fill_glue, stretch(fill_glue), unity);
+ dotest(fill_glue, stretch_order(fill_glue), fill);
+ dotest(fill_glue, shrink(fill_glue), 0);
+ dotest(fill_glue, shrink_order(fill_glue), normal);
+
+ dotest(ss_glue, width(ss_glue), 0);
+ dotest(ss_glue, type(ss_glue), glue_spec_node);
+ dotest(ss_glue, vlink(ss_glue), null);
+ dotest(ss_glue, stretch(ss_glue), unity);
+ dotest(ss_glue, stretch_order(ss_glue), fil);
+ dotest(ss_glue, shrink(ss_glue), unity);
+ dotest(ss_glue, shrink_order(ss_glue), fil);
+
+ dotest(fil_neg_glue, width(fil_neg_glue), 0);
+ dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node);
+ dotest(fil_neg_glue, vlink(fil_neg_glue), null);
+ dotest(fil_neg_glue, stretch(fil_neg_glue), -unity);
+ dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil);
+ dotest(fil_neg_glue, shrink(fil_neg_glue), 0);
+ dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal);
+}
+
+static void node_mem_dump(halfword p)
+{
+ halfword r;
+ for (r = my_prealloc + 1; r < var_mem_max; r++) {
+ if (vlink(r) == p) {
+ halfword s = r;
+ while (s > my_prealloc && varmem_sizes[s] == 0) {
+ s--;
+ }
+ if (s != null
+ && s != my_prealloc
+ && s != var_mem_max
+ && (r - s) < get_node_size(type(s), subtype(s))
+ && alink(s) != p) {
+ if (type(s) == disc_node) {
+ fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
+ get_node_name(type(s), subtype(s)), (int) s,
+ (int) vlink(s), (int) alink(s));
+ fprintf(stdout, "pre_break(%d,%d,%d), ",
+ (int) vlink_pre_break(s), (int) tlink(pre_break(s)),
+ (int) alink(pre_break(s)));
+ fprintf(stdout, "post_break(%d,%d,%d), ",
+ (int) vlink_post_break(s),
+ (int) tlink(post_break(s)),
+ (int) alink(post_break(s)));
+ fprintf(stdout, "no_break(%d,%d,%d)",
+ (int) vlink_no_break(s), (int) tlink(no_break(s)),
+ (int) alink(no_break(s)));
+ fprintf(stdout, "\n");
+ } else {
+ if (vlink(s) == p
+ || (type(s) == glyph_node && lig_ptr (s) == p)
+ || (type(s) == vlist_node && list_ptr(s) == p)
+ || (type(s) == hlist_node && list_ptr(s) == p)
+ || (type(s) == unset_node && list_ptr(s) == p)
+ || (type(s) == ins_node && ins_ptr (s) == p)
+ ) {
+ fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
+ get_node_name(type(s), subtype(s)), (int) s,
+ (int) vlink(s), (int) alink(s));
+ if (type(s) == glyph_node) {
+ fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s));
+ } else if (type(s) == vlist_node || type(s) == hlist_node) {
+ fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s));
+ }
+ fprintf(stdout, "\n");
+ } else {
+ if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) {
+ fprintf(stdout, " pointed to from %s node %d\n",
+ get_node_name(type(s), subtype(s)), (int) s);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+#endif
+
+static int free_error(halfword p)
+{
+ if (p > my_prealloc && p < var_mem_max) {
+#ifdef CHECK_NODE_USAGE
+ int i;
+ if (varmem_sizes[p] == 0) {
+ check_static_node_mem();
+ for (i = (my_prealloc + 1); i < var_mem_max; i++) {
+ if (varmem_sizes[i] > 0) {
+ check_node(i);
+ }
+ }
+ test_count++;
+ if (type(p) == glyph_node) {
+ formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p);
+ } else {
+ formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
+ }
+ node_mem_dump(p);
+ return 1;
+ }
+#endif
+ } else {
+ formatted_error("nodes", "attempt to free an impossible node %d", (int) p);
+ return 1;
+ }
+ return 0;
+}
+
+@ @c
+static int copy_error(halfword p)
+{
+ if (p >= 0 && p < var_mem_max) {
+#ifdef CHECK_NODE_USAGE
+ if (p > my_prealloc && varmem_sizes[p] == 0) {
+ if (type(p) == glyph_node) {
+ formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p);
+ } else {
+ formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
+ }
+ return 1;
+ }
+#endif
+ } else {
+ formatted_error("nodes", "attempt to copy an impossible node %d", (int) p);
+ return 1;
+ }
+ return 0;
+}
+
+@ @c
+halfword new_node(int i, int j)
+{
+ int s = get_node_size(i, j);
+ halfword n = get_node(s);
+ /*
+ It should be possible to do this memset at |free_node()|.
+
+ Both type() and subtype() will be set below, and vlink() is
+ set to null by |get_node()|, so we can do we clearing one
+ word less than |s|
+ */
+ (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1)));
+ switch (i) {
+ case glyph_node:
+ init_lang_data(n);
+ break;
+ case hlist_node:
+ case vlist_node:
+ box_dir(n) = -1;
+ break;
+ case disc_node:
+ pre_break(n) = pre_break_head(n);
+ type(pre_break(n)) = nesting_node;
+ subtype(pre_break(n)) = pre_break_head(0);
+ post_break(n) = post_break_head(n);
+ type(post_break(n)) = nesting_node;
+ subtype(post_break(n)) = post_break_head(0);
+ no_break(n) = no_break_head(n);
+ type(no_break(n)) = nesting_node;
+ subtype(no_break(n)) = no_break_head(0);
+ break;
+ case rule_node:
+ depth(n) = null_flag;
+ height(n) = null_flag;
+ width(n) = null_flag;
+ rule_dir(n) = -1;
+ rule_index(n) = 0;
+ rule_transform(n) = 0;
+ break;
+ case whatsit_node:
+ if (j == open_node) {
+ open_name(n) = get_nullstr();
+ open_area(n) = open_name(n);
+ open_ext(n) = open_name(n);
+ }
+ break;
+ case unset_node:
+ width(n) = null_flag;
+ break;
+ case pseudo_line_node:
+ case shape_node:
+ /* this is a trick that makes |pseudo_files| slightly slower,
+ but the overall allocation faster then an explicit test
+ at the top of |new_node()|.
+ */
+ if (j>0) {
+ free_node(n, variable_node_size);
+ n = slow_get_node(j);
+ (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1)));
+ }
+ break;
+ default:
+ break;
+ }
+ if (synctex_par) {
+ /* 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:
+ 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);
+ /* lua_properties_set */
+ }
+ type(n) = (quarterword) i;
+ subtype(n) = (quarterword) j;
+ return n;
+}
+
+halfword raw_glyph_node(void)
+{
+ register halfword n = get_node(glyph_node_size);
+ (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
+ type(n) = glyph_node;
+ subtype(n) = 0;
+ return n;
+}
+
+halfword new_glyph_node(void)
+{
+ register halfword n = get_node(glyph_node_size);
+ (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
+ type(n) = glyph_node;
+ subtype(n) = 0;
+ build_attribute_list(n);
+ /* lua_properties_set */
+ return n;
+}
+
+@ makes a duplicate of the node list that starts at |p| and returns a
+pointer to the new list
+
+@c
+halfword do_copy_node_list(halfword p, halfword end)
+{
+ halfword q = null; /* previous position in new list */
+ halfword h = null; /* head of the list */
+ register halfword s ;
+ lua_properties_push; /* saves stack and time */
+ while (p != end) {
+ s = copy_node(p);
+ if (h == null) {
+ h = s;
+ } else {
+ couple_nodes(q, s);
+ }
+ q = s;
+ p = vlink(p);
+ }
+ lua_properties_pop; /* saves stack and time */
+ return h;
+}
+
+halfword copy_node_list(halfword p)
+{
+ return do_copy_node_list(p, null);
+}
+
+#define copy_sub_list(target,source) do { \
+ if (source != null) { \
+ s = do_copy_node_list(source, null); \
+ target = s; \
+ } else { \
+ target = null; \
+ } \
+ } while (0)
+
+#define copy_sub_node(target,source) do { \
+ if (source != null) { \
+ s = copy_node(source); \
+ target = s ; \
+ } else { \
+ target = null; \
+ } \
+} while (0)
+
+@ make a dupe of a single node
+
+@c
+static void copy_node_wrapup_core(halfword p, halfword r)
+{
+ halfword s ;
+ switch (subtype(p)) {
+ case write_node:
+ case special_node:
+ add_token_ref(write_tokens(p));
+ break;
+ case late_lua_node:
+ copy_late_lua(r, p);
+ break;
+ case user_defined_node:
+ switch (user_node_type(p)) {
+ case 'a':
+ add_node_attr_ref(user_node_value(p));
+ break;
+ case 'l':
+ copy_user_lua(r, p);
+ break;
+ case 'n':
+ s = copy_node_list(user_node_value(p));
+ user_node_value(r) = s;
+ break;
+ case 's':
+ /* |add_string_ref(user_node_value(p));| */
+ break;
+ case 't':
+ add_token_ref(user_node_value(p));
+ break;
+ }
+ break;
+ default:
+ break ;
+ }
+}
+
+void copy_node_wrapup_dvi(halfword p, halfword r)
+{
+}
+
+void copy_node_wrapup_pdf(halfword p, halfword r)
+{
+ switch(subtype(p)) {
+ case pdf_literal_node:
+ copy_pdf_literal(r, p);
+ break;
+ case pdf_colorstack_node:
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ add_token_ref(pdf_colorstack_data(p));
+ break;
+ case pdf_setmatrix_node:
+ add_token_ref(pdf_setmatrix_data(p));
+ break;
+ case pdf_annot_node:
+ add_token_ref(pdf_annot_data(p));
+ break;
+ case pdf_start_link_node:
+ if (pdf_link_attr(r) != null)
+ add_token_ref(pdf_link_attr(r));
+ add_action_ref(pdf_link_action(r));
+ break;
+ case pdf_dest_node:
+ if (pdf_dest_named_id(p) > 0)
+ add_token_ref(pdf_dest_id(p));
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (pdf_thread_named_id(p) > 0)
+ add_token_ref(pdf_thread_id(p));
+ if (pdf_thread_attr(p) != null)
+ add_token_ref(pdf_thread_attr(p));
+ break;
+ default:
+ break;
+ }
+}
+
+halfword copy_node(const halfword p)
+{
+ halfword r; /* current node being fabricated for new list */
+ halfword w; /* whatsit subtype */
+ halfword t; /* type of node */
+ register halfword s; /* a helper variable for copying into variable mem */
+ register int i;
+ if (copy_error(p)) {
+ r = new_node(temp_node, 0);
+ return r;
+ }
+ t = type(p);
+ i = get_node_size(t,subtype(p));
+ r = get_node(i);
+
+ (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i));
+
+ /* possible speedup: */
+ /*
+ if t == glue_spec) {
+ return r;
+ }
+ */
+
+ if (synctex_par) {
+ /* handle synctex extension */
+ switch (t) {
+ case math_node:
+ synctex_tag_math(r) = cur_input.synctex_tag_field;
+ synctex_line_math(r) = line;
+ break;
+ case kern_node:
+ synctex_tag_kern(r) = cur_input.synctex_tag_field;
+ synctex_line_kern(r) = line;
+ break;
+ }
+ }
+ if (nodetype_has_attributes(t)) {
+ add_node_attr_ref(node_attr(p));
+ alink(r) = null;
+ lua_properties_copy(r,p);
+ }
+ vlink(r) = null;
+
+ switch (t) {
+ case glyph_node:
+ copy_sub_list(lig_ptr(r),lig_ptr(p)) ;
+ break;
+ case glue_node:
+ copy_sub_list(leader_ptr(r),leader_ptr(p)) ;
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ copy_sub_list(list_ptr(r),list_ptr(p)) ;
+ break;
+ case disc_node:
+ pre_break(r) = pre_break_head(r);
+ if (vlink_pre_break(p) != null) {
+ s = copy_node_list(vlink_pre_break(p));
+ alink(s) = pre_break(r);
+ tlink_pre_break(r) = tail_of_list(s);
+ vlink_pre_break(r) = s;
+ } else {
+ assert(tlink(pre_break(r)) == null);
+ }
+ post_break(r) = post_break_head(r);
+ if (vlink_post_break(p) != null) {
+ s = copy_node_list(vlink_post_break(p));
+ alink(s) = post_break(r);
+ tlink_post_break(r) = tail_of_list(s);
+ vlink_post_break(r) = s;
+ } else {
+ assert(tlink_post_break(r) == null);
+ }
+ no_break(r) = no_break_head(r);
+ if (vlink(no_break(p)) != null) {
+ s = copy_node_list(vlink_no_break(p));
+ alink(s) = no_break(r);
+ tlink_no_break(r) = tail_of_list(s);
+ vlink_no_break(r) = s;
+ } else {
+ assert(tlink_no_break(r) == null);
+ }
+ break;
+ case math_node:
+ break;
+ case ins_node:
+ copy_sub_list(ins_ptr(r),ins_ptr(p)) ;
+ break;
+ case margin_kern_node:
+ copy_sub_node(margin_char(r),margin_char(p));
+ break;
+ case mark_node:
+ add_token_ref(mark_ptr(p));
+ break;
+ case adjust_node:
+ copy_sub_list(adjust_ptr(r),adjust_ptr(p));
+ break;
+ case choice_node:
+ copy_sub_list(display_mlist(r),display_mlist(p)) ;
+ copy_sub_list(text_mlist(r),text_mlist(p)) ;
+ copy_sub_list(script_mlist(r),script_mlist(p)) ;
+ copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ;
+ break;
+ case simple_noad:
+ copy_sub_list(nucleus(r),nucleus(p)) ;
+ copy_sub_list(subscr(r),subscr(p)) ;
+ copy_sub_list(supscr(r),supscr(p)) ;
+ break;
+ case radical_noad:
+ copy_sub_list(nucleus(r),nucleus(p)) ;
+ copy_sub_list(subscr(r),subscr(p)) ;
+ copy_sub_list(supscr(r),supscr(p)) ;
+ copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
+ copy_sub_list(degree(r),degree(p)) ;
+ break;
+ case accent_noad:
+ copy_sub_list(nucleus(r),nucleus(p)) ;
+ copy_sub_list(subscr(r),subscr(p)) ;
+ copy_sub_list(supscr(r),supscr(p)) ;
+ copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ;
+ copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ;
+ copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ;
+ break;
+ case fence_noad:
+ copy_sub_node(delimiter(r),delimiter(p)) ;
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ copy_sub_list(math_list(r),math_list(p)) ;
+ break;
+ case fraction_noad:
+ copy_sub_list(numerator(r),numerator(p)) ;
+ copy_sub_list(denominator(r),denominator(p)) ;
+ copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
+ copy_sub_node(right_delimiter(r),right_delimiter(p)) ;
+ break;
+ case glue_spec_node:
+ case dir_node:
+ case local_par_node:
+ case boundary_node:
+ break;
+ case whatsit_node:
+ w = subtype(p) ;
+ if (w >= backend_first_pdf_whatsit) {
+ copy_node_wrapup_pdf(p,r);
+ } else if (w >= backend_first_dvi_whatsit) {
+ copy_node_wrapup_dvi(p,r);
+ } else {
+ copy_node_wrapup_core(p,r);
+ }
+ break;
+ }
+ return r;
+}
+
+/* x */
+
+#define free_sub_list(source) if (source != null) flush_node_list(source);
+#define free_sub_node(source) if (source != null) flush_node(source);
+
+@ @c
+
+static void flush_node_wrapup_core(halfword p)
+{
+ switch (subtype(p)) {
+ case open_node:
+ case write_node:
+ case close_node:
+ case save_pos_node:
+ break;
+ case special_node:
+ delete_token_ref(write_tokens(p));
+ break;
+ case late_lua_node:
+ free_late_lua(p);
+ break;
+ case user_defined_node:
+ switch (user_node_type(p)) {
+ case 'a':
+ delete_attribute_ref(user_node_value(p));
+ break;
+ case 'd':
+ break;
+ case 'l':
+ free_user_lua(user_node_value(p));
+ break;
+ case 'n':
+ flush_node_list(user_node_value(p));
+ break;
+ case 's':
+ /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */
+ break;
+ case 't':
+ delete_token_ref(user_node_value(p));
+ break;
+ default:
+ {
+ const char *hlp[] = {
+ "The type of the value in a user defined whatsit node should be one",
+ "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
+ "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
+ "know how to free the node's value. A memory leak may result.",
+ NULL
+ };
+ tex_error("Unidentified user defined whatsit", hlp);
+ }
+ break;
+ }
+ break;
+ }
+}
+
+void flush_node_wrapup_dvi(halfword p)
+{
+}
+
+void flush_node_wrapup_pdf(halfword p)
+{
+ switch(subtype(p)) {
+ case pdf_save_node:
+ case pdf_restore_node:
+ case pdf_refobj_node:
+ case pdf_end_link_node:
+ case pdf_end_thread_node:
+ break;
+ case pdf_literal_node:
+ free_pdf_literal(p);
+ break;
+ case pdf_colorstack_node:
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ delete_token_ref(pdf_colorstack_data(p));
+ break;
+ case pdf_setmatrix_node:
+ delete_token_ref(pdf_setmatrix_data(p));
+ break;
+ case pdf_annot_node:
+ delete_token_ref(pdf_annot_data(p));
+ break;
+ case pdf_link_data_node:
+ break;
+ case pdf_start_link_node:
+ if (pdf_link_attr(p) != null)
+ delete_token_ref(pdf_link_attr(p));
+ delete_action_ref(pdf_link_action(p));
+ break;
+ case pdf_dest_node:
+ if (pdf_dest_named_id(p) > 0)
+ delete_token_ref(pdf_dest_id(p));
+ break;
+ case pdf_action_node:
+ if (pdf_action_type(p) == pdf_action_user) {
+ delete_token_ref(pdf_action_tokens(p));
+ } else {
+ if (pdf_action_file(p) != null)
+ delete_token_ref(pdf_action_file(p));
+ if (pdf_action_type(p) == pdf_action_page)
+ delete_token_ref(pdf_action_tokens(p));
+ else if (pdf_action_named_id(p) > 0)
+ delete_token_ref(pdf_action_id(p));
+ }
+ break;
+ case pdf_thread_data_node:
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (pdf_thread_named_id(p) > 0)
+ delete_token_ref(pdf_thread_id(p));
+ if (pdf_thread_attr(p) != null)
+ delete_token_ref(pdf_thread_attr(p));
+ break;
+ }
+}
+
+void flush_node(halfword p)
+{
+ halfword w;
+ if (p == null) /* legal, but no-op */
+ return;
+ if (free_error(p))
+ return;
+ switch (type(p)) {
+ case glyph_node:
+ free_sub_list(lig_ptr(p));
+ break;
+ case glue_node:
+ free_sub_list(leader_ptr(p));
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ free_sub_list(list_ptr(p));
+ break;
+ case disc_node:
+ /* watch the start at temp node hack */
+ free_sub_list(vlink(pre_break(p)));
+ free_sub_list(vlink(post_break(p)));
+ free_sub_list(vlink(no_break(p)));
+ break;
+ case rule_node:
+ case kern_node:
+ case penalty_node:
+ case math_node:
+ break;
+ case glue_spec_node:
+ /* this allows free-ing of lua-allocated glue specs */
+//if (valid_node(p)) {
+// free_node(p, subtype(p));
+//}
+// return ;
+ break ;
+ case dir_node:
+ case local_par_node:
+ case boundary_node:
+ break;
+ case whatsit_node:
+ w = subtype(p) ;
+ if (w >= backend_first_pdf_whatsit) {
+ flush_node_wrapup_pdf(p);
+ } else if (w >= backend_first_dvi_whatsit) {
+ flush_node_wrapup_dvi(p);
+ } else {
+ flush_node_wrapup_core(p);
+ }
+ break;
+ case ins_node:
+ flush_node_list(ins_ptr(p));
+ break;
+ case margin_kern_node:
+ flush_node(margin_char(p));
+ break;
+ case mark_node:
+ delete_token_ref(mark_ptr(p));
+ break;
+ case adjust_node:
+ flush_node_list(adjust_ptr(p));
+ break;
+ case style_node: /* nothing to do */
+ break;
+ case choice_node:
+ free_sub_list(display_mlist(p));
+ free_sub_list(text_mlist(p));
+ free_sub_list(script_mlist(p));
+ free_sub_list(script_script_mlist(p));
+ break;
+ case simple_noad:
+ free_sub_list(nucleus(p));
+ free_sub_list(subscr(p));
+ free_sub_list(supscr(p));
+ break;
+ case radical_noad:
+ free_sub_list(nucleus(p));
+ free_sub_list(subscr(p));
+ free_sub_list(supscr(p));
+ free_sub_node(left_delimiter(p));
+ free_sub_list(degree(p));
+ break;
+ case accent_noad:
+ free_sub_list(nucleus(p));
+ free_sub_list(subscr(p));
+ free_sub_list(supscr(p));
+ free_sub_list(top_accent_chr(p));
+ free_sub_list(bot_accent_chr(p));
+ free_sub_list(overlay_accent_chr(p));
+ break;
+ case fence_noad:
+ free_sub_list(delimiter(p));
+ break;
+ case delim_node: /* nothing to do */
+ case math_char_node:
+ case math_text_char_node:
+ break;
+ case sub_box_node:
+ case sub_mlist_node:
+ free_sub_list(math_list(p));
+ break;
+ case fraction_noad:
+ free_sub_list(numerator(p));
+ free_sub_list(denominator(p));
+ free_sub_node(left_delimiter(p));
+ free_sub_node(right_delimiter(p));
+ break;
+ case pseudo_file_node:
+ free_sub_list(pseudo_lines(p));
+ break;
+ case pseudo_line_node:
+ case shape_node:
+ free_node(p, subtype(p));
+ return;
+ break;
+ case align_stack_node:
+ case span_node:
+ case movement_node:
+ case if_node:
+ case nesting_node:
+ case unhyphenated_node:
+ case hyphenated_node:
+ case delta_node:
+ case passive_node:
+ case inserting_node:
+ case split_up_node:
+ case expr_node:
+ case attribute_node:
+ case attribute_list_node:
+ case temp_node:
+ break;
+ default:
+ formatted_error("nodes","flushing weird node type %d", type(p));
+ return;
+ }
+ if (nodetype_has_attributes(type(p))) {
+ delete_attribute_ref(node_attr(p));
+ lua_properties_reset(p);
+ }
+ free_node(p, get_node_size(type(p), subtype(p)));
+ return;
+}
+
+@ @c
+void flush_node_list(halfword pp)
+{ /* erase list of nodes starting at |p| */
+ register halfword p = pp;
+ if (p == null) /* legal, but no-op */
+ return;
+ if (free_error(p))
+ return;
+ lua_properties_push; /* saves stack and time */
+ while (p != null) {
+ register halfword q = vlink(p);
+ flush_node(p);
+ p = q;
+ }
+ lua_properties_pop; /* saves stack and time */
+}
+
+@ @c
+static void check_node_wrapup_core(halfword p)
+{
+ switch (subtype(p)) {
+ /* frontend code */
+ case special_node:
+ check_token_ref(write_tokens(p));
+ break;
+ case user_defined_node:
+ switch (user_node_type(p)) {
+ case 'a':
+ check_attribute_ref(user_node_value(p));
+ break;
+ case 't':
+ check_token_ref(user_node_value(p));
+ break;
+ case 'n':
+ dorangetest(p, user_node_value(p), var_mem_max);
+ break;
+ case 's':
+ case 'd':
+ break;
+ default:
+ confusion("unknown user node type");
+ break;
+ }
+ break;
+ case open_node:
+ case write_node:
+ case close_node:
+ case save_pos_node:
+ break;
+ }
+}
+
+void check_node_wrapup_dvi(halfword p)
+{
+}
+
+void check_node_wrapup_pdf(halfword p)
+{
+ switch (subtype(p)) {
+ case pdf_literal_node:
+ if (pdf_literal_type(p) == normal)
+ check_token_ref(pdf_literal_data(p));
+ break;
+ case pdf_colorstack_node:
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ check_token_ref(pdf_colorstack_data(p));
+ break;
+ case pdf_setmatrix_node:
+ check_token_ref(pdf_setmatrix_data(p));
+ break;
+ case late_lua_node:
+ if (late_lua_name(p) > 0)
+ check_token_ref(late_lua_name(p));
+ if (late_lua_type(p) == normal)
+ check_token_ref(late_lua_data(p));
+ break;
+ case pdf_annot_node:
+ check_token_ref(pdf_annot_data(p));
+ break;
+ case pdf_start_link_node:
+ if (pdf_link_attr(p) != null)
+ check_token_ref(pdf_link_attr(p));
+ check_action_ref(pdf_link_action(p));
+ break;
+ case pdf_dest_node:
+ if (pdf_dest_named_id(p) > 0)
+ check_token_ref(pdf_dest_id(p));
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (pdf_thread_named_id(p) > 0)
+ check_token_ref(pdf_thread_id(p));
+ if (pdf_thread_attr(p) != null)
+ check_token_ref(pdf_thread_attr(p));
+ break;
+ case pdf_save_node:
+ case pdf_restore_node:
+ case pdf_refobj_node:
+ case pdf_end_link_node:
+ case pdf_end_thread_node:
+ break;
+ default:
+ confusion("wrapup pdf nodes");
+ break;
+ }
+}
+
+void check_node(halfword p)
+{
+ halfword w ;
+ switch (type(p)) {
+ case glyph_node:
+ dorangetest(p, lig_ptr(p), var_mem_max);
+ break;
+ case glue_node:
+ dorangetest(p, leader_ptr(p), var_mem_max);
+ break;
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ case align_record_node:
+ dorangetest(p, list_ptr(p), var_mem_max);
+ break;
+ case ins_node:
+ dorangetest(p, ins_ptr(p), var_mem_max);
+ break;
+ case whatsit_node:
+ w = subtype(p) ;
+ if (w >= backend_first_pdf_whatsit) {
+ check_node_wrapup_pdf(p);
+ } else if (w >= backend_first_dvi_whatsit) {
+ check_node_wrapup_dvi(p);
+ } else {
+ check_node_wrapup_core(p);
+ }
+ break;
+ case margin_kern_node:
+ check_node(margin_char(p));
+ break;
+ case math_node:
+ break;
+ case disc_node:
+ dorangetest(p, vlink(pre_break(p)), var_mem_max);
+ dorangetest(p, vlink(post_break(p)), var_mem_max);
+ dorangetest(p, vlink(no_break(p)), var_mem_max);
+ break;
+ case adjust_node:
+ dorangetest(p, adjust_ptr(p), var_mem_max);
+ break;
+ case pseudo_file_node:
+ dorangetest(p, pseudo_lines(p), var_mem_max);
+ break;
+ case pseudo_line_node:
+ case shape_node:
+ break;
+ case choice_node:
+ dorangetest(p, display_mlist(p), var_mem_max);
+ dorangetest(p, text_mlist(p), var_mem_max);
+ dorangetest(p, script_mlist(p), var_mem_max);
+ dorangetest(p, script_script_mlist(p), var_mem_max);
+ break;
+ case fraction_noad:
+ dorangetest(p, numerator(p), var_mem_max);
+ dorangetest(p, denominator(p), var_mem_max);
+ dorangetest(p, left_delimiter(p), var_mem_max);
+ dorangetest(p, right_delimiter(p), var_mem_max);
+ break;
+ case simple_noad:
+ dorangetest(p, nucleus(p), var_mem_max);
+ dorangetest(p, subscr(p), var_mem_max);
+ dorangetest(p, supscr(p), var_mem_max);
+ break;
+ case radical_noad:
+ dorangetest(p, nucleus(p), var_mem_max);
+ dorangetest(p, subscr(p), var_mem_max);
+ dorangetest(p, supscr(p), var_mem_max);
+ dorangetest(p, degree(p), var_mem_max);
+ dorangetest(p, left_delimiter(p), var_mem_max);
+ break;
+ case accent_noad:
+ dorangetest(p, nucleus(p), var_mem_max);
+ dorangetest(p, subscr(p), var_mem_max);
+ dorangetest(p, supscr(p), var_mem_max);
+ dorangetest(p, top_accent_chr(p), var_mem_max);
+ dorangetest(p, bot_accent_chr(p), var_mem_max);
+ dorangetest(p, overlay_accent_chr(p), var_mem_max);
+ break;
+ case fence_noad:
+ dorangetest(p, delimiter(p), var_mem_max);
+ break;
+ /*
+ case rule_node:
+ case kern_node:
+ case penalty_node:
+ case mark_node:
+ case style_node:
+ case attribute_list_node:
+ case attribute_node:
+ case glue_spec_node:
+ case temp_node:
+ case align_stack_node:
+ case movement_node:
+ case if_node:
+ case nesting_node:
+ case span_node:
+ case unhyphenated_node:
+ case hyphenated_node:
+ case delta_node:
+ case passive_node:
+ case expr_node:
+ case dir_node:
+ case boundary_node:
+ case local_par_node:
+ break;
+ default:
+ fprintf(stdout, "check_node: type is %d\n", type(p));
+ */
+ }
+}
+
+@ @c
+void fix_node_list(halfword head)
+{
+ halfword p, q;
+ if (head == null)
+ return;
+ p = head;
+ q = vlink(p);
+ while (q != null) {
+ alink(q) = p;
+ p = q;
+ q = vlink(p);
+ }
+}
+
+@ @c
+halfword get_node(int s)
+{
+ register halfword r;
+
+ if (s < MAX_CHAIN_SIZE) {
+ r = free_chain[s];
+ if (r != null) {
+ free_chain[s] = vlink(r);
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[r] = (char) s;
+#endif
+ vlink(r) = null;
+ var_used += s; /* maintain usage statistics */
+ return r;
+ }
+ /* this is the end of the 'inner loop' */
+ return slow_get_node(s);
+ } else {
+ normal_error("nodes","there is a problem in getting a node, case 1");
+ return null;
+ }
+}
+
+@ @c
+void free_node(halfword p, int s)
+{
+ if (p <= my_prealloc) {
+ formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p));
+ return;
+ }
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[p] = 0;
+#endif
+ if (s < MAX_CHAIN_SIZE) {
+ vlink(p) = free_chain[s];
+ free_chain[s] = p;
+ } else {
+ /* todo ? it is perhaps possible to merge this node with an existing rover */
+ node_size(p) = s;
+ vlink(p) = rover;
+ while (vlink(rover) != vlink(p)) {
+ rover = vlink(rover);
+ }
+ vlink(rover) = p;
+ }
+ /* maintain statistics */
+ var_used -= s;
+}
+
+@ @c
+static void free_node_chain(halfword q, int s)
+{
+ register halfword p = q;
+ while (vlink(p) != null) {
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[p] = 0;
+#endif
+ var_used -= s;
+ p = vlink(p);
+ }
+ var_used -= s;
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[p] = 0;
+#endif
+ vlink(p) = free_chain[s];
+ free_chain[s] = q;
+}
+
+@ At the start of the node memory area we reserve some special nodes,
+for instance frequently used glue specifications. We could as well just
+use new_glue here but for the moment we stick to the traditional approach.
+
+@c
+#define initialize_glue(n,wi,st,sh,sto,sho) \
+ vlink(n) = null; \
+ type(n) = glue_spec_node; \
+ width(n) = wi; \
+ stretch(n) = st; \
+ shrink(n) = sh; \
+ stretch_order(n) = sto; \
+ shrink_order(n) = sho;
+
+#define initialize_whatever(n,t) \
+ vinfo(n) = 0; \
+ type(n) = t; \
+ vlink(n) = null; \
+ alink(n) = null;
+
+#define initialize_point(n) \
+ type(n) = glyph_node; \
+ subtype(n) = 0; \
+ vlink(n) = null; \
+ vinfo(n + 1) = null; \
+ alink(n) = null; \
+ font(n) = 0; \
+ character(n) = '.'; \
+ vinfo(n + 3) = 0; \
+ vlink(n + 3) = 0; \
+ vinfo(n + 4) = 0; \
+ vlink(n + 4) = 0;
+
+void init_node_mem(int t)
+{
+ my_prealloc = var_mem_stat_max;
+
+ varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t);
+ if (varmem == NULL) {
+ overflow("node memory size", (unsigned) var_mem_max);
+ }
+ memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word));
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t);
+ if (varmem_sizes == NULL) {
+ overflow("node memory size", (unsigned) var_mem_max);
+ }
+ memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t);
+#endif
+ var_mem_max = t;
+ rover = var_mem_stat_max + 1;
+ vlink(rover) = rover;
+ node_size(rover) = (t - rover);
+ var_used = 0;
+
+ /* initialize static glue specs */
+
+ initialize_glue(zero_glue,0,0,0,0,0);
+ initialize_glue(sfi_glue,0,0,0,sfi,0);
+ initialize_glue(fil_glue,0,unity,0,fil,0);
+ initialize_glue(fill_glue,0,unity,0,fill,0);
+ initialize_glue(ss_glue,0,unity,unity,fil,fil);
+ initialize_glue(fil_neg_glue,0,-unity,0,fil,0);
+
+ /* initialize node list heads */
+
+ initialize_whatever(page_ins_head,temp_node);
+ initialize_whatever(contrib_head,temp_node);
+ initialize_whatever(page_head,temp_node);
+ initialize_whatever(temp_head,temp_node);
+ initialize_whatever(hold_head,temp_node);
+ initialize_whatever(adjust_head,temp_node);
+ initialize_whatever(pre_adjust_head,temp_node);
+ initialize_whatever(align_head,temp_node);
+
+ initialize_whatever(active,unhyphenated_node);
+ initialize_whatever(end_span,span_node);
+
+ initialize_point(begin_point);
+ initialize_point(end_point);
+}
+
+@ @c
+void dump_node_mem(void)
+{
+ dump_int(var_mem_max);
+ dump_int(rover);
+ dump_things(varmem[0], var_mem_max);
+#ifdef CHECK_NODE_USAGE
+ dump_things(varmem_sizes[0], var_mem_max);
+#endif
+ dump_things(free_chain[0], MAX_CHAIN_SIZE);
+ dump_int(var_used);
+ dump_int(my_prealloc);
+}
+
+@ it makes sense to enlarge the varmem array immediately
+@c
+
+void undump_node_mem(void)
+{
+ int x;
+ undump_int(x);
+ undump_int(rover);
+ var_mem_max = (x < 100000 ? 100000 : x);
+ varmem = xmallocarray(memory_word, (unsigned) var_mem_max);
+ undump_things(varmem[0], x);
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
+ memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char));
+ undump_things(varmem_sizes[0], x);
+#endif
+ undump_things(free_chain[0], MAX_CHAIN_SIZE);
+ undump_int(var_used);
+ undump_int(my_prealloc);
+ if (var_mem_max > x) {
+ /* todo ? it is perhaps possible to merge the new node with an existing rover */
+ vlink(x) = rover;
+ node_size(x) = (var_mem_max - x);
+ while (vlink(rover) != vlink(x)) {
+ rover = vlink(rover);
+ }
+ vlink(rover) = x;
+ }
+}
+
+@ @c
+halfword slow_get_node(int s)
+{
+ register int t;
+
+ RETRY:
+ t = node_size(rover);
+ if (vlink(rover) < var_mem_max && vlink(rover) != 0) {
+ if (t > s) {
+ /* allocating from the bottom helps decrease page faults */
+ register halfword r = rover;
+ rover += s;
+ vlink(rover) = vlink(r);
+ node_size(rover) = node_size(r) - s;
+ if (vlink(rover) != r) { /* list is longer than one */
+ halfword q = r;
+ while (vlink(q) != r) {
+ q = vlink(q);
+ }
+ vlink(q) += s;
+ } else {
+ vlink(rover) += s;
+ }
+ if (vlink(rover) < var_mem_max) {
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes[r] = (char) (s > 127 ? 127 : s);
+#endif
+ vlink(r) = null;
+ var_used += s; /* maintain usage statistics */
+ return r; /* this is the only exit */
+ } else {
+ normal_error("nodes","there is a problem in getting a node, case 2");
+ return null;
+ }
+ } else {
+ /* attempt to keep the free list small */
+ int x;
+ if (vlink(rover) != rover) {
+ if (t < MAX_CHAIN_SIZE) {
+ halfword l = vlink(rover);
+ vlink(rover) = free_chain[t];
+ free_chain[t] = rover;
+ rover = l;
+ while (vlink(l) != free_chain[t]) {
+ l = vlink(l);
+ }
+ vlink(l) = rover;
+ goto RETRY;
+ } else {
+ halfword l = rover;
+ while (vlink(rover) != l) {
+ if (node_size(rover) > s) {
+ goto RETRY;
+ }
+ rover = vlink(rover);
+ }
+ }
+ }
+ /* if we are still here, it was apparently impossible to get a match */
+ x = (var_mem_max >> 2) + s;
+ varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x));
+ if (varmem == NULL) {
+ overflow("node memory size", (unsigned) var_mem_max);
+ }
+ memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word));
+#ifdef CHECK_NODE_USAGE
+ varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x));
+ if (varmem_sizes == NULL) {
+ overflow("node memory size", (unsigned) var_mem_max);
+ }
+ memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char));
+#endif
+ /* todo ? it is perhaps possible to merge the new memory with an existing rover */
+ vlink(var_mem_max) = rover;
+ node_size(var_mem_max) = x;
+ while (vlink(rover) != vlink(var_mem_max)) {
+ rover = vlink(rover);
+ }
+ vlink(rover) = var_mem_max;
+ rover = var_mem_max;
+ var_mem_max += x;
+ goto RETRY;
+ }
+ } else {
+ normal_error("nodes","there is a problem in getting a node, case 3");
+ return null;
+ }
+}
+
+@ @c
+char *sprint_node_mem_usage(void)
+{
+ char *s;
+#ifdef CHECK_NODE_USAGE
+ char *ss;
+ int i;
+ int b = 0;
+ char msg[256];
+ int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 };
+ s = strdup("");
+ for (i = (var_mem_max - 1); i > my_prealloc; i--) {
+ if (varmem_sizes[i] > 0) {
+ if (type(i) > last_normal_node + last_whatsit_node) {
+ node_counts[last_normal_node + last_whatsit_node + 1]++;
+ } else if (type(i) == whatsit_node) {
+ node_counts[(subtype(i) + last_normal_node + 1)]++;
+ } else {
+ node_counts[type(i)]++;
+ }
+ }
+ }
+ for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) {
+ if (node_counts[i] > 0) {
+ int j =
+ (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0);
+ snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i],
+ get_node_name((i > last_normal_node ? whatsit_node : i), j));
+ ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1));
+ strcpy(ss, s);
+ strcat(ss, msg);
+ free(s);
+ s = ss;
+ b = 1;
+ }
+ }
+#else
+ s = strdup("");
+#endif
+ return s;
+}
+
+@ @c
+halfword list_node_mem_usage(void)
+{
+ halfword q = null;
+#ifdef CHECK_NODE_USAGE
+ halfword p = null;
+ halfword i, j;
+ char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
+ memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max);
+ for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) {
+ if (saved_varmem_sizes[i] > 0) {
+ j = copy_node(i);
+ if (p == null) {
+ q = j;
+ } else {
+ vlink(p) = j;
+ }
+ p = j;
+ }
+ }
+ free(saved_varmem_sizes);
+#endif
+ return q;
+}
+
+@ @c
+void print_node_mem_stats(void)
+{
+ int i, b;
+ halfword j;
+ char msg[256];
+ char *s;
+ int free_chain_counts[MAX_CHAIN_SIZE] = { 0 };
+ snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc));
+ tprint_nl(msg);
+ s = sprint_node_mem_usage();
+ tprint_nl(" ");
+ tprint(s);
+ free(s);
+ tprint(" nodes");
+ tprint_nl(" avail lists: ");
+ b = 0;
+ for (i = 1; i < MAX_CHAIN_SIZE; i++) {
+ for (j = free_chain[i]; j != null; j = vlink(j))
+ free_chain_counts[i]++;
+ if (free_chain_counts[i] > 0) {
+ snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]);
+ tprint(msg);
+ b = 1;
+ }
+ }
+ /* newline, if needed */
+ print_nlp();
+}
+
+/* this belongs in the web but i couldn't find the correct syntactic place */
+
+halfword new_span_node(halfword n, int s, scaled w)
+{
+ halfword p = new_node(span_node, 0);
+ span_link(p) = n;
+ span_span(p) = s;
+ width(p) = w;
+ return p;
+}
+
+@* Attribute stuff.
+
+@c
+static halfword new_attribute_node(unsigned int i, int v)
+{
+ register halfword r = get_node(attribute_node_size);
+ type(r) = attribute_node;
+ attribute_id(r) = (halfword) i;
+ attribute_value(r) = v;
+ /* not used but nicer in print */
+ subtype(r) = 0;
+ return r;
+}
+
+@ @c
+halfword copy_attribute_list(halfword n)
+{
+ halfword q = get_node(attribute_node_size);
+ register halfword p = q;
+ type(p) = attribute_list_node;
+ attr_list_ref(p) = 0;
+ n = vlink(n);
+ while (n != null) {
+ register halfword r = get_node(attribute_node_size);
+ /* the link will be fixed automatically in the next loop */
+ (void) memcpy((void *) (varmem + r), (void *) (varmem + n),
+ (sizeof(memory_word) * attribute_node_size));
+ vlink(p) = r;
+ p = r;
+ n = vlink(n);
+ }
+ return q;
+}
+
+@ @c
+void update_attribute_cache(void)
+{
+ halfword p;
+ register int i;
+ attr_list_cache = get_node(attribute_node_size);
+ type(attr_list_cache) = attribute_list_node;
+ attr_list_ref(attr_list_cache) = 0;
+ p = attr_list_cache;
+ for (i = 0; i <= max_used_attr; i++) {
+ register int v = attribute(i);
+ if (v > UNUSED_ATTRIBUTE) {
+ register halfword r = new_attribute_node((unsigned) i, v);
+ vlink(p) = r;
+ p = r;
+ }
+ }
+ if (vlink(attr_list_cache) == null) {
+ free_node(attr_list_cache, attribute_node_size);
+ attr_list_cache = null;
+ }
+ return;
+}
+
+@ @c
+void build_attribute_list(halfword b)
+{
+ if (max_used_attr >= 0) {
+ if (attr_list_cache == cache_disabled|| attr_list_cache == null) {
+ update_attribute_cache();
+ if (attr_list_cache == null)
+ return;
+ }
+ attr_list_ref(attr_list_cache)++;
+ node_attr(b) = attr_list_cache;
+ }
+}
+
+@ @c
+halfword current_attribute_list(void)
+{
+ if (max_used_attr >= 0) {
+ if (attr_list_cache == cache_disabled) {
+ update_attribute_cache();
+ }
+ return attr_list_cache ;
+ }
+ return null ;
+}
+
+
+@ @c
+void reassign_attribute(halfword n, halfword new)
+{
+ halfword old;
+ old = node_attr(n);
+ if (new == null) {
+ /* there is nothing to assign but we need to check for an old value */
+ if (old != null)
+ delete_attribute_ref(old); /* also nulls attr field of n */
+ } else if (old == null) {
+ /* nothing is assigned so we just do that now */
+ assign_attribute_ref(n,new);
+ } else if (old != new) {
+ /* something is assigned so we need to clean up and assign then */
+ delete_attribute_ref(old);
+ assign_attribute_ref(n,new);
+ }
+ /* else: same value so there is no need to assign and change the refcount */
+ node_attr(n) = new ;
+}
+
+@ @c
+void delete_attribute_ref(halfword b)
+{
+ if (b != null) {
+ if (type(b) == attribute_list_node){
+ attr_list_ref(b)--;
+ if (attr_list_ref(b) == 0) {
+ if (b == attr_list_cache)
+ attr_list_cache = cache_disabled;
+ free_node_chain(b, attribute_node_size);
+ }
+ /* maintain sanity */
+ if (attr_list_ref(b) < 0) {
+ attr_list_ref(b) = 0;
+ }
+ } else {
+ normal_error("nodes","trying to delete an attribute reference of a non attribute node");
+ }
+ }
+}
+
+void reset_node_properties(halfword b)
+{
+ if (b != null) {
+ lua_properties_reset(b);
+ }
+}
+
+@ |p| is an attr list head, or zero
+@c
+halfword do_set_attribute(halfword p, int i, int val)
+{
+ register halfword q;
+ register int j = 0;
+ if (p == null) { /* add a new head \& node */
+ q = get_node(attribute_node_size);
+ type(q) = attribute_list_node;
+ attr_list_ref(q) = 1;
+ p = new_attribute_node((unsigned) i, val);
+ vlink(q) = p;
+ return q;
+ }
+ q = p;
+ if (vlink(p) != null) {
+ while (vlink(p) != null) {
+ int t = attribute_id(vlink(p));
+ if (t == i && attribute_value(vlink(p)) == val)
+ return q; /* no need to do anything */
+ if (t >= i)
+ break;
+ j++;
+ p = vlink(p);
+ }
+
+ p = q;
+ while (j-- > 0)
+ p = vlink(p);
+ if (attribute_id(vlink(p)) == i) {
+ attribute_value(vlink(p)) = val;
+ } else { /* add a new node */
+ halfword r = new_attribute_node((unsigned) i, val);
+ vlink(r) = vlink(p);
+ vlink(p) = r;
+ }
+ return q;
+ } else {
+ normal_error("nodes","trying to set an attribute fails, case 1");
+ return null ;
+ }
+}
+
+@ @c
+void set_attribute(halfword n, int i, int val)
+{
+ register halfword p;
+ register int j = 0;
+ /* not all nodes can have an attribute list */
+ if (!nodetype_has_attributes(type(n)))
+ return;
+ /* if we have no list, we create one and quit */
+ p = node_attr(n);
+ if (p == null) { /* add a new head \& node */
+ p = get_node(attribute_node_size);
+ type(p) = attribute_list_node;
+ attr_list_ref(p) = 1;
+ node_attr(n) = p;
+ p = new_attribute_node((unsigned) i, val);
+ vlink(node_attr(n)) = p;
+ return;
+ }
+ /* we check if we have this attribute already and quit if the value stays the same */
+ if (vlink(p) != null) {
+ while (vlink(p) != null) {
+ int t = attribute_id(vlink(p));
+ if (t == i && attribute_value(vlink(p)) == val)
+ return;
+ if (t >= i)
+ break;
+ j++;
+ p = vlink(p);
+ }
+ /* j has now the position (if found) .. we assume a sorted list ! */
+ p = node_attr(n);
+
+ if (attr_list_ref(p) == 0 ) {
+ /* the list is invalid i.e. freed already */
+ formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n);
+ /* the still dangling list gets ref count 1 */
+ attr_list_ref(p) = 1;
+ } else if (attr_list_ref(p) == 1) {
+ /* this can really happen HH-LS */
+ if (p == attr_list_cache) {
+ /* we can invalidate the cache setting */
+ /* attr_list_cache = cache_disabled */
+ /* or save the list, as done below */
+ p = copy_attribute_list(p);
+ node_attr(n) = p;
+ /* the copied list gets ref count 1 */
+ attr_list_ref(p) = 1;
+ }
+ } else {
+ /* the list is used multiple times so we make a copy */
+ p = copy_attribute_list(p);
+ /* we decrement the ref count or the original */
+ delete_attribute_ref(node_attr(n));
+ node_attr(n) = p;
+ /* the copied list gets ref count 1 */
+ attr_list_ref(p) = 1;
+ }
+
+
+ /* we go to position j in the list */
+ while (j-- > 0)
+ p = vlink(p);
+ /* if we have a hit we just set the value otherwise we add a new node */
+ if (attribute_id(vlink(p)) == i) {
+ attribute_value(vlink(p)) = val;
+ } else { /* add a new node */
+ halfword r = new_attribute_node((unsigned) i, val);
+ vlink(r) = vlink(p);
+ vlink(p) = r;
+ }
+ } else {
+ normal_error("nodes","trying to set an attribute fails, case 2");
+ }
+}
+
+@ @c
+int unset_attribute(halfword n, int i, int val)
+{
+ register halfword p;
+ register int t;
+ register int j = 0;
+
+ if (!nodetype_has_attributes(type(n)))
+ return null;
+ p = node_attr(n);
+ if (p == null)
+ return UNUSED_ATTRIBUTE;
+ if (attr_list_ref(p) == 0) {
+ formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n);
+ return UNUSED_ATTRIBUTE;
+ }
+ if (vlink(p) != null) {
+ while (vlink(p) != null) {
+ t = attribute_id(vlink(p));
+ if (t > i)
+ return UNUSED_ATTRIBUTE;
+ if (t == i) {
+ p = vlink(p);
+ break;
+ }
+ j++;
+ p = vlink(p);
+ }
+ if (attribute_id(p) != i)
+ return UNUSED_ATTRIBUTE;
+ /* if we are still here, the attribute exists */
+ p = node_attr(n);
+ if (attr_list_ref(p) > 1 || p == attr_list_cache) {
+ halfword q = copy_attribute_list(p);
+ if (attr_list_ref(p) > 1) {
+ delete_attribute_ref(node_attr(n));
+ }
+ attr_list_ref(q) = 1;
+ node_attr(n) = q;
+ }
+ p = vlink(node_attr(n));
+ while (j-- > 0)
+ p = vlink(p);
+ t = attribute_value(p);
+ if (val == UNUSED_ATTRIBUTE || t == val) {
+ attribute_value(p) = UNUSED_ATTRIBUTE;
+ }
+ return t;
+ } else {
+ normal_error("nodes","trying to unset an attribute fails");
+ return null;
+ }
+}
+
+@ @c
+int has_attribute(halfword n, int i, int val)
+{
+ register halfword p;
+ if (!nodetype_has_attributes(type(n)))
+ return UNUSED_ATTRIBUTE;
+ p = node_attr(n);
+ if (p == null || vlink(p) == null)
+ return UNUSED_ATTRIBUTE;
+ p = vlink(p);
+ while (p != null) {
+ if (attribute_id(p) == i) {
+ int ret = attribute_value(p);
+ if (val == UNUSED_ATTRIBUTE || val == ret)
+ return ret;
+ return UNUSED_ATTRIBUTE;
+ } else if (attribute_id(p) > i) {
+ return UNUSED_ATTRIBUTE;
+ }
+ p = vlink(p);
+ }
+ return UNUSED_ATTRIBUTE;
+}
+
+@ @c
+void print_short_node_contents(halfword p)
+{
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ case ins_node:
+ case whatsit_node:
+ case mark_node:
+ case adjust_node:
+ case unset_node:
+ print_char('[');
+ print_char(']');
+ break;
+ case rule_node:
+ print_char('|');
+ break;
+ case glue_node:
+ if (! glue_is_zero(p))
+ print_char(' ');
+ break;
+ case math_node:
+ print_char('$');
+ break;
+ case disc_node:
+ short_display(vlink(pre_break(p)));
+ short_display(vlink(post_break(p)));
+ break;
+ }
+}
+
+@ @c
+static void show_pdftex_whatsit_rule_spec(int p)
+{
+ tprint("(");
+ print_rule_dimen(height(p));
+ print_char('+');
+ print_rule_dimen(depth(p));
+ tprint(")x");
+ print_rule_dimen(width(p));
+}
+
+@ Each new type of node that appears in our data structure must be capable
+of being displayed, copied, destroyed, and so on. The routines that we
+need for write-oriented whatsits are somewhat like those for mark nodes;
+other extensions might, of course, involve more subtlety here.
+
+@c
+static void print_write_whatsit(const char *s, pointer p)
+{
+ tprint_esc(s);
+ if (write_stream(p) < 16)
+ print_int(write_stream(p));
+ else if (write_stream(p) == 16)
+ print_char('*');
+ else
+ print_char('-');
+}
+
+@ @c
+static void show_node_wrapup_core(int p)
+{
+ switch (subtype(p)) {
+ case open_node:
+ print_write_whatsit("openout", p);
+ print_char('=');
+ print_file_name(open_name(p), open_area(p), open_ext(p));
+ break;
+ case write_node:
+ print_write_whatsit("write", p);
+ print_mark(write_tokens(p));
+ break;
+ case close_node:
+ print_write_whatsit("closeout", p);
+ break;
+ case special_node:
+ tprint_esc("special");
+ print_mark(write_tokens(p));
+ break;
+ case late_lua_node:
+ show_late_lua(p);
+ break;
+ case save_pos_node:
+ tprint_esc("savepos");
+ break;
+ case user_defined_node:
+ tprint_esc("whatsit");
+ print_int(user_node_id(p));
+ print_char('=');
+ switch (user_node_type(p)) {
+ case 'a':
+ tprint("<>");
+ break;
+ case 'n':
+ tprint("[");
+ show_node_list(user_node_value(p));
+ tprint("]");
+ break;
+ case 's':
+ print_char('"');
+ print(user_node_value(p));
+ print_char('"');
+ break;
+ case 't':
+ print_mark(user_node_value(p));
+ break;
+ default: /* only 'd' */
+ print_int(user_node_value(p));
+ break;
+ }
+ break;
+ }
+}
+
+void show_node_wrapup_dvi(int p)
+{
+}
+
+void show_node_wrapup_pdf(int p)
+{
+ switch (subtype(p)) {
+ case pdf_literal_node:
+ show_pdf_literal(p);
+ break;
+ case pdf_colorstack_node:
+ tprint_esc("pdfcolorstack ");
+ print_int(pdf_colorstack_stack(p));
+ switch (pdf_colorstack_cmd(p)) {
+ case colorstack_set:
+ tprint(" set ");
+ break;
+ case colorstack_push:
+ tprint(" push ");
+ break;
+ case colorstack_pop:
+ tprint(" pop");
+ break;
+ case colorstack_current:
+ tprint(" current");
+ break;
+ default:
+ confusion("colorstack");
+ break;
+ }
+ if (pdf_colorstack_cmd(p) <= colorstack_data)
+ print_mark(pdf_colorstack_data(p));
+ break;
+ case pdf_setmatrix_node:
+ tprint_esc("pdfsetmatrix");
+ print_mark(pdf_setmatrix_data(p));
+ break;
+ case pdf_save_node:
+ tprint_esc("pdfsave");
+ break;
+ case pdf_restore_node:
+ tprint_esc("pdfrestore");
+ break;
+ case pdf_refobj_node:
+ tprint_esc("pdfrefobj");
+ if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) {
+ if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
+ tprint(" attr");
+ lua_rawgeti(Luas, LUA_REGISTRYINDEX,
+ obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)));
+ print_char(' ');
+ tprint((const char *) lua_tostring(Luas, -1));
+ lua_pop(Luas, 1);
+ }
+ tprint(" stream");
+ }
+ if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p)))
+ tprint(" file");
+ if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
+ lua_rawgeti(Luas, LUA_REGISTRYINDEX,
+ obj_obj_data(static_pdf, pdf_obj_objnum(p)));
+ print_char(' ');
+ tprint((const char *) lua_tostring(Luas, -1));
+ lua_pop(Luas, 1);
+ }
+ break;
+ case pdf_annot_node:
+ tprint_esc("pdfannot");
+ show_pdftex_whatsit_rule_spec(p);
+ print_mark(pdf_annot_data(p));
+ break;
+ case pdf_start_link_node:
+ tprint_esc("pdfstartlink");
+ show_pdftex_whatsit_rule_spec(p);
+ if (pdf_link_attr(p) != null) {
+ tprint(" attr");
+ print_mark(pdf_link_attr(p));
+ }
+ tprint(" action");
+ if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) {
+ tprint(" user");
+ print_mark(pdf_action_tokens(pdf_link_action(p)));
+ return;
+ }
+ if (pdf_action_file(pdf_link_action(p)) != null) {
+ tprint(" file");
+ print_mark(pdf_action_file(pdf_link_action(p)));
+ }
+ switch (pdf_action_type(pdf_link_action(p))) {
+ case pdf_action_goto:
+ if (pdf_action_named_id(pdf_link_action(p)) > 0) {
+ tprint(" goto name");
+ print_mark(pdf_action_id(pdf_link_action(p)));
+ } else {
+ tprint(" goto num");
+ print_int(pdf_action_id(pdf_link_action(p)));
+ }
+ break;
+ case pdf_action_page:
+ tprint(" page");
+ print_int(pdf_action_id(pdf_link_action(p)));
+ print_mark(pdf_action_tokens(pdf_link_action(p)));
+ break;
+ case pdf_action_thread:
+ if (pdf_action_named_id(pdf_link_action(p)) > 0) {
+ tprint(" thread name");
+ print_mark(pdf_action_id(pdf_link_action(p)));
+ } else {
+ tprint(" thread num");
+ print_int(pdf_action_id(pdf_link_action(p)));
+ }
+ break;
+ default:
+ normal_error("pdf backend", "unknown action type for link");
+ break;
+ }
+ break;
+ case pdf_end_link_node:
+ tprint_esc("pdfendlink");
+ break;
+ case pdf_dest_node:
+ tprint_esc("pdfdest");
+ if (pdf_dest_named_id(p) > 0) {
+ tprint(" name");
+ print_mark(pdf_dest_id(p));
+ } else {
+ tprint(" num");
+ print_int(pdf_dest_id(p));
+ }
+ print_char(' ');
+ switch (pdf_dest_type(p)) {
+ case pdf_dest_xyz:
+ tprint("xyz");
+ if (pdf_dest_xyz_zoom(p) != null) {
+ tprint(" zoom");
+ print_int(pdf_dest_xyz_zoom(p));
+ }
+ break;
+ case pdf_dest_fitbh:
+ tprint("fitbh");
+ break;
+ case pdf_dest_fitbv:
+ tprint("fitbv");
+ break;
+ case pdf_dest_fitb:
+ tprint("fitb");
+ break;
+ case pdf_dest_fith:
+ tprint("fith");
+ break;
+ case pdf_dest_fitv:
+ tprint("fitv");
+ break;
+ case pdf_dest_fitr:
+ tprint("fitr");
+ show_pdftex_whatsit_rule_spec(p);
+ break;
+ case pdf_dest_fit:
+ tprint("fit");
+ break;
+ default:
+ tprint("unknown!");
+ break;
+ }
+ break;
+ case pdf_thread_node:
+ case pdf_start_thread_node:
+ if (subtype(p) == pdf_thread_node)
+ tprint_esc("pdfthread");
+ else
+ tprint_esc("pdfstartthread");
+ tprint("(");
+ print_rule_dimen(height(p));
+ print_char('+');
+ print_rule_dimen(depth(p));
+ tprint(")x");
+ print_rule_dimen(width(p));
+ if (pdf_thread_attr(p) != null) {
+ tprint(" attr");
+ print_mark(pdf_thread_attr(p));
+ }
+ if (pdf_thread_named_id(p) > 0) {
+ tprint(" name");
+ print_mark(pdf_thread_id(p));
+ } else {
+ tprint(" num");
+ print_int(pdf_thread_id(p));
+ }
+ break;
+ case pdf_end_thread_node:
+ tprint_esc("pdfendthread");
+ break;
+ default:
+ break;
+ }
+}
+
+@ Now we are ready for |show_node_list| itself. This procedure has been
+ written to be ``extra robust'' in the sense that it should not crash or get
+ into a loop even if the data structures have been messed up by bugs in
+ the rest of the program. You can safely call its parent routine
+ |show_box(p)| for arbitrary values of |p| when you are debugging \TeX.
+ However, in the presence of bad data, the procedure may
+ fetch a |memory_word| whose variant is different from the way it was stored;
+ for example, it might try to read |mem[p].hh| when |mem[p]|
+ contains a scaled integer, if |p| is a pointer that has been
+ clobbered or chosen at random.
+
+
+@ |str_room| need not be checked; see |show_box|
+
+@ Recursive calls on |show_node_list| therefore use the following pattern:
+@c
+#define node_list_display(A) do { \
+ append_char('.'); \
+ show_node_list(A); \
+ flush_char(); \
+} while (0)
+
+#define node_list_display_x(A,B) do { \
+ if ((B) != null) { \
+ append_char('.'); \
+ append_char(A); \
+ append_char(' '); \
+ show_node_list(B); \
+ flush_char(); \
+ flush_char(); \
+ flush_char(); \
+ } \
+} while (0)
+
+/* prints a node list symbolically */
+
+void show_node_list(int p)
+{
+ int n = 0; /* the number of items already printed at this level */
+ halfword w;
+ real g; /* a glue ratio, as a floating point number */
+ if ((int) cur_length > depth_threshold) {
+ if (p > null)
+ tprint(" []"); /* indicate that there's been some truncation */
+ return;
+ }
+ while (p != null) {
+ print_ln();
+ print_current_string(); /* display the nesting history */
+ if (tracing_online_par < -2)
+ print_int(p);
+ incr(n);
+ if (n > breadth_max) { /* time to stop */
+ tprint("etc.");
+ return;
+ }
+ /* Display node |p| */
+ if (is_char_node(p)) {
+ print_font_and_char(p);
+ if (is_ligature(p)) {
+ /* Display ligature |p|; */
+ tprint(" (ligature ");
+ if (is_leftboundary(p))
+ print_char('|');
+ font_in_short_display = font(p);
+ short_display(lig_ptr(p));
+ if (is_rightboundary(p))
+ print_char('|');
+ print_char(')');
+ }
+ } else {
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ case unset_node:
+ /* Display box |p|; */
+ if (type(p) == hlist_node)
+ tprint_esc("h");
+ else if (type(p) == vlist_node)
+ tprint_esc("v");
+ else
+ tprint_esc("unset");
+ tprint("box(");
+ print_scaled(height(p));
+ print_char('+');
+ print_scaled(depth(p));
+ tprint(")x");
+ print_scaled(width(p));
+ if (type(p) == unset_node) {
+ /* Display special fields of the unset node |p|; */
+ if (span_count(p) != min_quarterword) {
+ tprint(" (");
+ print_int(span_count(p) + 1);
+ tprint(" columns)");
+ }
+ if (glue_stretch(p) != 0) {
+ tprint(", stretch ");
+ print_glue(glue_stretch(p), glue_order(p), NULL);
+ }
+ if (glue_shrink(p) != 0) {
+ tprint(", shrink ");
+ print_glue(glue_shrink(p), glue_sign(p), NULL);
+ }
+ } else {
+ /* Display the value of |glue_set(p)| */
+ /* The code will have to change in this place if |glue_ratio| is
+ a structured type instead of an ordinary |real|. Note that this routine
+ should avoid arithmetic errors even if the |glue_set| field holds an
+ arbitrary random value. The following code assumes that a properly
+ formed nonzero |real| number has absolute value $2^{20}$ or more when
+ it is regarded as an integer; this precaution was adequate to prevent
+ floating point underflow on the author's computer.
+ */
+
+ g = (real) (glue_set(p));
+ if ((g != 0.0) && (glue_sign(p) != normal)) {
+ tprint(", glue set ");
+ if (glue_sign(p) == shrinking)
+ tprint("- ");
+ if (g > 20000.0 || g < -20000.0) {
+ if (g > 0.0)
+ print_char('>');
+ else
+ tprint("< -");
+ print_glue(20000 * unity, glue_order(p), NULL);
+ } else {
+ print_glue(round(unity * g), glue_order(p), NULL);
+ }
+ }
+
+ if (shift_amount(p) != 0) {
+ tprint(", shifted ");
+ print_scaled(shift_amount(p));
+ }
+ tprint(", direction ");
+ print_dir(box_dir(p));
+ }
+ node_list_display(list_ptr(p)); /* recursive call */
+ break;
+ case rule_node:
+ /* Display rule |p|; */
+ if (subtype(p) == normal_rule) {
+ tprint_esc("rule(");
+ } else if (subtype(p) == empty_rule) {
+ tprint_esc("norule(");
+ } else if (subtype(p) == user_rule) {
+ tprint_esc("userrule(");
+ } else if (subtype(p) == box_rule) {
+ tprint_esc("box(");
+ } else if (subtype(p) == image_rule) {
+ tprint_esc("image(");
+ }
+ print_rule_dimen(height(p));
+ print_char('+');
+ print_rule_dimen(depth(p));
+ tprint(")x");
+ print_rule_dimen(width(p));
+ break;
+ case ins_node:
+ /* Display insertion |p|; */
+ tprint_esc("insert");
+ print_int(subtype(p));
+ tprint(", natural size ");
+ print_scaled(height(p));
+ tprint("; split(");
+ print_spec(split_top_ptr(p), NULL);
+ print_char(',');
+ print_scaled(depth(p));
+ tprint("); float cost ");
+ print_int(float_cost(p));
+ node_list_display(ins_ptr(p)); /* recursive call */
+ break;
+ case dir_node:
+ if (dir_dir(p) < 0) {
+ tprint_esc("enddir");
+ print_char(' ');
+ print_dir(dir_dir(p) + dir_swap);
+ } else {
+ tprint_esc("begindir");
+ print_char(' ');
+ print_dir(dir_dir(p));
+ }
+ break;
+ case local_par_node:
+ tprint_esc("localpar");
+ append_char('.');
+ print_ln();
+ print_current_string();
+ tprint_esc("localinterlinepenalty");
+ print_char('=');
+ print_int(local_pen_inter(p));
+ print_ln();
+ print_current_string();
+ tprint_esc("localbrokenpenalty");
+ print_char('=');
+ print_int(local_pen_broken(p));
+ print_ln();
+ print_current_string();
+ tprint_esc("localleftbox");
+ if (local_box_left(p) == null) {
+ tprint("=null");
+ } else {
+ append_char('.');
+ show_node_list(local_box_left(p));
+ decr(cur_length);
+ }
+ print_ln();
+ print_current_string();
+ tprint_esc("localrightbox");
+ if (local_box_right(p) == null) {
+ tprint("=null");
+ } else {
+ append_char('.');
+ show_node_list(local_box_right(p));
+ decr(cur_length);
+ }
+ decr(cur_length);
+ break;
+ case boundary_node:
+ if (subtype(p)==0) {
+ tprint_esc("noboundary");
+ } else {
+ switch (subtype(p)) {
+ case 1:
+ tprint_esc("boundary");
+ break;
+ case 2:
+ tprint_esc("protrusionboundary");
+ break;
+ case 3:
+ tprint_esc("wordboundary");
+ break;
+ default:
+ tprint_esc("boundary");
+ print_char(':');
+ print_int(subtype(p));
+ break;
+ }
+ print_char('=');
+ print_int(boundary_value(p));
+ }
+ break;
+ case whatsit_node:
+ w = subtype(p) ;
+ if (w >= backend_first_pdf_whatsit) {
+ show_node_wrapup_pdf(p);
+ } else if (w >= backend_first_dvi_whatsit) {
+ show_node_wrapup_dvi(p);
+ } else {
+ show_node_wrapup_core(p);
+ }
+ break;
+ case glue_node:
+ /* Display glue |p|; */
+ if (subtype(p) >= a_leaders) {
+ /* Display leaders |p|; */
+ tprint_esc("");
+ switch (subtype(p)) {
+ case a_leaders:
+ break;
+ case c_leaders:
+ print_char('c');
+ break;
+ case x_leaders:
+ print_char('x');
+ break;
+ case g_leaders:
+ print_char('g');
+ break;
+ default:
+ normal_warning("nodes","weird glue leader subtype ignored");
+ }
+ tprint("leaders ");
+ print_spec(p, NULL);
+ node_list_display(leader_ptr(p)); /* recursive call */
+ } else {
+ tprint_esc("glue");
+ if (subtype(p) != normal) {
+ print_char('(');
+ if ((subtype(p) - 1) < thin_mu_skip_code) {
+ print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1));
+ } else if (subtype(p) < cond_math_glue) {
+ print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1));
+ } else if (subtype(p) == cond_math_glue) {
+ tprint_esc("nonscript");
+ } else {
+ tprint_esc("mskip");
+ }
+ print_char(')');
+ }
+ if (subtype(p) != cond_math_glue) {
+ print_char(' ');
+ if (subtype(p) < cond_math_glue)
+ print_spec(p, NULL);
+ else
+ print_spec(p, "mu");
+ }
+ }
+ break;
+ case margin_kern_node:
+ tprint_esc("kern");
+ print_scaled(width(p));
+ if (subtype(p) == left_side)
+ tprint(" (left margin)");
+ else
+ tprint(" (right margin)");
+ break;
+ case kern_node:
+ /* Display kern |p|; */
+ /* An ``explicit'' kern value is indicated implicitly by an explicit space. */
+ if (subtype(p) != mu_glue) {
+ tprint_esc("kern");
+ if (subtype(p) != normal)
+ print_char(' ');
+ print_scaled(width(p));
+ if (subtype(p) == accent_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 (!glue_is_zero(p)) {
+ tprint(", glued ");
+ print_spec(p, NULL);
+ } else if (surround(p) != 0) {
+ tprint(", surrounded ");
+ print_scaled(surround(p));
+ }
+ break;
+ case penalty_node:
+ /* Display penalty |p|; */
+ tprint_esc("penalty ");
+ print_int(penalty(p));
+ break;
+ case disc_node:
+ /* Display discretionary |p|; */
+ /* The |post_break| list of a discretionary node is indicated by a prefixed
+ `\.{\char'174}' instead of the `\..' before the |pre_break| list. */
+ /* We're not compatible anyway so ...
+ tprint_esc("discretionary");
+ print_int(disc_penalty(p));
+ print_char('|');
+ if (vlink(no_break(p)) != null) {
+ tprint(" replacing ");
+ node_list_display(vlink(no_break(p)));
+ }
+ node_list_display(vlink(pre_break(p)));
+ append_char('|');
+ show_node_list(vlink(post_break(p)));
+ flush_char();
+ */
+ tprint_esc("discretionary");
+ tprint(" (penalty ");
+ print_int(disc_penalty(p));
+ print_char(')');
+ node_list_display_x('<',vlink(pre_break(p)));
+ node_list_display_x('>',vlink(post_break(p)));
+ node_list_display_x('=',vlink(no_break(p)));
+ break;
+ case mark_node:
+ /* Display mark |p|; */
+ tprint_esc("mark");
+ if (mark_class(p) != 0) {
+ print_char('s');
+ print_int(mark_class(p));
+ }
+ print_mark(mark_ptr(p));
+ break;
+ case adjust_node:
+ /* Display adjustment |p|; */
+ tprint_esc("vadjust");
+ if (subtype(p) != 0)
+ tprint(" pre ");
+ node_list_display(adjust_ptr(p)); /* recursive call */
+ break;
+ case glue_spec_node:
+ tprint("<glue_spec ");
+ print_spec(p, NULL);
+ tprint(">");
+ break;
+ default:
+ show_math_node(p);
+ break;
+ }
+ }
+ p = vlink(p);
+ }
+}
+
+@ This routine finds the 'base' width of a horizontal box, using the same logic
+ that \TeX82 used for \.{\\predisplaywidth} */
+
+@c
+static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width)
+{
+ scaled d; /* increment to |v| */
+ scaled w = -max_dimen; /* calculated |size| */
+ scaled v = initial_width; /* |w| plus possible glue amount */
+ while (p != null) {
+ if (is_char_node(p)) {
+ d = glyph_width(p);
+ goto FOUND;
+ }
+ switch (type(p)) {
+ case hlist_node:
+ case vlist_node:
+ case rule_node:
+ d = width(p);
+ goto FOUND;
+ break;
+ case margin_kern_node:
+ d = width(p);
+ break;
+ case kern_node:
+ d = width(p);
+ break;
+ case disc_node:
+ /* at the end of the line we should actually take the pre */
+ if (no_break(p) != null) {
+ d = get_actual_box_width(r,vlink_no_break(p),0);
+ if (d <= -max_dimen || d >= max_dimen) {
+ d = 0;
+ }
+ } else {
+ d = 0;
+ }
+ goto FOUND;
+ break;
+ case math_node:
+ /* begin mathskip code */
+ if (glue_is_zero(p)) {
+ d = surround(p);
+ break;
+ } else {
+ /* fall through */
+ }
+ /* end mathskip code */
+ case glue_node:
+ /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set|
+ values, since such values are subject to system-dependent rounding.
+ System-dependent numbers are not allowed to infiltrate parameters like
+ |pre_display_size|, since \TeX82 is supposed to make the same decisions on all
+ machines.
+ */
+ d = width(p);
+ if (glue_sign(r) == stretching) {
+ if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0))
+ v = max_dimen;
+ } else if (glue_sign(r) == shrinking) {
+ if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0))
+ v = max_dimen;
+ }
+ if (subtype(p) >= a_leaders)
+ goto FOUND;
+ break;
+ default:
+ d = 0;
+ break;
+ }
+ if (v < max_dimen)
+ v = v + d;
+ goto NOT_FOUND;
+ FOUND:
+ if (v < max_dimen) {
+ v = v + d;
+ w = v;
+ } else {
+ w = max_dimen;
+ break;
+ }
+ NOT_FOUND:
+ p = vlink(p);
+ }
+ return w;
+}
+
+pointer actual_box_width(pointer r, scaled base_width)
+{
+ /* often this is the same as:
+ return + shift_amount(r) + base_width +
+ natural_sizes(list_ptr(r),null,(glue_ratio) glue_set(r),glue_sign(r),glue_order(r),box_dir(r));
+ */
+ return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width);
+}
+
+@ @c
+halfword tail_of_list(halfword p)
+{
+ halfword q = p;
+ while (vlink(q) != null)
+ q = vlink(q);
+ return q;
+}
+
+
+
+@ @c
+int var_used;
+
+@ Attribute lists need two extra globals to increase processing efficiency.
+|max_used_attr| limits the test loop that checks for set attributes, and
+|attr_list_cache| contains a pointer to an already created attribute list. It is
+set to the special value |cache_disabled| when the current value can no longer be
+trusted: after an assignment to an attribute register, and after a group has
+ended.
+
+@c
+int max_used_attr; /* maximum assigned attribute id */
+halfword attr_list_cache;
+
+@ From the computer's standpoint, \TeX's chief mission is to create
+horizontal and vertical lists. We shall now investigate how the elements
+of these lists are represented internally as nodes in the dynamic memory.
+
+A horizontal or vertical list is linked together by |link| fields in
+the first word of each node. Individual nodes represent boxes, glue,
+penalties, or special things like discretionary hyphens; because of this
+variety, some nodes are longer than others, and we must distinguish different
+kinds of nodes. We do this by putting a `|type|' field in the first word,
+together with the link and an optional `|subtype|'.
+
+@ Character nodes appear only in horizontal lists, never in vertical lists.
+
+An |hlist_node| stands for a box that was made from a horizontal list.
+Each |hlist_node| is seven words long, and contains the following fields
+(in addition to the mandatory |type| and |link|, which we shall not
+mention explicitly when discussing the other node types): The |height| and
+|width| and |depth| are scaled integers denoting the dimensions of the
+box. There is also a |shift_amount| field, a scaled integer indicating
+how much this box should be lowered (if it appears in a horizontal list),
+or how much it should be moved to the right (if it appears in a vertical
+list). There is a |list_ptr| field, which points to the beginning of the
+list from which this box was fabricated; if |list_ptr| is |null|, the box
+is empty. Finally, there are three fields that represent the setting of
+the glue: |glue_set(p)| is a word of type |glue_ratio| that represents
+the proportionality constant for glue setting; |glue_sign(p)| is
+|stretching| or |shrinking| or |normal| depending on whether or not the
+glue should stretch or shrink or remain rigid; and |glue_order(p)|
+specifies the order of infinity to which glue setting applies (|normal|,
+|sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used.
+
+@ The |new_null_box| function returns a pointer to an |hlist_node| in
+which all subfields have the values corresponding to `\.{\\hbox\{\}}'.
+The |subtype| field is set to |min_quarterword|, since that's the desired
+|span_count| value if this |hlist_node| is changed to an |unset_node|.
+
+@c
+halfword new_null_box(void)
+{ /* creates a new box node */
+ halfword p = new_node(hlist_node, min_quarterword);
+ box_dir(p) = text_direction_par;
+ return p;
+}
+
+@ A |vlist_node| is like an |hlist_node| in all respects except that it
+contains a vertical list.
+
+@ A |rule_node| stands for a solid black rectangle; it has |width|,
+|depth|, and |height| fields just as in an |hlist_node|. However, if
+any of these dimensions is $-2^{30}$, the actual value will be determined
+by running the rule up to the boundary of the innermost enclosing box.
+This is called a ``running dimension.'' The |width| is never running in
+an hlist; the |height| and |depth| are never running in a~vlist.
+
+@ A new rule node is delivered by the |new_rule| function. It
+makes all the dimensions ``running,'' so you have to change the
+ones that are not allowed to run.
+
+@c
+halfword new_rule(int s)
+{
+ halfword p = new_node(rule_node,s);
+ return p;
+}
+
+@ Insertions are represented by |ins_node| records, where the |subtype|
+indicates the corresponding box number. For example, `\.{\\insert 250}'
+leads to an |ins_node| whose |subtype| is |250+min_quarterword|.
+The |height| field of an |ins_node| is slightly misnamed; it actually holds
+the natural height plus depth of the vertical list being inserted.
+The |depth| field holds the |split_max_depth| to be used in case this
+insertion is split, and the |split_top_ptr| points to the corresponding
+|split_top_skip|. The |float_cost| field holds the |floating_penalty| that
+will be used if this insertion floats to a subsequent page after a
+split insertion of the same class. There is one more field, the
+|ins_ptr|, which points to the beginning of the vlist for the insertion.
+
+@ A |mark_node| has a |mark_ptr| field that points to the reference count
+of a token list that contains the user's \.{\\mark} text.
+In addition there is a |mark_class| field that contains the mark class.
+
+@ An |adjust_node|, which occurs only in horizontal lists,
+specifies material that will be moved out into the surrounding
+vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}'
+operation. The |adjust_ptr| field points to the vlist containing this
+material.
+
+@ A |glyph_node|, which occurs only in horizontal lists, specifies a
+glyph in a particular font, along with its attribute list. Older
+versions of \TeX\ could use token memory for characters, because the
+font,char combination would fit in a single word (both values were
+required to be strictly less than $2^{16}$). In LuaTeX, room is
+needed for characters that are larger than that, as well as a pointer
+to a potential attribute list, and the two displacement values.
+
+In turn, that made the node so large that it made sense to merge
+ligature glyphs as well, as that requires only one extra pointer. A
+few extra classes of glyph nodes will be introduced later. The
+unification of all those types makes it easier to manipulate lists of
+glyphs. The subtype differentiates various glyph kinds.
+
+First, here is a function that returns a pointer to a glyph node for a given
+glyph in a given font. If that glyph doesn't exist, |null| is returned
+instead. Nodes of this subtype are directly created only for accents
+and their base (through |make_accent|), and math nucleus items (in the
+conversion from |mlist| to |hlist|).
+
+@c
+halfword new_glyph(int f, int c)
+{
+ halfword p = null; /* the new node */
+ if ((f == 0) || (char_exists(f, c))) {
+ p = new_glyph_node();
+ set_to_glyph(p);
+ font(p) = f;
+ character(p) = c;
+ }
+ return p;
+}
+
+@ A subset of the glyphs nodes represent ligatures: characters
+fabricated from the interaction of two or more actual characters. The
+characters that generated the ligature have not been forgotten, since
+they are needed for diagnostic messages; the |lig_ptr| field points to
+a linked list of character nodes for all original characters that have
+been deleted. (This list might be empty if the characters that
+generated the ligature were retained in other nodes.)
+
+The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if
+the original source of the ligature included implicit left and/or
+right boundaries. These nodes are created by the C function |new_ligkern|.
+
+A third general type of glyphs could be called a character, as it
+only appears in lists that are not yet processed by the ligaturing and
+kerning steps of the program.
+
+|main_control| inserts these, and they are later converted to
+|subtype_normal| by |new_ligkern|.
+
+@c
+quarterword norm_min(int h)
+{
+ if (h <= 0)
+ return 1;
+ else if (h >= 255)
+ return 255;
+ else
+ return (quarterword) h;
+}
+
+halfword new_char(int f, int c)
+{
+ halfword p; /* the new node */
+ p = new_glyph_node();
+ set_to_character(p);
+ font(p) = f;
+ character(p) = c;
+ lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par);
+ return p;
+}
+
+@ Left and right ghost glyph nodes are the result of \.{\\leftghost}
+and \.{\\rightghost}, respectively. They are going to be removed by
+|new_ligkern|, at the end of which they are no longer needed.
+
+@ Here are a few handy helpers used by the list output routines.
+
+@c
+scaled glyph_width(halfword p)
+{
+ scaled w = char_width(font(p), character(p));
+ return w;
+}
+
+scaled glyph_height(halfword p)
+{
+ scaled w = char_height(font(p), character(p)) + y_displace(p);
+ if (w < 0)
+ w = 0;
+ return w;
+}
+
+scaled glyph_depth(halfword p)
+{
+ scaled w = char_depth(font(p), character(p));
+ if (y_displace(p) > 0)
+ w = w - y_displace(p);
+ if (w < 0)
+ w = 0;
+ return w;
+}
+
+@ A |disc_node|, which occurs only in horizontal lists, specifies a
+``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text
+that starts at |pre_break(p)| will precede the break, the text that starts at
+|post_break(p)| will follow the break, and text that appears in
+|no_break(p)| nodes will be ignored. For example, an ordinary
+discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with
+|pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|,
+and |no_break=null|.
+
+{TODO: Knuth said: All three of the discretionary texts must be lists
+that consist entirely of character, kern, box and rule nodes.}
+
+If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this
+break. Otherwise the |hyphen_penalty| will be charged. The texts will
+actually be substituted into the list by the line-breaking algorithm if it
+decides to make the break, and the discretionary node will disappear at
+that time; thus, the output routine sees only discretionaries that were
+not chosen.
+
+@c
+halfword new_disc(void)
+{ /* creates an empty |disc_node| */
+ halfword p = new_node(disc_node, 0);
+ disc_penalty(p) = hyphen_penalty_par;
+ return p;
+}
+
+@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
+|subtype| field in its first word says what `\\{whatsit}' it is, and
+implicitly determines the node size (which must be 2 or more) and the
+format of the remaining words. When a |whatsit_node| is encountered
+in a list, special actions are invoked; knowledgeable people who are
+careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
+things by adding code at the end of the program. For example, there
+might be a `\TeX nicolor' extension to specify different colors of ink,
+@^extensions to \TeX@>
+and the whatsit node might contain the desired parameters.
+
+The present implementation of \TeX\ treats the features associated with
+`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
+illustrate how such routines might be coded. We shall defer further
+discussion of extensions until the end of this program.
+
+@ A |math_node|, which occurs only in horizontal lists, appears before and
+after mathematical formulas. The |subtype| field is |before| before the
+formula and |after| after it. There is a |surround| field, which represents
+the amount of surrounding space inserted by \.{\\mathsurround}.
+
+@c
+halfword new_math(scaled w, int s)
+{
+ halfword p = new_node(math_node, s);
+ surround(p) = w;
+ return p;
+}
+
+@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|,
+|rule_node|, |ins_node|, |mark_node|, |adjust_node|,
+|disc_node|, |whatsit_node|, and |math_node| are at the low end of the
+type codes, by permitting a break at glue in a list if and only if the
+|type| of the previous node is less than |math_node|. Furthermore, a
+node is discarded after a break if its type is |math_node| or~more.
+
+@ A |glue_node| represents glue in a list. However, it is really only
+a pointer to a separate glue specification, since \TeX\ makes use of the
+fact that many essentially identical nodes of glue are usually present.
+If |p| points to a |glue_node|, |glue_ptr(p)| points to
+another packet of words that specify the stretch and shrink components, etc.
+
+Glue nodes also serve to represent leaders; the |subtype| is used to
+distinguish between ordinary glue (which is called |normal|) and the three
+kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|).
+The |leader_ptr| field points to a rule node or to a box node containing the
+leaders; it is set to |null| in ordinary glue nodes.
+
+Many kinds of glue are computed from \TeX's ``skip'' parameters, and
+it is helpful to know which parameter has led to a particular glue node.
+Therefore the |subtype| is set to indicate the source of glue, whenever
+it originated as a parameter. We will be defining symbolic names for the
+parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|,
+etc.); it suffices for now to say that the |subtype| of parametric glue
+will be the same as the parameter number, plus~one.
+
+@ In math formulas there are two more possibilities for the |subtype| in a
+glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu}
+instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}'
+feature that cancels the glue node immediately following if it appears
+in a subscript.
+
+@ A glue specification has a halfword reference count in its first word,
+@^reference counts@>
+representing |null| plus the number of glue nodes that point to it (less one).
+Note that the reference count appears in the same position as
+the |link| field in list nodes; this is the field that is initialized
+to |null| when a node is allocated, and it is also the field that is flagged
+by |empty_flag| in empty nodes.
+
+Glue specifications also contain three |scaled| fields, for the |width|,
+|stretch|, and |shrink| dimensions. Finally, there are two one-byte
+fields called |stretch_order| and |shrink_order|; these contain the
+orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|)
+corresponding to the stretch and shrink values.
+
+@ Here is a function that returns a pointer to a copy of a glue spec.
+The reference count in the copy is |null|, because there is assumed
+to be exactly one reference to the new specification.
+
+@c
+halfword new_spec(halfword q) /* safeguard for copying a glue node */
+{
+ if (q == null) {
+ return copy_node(zero_glue);
+ } else if (type(q) == glue_spec_node) {
+ return copy_node(q);
+ } else if (type(q) == glue_node) {
+ halfword p = copy_node(zero_glue);
+ width(p) = width(q);
+ stretch(p) = stretch(q);
+ shrink(p) = shrink(q);
+ stretch_order(p) = stretch_order(q);
+ shrink_order(p) = shrink_order(q);
+ return p;
+ } else {
+ /* alternatively we can issue a warning */
+ return copy_node(zero_glue);
+ }
+}
+
+@ And here's a function that creates a glue node for a given parameter
+identified by its code number; for example,
+|new_param_glue(line_skip_code)| returns a pointer to a glue node for the
+current \.{\\lineskip}.
+
+@c
+halfword new_param_glue(int n)
+{
+ halfword p = new_node(glue_node, n + 1);
+ halfword q = glue_par(n);
+ width(p) = width(q);
+ stretch(p) = stretch(q);
+ shrink(p) = shrink(q);
+ stretch_order(p) = stretch_order(q);
+ shrink_order(p) = shrink_order(q);
+ return p;
+}
+
+@ Glue nodes that are more or less anonymous are created by |new_glue|,
+whose argument points to a glue specification.
+
+@c
+halfword new_glue(halfword q)
+{
+ halfword p = new_node(glue_node, normal);
+ width(p) = width(q);
+ stretch(p) = stretch(q);
+ shrink(p) = shrink(q);
+ stretch_order(p) = stretch_order(q);
+ shrink_order(p) = shrink_order(q);
+ return p;
+}
+
+@ Still another subroutine is needed: This one is sort of a combination
+of |new_param_glue| and |new_glue|. It creates a glue node for one of
+the current glue parameters, but it makes a fresh copy of the glue
+specification, since that specification will probably be subject to change,
+while the parameter will stay put.
+
+/*
+ The global variable |temp_ptr| is set to the address of the new spec.
+*/
+
+@c
+halfword new_skip_param(int n)
+{
+ halfword p = new_node(glue_node, n + 1);
+ halfword q = glue_par(n);
+ width(p) = width(q);
+ stretch(p) = stretch(q);
+ shrink(p) = shrink(q);
+ stretch_order(p) = stretch_order(q);
+ shrink_order(p) = shrink_order(q);
+ return p;
+}
+
+@ A |kern_node| has a |width| field to specify a (normally negative)
+amount of spacing. This spacing correction appears in horizontal lists
+between letters like A and V when the font designer said that it looks
+better to move them closer together or further apart. A kern node can
+also appear in a vertical list, when its `|width|' denotes additional
+spacing in the vertical direction. The |subtype| is either |normal| (for
+kerns inserted from font information or math mode calculations) or |explicit|
+(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern|
+(for kerns inserted from non-math accents) or |mu_glue| (for kerns
+inserted from \.{\\mkern} specifications in math formulas).
+
+@ The |new_kern| function creates a kern node having a given width.
+
+@c
+halfword new_kern(scaled w)
+{
+ halfword p = new_node(kern_node, normal);
+ width(p) = w;
+ return p;
+}
+
+@ A |penalty_node| specifies the penalty associated with line or page
+breaking, in its |penalty| field. This field is a fullword integer, but
+the full range of integer values is not used: Any penalty |>=10000| is
+treated as infinity, and no break will be allowed for such high values.
+Similarly, any penalty |<=-10000| is treated as negative infinity, and a
+break will be forced.
+
+@ Anyone who has been reading the last few sections of the program will
+be able to guess what comes next.
+
+@c
+halfword new_penalty(int m)
+{
+ halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */
+ penalty(p) = m;
+ return p;
+}
+
+@ You might think that we have introduced enough node types by now. Well,
+almost, but there is one more: An |unset_node| has nearly the same format
+as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign}
+or \.{\\valign} that are not yet in their final form, since the box
+dimensions are their ``natural'' sizes before any glue adjustment has been
+made. The |glue_set| word is not present; instead, we have a |glue_stretch|
+field, which contains the total stretch of order |glue_order| that is
+present in the hlist or vlist being boxed.
+Similarly, the |shift_amount| field is replaced by a |glue_shrink| field,
+containing the total shrink of order |glue_sign| that is present.
+The |subtype| field is called |span_count|; an unset box typically
+contains the data for |qo(span_count)+1| columns.
+Unset nodes will be changed to box nodes when alignment is completed.
+
+In fact, there are still more types coming. When we get to math formula
+processing we will see that a |style_node| has |type=14|; and a number
+of larger type codes will also be defined, for use in math mode only.
+
+Warning: If any changes are made to these data structure layouts, such as
+changing any of the node sizes or even reordering the words of nodes,
+the |copy_node_list| procedure and the memory initialization code
+below may have to be changed. Such potentially dangerous parts of the
+program are listed in the index under `data structure assumptions'.
+@!@^data structure assumptions@>
+However, other references to the nodes are made symbolically in terms of
+the \.{WEB} macro definitions above, so that format changes will leave
+\TeX's other algorithms intact.
+@^system dependencies@>
+
+@ This function creates a |local_paragraph| node
+
+@c
+
+halfword make_local_par_node(int mode)
+{
+ int callback_id;
+ halfword q;
+ halfword p = new_node(local_par_node,0);
+ local_pen_inter(p) = local_inter_line_penalty_par;
+ local_pen_broken(p) = local_broken_penalty_par;
+ if (local_left_box_par != null) {
+ q = copy_node_list(local_left_box_par);
+ local_box_left(p) = q;
+ local_box_left_width(p) = width(local_left_box_par);
+ }
+ if (local_right_box_par != null) {
+ q = copy_node_list(local_right_box_par);
+ local_box_right(p) = q;
+ local_box_right_width(p) = width(local_right_box_par);
+ }
+ local_par_dir(p) = par_direction_par;
+ /* callback with node passed */
+ callback_id = callback_defined(insert_local_par_callback);
+ if (callback_id > 0) {
+ int sfix = lua_gettop(Luas);
+ if (!get_callback(Luas, callback_id)) {
+ lua_settop(Luas, sfix);
+ return p;
+ }
+ nodelist_to_lua(Luas, p);
+ lua_push_local_par_mode(Luas,mode)
+ if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */
+ char errmsg[256]; /* temp hack ... we will have a formatted error */
+ snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1));
+ errmsg[255]='\0';
+ lua_settop(Luas, sfix);
+ normal_error("insert_local_par",errmsg); /* to be done */
+ return p;
+ }
+ lua_settop(Luas, sfix);
+ }
+ /* done */
+ return p;
+}
|