diff options
Diffstat (limited to 'source/luametatex/source/tex/texnodes.c')
-rw-r--r-- | source/luametatex/source/tex/texnodes.c | 4794 |
1 files changed, 4794 insertions, 0 deletions
diff --git a/source/luametatex/source/tex/texnodes.c b/source/luametatex/source/tex/texnodes.c new file mode 100644 index 000000000..45e04dfd2 --- /dev/null +++ b/source/luametatex/source/tex/texnodes.c @@ -0,0 +1,4794 @@ +/* + See license.txt in the root of this project. +*/ + +# include "luametatex.h" + +/*tex + + This module started out using DEBUG 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. Only on + massive freeing we can gain. + +*/ + +node_memory_state_info lmt_node_memory_state = { + .nodes = NULL, + .nodesizes = NULL, + .free_chain = { null }, + .nodes_data = { + .minimum = min_node_size, + .maximum = max_node_size, + .size = siz_node_size, + .step = stp_node_size, + .allocated = 0, + .itemsize = sizeof(memoryword) + sizeof(char), + .top = 0, // beware, node pointers are just offsets below top + .ptr = 0, // total size in use + .initial = 0, + .offset = 0, + }, + .extra_data = { + .minimum = memory_data_unset, + .maximum = memory_data_unset, + .size = memory_data_unset, + .step = memory_data_unset, + .allocated = 0, + .itemsize = 1, + .top = 0, + .ptr = 0, + .initial = memory_data_unset, + .offset = 0, + }, + .reserved = 0, + .padding = 0, + .node_properties_id = 0, + .lua_properties_level = 0, + .attribute_cache = 0, + .max_used_attribute = 1, + .node_properties_table_size = 0, +}; + +/*tex Defined below. */ + +static void tex_aux_check_node (halfword node); +static halfword tex_aux_allocated_node (int size); + +/*tex + + The following definitions are used for keys at the \LUA\ end and provide an efficient way to + share hashed strings. For a long time we had this: + + static value_info lmt_node_fields_accent [10]; + + node_info lmt_node_data[] = { + { .id = hlist_node, .size = box_node_size, .subtypes = NULL, .fields = lmt_node_fields_list, .name = NULL, .lua = 0, .visible = 1 }, + .... + } ; + + etc but eventually we went a bit more dynamic because after all some helpers showeed up. This + brings many node properties together. Not all nodes are visible for users. Most of the + properties can be provided as lists. + + not all math noad fields ar ementioned here yet ... some are still experimental + +*/ + +void lmt_nodelib_initialize(void) { + + /*tes The subtypes of nodes. */ + + value_info + *subtypes_dir, *subtypes_par, *subtypes_glue, *subtypes_boundary, *subtypes_penalty, *subtypes_kern, + *subtypes_rule, *subtypes_glyph , *subtypes_disc, *subtypes_list, *subtypes_adjust, *subtypes_mark, + *subtypes_math, *subtypes_noad, *subtypes_radical, *subtypes_choice, *subtypes_accent, *subtypes_fence, *subtypes_split, + *subtypes_attribute; + + value_info + *lmt_node_fields_accent, *lmt_node_fields_adjust, *lmt_node_fields_attribute, + *lmt_node_fields_boundary, *lmt_node_fields_choice, *lmt_node_fields_delimiter, *lmt_node_fields_dir, + *lmt_node_fields_disc, *lmt_node_fields_fence, *lmt_node_fields_fraction, *lmt_node_fields_glue, + *lmt_node_fields_glue_spec, *lmt_node_fields_glyph, *lmt_node_fields_insert, *lmt_node_fields_split, + *lmt_node_fields_kern, *lmt_node_fields_list, *lmt_node_fields_par, *lmt_node_fields_mark, *lmt_node_fields_math, + *lmt_node_fields_math_char, *lmt_node_fields_math_text_char, *lmt_node_fields_noad, *lmt_node_fields_penalty, + *lmt_node_fields_radical, *lmt_node_fields_rule, *lmt_node_fields_style, *lmt_node_fields_parameter, + *lmt_node_fields_sub_box, *lmt_node_fields_sub_mlist, *lmt_node_fields_unset, *lmt_node_fields_whatsit; + + subtypes_dir = lmt_aux_allocate_value_info(cancel_dir_subtype); + + set_value_entry_key(subtypes_dir, normal_dir_subtype, normal) + set_value_entry_key(subtypes_dir, cancel_dir_subtype, cancel) + + subtypes_split = lmt_aux_allocate_value_info(insert_split_subtype); + + set_value_entry_key(subtypes_split, normal_split_subtype, normal) + set_value_entry_key(subtypes_split, insert_split_subtype, insert) + + subtypes_par = lmt_aux_allocate_value_info(math_par_subtype); + + set_value_entry_key(subtypes_par, vmode_par_par_subtype, vmodepar) + set_value_entry_key(subtypes_par, local_box_par_subtype, localbox) + set_value_entry_key(subtypes_par, hmode_par_par_subtype, hmodepar) + set_value_entry_key(subtypes_par, penalty_par_subtype, penalty) + set_value_entry_key(subtypes_par, math_par_subtype, math) + + subtypes_glue = lmt_aux_allocate_value_info(u_leaders); + + set_value_entry_key(subtypes_glue, user_skip_glue, userskip) + set_value_entry_key(subtypes_glue, line_skip_glue, lineskip) + set_value_entry_key(subtypes_glue, baseline_skip_glue, baselineskip) + set_value_entry_key(subtypes_glue, par_skip_glue, parskip) + set_value_entry_key(subtypes_glue, above_display_skip_glue, abovedisplayskip) + set_value_entry_key(subtypes_glue, below_display_skip_glue, belowdisplayskip) + set_value_entry_key(subtypes_glue, above_display_short_skip_glue, abovedisplayshortskip) + set_value_entry_key(subtypes_glue, below_display_short_skip_glue, belowdisplayshortskip) + set_value_entry_key(subtypes_glue, left_skip_glue, leftskip) + set_value_entry_key(subtypes_glue, right_skip_glue, rightskip) + set_value_entry_key(subtypes_glue, top_skip_glue, topskip) + set_value_entry_key(subtypes_glue, split_top_skip_glue, splittopskip) + set_value_entry_key(subtypes_glue, tab_skip_glue, tabskip) + set_value_entry_key(subtypes_glue, space_skip_glue, spaceskip) + set_value_entry_key(subtypes_glue, xspace_skip_glue, xspaceskip) + set_value_entry_key(subtypes_glue, zero_space_skip_glue, zerospaceskip) + set_value_entry_key(subtypes_glue, par_fill_left_skip_glue, parfillleftskip) + set_value_entry_key(subtypes_glue, par_fill_right_skip_glue, parfillskip) + set_value_entry_key(subtypes_glue, par_init_left_skip_glue, parinitleftskip) + set_value_entry_key(subtypes_glue, par_init_right_skip_glue, parinitrightskip) + set_value_entry_key(subtypes_glue, indent_skip_glue, indentskip) + set_value_entry_key(subtypes_glue, left_hang_skip_glue, lefthangskip) + set_value_entry_key(subtypes_glue, right_hang_skip_glue, righthangskip) + set_value_entry_key(subtypes_glue, correction_skip_glue, correctionskip) + set_value_entry_key(subtypes_glue, inter_math_skip_glue, intermathskip) + set_value_entry_key(subtypes_glue, ignored_glue, ignored) + set_value_entry_key(subtypes_glue, page_glue, page) + set_value_entry_key(subtypes_glue, math_skip_glue, mathskip) + set_value_entry_key(subtypes_glue, thin_mu_skip_glue, thinmuskip) + set_value_entry_key(subtypes_glue, med_mu_skip_glue, medmuskip) + set_value_entry_key(subtypes_glue, thick_mu_skip_glue, thickmuskip) + set_value_entry_key(subtypes_glue, conditional_math_glue, conditionalmathskip) + set_value_entry_key(subtypes_glue, rulebased_math_glue, rulebasedmathskip) + set_value_entry_key(subtypes_glue, mu_glue, muglue) + set_value_entry_key(subtypes_glue, a_leaders, leaders) + set_value_entry_key(subtypes_glue, c_leaders, cleaders) + set_value_entry_key(subtypes_glue, x_leaders, xleaders) + set_value_entry_key(subtypes_glue, g_leaders, gleaders) + set_value_entry_key(subtypes_glue, u_leaders, uleaders) + + subtypes_boundary = lmt_aux_allocate_value_info(word_boundary); + + set_value_entry_key(subtypes_boundary, cancel_boundary, cancel) + set_value_entry_key(subtypes_boundary, user_boundary, user) + set_value_entry_key(subtypes_boundary, protrusion_boundary, protrusion) + set_value_entry_key(subtypes_boundary, word_boundary, word) + + subtypes_penalty = lmt_aux_allocate_value_info(equation_number_penalty_subtype); + + set_value_entry_key(subtypes_penalty, user_penalty_subtype, userpenalty) + set_value_entry_key(subtypes_penalty, linebreak_penalty_subtype, linebreakpenalty) + set_value_entry_key(subtypes_penalty, line_penalty_subtype, linepenalty) + set_value_entry_key(subtypes_penalty, word_penalty_subtype, wordpenalty) + set_value_entry_key(subtypes_penalty, final_penalty_subtype, finalpenalty) + set_value_entry_key(subtypes_penalty, orphan_penalty_subtype, orphanpenalty) + set_value_entry_key(subtypes_penalty, math_pre_penalty_subtype, mathprepenalty) + set_value_entry_key(subtypes_penalty, math_post_penalty_subtype, mathpostpenalty) + set_value_entry_key(subtypes_penalty, before_display_penalty_subtype, beforedisplaypenalty) + set_value_entry_key(subtypes_penalty, after_display_penalty_subtype, afterdisplaypenalty) + set_value_entry_key(subtypes_penalty, equation_number_penalty_subtype, equationnumberpenalty) + + subtypes_kern = lmt_aux_allocate_value_info(vertical_math_kern_subtype); + + set_value_entry_key(subtypes_kern, font_kern_subtype, fontkern) + set_value_entry_key(subtypes_kern, explicit_kern_subtype, userkern) + set_value_entry_key(subtypes_kern, accent_kern_subtype, accentkern) + set_value_entry_key(subtypes_kern, italic_kern_subtype, italiccorrection) + set_value_entry_key(subtypes_kern, left_margin_kern_subtype, leftmarginkern) + set_value_entry_key(subtypes_kern, right_margin_kern_subtype, rightmarginkern) + set_value_entry_key(subtypes_kern, explicit_math_kern_subtype, mathkerns) + set_value_entry_key(subtypes_kern, math_shape_kern_subtype, mathshapekern) + set_value_entry_key(subtypes_kern, horizontal_math_kern_subtype, horizontalmathkern) + set_value_entry_key(subtypes_kern, vertical_math_kern_subtype, verticalmathkern) + + subtypes_rule = lmt_aux_allocate_value_info(image_rule_subtype); + + set_value_entry_key(subtypes_rule, normal_rule_subtype, normal) + set_value_entry_key(subtypes_rule, empty_rule_subtype, empty) + set_value_entry_key(subtypes_rule, strut_rule_subtype, strut) + set_value_entry_key(subtypes_rule, outline_rule_subtype, outline) + set_value_entry_key(subtypes_rule, user_rule_subtype, user) + set_value_entry_key(subtypes_rule, math_over_rule_subtype, over) + set_value_entry_key(subtypes_rule, math_under_rule_subtype, under) + set_value_entry_key(subtypes_rule, math_fraction_rule_subtype, fraction) + set_value_entry_key(subtypes_rule, math_radical_rule_subtype, radical) + set_value_entry_key(subtypes_rule, box_rule_subtype, box) + set_value_entry_key(subtypes_rule, image_rule_subtype, image) + + subtypes_glyph = lmt_aux_allocate_value_info(glyph_math_accent_subtype); + + set_value_entry_key(subtypes_glyph, glyph_unset_subtype, unset) + set_value_entry_key(subtypes_glyph, glyph_character_subtype, character) + set_value_entry_key(subtypes_glyph, glyph_ligature_subtype, ligature) + set_value_entry_key(subtypes_glyph, glyph_math_delimiter_subtype, delimiter); + set_value_entry_key(subtypes_glyph, glyph_math_extensible_subtype, extensible); + set_value_entry_key(subtypes_glyph, glyph_math_ordinary_subtype, ord); + set_value_entry_key(subtypes_glyph, glyph_math_operator_subtype, op); + set_value_entry_key(subtypes_glyph, glyph_math_binary_subtype, bin); + set_value_entry_key(subtypes_glyph, glyph_math_relation_subtype, rel); + set_value_entry_key(subtypes_glyph, glyph_math_open_subtype, open); + set_value_entry_key(subtypes_glyph, glyph_math_close_subtype, close); + set_value_entry_key(subtypes_glyph, glyph_math_punctuation_subtype, punct); + set_value_entry_key(subtypes_glyph, glyph_math_variable_subtype, variable); + set_value_entry_key(subtypes_glyph, glyph_math_active_subtype, active); + set_value_entry_key(subtypes_glyph, glyph_math_inner_subtype, inner); + set_value_entry_key(subtypes_glyph, glyph_math_over_subtype, over); + set_value_entry_key(subtypes_glyph, glyph_math_under_subtype, under); + set_value_entry_key(subtypes_glyph, glyph_math_fraction_subtype, fraction); + set_value_entry_key(subtypes_glyph, glyph_math_radical_subtype, radical); + set_value_entry_key(subtypes_glyph, glyph_math_middle_subtype, middle); + set_value_entry_key(subtypes_glyph, glyph_math_accent_subtype, accent); + + subtypes_disc = lmt_aux_allocate_value_info(syllable_discretionary_code); + + set_value_entry_key(subtypes_disc, normal_discretionary_code, discretionary) + set_value_entry_key(subtypes_disc, explicit_discretionary_code, explicit) + set_value_entry_key(subtypes_disc, automatic_discretionary_code, automatic) + set_value_entry_key(subtypes_disc, mathematics_discretionary_code, math) + set_value_entry_key(subtypes_disc, syllable_discretionary_code, regular) + + subtypes_fence = lmt_aux_allocate_value_info(no_fence_side); + + set_value_entry_key(subtypes_fence, unset_fence_side, unset) + set_value_entry_key(subtypes_fence, left_fence_side, left) + set_value_entry_key(subtypes_fence, middle_fence_side, middle) + set_value_entry_key(subtypes_fence, right_fence_side, right) + set_value_entry_key(subtypes_fence, left_operator_side, operator) + set_value_entry_key(subtypes_fence, no_fence_side, no) + + subtypes_list = lmt_aux_allocate_value_info(local_middle_list); + + set_value_entry_key(subtypes_list, unknown_list, unknown) + set_value_entry_key(subtypes_list, line_list, line) + set_value_entry_key(subtypes_list, hbox_list, box) + set_value_entry_key(subtypes_list, indent_list, indent) + set_value_entry_key(subtypes_list, container_list, container) + set_value_entry_key(subtypes_list, align_row_list, alignment) + set_value_entry_key(subtypes_list, align_cell_list, cell) + set_value_entry_key(subtypes_list, equation_list, equation) + set_value_entry_key(subtypes_list, equation_number_list, equationnumber) + set_value_entry_key(subtypes_list, math_list_list, math) + set_value_entry_key(subtypes_list, math_pack_list, mathpack) + set_value_entry_key(subtypes_list, math_char_list, mathchar) + set_value_entry_key(subtypes_list, math_h_extensible_list, hextensible) + set_value_entry_key(subtypes_list, math_v_extensible_list, vextensible) + set_value_entry_key(subtypes_list, math_h_delimiter_list, hdelimiter) + set_value_entry_key(subtypes_list, math_v_delimiter_list, vdelimiter) + set_value_entry_key(subtypes_list, math_over_delimiter_list, overdelimiter) + set_value_entry_key(subtypes_list, math_under_delimiter_list, underdelimiter) + set_value_entry_key(subtypes_list, math_numerator_list, numerator) + set_value_entry_key(subtypes_list, math_denominator_list, denominator) + set_value_entry_key(subtypes_list, math_modifier_list, modifier) + set_value_entry_key(subtypes_list, math_fraction_list, fraction) + set_value_entry_key(subtypes_list, math_nucleus_list, nucleus) + set_value_entry_key(subtypes_list, math_sup_list, sup) + set_value_entry_key(subtypes_list, math_sub_list, sub) + set_value_entry_key(subtypes_list, math_pre_post_list, prepost) + set_value_entry_key(subtypes_list, math_degree_list, degree) + set_value_entry_key(subtypes_list, math_scripts_list, scripts) + set_value_entry_key(subtypes_list, math_over_list, over) + set_value_entry_key(subtypes_list, math_under_list, under) + set_value_entry_key(subtypes_list, math_accent_list, accent) + set_value_entry_key(subtypes_list, math_radical_list, radical) + set_value_entry_key(subtypes_list, math_fence_list, fence) + set_value_entry_key(subtypes_list, math_rule_list, rule) + set_value_entry_key(subtypes_list, math_ghost_list, ghost) + set_value_entry_key(subtypes_list, insert_result_list, insert) + set_value_entry_key(subtypes_list, local_list, local) + set_value_entry_key(subtypes_list, local_left_list, left) + set_value_entry_key(subtypes_list, local_right_list, right) + set_value_entry_key(subtypes_list, local_middle_list, middle) + + subtypes_math = lmt_aux_allocate_value_info(end_inline_math); + + set_value_entry_key(subtypes_math, begin_inline_math, beginmath) + set_value_entry_key(subtypes_math, end_inline_math, endmath) + + subtypes_adjust = lmt_aux_allocate_value_info(local_adjust_code); + + set_value_entry_key(subtypes_adjust, pre_adjust_code, pre) + set_value_entry_key(subtypes_adjust, post_adjust_code, post) + set_value_entry_key(subtypes_adjust, local_adjust_code, local) + + subtypes_mark = lmt_aux_allocate_value_info(reset_mark_value_code); + + set_value_entry_key(subtypes_mark, set_mark_value_code, set) + set_value_entry_key(subtypes_mark, reset_mark_value_code, reset) + + subtypes_noad = lmt_aux_allocate_value_info(vcenter_noad_subtype); // last_noad_subtype + + set_value_entry_key(subtypes_noad, ordinary_noad_subtype, ord) + set_value_entry_key(subtypes_noad, operator_noad_subtype, op) + set_value_entry_key(subtypes_noad, binary_noad_subtype, bin) + set_value_entry_key(subtypes_noad, relation_noad_subtype, rel) + set_value_entry_key(subtypes_noad, open_noad_subtype, open) + set_value_entry_key(subtypes_noad, close_noad_subtype, close) + set_value_entry_key(subtypes_noad, punctuation_noad_subtype, punct) + set_value_entry_key(subtypes_noad, variable_noad_subtype, variable) + set_value_entry_key(subtypes_noad, active_noad_subtype, active) + set_value_entry_key(subtypes_noad, inner_noad_subtype, inner) + set_value_entry_key(subtypes_noad, under_noad_subtype, under) + set_value_entry_key(subtypes_noad, over_noad_subtype, over) + set_value_entry_key(subtypes_noad, fraction_noad_subtype, fraction) + set_value_entry_key(subtypes_noad, radical_noad_subtype, radical) + set_value_entry_key(subtypes_noad, middle_noad_subtype, middle) + set_value_entry_key(subtypes_noad, accent_noad_subtype, accent) + set_value_entry_key(subtypes_noad, fenced_noad_subtype, fenced) + set_value_entry_key(subtypes_noad, ghost_noad_subtype, ghost) + set_value_entry_key(subtypes_noad, vcenter_noad_subtype, vcenter) + + subtypes_choice = lmt_aux_allocate_value_info(discretionary_choice_subtype); + + set_value_entry_key(subtypes_choice, normal_choice_subtype, normal) + set_value_entry_key(subtypes_choice, discretionary_choice_subtype, discretionary) + + subtypes_radical = lmt_aux_allocate_value_info(h_extensible_radical_subtype); + + set_value_entry_key(subtypes_radical, normal_radical_subtype, normal) + set_value_entry_key(subtypes_radical, radical_radical_subtype, radical) + set_value_entry_key(subtypes_radical, root_radical_subtype, root) + set_value_entry_key(subtypes_radical, rooted_radical_subtype, rooted) + set_value_entry_key(subtypes_radical, under_delimiter_radical_subtype, underdelimiter) + set_value_entry_key(subtypes_radical, over_delimiter_radical_subtype, overdelimiter) + set_value_entry_key(subtypes_radical, delimiter_under_radical_subtype, delimiterunder) + set_value_entry_key(subtypes_radical, delimiter_over_radical_subtype, delimiterover) + set_value_entry_key(subtypes_radical, delimited_radical_subtype, delimited) + set_value_entry_key(subtypes_radical, h_extensible_radical_subtype, hextensible) + + subtypes_accent = lmt_aux_allocate_value_info(fixedboth_accent_subtype); + + set_value_entry_key(subtypes_accent, bothflexible_accent_subtype, bothflexible) + set_value_entry_key(subtypes_accent, fixedtop_accent_subtype, fixedtop) + set_value_entry_key(subtypes_accent, fixedbottom_accent_subtype, fixedbottom) + set_value_entry_key(subtypes_accent, fixedboth_accent_subtype, fixedboth) + + subtypes_attribute = lmt_aux_allocate_value_info(attribute_value_subtype); + + set_value_entry_key(subtypes_attribute, attribute_list_subtype, list) + set_value_entry_key(subtypes_attribute, attribute_value_subtype, value) + + /*tex The fields of nodes. */ + + lmt_node_fields_accent = lmt_aux_allocate_value_info(9); + + set_value_entry_val(lmt_node_fields_accent, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_accent, 1, node_list_field, nucleus); + set_value_entry_val(lmt_node_fields_accent, 2, node_list_field, sub); + set_value_entry_val(lmt_node_fields_accent, 3, node_list_field, sup); + set_value_entry_val(lmt_node_fields_accent, 4, node_list_field, accent); + set_value_entry_val(lmt_node_fields_accent, 5, node_list_field, bottomaccent); + set_value_entry_val(lmt_node_fields_accent, 6, node_list_field, topaccent); + set_value_entry_val(lmt_node_fields_accent, 7, node_list_field, overlayaccent); + set_value_entry_val(lmt_node_fields_accent, 8, node_list_field, fraction); + + lmt_node_fields_adjust = lmt_aux_allocate_value_info(2); + + set_value_entry_val(lmt_node_fields_adjust, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_adjust, 1, node_list_field, list); + + lmt_node_fields_attribute = lmt_aux_allocate_value_info(4); + + set_value_entry_val(lmt_node_fields_attribute, 0, integer_field, count); + set_value_entry_val(lmt_node_fields_attribute, 1, integer_field, data); + set_value_entry_val(lmt_node_fields_attribute, 2, integer_field, index); + set_value_entry_val(lmt_node_fields_attribute, 3, integer_field, value); + + /* Nothing */ + + lmt_node_fields_boundary = lmt_aux_allocate_value_info(2); + + set_value_entry_val(lmt_node_fields_boundary, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_boundary, 1, integer_field, data); + + lmt_node_fields_choice = lmt_aux_allocate_value_info(5); + + set_value_entry_val(lmt_node_fields_choice, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_choice, 1, node_list_field, display); + set_value_entry_val(lmt_node_fields_choice, 2, node_list_field, text); + set_value_entry_val(lmt_node_fields_choice, 3, node_list_field, script); + set_value_entry_val(lmt_node_fields_choice, 4, node_list_field, scriptscript); + + lmt_node_fields_delimiter = lmt_aux_allocate_value_info(5); + + set_value_entry_val(lmt_node_fields_delimiter, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_delimiter, 1, integer_field, smallfamily); + set_value_entry_val(lmt_node_fields_delimiter, 2, integer_field, smallchar); + set_value_entry_val(lmt_node_fields_delimiter, 3, integer_field, largefamily); + set_value_entry_val(lmt_node_fields_delimiter, 4, integer_field, largechar); + + lmt_node_fields_dir = lmt_aux_allocate_value_info(3); + + set_value_entry_val(lmt_node_fields_dir, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_dir, 1, integer_field, dir); + set_value_entry_val(lmt_node_fields_dir, 2, integer_field, level); + + lmt_node_fields_disc = lmt_aux_allocate_value_info( 6); + + set_value_entry_val(lmt_node_fields_disc, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_disc, 1, node_list_field, pre); + set_value_entry_val(lmt_node_fields_disc, 2, node_list_field, post); + set_value_entry_val(lmt_node_fields_disc, 3, node_list_field, replace); + set_value_entry_val(lmt_node_fields_disc, 4, integer_field, penalty); + set_value_entry_val(lmt_node_fields_disc, 5, integer_field, options); + + lmt_node_fields_fence = lmt_aux_allocate_value_info(10); + + set_value_entry_val(lmt_node_fields_fence, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_fence, 1, node_list_field, delimiter); + set_value_entry_val(lmt_node_fields_fence, 2, dimension_field, italic); + set_value_entry_val(lmt_node_fields_fence, 3, dimension_field, height); + set_value_entry_val(lmt_node_fields_fence, 4, dimension_field, depth); + set_value_entry_val(lmt_node_fields_fence, 5, integer_field, options); + set_value_entry_val(lmt_node_fields_fence, 6, integer_field, class); + set_value_entry_val(lmt_node_fields_fence, 7, integer_field, source); + set_value_entry_val(lmt_node_fields_fence, 8, node_list_field, top); + set_value_entry_val(lmt_node_fields_fence, 9, node_list_field, bottom); + + lmt_node_fields_fraction = lmt_aux_allocate_value_info(9); + + set_value_entry_val(lmt_node_fields_fraction, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_fraction, 1, dimension_field, width); + set_value_entry_val(lmt_node_fields_fraction, 2, node_list_field, numerator); + set_value_entry_val(lmt_node_fields_fraction, 3, node_list_field, denominator); + set_value_entry_val(lmt_node_fields_fraction, 4, node_list_field, left); + set_value_entry_val(lmt_node_fields_fraction, 5, node_list_field, right); + set_value_entry_val(lmt_node_fields_fraction, 6, node_list_field, middle); + set_value_entry_val(lmt_node_fields_fraction, 7, integer_field, fam); + set_value_entry_val(lmt_node_fields_fraction, 8, integer_field, options); + + lmt_node_fields_glue = lmt_aux_allocate_value_info(8); + + set_value_entry_val(lmt_node_fields_glue, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_glue, 1, node_list_field, leader); + set_value_entry_val(lmt_node_fields_glue, 2, dimension_field, width); + set_value_entry_val(lmt_node_fields_glue, 3, dimension_field, stretch); + set_value_entry_val(lmt_node_fields_glue, 4, dimension_field, shrink); + set_value_entry_val(lmt_node_fields_glue, 5, integer_field, stretchorder); + set_value_entry_val(lmt_node_fields_glue, 6, integer_field, shrinkorder); + set_value_entry_val(lmt_node_fields_glue, 7, integer_field, font); + + lmt_node_fields_glue_spec = lmt_aux_allocate_value_info(5); + + set_value_entry_val(lmt_node_fields_glue_spec, 0, dimension_field, width); + set_value_entry_val(lmt_node_fields_glue_spec, 1, dimension_field, stretch); + set_value_entry_val(lmt_node_fields_glue_spec, 2, dimension_field, shrink); + set_value_entry_val(lmt_node_fields_glue_spec, 3, integer_field, stretchorder); + set_value_entry_val(lmt_node_fields_glue_spec, 4, integer_field, shrinkorder); + + lmt_node_fields_glyph = lmt_aux_allocate_value_info(27); + + set_value_entry_val(lmt_node_fields_glyph, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_glyph, 1, integer_field, char); + set_value_entry_val(lmt_node_fields_glyph, 2, integer_field, font); + set_value_entry_val(lmt_node_fields_glyph, 3, integer_field, language); + set_value_entry_val(lmt_node_fields_glyph, 4, integer_field, lhmin); + set_value_entry_val(lmt_node_fields_glyph, 5, integer_field, rhmin); + set_value_entry_val(lmt_node_fields_glyph, 6, integer_field, uchyph); + set_value_entry_val(lmt_node_fields_glyph, 7, integer_field, state); + set_value_entry_val(lmt_node_fields_glyph, 8, dimension_field, left); + set_value_entry_val(lmt_node_fields_glyph, 9, dimension_field, right); + set_value_entry_val(lmt_node_fields_glyph, 10, dimension_field, xoffset); + set_value_entry_val(lmt_node_fields_glyph, 11, dimension_field, yoffset); + set_value_entry_val(lmt_node_fields_glyph, 12, dimension_field, xscale); + set_value_entry_val(lmt_node_fields_glyph, 13, dimension_field, yscale); + set_value_entry_val(lmt_node_fields_glyph, 14, dimension_field, width); + set_value_entry_val(lmt_node_fields_glyph, 15, dimension_field, height); + set_value_entry_val(lmt_node_fields_glyph, 16, dimension_field, depth); + set_value_entry_val(lmt_node_fields_glyph, 17, dimension_field, total); + set_value_entry_val(lmt_node_fields_glyph, 18, integer_field, expansion); + set_value_entry_val(lmt_node_fields_glyph, 19, integer_field, data); + set_value_entry_val(lmt_node_fields_glyph, 20, integer_field, script); + set_value_entry_val(lmt_node_fields_glyph, 21, integer_field, hyphenate); + set_value_entry_val(lmt_node_fields_glyph, 22, integer_field, options); + set_value_entry_val(lmt_node_fields_glyph, 23, integer_field, protected); + set_value_entry_val(lmt_node_fields_glyph, 24, integer_field, properties); + set_value_entry_val(lmt_node_fields_glyph, 25, integer_field, group); + set_value_entry_val(lmt_node_fields_glyph, 26, integer_field, index); + + lmt_node_fields_insert = lmt_aux_allocate_value_info(6); + + set_value_entry_val(lmt_node_fields_insert, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_insert, 1, integer_field, cost); + set_value_entry_val(lmt_node_fields_insert, 2, dimension_field, depth); + set_value_entry_val(lmt_node_fields_insert, 3, dimension_field, height); + set_value_entry_val(lmt_node_fields_insert, 4, integer_field, spec); + set_value_entry_val(lmt_node_fields_insert, 5, node_list_field, list); + + lmt_node_fields_split = lmt_aux_allocate_value_info(6); + + set_value_entry_val(lmt_node_fields_split, 0, attribute_field, height); + set_value_entry_val(lmt_node_fields_split, 1, integer_field, index); + set_value_entry_val(lmt_node_fields_split, 2, node_field, lastinsert); + set_value_entry_val(lmt_node_fields_split, 3, node_field, bestinsert); + set_value_entry_val(lmt_node_fields_split, 4, integer_field, stretchorder); + set_value_entry_val(lmt_node_fields_split, 5, integer_field, shrinkorder); + + lmt_node_fields_kern = lmt_aux_allocate_value_info(3); + + set_value_entry_val(lmt_node_fields_kern, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_kern, 1, dimension_field, kern); + set_value_entry_val(lmt_node_fields_kern, 2, integer_field, expansion); + + lmt_node_fields_list = lmt_aux_allocate_value_info(20); + + set_value_entry_val(lmt_node_fields_list, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_list, 1, dimension_field, width); + set_value_entry_val(lmt_node_fields_list, 2, dimension_field, depth); + set_value_entry_val(lmt_node_fields_list, 3, dimension_field, height); + set_value_entry_val(lmt_node_fields_list, 4, integer_field, direction); + set_value_entry_val(lmt_node_fields_list, 5, dimension_field, shift); + set_value_entry_val(lmt_node_fields_list, 6, integer_field, glueorder); + set_value_entry_val(lmt_node_fields_list, 7, integer_field, gluesign); + set_value_entry_val(lmt_node_fields_list, 8, integer_field, glueset); + set_value_entry_val(lmt_node_fields_list, 9, node_list_field, list); + set_value_entry_val(lmt_node_fields_list, 10, integer_field, orientation); + set_value_entry_val(lmt_node_fields_list, 11, integer_field, source); + set_value_entry_val(lmt_node_fields_list, 12, integer_field, target); + set_value_entry_val(lmt_node_fields_list, 13, dimension_field, woffset); + set_value_entry_val(lmt_node_fields_list, 14, dimension_field, hoffset); + set_value_entry_val(lmt_node_fields_list, 15, dimension_field, doffset); + set_value_entry_val(lmt_node_fields_list, 16, dimension_field, xoffset); + set_value_entry_val(lmt_node_fields_list, 17, dimension_field, yoffset); + set_value_entry_val(lmt_node_fields_list, 18, integer_field, state); + set_value_entry_val(lmt_node_fields_list, 19, integer_field, class); + + lmt_node_fields_par = lmt_aux_allocate_value_info(9); + set_value_entry_val(lmt_node_fields_par, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_par, 1, integer_field, interlinepenalty); + set_value_entry_val(lmt_node_fields_par, 2, integer_field, brokenpenalty); + set_value_entry_val(lmt_node_fields_par, 3, integer_field, dir); + set_value_entry_val(lmt_node_fields_par, 4, node_field, leftbox); + set_value_entry_val(lmt_node_fields_par, 5, dimension_field, leftboxwidth); + set_value_entry_val(lmt_node_fields_par, 6, node_field, rightbox); + set_value_entry_val(lmt_node_fields_par, 7, dimension_field, rightboxwidth); + set_value_entry_val(lmt_node_fields_par, 8, node_field, middlebox); + + lmt_node_fields_mark = lmt_aux_allocate_value_info(3); + + set_value_entry_val(lmt_node_fields_mark, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_mark, 1, integer_field, class); + set_value_entry_val(lmt_node_fields_mark, 2, token_list_field, mark); + + lmt_node_fields_math = lmt_aux_allocate_value_info(8); + + set_value_entry_val(lmt_node_fields_math, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_math, 1, integer_field, surround); + set_value_entry_val(lmt_node_fields_math, 2, dimension_field, width); + set_value_entry_val(lmt_node_fields_math, 3, dimension_field, stretch); + set_value_entry_val(lmt_node_fields_math, 4, dimension_field, shrink); + set_value_entry_val(lmt_node_fields_math, 5, integer_field, stretchorder); + set_value_entry_val(lmt_node_fields_math, 6, integer_field, shrinkorder); + set_value_entry_val(lmt_node_fields_math, 7, integer_field, penalty); + + lmt_node_fields_math_char = lmt_aux_allocate_value_info(7); + + set_value_entry_val(lmt_node_fields_math_char, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_math_char, 1, integer_field, fam); + set_value_entry_val(lmt_node_fields_math_char, 2, integer_field, char); + set_value_entry_val(lmt_node_fields_math_char, 3, integer_field, options); + set_value_entry_val(lmt_node_fields_math_char, 4, integer_field, properties); + set_value_entry_val(lmt_node_fields_math_char, 5, integer_field, group); + set_value_entry_val(lmt_node_fields_math_char, 6, integer_field, index); + + lmt_node_fields_math_text_char = lmt_aux_allocate_value_info(4); + + set_value_entry_val(lmt_node_fields_math_text_char, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_math_text_char, 1, integer_field, fam); + set_value_entry_val(lmt_node_fields_math_text_char, 2, integer_field, char); + set_value_entry_val(lmt_node_fields_math_text_char, 3, integer_field, options); + + lmt_node_fields_noad = lmt_aux_allocate_value_info(8); + + set_value_entry_val(lmt_node_fields_noad, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_noad, 1, node_list_field, nucleus); + set_value_entry_val(lmt_node_fields_noad, 2, node_list_field, sub); + set_value_entry_val(lmt_node_fields_noad, 3, node_list_field, sup); + set_value_entry_val(lmt_node_fields_noad, 4, node_list_field, subpre); + set_value_entry_val(lmt_node_fields_noad, 5, node_list_field, suppre); + set_value_entry_val(lmt_node_fields_noad, 6, node_list_field, prime); + set_value_entry_val(lmt_node_fields_noad, 7, integer_field, options); + + lmt_node_fields_penalty = lmt_aux_allocate_value_info(2); + + set_value_entry_val(lmt_node_fields_penalty, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_penalty, 1, integer_field, penalty); + + lmt_node_fields_radical = lmt_aux_allocate_value_info(11); + + set_value_entry_val(lmt_node_fields_radical, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_radical, 1, node_list_field, nucleus); + set_value_entry_val(lmt_node_fields_radical, 2, node_list_field, sub); + set_value_entry_val(lmt_node_fields_radical, 3, node_list_field, sup); + set_value_entry_val(lmt_node_fields_radical, 4, node_list_field, presub); + set_value_entry_val(lmt_node_fields_radical, 5, node_list_field, presup); + set_value_entry_val(lmt_node_fields_radical, 6, node_list_field, prime); + set_value_entry_val(lmt_node_fields_radical, 7, node_list_field, left); + set_value_entry_val(lmt_node_fields_radical, 8, node_list_field, degree); + set_value_entry_val(lmt_node_fields_radical, 9, dimension_field, width); + set_value_entry_val(lmt_node_fields_radical, 10, integer_field, options); + + lmt_node_fields_rule = lmt_aux_allocate_value_info(11); + + set_value_entry_val(lmt_node_fields_rule, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_rule, 1, dimension_field, width); + set_value_entry_val(lmt_node_fields_rule, 2, dimension_field, depth); + set_value_entry_val(lmt_node_fields_rule, 3, dimension_field, height); + set_value_entry_val(lmt_node_fields_rule, 4, dimension_field, xoffset); + set_value_entry_val(lmt_node_fields_rule, 5, dimension_field, yoffset); + set_value_entry_val(lmt_node_fields_rule, 6, dimension_field, left); + set_value_entry_val(lmt_node_fields_rule, 7, dimension_field, right); + set_value_entry_val(lmt_node_fields_rule, 8, integer_field, data); + set_value_entry_val(lmt_node_fields_rule, 9, integer_field, char); + set_value_entry_val(lmt_node_fields_rule, 10, integer_field, font); + + lmt_node_fields_style = lmt_aux_allocate_value_info(2); + + set_value_entry_val(lmt_node_fields_style, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_style, 1, integer_field, style); + + lmt_node_fields_parameter = lmt_aux_allocate_value_info(4); + + set_value_entry_val(lmt_node_fields_parameter, 0, integer_field, style); + set_value_entry_val(lmt_node_fields_parameter, 1, integer_field, name); + set_value_entry_val(lmt_node_fields_parameter, 2, integer_field, value); + set_value_entry_val(lmt_node_fields_parameter, 3, node_list_field, list); + + lmt_node_fields_sub_box = lmt_aux_allocate_value_info(2); + + set_value_entry_val(lmt_node_fields_sub_box, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_sub_box, 1, node_list_field, list); + + lmt_node_fields_sub_mlist = lmt_aux_allocate_value_info(2); + + set_value_entry_val(lmt_node_fields_sub_mlist, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_sub_mlist, 1, node_list_field, list); + + lmt_node_fields_unset = lmt_aux_allocate_value_info(11); + + set_value_entry_val(lmt_node_fields_unset, 0, attribute_field, attr); + set_value_entry_val(lmt_node_fields_unset, 1, dimension_field, width); + set_value_entry_val(lmt_node_fields_unset, 2, dimension_field, depth); + set_value_entry_val(lmt_node_fields_unset, 3, dimension_field, height); + set_value_entry_val(lmt_node_fields_unset, 4, integer_field, dir); + set_value_entry_val(lmt_node_fields_unset, 5, dimension_field, shrink); + set_value_entry_val(lmt_node_fields_unset, 6, integer_field, glueorder); + set_value_entry_val(lmt_node_fields_unset, 7, integer_field, gluesign); + set_value_entry_val(lmt_node_fields_unset, 8, dimension_field, stretch); + set_value_entry_val(lmt_node_fields_unset, 9, integer_field, span); + set_value_entry_val(lmt_node_fields_unset, 10, node_list_field, list); + + lmt_node_fields_whatsit = lmt_aux_allocate_value_info(1); + + set_value_entry_val(lmt_node_fields_whatsit, 0, attribute_field, attr); + + lmt_interface.node_data = lmt_memory_malloc((passive_node + 2) * sizeof(node_info)); + + /*tex + We start with the nodes that users can encounter. The order is mostly the one that \TEX\ + uses but we have move some around because we have some more and sometimes a bit different + kind of nodes. You should use abstractions anyway, so numbers mean nothing. In original + \TEX\ there are sometimes tests like |if (foo < kern_node)| but these have been replaces + by switches and (un)equality tests so that the order is not really important. + + Subtypes in nodes and codes in commands sometimes are sort of in sync but don't rely on + that! + */ + + lmt_interface.node_data[hlist_node] = (node_info) { .id = hlist_node, .size = box_node_size, .first = 0, .last = last_list_subtype, .subtypes = subtypes_list, .fields = lmt_node_fields_list, .name = lua_key(hlist), .lua = lua_key_index(hlist), .visible = 1 }; + lmt_interface.node_data[vlist_node] = (node_info) { .id = vlist_node, .size = box_node_size, .first = 0, .last = last_list_subtype, .subtypes = subtypes_list, .fields = lmt_node_fields_list, .name = lua_key(vlist), .lua = lua_key_index(vlist), .visible = 1 }; + lmt_interface.node_data[rule_node] = (node_info) { .id = rule_node, .size = rule_node_size, .first = 0, .last = last_rule_subtype, .subtypes = subtypes_rule, .fields = lmt_node_fields_rule, .name = lua_key(rule), .lua = lua_key_index(rule), .visible = 1 }; + lmt_interface.node_data[insert_node] = (node_info) { .id = insert_node, .size = insert_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_insert, .name = lua_key(insert), .lua = lua_key_index(insert), .visible = 1 }; + lmt_interface.node_data[mark_node] = (node_info) { .id = mark_node, .size = mark_node_size, .first = 0, .last = last_mark_subtype, .subtypes = subtypes_mark, .fields = lmt_node_fields_mark, .name = lua_key(mark), .lua = lua_key_index(mark), .visible = 1 }; + lmt_interface.node_data[adjust_node] = (node_info) { .id = adjust_node, .size = adjust_node_size, .first = 0, .last = last_adjust_subtype, .subtypes = subtypes_adjust, .fields = lmt_node_fields_adjust, .name = lua_key(adjust), .lua = lua_key_index(adjust), .visible = 1 }; + lmt_interface.node_data[boundary_node] = (node_info) { .id = boundary_node, .size = boundary_node_size, .first = 0, .last = last_boundary_subtype, .subtypes = subtypes_boundary, .fields = lmt_node_fields_boundary, .name = lua_key(boundary), .lua = lua_key_index(boundary), .visible = 1 }; + lmt_interface.node_data[disc_node] = (node_info) { .id = disc_node, .size = disc_node_size, .first = 0, .last = last_discretionary_subtype, .subtypes = subtypes_disc, .fields = lmt_node_fields_disc, .name = lua_key(disc), .lua = lua_key_index(disc), .visible = 1 }; + lmt_interface.node_data[whatsit_node] = (node_info) { .id = whatsit_node, .size = whatsit_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_whatsit, .name = lua_key(whatsit), .lua = lua_key_index(whatsit), .visible = 1 }; + lmt_interface.node_data[par_node] = (node_info) { .id = par_node, .size = par_node_size, .first = 0, .last = last_par_subtype, .subtypes = subtypes_par, .fields = lmt_node_fields_par, .name = lua_key(par), .lua = lua_key_index(par), .visible = 1 }; + lmt_interface.node_data[dir_node] = (node_info) { .id = dir_node, .size = dir_node_size, .first = 0, .last = last_dir_subtype, .subtypes = subtypes_dir, .fields = lmt_node_fields_dir, .name = lua_key(dir), .lua = lua_key_index(dir), .visible = 1 }; + lmt_interface.node_data[math_node] = (node_info) { .id = math_node, .size = math_node_size, .first = 0, .last = last_math_subtype, .subtypes = subtypes_math, .fields = lmt_node_fields_math, .name = lua_key(math), .lua = lua_key_index(math), .visible = 1 }; + lmt_interface.node_data[glue_node] = (node_info) { .id = glue_node, .size = glue_node_size, .first = 0, .last = last_glue_subtype, .subtypes = subtypes_glue, .fields = lmt_node_fields_glue, .name = lua_key(glue), .lua = lua_key_index(glue), .visible = 1 }; + lmt_interface.node_data[kern_node] = (node_info) { .id = kern_node, .size = kern_node_size, .first = 0, .last = last_kern_subtype, .subtypes = subtypes_kern, .fields = lmt_node_fields_kern, .name = lua_key(kern), .lua = lua_key_index(kern), .visible = 1 }; + lmt_interface.node_data[penalty_node] = (node_info) { .id = penalty_node, .size = penalty_node_size, .first = 0, .last = last_penalty_subtype, .subtypes = subtypes_penalty, .fields = lmt_node_fields_penalty, .name = lua_key(penalty), .lua = lua_key_index(penalty), .visible = 1 }; + lmt_interface.node_data[style_node] = (node_info) { .id = style_node, .size = style_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_style, .name = lua_key(style), .lua = lua_key_index(style), .visible = 1 }; + lmt_interface.node_data[choice_node] = (node_info) { .id = choice_node, .size = choice_node_size, .first = 0, .last = last_choice_subtype, .subtypes = subtypes_choice, .fields = lmt_node_fields_choice, .name = lua_key(choice), .lua = lua_key_index(choice), .visible = 1 }; + lmt_interface.node_data[parameter_node] = (node_info) { .id = parameter_node, .size = parameter_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_parameter, .name = lua_key(parameter), .lua = lua_key_index(parameter), .visible = 1 }; + lmt_interface.node_data[simple_noad] = (node_info) { .id = simple_noad, .size = noad_size, .first = 0, .last = last_noad_subtype, .subtypes = subtypes_noad, .fields = lmt_node_fields_noad, .name = lua_key(noad), .lua = lua_key_index(noad), .visible = 1 }; + lmt_interface.node_data[radical_noad] = (node_info) { .id = radical_noad, .size = radical_noad_size, .first = 0, .last = last_radical_subtype, .subtypes = subtypes_radical, .fields = lmt_node_fields_radical, .name = lua_key(radical), .lua = lua_key_index(radical), .visible = 1 }; + lmt_interface.node_data[fraction_noad] = (node_info) { .id = fraction_noad, .size = fraction_noad_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_fraction, .name = lua_key(fraction), .lua = lua_key_index(fraction), .visible = 1 }; + lmt_interface.node_data[accent_noad] = (node_info) { .id = accent_noad, .size = accent_noad_size, .first = 0, .last = last_accent_subtype, .subtypes = subtypes_accent, .fields = lmt_node_fields_accent, .name = lua_key(accent), .lua = lua_key_index(accent), .visible = 1 }; + lmt_interface.node_data[fence_noad] = (node_info) { .id = fence_noad, .size = fence_noad_size, .first = 0, .last = last_fence_subtype, .subtypes = subtypes_fence, .fields = lmt_node_fields_fence, .name = lua_key(fence), .lua = lua_key_index(fence), .visible = 1 }; + lmt_interface.node_data[math_char_node] = (node_info) { .id = math_char_node, .size = math_kernel_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_math_char, .name = lua_key(mathchar), .lua = lua_key_index(mathchar), .visible = 1 }; + lmt_interface.node_data[math_text_char_node] = (node_info) { .id = math_text_char_node, .size = math_kernel_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_math_text_char, .name = lua_key(mathtextchar), .lua = lua_key_index(mathtextchar), .visible = 1 }; + lmt_interface.node_data[sub_box_node] = (node_info) { .id = sub_box_node, .size = math_kernel_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_sub_box, .name = lua_key(subbox), .lua = lua_key_index(subbox), .visible = 1 }; + lmt_interface.node_data[sub_mlist_node] = (node_info) { .id = sub_mlist_node, .size = math_kernel_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_sub_mlist, .name = lua_key(submlist), .lua = lua_key_index(submlist), .visible = 1 }; + lmt_interface.node_data[delimiter_node] = (node_info) { .id = delimiter_node, .size = math_delimiter_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_delimiter, .name = lua_key(delimiter), .lua = lua_key_index(delimiter), .visible = 1 }; + lmt_interface.node_data[glyph_node] = (node_info) { .id = glyph_node, .size = glyph_node_size, .first = 0, .last = last_glyph_subtype, .subtypes = subtypes_glyph, .fields = lmt_node_fields_glyph, .name = lua_key(glyph), .lua = lua_key_index(glyph), .visible = 1 }; + + /*tex + Who knows when someone needs is, so for now we keep it exposed. + */ + + lmt_interface.node_data[unset_node] = (node_info) { .id = unset_node, .size = box_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_unset, .name = lua_key(unset), .lua = lua_key_index(unset), .visible = 1 }; + lmt_interface.node_data[specification_node] = (node_info) { .id = specification_node, .size = specification_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(specification), .lua = lua_key_index(specification), .visible = 0 }; + lmt_interface.node_data[align_record_node] = (node_info) { .id = align_record_node, .size = box_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_unset, .name = lua_key(alignrecord), .lua = lua_key_index(alignrecord), .visible = 1 }; + + /*tex + These nodes never show up in nodelists and are managed special. Messing with such nodes + directly is not a good idea. + */ + + lmt_interface.node_data[attribute_node] = (node_info) { .id = attribute_node, .size = attribute_node_size, .first = 0, .last = last_attribute_subtype, .subtypes = subtypes_attribute,.fields = lmt_node_fields_attribute, .name = lua_key(attribute), .lua = lua_key_index(attribute), .visible = 1 }; + + /* + We still expose the glue spec as they are the containers for skip registers but there is no + real need to use them at the user end. + */ + + lmt_interface.node_data[glue_spec_node] = (node_info) { .id = glue_spec_node, .size = glue_spec_size, .first = 0, .last = 0, .subtypes = NULL, .fields = lmt_node_fields_glue_spec, .name = lua_key(gluespec), .lua = lua_key_index(gluespec), .visible = 1 }; + + /*tex + This one sometimes shows up, especially when we temporarily need an alternative head pointer, + simply because we want to retain some head in case the original head is replaced. + */ + + lmt_interface.node_data[temp_node] = (node_info) { .id = temp_node, .size = temp_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(temp), .lua = lua_key_index(temp), .visible = 1 }; + + /*tex + The split nodes are used for insertions. + */ + + lmt_interface.node_data[split_node] = (node_info) { .id = split_node, .size = split_node_size, .first = 0, .last = last_split_subtype, .subtypes = subtypes_split, .fields = lmt_node_fields_split, .name = lua_key(split), .lua = lua_key_index(split), .visible = 1 }; + + /*tex + The following nodes are not meant for users. They are used internally for different purposes + and you should not encounter them in node lists. As with many nodes, they often are + allocated using fast methods so they never show up in the new, copy and flush handlers. + */ + + lmt_interface.node_data[expression_node] = (node_info) { .id = expression_node, .size = expression_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(expression), .lua = lua_key_index(expression), .visible = 0 }; + lmt_interface.node_data[math_spec_node] = (node_info) { .id = math_spec_node, .size = math_spec_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(mathspec), .lua = lua_key_index(mathspec), .visible = 0 }; + lmt_interface.node_data[font_spec_node] = (node_info) { .id = font_spec_node, .size = font_spec_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(fontspec), .lua = lua_key_index(fontspec), .visible = 0 }; + lmt_interface.node_data[nesting_node] = (node_info) { .id = nesting_node, .size = nesting_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(nestedlist), .lua = lua_key_index(nestedlist), .visible = 0 }; + lmt_interface.node_data[span_node] = (node_info) { .id = span_node, .size = span_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(span), .lua = lua_key_index(span), .visible = 0 }; + lmt_interface.node_data[align_stack_node] = (node_info) { .id = align_stack_node, .size = align_stack_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(alignstack), .lua = lua_key_index(alignstack), .visible = 0 }; + lmt_interface.node_data[noad_state_node] = (node_info) { .id = noad_state_node, .size = noad_state_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(noadstate), .lua = lua_key_index(noadstate), .visible = 0 }; + lmt_interface.node_data[if_node] = (node_info) { .id = if_node, .size = if_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(ifstack), .lua = lua_key_index(ifstack), .visible = 0 }; + lmt_interface.node_data[unhyphenated_node] = (node_info) { .id = unhyphenated_node, .size = active_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(unhyphenated), .lua = lua_key_index(unhyphenated), .visible = 0 }; + lmt_interface.node_data[hyphenated_node] = (node_info) { .id = hyphenated_node, .size = active_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(hyphenated), .lua = lua_key_index(hyphenated), .visible = 0 }; + lmt_interface.node_data[delta_node] = (node_info) { .id = delta_node, .size = delta_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(delta), .lua = lua_key_index(delta), .visible = 0 }; + lmt_interface.node_data[passive_node] = (node_info) { .id = passive_node, .size = passive_node_size, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = lua_key(passive), .lua = lua_key_index(passive), .visible = 0 }; + lmt_interface.node_data[passive_node + 1] = (node_info) { .id = -1, .size = -1, .first = 0, .last = 0, .subtypes = NULL, .fields = NULL, .name = NULL, .lua = 0, .visible = 0 }; + +} + +/*tex + + When we copy a node list, there are several possibilities: we do the same as a new node, we + copy the entry to the table in properties (a reference), we do a deep copy of a table in the + properties, we create a new table and give it the original one as a metatable. After some + experiments (that also included timing) with these scenarios I decided that a deep copy made no + sense, nor did nilling. In the end both the shallow copy and the metatable variant were both + ok, although the second ons is slower. The most important aspect to keep in mind is that + references to other nodes in properties no longer can be valid for that copy. We could use two + tables (one unique and one shared) or metatables but that only complicates matters. + + When defining a new node, we could already allocate a table but it is rather easy to do that at + the lua end e.g. using a metatable __index method. That way it is under macro package control. + + When deleting a node, we could keep the slot (e.g. setting it to false) but it could make + memory consumption raise unneeded when we have temporary large node lists and after that only + small lists. + + So, in the end this is what we ended up with. For the record, I also experimented with the + following: + + \startitemize + + \startitem + Copy attributes to the properties so that we have fast access at the \LUA\ end: in the + end the overhead is not compensated by speed and convenience, in fact, attributes are + not that slow when it comes to accessing them. + \stopitem + + \startitem + A bitset in the node but again the gain compared to attributes is neglectable and it + also demands a pretty string agreement over what bit represents what, and this is + unlikely to succeed in the tex community (I could use it for font handling, which is + cross package, but decided that it doesn't pay off. + \stopitem + + \stopitemize + + In case one wonders why properties make sense then, well, it is not so much speed that we gain, + but more convenience: storing all kind of (temporary) data in attributes is no fun and this + mechanism makes sure that properties are cleaned up when a node is freed. Also, the advantage + of a more or less global properties table is that we stay at the \LUA\ end. An alternative is + to store a reference in the node itself but that is complicated by the fact that the register + has some limitations (no numeric keys) and we also don't want to mess with it too much. + + We keep track of nesting so that we don't overflow the stack, and, what is more important, + don't keep resolving the registry index. + + We could add an index field to each node and use that one. But then we'd have to default to + false. It actually would look nicer in tracing: indices instead of pseudo memory slots. It + would not boost performance. A table like this is never really collected. + +*/ + +inline static void lmt_properties_push(lua_State * L) +{ + lmt_node_memory_state.lua_properties_level++ ; + if (lmt_node_memory_state.lua_properties_level == 1) { + lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id); + } +} + +inline static void lmt_properties_pop(lua_State * L) +{ + if (lmt_node_memory_state.lua_properties_level == 1) { + lua_pop(L, 1); + } + lmt_node_memory_state.lua_properties_level-- ; +} + +/*tex Resetting boils down to nilling. */ + +inline static void lmt_properties_reset(lua_State * L, halfword target) +{ + if (lmt_node_memory_state.lua_properties_level == 0) { + lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id); + lua_pushnil(L); + lua_rawseti(L, -2, target); + lua_pop(L, 1); + } else { + lua_pushnil(L); + lua_rawseti(L, -2, target); + } +} + +inline static void lmt_properties_copy(lua_State *L, halfword target, halfword source) +{ + if (lmt_node_memory_state.lua_properties_level == 0) { + lua_rawgeti(L, LUA_REGISTRYINDEX, lmt_node_memory_state.node_properties_id); + } + /* properties */ + if (lua_rawgeti(L, -1, source) == LUA_TTABLE) { + /* properties source */ + lua_createtable(L, 0, 1); + /* properties source {} */ + lua_insert(L, -2); + /* properties {} source */ + lua_push_key(__index); + /* properties {} source "__index" */ + lua_insert(L, -2); + /* properties {} "__index" source */ + lua_rawset(L, -3); + /* properties {__index=source} */ + lua_createtable(L, 0, 1); + /* properties {__index=source} {} */ + lua_insert(L, -2); + /* properties {} {__index=source} */ + lua_setmetatable(L, -2); + /* properties {}->{__index=source} */ + lua_rawseti(L, -2, target); + /* properties[target]={}->{__index=source} */ + } else { + /* properties nil */ + lua_pop(L, 1); + } + /* properties */ + if (lmt_node_memory_state.lua_properties_level == 0) { + lua_pop(L, 1); + } +} + +/*tex The public one: */ + +void tex_reset_node_properties(halfword b) +{ + if (b) { + lmt_properties_reset(lmt_lua_state.lua_instance, b); + } +} + +/*tex Here end the property handlers. */ + +static void tex_aux_node_range_test(halfword a, halfword b) +{ + if (b < 0 || b >= lmt_node_memory_state.nodes_data.allocated) { + tex_formatted_error("nodes", "node range test failed in %s node", lmt_interface.node_data[node_type(a)].name); + } +} + +/*tex + + Because of the 5-10\% overhead that \SYNTEX\ creates some options have been implemented + controlled by |synctex_anyway_mode|. + + \startabulate + \NC \type {1} \NC all but glyphs \NC \NR + \NC \type {2} \NC also glyphs \NC \NR + \NC \type {3} \NC glyphs and glue \NC \NR + \NC \type {4} \NC only glyphs \NC \NR + \stoptabulate + +*/ + +/*tex |if_stack| is called a lot so maybe optimize that one. */ + +/*tex This needs a cleanup ... there is no need to store the pointer location itself. */ + +inline static void tex_aux_preset_disc_node(halfword n) +{ + disc_pre_break(n) = disc_pre_break_node(n); + disc_post_break(n) = disc_post_break_node(n); + disc_no_break(n) = disc_no_break_node(n); + node_type(disc_pre_break(n)) = nesting_node; + node_type(disc_post_break(n)) = nesting_node; + node_type(disc_no_break(n)) = nesting_node; + node_subtype(disc_pre_break(n)) = pre_break_code; + node_subtype(disc_post_break(n)) = post_break_code; + node_subtype(disc_no_break(n)) = no_break_code; +} + +inline static void tex_aux_preset_node(halfword n, quarterword t) +{ + switch (t) { + case glyph_node: + break; + case hlist_node: + case vlist_node: + box_dir(n) = direction_unknown; + break; + case disc_node: + tex_aux_preset_disc_node(n); + break; + case rule_node: + rule_width(n) = null_flag; + rule_depth(n) = null_flag; + rule_height(n) = null_flag; + rule_data(n) = 0; + break; + case unset_node: + box_width(n) = null_flag; + break; + case specification_node: + tex_null_specification_list(n); + break; + case simple_noad: + case radical_noad: + case fraction_noad: + case accent_noad: + case fence_noad: + noad_family(n) = unused_math_family; + noad_style(n) = unused_math_style; + reset_noad_classes(n); /* unsets them */ + break; + } +} + +halfword tex_new_node(quarterword i, quarterword j) +{ + halfword s = get_node_size(i); + halfword n = tex_get_node(s); + + /*tex + + Both type() and subtype() will be set below, and node_next() is set to null by |get_node()|, + so we can clear one word less than |s|. + + */ + + memset((void *) (lmt_node_memory_state.nodes + n + 1), 0, (sizeof(memoryword) * ((size_t) s - 1))); + + if (tex_nodetype_is_complex(i)) { + tex_aux_preset_node(n, i); + if (input_file_state.mode > 0) { + /*tex See table above. */ + switch (i) { + case glyph_node: + if (input_file_state.mode > 1) { + glyph_input_file(n) = input_file_value(); + glyph_input_line(n) = input_line_value(); + } + break; + case hlist_node: + case vlist_node: + case unset_node: + box_input_file(n) = input_file_value(); + box_input_line(n) = input_line_value(); + break; + } + } + if (tex_nodetype_has_attributes(i)) { + attach_current_attribute_list(n); + } + } + /* last */ + node_type(n) = i; + node_subtype(n) = j; + return n; +} + +halfword tex_new_temp_node(void) +{ + halfword n = tex_get_node(temp_node_size); + node_type(n) = temp_node; + node_subtype(n) = 0; + memset((void *) (lmt_node_memory_state.nodes + n + 1), 0, (sizeof(memoryword) * (temp_node_size - 1))); + return n; +} + +static halfword tex_aux_new_glyph_node_with_attributes(halfword parent) +{ + halfword n = tex_get_node(glyph_node_size); + memset((void *) (lmt_node_memory_state.nodes + n + 1), 0, (sizeof(memoryword) * (glyph_node_size - 1))); + if (input_file_state.mode > 1) { + glyph_input_file(n) = input_file_value(); + glyph_input_line(n) = input_line_value(); + } + node_type(n) = glyph_node; + node_subtype(n) = glyph_unset_subtype; + if (parent) { + tex_attach_attribute_list_copy(n, parent); + } else { + attach_current_attribute_list(n); + } + return n; +} + +/*tex + This makes a duplicate of the node list that starts at |p| and returns a pointer to the new + list. +*/ + +halfword tex_copy_node_list(halfword p, halfword end) +{ + /*tex head of the list */ + halfword h = null; + /*tex previous position in new list */ + halfword q = null; + /*tex saves stack and time */ + lua_State *L = lmt_lua_state.lua_instance; + lmt_properties_push(L); + while (p != end) { + halfword s = tex_copy_node(p); + if (h) { + tex_couple_nodes(q, s); + } else { + h = s; + } + q = s; + p = node_next(p); + } + /*tex saves stack and time */ + lmt_properties_pop(L); + return h; +} + +/*tex Make a dupe of a single node. */ + +halfword tex_copy_node_only(halfword p) +{ + quarterword t = node_type(p); + int s = get_node_size(t); + halfword r = tex_get_node(s); + memcpy((void *) (lmt_node_memory_state.nodes + r), (void *) (lmt_node_memory_state.nodes + p), (sizeof(memoryword) )); + memset((void *) (lmt_node_memory_state.nodes + r + 1), 0, (sizeof(memoryword) * ((unsigned) s - 1))); + tex_aux_preset_node(r, t); + return r; +} + +/*tex + We really need to use macros here as we need the temporary variable because varmem can be + reallocated! We cross our fingers that the compiler doesn't optimize that one away. (The test + suite had a few cases where reallocation during a copy happens.) We can make |copy_stub| + local here. + */ + +# define copy_sub_list(target,source) do { \ + if (source) { \ + halfword copy_stub = tex_copy_node_list(source, null); \ + target = copy_stub; \ + } else { \ + target = null; \ + } \ + } while (0) + +# define copy_sub_node(target,source) do { \ + if (source) { \ + halfword copy_stub = tex_copy_node(source); \ + target = copy_stub ; \ + } else { \ + target = null; \ + } \ +} while (0) + +halfword tex_copy_node(halfword p) +{ + /*tex + We really need a stub for copying because mem might move in the meantime due to resizing! + */ + if (p < 0 || p >= lmt_node_memory_state.nodes_data.allocated) { + return tex_formatted_error("nodes", "attempt to copy an impossible node %d", (int) p); + } else if (p > lmt_node_memory_state.reserved && lmt_node_memory_state.nodesizes[p] == 0) { + return tex_formatted_error("nodes", "attempt to copy a free %s node %d", get_node_name(node_type(p)), (int) p); + } else { + /*tex type of node */ + halfword t = node_type(p); + int i = get_node_size(t); + /*tex current node being fabricated for new list */ + halfword r = tex_get_node(i); + /*tex this saves work */ + memcpy((void *) (lmt_node_memory_state.nodes + r), (void *) (lmt_node_memory_state.nodes + p), (sizeof(memoryword) * (unsigned) i)); + if (tex_nodetype_is_complex(i)) { + // halfword copy_stub; + if (tex_nodetype_has_attributes(t)) { + add_attribute_reference(node_attr(p)); + node_prev(r) = null; + lmt_properties_copy(lmt_lua_state.lua_instance, r, p); + } + node_next(r) = null; + switch (t) { + case glue_node: + copy_sub_list(glue_leader_ptr(r), glue_leader_ptr(p)); + break; + case hlist_node: + copy_sub_list(box_pre_adjusted(r), box_pre_adjusted(p)); + copy_sub_list(box_post_adjusted(r), box_post_adjusted(p)); + // fall through + case vlist_node: + copy_sub_list(box_pre_migrated(r), box_pre_migrated(p)); + copy_sub_list(box_post_migrated(r), box_post_migrated(p)); + // fall through + case unset_node: + copy_sub_list(box_list(r), box_list(p)); + break; + case disc_node: + disc_pre_break(r) = disc_pre_break_node(r); + if (disc_pre_break_head(p)) { + tex_set_disc_field(r, pre_break_code, tex_copy_node_list(disc_pre_break_head(p), null)); + } else { + tex_set_disc_field(r, pre_break_code, null); + } + disc_post_break(r) = disc_post_break_node(r); + if (disc_post_break_head(p)) { + tex_set_disc_field(r, post_break_code, tex_copy_node_list(disc_post_break_head(p), null)); + } else { + tex_set_disc_field(r, post_break_code, null); + } + disc_no_break(r) = disc_no_break_node(r); + if (disc_no_break_head(p)) { + tex_set_disc_field(r, no_break_code, tex_copy_node_list(disc_no_break_head(p), null)); + } else { + tex_set_disc_field(r, no_break_code, null); + } + break; + case insert_node: + copy_sub_list(insert_list(r), insert_list(p)) ; + break; + case mark_node: + tex_add_token_reference(mark_ptr(p)); + break; + case adjust_node: + copy_sub_list(adjust_list(r), adjust_list(p)); + break; + case choice_node: + copy_sub_list(choice_display_mlist(r), choice_display_mlist(p)) ; + copy_sub_list(choice_text_mlist(r), choice_text_mlist(p)) ; + copy_sub_list(choice_script_mlist(r), choice_script_mlist(p)) ; + copy_sub_list(choice_script_script_mlist(r), choice_script_script_mlist(p)) ; + break; + case simple_noad: + case radical_noad: + case fraction_noad: + case accent_noad: + copy_sub_list(noad_nucleus(r), noad_nucleus(p)) ; + copy_sub_list(noad_subscr(r), noad_subscr(p)) ; + copy_sub_list(noad_supscr(r), noad_supscr(p)) ; + copy_sub_list(noad_subprescr(r), noad_subprescr(p)) ; + copy_sub_list(noad_supprescr(r), noad_supprescr(p)) ; + copy_sub_list(noad_prime(r), noad_prime(p)) ; + copy_sub_list(noad_state(r), noad_state(p)) ; + switch (t) { + case radical_noad: + copy_sub_node(radical_left_delimiter(r), radical_left_delimiter(p)) ; + copy_sub_node(radical_right_delimiter(r), radical_right_delimiter(p)) ; + copy_sub_list(radical_degree(r), radical_degree(p)) ; + break; + case fraction_noad: + // copy_sub_list(fraction_numerator(r), fraction_numerator(p)) ; + // copy_sub_list(fraction_denominator(r), fraction_denominator(p)) ; + copy_sub_node(fraction_left_delimiter(r), fraction_left_delimiter(p)) ; + copy_sub_node(fraction_right_delimiter(r), fraction_right_delimiter(p)) ; + copy_sub_node(fraction_middle_delimiter(r), fraction_middle_delimiter(p)) ; + break; + case accent_noad: + copy_sub_list(accent_top_character(r), accent_top_character(p)) ; + copy_sub_list(accent_bottom_character(r), accent_bottom_character(p)) ; + copy_sub_list(accent_middle_character(r), accent_middle_character(p)) ; + break; + } + break; + case fence_noad: + /* in principle also scripts */ + copy_sub_node(fence_delimiter_list(r), fence_delimiter_list(p)) ; + copy_sub_node(fence_delimiter_top(r), fence_delimiter_top(p)) ; + copy_sub_node(fence_delimiter_bottom(r), fence_delimiter_bottom(p)) ; + break; + case sub_box_node: + case sub_mlist_node: + copy_sub_list(kernel_math_list(r), kernel_math_list(p)) ; + break; + case par_node: + /* can also be copy_sub_node */ + copy_sub_list(par_box_left(r), par_box_left(p)); + copy_sub_list(par_box_right(r), par_box_right(p)); + copy_sub_list(par_box_middle(r), par_box_middle(p)); + /* wipe copied fields */ + par_left_skip(r) = null; + par_right_skip(r) = null; + par_par_fill_left_skip(r) = null; + par_par_fill_right_skip(r) = null; + par_par_init_left_skip(r) = null; + par_par_init_right_skip(r) = null; + par_baseline_skip(r) = null; + par_line_skip(r) = null; + par_par_shape(r) = null; + par_inter_line_penalties(r) = null; + par_club_penalties(r) = null; + par_widow_penalties(r) = null; + par_display_widow_penalties(r) = null; + par_orphan_penalties(r) = null; + /* really copy fields */ + tex_set_par_par(r, par_left_skip_code, tex_get_par_par(p, par_left_skip_code), 1); + tex_set_par_par(r, par_right_skip_code, tex_get_par_par(p, par_right_skip_code), 1); + tex_set_par_par(r, par_par_fill_left_skip_code, tex_get_par_par(p, par_par_fill_left_skip_code), 1); + tex_set_par_par(r, par_par_fill_right_skip_code, tex_get_par_par(p, par_par_fill_right_skip_code), 1); + tex_set_par_par(r, par_par_init_left_skip_code, tex_get_par_par(p, par_par_init_left_skip_code), 1); + tex_set_par_par(r, par_par_init_right_skip_code, tex_get_par_par(p, par_par_init_right_skip_code), 1); + tex_set_par_par(r, par_baseline_skip_code, tex_get_par_par(p, par_baseline_skip_code), 1); + tex_set_par_par(r, par_line_skip_code, tex_get_par_par(p, par_line_skip_code), 1); + tex_set_par_par(r, par_par_shape_code, tex_get_par_par(p, par_par_shape_code), 1); + tex_set_par_par(r, par_inter_line_penalties_code, tex_get_par_par(p, par_inter_line_penalties_code), 1); + tex_set_par_par(r, par_club_penalties_code, tex_get_par_par(p, par_club_penalties_code), 1); + tex_set_par_par(r, par_widow_penalties_code, tex_get_par_par(p, par_widow_penalties_code), 1); + tex_set_par_par(r, par_display_widow_penalties_code, tex_get_par_par(p, par_display_widow_penalties_code), 1); + tex_set_par_par(r, par_orphan_penalties_code, tex_get_par_par(p, par_orphan_penalties_code), 1); + /* tokens, we could mess with a ref count instead */ + par_end_par_tokens(r) = par_end_par_tokens(p); + tex_add_token_reference(par_end_par_tokens(p)); + break; + case specification_node: + tex_copy_specification_list(r, p); + break; + default: + break; + } + } + return r; + } +} + +inline static void tex_aux_free_sub_node_list(halfword source) +{ + if (source) { + tex_flush_node_list(source); + } +} + +inline static void tex_aux_free_sub_node(halfword source) +{ + if (source) { + tex_flush_node(source); + } +} + +/* We don't need the checking for attributes if we make these lists frozen. */ + +void tex_flush_node(halfword p) +{ + if (! p) { + /*tex legal, but no-op. */ + return; + } else if (p <= lmt_node_memory_state.reserved || p >= lmt_node_memory_state.nodes_data.allocated) { + tex_formatted_error("nodes", "attempt to free an impossible node %d of type %d", (int) p, node_type(p)); + } else if (lmt_node_memory_state.nodesizes[p] == 0) { + for (int i = (lmt_node_memory_state.reserved + 1); i < lmt_node_memory_state.nodes_data.allocated; i++) { + if (lmt_node_memory_state.nodesizes[i] > 0) { + tex_aux_check_node(i); + } + } + tex_formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(node_type(p)), (int) p); + } else { + int t = node_type(p); + if (tex_nodetype_is_complex(t)) { + switch (t) { + case glue_node: + tex_aux_free_sub_node_list(glue_leader_ptr(p)); + break; + case hlist_node: + tex_aux_free_sub_node_list(box_pre_adjusted(p)); + tex_aux_free_sub_node_list(box_post_adjusted(p)); + // fall through + case vlist_node: + tex_aux_free_sub_node_list(box_pre_migrated(p)); + tex_aux_free_sub_node_list(box_post_migrated(p)); + // fall through + case unset_node: + tex_aux_free_sub_node_list(box_list(p)); + break; + case disc_node: + /*tex Watch the start at temp node hack! */ + tex_aux_free_sub_node_list(disc_pre_break_head(p)); + tex_aux_free_sub_node_list(disc_post_break_head(p)); + tex_aux_free_sub_node_list(disc_no_break_head(p)); + break; + case par_node: + tex_aux_free_sub_node_list(par_box_left(p)); + tex_aux_free_sub_node_list(par_box_right(p)); + tex_aux_free_sub_node_list(par_box_middle(p)); + /* we could check for the flag */ + tex_flush_node(par_left_skip(p)); + tex_flush_node(par_right_skip(p)); + tex_flush_node(par_par_fill_left_skip(p)); + tex_flush_node(par_par_fill_right_skip(p)); + tex_flush_node(par_par_init_left_skip(p)); + tex_flush_node(par_par_init_right_skip(p)); + tex_flush_node(par_baseline_skip(p)); + tex_flush_node(par_line_skip(p)); + tex_flush_node(par_par_shape(p)); + tex_flush_node(par_club_penalties(p)); + tex_flush_node(par_inter_line_penalties(p)); + tex_flush_node(par_widow_penalties(p)); + tex_flush_node(par_display_widow_penalties(p)); + tex_flush_node(par_orphan_penalties(p)); + /* tokens */ + tex_flush_token_list(par_end_par_tokens(p)); + break; + case insert_node: + tex_flush_node_list(insert_list(p)); + break; + case mark_node: + tex_delete_token_reference(mark_ptr(p)); + break; + case adjust_node: + tex_flush_node_list(adjust_list(p)); + break; + case choice_node: + tex_aux_free_sub_node_list(choice_display_mlist(p)); + tex_aux_free_sub_node_list(choice_text_mlist(p)); + tex_aux_free_sub_node_list(choice_script_mlist(p)); + tex_aux_free_sub_node_list(choice_script_script_mlist(p)); + break; + case simple_noad: + case fraction_noad: + case radical_noad: + case accent_noad: + tex_aux_free_sub_node_list(noad_nucleus(p)); + tex_aux_free_sub_node_list(noad_subscr(p)); + tex_aux_free_sub_node_list(noad_supscr(p)); + tex_aux_free_sub_node_list(noad_subprescr(p)); + tex_aux_free_sub_node_list(noad_supprescr(p)); + tex_aux_free_sub_node_list(noad_prime(p)); + tex_aux_free_sub_node_list(noad_state(p)); + switch (t) { + case fraction_noad: + // tex_aux_free_sub_node_list(fraction_numerator(p)); + // tex_aux_free_sub_node_list(fraction_denominator(p)); + tex_aux_free_sub_node(fraction_left_delimiter(p)); + tex_aux_free_sub_node(fraction_right_delimiter(p)); + tex_aux_free_sub_node(fraction_middle_delimiter(p)); + break; + case radical_noad: + tex_aux_free_sub_node(radical_left_delimiter(p)); + tex_aux_free_sub_node(radical_right_delimiter(p)); + tex_aux_free_sub_node_list(radical_degree(p)); + break; + case accent_noad: + tex_aux_free_sub_node_list(accent_top_character(p)); + tex_aux_free_sub_node_list(accent_bottom_character(p)); + tex_aux_free_sub_node_list(accent_middle_character(p)); + break; + } + break; + case fence_noad: + tex_aux_free_sub_node_list(fence_delimiter_list(p)); + tex_aux_free_sub_node_list(fence_delimiter_top(p)); + tex_aux_free_sub_node_list(fence_delimiter_bottom(p)); + break; + case sub_box_node: + case sub_mlist_node: + tex_aux_free_sub_node_list(kernel_math_list(p)); + break; + case specification_node: + tex_dispose_specification_list(p); + break; + default: + break; + } + if (tex_nodetype_has_attributes(t)) { + delete_attribute_reference(node_attr(p)); + node_attr(p) = null; /* when we debug */ + lmt_properties_reset(lmt_lua_state.lua_instance, p); + } + } + tex_free_node(p, get_node_size(t)); + } +} + +/*tex Erase the list of nodes starting at |pp|. */ + +void tex_flush_node_list(halfword l) +{ + if (! l) { + /*tex Legal, but no-op. */ + return; + } else if (l <= lmt_node_memory_state.reserved || l >= lmt_node_memory_state.nodes_data.allocated) { + tex_formatted_error("nodes", "attempt to free an impossible node list %d of type %d", (int) l, node_type(l)); + } else if (lmt_node_memory_state.nodesizes[l] == 0) { + for (int i = (lmt_node_memory_state.reserved + 1); i < lmt_node_memory_state.nodes_data.allocated; i++) { + if (lmt_node_memory_state.nodesizes[i] > 0) { + tex_aux_check_node(i); + } + } + tex_formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(node_type(l)), (int) l); + } else { + /*tex Saves stack and time. */ + lua_State *L = lmt_lua_state.lua_instance; + lmt_properties_push(L); + while (l) { + halfword nxt = node_next(l); + tex_flush_node(l); + l = nxt; + } + /*tex Saves stack and time. */ + lmt_properties_pop(L); + } +} + +static void tex_aux_check_node(halfword p) +{ + halfword t = node_type(p); + switch (t) { + case glue_node: + tex_aux_node_range_test(p, glue_leader_ptr(p)); + break; + case hlist_node: + tex_aux_node_range_test(p, box_pre_adjusted(p)); + tex_aux_node_range_test(p, box_post_adjusted(p)); + // fall through + case vlist_node: + tex_aux_node_range_test(p, box_pre_migrated(p)); + tex_aux_node_range_test(p, box_post_migrated(p)); + // fall through + case unset_node: + case align_record_node: + tex_aux_node_range_test(p, box_list(p)); + break; + case insert_node: + tex_aux_node_range_test(p, insert_list(p)); + break; + case disc_node: + tex_aux_node_range_test(p, disc_pre_break_head(p)); + tex_aux_node_range_test(p, disc_post_break_head(p)); + tex_aux_node_range_test(p, disc_no_break_head(p)); + break; + case adjust_node: + tex_aux_node_range_test(p, adjust_list(p)); + break; + case choice_node: + tex_aux_node_range_test(p, choice_display_mlist(p)); + tex_aux_node_range_test(p, choice_text_mlist(p)); + tex_aux_node_range_test(p, choice_script_mlist(p)); + tex_aux_node_range_test(p, choice_script_script_mlist(p)); + break; + case simple_noad: + case radical_noad: + case fraction_noad: + case accent_noad: + tex_aux_node_range_test(p, noad_nucleus(p)); + tex_aux_node_range_test(p, noad_subscr(p)); + tex_aux_node_range_test(p, noad_supscr(p)); + tex_aux_node_range_test(p, noad_subprescr(p)); + tex_aux_node_range_test(p, noad_supprescr(p)); + tex_aux_node_range_test(p, noad_prime(p)); + tex_aux_node_range_test(p, noad_state(p)); + switch (t) { + case radical_noad: + tex_aux_node_range_test(p, radical_degree(p)); + tex_aux_node_range_test(p, radical_left_delimiter(p)); + tex_aux_node_range_test(p, radical_right_delimiter(p)); + break; + case fraction_noad: + // tex_aux_node_range_test(p, fraction_numerator(p)); + // tex_aux_node_range_test(p, fraction_denominator(p)); + tex_aux_node_range_test(p, fraction_left_delimiter(p)); + tex_aux_node_range_test(p, fraction_right_delimiter(p)); + tex_aux_node_range_test(p, fraction_middle_delimiter(p)); + break; + case accent_noad: + tex_aux_node_range_test(p, accent_top_character(p)); + tex_aux_node_range_test(p, accent_bottom_character(p)); + tex_aux_node_range_test(p, accent_middle_character(p)); + break; + } + break; + case fence_noad: + tex_aux_node_range_test(p, fence_delimiter_list(p)); + tex_aux_node_range_test(p, fence_delimiter_top(p)); + tex_aux_node_range_test(p, fence_delimiter_bottom(p)); + break; + case par_node: + tex_aux_node_range_test(p, par_box_left(p)); + tex_aux_node_range_test(p, par_box_right(p)); + tex_aux_node_range_test(p, par_box_middle(p)); + tex_aux_node_range_test(p, par_left_skip(p)); + tex_aux_node_range_test(p, par_right_skip(p)); + tex_aux_node_range_test(p, par_baseline_skip(p)); + tex_aux_node_range_test(p, par_line_skip(p)); + tex_aux_node_range_test(p, par_par_shape(p)); + tex_aux_node_range_test(p, par_club_penalties(p)); + tex_aux_node_range_test(p, par_inter_line_penalties(p)); + tex_aux_node_range_test(p, par_widow_penalties(p)); + tex_aux_node_range_test(p, par_display_widow_penalties(p)); + tex_aux_node_range_test(p, par_orphan_penalties(p)); + tex_aux_node_range_test(p, par_par_fill_left_skip(p)); + tex_aux_node_range_test(p, par_par_fill_right_skip(p)); + tex_aux_node_range_test(p, par_par_init_left_skip(p)); + tex_aux_node_range_test(p, par_par_init_right_skip(p)); + break; + default: + break; + } +} + +/* +halfword fix_node_list(halfword head) +{ + if (head) { + halfword tail = head; + halfword next = node_next(head); + while (next) { + node_prev(next) = tail; + tail = next; + next = node_next(tail); + } + return tail; + } else { + return null; + } +} +*/ + +halfword tex_get_node(int size) +{ + if (size < max_chain_size) { + halfword p = lmt_node_memory_state.free_chain[size]; + if (p) { + lmt_node_memory_state.free_chain[size] = node_next(p); + lmt_node_memory_state.nodesizes[p] = (char) size; + node_next(p) = null; + lmt_node_memory_state.nodes_data.ptr += size; + return p; + } else { + return tex_aux_allocated_node(size); + } + } else { + return tex_normal_error("nodes", "there is a problem in getting a node, case 1"); + } +} + +void tex_free_node(halfword p, int size) /* no need to pass size, we can get is here */ +{ + if (p > lmt_node_memory_state.reserved && size < max_chain_size) { + lmt_node_memory_state.nodesizes[p] = 0; + node_next(p) = lmt_node_memory_state.free_chain[size]; + lmt_node_memory_state.free_chain[size] = p; + lmt_node_memory_state.nodes_data.ptr -= size; + } else { + tex_formatted_error("nodes", "node number %d of type %d with size %d should not be freed", (int) p, node_type(p), size); + } +} + +/*tex + + At the start of the node memory area we reserve some special nodes, for instance frequently + used glue specifications. We could as well just use new_glue here but for the moment we stick + to the traditional approach. We can omit the zeroing because it's already done. + +*/ + +static void tex_aux_initialize_glue(halfword n, scaled wi, scaled st, scaled sh, halfword sto, halfword sho) +{ + // memset((void *) (node_memory_state.nodes + n), 0, (sizeof(memoryword) * node_memory_state.nodesizes[glue_spec_node])); + node_type(n) = glue_spec_node; + glue_amount(n) = wi; + glue_stretch(n) = st; + glue_shrink(n) = sh; + glue_stretch_order(n) = sto; + glue_shrink_order(n) = sho; +} + +static void tex_aux_initialize_whatever_node(halfword n, quarterword t) +{ + // memset((void *) (node_memory_state.nodes + n), 0, (sizeof(memoryword) * node_memory_state.nodesizes[t])); + node_type(n) = t; +} + +static void tex_aux_initialize_character(halfword n, halfword chr) +{ + // memset((void *) (node_memory_state.nodes + n), 0, (sizeof(memoryword) * node_memory_state.nodesizes[glyph_node])); + node_type(n) = glyph_node; + glyph_character(n) = chr; +} +# define reserved_node_slots 32 + +void tex_initialize_node_mem() +{ + memoryword *nodes = NULL; + char *sizes = NULL; + int size = 0; + if (lmt_main_state.run_state == initializing_state) { + size = lmt_node_memory_state.nodes_data.minimum; + lmt_node_memory_state.reserved = last_reserved; + lmt_node_memory_state.nodes_data.top = last_reserved + 1; + lmt_node_memory_state.nodes_data.allocated = size; + lmt_node_memory_state.nodes_data.ptr = last_reserved; + } else { + size = lmt_node_memory_state.nodes_data.allocated; + lmt_node_memory_state.nodes_data.initial = lmt_node_memory_state.nodes_data.ptr; + } + if (size >0) { + nodes = aux_allocate_clear_array(sizeof(memoryword), size, reserved_node_slots); + sizes = aux_allocate_clear_array(sizeof(char), size, reserved_node_slots); + } + if (nodes && sizes) { + lmt_node_memory_state.nodes = nodes; + lmt_node_memory_state.nodesizes = sizes; + } else { + tex_overflow_error("nodes", size); + } +} + +void tex_initialize_nodes(void) +{ + if (lmt_main_state.run_state == initializing_state) { + /*tex Initialize static glue specs. */ + + tex_aux_initialize_glue(zero_glue, 0, 0, 0, 0, 0); + tex_aux_initialize_glue(fi_glue, 0, 0, 0, fi_glue_order, 0); + tex_aux_initialize_glue(fil_glue, 0, unity, 0, fil_glue_order, 0); + tex_aux_initialize_glue(fill_glue, 0, unity, 0, fill_glue_order, 0); + tex_aux_initialize_glue(filll_glue, 0, unity, unity, fil_glue_order, fil_glue_order); + tex_aux_initialize_glue(fil_neg_glue, 0, -unity, 0, fil_glue_order, 0); + + /*tex Initialize node list heads. */ + + tex_aux_initialize_whatever_node(page_insert_head, temp_node); /* actually a split node */ + tex_aux_initialize_whatever_node(contribute_head, temp_node); + tex_aux_initialize_whatever_node(page_head, temp_node); + tex_aux_initialize_whatever_node(temp_head, temp_node); + tex_aux_initialize_whatever_node(hold_head, temp_node); + tex_aux_initialize_whatever_node(post_adjust_head, temp_node); + tex_aux_initialize_whatever_node(pre_adjust_head, temp_node); + tex_aux_initialize_whatever_node(post_migrate_head, temp_node); + tex_aux_initialize_whatever_node(pre_migrate_head, temp_node); + tex_aux_initialize_whatever_node(align_head, temp_node); + tex_aux_initialize_whatever_node(active_head, unhyphenated_node); + tex_aux_initialize_whatever_node(end_span, span_node); + + tex_aux_initialize_character(begin_period, '.'); + tex_aux_initialize_character(end_period, '.'); + } +} + +void tex_dump_node_mem(dumpstream f) +{ + dump_int(f, lmt_node_memory_state.nodes_data.allocated); + dump_int(f, lmt_node_memory_state.nodes_data.top); + dump_things(f, lmt_node_memory_state.nodes[0], (size_t) lmt_node_memory_state.nodes_data.top + 1); + dump_things(f, lmt_node_memory_state.nodesizes[0], lmt_node_memory_state.nodes_data.top); + dump_things(f, lmt_node_memory_state.free_chain[0], max_chain_size); + dump_int(f, lmt_node_memory_state.nodes_data.ptr); + dump_int(f, lmt_node_memory_state.reserved); +} + +/*tex + Node memory is (currently) also used for some stack related nodes. Using dedicated arrays instead + makes sense but on the other hand this is the charm of \TEX. Variable nodes are no longer using + the node pool so we don't need clever code to reclaim space. We have plenty anyway. +*/ + +void tex_undump_node_mem(dumpstream f) // todo: check allocation +{ + undump_int(f, lmt_node_memory_state.nodes_data.allocated); + undump_int(f, lmt_node_memory_state.nodes_data.top); + tex_initialize_node_mem(); + undump_things(f, lmt_node_memory_state.nodes[0], (size_t) lmt_node_memory_state.nodes_data.top + 1); + undump_things(f, lmt_node_memory_state.nodesizes[0], (size_t) lmt_node_memory_state.nodes_data.top); + undump_things(f, lmt_node_memory_state.free_chain[0], max_chain_size); + undump_int(f, lmt_node_memory_state.nodes_data.ptr); + undump_int(f, lmt_node_memory_state.reserved); +} + +static halfword tex_aux_allocated_node(int s) +{ + int old = lmt_node_memory_state.nodes_data.top; + int new = old + s; + if (new > lmt_node_memory_state.nodes_data.allocated) { + if (lmt_node_memory_state.nodes_data.allocated + lmt_node_memory_state.nodes_data.step <= lmt_node_memory_state.nodes_data.size) { + memoryword *nodes = aux_reallocate_array(lmt_node_memory_state.nodes, sizeof(memoryword), lmt_node_memory_state.nodes_data.allocated + lmt_node_memory_state.nodes_data.step, reserved_node_slots); + char *sizes = aux_reallocate_array(lmt_node_memory_state.nodesizes, sizeof(char), lmt_node_memory_state.nodes_data.allocated + lmt_node_memory_state.nodes_data.step, reserved_node_slots); + if (nodes && sizes) { + lmt_node_memory_state.nodes = nodes; + lmt_node_memory_state.nodesizes = sizes; + memset((void *) (nodes + lmt_node_memory_state.nodes_data.allocated), 0, (size_t) lmt_node_memory_state.nodes_data.step * sizeof(memoryword)); + memset((void *) (sizes + lmt_node_memory_state.nodes_data.allocated), 0, (size_t) lmt_node_memory_state.nodes_data.step * sizeof(char)); + lmt_node_memory_state.nodes_data.allocated += lmt_node_memory_state.nodes_data.step; + lmt_run_memory_callback("node", 1); + } else { + lmt_run_memory_callback("node", 0); + tex_overflow_error("node memory size", lmt_node_memory_state.nodes_data.size); + } + } + if (new > lmt_node_memory_state.nodes_data.allocated) { + tex_overflow_error("node memory size", lmt_node_memory_state.nodes_data.size); + } + } + /* We allocate way larger than the maximum size. */ + // printf("old=%i size=%i new=%i\n",old,s,new); + lmt_node_memory_state.nodesizes[old] = (char) s; + lmt_node_memory_state.nodes_data.top = new; + return old; +} + +int tex_n_of_used_nodes(int counts[]) +{ + int n = 0; + for (int i = 0; i < max_node_type; i++) { + counts[i] = 0; + } + for (int i = lmt_node_memory_state.nodes_data.top; i > lmt_node_memory_state.reserved; i--) { + if (lmt_node_memory_state.nodesizes[i] > 0 && (node_type(i) <= max_node_type)) { + counts[node_type(i)] += 1; + } + } + for (int i = 0; i < max_node_type; i++) { + n += counts[i]; + } + return n; +} + +halfword tex_list_node_mem_usage(void) +{ + char *saved_varmem_sizes = aux_allocate_array(sizeof(char), lmt_node_memory_state.nodes_data.allocated, 1); + if (saved_varmem_sizes) { + halfword q = null; + halfword p = null; + memcpy(saved_varmem_sizes, lmt_node_memory_state.nodesizes, (size_t) lmt_node_memory_state.nodes_data.allocated); + for (halfword i = lmt_node_memory_state.reserved + 1; i < (lmt_node_memory_state.nodes_data.allocated - 1); i++) { + if (saved_varmem_sizes[i] > 0) { + halfword j = tex_copy_node(i); + if (p) { + node_next(p) = j; + } else { + q = j; + } + p = j; + } + } + aux_deallocate_array(saved_varmem_sizes); + return q; + } else { + return null; + } +} + +/* + Now comes some attribute stuff. We could have a fast allocator for them and a dedicated pool + (actually for each node tyep I guess). +*/ + +inline static halfword tex_aux_new_attribute_list_node(halfword count) +{ + halfword r = tex_get_node(attribute_node_size); + node_type(r) = attribute_node; + node_subtype(r) = attribute_list_subtype; + attribute_unset(r) = 0; + attribute_count(r) = count; + return r; +} + +inline static halfword tex_aux_new_attribute_node(halfword index, int value) +{ + halfword r = tex_get_node(attribute_node_size); + node_type(r) = attribute_node; + node_subtype(r) = attribute_value_subtype; + attribute_index(r) = index; + attribute_value(r) = value; + return r; +} + +inline static halfword tex_aux_copy_attribute_node(halfword n) +{ + halfword a = tex_get_node(attribute_node_size); + memcpy((void *) (lmt_node_memory_state.nodes + a), (void *) (lmt_node_memory_state.nodes + n), (sizeof(memoryword) * attribute_node_size)); + return a; +} + +halfword tex_copy_attribute_list(halfword a_old) +{ + if (a_old && a_old != attribute_cache_disabled) { + halfword a_new = tex_aux_new_attribute_list_node(0); + halfword p_old = a_old; + halfword p_new = a_new; + p_old = node_next(p_old); + while (p_old) { + halfword a = tex_copy_node(p_old); + node_next(p_new) = a; + p_new = a; + p_old = node_next(p_old); + } + node_next(p_new) = null; + return a_new; + } else { + return a_old; + } +} + +halfword tex_copy_attribute_list_set(halfword a_old, int index, int value) +{ + halfword a_new = tex_aux_new_attribute_list_node(0); + halfword p_new = a_new; + int done = 0; + if (a_old && a_old != attribute_cache_disabled) { + halfword p_old = node_next(a_old); + while (p_old) { + halfword i = attribute_index(p_old); + if (! done && i >= index) { + if (value != unused_attribute_value) { + halfword a = tex_aux_new_attribute_node(index, value); + node_next(p_new) = a; + p_new = a; + } + done = 1; + if (i == index) { + goto CONTINUE; + } + } + /* APPEND: */ + { + halfword a = tex_aux_copy_attribute_node(p_old); + node_next(p_new) = a; + p_new = a; + } + CONTINUE: + p_old = node_next(p_old); + } + node_next(p_new) = null; + } + if (! done && value != unused_attribute_value) { + halfword b = tex_aux_new_attribute_node(index, value); + node_next(p_new) = b; + } + return a_new; +} + +static void tex_aux_update_attribute_cache(void) +{ + halfword p = tex_aux_new_attribute_list_node(0); + set_current_attribute_state(p); + for (int i = 0; i <= lmt_node_memory_state.max_used_attribute; i++) { + int v = attribute_register(i); + if (v > unused_attribute_value) { + halfword r = tex_aux_new_attribute_node(i, v); + node_next(p) = r; + p = r; + } + } + if (! node_next(current_attribute_state)) { + tex_free_node(current_attribute_state, attribute_node_size); + set_current_attribute_state(null); + } else { + add_attribute_reference(current_attribute_state); + } +} + +void tex_build_attribute_list(halfword target) +{ + if (lmt_node_memory_state.max_used_attribute >= 0) { + if (! current_attribute_state || current_attribute_state == attribute_cache_disabled) { + tex_aux_update_attribute_cache(); + if (! current_attribute_state) { + return; + } + } + add_attribute_reference(current_attribute_state); + /*tex Checking for validity happens before the call; the subtype can be unset (yet). */ + node_attr(target) = current_attribute_state; + } +} + +halfword tex_current_attribute_list(void) +{ + if (lmt_node_memory_state.max_used_attribute >= 0) { + if (! current_attribute_state || current_attribute_state == attribute_cache_disabled) { + tex_aux_update_attribute_cache(); + } + return current_attribute_state; + } else { + return null ; + } +} + +/*tex + + There can be some gain in setting |attr_last_unset_enabled| but only when a lot of unsetting + happens with rather long attribute lists, which actually is rare. + + One tricky aspect if attributes is that when we test for a list head being the same, we have + the problem that freeing and (re)allocating can result in the same node address. Flushing in + reverse order sort of prevents that. + +*/ + +void tex_dereference_attribute_list(halfword a) +{ + if (a && a != attribute_cache_disabled) { + if (node_type(a) == attribute_node && node_subtype(a) == attribute_list_subtype){ + if (attribute_count(a) > 0) { + --attribute_count(a); + if (attribute_count(a) == 0) { + if (a == current_attribute_state) { + set_current_attribute_state(attribute_cache_disabled); + } + { + int u = 0; + /* this works (different order) */ + while (a) { + halfword n = node_next(a); + lmt_node_memory_state.nodesizes[a] = 0; + node_next(a) = lmt_node_memory_state.free_chain[attribute_node_size]; + lmt_node_memory_state.free_chain[attribute_node_size] = a; + ++u; + a = n; + } + /* this doesn't always (which is weird) */ + // halfword h = a; + // halfword t = a; + // while (a) { + // lmt_node_memory_state.nodesizes[a] = 0; + // ++u; + // t = a; + // a = node_next(a); + // } + // node_next(t) = lmt_node_memory_state.free_chain[attribute_node_size]; + // lmt_node_memory_state.free_chain[attribute_node_size] = h; + /* */ + lmt_node_memory_state.nodes_data.ptr -= u * attribute_node_size; + } + } + } else { + tex_formatted_error("nodes", "zero referenced attribute list %i", a); + } + } else { + tex_formatted_error("nodes", "trying to delete an attribute reference of a non attribute list node %i (%i)", a, node_type(a)); + } + } +} + +/*tex + Here |p| is an attr list head, or zero. This one works on a copy, so we can overwrite a value! +*/ + +halfword tex_patch_attribute_list(halfword list, int index, int value) +{ + if (list == attribute_cache_disabled) { + return list; + } else if (list) { + halfword current = node_next(list); + halfword previous = list; + while (current) { + int i = attribute_index(current); + if (i == index) { + /*tex Replace: */ + attribute_value(current) = value; + return list; + } else if (i > index) { + /*tex Prepend: */ + halfword r = tex_aux_new_attribute_node(index, value); + node_next(previous) = r; + node_next(r) = current; + return list; + } else { + previous = current; + current = node_next(current); + } + } + { + /*tex Append: */ + halfword r = tex_aux_new_attribute_node(index, value); + node_next(r) = node_next(previous); + node_next(previous) = r; + } + } else { + /*tex Watch out, we don't set a ref count, this branch is not seen anyway. */ + halfword r = tex_aux_new_attribute_node(index, value); + list = tex_aux_new_attribute_list_node(0); + node_next(list) = r; + } + return list; +} + +/* todo: combine set and unset */ + +void tex_set_attribute(halfword target, int index, int value) +{ + /*tex Not all nodes can have an attribute list. */ + if (tex_nodetype_has_attributes(node_type(target))) { + if (value == unused_attribute_value) { + tex_unset_attribute(target, index, value); + } else { + /*tex If we have no list, we create one and quit. */ + halfword a = node_attr(target); + /* needs checking: can we get an empty one here indeed, the vlink test case ... */ + if (a) { + halfword p = node_next(a); + while (p) { + int i = attribute_index(p); + if (i == index) { + if (attribute_value(p) == value) { + return; + } else { + break; + } + } else if (i > index) { + break; + } else { + p = node_next(p); + } + } + // p = tex_copy_attribute_list_set(a, index, value); + // tex_attach_attribute_list_attribute(target, p); + // } else { + // halfword p = tex_copy_attribute_list_set(null, index, value); + // tex_attach_attribute_list_attribute(target, p); + // } + } + a = tex_copy_attribute_list_set(a, index, value); + tex_attach_attribute_list_attribute(target, a); + } + } +} + +int tex_unset_attribute(halfword target, int index, int value) +{ + if (tex_nodetype_has_attributes(node_type(target))) { + halfword p = node_attr(target); + if (p) { + halfword c = node_next(p); + while (c) { + halfword i = attribute_index(c); + if (i == index) { + halfword v = attribute_value(c); + if (v != value) { + halfword l = tex_copy_attribute_list_set(p, index, value); + tex_attach_attribute_list_attribute(target, l); + } + return v; + } else if (i > index) { + return unused_attribute_value; + } + c = node_next(c); + } + } + } + return unused_attribute_value; +} + +void tex_unset_attributes(halfword first, halfword last, int index) +{ + halfword a = null; + halfword q = null; + halfword n = first; + while (n) { + if (tex_nodetype_has_attributes(node_type(n))) { + halfword p = node_attr(n); + if (p) { + if (p == q) { + tex_attach_attribute_list_attribute(n, a); + } else { + halfword c = node_next(p); + while (c) { + halfword i = attribute_index(c); + if (i == index) { + q = p; + a = tex_copy_attribute_list_set(p, index, unused_attribute_value); /* check */ + tex_attach_attribute_list_attribute(n, a); + break; + } else if (i > index) { + break; + } + c = node_next(c); + } + } + } + } + if (n == last) { + break; + } else { + n = node_next(n); + } + } +} + +int tex_has_attribute(halfword n, int index, int value) +{ + if (tex_nodetype_has_attributes(node_type(n))) { + halfword p = node_attr(n); + if (p) { + p = node_next(p); + while (p) { + if (attribute_index(p) == index) { + int v = attribute_value(p); + if (value == v || value == unused_attribute_value) { + return v; + } else { + return unused_attribute_value; + } + } else if (attribute_index(p) > index) { + return unused_attribute_value; + } + p = node_next(p); + } + } + } + return unused_attribute_value; +} + +/*tex + Because we have more detail available we provide node names and show a space when we have one. + The disc nodes are also more granular. I might drop the font in showing glyph nodes. A previous + version used full node types inside brackets but we now collapse the node types and use only + the first character of the type. Eventually I might come up with some variants. + */ + +void tex_print_short_node_contents(halfword p) +{ + int collapsing = 0; + while (p) { + switch (node_type(p)) { + case rule_node: + if (collapsing) { tex_print_char(']'); collapsing = 0; } + tex_print_char('|'); + break; + case glue_node: + switch (node_subtype(p)) { + case space_skip_glue: + case xspace_skip_glue: + case zero_space_skip_glue: + if (collapsing) { tex_print_char(']'); collapsing = 0; } + tex_print_char(' '); + break; + default: + goto DEFAULT; + } + break; + case math_node: + if (collapsing) { tex_print_char(']'); collapsing = 0; } + tex_print_char('$'); + break; + case disc_node: + if (collapsing) { tex_print_char(']'); collapsing = 0; } + tex_print_str("[["); + tex_print_short_node_contents(disc_pre_break_head(p)); + tex_print_str("]["); + tex_print_short_node_contents(disc_post_break_head(p)); + tex_print_str("]["); + tex_print_short_node_contents(disc_no_break_head(p)); + tex_print_str("]]"); + break; + case dir_node: + if (collapsing) { tex_print_char(']'); collapsing = 0; } + if (node_subtype(p) == cancel_dir_subtype) { + tex_print_str(" >"); + } else { + tex_print_str(dir_direction(p) ? "<r2l " : "<l2r "); + } + break; + case glyph_node: + if (collapsing) { tex_print_char(']'); collapsing = 0; } + if (glyph_font(p) != lmt_print_state.font_in_short_display) { + tex_print_font_identifier(glyph_font(p)); + tex_print_char(' '); + lmt_print_state.font_in_short_display = glyph_font(p); + } + tex_print_tex_str(glyph_character(p)); + break; + case par_node: + if (collapsing) { tex_print_char(']'); collapsing = 0; } + tex_print_str(par_dir(p) ? "<r2l p>" : "<l2r p>"); + break; + default: + DEFAULT: + if (! collapsing) { + tex_print_char('['); + collapsing = 1; + } + tex_print_char(lmt_interface.node_data[node_type(p)].name[0]); + break; + } + p = node_next(p); + } + if (collapsing) { + tex_print_char(']'); + } +} + +/*tex + + Now we are ready for |show_node_list| itself. This procedure has been written to be \quote + {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 |memoryword| 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. + +*/ + +void tex_print_node_list(halfword p, const char *what, int threshold, int max) +{ + if (p) { + if (what) { + tex_append_char('.'); + tex_append_char('.'); + tex_print_levels(); + tex_print_current_string(); + tex_print_str_esc(what); + } else { + /*tex This happens in math. */ + } + tex_append_char('.'); + tex_append_char('.'); + tex_show_node_list(p, threshold, max); // show_box_depth_par, show_box_breadth_par + tex_flush_char(); + tex_flush_char(); + if (what) { + tex_flush_char(); + tex_flush_char(); + } + } +} + +/*tex + + Print a node list symbolically. This one is adaped to the fact that we have a bit more + granularity in subtypes and some more fields. It is therefore not compatible with traditional + \TEX. This is work in progress. I will also normalize some subtype names so ... + +*/ + +static void tex_aux_show_attr_list(halfword p) +{ + p = node_attr(p); + if (p) { + int callback_id = lmt_callback_defined(get_attribute_callback); + if (tracing_nodes_par > 1) { + tex_print_format("<%i#%i>", p, attribute_count(p)); + } + tex_print_char('['); + p = node_next(p); + while (p) { + halfword k = attribute_index(p); + halfword v = attribute_value(p); + if (callback_id) { + strnumber u = tex_save_cur_string(); + char *ks = NULL; + char *vs = NULL; + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "dd->RR", k, v, &ks, &vs); + tex_restore_cur_string(u); + if (ks) { + tex_print_str(ks); + lmt_memory_free(ks); + } else { + tex_print_int(k); + } + tex_print_char('='); + if (vs) { + tex_print_str(vs); + lmt_memory_free(vs); + } else { + tex_print_int(v); + } + } else { + tex_print_int(k); + tex_print_char('='); + tex_print_int(v); + }; + p = node_next(p); + if (p) { + tex_print_char(','); + } + } + tex_print_char(']'); + } +} + +void tex_print_name(halfword n, const char* what) +{ + tex_print_str_esc(what); + if (tracing_nodes_par > 0) { + tex_print_char('<'); + tex_print_int(n); + tex_print_char('>'); + } +} + +static void tex_aux_print_subtype_and_attributes_str(halfword p, const char *n) +{ + if (show_node_details_par > 0) { + tex_print_char('['); + tex_print_str(n); + tex_print_char(']'); + } + if (show_node_details_par > 1 && tex_nodetype_has_attributes(node_type(p))) { + tex_aux_show_attr_list(p); + } +} + +void tex_print_extended_subtype(halfword p, quarterword s) +{ + halfword st = s; + switch (p ? node_type(p) : simple_noad) { + case hlist_node: + if (s > noad_class_list_base) { + st -= noad_class_list_base; + } + case simple_noad: + case math_char_node: + { + int callback_id = lmt_callback_defined(get_noad_class_callback); + if (callback_id) { + strnumber u = tex_save_cur_string(); + char *v = NULL; + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "d->R", st, &v); + tex_restore_cur_string(u); + if (v) { + if (p && node_type(p) == hlist_node) { + tex_print_str("math"); + } + tex_print_str(v); + lmt_memory_free(v); + break; + } + } + /* fall through */ + } + break; + default: + tex_print_int(s); + break; + } +} + +static void tex_print_subtype_and_attributes_info(halfword p, quarterword s, node_info *data) +{ + if (show_node_details_par > 0) { + tex_print_char('['); + if (data && data->subtypes && s >= data->first && s <= data->last) { + tex_print_str(data->subtypes[s].name); + } else { + tex_print_extended_subtype(p, s); + } + tex_print_char(']'); + } + if (show_node_details_par > 1 && tex_nodetype_has_attributes(node_type(p))) { + tex_aux_show_attr_list(p); + } +} + +static void tex_print_node_and_details(halfword p) +{ + halfword type = node_type(p); + quarterword subtype = node_subtype(p); + tex_print_name(p, lmt_interface.node_data[type].name); + switch (type) { + case temp_node: + case whatsit_node: + return; + } + tex_print_subtype_and_attributes_info(p, subtype, &lmt_interface.node_data[type]); +} + +static void tex_aux_print_subtype_and_attributes_int(halfword p, halfword n) +{ + if (show_node_details_par > 0) { \ + tex_print_char('['); + tex_print_int(n); + tex_print_char(']'); + } + if (show_node_details_par > 1 && tex_nodetype_has_attributes(node_type(p))) { + tex_aux_show_attr_list(p); + } +} + +const char *tex_aux_subtype_str(halfword n) +{ + if (n) { + node_info *data = &lmt_interface.node_data[node_type(n)]; + if (data && data->subtypes && node_subtype(n) >= data->first && node_subtype(n) <= data->last) { + return data->subtypes[node_subtype(n)].name; + } + } + return ""; +} + +/*tex + + We're not downward compatible here and it might even evolve a bit (and maybe I'll add a + compability mode too). We have way more information and plenty of log space so there is no + need to be compact. Consider it work in progress. + + I admit that there is some self interest here in adding more detail. At some point (around + ctx 2019) I needed to see attribute values in the trace so I added that option which in turn + made me reformat the output a bit. Of course it makes sense to have the whole show be a + callback (and I might actually do that) but on the other hand it's so integral to \TEX\ that + it doesn't add much and in all the years that \LUATEX| is now arround I never really needed + it anyway. + + One option is to go completely |\node[key=value,key={value,value}]| here as that can be easily + parsed. It's to be decided. + + What is the string pool char data used for here? + + Per version 2.09.22 we use the values from the node definitions which is more consistent and + also makes the binary somewhat smaller. It's all in the details. It's a typical example of + a change doen when we're stabel for a while (as it influences tracing). + +*/ + +void tex_print_specnode(halfword v, int unit) +{ + if (tracing_nodes_par > 2) { + tex_print_format("<%i>", v); + } + tex_print_spec(v, unit); +} + +void tex_aux_show_dictionary(halfword p, halfword properties, halfword group, halfword index,halfword font, halfword character) +{ + int callback_id = lmt_callback_defined(get_math_dictionary_callback); + if (callback_id) { + strnumber u = tex_save_cur_string(); + char *s = NULL; + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "Nddddd->R", p, properties, group, index, font, character, &s); + tex_restore_cur_string(u); + if (s) { + tex_print_str(", "); + tex_print_str(s); + lmt_memory_free(s); + return; + } + } + if (properties) { + tex_print_str(", properties "); + tex_print_qhex(properties); + } + if (group) { + tex_print_str(", group "); + tex_print_qhex(group); + } + if (index) { + tex_print_str(", index "); + tex_print_qhex(index); + } +} + +void tex_show_node_list(halfword p, int threshold, int max) +{ + if ((int) lmt_string_pool_state.string_temp_top > threshold) { + if (p > null) { + /*tex Indicate that there's been some truncation. */ + tex_print_format("[tracing depth %i reached]", threshold); + } + return; + } else { + /*tex The number of items already printed at this level: */ + int n = 0; + if (max <= 0) { + max = 5; + } + while (p) { + tex_print_levels(); + tex_print_current_string(); + ++n; + if (n > max) { + /*tex Time to stop. */ + halfword t = tex_tail_of_node_list(p); + if (t == p) { + /*tex We've showed the whole list. */ + return; + } else if (p == node_prev(t)) { + // /*tex We're just before the end. */ + } else { + tex_print_format("[tracing breadth %i reached]", max); + return; + } + } + tex_print_node_and_details(p); + switch (node_type(p)) { + case glyph_node: + if (show_node_details_par > 0) { + scaledwhd whd = tex_char_whd_from_glyph(p); + if (glyph_protected(p)) { + tex_print_str(", protected"); + } + /* effective */ + if (whd.wd) { + tex_print_str(", wd "); + tex_print_dimension(whd.wd, pt_unit); + } + if (whd.ht) { + tex_print_str(", ht "); + tex_print_dimension(whd.ht, pt_unit); + } + if (whd.dp) { + tex_print_str(", dp "); + tex_print_dimension(whd.dp, pt_unit); + } + if (whd.ic) { + tex_print_str(", ic "); + tex_print_dimension(whd.ic, pt_unit); + } + /* */ + if (get_glyph_language(p)) { + tex_print_str(", language (n="); + tex_print_int(get_glyph_language(p)); + tex_print_str(",l="); + tex_print_int(get_glyph_lhmin(p)); + tex_print_str(",r="); + tex_print_int(get_glyph_rhmin(p)); + tex_print_char(')'); + } + if (get_glyph_script(p)) { + tex_print_str(", script "); + tex_print_int(get_glyph_script(p)); + } + if (get_glyph_hyphenate(p)) { + tex_print_str(", hyphenationmode "); + tex_print_qhex(get_glyph_hyphenate(p)); + } + if (glyph_x_offset(p)) { + tex_print_str(", xoffset "); + tex_print_dimension(glyph_x_offset(p), pt_unit); + } + if (glyph_y_offset(p)) { + tex_print_str(", yoffset "); + tex_print_dimension(glyph_y_offset(p), pt_unit); + } + if (glyph_left(p)) { + tex_print_str(", left "); + tex_print_dimension(glyph_left(p), pt_unit); + } + if (glyph_right(p)) { + tex_print_str(", right "); + tex_print_dimension(glyph_right(p), pt_unit); + } + if (glyph_raise(p)) { + tex_print_str(", raise "); + tex_print_dimension(glyph_raise(p), pt_unit); + } + if (glyph_expansion(p)) { + tex_print_str(", expansion "); + tex_print_int(glyph_expansion(p)); + } + if (glyph_scale(p) && glyph_scale(p) != 1000) { + tex_print_str(", scale "); + tex_print_int(glyph_scale(p)); + } + if (glyph_x_scale(p) && glyph_x_scale(p) != 1000) { + tex_print_str(", xscale "); + tex_print_int(glyph_x_scale(p)); + } + if (glyph_y_scale(p) && glyph_y_scale(p) != 1000) { + tex_print_str(", yscale "); + tex_print_int(glyph_y_scale(p)); + } + if (glyph_data(p)) { + tex_print_str(", data "); + tex_print_int(glyph_data(p)); + } + if (glyph_state(p)) { + tex_print_str(", state "); + tex_print_int(glyph_state(p)); + } + if (glyph_options(p)) { + tex_print_str(", options "); + tex_print_qhex(glyph_options(p)); + } + if (glyph_discpart(p)) { + tex_print_str(", discpart "); + tex_print_int(glyph_discpart(p)); + } + tex_aux_show_dictionary(p, glyph_properties(p), glyph_group(p), glyph_index(p), glyph_font(p), glyph_character(p)); + } + tex_print_str(", font "); + /* this could be a callback */ + tex_print_font_identifier(glyph_font(p)); /* for now consistent with others, might change */ + tex_print_str(", glyph "); + tex_print_char_identifier(glyph_character(p)); + break; + case hlist_node: + case vlist_node: + case unset_node: + /*tex Display box |p|. */ + if (box_width(p)) { + tex_print_str(", width "); + tex_print_dimension(box_width(p), pt_unit); + } + if (box_height(p)) { + tex_print_str(", height "); + tex_print_dimension(box_height(p), pt_unit); + } + if (box_depth(p)) { + tex_print_str(", depth "); + tex_print_dimension(box_depth(p), pt_unit); + } + if (node_type(p) == unset_node) { + /*tex Display special fields of the unset node |p|. */ + if (box_span_count(p)) { + tex_print_str(", columns "); + tex_print_int(box_span_count(p) + 1); + } + if (box_glue_stretch(p)) { + tex_print_str(", stretch "); + tex_print_glue(box_glue_stretch(p), box_glue_order(p), no_unit); + } + if (box_glue_shrink(p)) { + tex_print_str(", shrink "); + tex_print_glue(box_glue_shrink(p), box_glue_sign(p), no_unit); + } + } else { + /*tex + + Display the value of |glue_set(p)|. The code will have to change in + this place if |glue_ratio| is a structured type instead of an + ordinary |real|. Note that this routine should avoid arithmetic + errors even if the |glue_set| field holds an arbitrary random value. + The following code assumes that a properly formed nonzero |real| + number has absolute value $2^{20}$ or more when it is regarded as an + integer; this precaution was adequate to prevent floating point + underflow on the author's computer. + + */ + double g = (double) (box_glue_set(p)); + if ((g != 0.0) && (box_glue_sign(p) != normal_glue_sign)) { + tex_print_str(", glue "); /*tex This was |glue set|. */ + if (box_glue_sign(p) == shrinking_glue_sign) { + tex_print_str("- "); + } + if (g > 20000.0 || g < -20000.0) { + if (g > 0.0) { + tex_print_char('>'); + } else { + tex_print_str("< -"); + } + tex_print_glue(20000 * unity, box_glue_order(p), no_unit); + } else { + tex_print_glue((scaled) glueround(unity *g), box_glue_order(p), no_unit); + } + } + if (box_shift_amount(p) != 0) { + tex_print_str(", shifted "); + tex_print_dimension(box_shift_amount(p), pt_unit); + } + if (valid_direction(box_dir(p))) { + tex_print_str(", direction "); + switch (box_dir(p)) { + case 0 : tex_print_str("l2r"); break; + case 1 : tex_print_str("r2l"); break; + default : tex_print_str("unset"); break; + } + } + if (box_geometry(p)) { + tex_print_str(", geometry "); + tex_print_qhex(box_geometry(p)); + if (tex_has_box_geometry(p, orientation_geometry)) { + tex_print_str(", orientation "); + tex_print_qhex(box_orientation(p)); + } + if (tex_has_box_geometry(p, offset_geometry)) { + tex_print_str(", offset("); + tex_print_dimension(box_x_offset(p), pt_unit); + tex_print_char(','); + tex_print_dimension(box_y_offset(p), pt_unit); + tex_print_char(')'); + } + if (tex_has_box_geometry(p, anchor_geometry)) { + if (box_anchor(p)) { + tex_print_str(", anchor "); + tex_print_qhex(box_anchor(p)); + } + if (box_source_anchor(p)) { + tex_print_str(", source "); + tex_print_int(box_source_anchor(p)); + } + if (box_target_anchor(p)) { + tex_print_str(", target "); + tex_print_int(box_target_anchor(p)); + } + } + } + if (box_index(p)) { + tex_print_str(", index "); + tex_print_int(box_index(p)); + } + if (box_package_state(p)) { + tex_print_str(", state "); + tex_print_int(box_package_state(p)); + } + } + tex_print_node_list(box_pre_adjusted(p), "preadjusted", threshold, max); + tex_print_node_list(box_pre_migrated(p), "premigrated", threshold, max); + tex_print_node_list(box_list(p), "list", threshold, max); + tex_print_node_list(box_post_migrated(p), "postmigrated", threshold, max); + tex_print_node_list(box_post_adjusted(p), "postadjusted", threshold, max); + break; + case rule_node: + /*tex Display rule |p|. */ + if (rule_width(p)) { + tex_print_str(", width "); + tex_print_rule_dimen(rule_width(p)); + } + if (rule_height(p)) { + tex_print_str(", height "); + tex_print_rule_dimen(rule_height(p)); + } + if (rule_depth(p)) { + tex_print_str(", depth "); + tex_print_rule_dimen(rule_depth(p)); + } + if (rule_left(p)) { + tex_print_str(", left / top "); + tex_print_rule_dimen(rule_left(p)); + } + if (rule_right(p)) { + tex_print_str(", right / bottom "); + tex_print_rule_dimen(rule_right(p)); + } + if (rule_x_offset(p)) { + tex_print_str(", xoffset "); + tex_print_rule_dimen(rule_x_offset(p)); + } + if (rule_y_offset(p)) { + tex_print_str(", yoffset "); + tex_print_rule_dimen(rule_y_offset(p)); + } + if (rule_font(p)) { + if (rule_font(p) < 0 || rule_font(p) >= rule_font_fam_offset) { + tex_print_str(", font "); + tex_print_font_identifier(rule_font(p)); + } else { + tex_print_str(", family "); + tex_print_int(rule_font(p) - rule_font_fam_offset); + } + } + if (rule_character(p)) { + tex_print_str(", character "); + tex_print_char_identifier(rule_character(p)); + } + break; + case insert_node: + /*tex Display insertion |p|. The natural size is the sum of height and depth. */ + tex_print_str(", index "); + tex_print_int(insert_index(p)); + tex_print_str(", total height "); + tex_print_dimension(insert_total_height(p), pt_unit); + tex_print_str(", max depth "); + tex_print_dimension(insert_max_depth(p), pt_unit); + tex_print_str(", split glue ("); + tex_print_specnode(insert_split_top(p), no_unit); + tex_print_str("), float cost "); + tex_print_int(insert_float_cost(p)); + tex_print_node_list(insert_list(p), "list", threshold, max); + break; + case dir_node: + tex_print_str(", direction "); + switch (dir_direction(p)) { + case direction_l2r : tex_print_str("l2r"); break; + case direction_r2l : tex_print_str("r2l"); break; + default : tex_print_str("unset"); break; + } + break; + case par_node: + { + halfword v; + /*tex We're already past processing so we only show the stored values. */ + if (node_subtype(p) == vmode_par_par_subtype) { + if (tex_par_state_is_set(p, par_par_shape_code) ) { v = par_par_shape(p) ; if (v) { tex_print_str(", parshape * "); } } + if (tex_par_state_is_set(p, par_inter_line_penalties_code) ) { v = par_inter_line_penalties(p) ; if (v) { tex_print_str(", interlinepenalties * "); } } + if (tex_par_state_is_set(p, par_club_penalties_code) ) { v = par_club_penalties(p) ; if (v) { tex_print_str(", clubpenalties * "); } } + if (tex_par_state_is_set(p, par_widow_penalties_code) ) { v = par_widow_penalties(p) ; if (v) { tex_print_str(", widowpenalties * "); } } + if (tex_par_state_is_set(p, par_display_widow_penalties_code)) { v = par_display_widow_penalties(p) ; if (v) { tex_print_str(", displsaywidowpenalties * "); } } + if (tex_par_state_is_set(p, par_orphan_penalties_code) ) { v = par_orphan_penalties(p) ; if (v) { tex_print_str(", orphanpenalties * "); } } + if (tex_par_state_is_set(p, par_hang_indent_code) ) { v = par_hang_indent(p) ; if (v) { tex_print_str(", hangindent "); tex_print_dimension(v, pt_unit); } } + if (tex_par_state_is_set(p, par_hang_after_code) ) { v = par_hang_after(p) ; if (v) { tex_print_str(", hangafter "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_hsize_code) ) { v = par_hsize(p) ; if (v) { tex_print_str(", hsize "); tex_print_dimension(v, pt_unit); } } + if (tex_par_state_is_set(p, par_right_skip_code) ) { v = par_right_skip(p) ; if (! tex_glue_is_zero(v)) { tex_print_str(", rightskip "); tex_print_specnode (v, pt_unit); } } + if (tex_par_state_is_set(p, par_left_skip_code) ) { v = par_left_skip(p) ; if (! tex_glue_is_zero(v)) { tex_print_str(", leftskip "); tex_print_specnode (v, pt_unit); } } + if (tex_par_state_is_set(p, par_last_line_fit_code) ) { v = par_last_line_fit(p) ; if (v) { tex_print_str(", lastlinefit "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_pre_tolerance_code) ) { v = par_pre_tolerance(p) ; if (v) { tex_print_str(", pretolerance "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_tolerance_code) ) { v = par_tolerance(p) ; if (v) { tex_print_str(", tolerance "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_looseness_code) ) { v = par_looseness(p) ; if (v) { tex_print_str(", looseness "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_adjust_spacing_code) ) { v = par_adjust_spacing(p) ; if (v) { tex_print_str(", adjustaspacing "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_adj_demerits_code) ) { v = par_adj_demerits(p) ; if (v) { tex_print_str(", adjdemerits "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_protrude_chars_code) ) { v = par_protrude_chars(p) ; if (v) { tex_print_str(", protrudechars "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_line_penalty_code) ) { v = par_line_penalty(p) ; if (v) { tex_print_str(", linepenalty "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_double_hyphen_demerits_code) ) { v = par_double_hyphen_demerits(p) ; if (v) { tex_print_str(", doublehyphendemerits "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_final_hyphen_demerits_code) ) { v = par_final_hyphen_demerits(p) ; if (v) { tex_print_str(", finalhyphendemerits "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_inter_line_penalty_code) ) { v = par_inter_line_penalty(p) ; if (v) { tex_print_str(", interlinepenalty "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_club_penalty_code) ) { v = par_club_penalty(p) ; if (v) { tex_print_str(", clubpenalty "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_widow_penalty_code) ) { v = par_widow_penalty(p) ; if (v) { tex_print_str(", widowpenalty "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_display_widow_penalty_code) ) { v = par_display_widow_penalty(p) ; if (v) { tex_print_str(", displaywidowpenalty "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_orphan_penalty_code) ) { v = par_orphan_penalty(p) ; if (v) { tex_print_str(", orphanpenalty "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_broken_penalty_code) ) { v = par_broken_penalty(p) ; if (v) { tex_print_str(", brokenpenalty "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_emergency_stretch_code) ) { v = par_emergency_stretch(p) ; if (v) { tex_print_str(", emergencystretch "); tex_print_dimension(v, pt_unit); } } + if (tex_par_state_is_set(p, par_par_indent_code) ) { v = par_par_indent(p) ; if (v) { tex_print_str(", parindent "); tex_print_dimension(v, pt_unit); } } + if (tex_par_state_is_set(p, par_par_fill_left_skip_code) ) { v = par_par_fill_left_skip(p) ; if (! tex_glue_is_zero(v)) { tex_print_str(", parfilleftskip "); tex_print_specnode (v, pt_unit); } } + if (tex_par_state_is_set(p, par_par_fill_right_skip_code) ) { v = par_par_fill_right_skip(p) ; if (! tex_glue_is_zero(v)) { tex_print_str(", parfillskip "); tex_print_specnode (v, pt_unit); } } + if (tex_par_state_is_set(p, par_par_init_left_skip_code) ) { v = par_par_init_left_skip(p) ; if (! tex_glue_is_zero(v)) { tex_print_str(", parinitleftskip "); tex_print_specnode (v, pt_unit); } } + if (tex_par_state_is_set(p, par_par_init_right_skip_code) ) { v = par_par_init_right_skip(p) ; if (! tex_glue_is_zero(v)) { tex_print_str(", parinitrightskip "); tex_print_specnode (v, pt_unit); } } + if (tex_par_state_is_set(p, par_baseline_skip_code) ) { v = par_baseline_skip(p) ; if (! tex_glue_is_zero(v)) { tex_print_str(", baselineskip "); tex_print_specnode (v, pt_unit); } } + if (tex_par_state_is_set(p, par_line_skip_code) ) { v = par_line_skip(p) ; if (! tex_glue_is_zero(v)) { tex_print_str(", lineskip "); tex_print_specnode (v, pt_unit); } } + if (tex_par_state_is_set(p, par_line_skip_limit_code) ) { v = par_line_skip_limit(p) ; if (v) { tex_print_str(", lineskiplimt "); tex_print_dimension(v, pt_unit); } } + if (tex_par_state_is_set(p, par_adjust_spacing_step_code) ) { v = par_adjust_spacing_step(p) ; if (v > 0) { tex_print_str(", adjustspacingstep "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_adjust_spacing_shrink_code) ) { v = par_adjust_spacing_shrink(p) ; if (v > 0) { tex_print_str(", adjustspacingshrink "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_adjust_spacing_stretch_code) ) { v = par_adjust_spacing_stretch(p) ; if (v > 0) { tex_print_str(", adjustspacingstretch "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_hyphenation_mode_code) ) { v = par_hyphenation_mode(p) ; if (v > 0) { tex_print_str(", hyphenationmode "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_shaping_penalties_mode_code) ) { v = par_shaping_penalties_mode(p) ; if (v > 0) { tex_print_str(", shapingpenaltiesmode "); tex_print_int (v); } } + if (tex_par_state_is_set(p, par_shaping_penalty_code) ) { v = par_shaping_penalty(p) ; if (v > 0) { tex_print_str(", shapingpenalty "); tex_print_int (v); } } + } + /* local boxes */ + v = tex_get_local_left_width(p) ; if (v) { tex_print_str(", leftboxwidth "); tex_print_dimension(v, pt_unit); } + v = tex_get_local_right_width(p) ; if (v) { tex_print_str(", rightboxwidth "); tex_print_dimension(v, pt_unit); } + tex_print_node_list(par_box_left(p), "leftbox", threshold, max); + tex_print_node_list(par_box_right(p), "rightbox", threshold, max); + tex_print_node_list(par_box_middle(p), "middlebox", threshold, max); + } + break; + case boundary_node: + if (boundary_data(p)) { + tex_print_str(", data "); + tex_print_int(boundary_data(p)); + } + break; + case whatsit_node: + { + int callback_id = lmt_callback_defined(show_whatsit_callback); + /*tex we always print this */ + if (callback_id) { + strnumber u = tex_save_cur_string(); + char *s = NULL; + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "Nd->S", p, 1, &s); + tex_restore_cur_string(u); + if (s) { + tex_aux_print_subtype_and_attributes_str(p, s); + lmt_memory_free(s); + } else { + tex_aux_print_subtype_and_attributes_int(p, node_subtype(p)); + } + } else { + tex_aux_print_subtype_and_attributes_int(p, node_subtype(p)); + } + /*tex but optionally there can be more */ + if (callback_id) { + int l = lmt_string_pool_state.string_temp_top / 2; + strnumber u = tex_save_cur_string(); + /*tex Todo: the tracing needs checking. */ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "Nddddd->", p, 2, l, (tracing_levels_par & (tracing_levels_group | tracing_levels_input)), cur_level, lmt_input_state.input_stack_data.ptr); + tex_restore_cur_string(u); + } + } + break; + case glue_node: + /*tex Display glue |p|. */ + if (is_leader(p)) { + /*tex Display leaders |p|. */ + tex_print_str(", leader "); + tex_print_specnode(p, no_unit); + tex_print_node_list(glue_leader_ptr(p), "list", threshold, max); + } else { + if (node_subtype(p) != conditional_math_glue && node_subtype(p) != rulebased_math_glue) { + tex_print_char(' '); + tex_print_specnode(p, node_subtype(p) < conditional_math_glue ? pt_unit : mu_unit); /* was |no_unit : mu_unit| */ + } + if (glue_data(p)) { + tex_print_str(", data "); + tex_print_int(glue_data(p)); + } + if (node_subtype(p) == space_skip_glue && glue_font(p)) { + tex_print_str(", font "); + tex_print_int(glue_font(p)); + } + } + break; + case kern_node: + /*tex Display kern |p| */ + tex_print_str(", amount "); + tex_print_dimension(kern_amount(p), pt_unit); + if (node_subtype(p) != explicit_math_kern_subtype) { + tex_print_unit(pt_unit); + if (kern_expansion(p)) { + tex_print_str(", expansion "); + tex_print_int(kern_expansion(p)); + } + } else { + tex_print_unit(mu_unit); + } + break; + case math_node: + /*tex Display math node |p|. */ + if (! tex_math_glue_is_zero(p)) { + tex_print_str(", glued "); + tex_print_specnode(p, no_unit); + } else if (math_surround(p)) { + tex_print_str(", surrounded "); + tex_print_dimension(math_surround(p), pt_unit); + } + if (math_penalty(p)) { + tex_print_str(", penalty "); + tex_print_int(math_penalty(p)); + } + break; + case penalty_node: + /*tex Display penalty |p|. */ + tex_print_str(", amount "); + tex_print_int(penalty_amount(p)); + break; + case disc_node: + if (disc_class(p) != unset_disc_class) { + tex_print_str(", class "); + tex_print_int(disc_class(p)); + } + if (disc_options(p)) { + tex_print_str(", options "); + tex_print_qhex(disc_options(p)); + } + tex_print_str(", penalty "); + tex_print_int(disc_penalty(p)); + tex_print_node_list(disc_pre_break_head(p), "prebreaklist", threshold, max); + tex_print_node_list(disc_post_break_head(p), "postbreaklist", threshold, max); + tex_print_node_list(disc_no_break_head(p), "nobreaklist", threshold, max); + break; + case mark_node: + /*tex Display mark |p|. */ + tex_print_str(", index "); + tex_print_int(mark_index(p)); + if (node_subtype(p) == reset_mark_value_code) { + tex_print_str(", reset"); + } else { + tex_print_token_list(NULL, token_link(mark_ptr(p))); /*tex We have a ref count token. */ + } + break; + case adjust_node: + /*tex Display adjustment |p|. */ + if (adjust_options(p)) { + tex_print_str(", options "); + tex_print_qhex(adjust_options(p)); + } + if (adjust_index(p)) { + tex_print_str(", index "); + tex_print_int(adjust_index(p)); + } + if (has_adjust_option(p, adjust_option_depth_before) && adjust_depth_before(p)) { + tex_print_str(", depthbefore "); + tex_print_dimension(adjust_depth_before(p), pt_unit); + } + if (has_adjust_option(p, adjust_option_depth_after) &&adjust_depth_before(p)) { + tex_print_str(", depthafter "); + tex_print_dimension(adjust_depth_after(p), pt_unit); + } + tex_print_node_list(adjust_list(p), "list", threshold, max); + break; + case glue_spec_node: + case math_spec_node: + case font_spec_node: + /*tex This is actually an error! */ + break; + case align_record_node: + tex_print_token_list(NULL, align_record_pre_part(p)); /*tex No ref count token here. */ + tex_print_levels(); + tex_print_str("..<content>"); + tex_print_token_list(NULL, align_record_post_part(p)); /*tex No ref count token here. */ + break; + case temp_node: + break; + default: + if (! tex_show_math_node(p, threshold, max)) { + tex_print_format("<unknown node type %i>", node_type(p)); + } + break; + } + p = node_next(p); + } + } +} + +/*tex + + This routine finds the base width of a horizontal box, using the same logic that \TEX82\ used + for |\predisplaywidth|. + +*/ + +static halfword tex_aux_get_actual_box_width(halfword r, halfword p, scaled initial_width) +{ + /*tex calculated |size| */ + scaled w = -max_dimen; + /*tex |w| plus possible glue amount */ + scaled v = initial_width; + while (p) { + /*tex increment to |v| */ + scaled d; + switch (node_type(p)) { + case glyph_node: + d = tex_glyph_width(p); + goto FOUND; + case hlist_node: + case vlist_node: + d = box_width(p); + goto FOUND; + case rule_node: + d = rule_width(p); + goto FOUND; + case kern_node: + d = kern_amount(p); + break; + case disc_node: + /*tex At the end of the line we should actually take the |pre|. */ + if (disc_no_break(p)) { + d = tex_aux_get_actual_box_width(r, disc_no_break_head(p),0); + if (d <= -max_dimen || d >= max_dimen) { + d = 0; + } + } else { + d = 0; + } + goto FOUND; + case math_node: + if (tex_math_glue_is_zero(p)) { + d = math_surround(p); + } else { + d = math_amount(p); + switch (box_glue_sign(r)) { + case stretching_glue_sign: + if ((box_glue_order(r) == math_stretch_order(p)) && math_stretch(p)) { + v = max_dimen; + } + break; + case shrinking_glue_sign: + if ((box_glue_order(r) == math_shrink_order(p)) && math_shrink(p)) { + v = max_dimen; + } + break; + } + break; + } + break; + case glue_node: + /*tex + We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set| + values, since such values are subject to system-dependent rounding. System + dependent numbers are not allowed to infiltrate parameters like + |pre_display_size|, since \TEX82 is supposed to make the same decisions on + all machines. + */ + d = glue_amount(p); + if (box_glue_sign(r) == stretching_glue_sign) { + if ((box_glue_order(r) == glue_stretch_order(p)) && glue_stretch(p)) { + v = max_dimen; + } + } else if (box_glue_sign(r) == shrinking_glue_sign) { + if ((box_glue_order(r) == glue_shrink_order(p)) && glue_shrink(p)) { + v = max_dimen; + } + } + if (is_leader(p)) { + goto FOUND; + } + break; + default: + d = 0; + break; + } + if (v < max_dimen) { + v += d; + } + goto NOT_FOUND; + FOUND: + if (v < max_dimen) { + v += d; + w = v; + } else { + w = max_dimen; + break; + } + NOT_FOUND: + p = node_next(p); + } + return w; +} + +halfword tex_actual_box_width(halfword r, scaled base_width) +{ + /*tex + + Often this is the same as: + + \starttyping + return + shift_amount(r) + base_width + + natural_sizes(list_ptr(r),null,(glueratio) box_glue_set(r),box_glue_sign(r),box_glue_order(r),box_dir(r)); + \stoptyping + */ + return tex_aux_get_actual_box_width(r, box_list(r), box_shift_amount(r) + base_width); +} + +int tex_list_has_glyph(halfword list) +{ + while (list) { + switch (node_type(list)) { + case glyph_node: + case disc_node: + return 1; + default: + list = node_next(list); + break; + } + } + return 0; +} + +/*tex + + Attribute lists need two extra globals to increase processing efficiency. |max_used_attr| + limits the test loop that checks for set attributes, and |attr_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. + + From the computer's standpoint, \TEX's chief mission is to create horizontal and vertical + lists. We shall now investigate how the elements of these lists are represented internally as + nodes in the dynamic memory. + + A horizontal or vertical list is linked together by |link| fields in the first word of each + node. Individual nodes represent boxes, glue, penalties, or special things like discretionary + hyphens; because of this variety, some nodes are longer than others, and we must distinguish + different kinds of nodes. We do this by putting a |type| field in the first word, together + with the link and an optional |subtype|. + + Character nodes appear only in horizontal lists, never in vertical lists. + + An |hlist_node| stands for a box that was made from a horizontal list. Each |hlist_node| is + seven words long, and contains the following fields (in addition to the mandatory |type| and + |link|, which we shall not mention explicitly when discussing the other node types): The + |height| and |width| and |depth| are scaled integers denoting the dimensions of the box. There + is also a |shift_amount| field, a scaled integer indicating how much this box should be + lowered (if it appears in a horizontal list), or how much it should be moved to the right (if + it appears in a vertical list). There is a |list_ptr| field, which points to the beginning of + the list from which this box was fabricated; if |list_ptr| is |null|, the box is empty. Finally, + there are three fields that represent the setting of the glue: |glue_set(p)| is a word of type + |glue_ratio| that represents the proportionality constant for glue setting; |glue_sign(p)| is + |stretching| or |shrinking| or |normal| depending on whether or not the glue should stretch or + shrink or remain rigid; and |glue_order(p)| specifies the order of infinity to which glue + setting applies (|normal|, |sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used. + + The |new_null_box| function returns a pointer to an |hlist_node| in which all subfields have + the values corresponding to |\hbox{}|. The |subtype| field is set to |min_quarterword|, since + that's the desired |span_count| value if this |hlist_node| is changed to an |unset_node|. + +*/ + +/*tex Create a new box node. */ + +halfword tex_new_null_box_node(quarterword t, quarterword s) +{ + // halfword p = tex_new_node(hlist_node, min_quarterword); + halfword p = tex_new_node(t, s); + box_dir(p) = (singleword) text_direction_par; + return p; +} + +/*tex + + A |vlist_node| is like an |hlist_node| in all respects except that it contains a vertical list. + + A |rule_node| stands for a solid black rectangle; it has |width|, |depth|, and |height| fields + just as in an |hlist_node|. However, if any of these dimensions is $-2^{30}$, the actual value + will be determined by running the rule up to the boundary of the innermost enclosing box. This + is called a \quote {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 \quote + {running}, so you have to change the ones that are not allowed to run. + +*/ + +halfword tex_new_rule_node(quarterword s) +{ + return tex_new_node(rule_node, s); +} + +/*tex + + Insertions are represented by |insert_node| records, where the |subtype| indicates the + corresponding box number. For example, |\insert 250| leads to an |insert_node| whose |subtype| + is |250 + min_quarterword|. The |height| field of an |insert_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 |insert_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|). + + We no longer check if the glyph exists because a replacement can be used instead. We copy some + properties when there is a parent passed. + +*/ + +halfword tex_new_glyph_node(quarterword s, halfword f, halfword c, halfword parent) +{ + halfword p = parent && node_type(parent) == glyph_node ? tex_copy_node(parent) : tex_aux_new_glyph_node_with_attributes(parent); + node_subtype(p) = s; + glyph_font(p) = f; + glyph_character(p) = c; + tex_char_process(f, c); + return p; +} + +/*tex + + A subset of the glyphs nodes represent ligatures: characters fabricated from the interaction + of two or more actual characters. The characters that generated the ligature have not been + forgotten, since they are needed for diagnostic messages; the |lig_ptr| field points to a + linked list of character nodes for all original characters that have been deleted. (This list + might be empty if the characters that generated the ligature were retained in other nodes.) + Remark: we no longer keep track of ligatures via |lig_ptr| because there is no guarantee that + they are consistently tracked; they are something internal anyway. Of course one can provide an + alternative at the \LUA\ end (which is what we do in \CONTEXT). + + The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if the original source of the + ligature included implicit left and/or right boundaries. These nodes are created by the C + function |new_ligkern|. + + A third general type of glyphs could be called a character, as it only appears in lists that + are not yet processed by the ligaturing and kerning steps of the program. + + |main_control| inserts these, and they are later converted to |subtype_normal| by |new_ligkern|. + +*/ + +/* +quarterword norm_min(int h) +{ + if (h <= 0) + return 1; + else if (h >= 255) + return 255; + else + return (quarterword) h; +} +*/ + +halfword tex_new_char_node(quarterword subtype, halfword fnt, halfword chr, int all) +{ + halfword p = tex_aux_new_glyph_node_with_attributes(null); + node_subtype(p) = subtype; + glyph_font(p) = fnt; + glyph_character(p) = chr; + if (all) { + glyph_data(p) = glyph_data_par; + /* no state */ + set_glyph_script(p, glyph_script_par); + set_glyph_language(p, cur_lang_par); + set_glyph_lhmin(p, left_hyphen_min_par); + set_glyph_rhmin(p, right_hyphen_min_par); + set_glyph_hyphenate(p, hyphenation_mode_par); + set_glyph_options(p, glyph_options_par); + set_glyph_scale(p, glyph_scale_par); + set_glyph_x_scale(p, glyph_x_scale_par); + set_glyph_y_scale(p, glyph_y_scale_par); + set_glyph_x_offset(p, glyph_x_offset_par); + set_glyph_y_offset(p, glyph_y_offset_par); + } + if (! tex_char_exists(fnt, chr)) { + int callback_id = lmt_callback_defined(missing_character_callback); + if (callback_id > 0) { + /* maybe direct node */ + lmt_run_callback(lmt_lua_state.lua_instance, callback_id, "Ndd->", p, fnt, chr); + } + } + return p; +} + +halfword tex_new_text_glyph(halfword fnt, halfword chr) +{ + halfword p = tex_get_node(glyph_node_size); + memset((void *) (lmt_node_memory_state.nodes + p + 1), 0, (sizeof(memoryword) * (glyph_node_size - 1))); + node_type(p) = glyph_node; + node_subtype(p) = glyph_unset_subtype; + glyph_font(p) = fnt; + glyph_character(p) = chr; + glyph_data(p) = glyph_data_par; + /* no state */ + set_glyph_script(p, glyph_script_par); + set_glyph_language(p, cur_lang_par); + set_glyph_lhmin(p, left_hyphen_min_par); + set_glyph_rhmin(p, right_hyphen_min_par); + set_glyph_hyphenate(p, hyphenation_mode_par); + set_glyph_options(p, glyph_options_par); + set_glyph_scale(p, glyph_scale_par); + set_glyph_x_scale(p, glyph_x_scale_par); + set_glyph_y_scale(p, glyph_y_scale_par); + set_glyph_x_offset(p, glyph_x_offset_par); + set_glyph_y_offset(p, glyph_y_offset_par); + return p; +} + +/*tex + + Here are a few handy helpers used by the list output routines. + + We had an xadvance but dropped it but it might come back eventually. The offsets are mostly + there to deal with anchoring and we assume kerns to be used to complement x offsets if needed: + just practical decisions made long ago. + + Why do we check y offset being positive for dp but not for ht? Maybe change this to be + consistent? Anyway, we have adapted \LUATEX\ so ... + + \startitemize + \startitem what we had before \stopitem + \startitem compensate height and depth \stopitem + \startitem compensate height and depth, take max \stopitem + \startitem we keep height and depth \stopitem + \stopitemize + +*/ + +/*tex These should move to the texfont.c as we have too many variants now. */ + +scaled tex_glyph_width(halfword p) +{ + scaled w = tex_char_width_from_glyph(p); + scaled x = glyph_x_offset(p); + if (x && tex_has_glyph_option(p, glyph_option_apply_x_offset)) { + w += x; /* or after expansion? needs testing */ + } + w -= (glyph_left(p) + glyph_right(p)); + return w; +} + +scaled tex_glyph_width_ex(halfword p) +{ + scaled w = tex_char_width_from_glyph(p); + scaled x = glyph_x_offset(p); + if (x && tex_has_glyph_option(p, glyph_option_apply_x_offset)) { + w += x; /* or after expansion? needs testing */ + } + w -= (glyph_left(p) + glyph_right(p)); + if (glyph_expansion(p)) { + w = w + tex_ext_xn_over_d(w, 1000000 + glyph_expansion(p), 1000000); + } + return w; +} + +scaled tex_glyph_height(halfword p) +{ + scaled h = tex_char_height_from_glyph(p) + glyph_raise(p); + scaled y = glyph_y_offset(p); + if (y && tex_has_glyph_option(p, glyph_option_apply_y_offset)) { + h += y; + } + return h < 0 ? 0 : h; +} + +scaled tex_glyph_depth(halfword p) /* not used */ +{ + scaled d = tex_char_depth_from_glyph(p) - glyph_raise(p); + scaled y = glyph_y_offset(p); + if (y && tex_has_glyph_option(p, glyph_option_apply_y_offset)) { + d -= y; + } + return d < 0 ? 0 : d; +} + +scaledwhd tex_glyph_dimensions(halfword p) +{ + scaledwhd whd = { 0, 0, 0 }; + scaled x = glyph_x_offset(p); + scaled y = glyph_y_offset(p); + whd.ht = tex_char_height_from_glyph(p) + glyph_raise(p); + whd.dp = tex_char_depth_from_glyph(p) - glyph_raise(p); + whd.wd = tex_char_width_from_glyph(p) - (glyph_left(p) + glyph_right(p)); + if (x && tex_has_glyph_option(p, glyph_option_apply_x_offset)) { + whd.wd += x; + } + if (y && tex_has_glyph_option(p, glyph_option_apply_y_offset)) { + whd.ht += y; + whd.dp -= y; + } + if (whd.ht < 0) { + whd.ht = 0; + } + if (whd.dp < 0) { + whd.dp = 0; + } + return whd; +} + +scaledwhd tex_glyph_dimensions_ex(halfword p) +{ + scaledwhd whd = { 0, 0, 0 }; + scaled x = glyph_x_offset(p); + scaled y = glyph_y_offset(p); + whd.ht = tex_char_height_from_glyph(p) + glyph_raise(p); + whd.dp = tex_char_depth_from_glyph(p) - glyph_raise(p); + whd.wd = tex_char_width_from_glyph(p) - (glyph_left(p) + glyph_right(p)); + if (x && tex_has_glyph_option(p, glyph_option_apply_x_offset)) { + whd.wd += x; + } + if (y && tex_has_glyph_option(p, glyph_option_apply_y_offset)) { + whd.ht += y; + whd.dp -= y; + } + if (whd.ht < 0) { + whd.ht = 0; + } + if (whd.dp < 0) { + whd.dp = 0; + } + if (whd.wd && glyph_expansion(p)) { + whd.wd = tex_ext_xn_over_d(whd.wd, 1000000 + glyph_expansion(p), 1000000); + } + return whd; +} + +scaled tex_glyph_total(halfword p) +{ + scaled ht = tex_char_height_from_glyph(p); + scaled dp = tex_char_depth_from_glyph(p); + if (ht < 0) { + ht = 0; + } + if (dp < 0) { + dp = 0; + } + return ht + dp; +} + +int tex_glyph_has_dimensions(halfword p) +{ + scaled offset = glyph_x_offset(p); + scaled amount = tex_char_width_from_glyph(p); + if (offset && tex_has_glyph_option(p, glyph_option_apply_x_offset)) { + amount += offset; + } + amount -= (glyph_left(p) + glyph_right(p)); + if (amount) { + return 1; + } else { + amount = tex_char_total_from_glyph(p); + /* here offset adn raise just moves */ + return amount != 0; + } +} + +halfword tex_kern_dimension(halfword p) +{ + return kern_amount(p); +} + +halfword tex_kern_dimension_ex(halfword p) +{ + halfword k = kern_amount(p); + if (k && kern_expansion(p)) { + k = tex_ext_xn_over_d(k, 1000000 + kern_expansion(p), 1000000); + } + return k; +} + +scaledwhd tex_pack_dimensions(halfword p) +{ + scaledwhd whd = { 0, 0, 0 }; + whd.ht = box_height(p); + whd.dp = box_depth(p); + whd.wd = box_width(p); + return whd; +} + +/*tex + + A |disc_node|, which occurs only in horizontal lists, specifies a \quote {discretionary} + line break. If such a break occurs at node |p|, the text that starts at |pre_break(p)| will + precede the break, the text that starts at |post_break(p)| will follow the break, and text + that appears in |no_break(p)| nodes will be ignored. For example, an ordinary discretionary + hyphen, indicated by |\-|, yields a |disc_node| with |pre_break| pointing to a |char_node| + containing a hyphen, |post_break = null|, and |no_break=null|. + + If |subtype(p) = automatic_disc|, the |ex_hyphen_penalty| will be charged for this break. + Otherwise the |hyphen_penalty| will be charged. The texts will actually be substituted into + the list by the line-breaking algorithm if it decides to make the break, and the discretionary + node will disappear at that time; thus, the output routine sees only discretionaries that were + not chosen. + +*/ + +halfword tex_new_disc_node(quarterword s) +{ + halfword p = tex_new_node(disc_node, s); + disc_penalty(p) = hyphen_penalty_par; + disc_class(p) = unset_disc_class; + return p; +} + +/*tex + + The program above includes a bunch of \quote {hooks} that allow further capabilities to be + added without upsetting \TEX's basic structure. Most of these hooks are concerned with \quote + {whatsit} nodes, which are intended to be used for special purposes; whenever a new extension + to \TEX\ involves a new kind of whatsit node, a corresponding change needs to be made to the + routines below that deal with such nodes, but it will usually be unnecessary to make many + changes to the other parts of this program. + + In order to demonstrate how extensions can be made, we shall treat |\write|, |\openout|, + |\closeout|, |\immediate|, and |\special| as if they were extensions. These commands are + actually primitives of \TEX, and they should appear in all implementations of the system; but + let's try to imagine that they aren't. Then the program below illustrates how a person could + add them. + + Sometimes, of course, an extension will require changes to \TEX\ itself; no system of hooks + could be complete enough for all conceivable extensions. The features associated with |\write| + are almost all confined to the following paragraphs, but there are small parts of the |print_ln| + and |print_char| procedures that were introduced specifically to |\write| characters. + Furthermore one of the token lists recognized by the scanner is a |write_text|; and there are a + few other miscellaneous places where we have already provided for some aspect of |\write|. The + goal of a \TeX\ extender should be to minimize alterations to the standard parts of the program, + and to avoid them completely if possible. He or she should also be quite sure that there's no + easy way to accomplish the desired goals with the standard features that \TEX\ already has. + \quote {Think thrice before extending}, because that may save a lot of work, and it will also + keep incompatible extensions of \TEX\ from proliferating. + + First let's consider the format of whatsit nodes that are used to represent the data associated + with |\write| and its relatives. Recall that a whatsit has |type=whatsit_node|, and the |subtype| + is supposed to distinguish different kinds of whatsits. Each node occupies two or more words; + the exact number is immaterial, as long as it is readily determined from the |subtype| or other + data. + + We shall introduce five |subtype| values here, corresponding to the control sequences |\openout|, + |\write|, |\closeout|, and |\special|. The second word of I/O whatsits has a |write_stream| + field that identifies the write-stream number (0 to 15, or 16 for out-of-range and positive, or + 17 for out-of-range and negative). In the case of |\write| and |\special|, there is also a field + that points to the reference count of a token list that should be sent. In the case of |\openout|, + we need three words and three auxiliary subfields to hold the string numbers for name, area, and + extension. + + Extensions might introduce new command codes; but it's best to use |extension| with a modifier, + whenever possible, so that |main_control| stays the same. + + The sixteen possible |\write| streams are represented by the |write_file| array. The |j|th file + is open if and only if |write_open[j]=true|. The last two streams are special; |write_open[16]| + represents a stream number greater than 15, while |write_open[17]| represents a negative stream + number, and both of these variables are always |false|. + + Writing to files is delegated to \LUA, so we have no write channels. + + To write a token list, we must run it through \TEX's scanner, expanding macros and |\the| and + |\number|, etc. This might cause runaways, if a delimited macro parameter isn't matched, and + runaways would be extremely confusing since we are calling on \TEX's scanner in the middle of + a |\shipout| command. Therefore we will put a dummy control sequence as a \quote {stopper}, + right after the token list. This control sequence is artificially defined to be |\outer|. + + The presence of |\immediate| causes the |do_extension| procedure to descend to one level of + recursion. Nothing happens unless |\immediate| is followed by |\openout|, |\write|, or + |\closeout|. + + Here is a subroutine that creates a whatsit node having a given |subtype| and a given number + of words. It initializes only the first word of the whatsit, and appends it to the current + list. + + 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 \quote {\TEX nicolor} extension to specify different colors of + ink, and the whatsit node might contain the desired parameters. + + The present implementation of \TEX\ treats the features associated with |\write| and |\special| + as if they were extensions, in order to illustrate how such routines might be coded. We shall + defer further discussion of extensions until the end of this program. + + However, in \LUAMETATEX\ we only have a generic whatsit node, a small one that can be used to + implement whatever you like, using \LUA. So, all we have here is the above comment as + guideline for that. + + \TEX\ makes use of the fact that |hlist_node|, |vlist_node|, |rule_node|, |insert_node|, + |mark_node|, |adjust_node|, |disc_node|, |whatsit_node|, and |math_node| are at the low end of + the type codes, by permitting a break at glue in a list if and only if the |type| of the + previous node is less than |math_node|. Furthermore, a node is discarded after a break if its + type is |math_node| or~more. + + A |glue_node| represents glue in a list. However, it is really only a pointer to a separate + glue specification, since \TEX\ makes use of the fact that many essentially identical nodes of + glue are usually present. If |p| points to a |glue_node|, |glue_ptr(p)| points to another packet + of words that specify the stretch and shrink components, etc. + + Glue nodes also serve to represent leaders; the |subtype| is used to distinguish between + ordinary glue (which is called |normal|) and the three kinds of leaders (which are called + |a_leaders|, |c_leaders|, and |x_leaders|). The |leader_ptr| field points to a rule node or to + a box node containing the leaders; it is set to |null| in ordinary glue nodes. + + Many kinds of glue are computed from \TEX's skip parameters, and it is helpful to know which + parameter has led to a particular glue node. Therefore the |subtype| is set to indicate the + source of glue, whenever it originated as a parameter. We will be defining symbolic names for + the parameter numbers later (e.g., |line_skip_code = 0|, |baseline_skip_code = 1|, etc.); it + suffices for now to say that the |subtype| of parametric glue will be the same as the parameter + number, plus~one. + + In math formulas there are two more possibilities for the |subtype| in a glue node: |mu_glue| + denotes an |\mskip| (where the units are scaled |mu| instead of scaled |pt|); and + |cond_math_glue| denotes the |\nonscript| feature that cancels the glue node immediately + following if it appears in a subscript. + + A glue specification has a halfword reference count in its first word, representing |null| + plus the number of glue nodes that point to it (less one). Note that the reference count + appears in the same position as the |link| field in list nodes; this is the field that is + initialized to |null| when a node is allocated, and it is also the field that is flagged by + |empty_flag| in empty nodes. + + Glue specifications also contain three |scaled| fields, for the |width|, |stretch|, and + |shrink| dimensions. Finally, there are two one-byte fields called |stretch_order| and + |shrink_order|; these contain the orders of infinity (|normal|, |sfi|, |fil|, |fill|, or + |filll|) corresponding to the stretch and shrink values. + + Here is a function that returns a pointer to a copy of a glue spec. The reference count in the + copy is |null|, because there is assumed to be exactly one reference to the new specification. + +*/ + +halfword tex_new_glue_spec_node(halfword q) +{ + if (q) { + switch (node_type(q)) { + case glue_spec_node: + return tex_copy_node(q); + case glue_node: + { + halfword p = tex_copy_node(zero_glue); + glue_amount(p) = glue_amount(q); + glue_stretch(p) = glue_stretch(q); + glue_shrink(p) = glue_shrink(q); + glue_stretch_order(p) = glue_stretch_order(q); + glue_shrink_order(p) = glue_shrink_order(q); + return p; + } + } + } + return tex_copy_node(zero_glue); +} + +/*tex + + And here's a function that creates a glue node for a given parameter identified by its code + number; for example, |new_param_glue(line_skip_code)| returns a pointer to a glue node for the + current |\lineskip|. + +*/ + +halfword tex_new_param_glue_node(quarterword p, quarterword s) +{ + halfword n = tex_new_node(glue_node, s); + halfword g = glue_parameter(p); + if (g) { + memcpy((void *) (lmt_node_memory_state.nodes + n + 2), (void *) (lmt_node_memory_state.nodes + g + 2), (glue_spec_size - 2) * (sizeof(memoryword))); + } + return n; +} + +/*tex + + Glue nodes that are more or less anonymous are created by |new_glue|, whose argument points to + a glue specification. + +*/ + +halfword tex_new_glue_node(halfword q, quarterword s) +{ + halfword p = tex_new_node(glue_node, s); + memcpy((void *) (lmt_node_memory_state.nodes + p + 2), (void *) (lmt_node_memory_state.nodes + q + 2), (glue_spec_size - 2) * (sizeof(memoryword))); + return p; +} + +/*tex + + Still another subroutine is needed: |new_skip_param|. 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. + + Remark: as we have copies we don't need this one can use |new_param_glue| instead. + +*/ + +/*tex + + A |kern_node| has a |width| field to specify a (normally negative) amount of spacing. This + spacing correction appears in horizontal lists between letters like A and V when the font + designer said that it looks better to move them closer together or further apart. A kern node + can also appear in a vertical list, when its |width| denotes additional spacing in the vertical + direction. The |subtype| is either |normal| (for kerns inserted from font information or math + mode calculations) or |explicit| (for kerns inserted from |\kern| and |\/| commands) or + |acc_kern| (for kerns inserted from non-math accents) or |mu_glue| (for kerns inserted from + |\mkern| specifications in math formulas). + + The |new_kern| function creates a (font) kern node having a given width. + +*/ + +halfword tex_new_kern_node(scaled w, quarterword s) +{ + halfword p = tex_new_node(kern_node, s); + kern_amount(p) = w; + return p; +} + +/*tex + + A |penalty_node| specifies the penalty associated with line or page breaking, in its |penalty| + field. This field is a fullword integer, but the full range of integer values is not used: + Any penalty |>=10000| is treated as infinity, and no break will be allowed for such high values. + Similarly, any penalty |<= -10000| is treated as negative infinity, and a break will be forced. + + Anyone who has been reading the last few sections of the program will be able to guess what + comes next. + +*/ + +halfword tex_new_penalty_node(halfword m, quarterword s) +{ + halfword p = tex_new_node(penalty_node, s); + penalty_amount(p) = m; + return p; +} + +/*tex + + You might think that we have introduced enough node types by now. Well, almost, but there is + one more: An |unset_node| has nearly the same format as an |hlist_node| or |vlist_node|; it is + used for entries in |\halign| or |\valign| that are not yet in their final form, since the box + dimensions are their \quote {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. 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. + + Some day we might store the current paragraph properties in this node. Actually, we already + store the interline and broken penalties. But it then also demands adaptation if the functions + that deal with breaking (we can just pass the local par node) and related specification node + cleanups. We could either snapshot parameters before a group ends, or we can add a lots of + |\local...| parameters. + +*/ + +halfword tex_new_par_node(quarterword mode) +{ + int callback_id, top; + halfword p = tex_new_node(par_node, mode); + /* */ + tex_set_local_interline_penalty(p, local_interline_penalty_par); + tex_set_local_broken_penalty(p, local_broken_penalty_par); + par_dir(p) = par_direction_par; + /* */ + tex_add_local_boxes(p); + if (mode != local_box_par_subtype) { + /*tex Callback with node passed. Todo: move to luanode with the rest of callbacks. */ + callback_id = lmt_callback_defined(insert_par_callback); + if (callback_id > 0) { + lua_State *L = lmt_lua_state.lua_instance; + if (lmt_callback_okay(L, callback_id, &top)) { + int i; + lmt_node_list_to_lua(L, p); + lmt_push_par_mode(L, mode); + i = lmt_callback_call(L, 2, 0 ,top); + if (i) { + lmt_callback_error(L, top, i); + } else { + lmt_callback_wrapup(L, top); + } + } + } + } + return p; +} + +static halfword tex_aux_internal_to_par_code(halfword cmd, halfword index) { + switch (cmd) { + case internal_int_cmd: + switch (index) { + case hang_after_code : return par_hang_after_code; + case adjust_spacing_code : return par_adjust_spacing_code; + case protrude_chars_code : return par_protrude_chars_code; + case pre_tolerance_code : return par_pre_tolerance_code; + case tolerance_code : return par_tolerance_code; + case looseness_code : return par_looseness_code; + case last_line_fit_code : return par_last_line_fit_code; + case line_penalty_code : return par_line_penalty_code; + case inter_line_penalty_code : return par_inter_line_penalty_code; + case club_penalty_code : return par_club_penalty_code; + case widow_penalty_code : return par_widow_penalty_code; + case display_widow_penalty_code : return par_display_widow_penalty_code; + case orphan_penalty_code : return par_orphan_penalty_code; + case broken_penalty_code : return par_broken_penalty_code; + case adj_demerits_code : return par_adj_demerits_code; + case double_hyphen_demerits_code : return par_double_hyphen_demerits_code; + case final_hyphen_demerits_code : return par_final_hyphen_demerits_code; + case shaping_penalties_mode_code : return par_shaping_penalties_mode_code; + case shaping_penalty_code : return par_shaping_penalty_code; + } + case internal_dimen_cmd: + switch (index) { + case hsize_code : return par_hsize_code; + case hang_indent_code : return par_hang_indent_code; + case par_indent_code : return par_par_indent_code; + case emergency_stretch_code : return par_emergency_stretch_code; + case line_skip_limit_code : return par_line_skip_limit_code; + } + case internal_glue_cmd: + switch (index) { + case left_skip_code : return par_left_skip_code; + case right_skip_code : return par_right_skip_code; + case par_fill_left_skip_code : return par_par_fill_left_skip_code; + case par_fill_right_skip_code : return par_par_fill_right_skip_code; + case par_init_left_skip_code : return par_par_init_left_skip_code; + case par_init_right_skip_code : return par_par_init_right_skip_code; + case baseline_skip_code : return par_baseline_skip_code; + case line_skip_code : return par_line_skip_code; + } + case specification_reference_cmd: + switch (index) { + case par_shape_code : return par_par_shape_code; + case inter_line_penalties_code : return par_inter_line_penalties_code; + case club_penalties_code : return par_club_penalties_code; + case widow_penalties_code : return par_widow_penalties_code; + case display_widow_penalties_code: return par_display_widow_penalties_code; + case orphan_penalties_code : return par_orphan_penalties_code; + } + } + return -1; +} + +void tex_update_par_par(halfword cmd, halfword index) +{ + halfword code = tex_aux_internal_to_par_code(cmd, index); + if (code >= 0) { + halfword par = tex_find_par_par(cur_list.head); + if (par) { + tex_snapshot_par(par, code); + } + } +} + +halfword tex_get_par_par(halfword p, halfword what) +{ + int set = tex_par_state_is_set(p, what); + switch (what) { + case par_par_shape_code: return set ? par_par_shape(p) : par_shape_par; + case par_inter_line_penalties_code: return set ? par_inter_line_penalties(p) : inter_line_penalties_par; + case par_club_penalties_code: return set ? par_club_penalties(p) : club_penalties_par; + case par_widow_penalties_code: return set ? par_widow_penalties(p) : widow_penalties_par; + case par_display_widow_penalties_code: return set ? par_display_widow_penalties(p) : display_widow_penalties_par; + case par_orphan_penalties_code: return set ? par_orphan_penalties(p) : orphan_penalties_par; + case par_hang_indent_code: return set ? par_hang_indent(p) : hang_indent_par; + case par_hang_after_code: return set ? par_hang_after(p) : hang_after_par; + case par_hsize_code: return set ? par_hsize(p) : hsize_par; + case par_left_skip_code: return set ? par_left_skip(p) : left_skip_par; + case par_right_skip_code: return set ? par_right_skip(p) : right_skip_par; + case par_last_line_fit_code: return set ? par_last_line_fit(p) : last_line_fit_par; + case par_pre_tolerance_code: return set ? par_pre_tolerance(p) : pre_tolerance_par; + case par_tolerance_code: return set ? par_tolerance(p) : tolerance_par; + case par_looseness_code: return set ? par_looseness(p) : looseness_par; + case par_adjust_spacing_code: return set ? par_adjust_spacing(p) : adjust_spacing_par; + case par_adj_demerits_code: return set ? par_adj_demerits(p) : adj_demerits_par; + case par_protrude_chars_code: return set ? par_protrude_chars(p) : protrude_chars_par; + case par_line_penalty_code: return set ? par_line_penalty(p) : line_penalty_par; + case par_double_hyphen_demerits_code: return set ? par_double_hyphen_demerits(p) : double_hyphen_demerits_par; + case par_final_hyphen_demerits_code: return set ? par_final_hyphen_demerits(p) : final_hyphen_demerits_par; + case par_inter_line_penalty_code: return set ? par_inter_line_penalty(p) : inter_line_penalty_par; + case par_club_penalty_code: return set ? par_club_penalty(p) : club_penalty_par; + case par_widow_penalty_code: return set ? par_widow_penalty(p) : widow_penalty_par; + case par_display_widow_penalty_code: return set ? par_display_widow_penalty(p) : display_widow_penalty_par; + case par_orphan_penalty_code: return set ? par_orphan_penalty(p) : orphan_penalty_par; + case par_broken_penalty_code: return set ? par_broken_penalty(p) : broken_penalty_par; + case par_emergency_stretch_code: return set ? par_emergency_stretch(p) : emergency_stretch_par; + case par_par_indent_code: return set ? par_par_indent(p) : par_indent_par; + case par_par_fill_left_skip_code: return set ? par_par_fill_left_skip(p) : par_fill_left_skip_par; + case par_par_fill_right_skip_code: return set ? par_par_fill_right_skip(p) : par_fill_right_skip_par; + case par_par_init_left_skip_code: return set ? par_par_init_left_skip(p) : par_init_left_skip_par; + case par_par_init_right_skip_code: return set ? par_par_init_right_skip(p) : par_init_right_skip_par; + case par_baseline_skip_code: return set ? par_baseline_skip(p) : baseline_skip_par; + case par_line_skip_code: return set ? par_line_skip(p) : line_skip_par; + case par_line_skip_limit_code: return set ? par_line_skip_limit(p) : line_skip_limit_par; + case par_adjust_spacing_step_code: return set ? par_adjust_spacing_step(p) : adjust_spacing_step_par; + case par_adjust_spacing_shrink_code: return set ? par_adjust_spacing_shrink(p) : adjust_spacing_shrink_par; + case par_adjust_spacing_stretch_code: return set ? par_adjust_spacing_stretch(p) : adjust_spacing_stretch_par; + case par_hyphenation_mode_code: return set ? par_hyphenation_mode(p) : hyphenation_mode_par; + case par_shaping_penalties_mode_code: return set ? par_shaping_penalties_mode(p) : shaping_penalties_mode_par; + case par_shaping_penalty_code: return set ? par_shaping_penalty(p) : shaping_penalty_par; + } + return null; +} + +void tex_set_par_par(halfword p, halfword what, halfword v, int force) +{ + if (force || tex_par_state_is_set(p, what)) { + switch (what) { + case par_hsize_code: + par_hsize(p) = v; + break; + case par_left_skip_code: + if (par_left_skip(p)) { + tex_flush_node(par_left_skip(p)); + } + par_left_skip(p) = v ? tex_copy_node(v) : null; + break; + case par_right_skip_code: + if (par_right_skip(p)) { + tex_flush_node(par_right_skip(p)); + } + par_right_skip(p) = v ? tex_copy_node(v) : null; + break; + case par_hang_indent_code: + par_hang_indent(p) = v; + break; + case par_hang_after_code: + par_hang_after(p) = v; + break; + case par_par_indent_code: + par_par_indent(p) = v; + break; + case par_par_fill_left_skip_code: + if (par_par_fill_left_skip(p)) { + tex_flush_node(par_par_fill_left_skip(p)); + } + par_par_fill_left_skip(p) = v ? tex_copy_node(v) : null; + break; + case par_par_fill_right_skip_code: + if (par_par_fill_right_skip(p)) { + tex_flush_node(par_par_fill_right_skip(p)); + } + par_par_fill_right_skip(p) = v ? tex_copy_node(v) : null; + break; + case par_par_init_left_skip_code: + if (par_par_init_left_skip(p)) { + tex_flush_node(par_par_init_left_skip(p)); + } + par_par_init_left_skip(p) = v ? tex_copy_node(v) : null; + break; + case par_par_init_right_skip_code: + if (par_par_init_right_skip(p)) { + tex_flush_node(par_par_init_right_skip(p)); + } + par_par_init_right_skip(p) = v ? tex_copy_node(v) : null; + break; + case par_adjust_spacing_code: + par_adjust_spacing(p) = v; + break; + case par_protrude_chars_code: + par_protrude_chars(p) = v; + break; + case par_pre_tolerance_code: + par_pre_tolerance(p) = v; + break; + case par_tolerance_code: + par_tolerance(p) = v; + break; + case par_emergency_stretch_code: + par_emergency_stretch(p) = v; + break; + case par_looseness_code: + par_looseness(p) = v; + break; + case par_last_line_fit_code: + par_last_line_fit(p) = v; + break; + case par_line_penalty_code: + par_line_penalty(p) = v; + break; + case par_inter_line_penalty_code: + par_inter_line_penalty(p) = v; + break; + case par_club_penalty_code: + par_club_penalty(p) = v; + break; + case par_widow_penalty_code: + par_widow_penalty(p) = v; + break; + case par_display_widow_penalty_code: + par_display_widow_penalty(p) = v; + break; + case par_orphan_penalty_code: + par_orphan_penalty(p) = v; + break; + case par_broken_penalty_code: + par_broken_penalty(p) = v; + break; + case par_adj_demerits_code: + par_adj_demerits(p) = v; + break; + case par_double_hyphen_demerits_code: + par_double_hyphen_demerits(p) = v; + break; + case par_final_hyphen_demerits_code: + par_final_hyphen_demerits(p) = v; + break; + case par_par_shape_code: + if (par_par_shape(p)) { + tex_flush_node(par_par_shape(p)); + } + par_par_shape(p) = v ? tex_copy_node(v) : null; + break; + case par_inter_line_penalties_code: + if (par_inter_line_penalties(p)) { + tex_flush_node(par_inter_line_penalties(p)); + } + par_inter_line_penalties(p) = v ? tex_copy_node(v) : null; + break; + case par_club_penalties_code: + if (par_club_penalties(p)) { + tex_flush_node(par_club_penalties(p)); + } + par_club_penalties(p) = v ? tex_copy_node(v) : null; + break; + case par_widow_penalties_code: + if (par_widow_penalties(p)) { + tex_flush_node(par_widow_penalties(p)); + } + par_widow_penalties(p) = v ? tex_copy_node(v) : null; + break; + case par_display_widow_penalties_code: + if (par_display_widow_penalties(p)) { + tex_flush_node(par_display_widow_penalties(p)); + } + par_display_widow_penalties(p) = v ? tex_copy_node(v) : null; + break; + case par_orphan_penalties_code: + if (par_orphan_penalties(p)) { + tex_flush_node(par_orphan_penalties(p)); + } + par_orphan_penalties(p) = v ? tex_copy_node(v) : null; + break; + case par_baseline_skip_code: + if (par_baseline_skip(p)) { + tex_flush_node(par_baseline_skip(p)); + } + par_baseline_skip(p) = v ? tex_copy_node(v) : null; + break; + case par_line_skip_code: + if (par_line_skip(p)) { + tex_flush_node(par_line_skip(p)); + } + par_line_skip(p) = v ? tex_copy_node(v) : null; + break; + case par_line_skip_limit_code: + par_line_skip_limit(p) = v; + break; + case par_adjust_spacing_step_code: + par_adjust_spacing_step(p) = v; + break; + case par_adjust_spacing_shrink_code: + par_adjust_spacing_shrink(p) = v; + break; + case par_adjust_spacing_stretch_code: + par_adjust_spacing_stretch(p) = v; + break; + case par_hyphenation_mode_code: + par_hyphenation_mode(p) = v; + break; + case par_shaping_penalties_mode_code: + par_shaping_penalties_mode(p) = v; + break; + case par_shaping_penalty_code: + par_shaping_penalty(p) = v; + break; + } + tex_set_par_state(p, what); + } +} + +void tex_snapshot_par(halfword p, halfword what) +{ + if (p && lmt_main_state.run_state != initializing_state) { + int unset = 0; + if (what) { + if (what < 0) { + unset = 1; + what = -what; + } + if (what > par_all_category) { + what = par_all_category; + } + } else { + unset = 1; + what = par_all_category; + } + if (tex_par_to_be_set(what, par_hsize_code)) { tex_set_par_par(p, par_hsize_code, unset ? null : hsize_par, 1); } + if (tex_par_to_be_set(what, par_left_skip_code)) { tex_set_par_par(p, par_left_skip_code, unset ? null : left_skip_par, 1); } + if (tex_par_to_be_set(what, par_right_skip_code)) { tex_set_par_par(p, par_right_skip_code, unset ? null : right_skip_par, 1); } + if (tex_par_to_be_set(what, par_hang_indent_code)) { tex_set_par_par(p, par_hang_indent_code, unset ? null : hang_indent_par, 1); } + if (tex_par_to_be_set(what, par_hang_after_code)) { tex_set_par_par(p, par_hang_after_code, unset ? null : hang_after_par, 1); } + if (tex_par_to_be_set(what, par_par_indent_code)) { tex_set_par_par(p, par_par_indent_code, unset ? null : par_indent_par, 1); } + if (tex_par_to_be_set(what, par_par_fill_left_skip_code)) { tex_set_par_par(p, par_par_fill_left_skip_code, unset ? null : par_fill_left_skip_par, 1); } + if (tex_par_to_be_set(what, par_par_fill_right_skip_code)) { tex_set_par_par(p, par_par_fill_right_skip_code, unset ? null : par_fill_right_skip_par, 1); } + if (tex_par_to_be_set(what, par_par_init_left_skip_code)) { tex_set_par_par(p, par_par_init_left_skip_code, unset ? null : par_init_left_skip_par, 1); } + if (tex_par_to_be_set(what, par_par_init_right_skip_code)) { tex_set_par_par(p, par_par_init_right_skip_code, unset ? null : par_init_right_skip_par, 1); } + if (tex_par_to_be_set(what, par_adjust_spacing_code)) { tex_set_par_par(p, par_adjust_spacing_code, unset ? null : adjust_spacing_par, 1); } + if (tex_par_to_be_set(what, par_protrude_chars_code)) { tex_set_par_par(p, par_protrude_chars_code, unset ? null : protrude_chars_par, 1); } + if (tex_par_to_be_set(what, par_pre_tolerance_code)) { tex_set_par_par(p, par_pre_tolerance_code, unset ? null : pre_tolerance_par, 1); } + if (tex_par_to_be_set(what, par_tolerance_code)) { tex_set_par_par(p, par_tolerance_code, unset ? null : tolerance_par, 1); } + if (tex_par_to_be_set(what, par_emergency_stretch_code)) { tex_set_par_par(p, par_emergency_stretch_code, unset ? null : emergency_stretch_par, 1); } + if (tex_par_to_be_set(what, par_looseness_code)) { tex_set_par_par(p, par_looseness_code, unset ? null : looseness_par, 1); } + if (tex_par_to_be_set(what, par_last_line_fit_code)) { tex_set_par_par(p, par_last_line_fit_code, unset ? null : last_line_fit_par, 1); } + if (tex_par_to_be_set(what, par_line_penalty_code)) { tex_set_par_par(p, par_line_penalty_code, unset ? null : line_penalty_par, 1); } + if (tex_par_to_be_set(what, par_inter_line_penalty_code)) { tex_set_par_par(p, par_inter_line_penalty_code, unset ? null : inter_line_penalty_par, 1); } + if (tex_par_to_be_set(what, par_club_penalty_code)) { tex_set_par_par(p, par_club_penalty_code, unset ? null : club_penalty_par, 1); } + if (tex_par_to_be_set(what, par_widow_penalty_code)) { tex_set_par_par(p, par_widow_penalty_code, unset ? null : widow_penalty_par, 1); } + if (tex_par_to_be_set(what, par_display_widow_penalty_code)) { tex_set_par_par(p, par_display_widow_penalty_code, unset ? null : display_widow_penalty_par, 1); } + if (tex_par_to_be_set(what, par_orphan_penalty_code)) { tex_set_par_par(p, par_orphan_penalty_code, unset ? null : orphan_penalty_par, 1); } + if (tex_par_to_be_set(what, par_broken_penalty_code)) { tex_set_par_par(p, par_broken_penalty_code, unset ? null : broken_penalty_par, 1); } + if (tex_par_to_be_set(what, par_adj_demerits_code)) { tex_set_par_par(p, par_adj_demerits_code, unset ? null : adj_demerits_par, 1); } + if (tex_par_to_be_set(what, par_double_hyphen_demerits_code)) { tex_set_par_par(p, par_double_hyphen_demerits_code, unset ? null : double_hyphen_demerits_par, 1); } + if (tex_par_to_be_set(what, par_final_hyphen_demerits_code)) { tex_set_par_par(p, par_final_hyphen_demerits_code, unset ? null : final_hyphen_demerits_par, 1); } + if (tex_par_to_be_set(what, par_par_shape_code)) { tex_set_par_par(p, par_par_shape_code, unset ? null : par_shape_par, 1); } + if (tex_par_to_be_set(what, par_inter_line_penalties_code)) { tex_set_par_par(p, par_inter_line_penalties_code, unset ? null : inter_line_penalties_par, 1); } + if (tex_par_to_be_set(what, par_club_penalties_code)) { tex_set_par_par(p, par_club_penalties_code, unset ? null : club_penalties_par, 1); } + if (tex_par_to_be_set(what, par_widow_penalties_code)) { tex_set_par_par(p, par_widow_penalties_code, unset ? null : widow_penalties_par, 1); } + if (tex_par_to_be_set(what, par_display_widow_penalties_code)) { tex_set_par_par(p, par_display_widow_penalties_code, unset ? null : display_widow_penalties_par, 1); } + if (tex_par_to_be_set(what, par_orphan_penalties_code)) { tex_set_par_par(p, par_orphan_penalties_code, unset ? null : orphan_penalties_par, 1); } + if (tex_par_to_be_set(what, par_baseline_skip_code)) { tex_set_par_par(p, par_baseline_skip_code, unset ? null : baseline_skip_par, 1); } + if (tex_par_to_be_set(what, par_line_skip_code)) { tex_set_par_par(p, par_line_skip_code, unset ? null : line_skip_par, 1); } + if (tex_par_to_be_set(what, par_line_skip_limit_code)) { tex_set_par_par(p, par_line_skip_limit_code, unset ? null : line_skip_limit_par, 1); } + if (tex_par_to_be_set(what, par_adjust_spacing_step_code)) { tex_set_par_par(p, par_adjust_spacing_step_code, unset ? null : adjust_spacing_step_par, 1); } + if (tex_par_to_be_set(what, par_adjust_spacing_shrink_code)) { tex_set_par_par(p, par_adjust_spacing_shrink_code, unset ? null : adjust_spacing_shrink_par, 1); } + if (tex_par_to_be_set(what, par_adjust_spacing_stretch_code)) { tex_set_par_par(p, par_adjust_spacing_stretch_code, unset ? null : adjust_spacing_stretch_par, 1); } + if (tex_par_to_be_set(what, par_hyphenation_mode_code)) { tex_set_par_par(p, par_hyphenation_mode_code, unset ? null : hyphenation_mode_par, 1); } + if (tex_par_to_be_set(what, par_shaping_penalties_mode_code)) { tex_set_par_par(p, par_shaping_penalties_mode_code, unset ? null : shaping_penalties_mode_par, 1); } + if (tex_par_to_be_set(what, par_shaping_penalty_code)) { tex_set_par_par(p, par_shaping_penalty_code, unset ? null : shaping_penalty_par, 1); } + + if (what == par_all_category) { + par_state(p) = unset ? 0 : par_all_category; + } else if (unset) { + par_state(p) &= ~(what | par_state(p)); + } else { + par_state(p) |= what; + } + } +} + +halfword tex_find_par_par(halfword head) +{ + if (head) { + if (node_type(head) == temp_node) { + head = node_next(head); + } + if (head && node_type(head) == par_node) { + return head; + } + } + return null; +} + +halfword tex_reversed_node_list(halfword list) +{ + if (list) { + halfword prev = list; + halfword last = list; + list = node_next(list); + if (list) { + while (1) { + halfword next = node_next(list); + tex_couple_nodes(list, prev); + if (node_type(list) == dir_node) { + node_subtype(list) = node_subtype(list) == cancel_dir_subtype ? normal_dir_subtype : cancel_dir_subtype ; + } + if (next) { + prev = list; + list = next; + } else { + node_next(last) = null; + node_prev(list) = null; + return list; + } + } + } + } + return list; +} + +/* */ + +halfword tex_new_specification_node(halfword n, quarterword s, halfword options) +{ + halfword p = tex_new_node(specification_node, s); + tex_new_specification_list(p, n, options); + return p; +} + +void tex_dispose_specification_nodes(void) { + if (par_shape_par) { tex_flush_node(par_shape_par); par_shape_par = null; } + if (inter_line_penalties_par) { tex_flush_node(inter_line_penalties_par); inter_line_penalties_par = null; } + if (club_penalties_par) { tex_flush_node(club_penalties_par); club_penalties_par = null; } + if (widow_penalties_par) { tex_flush_node(widow_penalties_par); widow_penalties_par = null; } + if (display_widow_penalties_par) { tex_flush_node(display_widow_penalties_par); display_widow_penalties_par = null; } + if (math_forward_penalties_par) { tex_flush_node(math_forward_penalties_par); math_forward_penalties_par = null; } + if (math_backward_penalties_par) { tex_flush_node(math_backward_penalties_par); math_backward_penalties_par = null; } + if (orphan_penalties_par) { tex_flush_node(orphan_penalties_par); orphan_penalties_par = null; } +} + +void tex_null_specification_list(halfword a) +{ + specification_pointer(a) = NULL; + specification_count(a) = 0; +} + +static void *tex_aux_allocate_specification(int n, size_t *s) +{ + void *p = NULL; + *s = n * sizeof(memoryword); + lmt_node_memory_state.extra_data.allocated += (int) *s; + lmt_node_memory_state.extra_data.ptr = lmt_node_memory_state.extra_data.allocated; + if (lmt_node_memory_state.extra_data.ptr > lmt_node_memory_state.extra_data.top) { + lmt_node_memory_state.extra_data.top = lmt_node_memory_state.extra_data.ptr; + } + p = lmt_memory_malloc(*s); + if (! p) { + tex_overflow_error("nodes", (int) *s); + } + return p; +} + +static void tex_aux_deallocate_specification(void *p, int n) +{ + size_t s = n * sizeof(memoryword); + lmt_node_memory_state.extra_data.allocated -= (int) s; + lmt_node_memory_state.extra_data.ptr = lmt_node_memory_state.extra_data.allocated; + lmt_memory_free(p); +} + +void tex_new_specification_list(halfword a, halfword n, halfword o) +{ + size_t s = 0; + specification_pointer(a) = tex_aux_allocate_specification(n, &s); + specification_count(a) = specification_pointer(a) ? n : 0; + specification_options(a) = o; +} + +void tex_dispose_specification_list(halfword a) +{ + if (specification_pointer(a)) { + tex_aux_deallocate_specification(specification_pointer(a), specification_count(a)); + specification_pointer(a) = NULL; + specification_count(a) = 0; + specification_options(a) = 0; + } +} + +void tex_copy_specification_list(halfword a, halfword b) { + if (specification_pointer(b)) { + size_t s = 0; + specification_pointer(a) = tex_aux_allocate_specification(specification_count(b), &s); + if (specification_pointer(a) && specification_pointer(b)) { + specification_count(a) = specification_count(b); + specification_options(a) = specification_options(b); + memcpy(specification_pointer(a), specification_pointer(b), s); + } else { + specification_count(a) = 0; + specification_options(a) = 0; + } + } +} + +void tex_shift_specification_list(halfword a, int n, int rotate) +{ + if (specification_pointer(a)) { + halfword c = specification_count(a); + if (rotate) { + if (n > 0 && c > 0 && n < c && c != n) { + size_t s = 0; + memoryword *b = tex_aux_allocate_specification(c, &s); + memoryword *p = specification_pointer(a); + halfword m = c - n; + s = m * sizeof(memoryword); + memcpy(b, p + n, s); + s = n * sizeof(memoryword); + memcpy(b + m, p, s); + tex_aux_deallocate_specification(specification_pointer(a), c); + specification_pointer(a) = b; + } + } else { + halfword o = 0; + halfword m = 0; + memoryword *b = NULL; + if (n > 0 && c > 0 && n < c) { + size_t s = 0; + memoryword *p = specification_pointer(a); + o = specification_options(a); + m = c - n; + b = tex_aux_allocate_specification(m, &s); + memcpy(b, p + n, s); + } + if (c > 0) { + tex_aux_deallocate_specification(specification_pointer(a), c); + } + specification_pointer(a) = b; + specification_count(a) = m; + specification_options(a) = o; + } + } +} + +/* */ + +void tex_set_disc_field(halfword target, halfword location, halfword source) +{ + switch (location) { + case pre_break_code: target = disc_pre_break(target); break; + case post_break_code: target = disc_post_break(target); break; + case no_break_code: target = disc_no_break(target); break; + } + node_prev(source) = null; /* don't expose this one! */ + if (source) { + node_head(target) = source ; + node_tail(target) = tex_tail_of_node_list(source); + } else { + node_head(target) = null; + node_tail(target) = null; + } +} + +void tex_check_disc_field(halfword n) +{ + halfword p = disc_pre_break_head(n); + disc_pre_break_tail(n) = p ? tex_tail_of_node_list(p) : null; + p = disc_post_break_head(n); + disc_post_break_tail(n) = p ? tex_tail_of_node_list(p) : null; + p = disc_no_break_head(n); + disc_no_break_tail(n) = p ? tex_tail_of_node_list(p) : null; +} + +void tex_set_discpart(halfword d, halfword h, halfword t, halfword code) +{ + switch (node_subtype(d)) { + case automatic_discretionary_code: + case mathematics_discretionary_code: + code = glyph_discpart_always; + break; + } + halfword c = h; + while (c) { + if (node_type(c) == glyph_node) { + set_glyph_discpart(c, code); + } + if (c == t) { + break; + } else { + c = node_next(c); + } + } +} + +halfword tex_flatten_discretionaries(halfword head, int *count, int nest) +{ + halfword current = head; + while (current) { + halfword next = node_next(current); + switch (node_type(current)) { + case disc_node: + { + halfword d = current; + halfword h = disc_no_break_head(d); + halfword t = disc_no_break_tail(d); + if (h) { + tex_set_discpart(current, h, t, glyph_discpart_replace); + tex_try_couple_nodes(t, next); + if (current == head) { + head = h; + } else { + tex_try_couple_nodes(node_prev(current), h); + } + disc_no_break_head(d) = null ; + } else if (current == head) { + head = next; + } else { + tex_try_couple_nodes(node_prev(current), next); + } + tex_flush_node(d); + if (count) { + *count += 1; + } + break; + } + case hlist_node: + case vlist_node: + if (nest) { + halfword list = box_list(current); + if (list) { + box_list(current) = tex_flatten_discretionaries(list, count, nest); + } + break; + } + } + current = next; + } + return head; +} + +void tex_flatten_leaders(halfword box, int *count) +{ + halfword head = box ? box_list(box) : null; + if (head) { + halfword current = head; + while (current) { + halfword next = node_next(current); + if (node_type(current) == glue_node && node_subtype(current) == u_leaders) { + halfword b = glue_leader_ptr(current); + if (b && (node_type(b) == hlist_node || node_type(b) == vlist_node)) { + halfword p = null; + halfword a = glue_amount(current); + double w = (double) a; + switch (box_glue_sign(box)) { + case stretching_glue_sign: + if (glue_stretch_order(current) == box_glue_order(box)) { + w += glue_stretch(current) * (double) box_glue_set(box); + } + break; + case shrinking_glue_sign: + if (glue_shrink_order(current) == box_glue_order(box)) { + w -= glue_shrink(current) * (double) box_glue_set(box); + } + break; + } + if (node_type(b) == hlist_node) { + p = tex_hpack(box_list(b), scaledround(w), packing_exactly, box_dir(b), holding_none_option); + } else { + p = tex_vpack(box_list(b), scaledround(w), packing_exactly, 0, box_dir(b), holding_none_option); + } + box_list(b) = box_list(p); + box_width(b) = box_width(p); + box_height(b) = box_height(p); + box_depth(b) = box_depth(p); + box_glue_order(b) = box_glue_order(p); + box_glue_sign(b) = box_glue_sign(p); + box_glue_set(b) = box_glue_set(p); + set_box_package_state(b, package_u_leader_set); + box_list(p) = null; + tex_flush_node(p); + glue_leader_ptr(current) = null; + tex_flush_node(current); + tex_try_couple_nodes(b, next); + if (current == head) { + box_list(box) = b; + } else { + tex_try_couple_nodes(node_prev(current), b); + } + if (count) { + *count += 1; + } + } + } + current = next; + } + } +} + +/*tex + This could of course be done in a \LUA\ loop but this is likely to be applied always so we + provide a helper, also because we need to check the font. Adding this sort of violates the + principle that we should this in \LUA\ instead but this time I permits myself to cheat. +*/ + +void tex_soften_hyphens(halfword head, int *found, int *replaced) +{ + halfword current = head; + while (current) { + switch (node_type(current)) { + case glyph_node: + { + if (glyph_character(current) == 0x2D) { + /* + We actually need a callback for this? Or we can have a nested loop + helper in the nodelib. + */ + ++(*found); + switch (glyph_discpart(current)) { + case glyph_discpart_unset: + /*tex Never seen by any disc handler. */ + set_glyph_discpart(current, glyph_discpart_always); + case glyph_discpart_always: + /*tex A hard coded - in the input. */ + break; + default : + if (tex_char_exists(glyph_font(current), 0xAD)) { + ++(*replaced); + glyph_character(current) = 0xAD; + } + break; + } + } + break; + } + case hlist_node: + case vlist_node: + { + halfword list = box_list(current); + if (list) { + tex_soften_hyphens(list, found, replaced); + } + break; + } + } + current = node_next(current); + } +} + +halfword tex_harden_spaces(halfword head, halfword tolerance, int* count) +{ + /* todo: take the context code */ + (void) tolerance; + (void) count; + return head; +} + +halfword tex_get_special_node_list(special_node_list_types list, halfword *tail) +{ + halfword h = null; + halfword t = null; + switch (list) { + case page_insert_list_type: + h = node_next(page_insert_head); + if (h == page_insert_head) { + h = null; + } + break; + case contribute_list_type: + h = node_next(contribute_head); + break; + case page_list_type: + h = node_next(page_head); + t = lmt_page_builder_state.page_tail; + break; + case temp_list_type: + h = node_next(temp_head); + break; + case hold_list_type: + h = node_next(hold_head); + break; + case post_adjust_list_type: + h = node_next(post_adjust_head); + t = lmt_packaging_state.post_adjust_tail; + break; + case pre_adjust_list_type: + h = node_next(pre_adjust_head); + t = lmt_packaging_state.pre_adjust_tail; + break; + case post_migrate_list_type: + h = node_next(post_migrate_head); + t = lmt_packaging_state.post_migrate_tail; + break; + case pre_migrate_list_type: + h = node_next(pre_migrate_head); + t = lmt_packaging_state.pre_migrate_tail; + break; + case align_list_type: + h = node_next(align_head); + break; + case page_discards_list_type: + h = lmt_packaging_state.page_discards_head; + break; + case split_discards_list_type: + h = lmt_packaging_state.split_discards_head; + break; + } + node_prev(h) = null; + if (tail) { + *tail = t ? t : (h ? tex_tail_of_node_list(h) : null); + } + return h; +}; + +int tex_is_special_node_list(halfword n, int *istail) +{ + if (istail) { + *istail = 0; + } + if (! n) { + return -1; + } else if (n == node_next(page_insert_head)) { + return page_insert_list_type; + } else if (n == node_next(contribute_head)) { + return contribute_list_type; + } else if (n == node_next(page_head) || n == lmt_page_builder_state.page_tail) { + if (istail && n == lmt_page_builder_state.page_tail) { + *istail = 0; + } + return page_list_type; + } else if (n == node_next(temp_head)) { + return temp_list_type; + } else if (n == node_next(hold_head)) { + return hold_list_type; + } else if (n == node_next(post_adjust_head) || n == lmt_packaging_state.post_adjust_tail) { + if (istail && n == lmt_packaging_state.post_adjust_tail) { + *istail = 0; + } + return post_adjust_list_type; + } else if (n == node_next(pre_adjust_head) || n == lmt_packaging_state.pre_adjust_tail) { + if (istail && n == lmt_packaging_state.pre_adjust_tail) { + *istail = 0; + } + return pre_adjust_list_type; + } else if (n == node_next(post_migrate_head) || n == lmt_packaging_state.post_migrate_tail) { + if (istail && n == lmt_packaging_state.post_migrate_tail) { + *istail = 0; + } + return post_migrate_list_type; + } else if (n == node_next(pre_migrate_head) || n == lmt_packaging_state.pre_migrate_tail) { + if (istail && n == lmt_packaging_state.pre_migrate_tail) { + *istail = 0; + } + return pre_migrate_list_type; + } else if (n == node_next(align_head)) { + return align_list_type; + } else if (n == lmt_packaging_state.page_discards_head) { + return page_discards_list_type; + } else if (n == lmt_packaging_state.split_discards_head) { + return split_discards_list_type; + // } else if (n == lmt_page_builder_state.best_page_break) { + // return 10000; + } else { + return -1; + } +}; + +void tex_set_special_node_list(special_node_list_types list, halfword head) +{ + switch (list) { + case page_insert_list_type: + /*tex This is a circular list where page_insert_head stays. */ + if (head) { + node_next(page_insert_head) = head; + node_next(tex_tail_of_node_list(head)) = page_insert_head; + } else { + node_next(page_insert_head) = page_insert_head; + } + break; + case contribute_list_type: + node_next(contribute_head) = head; + contribute_tail = head ? tex_tail_of_node_list(head) : contribute_head; + break; + case page_list_type: + node_next(page_head) = head; + lmt_page_builder_state.page_tail = head ? tex_tail_of_node_list(head) : page_head; + break; + case temp_list_type: + node_next(temp_head) = head; + break; + case hold_list_type: + node_next(hold_head) = head; + break; + case post_adjust_list_type: + node_next(post_adjust_head) = head; + lmt_packaging_state.post_adjust_tail = head ? tex_tail_of_node_list(head) : post_adjust_head; + break; + case pre_adjust_list_type: + node_next(pre_adjust_head) = head; + lmt_packaging_state.pre_adjust_tail = head ? tex_tail_of_node_list(head) : pre_adjust_head; + break; + case post_migrate_list_type: + node_next(post_migrate_head) = head; + lmt_packaging_state.post_migrate_tail = head ? tex_tail_of_node_list(head) : post_migrate_head; + break; + case pre_migrate_list_type: + node_next(pre_migrate_head) = head; + lmt_packaging_state.pre_migrate_tail = head ? tex_tail_of_node_list(head) : pre_migrate_head; + break; + case align_list_type: + node_next(align_head) = head; + break; + case page_discards_list_type: + lmt_packaging_state.page_discards_head = head; + break; + case split_discards_list_type: + lmt_packaging_state.split_discards_head = head; + break; + } +}; + +scaled tex_effective_glue(halfword parent, halfword glue) +{ + if (parent && glue) { + switch (node_type(glue)) { + case glue_node: + case glue_spec_node: + switch (node_type(parent)) { + case hlist_node: + case vlist_node: + { + double w = (double) glue_amount(glue); + switch (box_glue_sign(parent)) { + case stretching_glue_sign: + if (glue_stretch_order(glue) == box_glue_order(parent)) { + w += glue_stretch(glue) * (double) box_glue_set(parent); + } + break; + case shrinking_glue_sign: + if (glue_shrink_order(glue) == box_glue_order(parent)) { + w -= glue_shrink(glue) * (double) box_glue_set(parent); + } + break; + } + return (scaled) lmt_roundedfloat(w); + } + default: + return glue_amount(glue); + } + break; + } + } + return 0; +} |