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