1% texnodes.w 2% 3% Copyright 2006-2010 Taco Hoekwater <taco@@luatex.org> 4% 5% This file is part of LuaTeX. 6% 7% LuaTeX is free software; you can redistribute it and/or modify it under 8% the terms of the GNU General Public License as published by the Free 9% Software Foundation; either version 2 of the License, or (at your 10% option) any later version. 11% 12% LuaTeX is distributed in the hope that it will be useful, but WITHOUT 13% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 15% License for more details. 16% 17% You should have received a copy of the GNU General Public License along 18% with LuaTeX; if not, see <http://www.gnu.org/licenses/>. 19 20@ @c 21 22 23#include "ptexlib.h" 24#include "lua/luatex-api.h" 25 26@ @c 27#undef name 28 29#define noDEBUG 30#define DEBUG_OUT stdout 31 32#define adjust_pre subtype 33#define attribute(A) eqtb[attribute_base+(A)].cint 34 35#define uc_hyph int_par(uc_hyph_code) 36#define cur_lang int_par(cur_lang_code) 37#define left_hyphen_min int_par(left_hyphen_min_code) 38#define right_hyphen_min int_par(right_hyphen_min_code) 39 40#define text_direction int_par(text_direction_code) 41 42#define MAX_CHAIN_SIZE 13 43 44memory_word *volatile varmem = NULL; 45 46#ifndef NDEBUG 47char *varmem_sizes = NULL; 48#endif 49 50halfword var_mem_max = 0; 51halfword rover = 0; 52 53halfword free_chain[MAX_CHAIN_SIZE] = { null }; 54 55static int my_prealloc = 0; 56 57int fix_node_lists = 1; 58 59int free_error_seen = 0; 60int copy_error_seen = 0; 61 62halfword slow_get_node(int s); /* defined below */ 63int copy_error(halfword p); /* define below */ 64 65#define fake_node 100 66#define fake_node_size 2 67#define fake_node_name "fake" 68 69#define variable_node_size 2 70 71const char *node_fields_list[] = 72 { "attr", "width", "depth", "height", "dir", "shift", 73 "glue_order", "glue_sign", "glue_set", "head", NULL 74}; 75const char *node_fields_rule[] = 76 { "attr", "width", "depth", "height", "dir", NULL }; 77const char *node_fields_insert[] = 78 { "attr", "cost", "depth", "height", "spec", "head", NULL }; 79const char *node_fields_mark[] = { "attr", "class", "mark", NULL }; 80const char *node_fields_adjust[] = { "attr", "head", NULL }; 81const char *node_fields_disc[] = { "attr", "pre", "post", "replace", NULL }; 82const char *node_fields_math[] = { "attr", "surround", NULL }; 83const char *node_fields_glue[] = { "attr", "spec", "leader", NULL }; 84const char *node_fields_kern[] = { "attr", "kern", "expansion_factor", NULL }; 85const char *node_fields_penalty[] = { "attr", "penalty", NULL }; 86 87const char *node_fields_unset[] = 88 { "attr", "width", "depth", "height", "dir", "shrink", 89 "glue_order", "glue_sign", "stretch", "span", "head", NULL 90}; 91const char *node_fields_margin_kern[] = { "attr", "width", "glyph", NULL }; 92 93const char *node_fields_glyph[] = 94 { "attr", "char", "font", "lang", "left", "right", "uchyph", 95 "components", "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL 96}; 97const char *node_fields_style[] = { "attr", "style", NULL }; 98const char *node_fields_choice[] = 99 { "attr", "display", "text", "script", "scriptscript", NULL }; 100const char *node_fields_ord[] = { "attr", "nucleus", "sub", "sup", NULL }; 101const char *node_fields_op[] = { "attr", "nucleus", "sub", "sup", NULL }; 102const char *node_fields_bin[] = { "attr", "nucleus", "sub", "sup", NULL }; 103const char *node_fields_rel[] = { "attr", "nucleus", "sub", "sup", NULL }; 104const char *node_fields_open[] = { "attr", "nucleus", "sub", "sup", NULL }; 105const char *node_fields_close[] = { "attr", "nucleus", "sub", "sup", NULL }; 106const char *node_fields_punct[] = { "attr", "nucleus", "sub", "sup", NULL }; 107const char *node_fields_inner[] = { "attr", "nucleus", "sub", "sup", NULL }; 108const char *node_fields_under[] = { "attr", "nucleus", "sub", "sup", NULL }; 109const char *node_fields_over[] = { "attr", "nucleus", "sub", "sup", NULL }; 110const char *node_fields_vcenter[] = { "attr", "nucleus", "sub", "sup", NULL }; 111const char *node_fields_radical[] = 112 { "attr", "nucleus", "sub", "sup", "left", "degree", NULL }; 113const char *node_fields_fraction[] = 114 { "attr", "width", "num", "denom", "left", "right", NULL }; 115const char *node_fields_accent[] = 116 { "attr", "nucleus", "sub", "sup", "accent", "bot_accent", NULL }; 117const char *node_fields_fence[] = { "attr", "delim", NULL }; 118const char *node_fields_math_char[] = { "attr", "fam", "char", NULL }; 119const char *node_fields_sub_box[] = { "attr", "head", NULL }; 120const char *node_fields_sub_mlist[] = { "attr", "head", NULL }; 121const char *node_fields_math_text_char[] = { "attr", "fam", "char", NULL }; 122const char *node_fields_delim[] = 123 { "attr", "small_fam", "small_char", "large_fam", "large_char", NULL }; 124 125const char *node_fields_inserting[] = 126 { "height", "last_ins_ptr", "best_ins_ptr", NULL }; 127 128const char *node_fields_splitup[] = { "height", "last_ins_ptr", "best_ins_ptr", 129 "broken_ptr", "broken_ins", NULL 130}; 131 132const char *node_fields_action[] = { "action_type", "named_id", "action_id", 133 "file", "new_window", "data", "ref_count", NULL 134}; 135const char *node_fields_attribute[] = { "number", "value", NULL }; 136 137const char *node_fields_glue_spec[] = { "width", "stretch", "shrink", 138 "stretch_order", "shrink_order", "ref_count", "writable", NULL 139}; 140const char *node_fields_attribute_list[] = { NULL }; 141 142const char *node_fields_whatsit_open[] = 143 { "attr", "stream", "name", "area", "ext", NULL }; 144const char *node_fields_whatsit_write[] = { "attr", "stream", "data", NULL }; 145const char *node_fields_whatsit_close[] = { "attr", "stream", NULL }; 146const char *node_fields_whatsit_special[] = { "attr", "data", NULL }; 147 148const char *node_fields_whatsit_local_par[] = 149 { "attr", "pen_inter", "pen_broken", "dir", 150 "box_left", "box_left_width", "box_right", "box_right_width", NULL 151}; 152const char *node_fields_whatsit_dir[] = 153 { "attr", "dir", "level", "dvi_ptr", "dvi_h", NULL }; 154 155const char *node_fields_whatsit_pdf_literal[] = 156 { "attr", "mode", "data", NULL }; 157const char *node_fields_whatsit_pdf_refobj[] = { "attr", "objnum", NULL }; 158const char *node_fields_whatsit_pdf_refxform[] = 159 { "attr", "width", "depth", "height", "objnum", NULL }; 160const char *node_fields_whatsit_pdf_refximage[] = 161 { "attr", "width", "depth", "height", "transform", "index", NULL }; 162const char *node_fields_whatsit_pdf_annot[] = 163 { "attr", "width", "depth", "height", "objnum", "data", NULL }; 164const char *node_fields_whatsit_pdf_start_link[] = 165 { "attr", "width", "depth", "height", 166 "objnum", "link_attr", "action", NULL 167}; 168const char *node_fields_whatsit_pdf_end_link[] = { "attr", NULL }; 169 170const char *node_fields_whatsit_pdf_dest[] = 171 { "attr", "width", "depth", "height", 172 "named_id", "dest_id", "dest_type", "xyz_zoom", "objnum", NULL 173}; 174 175const char *node_fields_whatsit_pdf_thread[] = 176 { "attr", "width", "depth", "height", 177 "named_id", "thread_id", "thread_attr", NULL 178}; 179 180const char *node_fields_whatsit_pdf_start_thread[] = 181 { "attr", "width", "depth", "height", 182 "named_id", "thread_id", "thread_attr", NULL 183}; 184const char *node_fields_whatsit_pdf_end_thread[] = { "attr", NULL }; 185const char *node_fields_whatsit_pdf_save_pos[] = { "attr", NULL }; 186const char *node_fields_whatsit_late_lua[] = 187 { "attr", "reg", "data", "name", "string", NULL }; 188const char *node_fields_whatsit_close_lua[] = { "attr", "reg", NULL }; 189const char *node_fields_whatsit_pdf_colorstack[] = 190 { "attr", "stack", "cmd", "data", NULL }; 191const char *node_fields_whatsit_pdf_setmatrix[] = { "attr", "data", NULL }; 192const char *node_fields_whatsit_pdf_save[] = { "attr", NULL }; 193const char *node_fields_whatsit_pdf_restore[] = { "attr", NULL }; 194const char *node_fields_whatsit_cancel_boundary[] = { "attr", NULL }; 195const char *node_fields_whatsit_user_defined[] = 196 { "attr", "user_id", "type", "value", NULL }; 197 198node_info node_data[] = { 199 {hlist_node, box_node_size, node_fields_list, "hlist"}, 200 {vlist_node, box_node_size, node_fields_list, "vlist"}, 201 {rule_node, rule_node_size, node_fields_rule, "rule"}, 202 {ins_node, ins_node_size, node_fields_insert, "ins"}, 203 {mark_node, mark_node_size, node_fields_mark, "mark"}, 204 {adjust_node, adjust_node_size, node_fields_adjust, "adjust"}, 205 {fake_node, fake_node_size, NULL, fake_node_name}, /* don't touch this! */ 206 {disc_node, disc_node_size, node_fields_disc, "disc"}, 207 {whatsit_node, -1, NULL, "whatsit"}, 208 {math_node, math_node_size, node_fields_math, "math"}, 209 {glue_node, glue_node_size, node_fields_glue, "glue"}, 210 {kern_node, kern_node_size, node_fields_kern, "kern"}, 211 {penalty_node, penalty_node_size, node_fields_penalty, "penalty"}, 212 {unset_node, box_node_size, node_fields_unset, "unset"}, 213 {style_node, style_node_size, node_fields_style, "style"}, 214 {choice_node, style_node_size, node_fields_choice, "choice"}, 215 {simple_noad, noad_size, node_fields_ord, "noad"}, 216 {old_op_noad, noad_size, node_fields_op, "op"}, 217 {old_bin_noad, noad_size, node_fields_bin, "bin"}, 218 {old_rel_noad, noad_size, node_fields_rel, "rel"}, 219 {old_open_noad, noad_size, node_fields_open, "open"}, 220 {old_close_noad, noad_size, node_fields_close, "close"}, 221 {old_punct_noad, noad_size, node_fields_punct, "punct"}, 222 {old_inner_noad, noad_size, node_fields_inner, "inner"}, 223 {radical_noad, radical_noad_size, node_fields_radical, "radical"}, 224 {fraction_noad, fraction_noad_size, node_fields_fraction, "fraction"}, 225 {old_under_noad, noad_size, node_fields_under, "under"}, 226 {old_over_noad, noad_size, node_fields_over, "over"}, 227 {accent_noad, accent_noad_size, node_fields_accent, "accent"}, 228 {old_vcenter_noad, noad_size, node_fields_vcenter, "vcenter"}, 229 {fence_noad, fence_noad_size, node_fields_fence, "fence"}, 230 {math_char_node, math_kernel_node_size, node_fields_math_char, "math_char"}, 231 {sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box"}, 232 {sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist"}, 233 {math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char"}, 234 {delim_node, math_shield_node_size, node_fields_delim, "delim"}, 235 {margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern"}, 236 {glyph_node, glyph_node_size, node_fields_glyph, "glyph"}, 237 {align_record_node, box_node_size, NULL, "align_record"}, 238 {pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file"}, 239 {pseudo_line_node, variable_node_size, NULL, "pseudo_line"}, 240 {inserting_node, page_ins_node_size, node_fields_inserting, "page_insert"}, 241 {split_up_node, page_ins_node_size, node_fields_splitup, "split_insert"}, 242 {expr_node, expr_node_size, NULL, "expr_stack"}, 243 {nesting_node, nesting_node_size, NULL, "nested_list"}, 244 {span_node, span_node_size, NULL, "span"}, 245 {attribute_node, attribute_node_size, node_fields_attribute, "attribute"}, 246 {glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec"}, 247 {attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list"}, 248 {action_node, pdf_action_size, node_fields_action, "action"}, 249 {temp_node, temp_node_size, NULL, "temp"}, 250 {align_stack_node, align_stack_node_size, NULL, "align_stack"}, 251 {movement_node, movement_node_size, NULL, "movement_stack"}, 252 {if_node, if_node_size, NULL, "if_stack"}, 253 {unhyphenated_node, active_node_size, NULL, "unhyphenated"}, 254 {hyphenated_node, active_node_size, NULL, "hyphenated"}, 255 {delta_node, delta_node_size, NULL, "delta"}, 256 {passive_node, passive_node_size, NULL, "passive"}, 257 {shape_node, variable_node_size, NULL, "shape"}, 258 {-1, -1, NULL, NULL} 259}; 260 261#define last_normal_node shape_node 262 263node_info whatsit_node_data[] = { 264 {open_node, open_node_size, node_fields_whatsit_open, "open"}, 265 {write_node, write_node_size, node_fields_whatsit_write, "write"}, 266 {close_node, close_node_size, node_fields_whatsit_close, "close"}, 267 {special_node, special_node_size, node_fields_whatsit_special, "special"}, 268 {fake_node, fake_node_size, NULL, fake_node_name}, 269 {fake_node, fake_node_size, NULL, fake_node_name}, 270 {local_par_node, local_par_size, node_fields_whatsit_local_par, 271 "local_par"}, 272 {dir_node, dir_node_size, node_fields_whatsit_dir, "dir"}, 273 {pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, 274 "pdf_literal"}, 275 {fake_node, fake_node_size, NULL, fake_node_name}, 276 {pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, 277 "pdf_refobj"}, 278 {fake_node, fake_node_size, NULL, fake_node_name}, 279 {pdf_refxform_node, pdf_refxform_node_size, 280 node_fields_whatsit_pdf_refxform, "pdf_refxform"}, 281 {fake_node, fake_node_size, NULL, fake_node_name}, 282 {pdf_refximage_node, pdf_refximage_node_size, 283 node_fields_whatsit_pdf_refximage, "pdf_refximage"}, 284 {pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, 285 "pdf_annot"}, 286 {pdf_start_link_node, pdf_annot_node_size, 287 node_fields_whatsit_pdf_start_link, "pdf_start_link"}, 288 {pdf_end_link_node, pdf_end_link_node_size, 289 node_fields_whatsit_pdf_end_link, "pdf_end_link"}, 290 {fake_node, fake_node_size, NULL, fake_node_name}, 291 {pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, 292 "pdf_dest"}, 293 {pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, 294 "pdf_thread"}, 295 {pdf_start_thread_node, pdf_thread_node_size, 296 node_fields_whatsit_pdf_start_thread, "pdf_start_thread"}, 297 {pdf_end_thread_node, pdf_end_thread_node_size, 298 node_fields_whatsit_pdf_end_thread, "pdf_end_thread"}, 299 {pdf_save_pos_node, pdf_save_pos_node_size, 300 node_fields_whatsit_pdf_save_pos, "pdf_save_pos"}, 301 {pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data"}, 302 {pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data"}, 303 {fake_node, fake_node_size, NULL, fake_node_name}, 304 {fake_node, fake_node_size, NULL, fake_node_name}, 305 {fake_node, fake_node_size, NULL, fake_node_name}, 306 {fake_node, fake_node_size, NULL, fake_node_name}, 307 {fake_node, fake_node_size, NULL, fake_node_name}, 308 {fake_node, fake_node_size, NULL, fake_node_name}, 309 {fake_node, fake_node_size, NULL, fake_node_name}, 310 {fake_node, fake_node_size, NULL, fake_node_name}, 311 {fake_node, fake_node_size, NULL, fake_node_name}, 312 {late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, 313 "late_lua"}, 314 {close_lua_node, write_node_size, node_fields_whatsit_close_lua, 315 "close_lua"}, 316 {fake_node, fake_node_size, NULL, fake_node_name}, 317 {fake_node, fake_node_size, NULL, fake_node_name}, 318 {pdf_colorstack_node, pdf_colorstack_node_size, 319 node_fields_whatsit_pdf_colorstack, "pdf_colorstack"}, 320 {pdf_setmatrix_node, pdf_setmatrix_node_size, 321 node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix"}, 322 {pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, 323 "pdf_save"}, 324 {pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, 325 "pdf_restore"}, 326 {cancel_boundary_node, cancel_boundary_size, 327 node_fields_whatsit_cancel_boundary, "cancel_boundary"}, 328 {user_defined_node, user_defined_node_size, 329 node_fields_whatsit_user_defined, "user_defined"}, 330 {-1, -1, NULL, NULL} 331}; 332 333#define last_whatsit_node user_defined_node 334 335/* hh: experiment */ 336 337/* 338 339When we copy a node list, there are several possibilities: we do the same as a new node, 340we copy the entry to the table in properties (a reference), we do a deep copy of a table 341in the properties, we create a new table and give it the original one as a metatable. 342After some experiments (that also included timing) with these scenarios I decided that a 343deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable 344variant were both ok, although the second ons is slower. The most important aspect to keep 345in mind is that references to other nodes in properties no longer can be valid for that 346copy. We could use two tables (one unique and one shared) or metatables but that only 347complicates matters. 348 349When defining a new node, we could already allocate a table but it is rather easy to do 350that at the lua end e.g. using a metatable __index method. That way it is under macro 351package control. 352 353When deleting a node, we could keep the slot (e.g. setting it to false) but it could make 354memory consumption raise unneeded when we have temporary large node lists and after that 355only small lists. 356 357So, in the end this is what we ended up with. For the record, I also experimented with the 358following: 359 360- copy attributes to the properties so that we hav efast access at the lua end: in the end 361 the overhead is not compensated by speed and convenience, in fact, attributes are not 362 that slow when it comes to accessing them 363- a bitset in the node but again the gain compared to attributes is neglectable and it also 364 demands a pretty string agreement over what bit represents what, and this is unlikely to 365 succeed in the tex community (I could use it for font handling, which is cross package, 366 but decided that it doesn't pay off 367 368In case one wonders why properties make sense then, well, it is not so much speed that we 369gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and 370this mechanism makes sure that properties are cleaned up when a node is freed. Also, the 371advantage of a more or less global properties table is that we stay at the lua end. An 372alternative is to store a reference in the node itself but that is complicated by the fact 373that the register has some limitations (no numeric keys) and we also don't want to mess with 374it too much. 375 376*/ 377 378int lua_properties_level = 0 ; /* can be private */ 379int lua_properties_enabled = 0 ; 380int lua_properties_use_metatable = 0 ; 381 382/* We keep track of nesting so that we don't oveflow the stack, and, what is more important, 383don't keep resolving the registry index. */ 384 385#define lua_properties_push do { \ 386 if (lua_properties_enabled) { \ 387 lua_properties_level = lua_properties_level + 1 ; \ 388 if (lua_properties_level == 1) { \ 389 lua_rawgeti(Luas, LUA_REGISTRYINDEX, luaS_index(node_properties)); \ 390 lua_gettable(Luas, LUA_REGISTRYINDEX); \ 391 } \ 392 } \ 393} while(0) 394 395#define lua_properties_pop do { \ 396 if (lua_properties_enabled) { \ 397 if (lua_properties_level == 1) \ 398 lua_pop(Luas,1); \ 399 lua_properties_level = lua_properties_level - 1 ; \ 400 } \ 401} while(0) 402 403/* No setting is needed: */ 404 405#define lua_properties_set(target) do { \ 406} while(0) 407 408/* Resetting boils down to nilling. */ 409 410#define lua_properties_reset(target) do { \ 411 if (lua_properties_enabled) { \ 412 if (lua_properties_level == 0) { \ 413 lua_rawgeti(Luas, LUA_REGISTRYINDEX, luaS_index(node_properties)); \ 414 lua_gettable(Luas, LUA_REGISTRYINDEX); \ 415 lua_pushnil(Luas); \ 416 lua_rawseti(Luas,-2,target); \ 417 lua_pop(Luas,1); \ 418 } else { \ 419 lua_pushnil(Luas); \ 420 lua_rawseti(Luas,-2,target); \ 421 } \ 422 } \ 423} while(0) 424 425/* For a moment I considered supporting all kind of data types but in practice 426that makes no sense. So we stick to a cheap shallow copy with as option a 427metatable. Btw, a deep copy would look like this: 428 429static void copy_lua_table(lua_State* L, int index) { 430 lua_newtable(L); 431 lua_pushnil(L); 432 while(lua_next(L, index-1) != 0) { 433 lua_pushvalue(L, -2); 434 lua_insert(L, -2); 435 if (lua_type(L,-1)==LUA_TTABLE) 436 copy_lua_table(L,-1); 437 lua_settable(L, -4); 438 } 439 lua_pop(L,1); 440} 441 442#define lua_properties_copy(target, source) do { \ 443 if (lua_properties_enabled) { \ 444 lua_pushnumber(Luas,source); \ 445 lua_rawget(Luas,-2); \ 446 if (lua_type(Luas,-1)==LUA_TTABLE) { \ 447 copy_lua_table(Luas,-1); \ 448 lua_pushnumber(Luas,target); \ 449 lua_insert(Luas,-2); \ 450 lua_rawset(Luas,-3); \ 451 } else { \ 452 lua_pop(Luas,1); \ 453 } \ 454 } \ 455} while(0) 456 457*/ 458 459/* isn't there a faster way to metatable? */ 460 461#define lua_properties_copy(target,source) do { \ 462 if (lua_properties_enabled) { \ 463 if (lua_properties_level == 0) { \ 464 lua_rawgeti(Luas, LUA_REGISTRYINDEX, luaS_index(node_properties)); \ 465 lua_gettable(Luas, LUA_REGISTRYINDEX); \ 466 lua_rawgeti(Luas,-1,source); \ 467 if (lua_type(Luas,-1)==LUA_TTABLE) { \ 468 if (lua_properties_use_metatable) { \ 469 lua_newtable(Luas); \ 470 lua_insert(Luas,-2); \ 471 lua_setfield(Luas,-2,"__index"); \ 472 lua_newtable(Luas); \ 473 lua_insert(Luas,-2); \ 474 lua_setmetatable(Luas,-2); \ 475 } \ 476 lua_rawseti(Luas,-2,target); \ 477 } else { \ 478 lua_pop(Luas,1); \ 479 } \ 480 lua_pop(Luas,1); \ 481 } else { \ 482 lua_rawgeti(Luas,-1,source); \ 483 if (lua_type(Luas,-1)==LUA_TTABLE) { \ 484 if (lua_properties_use_metatable) { \ 485 lua_newtable(Luas); \ 486 lua_insert(Luas,-2); \ 487 lua_setfield(Luas,-2,"__index"); \ 488 lua_newtable(Luas); \ 489 lua_insert(Luas,-2); \ 490 lua_setmetatable(Luas,-2); \ 491 } \ 492 lua_rawseti(Luas,-2,target); \ 493 } else { \ 494 lua_pop(Luas,1); \ 495 } \ 496 } \ 497 } \ 498} while(0) 499 500/* Here end the property handlers. */ 501 502@ @c 503halfword new_node(int i, int j) 504{ 505 int s; 506 halfword n; 507 s = get_node_size(i, j); 508 n = get_node(s); 509 /* it should be possible to do this memset at |free_node()| */ 510 /* type() and subtype() will be set below, and vlink() is 511 set to null by |get_node()|, so we can do we clearing one 512 word less than |s| */ 513 (void) memset((void *) (varmem + n + 1), 0, 514 (sizeof(memory_word) * ((unsigned) s - 1))); 515 switch (i) { 516 case glyph_node: 517 init_lang_data(n); 518 break; 519 case hlist_node: 520 case vlist_node: 521 box_dir(n) = -1; 522 break; 523 case whatsit_node: 524 if (j == open_node) { 525 open_name(n) = get_nullstr(); 526 open_area(n) = open_name(n); 527 open_ext(n) = open_name(n); 528 } 529 break; 530 case disc_node: 531 pre_break(n) = pre_break_head(n); 532 type(pre_break(n)) = nesting_node; 533 subtype(pre_break(n)) = pre_break_head(0); 534 post_break(n) = post_break_head(n); 535 type(post_break(n)) = nesting_node; 536 subtype(post_break(n)) = post_break_head(0); 537 no_break(n) = no_break_head(n); 538 type(no_break(n)) = nesting_node; 539 subtype(no_break(n)) = no_break_head(0); 540 break; 541 case rule_node: 542 depth(n) = null_flag; 543 height(n) = null_flag; 544 rule_dir(n) = -1; 545 /* fall through */ 546 case unset_node: 547 width(n) = null_flag; 548 break; 549 case pseudo_line_node: 550 case shape_node: 551 /* this is a trick that makes |pseudo_files| slightly slower, 552 but the overall allocation faster then an explicit test 553 at the top of |new_node()|. 554 */ 555 if (j>0) { 556 free_node(n, variable_node_size); 557 n = slow_get_node(j); 558 (void) memset((void *) (varmem + n + 1), 0, 559 (sizeof(memory_word) * ((unsigned) j - 1))); 560 } 561 break; 562 default: 563 break; 564 } 565 /* handle synctex extension */ 566 switch (i) { 567 case math_node: 568 synctex_tag_math(n) = cur_input.synctex_tag_field; 569 synctex_line_math(n) = line; 570 break; 571 case glue_node: 572 synctex_tag_glue(n) = cur_input.synctex_tag_field; 573 synctex_line_glue(n) = line; 574 break; 575 case kern_node: 576 if (j != 0) { 577 synctex_tag_kern(n) = cur_input.synctex_tag_field; 578 synctex_line_kern(n) = line; 579 } 580 break; 581 case hlist_node: 582 case vlist_node: 583 case unset_node: 584 synctex_tag_box(n) = cur_input.synctex_tag_field; 585 synctex_line_box(n) = line; 586 break; 587 case rule_node: 588 synctex_tag_rule(n) = cur_input.synctex_tag_field; 589 synctex_line_rule(n) = line; 590 break; 591 } 592 /* take care of attributes */ 593 if (nodetype_has_attributes(i)) { 594 build_attribute_list(n); 595 /* lua_properties_set */ 596 } 597 type(n) = (quarterword) i; 598 subtype(n) = (quarterword) j; 599#ifdef DEBUG 600 fprintf(DEBUG_OUT, "Alloc-ing %s node %d\n", 601 get_node_name(type(n), subtype(n)), (int) n); 602#endif 603 return n; 604} 605 606halfword raw_glyph_node(void) 607{ 608 register halfword n; 609 n = get_node(glyph_node_size); 610 (void) memset((void *) (varmem + n + 1), 0, 611 (sizeof(memory_word) * (glyph_node_size - 1))); 612 type(n) = glyph_node; 613 subtype(n) = 0; 614 return n; 615} 616 617halfword new_glyph_node(void) 618{ 619 register halfword n; 620 n = get_node(glyph_node_size); 621 (void) memset((void *) (varmem + n + 1), 0, 622 (sizeof(memory_word) * (glyph_node_size - 1))); 623 type(n) = glyph_node; 624 subtype(n) = 0; 625 build_attribute_list(n); 626 /* lua_properties_set */ 627 return n; 628} 629 630 631@ makes a duplicate of the node list that starts at |p| and returns a 632 pointer to the new list 633@c 634halfword do_copy_node_list(halfword p, halfword end) 635{ 636 halfword q = null; /* previous position in new list */ 637 halfword h = null; /* head of the list */ 638 register halfword s ; 639 copy_error_seen = 0; 640 lua_properties_push; /* saves stack and time */ 641 while (p != end) { 642 s = copy_node(p); 643 if (h == null) { 644 h = s; 645 } else { 646 couple_nodes(q, s); 647 } 648 q = s; 649 p = vlink(p); 650 } 651 lua_properties_pop; /* saves stack and time */ 652 return h; 653} 654 655halfword copy_node_list(halfword p) 656{ 657 return do_copy_node_list(p, null); 658} 659 660/* There is no gain in using a temp var: 661 662 #define copy_sub_list(target,source) do { \ 663 l = source; \ 664 if (l != null) { \ 665 s = copy_node_list(l); \ 666 target = s; \ 667 } else { \ 668 target = null; \ 669 } \ 670 } while (0) 671 672 #define copy_sub_node(target,source) do { \ 673 l = source; \ 674 if (l != null) { \ 675 s = copy_node(l); \ 676 target = s ; \ 677 } else { \ 678 target = null; \ 679 } \ 680 } while (0) 681 682 So we use: 683*/ 684 685#define copy_sub_list(target,source) do { \ 686 if (source != null) { \ 687 s = do_copy_node_list(source, null); \ 688 target = s; \ 689 } else { \ 690 target = null; \ 691 } \ 692 } while (0) 693 694#define copy_sub_node(target,source) do { \ 695 if (source != null) { \ 696 s = copy_node(source); \ 697 target = s ; \ 698 } else { \ 699 target = null; \ 700 } \ 701} while (0) 702 703@ make a dupe of a single node 704@c 705halfword copy_node(const halfword p) 706{ 707 halfword r; /* current node being fabricated for new list */ 708 register halfword s; /* a helper variable for copying into variable mem */ 709 register int i; 710 if (copy_error(p)) { 711 r = new_node(temp_node, 0); 712 return r; 713 } 714 i = get_node_size(type(p), subtype(p)); 715 r = get_node(i); 716 717 (void) memcpy((void *) (varmem + r), (void *) (varmem + p), 718 (sizeof(memory_word) * (unsigned) i)); 719 720 /* handle synctex extension */ 721 switch (type(p)) { 722 case math_node: 723 synctex_tag_math(r) = cur_input.synctex_tag_field; 724 synctex_line_math(r) = line; 725 break; 726 case kern_node: 727 synctex_tag_kern(r) = cur_input.synctex_tag_field; 728 synctex_line_kern(r) = line; 729 break; 730 } 731 if (nodetype_has_attributes(type(p))) { 732 add_node_attr_ref(node_attr(p)); 733 alink(r) = null; 734 lua_properties_copy(r,p); 735 } 736 vlink(r) = null; 737 738 switch (type(p)) { 739 case glyph_node: 740 copy_sub_list(lig_ptr(r),lig_ptr(p)) ; 741 break; 742 case glue_node: 743 add_glue_ref(glue_ptr(p)); 744 copy_sub_list(leader_ptr(r),leader_ptr(p)) ; 745 break; 746 case hlist_node: 747 case vlist_node: 748 case unset_node: 749 copy_sub_list(list_ptr(r),list_ptr(p)) ; 750 break; 751 case disc_node: 752 pre_break(r) = pre_break_head(r); 753 if (vlink_pre_break(p) != null) { 754 s = copy_node_list(vlink_pre_break(p)); 755 alink(s) = pre_break(r); 756 tlink_pre_break(r) = tail_of_list(s); 757 vlink_pre_break(r) = s; 758 } else { 759 assert(tlink(pre_break(r)) == null); 760 } 761 post_break(r) = post_break_head(r); 762 if (vlink_post_break(p) != null) { 763 s = copy_node_list(vlink_post_break(p)); 764 alink(s) = post_break(r); 765 tlink_post_break(r) = tail_of_list(s); 766 vlink_post_break(r) = s; 767 } else { 768 assert(tlink_post_break(r) == null); 769 } 770 no_break(r) = no_break_head(r); 771 if (vlink(no_break(p)) != null) { 772 s = copy_node_list(vlink_no_break(p)); 773 alink(s) = no_break(r); 774 tlink_no_break(r) = tail_of_list(s); 775 vlink_no_break(r) = s; 776 } else { 777 assert(tlink_no_break(r) == null); 778 } 779 break; 780 case ins_node: 781 add_glue_ref(split_top_ptr(p)); 782 copy_sub_list(ins_ptr(r),ins_ptr(p)) ; 783 break; 784 case margin_kern_node: 785 copy_sub_node(margin_char(r),margin_char(p)); 786 break; 787 case mark_node: 788 add_token_ref(mark_ptr(p)); 789 break; 790 case adjust_node: 791 copy_sub_list(adjust_ptr(r),adjust_ptr(p)); 792 break; 793 case choice_node: 794 copy_sub_list(display_mlist(r),display_mlist(p)) ; 795 copy_sub_list(text_mlist(r),text_mlist(p)) ; 796 copy_sub_list(script_mlist(r),script_mlist(p)) ; 797 copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ; 798 break; 799 case simple_noad: 800 case radical_noad: 801 case accent_noad: 802 copy_sub_list(nucleus(r),nucleus(p)) ; 803 copy_sub_list(subscr(r),subscr(p)) ; 804 copy_sub_list(supscr(r),supscr(p)) ; 805 if (type(p) == accent_noad) { 806 copy_sub_list(accent_chr(r),accent_chr(p)) ; 807 copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ; 808 } else if (type(p) == radical_noad) { 809 copy_sub_node(left_delimiter(r),left_delimiter(p)) ; 810 copy_sub_list(degree(r),degree(p)) ; 811 } 812 break; 813 case fence_noad: 814 copy_sub_node(delimiter(r),delimiter(p)) ; 815 break; 816 case sub_box_node: 817 case sub_mlist_node: 818 copy_sub_list(math_list(r),math_list(p)) ; 819 break; 820 case fraction_noad: 821 copy_sub_list(numerator(r),numerator(p)) ; 822 copy_sub_list(denominator(r),denominator(p)) ; 823 copy_sub_node(left_delimiter(r),left_delimiter(p)) ; 824 copy_sub_node(right_delimiter(r),right_delimiter(p)) ; 825 break; 826 case glue_spec_node: 827 glue_ref_count(r) = null; 828 break; 829 case whatsit_node: 830 switch (subtype(p)) { 831 case dir_node: 832 case local_par_node: 833 break; 834 case write_node: 835 case special_node: 836 add_token_ref(write_tokens(p)); 837 break; 838 case pdf_literal_node: 839 copy_pdf_literal(r, p); 840 break; 841 case pdf_colorstack_node: 842 if (pdf_colorstack_cmd(p) <= colorstack_data) 843 add_token_ref(pdf_colorstack_data(p)); 844 break; 845 case pdf_setmatrix_node: 846 add_token_ref(pdf_setmatrix_data(p)); 847 break; 848 case late_lua_node: 849 copy_late_lua(r, p); 850 break; 851 case pdf_annot_node: 852 add_token_ref(pdf_annot_data(p)); 853 break; 854 case pdf_start_link_node: 855 if (pdf_link_attr(r) != null) 856 add_token_ref(pdf_link_attr(r)); 857 add_action_ref(pdf_link_action(r)); 858 break; 859 case pdf_dest_node: 860 if (pdf_dest_named_id(p) > 0) 861 add_token_ref(pdf_dest_id(p)); 862 break; 863 case pdf_thread_node: 864 case pdf_start_thread_node: 865 if (pdf_thread_named_id(p) > 0) 866 add_token_ref(pdf_thread_id(p)); 867 if (pdf_thread_attr(p) != null) 868 add_token_ref(pdf_thread_attr(p)); 869 break; 870 case user_defined_node: 871 switch (user_node_type(p)) { 872 case 'a': 873 add_node_attr_ref(user_node_value(p)); 874 break; 875 case 'l': 876 copy_user_lua(r, p); 877 break; 878 case 'n': 879 s = copy_node_list(user_node_value(p)); 880 user_node_value(r) = s; 881 break; 882 case 's': 883 /* |add_string_ref(user_node_value(p));| *//* if this was mpost .. */ 884 break; 885 case 't': 886 add_token_ref(user_node_value(p)); 887 break; 888 } 889 break; 890#if 0 891 case style_node: 892 case delim_node: 893 case math_char_node: 894 case math_text_char_node: 895 break; 896#else 897 default: 898#endif 899 break; 900 } 901 break; 902 } 903#ifdef DEBUG 904 fprintf(DEBUG_OUT, "Alloc-ing %s node %d (copy of %d)\n", 905 get_node_name(type(r), subtype(r)), (int) r, (int) p); 906#endif 907 return r; 908} 909 910@ @c 911int valid_node(halfword p) 912{ 913 if (p > my_prealloc) { 914 if (p < var_mem_max) { 915#ifndef NDEBUG 916 if (varmem_sizes[p] > 0) 917#endif 918 return 1; 919 } 920 } else { 921 return 0; 922 } 923 return 0; 924} 925 926@ @c 927static void do_free_error(halfword p) 928{ 929 halfword r; 930 char errstr[255] = { 0 }; 931 const char *errhlp[] = { 932 "When I tried to free the node mentioned in the error message, it turned", 933 "out it was not (or no longer) actually in use.", 934 "Errors such as these are often caused by Lua node list alteration,", 935 "but could also point to a bug in the executable. It should be safe to continue.", 936 NULL 937 }; 938 939 check_node_mem(); 940 if (free_error_seen) 941 return; 942 943 r = null; 944 free_error_seen = 1; 945 if (type(p) == glyph_node) { 946 snprintf(errstr, 255, 947 "Attempt to double-free glyph (%c) node %d, ignored", 948 (int) character(p), (int) p); 949 } else { 950 snprintf(errstr, 255, "Attempt to double-free %s node %d, ignored", 951 get_node_name(type(p), subtype(p)), (int) p); 952 } 953 tex_error(errstr, errhlp); 954#ifndef NDEBUG 955 for (r = my_prealloc + 1; r < var_mem_max; r++) { 956 if (vlink(r) == p) { 957 halfword s = r; 958 while (s > my_prealloc && varmem_sizes[s] == 0) 959 s--; 960 if (s != null 961 && s != my_prealloc 962 && s != var_mem_max 963 && (r - s) < get_node_size(type(s), subtype(s)) 964 && alink(s) != p) { 965 966 if (type(s) == disc_node) { 967 fprintf(stdout, 968 " pointed to from %s node %d (vlink %d, alink %d): ", 969 get_node_name(type(s), subtype(s)), (int) s, 970 (int) vlink(s), (int) alink(s)); 971 fprintf(stdout, "pre_break(%d,%d,%d), ", 972 (int) vlink_pre_break(s), (int) tlink(pre_break(s)), 973 (int) alink(pre_break(s))); 974 fprintf(stdout, "post_break(%d,%d,%d), ", 975 (int) vlink_post_break(s), 976 (int) tlink(post_break(s)), 977 (int) alink(post_break(s))); 978 fprintf(stdout, "no_break(%d,%d,%d)", 979 (int) vlink_no_break(s), (int) tlink(no_break(s)), 980 (int) alink(no_break(s))); 981 fprintf(stdout, "\n"); 982 } else { 983 if (vlink(s) == p 984 || (type(s) == glyph_node && lig_ptr(s) == p) 985 || (type(s) == vlist_node && list_ptr(s) == p) 986 || (type(s) == hlist_node && list_ptr(s) == p) 987 || (type(s) == unset_node && list_ptr(s) == p) 988 || (type(s) == ins_node && ins_ptr(s) == p) 989 ) { 990 fprintf(stdout, 991 " pointed to from %s node %d (vlink %d, alink %d): ", 992 get_node_name(type(s), subtype(s)), (int) s, 993 (int) vlink(s), (int) alink(s)); 994 if (type(s) == glyph_node) { 995 fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); 996 } else if (type(s) == vlist_node 997 || type(s) == hlist_node) { 998 fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); 999 } 1000 fprintf(stdout, "\n"); 1001 } else { 1002 if ((type(s) != penalty_node) 1003 && (type(s) != math_node) 1004 && (type(s) != kern_node) 1005 ) { 1006 fprintf(stdout, " pointed to from %s node %d\n", 1007 get_node_name(type(s), subtype(s)), 1008 (int) s); 1009 } 1010 } 1011 } 1012 } 1013 } 1014 } 1015#endif 1016} 1017 1018static int free_error(halfword p) 1019{ 1020 assert(p > my_prealloc); 1021 assert(p < var_mem_max); 1022#ifndef NDEBUG 1023 if (varmem_sizes[p] == 0) { 1024 do_free_error(p); 1025 return 1; /* double free */ 1026 } 1027#endif 1028 return 0; 1029} 1030 1031 1032@ @c 1033static void do_copy_error(halfword p) 1034{ 1035 char errstr[255] = { 0 }; 1036 const char *errhlp[] = { 1037 "When I tried to copy the node mentioned in the error message, it turned", 1038 "out it was not (or no longer) actually in use.", 1039 "Errors such as these are often caused by Lua node list alteration,", 1040 "but could also point to a bug in the executable. It should be safe to continue.", 1041 NULL 1042 }; 1043 1044 if (copy_error_seen) 1045 return; 1046 1047 copy_error_seen = 1; 1048 if (type(p) == glyph_node) { 1049 snprintf(errstr, 255, 1050 "Attempt to copy free glyph (%c) node %d, ignored", 1051 (int) character(p), (int) p); 1052 } else { 1053 snprintf(errstr, 255, "Attempt to copy free %s node %d, ignored", 1054 get_node_name(type(p), subtype(p)), (int) p); 1055 } 1056 tex_error(errstr, errhlp); 1057} 1058 1059 1060int copy_error(halfword p) 1061{ 1062 assert(p >= 0); 1063 assert(p < var_mem_max); 1064#ifndef NDEBUG 1065 if (p > my_prealloc && varmem_sizes[p] == 0) { 1066 do_copy_error(p); 1067 return 1; /* copy free node */ 1068 } 1069#endif 1070 return 0; 1071} 1072 1073/* No gain in a helper: 1074 1075 #define free_sub_list(source) do { \ 1076 l = source; \ 1077 if (l != null) \ 1078 flush_node_list(l); \ 1079 } while (0) 1080 1081 #define free_sub_node(source) do { \ 1082 l = source; \ 1083 if (l != null) \ 1084 flush_node(l); \ 1085 } while (0) 1086 1087 So: 1088 1089*/ 1090 1091#define free_sub_list(source) do { \ 1092 if (source != null) \ 1093 flush_node_list(source); \ 1094} while (0) 1095 1096#define free_sub_node(source) do { \ 1097 if (source != null) \ 1098 flush_node(source); \ 1099} while (0) 1100 1101@ @c 1102void flush_node(halfword p) 1103{ 1104 1105 if (p == null) /* legal, but no-op */ 1106 return; 1107 1108#ifdef DEBUG 1109 fprintf(DEBUG_OUT, "Free-ing %s node %d\n", 1110 get_node_name(type(p), subtype(p)), (int) p); 1111#endif 1112 if (free_error(p)) 1113 return; 1114 1115 switch (type(p)) { 1116 case glyph_node: 1117 free_sub_list(lig_ptr(p)); 1118 break; 1119 case glue_node: 1120 delete_glue_ref(glue_ptr(p)); 1121 free_sub_list(leader_ptr(p)); 1122 break; 1123 case hlist_node: 1124 case vlist_node: 1125 case unset_node: 1126 free_sub_list(list_ptr(p)); 1127 break; 1128 case disc_node: 1129 free_sub_list(vlink(pre_break(p))); 1130 free_sub_list(vlink(post_break(p))); 1131 free_sub_list(vlink(no_break(p))); 1132// why not: free_sub_list(pre_break(p)); 1133// why not: free_sub_list(post_break(p)); 1134// why not: free_sub_list(no_break(p)); 1135 break; 1136 case rule_node: 1137 case kern_node: 1138 case math_node: 1139 case penalty_node: 1140 break; 1141 case glue_spec_node: 1142 /* this allows free-ing of lua-allocated glue specs */ 1143 if (valid_node(p)) { 1144 if (glue_ref_count(p)!=null) { 1145 decr(glue_ref_count(p)); 1146 } else { 1147 free_node(p, get_node_size(type(p), subtype(p))); 1148 } 1149 } 1150 return ; 1151 break ; 1152 case whatsit_node: 1153 switch (subtype(p)) { 1154 1155 case dir_node: 1156 break; 1157 case open_node: 1158 case write_node: 1159 case close_node: 1160 case pdf_save_node: 1161 case pdf_restore_node: 1162 case cancel_boundary_node: 1163 case close_lua_node: 1164 case pdf_refobj_node: 1165 case pdf_refxform_node: 1166 case pdf_refximage_node: 1167 case pdf_end_link_node: 1168 case pdf_end_thread_node: 1169 case pdf_save_pos_node: 1170 case local_par_node: 1171 break; 1172 1173 case special_node: 1174 delete_token_ref(write_tokens(p)); 1175 break; 1176 case pdf_literal_node: 1177 free_pdf_literal(p); 1178 break; 1179 case pdf_colorstack_node: 1180 if (pdf_colorstack_cmd(p) <= colorstack_data) 1181 delete_token_ref(pdf_colorstack_data(p)); 1182 break; 1183 case pdf_setmatrix_node: 1184 delete_token_ref(pdf_setmatrix_data(p)); 1185 break; 1186 case late_lua_node: 1187 free_late_lua(p); 1188 break; 1189 case pdf_annot_node: 1190 delete_token_ref(pdf_annot_data(p)); 1191 break; 1192 1193 case pdf_link_data_node: 1194 break; 1195 1196 case pdf_start_link_node: 1197 if (pdf_link_attr(p) != null) 1198 delete_token_ref(pdf_link_attr(p)); 1199 delete_action_ref(pdf_link_action(p)); 1200 break; 1201 case pdf_dest_node: 1202 if (pdf_dest_named_id(p) > 0) 1203 delete_token_ref(pdf_dest_id(p)); 1204 break; 1205 1206 case pdf_thread_data_node: 1207 break; 1208 1209 case pdf_thread_node: 1210 case pdf_start_thread_node: 1211 if (pdf_thread_named_id(p) > 0) 1212 delete_token_ref(pdf_thread_id(p)); 1213 if (pdf_thread_attr(p) != null) 1214 delete_token_ref(pdf_thread_attr(p)); 1215 break; 1216 case user_defined_node: 1217 switch (user_node_type(p)) { 1218 case 'a': 1219 delete_attribute_ref(user_node_value(p)); 1220 break; 1221 case 'd': 1222 break; 1223 case 'n': 1224 flush_node_list(user_node_value(p)); 1225 break; 1226 case 's': 1227 /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */ 1228 break; 1229 case 't': 1230 delete_token_ref(user_node_value(p)); 1231 break; 1232 default: 1233 { 1234 const char *hlp[] = { 1235 "The type of the value in a user defined whatsit node should be one", 1236 "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),", 1237 "or 't' (tokenlist). Yours has an unknown type, and therefore I don't", 1238 "know how to free the node's value. A memory leak may result.", 1239 NULL 1240 }; 1241 tex_error("Unidentified user defined whatsit", hlp); 1242 } 1243 break; 1244 } 1245 break; 1246 1247 default: 1248 confusion("ext3"); 1249 return; 1250 1251 } 1252 break; 1253 case ins_node: 1254 flush_node_list(ins_ptr(p)); 1255 delete_glue_ref(split_top_ptr(p)); 1256 break; 1257 case margin_kern_node: 1258 flush_node(margin_char(p)); 1259 break; 1260 case mark_node: 1261 delete_token_ref(mark_ptr(p)); 1262 break; 1263 case adjust_node: 1264 flush_node_list(adjust_ptr(p)); 1265 break; 1266 case style_node: /* nothing to do */ 1267 break; 1268 case choice_node: 1269 free_sub_list(display_mlist(p)); 1270 free_sub_list(text_mlist(p)); 1271 free_sub_list(script_mlist(p)); 1272 free_sub_list(script_script_mlist(p)); 1273 break; 1274 case simple_noad: 1275 case radical_noad: 1276 case accent_noad: 1277 free_sub_list(nucleus(p)); 1278 free_sub_list(subscr(p)); 1279 free_sub_list(supscr(p)); 1280 if (type(p) == accent_noad) { 1281 free_sub_list(accent_chr(p)); 1282 free_sub_list(bot_accent_chr(p)); 1283 } else if (type(p) == radical_noad) { 1284 free_sub_node(left_delimiter(p)); 1285 free_sub_list(degree(p)); 1286 } 1287 break; 1288 case fence_noad: 1289 free_sub_list(delimiter(p)); 1290 break; 1291 case delim_node: /* nothing to do */ 1292 case math_char_node: 1293 case math_text_char_node: 1294 break; 1295 case sub_box_node: 1296 case sub_mlist_node: 1297 free_sub_list(math_list(p)); 1298 break; 1299 case fraction_noad: 1300 free_sub_list(numerator(p)); 1301 free_sub_list(denominator(p)); 1302 free_sub_node(left_delimiter(p)); 1303 free_sub_node(right_delimiter(p)); 1304 break; 1305 case pseudo_file_node: 1306 free_sub_list(pseudo_lines(p)); 1307 break; 1308 case pseudo_line_node: 1309 case shape_node: 1310 free_node(p, subtype(p)); 1311 return; 1312 break; 1313 case align_stack_node: 1314 case span_node: 1315 case movement_node: 1316 case if_node: 1317 case nesting_node: 1318 case unhyphenated_node: 1319 case hyphenated_node: 1320 case delta_node: 1321 case passive_node: 1322 case action_node: 1323 case inserting_node: 1324 case split_up_node: 1325 case expr_node: 1326 case attribute_node: 1327 case attribute_list_node: 1328 case temp_node: 1329 break; 1330 default: 1331 fprintf(stdout, "flush_node: type is %d\n", type(p)); 1332 return; 1333 1334 } 1335 if (nodetype_has_attributes(type(p))) { 1336 delete_attribute_ref(node_attr(p)); 1337 lua_properties_reset(p); 1338 } 1339 free_node(p, get_node_size(type(p), subtype(p))); 1340 return; 1341} 1342 1343@ @c 1344void flush_node_list(halfword pp) 1345{ /* erase list of nodes starting at |p| */ 1346 register halfword p = pp; 1347 free_error_seen = 0; 1348 if (p == null) /* legal, but no-op */ 1349 return; 1350 if (free_error(p)) 1351 return; 1352 lua_properties_push; /* saves stack and time */ 1353 while (p != null) { 1354 register halfword q = vlink(p); 1355 flush_node(p); 1356 p = q; 1357 } 1358 lua_properties_pop; /* saves stack and time */ 1359} 1360 1361@ @c 1362static int test_count = 1; 1363 1364#define dorangetest(a,b,c) do { \ 1365 if (!(b>=0 && b<c)) { \ 1366 fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \ 1367 (int)a, (int)b, (int)c, __LINE__,test_count); \ 1368 confusion("dorangetest"); \ 1369 } } while (0) 1370 1371#define dotest(a,b,c) do { \ 1372 if (b!=c) { \ 1373 fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \ 1374 (int)a, (int)b, (int)c, __LINE__,test_count); \ 1375 confusion("dotest"); \ 1376 } } while (0) 1377 1378#define check_action_ref(a) { dorangetest(p,a,var_mem_max); } 1379#define check_glue_ref(a) { dorangetest(p,a,var_mem_max); } 1380#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); } 1381#define check_token_ref(a) assert(1) 1382 1383void check_node(halfword p) 1384{ 1385 1386 switch (type(p)) { 1387 case glyph_node: 1388 dorangetest(p, lig_ptr(p), var_mem_max); 1389 break; 1390 case glue_node: 1391 check_glue_ref(glue_ptr(p)); 1392 dorangetest(p, leader_ptr(p), var_mem_max); 1393 break; 1394 case hlist_node: 1395 case vlist_node: 1396 case unset_node: 1397 case align_record_node: 1398 dorangetest(p, list_ptr(p), var_mem_max); 1399 break; 1400 case ins_node: 1401 dorangetest(p, ins_ptr(p), var_mem_max); 1402 check_glue_ref(split_top_ptr(p)); 1403 break; 1404 case whatsit_node: 1405 switch (subtype(p)) { 1406 case special_node: 1407 check_token_ref(write_tokens(p)); 1408 break; 1409 case pdf_literal_node: 1410 if (pdf_literal_type(p) == normal) 1411 check_token_ref(pdf_literal_data(p)); 1412 break; 1413 case pdf_colorstack_node: 1414 if (pdf_colorstack_cmd(p) <= colorstack_data) 1415 check_token_ref(pdf_colorstack_data(p)); 1416 break; 1417 case pdf_setmatrix_node: 1418 check_token_ref(pdf_setmatrix_data(p)); 1419 break; 1420 case late_lua_node: 1421 if (late_lua_name(p) > 0) 1422 check_token_ref(late_lua_name(p)); 1423 if (late_lua_type(p) == normal) 1424 check_token_ref(late_lua_data(p)); 1425 break; 1426 case pdf_annot_node: 1427 check_token_ref(pdf_annot_data(p)); 1428 break; 1429 case pdf_start_link_node: 1430 if (pdf_link_attr(p) != null) 1431 check_token_ref(pdf_link_attr(p)); 1432 check_action_ref(pdf_link_action(p)); 1433 break; 1434 case pdf_dest_node: 1435 if (pdf_dest_named_id(p) > 0) 1436 check_token_ref(pdf_dest_id(p)); 1437 break; 1438 case pdf_thread_node: 1439 case pdf_start_thread_node: 1440 if (pdf_thread_named_id(p) > 0) 1441 check_token_ref(pdf_thread_id(p)); 1442 if (pdf_thread_attr(p) != null) 1443 check_token_ref(pdf_thread_attr(p)); 1444 break; 1445 case user_defined_node: 1446 switch (user_node_type(p)) { 1447 case 'a': 1448 check_attribute_ref(user_node_value(p)); 1449 break; 1450 case 't': 1451 check_token_ref(user_node_value(p)); 1452 break; 1453 case 'n': 1454 dorangetest(p, user_node_value(p), var_mem_max); 1455 break; 1456 case 's': 1457 case 'd': 1458 break; 1459 default: 1460 confusion("extuser"); 1461 break; 1462 } 1463 break; 1464 case dir_node: 1465 case open_node: 1466 case write_node: 1467 case close_node: 1468 case pdf_save_node: 1469 case pdf_restore_node: 1470 case cancel_boundary_node: 1471 case close_lua_node: 1472 case pdf_refobj_node: 1473 case pdf_refxform_node: 1474 case pdf_refximage_node: 1475 case pdf_end_link_node: 1476 case pdf_end_thread_node: 1477 case pdf_save_pos_node: 1478 case local_par_node: 1479 break; 1480 default: 1481 confusion("ext3"); 1482 } 1483 break; 1484 case margin_kern_node: 1485 check_node(margin_char(p)); 1486 break; 1487 case disc_node: 1488 dorangetest(p, vlink(pre_break(p)), var_mem_max); 1489 dorangetest(p, vlink(post_break(p)), var_mem_max); 1490 dorangetest(p, vlink(no_break(p)), var_mem_max); 1491 break; 1492 case adjust_node: 1493 dorangetest(p, adjust_ptr(p), var_mem_max); 1494 break; 1495 case pseudo_file_node: 1496 dorangetest(p, pseudo_lines(p), var_mem_max); 1497 break; 1498 case pseudo_line_node: 1499 case shape_node: 1500 break; 1501 case choice_node: 1502 dorangetest(p, display_mlist(p), var_mem_max); 1503 dorangetest(p, text_mlist(p), var_mem_max); 1504 dorangetest(p, script_mlist(p), var_mem_max); 1505 dorangetest(p, script_script_mlist(p), var_mem_max); 1506 break; 1507 case fraction_noad: 1508 dorangetest(p, numerator(p), var_mem_max); 1509 dorangetest(p, denominator(p), var_mem_max); 1510 dorangetest(p, left_delimiter(p), var_mem_max); 1511 dorangetest(p, right_delimiter(p), var_mem_max); 1512 break; 1513 case simple_noad: 1514 dorangetest(p, nucleus(p), var_mem_max); 1515 dorangetest(p, subscr(p), var_mem_max); 1516 dorangetest(p, supscr(p), var_mem_max); 1517 break; 1518 case radical_noad: 1519 dorangetest(p, nucleus(p), var_mem_max); 1520 dorangetest(p, subscr(p), var_mem_max); 1521 dorangetest(p, supscr(p), var_mem_max); 1522 dorangetest(p, degree(p), var_mem_max); 1523 dorangetest(p, left_delimiter(p), var_mem_max); 1524 break; 1525 case accent_noad: 1526 dorangetest(p, nucleus(p), var_mem_max); 1527 dorangetest(p, subscr(p), var_mem_max); 1528 dorangetest(p, supscr(p), var_mem_max); 1529 dorangetest(p, accent_chr(p), var_mem_max); 1530 dorangetest(p, bot_accent_chr(p), var_mem_max); 1531 break; 1532 case fence_noad: 1533 dorangetest(p, delimiter(p), var_mem_max); 1534 break; 1535 case rule_node: 1536 case kern_node: 1537 case math_node: 1538 case penalty_node: 1539 case mark_node: 1540 case style_node: 1541 case attribute_list_node: 1542 case attribute_node: 1543 case glue_spec_node: 1544 case temp_node: 1545 case align_stack_node: 1546 case movement_node: 1547 case if_node: 1548 case nesting_node: 1549 case span_node: 1550 case unhyphenated_node: 1551 case hyphenated_node: 1552 case delta_node: 1553 case passive_node: 1554 case expr_node: 1555 break; 1556 default: 1557 fprintf(stdout, "check_node: type is %d\n", type(p)); 1558 } 1559} 1560 1561@ @c 1562static void check_static_node_mem(void) 1563{ 1564 dotest(zero_glue, width(zero_glue), 0); 1565 dotest(zero_glue, type(zero_glue), glue_spec_node); 1566 dotest(zero_glue, vlink(zero_glue), null); 1567 dotest(zero_glue, stretch(zero_glue), 0); 1568 dotest(zero_glue, stretch_order(zero_glue), normal); 1569 dotest(zero_glue, shrink(zero_glue), 0); 1570 dotest(zero_glue, shrink_order(zero_glue), normal); 1571 1572 dotest(sfi_glue, width(sfi_glue), 0); 1573 dotest(sfi_glue, type(sfi_glue), glue_spec_node); 1574 dotest(sfi_glue, vlink(sfi_glue), null); 1575 dotest(sfi_glue, stretch(sfi_glue), 0); 1576 dotest(sfi_glue, stretch_order(sfi_glue), sfi); 1577 dotest(sfi_glue, shrink(sfi_glue), 0); 1578 dotest(sfi_glue, shrink_order(sfi_glue), normal); 1579 1580 dotest(fil_glue, width(fil_glue), 0); 1581 dotest(fil_glue, type(fil_glue), glue_spec_node); 1582 dotest(fil_glue, vlink(fil_glue), null); 1583 dotest(fil_glue, stretch(fil_glue), unity); 1584 dotest(fil_glue, stretch_order(fil_glue), fil); 1585 dotest(fil_glue, shrink(fil_glue), 0); 1586 dotest(fil_glue, shrink_order(fil_glue), normal); 1587 1588 dotest(fill_glue, width(fill_glue), 0); 1589 dotest(fill_glue, type(fill_glue), glue_spec_node); 1590 dotest(fill_glue, vlink(fill_glue), null); 1591 dotest(fill_glue, stretch(fill_glue), unity); 1592 dotest(fill_glue, stretch_order(fill_glue), fill); 1593 dotest(fill_glue, shrink(fill_glue), 0); 1594 dotest(fill_glue, shrink_order(fill_glue), normal); 1595 1596 dotest(ss_glue, width(ss_glue), 0); 1597 dotest(ss_glue, type(ss_glue), glue_spec_node); 1598 dotest(ss_glue, vlink(ss_glue), null); 1599 dotest(ss_glue, stretch(ss_glue), unity); 1600 dotest(ss_glue, stretch_order(ss_glue), fil); 1601 dotest(ss_glue, shrink(ss_glue), unity); 1602 dotest(ss_glue, shrink_order(ss_glue), fil); 1603 1604 dotest(fil_neg_glue, width(fil_neg_glue), 0); 1605 dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node); 1606 dotest(fil_neg_glue, vlink(fil_neg_glue), null); 1607 dotest(fil_neg_glue, stretch(fil_neg_glue), -unity); 1608 dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil); 1609 dotest(fil_neg_glue, shrink(fil_neg_glue), 0); 1610 dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal); 1611} 1612 1613@ @c 1614void check_node_mem(void) 1615{ 1616 int i; 1617 check_static_node_mem(); 1618#ifndef NDEBUG 1619 for (i = (my_prealloc + 1); i < var_mem_max; i++) { 1620 if (varmem_sizes[i] > 0) { 1621 check_node(i); 1622 } 1623 } 1624#endif 1625 test_count++; 1626} 1627 1628@ @c 1629void fix_node_list(halfword head) 1630{ 1631 halfword p, q; 1632 if (head == null) 1633 return; 1634 p = head; 1635 q = vlink(p); 1636 while (q != null) { 1637 alink(q) = p; 1638 p = q; 1639 q = vlink(p); 1640 } 1641} 1642 1643@ @c 1644halfword get_node(int s) 1645{ 1646 register halfword r; 1647#if 0 1648 check_static_node_mem(); 1649#endif 1650 assert(s < MAX_CHAIN_SIZE); 1651 1652 r = free_chain[s]; 1653 if (r != null) { 1654 free_chain[s] = vlink(r); 1655#ifndef NDEBUG 1656 varmem_sizes[r] = (char) s; 1657#endif 1658 vlink(r) = null; 1659 var_used += s; /* maintain usage statistics */ 1660 return r; 1661 } 1662 /* this is the end of the 'inner loop' */ 1663 return slow_get_node(s); 1664} 1665 1666@ @c 1667#ifdef DEBUG 1668static void print_free_chain(int c) 1669{ 1670 halfword p = free_chain[c]; 1671 fprintf(stdout, "\nfree chain[%d] =\n ", c); 1672 while (p != null) { 1673 fprintf(stdout, "%d,", (int) p); 1674 p = vlink(p); 1675 } 1676 fprintf(stdout, "null;\n"); 1677} 1678#endif 1679 1680@ @c 1681void free_node(halfword p, int s) 1682{ 1683 1684 if (p <= my_prealloc) { 1685 fprintf(stdout, "node %d (type %d) should not be freed!\n", (int) p, 1686 type(p)); 1687 return; 1688 } 1689#ifndef NDEBUG 1690 varmem_sizes[p] = 0; 1691#endif 1692 if (s < MAX_CHAIN_SIZE) { 1693 vlink(p) = free_chain[s]; 1694 free_chain[s] = p; 1695 } else { 1696 /* todo ? it is perhaps possible to merge this node with an existing rover */ 1697 node_size(p) = s; 1698 vlink(p) = rover; 1699 while (vlink(rover) != vlink(p)) { 1700 rover = vlink(rover); 1701 } 1702 vlink(rover) = p; 1703 } 1704 var_used -= s; /* maintain statistics */ 1705} 1706 1707@ @c 1708static void free_node_chain(halfword q, int s) 1709{ 1710 register halfword p = q; 1711 while (vlink(p) != null) { 1712#ifndef NDEBUG 1713 varmem_sizes[p] = 0; 1714#endif 1715 var_used -= s; 1716 p = vlink(p); 1717 } 1718 var_used -= s; 1719#ifndef NDEBUG 1720 varmem_sizes[p] = 0; 1721#endif 1722 vlink(p) = free_chain[s]; 1723 free_chain[s] = q; 1724} 1725 1726 1727@ @c 1728void init_node_mem(int t) 1729{ 1730 my_prealloc = var_mem_stat_max; 1731 assert(whatsit_node_data[user_defined_node].id == user_defined_node); 1732 assert(node_data[passive_node].id == passive_node); 1733 1734 varmem = 1735 (memory_word *) realloc((void *) varmem, 1736 sizeof(memory_word) * (unsigned) t); 1737 if (varmem == NULL) { 1738 overflow("node memory size", (unsigned) var_mem_max); 1739 } 1740 memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word)); 1741 1742#ifndef NDEBUG 1743 varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t); 1744 if (varmem_sizes == NULL) { 1745 overflow("node memory size", (unsigned) var_mem_max); 1746 } 1747 memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t); 1748#endif 1749 var_mem_max = t; 1750 rover = var_mem_stat_max + 1; 1751 vlink(rover) = rover; 1752 node_size(rover) = (t - rover); 1753 var_used = 0; 1754 /* initialize static glue specs */ 1755 glue_ref_count(zero_glue) = null + 1; 1756 width(zero_glue) = 0; 1757 type(zero_glue) = glue_spec_node; 1758 vlink(zero_glue) = null; 1759 stretch(zero_glue) = 0; 1760 stretch_order(zero_glue) = normal; 1761 shrink(zero_glue) = 0; 1762 shrink_order(zero_glue) = normal; 1763 glue_ref_count(sfi_glue) = null + 1; 1764 width(sfi_glue) = 0; 1765 type(sfi_glue) = glue_spec_node; 1766 vlink(sfi_glue) = null; 1767 stretch(sfi_glue) = 0; 1768 stretch_order(sfi_glue) = sfi; 1769 shrink(sfi_glue) = 0; 1770 shrink_order(sfi_glue) = normal; 1771 glue_ref_count(fil_glue) = null + 1; 1772 width(fil_glue) = 0; 1773 type(fil_glue) = glue_spec_node; 1774 vlink(fil_glue) = null; 1775 stretch(fil_glue) = unity; 1776 stretch_order(fil_glue) = fil; 1777 shrink(fil_glue) = 0; 1778 shrink_order(fil_glue) = normal; 1779 glue_ref_count(fill_glue) = null + 1; 1780 width(fill_glue) = 0; 1781 type(fill_glue) = glue_spec_node; 1782 vlink(fill_glue) = null; 1783 stretch(fill_glue) = unity; 1784 stretch_order(fill_glue) = fill; 1785 shrink(fill_glue) = 0; 1786 shrink_order(fill_glue) = normal; 1787 glue_ref_count(ss_glue) = null + 1; 1788 width(ss_glue) = 0; 1789 type(ss_glue) = glue_spec_node; 1790 vlink(ss_glue) = null; 1791 stretch(ss_glue) = unity; 1792 stretch_order(ss_glue) = fil; 1793 shrink(ss_glue) = unity; 1794 shrink_order(ss_glue) = fil; 1795 glue_ref_count(fil_neg_glue) = null + 1; 1796 width(fil_neg_glue) = 0; 1797 type(fil_neg_glue) = glue_spec_node; 1798 vlink(fil_neg_glue) = null; 1799 stretch(fil_neg_glue) = -unity; 1800 stretch_order(fil_neg_glue) = fil; 1801 shrink(fil_neg_glue) = 0; 1802 shrink_order(fil_neg_glue) = normal; 1803 /* initialize node list heads */ 1804 vinfo(page_ins_head) = 0; 1805 type(page_ins_head) = temp_node; 1806 vlink(page_ins_head) = null; 1807 alink(page_ins_head) = null; 1808 vinfo(contrib_head) = 0; 1809 type(contrib_head) = temp_node; 1810 vlink(contrib_head) = null; 1811 alink(contrib_head) = null; 1812 vinfo(page_head) = 0; 1813 type(page_head) = temp_node; 1814 vlink(page_head) = null; 1815 alink(page_head) = null; 1816 vinfo(temp_head) = 0; 1817 type(temp_head) = temp_node; 1818 vlink(temp_head) = null; 1819 alink(temp_head) = null; 1820 vinfo(hold_head) = 0; 1821 type(hold_head) = temp_node; 1822 vlink(hold_head) = null; 1823 alink(hold_head) = null; 1824 vinfo(adjust_head) = 0; 1825 type(adjust_head) = temp_node; 1826 vlink(adjust_head) = null; 1827 alink(adjust_head) = null; 1828 vinfo(pre_adjust_head) = 0; 1829 type(pre_adjust_head) = temp_node; 1830 vlink(pre_adjust_head) = null; 1831 alink(pre_adjust_head) = null; 1832 vinfo(active) = 0; 1833 type(active) = unhyphenated_node; 1834 vlink(active) = null; 1835 alink(active) = null; 1836 vinfo(align_head) = 0; 1837 type(align_head) = temp_node; 1838 vlink(align_head) = null; 1839 alink(align_head) = null; 1840 vinfo(end_span) = 0; 1841 type(end_span) = span_node; 1842 vlink(end_span) = null; 1843 alink(end_span) = null; 1844 type(begin_point) = glyph_node; 1845 subtype(begin_point) = 0; 1846 vlink(begin_point) = null; 1847 vinfo(begin_point + 1) = null; 1848 alink(begin_point) = null; 1849 font(begin_point) = 0; 1850 character(begin_point) = '.'; 1851 vinfo(begin_point + 3) = 0; 1852 vlink(begin_point + 3) = 0; 1853 vinfo(begin_point + 4) = 0; 1854 vlink(begin_point + 4) = 0; 1855 type(end_point) = glyph_node; 1856 subtype(end_point) = 0; 1857 vlink(end_point) = null; 1858 vinfo(end_point + 1) = null; 1859 alink(end_point) = null; 1860 font(end_point) = 0; 1861 character(end_point) = '.'; 1862 vinfo(end_point + 3) = 0; 1863 vlink(end_point + 3) = 0; 1864 vinfo(end_point + 4) = 0; 1865 vlink(end_point + 4) = 0; 1866} 1867 1868@ @c 1869void dump_node_mem(void) 1870{ 1871 dump_int(var_mem_max); 1872 dump_int(rover); 1873 dump_things(varmem[0], var_mem_max); 1874#ifndef NDEBUG 1875 dump_things(varmem_sizes[0], var_mem_max); 1876#endif 1877 dump_things(free_chain[0], MAX_CHAIN_SIZE); 1878 dump_int(var_used); 1879 dump_int(my_prealloc); 1880} 1881 1882@ it makes sense to enlarge the varmem array immediately 1883@c 1884void undump_node_mem(void) 1885{ 1886 int x; 1887 undump_int(x); 1888 undump_int(rover); 1889 var_mem_max = (x < 100000 ? 100000 : x); 1890 varmem = xmallocarray(memory_word, (unsigned) var_mem_max); 1891#if 0 1892 memset ((void *)varmem,0,x*sizeof(memory_word)); 1893#endif 1894 undump_things(varmem[0], x); 1895#ifndef NDEBUG 1896 varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); 1897 memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char)); 1898 undump_things(varmem_sizes[0], x); 1899#endif 1900 undump_things(free_chain[0], MAX_CHAIN_SIZE); 1901 undump_int(var_used); 1902 undump_int(my_prealloc); 1903 if (var_mem_max > x) { 1904 /* todo ? it is perhaps possible to merge the new node with an existing rover */ 1905 vlink(x) = rover; 1906 node_size(x) = (var_mem_max - x); 1907 while (vlink(rover) != vlink(x)) { 1908 rover = vlink(rover); 1909 } 1910 vlink(rover) = x; 1911 } 1912} 1913 1914#if 0 1915void test_rovers(char *s) 1916{ 1917 int q = rover; 1918 int r = q; 1919 fprintf(stdout, "%s: {rover=%d,size=%d,link=%d}", s, r, node_size(r), 1920 vlink(r)); 1921 while (vlink(r) != q) { 1922 r = vlink(r); 1923 fprintf(stdout, ",{rover=%d,size=%d,link=%d}", r, node_size(r), 1924 vlink(r)); 1925 } 1926 fprintf(stdout, "\n"); 1927} 1928#else 1929# define test_rovers(a) 1930#endif 1931 1932@ @c 1933halfword slow_get_node(int s) 1934{ 1935 register int t; 1936 1937 RETRY: 1938 t = node_size(rover); 1939 assert(vlink(rover) < var_mem_max); 1940 assert(vlink(rover) != 0); 1941 test_rovers("entry"); 1942 if (t > s) { 1943 register halfword r; 1944 /* allocating from the bottom helps decrease page faults */ 1945 r = rover; 1946 rover += s; 1947 vlink(rover) = vlink(r); 1948 node_size(rover) = node_size(r) - s; 1949 if (vlink(rover) != r) { /* list is longer than one */ 1950 halfword q = r; 1951 while (vlink(q) != r) { 1952 q = vlink(q); 1953 } 1954 vlink(q) += s; 1955 } else { 1956 vlink(rover) += s; 1957 } 1958 test_rovers("taken"); 1959 assert(vlink(rover) < var_mem_max); 1960#ifndef NDEBUG 1961 varmem_sizes[r] = (char) (s > 127 ? 127 : s); 1962#endif 1963 vlink(r) = null; 1964 var_used += s; /* maintain usage statistics */ 1965 return r; /* this is the only exit */ 1966 } else { 1967 int x; 1968 /* attempt to keep the free list small */ 1969 if (vlink(rover) != rover) { 1970 if (t < MAX_CHAIN_SIZE) { 1971 halfword l = vlink(rover); 1972 vlink(rover) = free_chain[t]; 1973 free_chain[t] = rover; 1974 rover = l; 1975 while (vlink(l) != free_chain[t]) { 1976 l = vlink(l); 1977 } 1978 vlink(l) = rover; 1979 test_rovers("outtake"); 1980 goto RETRY; 1981 } else { 1982 halfword l = rover; 1983 while (vlink(rover) != l) { 1984 if (node_size(rover) > s) { 1985 goto RETRY; 1986 } 1987 rover = vlink(rover); 1988 } 1989 } 1990 } 1991 /* if we are still here, it was apparently impossible to get a match */ 1992 x = (var_mem_max >> 2) + s; 1993 varmem = 1994 (memory_word *) realloc((void *) varmem, 1995 sizeof(memory_word) * 1996 (unsigned) (var_mem_max + x)); 1997 if (varmem == NULL) { 1998 overflow("node memory size", (unsigned) var_mem_max); 1999 } 2000 memset((void *) (varmem + var_mem_max), 0, 2001 (unsigned) x * sizeof(memory_word)); 2002 2003#ifndef NDEBUG 2004 varmem_sizes = 2005 (char *) realloc(varmem_sizes, 2006 sizeof(char) * (unsigned) (var_mem_max + x)); 2007 if (varmem_sizes == NULL) { 2008 overflow("node memory size", (unsigned) var_mem_max); 2009 } 2010 memset((void *) (varmem_sizes + var_mem_max), 0, 2011 (unsigned) (x) * sizeof(char)); 2012#endif 2013 2014 /* todo ? it is perhaps possible to merge the new memory with an existing rover */ 2015 vlink(var_mem_max) = rover; 2016 node_size(var_mem_max) = x; 2017 while (vlink(rover) != vlink(var_mem_max)) { 2018 rover = vlink(rover); 2019 } 2020 vlink(rover) = var_mem_max; 2021 rover = var_mem_max; 2022 test_rovers("realloc"); 2023 var_mem_max += x; 2024 goto RETRY; 2025 } 2026} 2027 2028@ @c 2029char *sprint_node_mem_usage(void) 2030{ 2031 int i, b; 2032 2033 char *s, *ss; 2034#ifndef NDEBUG 2035 char msg[256]; 2036 int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; 2037 2038 for (i = (var_mem_max - 1); i > my_prealloc; i--) { 2039 if (varmem_sizes[i] > 0) { 2040 if (type(i) > last_normal_node + last_whatsit_node) { 2041 node_counts[last_normal_node + last_whatsit_node + 1]++; 2042 } else if (type(i) == whatsit_node) { 2043 node_counts[(subtype(i) + last_normal_node + 1)]++; 2044 } else { 2045 node_counts[type(i)]++; 2046 } 2047 } 2048 } 2049 s = strdup(""); 2050 b = 0; 2051 for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) { 2052 if (node_counts[i] > 0) { 2053 int j = 2054 (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0); 2055 snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], 2056 get_node_name((i > last_normal_node ? whatsit_node : i), 2057 j)); 2058 ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1)); 2059 strcpy(ss, s); 2060 strcat(ss, msg); 2061 free(s); 2062 s = ss; 2063 b = 1; 2064 } 2065 } 2066#else 2067 s = strdup(""); 2068#endif 2069 return s; 2070} 2071 2072@ @c 2073halfword list_node_mem_usage(void) 2074{ 2075 halfword i, j; 2076 halfword p = null, q = null; 2077#ifndef NDEBUG 2078 char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); 2079 memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max); 2080 for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { 2081 if (saved_varmem_sizes[i] > 0) { 2082 j = copy_node(i); 2083 if (p == null) { 2084 q = j; 2085 } else { 2086 vlink(p) = j; 2087 } 2088 p = j; 2089 } 2090 } 2091 free(saved_varmem_sizes); 2092#endif 2093 return q; 2094} 2095 2096@ @c 2097void print_node_mem_stats(void) 2098{ 2099 int i, b; 2100 halfword j; 2101 char msg[256]; 2102 char *s; 2103 int free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; 2104 snprintf(msg, 255, " %d words of node memory still in use:", 2105 (int) (var_used + my_prealloc)); 2106 tprint_nl(msg); 2107 s = sprint_node_mem_usage(); 2108 tprint_nl(" "); 2109 tprint(s); 2110 free(s); 2111 tprint(" nodes"); 2112 tprint_nl(" avail lists: "); 2113 b = 0; 2114 for (i = 1; i < MAX_CHAIN_SIZE; i++) { 2115 for (j = free_chain[i]; j != null; j = vlink(j)) 2116 free_chain_counts[i]++; 2117 if (free_chain_counts[i] > 0) { 2118 snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, 2119 (int) free_chain_counts[i]); 2120 tprint(msg); 2121 b = 1; 2122 } 2123 } 2124 print_nlp(); /* newline, if needed */ 2125} 2126 2127/* this belongs in the web but i couldn't find the correct syntactic place */ 2128 2129halfword new_span_node(halfword n, int s, scaled w) 2130{ 2131 halfword p = new_node(span_node, 0); 2132 span_link(p) = n; 2133 span_span(p) = s; 2134 width(p) = w; 2135 return p; 2136} 2137 2138@* Attribute stuff. 2139 2140@c 2141static halfword new_attribute_node(unsigned int i, int v) 2142{ 2143 register halfword r = get_node(attribute_node_size); 2144 type(r) = attribute_node; 2145 attribute_id(r) = (halfword) i; 2146 attribute_value(r) = v; 2147 /* not used but nicer in print */ 2148 subtype(r) = 0; 2149 return r; 2150} 2151 2152@ @c 2153halfword copy_attribute_list(halfword n) 2154{ 2155 halfword q = get_node(attribute_node_size); 2156 register halfword p = q; 2157 type(p) = attribute_list_node; 2158 attr_list_ref(p) = 0; 2159 n = vlink(n); 2160 while (n != null) { 2161 register halfword r = get_node(attribute_node_size); 2162 /* the link will be fixed automatically in the next loop */ 2163 (void) memcpy((void *) (varmem + r), (void *) (varmem + n), 2164 (sizeof(memory_word) * attribute_node_size)); 2165 vlink(p) = r; 2166 p = r; 2167 n = vlink(n); 2168 } 2169 return q; 2170} 2171 2172@ @c 2173void update_attribute_cache(void) 2174{ 2175 halfword p; 2176 register int i; 2177 attr_list_cache = get_node(attribute_node_size); 2178 type(attr_list_cache) = attribute_list_node; 2179 attr_list_ref(attr_list_cache) = 0; 2180 p = attr_list_cache; 2181 for (i = 0; i <= max_used_attr; i++) { 2182 register int v = attribute(i); 2183 if (v > UNUSED_ATTRIBUTE) { 2184 register halfword r = new_attribute_node((unsigned) i, v); 2185 vlink(p) = r; 2186 p = r; 2187 } 2188 } 2189 if (vlink(attr_list_cache) == null) { 2190 free_node(attr_list_cache, attribute_node_size); 2191 attr_list_cache = null; 2192 } 2193 return; 2194} 2195 2196@ @c 2197void build_attribute_list(halfword b) 2198{ 2199 if (max_used_attr >= 0) { 2200 if (attr_list_cache == cache_disabled|| attr_list_cache == null) { 2201 update_attribute_cache(); 2202 if (attr_list_cache == null) 2203 return; 2204 } 2205 attr_list_ref(attr_list_cache)++; 2206 node_attr(b) = attr_list_cache; 2207#ifdef DEBUG 2208 fprintf(DEBUG_OUT, "Added attrlist (%d) to node %d (count=%d)\n", 2209 node_attr(b), b, attr_list_ref(attr_list_cache)); 2210#endif 2211 } 2212} 2213 2214 2215@ @c 2216halfword current_attribute_list(void) 2217{ 2218 if (max_used_attr >= 0) { 2219 if (attr_list_cache == cache_disabled) { 2220 update_attribute_cache(); 2221 } 2222 return attr_list_cache ; 2223 } 2224 return null ; 2225} 2226 2227 2228@ @c 2229void reassign_attribute(halfword n, halfword new) 2230{ 2231 halfword old; 2232 old = node_attr(n); 2233 if (new == null) { 2234 /* there is nothing to assign but we need to check for an old value */ 2235 if (old != null) 2236 delete_attribute_ref(old); /* also nulls attr field of n */ 2237 } else if (old == null) { 2238 /* nothing is assigned so we just do that now */ 2239 assign_attribute_ref(n,new); 2240 } else if (old != new) { 2241 /* something is assigned so we need to clean up and assign then */ 2242 delete_attribute_ref(old); 2243 assign_attribute_ref(n,new); 2244 } 2245 /* else: same value so there is no need to assign and change the refcount */ 2246 node_attr(n) = new ; 2247} 2248 2249 2250 2251@ @c 2252void delete_attribute_ref(halfword b) 2253{ 2254 if (b != null) { 2255 assert(type(b) == attribute_list_node); 2256 attr_list_ref(b)--; 2257#ifdef DEBUG 2258 fprintf(DEBUG_OUT, "Removed attrlistref (%d) (count=%d)\n", b, 2259 attr_list_ref(b)); 2260#endif 2261 if (attr_list_ref(b) == 0) { 2262 if (b == attr_list_cache) 2263 attr_list_cache = cache_disabled; 2264 free_node_chain(b, attribute_node_size); 2265 } 2266 /* maintain sanity */ 2267 if (attr_list_ref(b) < 0) 2268 attr_list_ref(b) = 0; 2269 } 2270} 2271 2272@ |p| is an attr list head, or zero 2273@c 2274halfword do_set_attribute(halfword p, int i, int val) 2275{ 2276 register halfword q; 2277 register int j = 0; 2278 if (p == null) { /* add a new head \& node */ 2279 q = get_node(attribute_node_size); 2280 type(q) = attribute_list_node; 2281 attr_list_ref(q) = 1; 2282 p = new_attribute_node((unsigned) i, val); 2283 vlink(q) = p; 2284 return q; 2285 } 2286 q = p; 2287 assert(vlink(p) != null); 2288 while (vlink(p) != null) { 2289 int t = attribute_id(vlink(p)); 2290 if (t == i && attribute_value(vlink(p)) == val) 2291 return q; /* no need to do anything */ 2292 if (t >= i) 2293 break; 2294 j++; 2295 p = vlink(p); 2296 } 2297 2298 p = q; 2299 while (j-- > 0) 2300 p = vlink(p); 2301 if (attribute_id(vlink(p)) == i) { 2302 attribute_value(vlink(p)) = val; 2303 } else { /* add a new node */ 2304 halfword r = new_attribute_node((unsigned) i, val); 2305 vlink(r) = vlink(p); 2306 vlink(p) = r; 2307 } 2308 return q; 2309} 2310 2311@ @c 2312void set_attribute(halfword n, int i, int val) 2313{ 2314 register halfword p; 2315 register int j = 0; 2316 /* not all nodes can have an attribute list */ 2317 if (!nodetype_has_attributes(type(n))) 2318 return; 2319 /* if we have no list, we create one and quit */ 2320 p = node_attr(n); 2321 if (p == null) { /* add a new head \& node */ 2322 p = get_node(attribute_node_size); 2323 type(p) = attribute_list_node; 2324 attr_list_ref(p) = 1; 2325 node_attr(n) = p; 2326 p = new_attribute_node((unsigned) i, val); 2327 vlink(node_attr(n)) = p; 2328 return; 2329 } 2330 /* we check if we have this attribute already and quit if the value stays the same */ 2331 assert(vlink(p) != null); 2332 while (vlink(p) != null) { 2333 int t = attribute_id(vlink(p)); 2334 if (t == i && attribute_value(vlink(p)) == val) 2335 return; 2336 if (t >= i) 2337 break; 2338 j++; 2339 p = vlink(p); 2340 } 2341 /* j has now the position (if found) .. we assume a sorted list ! */ 2342 p = node_attr(n); 2343 2344 if (attr_list_ref(p) == 0 ) { 2345 /* the list is invalid i.e. freed already */ 2346 fprintf(stdout,"Node %d has an attribute list that is free already\n",(int) n); 2347 /* the still dangling list gets ref count 1 */ 2348 attr_list_ref(p) = 1; 2349 } else if (attr_list_ref(p) == 1) { 2350 /* this can really happen HH-LS */ 2351 if (p == attr_list_cache) { 2352 /* we can invalidate the cache setting */ 2353 /* attr_list_cache = cache_disabled */ 2354 /* or save the list, as done below */ 2355 p = copy_attribute_list(p); 2356 node_attr(n) = p; 2357 /* the copied list gets ref count 1 */ 2358 attr_list_ref(p) = 1; 2359 } 2360 } else { 2361 /* the list is used multiple times so we make a copy */ 2362 p = copy_attribute_list(p); 2363 /* we decrement the ref count or the original */ 2364 delete_attribute_ref(node_attr(n)); 2365 node_attr(n) = p; 2366 /* the copied list gets ref count 1 */ 2367 attr_list_ref(p) = 1; 2368 } 2369 2370 2371 /* we go to position j in the list */ 2372 while (j-- > 0) 2373 p = vlink(p); 2374 /* if we have a hit we just set the value otherwise we add a new node */ 2375 if (attribute_id(vlink(p)) == i) { 2376 attribute_value(vlink(p)) = val; 2377 } else { /* add a new node */ 2378 halfword r = new_attribute_node((unsigned) i, val); 2379 vlink(r) = vlink(p); 2380 vlink(p) = r; 2381 } 2382 return; 2383} 2384 2385 2386@ @c 2387int unset_attribute(halfword n, int i, int val) 2388{ 2389 register halfword p; 2390 register int t; 2391 register int j = 0; 2392 2393 if (!nodetype_has_attributes(type(n))) 2394 return null; 2395 p = node_attr(n); 2396 if (p == null) 2397 return UNUSED_ATTRIBUTE; 2398 if (attr_list_ref(p) == 0) { 2399 fprintf(stdout, 2400 "Node %d has an attribute list that is free already\n", 2401 (int) n); 2402 return UNUSED_ATTRIBUTE; 2403 } 2404 assert(vlink(p) != null); 2405 while (vlink(p) != null) { 2406 t = attribute_id(vlink(p)); 2407 if (t > i) 2408 return UNUSED_ATTRIBUTE; 2409 if (t == i) { 2410 p = vlink(p); 2411 break; 2412 } 2413 j++; 2414 p = vlink(p); 2415 } 2416 if (attribute_id(p) != i) 2417 return UNUSED_ATTRIBUTE; 2418 /* if we are still here, the attribute exists */ 2419 p = node_attr(n); 2420 if (attr_list_ref(p) > 1 || p == attr_list_cache) { 2421 halfword q = copy_attribute_list(p); 2422 if (attr_list_ref(p) > 1) { 2423 delete_attribute_ref(node_attr(n)); 2424 } 2425 attr_list_ref(q) = 1; 2426 node_attr(n) = q; 2427 } 2428 p = vlink(node_attr(n)); 2429 while (j-- > 0) 2430 p = vlink(p); 2431 t = attribute_value(p); 2432 if (val == UNUSED_ATTRIBUTE || t == val) { 2433 attribute_value(p) = UNUSED_ATTRIBUTE; 2434 } 2435 return t; 2436} 2437 2438@ @c 2439int has_attribute(halfword n, int i, int val) 2440{ 2441 register halfword p; 2442 if (!nodetype_has_attributes(type(n))) 2443 return UNUSED_ATTRIBUTE; 2444 p = node_attr(n); 2445 if (p == null || vlink(p) == null) 2446 return UNUSED_ATTRIBUTE; 2447 p = vlink(p); 2448 while (p != null) { 2449 if (attribute_id(p) == i) { 2450 int ret = attribute_value(p); 2451 if (val == UNUSED_ATTRIBUTE || val == ret) 2452 return ret; 2453 return UNUSED_ATTRIBUTE; 2454 } else if (attribute_id(p) > i) { 2455 return UNUSED_ATTRIBUTE; 2456 } 2457 p = vlink(p); 2458 } 2459 return UNUSED_ATTRIBUTE; 2460} 2461 2462@ @c 2463void print_short_node_contents(halfword p) 2464{ 2465 switch (type(p)) { 2466 case hlist_node: 2467 case vlist_node: 2468 case ins_node: 2469 case whatsit_node: 2470 case mark_node: 2471 case adjust_node: 2472 case unset_node: 2473 print_char('['); 2474 print_char(']'); 2475 break; 2476 case rule_node: 2477 print_char('|'); 2478 break; 2479 case glue_node: 2480 if (glue_ptr(p) != zero_glue) 2481 print_char(' '); 2482 break; 2483 case math_node: 2484 print_char('$'); 2485 break; 2486 case disc_node: 2487 short_display(vlink(pre_break(p))); 2488 short_display(vlink(post_break(p))); 2489 break; 2490 default: 2491 assert(1); 2492 break; 2493 } 2494} 2495 2496 2497@ @c 2498static void show_pdftex_whatsit_rule_spec(int p) 2499{ 2500 tprint("("); 2501 print_rule_dimen(height(p)); 2502 print_char('+'); 2503 print_rule_dimen(depth(p)); 2504 tprint(")x"); 2505 print_rule_dimen(width(p)); 2506} 2507 2508 2509 2510@ Each new type of node that appears in our data structure must be capable 2511of being displayed, copied, destroyed, and so on. The routines that we 2512need for write-oriented whatsits are somewhat like those for mark nodes; 2513other extensions might, of course, involve more subtlety here. 2514 2515@c 2516static void print_write_whatsit(const char *s, pointer p) 2517{ 2518 tprint_esc(s); 2519 if (write_stream(p) < 16) 2520 print_int(write_stream(p)); 2521 else if (write_stream(p) == 16) 2522 print_char('*'); 2523 else 2524 print_char('-'); 2525} 2526 2527 2528@ @c 2529static void show_whatsit_node(int p) 2530{ 2531 switch (subtype(p)) { 2532 case open_node: 2533 print_write_whatsit("openout", p); 2534 print_char('='); 2535 print_file_name(open_name(p), open_area(p), open_ext(p)); 2536 break; 2537 case write_node: 2538 print_write_whatsit("write", p); 2539 print_mark(write_tokens(p)); 2540 break; 2541 case close_node: 2542 print_write_whatsit("closeout", p); 2543 break; 2544 case special_node: 2545 tprint_esc("special"); 2546 print_mark(write_tokens(p)); 2547 break; 2548 case dir_node: 2549 if (dir_dir(p) < 0) { 2550 tprint_esc("enddir"); 2551 print_char(' '); 2552 print_dir(dir_dir(p) + 64); 2553 } else { 2554 tprint_esc("begindir"); 2555 print_char(' '); 2556 print_dir(dir_dir(p)); 2557 } 2558 break; 2559 case local_par_node: 2560 tprint_esc("whatsit"); 2561 append_char('.'); 2562 print_ln(); 2563 print_current_string(); 2564 tprint_esc("localinterlinepenalty"); 2565 print_char('='); 2566 print_int(local_pen_inter(p)); 2567 print_ln(); 2568 print_current_string(); 2569 tprint_esc("localbrokenpenalty"); 2570 print_char('='); 2571 print_int(local_pen_broken(p)); 2572 print_ln(); 2573 print_current_string(); 2574 tprint_esc("localleftbox"); 2575 if (local_box_left(p) == null) { 2576 tprint("=null"); 2577 } else { 2578 append_char('.'); 2579 show_node_list(local_box_left(p)); 2580 decr(cur_length); 2581 } 2582 print_ln(); 2583 print_current_string(); 2584 tprint_esc("localrightbox"); 2585 if (local_box_right(p) == null) { 2586 tprint("=null"); 2587 } else { 2588 append_char('.'); 2589 show_node_list(local_box_right(p)); 2590 decr(cur_length); 2591 } 2592 decr(cur_length); 2593 break; 2594 case pdf_literal_node: 2595 show_pdf_literal(p); 2596 break; 2597 case pdf_colorstack_node: 2598 tprint_esc("pdfcolorstack "); 2599 print_int(pdf_colorstack_stack(p)); 2600 switch (pdf_colorstack_cmd(p)) { 2601 case colorstack_set: 2602 tprint(" set "); 2603 break; 2604 case colorstack_push: 2605 tprint(" push "); 2606 break; 2607 case colorstack_pop: 2608 tprint(" pop"); 2609 break; 2610 case colorstack_current: 2611 tprint(" current"); 2612 break; 2613 default: 2614 confusion("pdfcolorstack"); 2615 break; 2616 } 2617 if (pdf_colorstack_cmd(p) <= colorstack_data) 2618 print_mark(pdf_colorstack_data(p)); 2619 break; 2620 case pdf_setmatrix_node: 2621 tprint_esc("pdfsetmatrix"); 2622 print_mark(pdf_setmatrix_data(p)); 2623 break; 2624 case pdf_save_node: 2625 tprint_esc("pdfsave"); 2626 break; 2627 case pdf_restore_node: 2628 tprint_esc("pdfrestore"); 2629 break; 2630 case cancel_boundary_node: 2631 tprint_esc("noboundary"); 2632 break; 2633 case late_lua_node: 2634 show_late_lua(p); 2635 break; 2636 case close_lua_node: 2637 tprint_esc("closelua"); 2638 print_int(late_lua_reg(p)); 2639 break; 2640 case pdf_refobj_node: 2641 tprint_esc("pdfrefobj"); 2642 if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) { 2643 if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { 2644 tprint(" attr"); 2645 lua_rawgeti(Luas, LUA_REGISTRYINDEX, 2646 obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p))); 2647 print_char(' '); 2648 tprint((const char *) lua_tostring(Luas, -1)); 2649 lua_pop(Luas, 1); 2650 } 2651 tprint(" stream"); 2652 } 2653 if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p))) 2654 tprint(" file"); 2655 if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { 2656 lua_rawgeti(Luas, LUA_REGISTRYINDEX, 2657 obj_obj_data(static_pdf, pdf_obj_objnum(p))); 2658 print_char(' '); 2659 tprint((const char *) lua_tostring(Luas, -1)); 2660 lua_pop(Luas, 1); 2661 } 2662 break; 2663 case pdf_refxform_node: 2664 case pdf_refximage_node: 2665 if (subtype(p) == pdf_refxform_node) 2666 tprint_esc("pdfrefxform"); 2667 else 2668 tprint_esc("pdfrefximage"); 2669 tprint("("); 2670 print_scaled(height(p)); 2671 print_char('+'); 2672 print_scaled(depth(p)); 2673 tprint(")x"); 2674 print_scaled(width(p)); 2675 break; 2676 case pdf_annot_node: 2677 tprint_esc("pdfannot"); 2678 show_pdftex_whatsit_rule_spec(p); 2679 print_mark(pdf_annot_data(p)); 2680 break; 2681 case pdf_start_link_node: 2682 tprint_esc("pdfstartlink"); 2683 show_pdftex_whatsit_rule_spec(p); 2684 if (pdf_link_attr(p) != null) { 2685 tprint(" attr"); 2686 print_mark(pdf_link_attr(p)); 2687 } 2688 tprint(" action"); 2689 if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) { 2690 tprint(" user"); 2691 print_mark(pdf_action_tokens(pdf_link_action(p))); 2692 return; 2693 } 2694 if (pdf_action_file(pdf_link_action(p)) != null) { 2695 tprint(" file"); 2696 print_mark(pdf_action_file(pdf_link_action(p))); 2697 } 2698 switch (pdf_action_type(pdf_link_action(p))) { 2699 case pdf_action_goto: 2700 if (pdf_action_named_id(pdf_link_action(p)) > 0) { 2701 tprint(" goto name"); 2702 print_mark(pdf_action_id(pdf_link_action(p))); 2703 } else { 2704 tprint(" goto num"); 2705 print_int(pdf_action_id(pdf_link_action(p))); 2706 } 2707 break; 2708 case pdf_action_page: 2709 tprint(" page"); 2710 print_int(pdf_action_id(pdf_link_action(p))); 2711 print_mark(pdf_action_tokens(pdf_link_action(p))); 2712 break; 2713 case pdf_action_thread: 2714 if (pdf_action_named_id(pdf_link_action(p)) > 0) { 2715 tprint(" thread name"); 2716 print_mark(pdf_action_id(pdf_link_action(p))); 2717 } else { 2718 tprint(" thread num"); 2719 print_int(pdf_action_id(pdf_link_action(p))); 2720 } 2721 break; 2722 default: 2723 pdf_error("displaying", "unknown action type"); 2724 break; 2725 } 2726 break; 2727 case pdf_end_link_node: 2728 tprint_esc("pdfendlink"); 2729 break; 2730 case pdf_dest_node: 2731 tprint_esc("pdfdest"); 2732 if (pdf_dest_named_id(p) > 0) { 2733 tprint(" name"); 2734 print_mark(pdf_dest_id(p)); 2735 } else { 2736 tprint(" num"); 2737 print_int(pdf_dest_id(p)); 2738 } 2739 print_char(' '); 2740 switch (pdf_dest_type(p)) { 2741 case pdf_dest_xyz: 2742 tprint("xyz"); 2743 if (pdf_dest_xyz_zoom(p) != null) { 2744 tprint(" zoom"); 2745 print_int(pdf_dest_xyz_zoom(p)); 2746 } 2747 break; 2748 case pdf_dest_fitbh: 2749 tprint("fitbh"); 2750 break; 2751 case pdf_dest_fitbv: 2752 tprint("fitbv"); 2753 break; 2754 case pdf_dest_fitb: 2755 tprint("fitb"); 2756 break; 2757 case pdf_dest_fith: 2758 tprint("fith"); 2759 break; 2760 case pdf_dest_fitv: 2761 tprint("fitv"); 2762 break; 2763 case pdf_dest_fitr: 2764 tprint("fitr"); 2765 show_pdftex_whatsit_rule_spec(p); 2766 break; 2767 case pdf_dest_fit: 2768 tprint("fit"); 2769 break; 2770 default: 2771 tprint("unknown!"); 2772 break; 2773 } 2774 break; 2775 case pdf_thread_node: 2776 case pdf_start_thread_node: 2777 if (subtype(p) == pdf_thread_node) 2778 tprint_esc("pdfthread"); 2779 else 2780 tprint_esc("pdfstartthread"); 2781 tprint("("); 2782 print_rule_dimen(height(p)); 2783 print_char('+'); 2784 print_rule_dimen(depth(p)); 2785 tprint(")x"); 2786 print_rule_dimen(width(p)); 2787 if (pdf_thread_attr(p) != null) { 2788 tprint(" attr"); 2789 print_mark(pdf_thread_attr(p)); 2790 } 2791 if (pdf_thread_named_id(p) > 0) { 2792 tprint(" name"); 2793 print_mark(pdf_thread_id(p)); 2794 } else { 2795 tprint(" num"); 2796 print_int(pdf_thread_id(p)); 2797 } 2798 break; 2799 case pdf_end_thread_node: 2800 tprint_esc("pdfendthread"); 2801 break; 2802 case pdf_save_pos_node: 2803 tprint_esc("pdfsavepos"); 2804 break; 2805 case user_defined_node: 2806 tprint_esc("whatsit"); 2807 print_int(user_node_id(p)); 2808 print_char('='); 2809 switch (user_node_type(p)) { 2810 case 'a': 2811 tprint("<>"); 2812 break; 2813 case 'n': 2814 tprint("["); 2815 show_node_list(user_node_value(p)); 2816 tprint("]"); 2817 break; 2818 case 's': 2819 print_char('"'); 2820 print(user_node_value(p)); 2821 print_char('"'); 2822 break; 2823 case 't': 2824 print_mark(user_node_value(p)); 2825 break; 2826 default: /* only 'd' */ 2827 print_int(user_node_value(p)); 2828 break; 2829 } 2830 break; 2831 default: 2832 tprint("whatsit?"); 2833 break; 2834 } 2835} 2836 2837 2838 2839@ Now we are ready for |show_node_list| itself. This procedure has been 2840 written to be ``extra robust'' in the sense that it should not crash or get 2841 into a loop even if the data structures have been messed up by bugs in 2842 the rest of the program. You can safely call its parent routine 2843 |show_box(p)| for arbitrary values of |p| when you are debugging \TeX. 2844 However, in the presence of bad data, the procedure may 2845 fetch a |memory_word| whose variant is different from the way it was stored; 2846 for example, it might try to read |mem[p].hh| when |mem[p]| 2847 contains a scaled integer, if |p| is a pointer that has been 2848 clobbered or chosen at random. 2849 2850 2851@ |str_room| need not be checked; see |show_box| 2852 2853@ Recursive calls on |show_node_list| therefore use the following pattern: 2854@c 2855#define node_list_display(A) do { \ 2856 append_char('.'); \ 2857 show_node_list(A); \ 2858 flush_char(); \ 2859} while (0) 2860 2861void show_node_list(int p) 2862{ /* prints a node list symbolically */ 2863 int n; /* the number of items already printed at this level */ 2864 real g; /* a glue ratio, as a floating point number */ 2865 if ((int) cur_length > depth_threshold) { 2866 if (p > null) 2867 tprint(" []"); /* indicate that there's been some truncation */ 2868 return; 2869 } 2870 n = 0; 2871 while (p != null) { 2872 print_ln(); 2873 print_current_string(); /* display the nesting history */ 2874 if (int_par(tracing_online_code) < -2) 2875 print_int(p); 2876 incr(n); 2877 if (n > breadth_max) { /* time to stop */ 2878 tprint("etc."); 2879 return; 2880 } 2881 /* Display node |p| */ 2882 if (is_char_node(p)) { 2883 print_font_and_char(p); 2884 if (is_ligature(p)) { 2885 /* Display ligature |p|; */ 2886 tprint(" (ligature "); 2887 if (is_leftboundary(p)) 2888 print_char('|'); 2889 font_in_short_display = font(p); 2890 short_display(lig_ptr(p)); 2891 if (is_rightboundary(p)) 2892 print_char('|'); 2893 print_char(')'); 2894 } 2895 } else { 2896 switch (type(p)) { 2897 case hlist_node: 2898 case vlist_node: 2899 case unset_node: 2900 /* Display box |p|; */ 2901 if (type(p) == hlist_node) 2902 tprint_esc("h"); 2903 else if (type(p) == vlist_node) 2904 tprint_esc("v"); 2905 else 2906 tprint_esc("unset"); 2907 tprint("box("); 2908 print_scaled(height(p)); 2909 print_char('+'); 2910 print_scaled(depth(p)); 2911 tprint(")x"); 2912 print_scaled(width(p)); 2913 if (type(p) == unset_node) { 2914 /* Display special fields of the unset node |p|; */ 2915 if (span_count(p) != min_quarterword) { 2916 tprint(" ("); 2917 print_int(span_count(p) + 1); 2918 tprint(" columns)"); 2919 } 2920 if (glue_stretch(p) != 0) { 2921 tprint(", stretch "); 2922 print_glue(glue_stretch(p), glue_order(p), NULL); 2923 } 2924 if (glue_shrink(p) != 0) { 2925 tprint(", shrink "); 2926 print_glue(glue_shrink(p), glue_sign(p), NULL); 2927 } 2928 } else { 2929 /* Display the value of |glue_set(p)| */ 2930 /* The code will have to change in this place if |glue_ratio| is 2931 a structured type instead of an ordinary |real|. Note that this routine 2932 should avoid arithmetic errors even if the |glue_set| field holds an 2933 arbitrary random value. The following code assumes that a properly 2934 formed nonzero |real| number has absolute value $2^{20}$ or more when 2935 it is regarded as an integer; this precaution was adequate to prevent 2936 floating point underflow on the author's computer. 2937 */ 2938 2939 g = (real) (glue_set(p)); 2940 if ((g != 0.0) && (glue_sign(p) != normal)) { 2941 tprint(", glue set "); 2942 if (glue_sign(p) == shrinking) 2943 tprint("- "); 2944 if (g > 20000.0 || g < -20000.0) { 2945 if (g > 0.0) 2946 print_char('>'); 2947 else 2948 tprint("< -"); 2949 print_glue(20000 * unity, glue_order(p), NULL); 2950 } else { 2951 print_glue(round(unity * g), glue_order(p), NULL); 2952 } 2953 } 2954 2955 if (shift_amount(p) != 0) { 2956 tprint(", shifted "); 2957 print_scaled(shift_amount(p)); 2958 } 2959 tprint(", direction "); 2960 print_dir(box_dir(p)); 2961 } 2962 node_list_display(list_ptr(p)); /* recursive call */ 2963 break; 2964 case rule_node: 2965 /* Display rule |p|; */ 2966 tprint_esc("rule("); 2967 print_rule_dimen(height(p)); 2968 print_char('+'); 2969 print_rule_dimen(depth(p)); 2970 tprint(")x"); 2971 print_rule_dimen(width(p)); 2972 break; 2973 case ins_node: 2974 /* Display insertion |p|; */ 2975 tprint_esc("insert"); 2976 print_int(subtype(p)); 2977 tprint(", natural size "); 2978 print_scaled(height(p)); 2979 tprint("; split("); 2980 print_spec(split_top_ptr(p), NULL); 2981 print_char(','); 2982 print_scaled(depth(p)); 2983 tprint("); float cost "); 2984 print_int(float_cost(p)); 2985 node_list_display(ins_ptr(p)); /* recursive call */ 2986 break; 2987 case whatsit_node: 2988 show_whatsit_node(p); 2989 break; 2990 case glue_node: 2991 /* Display glue |p|; */ 2992 if (subtype(p) >= a_leaders) { 2993 /* Display leaders |p|; */ 2994 tprint_esc(""); 2995 switch (subtype(p)) { 2996 case a_leaders: 2997 break; 2998 case c_leaders: 2999 print_char('c'); 3000 break; 3001 case x_leaders: 3002 print_char('x'); 3003 break; 3004 case g_leaders: 3005 print_char('g'); 3006 break; 3007 default: 3008 assert(0); 3009 } 3010 tprint("leaders "); 3011 print_spec(glue_ptr(p), NULL); 3012 node_list_display(leader_ptr(p)); /* recursive call */ 3013 } else { 3014 tprint_esc("glue"); 3015 if (subtype(p) != normal) { 3016 print_char('('); 3017 if ((subtype(p) - 1) < thin_mu_skip_code) { 3018 print_cmd_chr(assign_glue_cmd, 3019 glue_base + (subtype(p) - 1)); 3020 } else if (subtype(p) < cond_math_glue) { 3021 print_cmd_chr(assign_mu_glue_cmd, 3022 glue_base + (subtype(p) - 1)); 3023 } else if (subtype(p) == cond_math_glue) { 3024 tprint_esc("nonscript"); 3025 } else { 3026 tprint_esc("mskip"); 3027 } 3028 print_char(')'); 3029 } 3030 if (subtype(p) != cond_math_glue) { 3031 print_char(' '); 3032 if (subtype(p) < cond_math_glue) 3033 print_spec(glue_ptr(p), NULL); 3034 else 3035 print_spec(glue_ptr(p), "mu"); 3036 } 3037 } 3038 break; 3039 case margin_kern_node: 3040 tprint_esc("kern"); 3041 print_scaled(width(p)); 3042 if (subtype(p) == left_side) 3043 tprint(" (left margin)"); 3044 else 3045 tprint(" (right margin)"); 3046 break; 3047 case kern_node: 3048 /* Display kern |p|; */ 3049 /* An ``explicit'' kern value is indicated implicitly by an explicit space. */ 3050 if (subtype(p) != mu_glue) { 3051 tprint_esc("kern"); 3052 if (subtype(p) != normal) 3053 print_char(' '); 3054 print_scaled(width(p)); 3055 if (subtype(p) == acc_kern) 3056 tprint(" (for accent)"); 3057 } else { 3058 tprint_esc("mkern"); 3059 print_scaled(width(p)); 3060 tprint("mu"); 3061 } 3062 break; 3063 case math_node: 3064 /* Display math node |p|; */ 3065 tprint_esc("math"); 3066 if (subtype(p) == before) 3067 tprint("on"); 3068 else 3069 tprint("off"); 3070 if (width(p) != 0) { 3071 tprint(", surrounded "); 3072 print_scaled(width(p)); 3073 } 3074 break; 3075 case penalty_node: 3076 /* Display penalty |p|; */ 3077 tprint_esc("penalty "); 3078 print_int(penalty(p)); 3079 break; 3080 case disc_node: 3081 /* Display discretionary |p|; */ 3082 /* The |post_break| list of a discretionary node is indicated by a prefixed 3083 `\.{\char'174}' instead of the `\..' before the |pre_break| list. */ 3084 tprint_esc("discretionary"); 3085 if (vlink(no_break(p)) != null) { 3086 tprint(" replacing "); 3087 node_list_display(vlink(no_break(p))); 3088 } 3089 node_list_display(vlink(pre_break(p))); /* recursive call */ 3090 append_char('|'); 3091 show_node_list(vlink(post_break(p))); 3092 flush_char(); /* recursive call */ 3093 break; 3094 case mark_node: 3095 /* Display mark |p|; */ 3096 tprint_esc("mark"); 3097 if (mark_class(p) != 0) { 3098 print_char('s'); 3099 print_int(mark_class(p)); 3100 } 3101 print_mark(mark_ptr(p)); 3102 break; 3103 case adjust_node: 3104 /* Display adjustment |p|; */ 3105 tprint_esc("vadjust"); 3106 if (adjust_pre(p) != 0) 3107 tprint(" pre "); 3108 node_list_display(adjust_ptr(p)); /* recursive call */ 3109 break; 3110 case glue_spec_node: 3111 tprint("<glue_spec "); 3112 print_spec(p, NULL); 3113 tprint(">"); 3114 break; 3115 default: 3116 show_math_node(p); 3117 break; 3118 } 3119 } 3120 p = vlink(p); 3121 } 3122} 3123 3124@ This routine finds the 'base' width of a horizontal box, using the same logic 3125 that \TeX82 used for \.{\\predisplaywidth} */ 3126 3127@c 3128pointer actual_box_width(pointer r, scaled base_width) 3129{ 3130 scaled w; /* calculated |size| */ 3131 pointer p; /* current node when calculating |pre_display_size| */ 3132 pointer q; /* glue specification when calculating |pre_display_size| */ 3133 scaled d; /* increment to |v| */ 3134 scaled v; /* |w| plus possible glue amount */ 3135 w = -max_dimen; 3136 v = shift_amount(r) + base_width; 3137 p = list_ptr(r); 3138 while (p != null) { 3139 if (is_char_node(p)) { 3140 d = glyph_width(p); 3141 goto found; 3142 } 3143 switch (type(p)) { 3144 case hlist_node: 3145 case vlist_node: 3146 case rule_node: 3147 d = width(p); 3148 goto found; 3149 break; 3150 case margin_kern_node: 3151 d = width(p); 3152 break; 3153 case kern_node: 3154 d = width(p); 3155 break; 3156 case math_node: 3157 d = surround(p); 3158 break; 3159 case glue_node: 3160 /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set| 3161 values, since such values are subject to system-dependent rounding. 3162 System-dependent numbers are not allowed to infiltrate parameters like 3163 |pre_display_size|, since \TeX82 is supposed to make the same decisions on all 3164 machines. 3165 */ 3166 q = glue_ptr(p); 3167 d = width(q); 3168 if (glue_sign(r) == stretching) { 3169 if ((glue_order(r) == stretch_order(q)) 3170 && (stretch(q) != 0)) 3171 v = max_dimen; 3172 } else if (glue_sign(r) == shrinking) { 3173 if ((glue_order(r) == shrink_order(q)) 3174 && (shrink(q) != 0)) 3175 v = max_dimen; 3176 } 3177 if (subtype(p) >= a_leaders) 3178 goto found; 3179 break; 3180 case whatsit_node: 3181 if ((subtype(p) == pdf_refxform_node) 3182 || (subtype(p) == pdf_refximage_node)) 3183 d = width(p); 3184 else 3185 d = 0; 3186 break; 3187 default: 3188 d = 0; 3189 break; 3190 } 3191 if (v < max_dimen) 3192 v = v + d; 3193 goto not_found; 3194 found: 3195 if (v < max_dimen) { 3196 v = v + d; 3197 w = v; 3198 } else { 3199 w = max_dimen; 3200 break; 3201 } 3202 not_found: 3203 p = vlink(p); 3204 } 3205 return w; 3206} 3207 3208 3209@ @c 3210halfword tail_of_list(halfword p) 3211{ 3212 halfword q = p; 3213 while (vlink(q) != null) 3214 q = vlink(q); 3215 return q; 3216} 3217 3218 3219@ |delete_glue_ref| is called when a pointer to a glue 3220 specification is being withdrawn. 3221 3222@c 3223#define fast_delete_glue_ref(A) do { \ 3224 if (glue_ref_count(A)==null) { \ 3225 flush_node(A); \ 3226 } else { \ 3227 decr(glue_ref_count(A)); \ 3228 } \ 3229 } while (0) 3230 3231void delete_glue_ref(halfword p) 3232{ /* |p| points to a glue specification */ 3233 assert(type(p) == glue_spec_node); 3234 fast_delete_glue_ref(p); 3235} 3236 3237@ @c 3238int var_used; 3239halfword temp_ptr; /* a pointer variable for occasional emergency use */ 3240 3241 3242@ Attribute lists need two extra globals to increase processing efficiency. 3243|max_used_attr| limits the test loop that checks for set attributes, and 3244|attr_list_cache| contains a pointer to an already created attribute list. It is 3245set to the special value |cache_disabled| when the current value can no longer be 3246trusted: after an assignment to an attribute register, and after a group has 3247ended. 3248 3249@c 3250int max_used_attr; /* maximum assigned attribute id */ 3251halfword attr_list_cache; 3252 3253@ From the computer's standpoint, \TeX's chief mission is to create 3254horizontal and vertical lists. We shall now investigate how the elements 3255of these lists are represented internally as nodes in the dynamic memory. 3256 3257A horizontal or vertical list is linked together by |link| fields in 3258the first word of each node. Individual nodes represent boxes, glue, 3259penalties, or special things like discretionary hyphens; because of this 3260variety, some nodes are longer than others, and we must distinguish different 3261kinds of nodes. We do this by putting a `|type|' field in the first word, 3262together with the link and an optional `|subtype|'. 3263 3264 3265@ Character nodes appear only in horizontal lists, never in vertical lists. 3266 3267An |hlist_node| stands for a box that was made from a horizontal list. 3268Each |hlist_node| is seven words long, and contains the following fields 3269(in addition to the mandatory |type| and |link|, which we shall not 3270mention explicitly when discussing the other node types): The |height| and 3271|width| and |depth| are scaled integers denoting the dimensions of the 3272box. There is also a |shift_amount| field, a scaled integer indicating 3273how much this box should be lowered (if it appears in a horizontal list), 3274or how much it should be moved to the right (if it appears in a vertical 3275list). There is a |list_ptr| field, which points to the beginning of the 3276list from which this box was fabricated; if |list_ptr| is |null|, the box 3277is empty. Finally, there are three fields that represent the setting of 3278the glue: |glue_set(p)| is a word of type |glue_ratio| that represents 3279the proportionality constant for glue setting; |glue_sign(p)| is 3280|stretching| or |shrinking| or |normal| depending on whether or not the 3281glue should stretch or shrink or remain rigid; and |glue_order(p)| 3282specifies the order of infinity to which glue setting applies (|normal|, 3283|sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used. 3284 3285@ The |new_null_box| function returns a pointer to an |hlist_node| in 3286which all subfields have the values corresponding to `\.{\\hbox\{\}}'. 3287The |subtype| field is set to |min_quarterword|, since that's the desired 3288|span_count| value if this |hlist_node| is changed to an |unset_node|. 3289 3290@c 3291halfword new_null_box(void) 3292{ /* creates a new box node */ 3293 halfword p; /* the new node */ 3294 p = new_node(hlist_node, min_quarterword); 3295 box_dir(p) = text_direction; 3296 return p; 3297} 3298 3299 3300@ A |vlist_node| is like an |hlist_node| in all respects except that it 3301contains a vertical list. 3302 3303 3304@ A |rule_node| stands for a solid black rectangle; it has |width|, 3305|depth|, and |height| fields just as in an |hlist_node|. However, if 3306any of these dimensions is $-2^{30}$, the actual value will be determined 3307by running the rule up to the boundary of the innermost enclosing box. 3308This is called a ``running dimension.'' The |width| is never running in 3309an hlist; the |height| and |depth| are never running in a~vlist. 3310 3311@ A new rule node is delivered by the |new_rule| function. It 3312makes all the dimensions ``running,'' so you have to change the 3313ones that are not allowed to run. 3314 3315@c 3316halfword new_rule(void) 3317{ 3318 halfword p; /* the new node */ 3319 p = new_node(rule_node, 0); /* the |subtype| is not used */ 3320 return p; 3321} 3322 3323 3324@ Insertions are represented by |ins_node| records, where the |subtype| 3325indicates the corresponding box number. For example, `\.{\\insert 250}' 3326leads to an |ins_node| whose |subtype| is |250+min_quarterword|. 3327The |height| field of an |ins_node| is slightly misnamed; it actually holds 3328the natural height plus depth of the vertical list being inserted. 3329The |depth| field holds the |split_max_depth| to be used in case this 3330insertion is split, and the |split_top_ptr| points to the corresponding 3331|split_top_skip|. The |float_cost| field holds the |floating_penalty| that 3332will be used if this insertion floats to a subsequent page after a 3333split insertion of the same class. There is one more field, the 3334|ins_ptr|, which points to the beginning of the vlist for the insertion. 3335 3336@ A |mark_node| has a |mark_ptr| field that points to the reference count 3337of a token list that contains the user's \.{\\mark} text. 3338In addition there is a |mark_class| field that contains the mark class. 3339 3340@ An |adjust_node|, which occurs only in horizontal lists, 3341specifies material that will be moved out into the surrounding 3342vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}' 3343operation. The |adjust_ptr| field points to the vlist containing this 3344material. 3345 3346@ A |glyph_node|, which occurs only in horizontal lists, specifies a 3347glyph in a particular font, along with its attribute list. Older 3348versions of \TeX\ could use token memory for characters, because the 3349font,char combination would fit in a single word (both values were 3350required to be strictly less than $2^{16}$). In LuaTeX, room is 3351needed for characters that are larger than that, as well as a pointer 3352to a potential attribute list, and the two displacement values. 3353 3354In turn, that made the node so large that it made sense to merge 3355ligature glyphs as well, as that requires only one extra pointer. A 3356few extra classes of glyph nodes will be introduced later. The 3357unification of all those types makes it easier to manipulate lists of 3358glyphs. The subtype differentiates various glyph kinds. 3359 3360First, here is a function that returns a pointer to a glyph node for a given 3361glyph in a given font. If that glyph doesn't exist, |null| is returned 3362instead. Nodes of this subtype are directly created only for accents 3363and their base (through |make_accent|), and math nucleus items (in the 3364conversion from |mlist| to |hlist|). 3365 3366@c 3367halfword new_glyph(int f, int c) 3368{ 3369 halfword p = null; /* the new node */ 3370 if ((f == 0) || (char_exists(f, c))) { 3371 p = new_glyph_node(); 3372 set_to_glyph(p); 3373 font(p) = f; 3374 character(p) = c; 3375 } 3376 return p; 3377} 3378 3379 3380@ A subset of the glyphs nodes represent ligatures: characters 3381fabricated from the interaction of two or more actual characters. The 3382characters that generated the ligature have not been forgotten, since 3383they are needed for diagnostic messages; the |lig_ptr| field points to 3384a linked list of character nodes for all original characters that have 3385been deleted. (This list might be empty if the characters that 3386generated the ligature were retained in other nodes.) 3387 3388The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if 3389the original source of the ligature included implicit left and/or 3390right boundaries. These nodes are created by the C function |new_ligkern|. 3391 3392A third general type of glyphs could be called a character, as it 3393only appears in lists that are not yet processed by the ligaturing and 3394kerning steps of the program. 3395 3396|main_control| inserts these, and they are later converted to 3397|subtype_normal| by |new_ligkern|. 3398 3399@c 3400quarterword norm_min(int h) 3401{ 3402 if (h <= 0) 3403 return 1; 3404 else if (h >= 255) 3405 return 255; 3406 else 3407 return (quarterword) h; 3408} 3409 3410halfword new_char(int f, int c) 3411{ 3412 halfword p; /* the new node */ 3413 p = new_glyph_node(); 3414 set_to_character(p); 3415 font(p) = f; 3416 character(p) = c; 3417 lang_data(p) = 3418 make_lang_data(uc_hyph, cur_lang, left_hyphen_min, right_hyphen_min); 3419 return p; 3420} 3421 3422 3423@ Left and right ghost glyph nodes are the result of \.{\\leftghost} 3424and \.{\\rightghost}, respectively. They are going to be removed by 3425|new_ligkern|, at the end of which they are no longer needed. 3426 3427@ Here are a few handy helpers used by the list output routines. 3428 3429@c 3430scaled glyph_width(halfword p) 3431{ 3432 scaled w; 3433 w = char_width(font(p), character(p)); 3434 return w; 3435} 3436 3437scaled glyph_height(halfword p) 3438{ 3439 scaled w; 3440 w = char_height(font(p), character(p)) + y_displace(p); 3441 if (w < 0) 3442 w = 0; 3443 return w; 3444} 3445 3446scaled glyph_depth(halfword p) 3447{ 3448 scaled w; 3449 w = char_depth(font(p), character(p)); 3450 if (y_displace(p) > 0) 3451 w = w - y_displace(p); 3452 if (w < 0) 3453 w = 0; 3454 return w; 3455} 3456 3457 3458@ A |disc_node|, which occurs only in horizontal lists, specifies a 3459``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text 3460that starts at |pre_break(p)| will precede the break, the text that starts at 3461|post_break(p)| will follow the break, and text that appears in 3462|no_break(p)| nodes will be ignored. For example, an ordinary 3463discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with 3464|pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|, 3465and |no_break=null|. 3466 3467{TODO: Knuth said: All three of the discretionary texts must be lists 3468that consist entirely of character, kern, box and rule nodes.} 3469 3470If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this 3471break. Otherwise the |hyphen_penalty| will be charged. The texts will 3472actually be substituted into the list by the line-breaking algorithm if it 3473decides to make the break, and the discretionary node will disappear at 3474that time; thus, the output routine sees only discretionaries that were 3475not chosen. 3476 3477@c 3478halfword new_disc(void) 3479{ /* creates an empty |disc_node| */ 3480 halfword p; /* the new node */ 3481 p = new_node(disc_node, 0); 3482 return p; 3483} 3484 3485@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The 3486|subtype| field in its first word says what `\\{whatsit}' it is, and 3487implicitly determines the node size (which must be 2 or more) and the 3488format of the remaining words. When a |whatsit_node| is encountered 3489in a list, special actions are invoked; knowledgeable people who are 3490careful not to mess up the rest of \TeX\ are able to make \TeX\ do new 3491things by adding code at the end of the program. For example, there 3492might be a `\TeX nicolor' extension to specify different colors of ink, 3493@^extensions to \TeX@> 3494and the whatsit node might contain the desired parameters. 3495 3496The present implementation of \TeX\ treats the features associated with 3497`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to 3498illustrate how such routines might be coded. We shall defer further 3499discussion of extensions until the end of this program. 3500 3501@ A |math_node|, which occurs only in horizontal lists, appears before and 3502after mathematical formulas. The |subtype| field is |before| before the 3503formula and |after| after it. There is a |surround| field, which represents 3504the amount of surrounding space inserted by \.{\\mathsurround}. 3505 3506@c 3507halfword new_math(scaled w, int s) 3508{ 3509 halfword p; /* the new node */ 3510 p = new_node(math_node, s); 3511 surround(p) = w; 3512 return p; 3513} 3514 3515@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, 3516|rule_node|, |ins_node|, |mark_node|, |adjust_node|, 3517|disc_node|, |whatsit_node|, and |math_node| are at the low end of the 3518type codes, by permitting a break at glue in a list if and only if the 3519|type| of the previous node is less than |math_node|. Furthermore, a 3520node is discarded after a break if its type is |math_node| or~more. 3521 3522@ A |glue_node| represents glue in a list. However, it is really only 3523a pointer to a separate glue specification, since \TeX\ makes use of the 3524fact that many essentially identical nodes of glue are usually present. 3525If |p| points to a |glue_node|, |glue_ptr(p)| points to 3526another packet of words that specify the stretch and shrink components, etc. 3527 3528Glue nodes also serve to represent leaders; the |subtype| is used to 3529distinguish between ordinary glue (which is called |normal|) and the three 3530kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|). 3531The |leader_ptr| field points to a rule node or to a box node containing the 3532leaders; it is set to |null| in ordinary glue nodes. 3533 3534Many kinds of glue are computed from \TeX's ``skip'' parameters, and 3535it is helpful to know which parameter has led to a particular glue node. 3536Therefore the |subtype| is set to indicate the source of glue, whenever 3537it originated as a parameter. We will be defining symbolic names for the 3538parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|, 3539etc.); it suffices for now to say that the |subtype| of parametric glue 3540will be the same as the parameter number, plus~one. 3541 3542@ In math formulas there are two more possibilities for the |subtype| in a 3543glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu} 3544instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}' 3545feature that cancels the glue node immediately following if it appears 3546in a subscript. 3547 3548@ A glue specification has a halfword reference count in its first word, 3549@^reference counts@> 3550representing |null| plus the number of glue nodes that point to it (less one). 3551Note that the reference count appears in the same position as 3552the |link| field in list nodes; this is the field that is initialized 3553to |null| when a node is allocated, and it is also the field that is flagged 3554by |empty_flag| in empty nodes. 3555 3556Glue specifications also contain three |scaled| fields, for the |width|, 3557|stretch|, and |shrink| dimensions. Finally, there are two one-byte 3558fields called |stretch_order| and |shrink_order|; these contain the 3559orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|) 3560corresponding to the stretch and shrink values. 3561 3562@ Here is a function that returns a pointer to a copy of a glue spec. 3563The reference count in the copy is |null|, because there is assumed 3564to be exactly one reference to the new specification. 3565 3566@c 3567halfword new_spec(halfword p) 3568{ /* duplicates a glue specification */ 3569 halfword q; /* the new spec */ 3570 q = copy_node(p); 3571 glue_ref_count(q) = null; 3572 return q; 3573} 3574 3575@ And here's a function that creates a glue node for a given parameter 3576identified by its code number; for example, 3577|new_param_glue(line_skip_code)| returns a pointer to a glue node for the 3578current \.{\\lineskip}. 3579 3580@c 3581halfword new_param_glue(int n) 3582{ 3583 halfword p; /* the new node */ 3584 halfword q; /* the glue specification */ 3585 p = new_node(glue_node, n + 1); 3586 q = glue_par(n); 3587 glue_ptr(p) = q; 3588 incr(glue_ref_count(q)); 3589 return p; 3590} 3591 3592@ Glue nodes that are more or less anonymous are created by |new_glue|, 3593whose argument points to a glue specification. 3594 3595@c 3596halfword new_glue(halfword q) 3597{ 3598 halfword p; /* the new node */ 3599 p = new_node(glue_node, normal); 3600 glue_ptr(p) = q; 3601 incr(glue_ref_count(q)); 3602 return p; 3603} 3604 3605@ Still another subroutine is needed: This one is sort of a combination 3606of |new_param_glue| and |new_glue|. It creates a glue node for one of 3607the current glue parameters, but it makes a fresh copy of the glue 3608specification, since that specification will probably be subject to change, 3609while the parameter will stay put. The global variable |temp_ptr| is 3610set to the address of the new spec. 3611 3612@c 3613halfword new_skip_param(int n) 3614{ 3615 halfword p; /* the new node */ 3616 temp_ptr = new_spec(glue_par(n)); 3617 p = new_glue(temp_ptr); 3618 glue_ref_count(temp_ptr) = null; 3619 subtype(p) = (quarterword) (n + 1); 3620 return p; 3621} 3622 3623@ A |kern_node| has a |width| field to specify a (normally negative) 3624amount of spacing. This spacing correction appears in horizontal lists 3625between letters like A and V when the font designer said that it looks 3626better to move them closer together or further apart. A kern node can 3627also appear in a vertical list, when its `|width|' denotes additional 3628spacing in the vertical direction. The |subtype| is either |normal| (for 3629kerns inserted from font information or math mode calculations) or |explicit| 3630(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern| 3631(for kerns inserted from non-math accents) or |mu_glue| (for kerns 3632inserted from \.{\\mkern} specifications in math formulas). 3633 3634@ The |new_kern| function creates a kern node having a given width. 3635 3636@c 3637halfword new_kern(scaled w) 3638{ 3639 halfword p; /* the new node */ 3640 p = new_node(kern_node, normal); 3641 width(p) = w; 3642 return p; 3643} 3644 3645@ A |penalty_node| specifies the penalty associated with line or page 3646breaking, in its |penalty| field. This field is a fullword integer, but 3647the full range of integer values is not used: Any penalty |>=10000| is 3648treated as infinity, and no break will be allowed for such high values. 3649Similarly, any penalty |<=-10000| is treated as negative infinity, and a 3650break will be forced. 3651 3652@ Anyone who has been reading the last few sections of the program will 3653be able to guess what comes next. 3654 3655@c 3656halfword new_penalty(int m) 3657{ 3658 halfword p; /* the new node */ 3659 p = new_node(penalty_node, 0); /* the |subtype| is not used */ 3660 penalty(p) = m; 3661 return p; 3662} 3663 3664@ You might think that we have introduced enough node types by now. Well, 3665almost, but there is one more: An |unset_node| has nearly the same format 3666as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} 3667or \.{\\valign} that are not yet in their final form, since the box 3668dimensions are their ``natural'' sizes before any glue adjustment has been 3669made. The |glue_set| word is not present; instead, we have a |glue_stretch| 3670field, which contains the total stretch of order |glue_order| that is 3671present in the hlist or vlist being boxed. 3672Similarly, the |shift_amount| field is replaced by a |glue_shrink| field, 3673containing the total shrink of order |glue_sign| that is present. 3674The |subtype| field is called |span_count|; an unset box typically 3675contains the data for |qo(span_count)+1| columns. 3676Unset nodes will be changed to box nodes when alignment is completed. 3677 3678In fact, there are still more types coming. When we get to math formula 3679processing we will see that a |style_node| has |type=14|; and a number 3680of larger type codes will also be defined, for use in math mode only. 3681 3682Warning: If any changes are made to these data structure layouts, such as 3683changing any of the node sizes or even reordering the words of nodes, 3684the |copy_node_list| procedure and the memory initialization code 3685below may have to be changed. Such potentially dangerous parts of the 3686program are listed in the index under `data structure assumptions'. 3687@!@^data structure assumptions@> 3688However, other references to the nodes are made symbolically in terms of 3689the \.{WEB} macro definitions above, so that format changes will leave 3690\TeX's other algorithms intact. 3691@^system dependencies@> 3692