boundary nodes made consistent (cleanup and document): WARNING: bump the format numbe...
[luatex.git] / source / texk / web2c / luatexdir / tex / texnodes.w
blob4cbd0c00beaef2ee83cc706a8a9ad20c7d97be7e
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_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) { confusion("fuzzy token cleanup in node"); }
631 #ifdef CHECK_NODE_USAGE
633 static void check_static_node_mem(void)
635 dotest(zero_glue, width(zero_glue), 0);
636 dotest(zero_glue, type(zero_glue), glue_spec_node);
637 dotest(zero_glue, vlink(zero_glue), null);
638 dotest(zero_glue, stretch(zero_glue), 0);
639 dotest(zero_glue, stretch_order(zero_glue), normal);
640 dotest(zero_glue, shrink(zero_glue), 0);
641 dotest(zero_glue, shrink_order(zero_glue), normal);
643 dotest(sfi_glue, width(sfi_glue), 0);
644 dotest(sfi_glue, type(sfi_glue), glue_spec_node);
645 dotest(sfi_glue, vlink(sfi_glue), null);
646 dotest(sfi_glue, stretch(sfi_glue), 0);
647 dotest(sfi_glue, stretch_order(sfi_glue), sfi);
648 dotest(sfi_glue, shrink(sfi_glue), 0);
649 dotest(sfi_glue, shrink_order(sfi_glue), normal);
651 dotest(fil_glue, width(fil_glue), 0);
652 dotest(fil_glue, type(fil_glue), glue_spec_node);
653 dotest(fil_glue, vlink(fil_glue), null);
654 dotest(fil_glue, stretch(fil_glue), unity);
655 dotest(fil_glue, stretch_order(fil_glue), fil);
656 dotest(fil_glue, shrink(fil_glue), 0);
657 dotest(fil_glue, shrink_order(fil_glue), normal);
659 dotest(fill_glue, width(fill_glue), 0);
660 dotest(fill_glue, type(fill_glue), glue_spec_node);
661 dotest(fill_glue, vlink(fill_glue), null);
662 dotest(fill_glue, stretch(fill_glue), unity);
663 dotest(fill_glue, stretch_order(fill_glue), fill);
664 dotest(fill_glue, shrink(fill_glue), 0);
665 dotest(fill_glue, shrink_order(fill_glue), normal);
667 dotest(ss_glue, width(ss_glue), 0);
668 dotest(ss_glue, type(ss_glue), glue_spec_node);
669 dotest(ss_glue, vlink(ss_glue), null);
670 dotest(ss_glue, stretch(ss_glue), unity);
671 dotest(ss_glue, stretch_order(ss_glue), fil);
672 dotest(ss_glue, shrink(ss_glue), unity);
673 dotest(ss_glue, shrink_order(ss_glue), fil);
675 dotest(fil_neg_glue, width(fil_neg_glue), 0);
676 dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node);
677 dotest(fil_neg_glue, vlink(fil_neg_glue), null);
678 dotest(fil_neg_glue, stretch(fil_neg_glue), -unity);
679 dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil);
680 dotest(fil_neg_glue, shrink(fil_neg_glue), 0);
681 dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal);
684 static void node_mem_dump(halfword p)
686 halfword r;
687 for (r = my_prealloc + 1; r < var_mem_max; r++) {
688 if (vlink(r) == p) {
689 halfword s = r;
690 while (s > my_prealloc && varmem_sizes[s] == 0) {
691 s--;
693 if (s != null
694 && s != my_prealloc
695 && s != var_mem_max
696 && (r - s) < get_node_size(type(s), subtype(s))
697 && alink(s) != p) {
698 if (type(s) == disc_node) {
699 fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
700 get_node_name(type(s), subtype(s)), (int) s,
701 (int) vlink(s), (int) alink(s));
702 fprintf(stdout, "pre_break(%d,%d,%d), ",
703 (int) vlink_pre_break(s), (int) tlink(pre_break(s)),
704 (int) alink(pre_break(s)));
705 fprintf(stdout, "post_break(%d,%d,%d), ",
706 (int) vlink_post_break(s),
707 (int) tlink(post_break(s)),
708 (int) alink(post_break(s)));
709 fprintf(stdout, "no_break(%d,%d,%d)",
710 (int) vlink_no_break(s), (int) tlink(no_break(s)),
711 (int) alink(no_break(s)));
712 fprintf(stdout, "\n");
713 } else {
714 if (vlink(s) == p
715 || (type(s) == glyph_node && lig_ptr (s) == p)
716 || (type(s) == vlist_node && list_ptr(s) == p)
717 || (type(s) == hlist_node && list_ptr(s) == p)
718 || (type(s) == unset_node && list_ptr(s) == p)
719 || (type(s) == ins_node && ins_ptr (s) == p)
721 fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
722 get_node_name(type(s), subtype(s)), (int) s,
723 (int) vlink(s), (int) alink(s));
724 if (type(s) == glyph_node) {
725 fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s));
726 } else if (type(s) == vlist_node || type(s) == hlist_node) {
727 fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s));
729 fprintf(stdout, "\n");
730 } else {
731 if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) {
732 fprintf(stdout, " pointed to from %s node %d\n",
733 get_node_name(type(s), subtype(s)), (int) s);
742 #endif
744 static int free_error(halfword p)
746 if (p > my_prealloc && p < var_mem_max) {
747 #ifdef CHECK_NODE_USAGE
748 int i;
749 if (varmem_sizes[p] == 0) {
750 check_static_node_mem();
751 for (i = (my_prealloc + 1); i < var_mem_max; i++) {
752 if (varmem_sizes[i] > 0) {
753 check_node(i);
756 test_count++;
757 if (type(p) == glyph_node) {
758 formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p);
759 } else {
760 formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
762 node_mem_dump(p);
763 return 1;
765 #endif
766 } else {
767 formatted_error("nodes", "attempt to free an impossible node %d", (int) p);
768 return 1;
770 return 0;
773 @ @c
774 static int copy_error(halfword p)
776 if (p >= 0 && p < var_mem_max) {
777 #ifdef CHECK_NODE_USAGE
778 if (p > my_prealloc && varmem_sizes[p] == 0) {
779 if (type(p) == glyph_node) {
780 formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p);
781 } else {
782 formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
784 return 1;
786 #endif
787 } else {
788 formatted_error("nodes", "attempt to copy an impossible node %d", (int) p);
789 return 1;
791 return 0;
794 @ @c
795 halfword new_node(int i, int j)
797 int s = get_node_size(i, j);
798 halfword n = get_node(s);
800 It should be possible to do this memset at |free_node()|.
802 Both type() and subtype() will be set below, and vlink() is
803 set to null by |get_node()|, so we can do we clearing one
804 word less than |s|
806 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1)));
807 switch (i) {
808 case glyph_node:
809 init_lang_data(n);
810 break;
811 case hlist_node:
812 case vlist_node:
813 box_dir(n) = -1;
814 break;
815 case disc_node:
816 pre_break(n) = pre_break_head(n);
817 type(pre_break(n)) = nesting_node;
818 subtype(pre_break(n)) = pre_break_head(0);
819 post_break(n) = post_break_head(n);
820 type(post_break(n)) = nesting_node;
821 subtype(post_break(n)) = post_break_head(0);
822 no_break(n) = no_break_head(n);
823 type(no_break(n)) = nesting_node;
824 subtype(no_break(n)) = no_break_head(0);
825 break;
826 case rule_node:
827 depth(n) = null_flag;
828 height(n) = null_flag;
829 width(n) = null_flag;
830 rule_dir(n) = -1;
831 rule_index(n) = 0;
832 rule_transform(n) = 0;
833 break;
834 case whatsit_node:
835 if (j == open_node) {
836 open_name(n) = get_nullstr();
837 open_area(n) = open_name(n);
838 open_ext(n) = open_name(n);
840 break;
841 case unset_node:
842 width(n) = null_flag;
843 break;
844 case pseudo_line_node:
845 case shape_node:
846 /* this is a trick that makes |pseudo_files| slightly slower,
847 but the overall allocation faster then an explicit test
848 at the top of |new_node()|.
850 if (j>0) {
851 free_node(n, variable_node_size);
852 n = slow_get_node(j);
853 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1)));
855 break;
856 default:
857 break;
859 if (int_par(synctex_code)) {
860 /* handle synctex extension */
861 switch (i) {
862 case math_node:
863 synctex_tag_math(n) = cur_input.synctex_tag_field;
864 synctex_line_math(n) = line;
865 break;
866 case glue_node:
867 synctex_tag_glue(n) = cur_input.synctex_tag_field;
868 synctex_line_glue(n) = line;
869 break;
870 case kern_node:
871 if (j != 0) {
872 synctex_tag_kern(n) = cur_input.synctex_tag_field;
873 synctex_line_kern(n) = line;
875 break;
876 case hlist_node:
877 case vlist_node:
878 case unset_node:
879 synctex_tag_box(n) = cur_input.synctex_tag_field;
880 synctex_line_box(n) = line;
881 break;
882 case rule_node:
883 synctex_tag_rule(n) = cur_input.synctex_tag_field;
884 synctex_line_rule(n) = line;
885 break;
888 /* take care of attributes */
889 if (nodetype_has_attributes(i)) {
890 build_attribute_list(n);
891 /* lua_properties_set */
893 type(n) = (quarterword) i;
894 subtype(n) = (quarterword) j;
895 return n;
898 halfword raw_glyph_node(void)
900 register halfword n = get_node(glyph_node_size);
901 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
902 type(n) = glyph_node;
903 subtype(n) = 0;
904 return n;
907 halfword new_glyph_node(void)
909 register halfword n = get_node(glyph_node_size);
910 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
911 type(n) = glyph_node;
912 subtype(n) = 0;
913 build_attribute_list(n);
914 /* lua_properties_set */
915 return n;
918 @ makes a duplicate of the node list that starts at |p| and returns a
919 pointer to the new list
922 halfword do_copy_node_list(halfword p, halfword end)
924 halfword q = null; /* previous position in new list */
925 halfword h = null; /* head of the list */
926 register halfword s ;
927 lua_properties_push; /* saves stack and time */
928 while (p != end) {
929 s = copy_node(p);
930 if (h == null) {
931 h = s;
932 } else {
933 couple_nodes(q, s);
935 q = s;
936 p = vlink(p);
938 lua_properties_pop; /* saves stack and time */
939 return h;
942 halfword copy_node_list(halfword p)
944 return do_copy_node_list(p, null);
947 #define copy_sub_list(target,source) do { \
948 if (source != null) { \
949 s = do_copy_node_list(source, null); \
950 target = s; \
951 } else { \
952 target = null; \
954 } while (0)
956 #define copy_sub_node(target,source) do { \
957 if (source != null) { \
958 s = copy_node(source); \
959 target = s ; \
960 } else { \
961 target = null; \
963 } while (0)
965 @ make a dupe of a single node
968 static void copy_node_wrapup_core(halfword p, halfword r)
970 halfword s ;
971 switch (subtype(p)) {
972 case write_node:
973 case special_node:
974 add_token_ref(write_tokens(p));
975 break;
976 case late_lua_node:
977 copy_late_lua(r, p);
978 break;
979 case user_defined_node:
980 switch (user_node_type(p)) {
981 case 'a':
982 add_node_attr_ref(user_node_value(p));
983 break;
984 case 'l':
985 copy_user_lua(r, p);
986 break;
987 case 'n':
988 s = copy_node_list(user_node_value(p));
989 user_node_value(r) = s;
990 break;
991 case 's':
992 /* |add_string_ref(user_node_value(p));| */
993 break;
994 case 't':
995 add_token_ref(user_node_value(p));
996 break;
998 break;
999 default:
1000 break ;
1004 void copy_node_wrapup_dvi(halfword p, halfword r)
1008 void copy_node_wrapup_pdf(halfword p, halfword r)
1010 switch(subtype(p)) {
1011 case pdf_literal_node:
1012 copy_pdf_literal(r, p);
1013 break;
1014 case pdf_colorstack_node:
1015 if (pdf_colorstack_cmd(p) <= colorstack_data)
1016 add_token_ref(pdf_colorstack_data(p));
1017 break;
1018 case pdf_setmatrix_node:
1019 add_token_ref(pdf_setmatrix_data(p));
1020 break;
1021 case pdf_annot_node:
1022 add_token_ref(pdf_annot_data(p));
1023 break;
1024 case pdf_start_link_node:
1025 if (pdf_link_attr(r) != null)
1026 add_token_ref(pdf_link_attr(r));
1027 add_action_ref(pdf_link_action(r));
1028 break;
1029 case pdf_dest_node:
1030 if (pdf_dest_named_id(p) > 0)
1031 add_token_ref(pdf_dest_id(p));
1032 break;
1033 case pdf_thread_node:
1034 case pdf_start_thread_node:
1035 if (pdf_thread_named_id(p) > 0)
1036 add_token_ref(pdf_thread_id(p));
1037 if (pdf_thread_attr(p) != null)
1038 add_token_ref(pdf_thread_attr(p));
1039 break;
1040 default:
1041 break;
1045 halfword copy_node(const halfword p)
1047 halfword r; /* current node being fabricated for new list */
1048 halfword w ; /* whatsit subtype */
1049 register halfword s; /* a helper variable for copying into variable mem */
1050 register int i;
1051 if (copy_error(p)) {
1052 r = new_node(temp_node, 0);
1053 return r;
1055 i = get_node_size(type(p), subtype(p));
1056 r = get_node(i);
1058 (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i));
1060 if (int_par(synctex_code)) {
1061 /* handle synctex extension */
1062 switch (type(p)) {
1063 case math_node:
1064 synctex_tag_math(r) = cur_input.synctex_tag_field;
1065 synctex_line_math(r) = line;
1066 break;
1067 case kern_node:
1068 synctex_tag_kern(r) = cur_input.synctex_tag_field;
1069 synctex_line_kern(r) = line;
1070 break;
1073 if (nodetype_has_attributes(type(p))) {
1074 add_node_attr_ref(node_attr(p));
1075 alink(r) = null;
1076 lua_properties_copy(r,p);
1078 vlink(r) = null;
1080 switch (type(p)) {
1081 case glyph_node:
1082 copy_sub_list(lig_ptr(r),lig_ptr(p)) ;
1083 break;
1084 case glue_node:
1085 copy_sub_list(leader_ptr(r),leader_ptr(p)) ;
1086 break;
1087 case hlist_node:
1088 case vlist_node:
1089 case unset_node:
1090 copy_sub_list(list_ptr(r),list_ptr(p)) ;
1091 break;
1092 case disc_node:
1093 pre_break(r) = pre_break_head(r);
1094 if (vlink_pre_break(p) != null) {
1095 s = copy_node_list(vlink_pre_break(p));
1096 alink(s) = pre_break(r);
1097 tlink_pre_break(r) = tail_of_list(s);
1098 vlink_pre_break(r) = s;
1099 } else {
1100 assert(tlink(pre_break(r)) == null);
1102 post_break(r) = post_break_head(r);
1103 if (vlink_post_break(p) != null) {
1104 s = copy_node_list(vlink_post_break(p));
1105 alink(s) = post_break(r);
1106 tlink_post_break(r) = tail_of_list(s);
1107 vlink_post_break(r) = s;
1108 } else {
1109 assert(tlink_post_break(r) == null);
1111 no_break(r) = no_break_head(r);
1112 if (vlink(no_break(p)) != null) {
1113 s = copy_node_list(vlink_no_break(p));
1114 alink(s) = no_break(r);
1115 tlink_no_break(r) = tail_of_list(s);
1116 vlink_no_break(r) = s;
1117 } else {
1118 assert(tlink_no_break(r) == null);
1120 break;
1121 case math_node:
1122 break;
1123 case ins_node:
1124 copy_sub_list(ins_ptr(r),ins_ptr(p)) ;
1125 break;
1126 case margin_kern_node:
1127 copy_sub_node(margin_char(r),margin_char(p));
1128 break;
1129 case mark_node:
1130 add_token_ref(mark_ptr(p));
1131 break;
1132 case adjust_node:
1133 copy_sub_list(adjust_ptr(r),adjust_ptr(p));
1134 break;
1135 case choice_node:
1136 copy_sub_list(display_mlist(r),display_mlist(p)) ;
1137 copy_sub_list(text_mlist(r),text_mlist(p)) ;
1138 copy_sub_list(script_mlist(r),script_mlist(p)) ;
1139 copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ;
1140 break;
1141 case simple_noad:
1142 copy_sub_list(nucleus(r),nucleus(p)) ;
1143 copy_sub_list(subscr(r),subscr(p)) ;
1144 copy_sub_list(supscr(r),supscr(p)) ;
1145 break;
1146 case radical_noad:
1147 copy_sub_list(nucleus(r),nucleus(p)) ;
1148 copy_sub_list(subscr(r),subscr(p)) ;
1149 copy_sub_list(supscr(r),supscr(p)) ;
1150 copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
1151 copy_sub_list(degree(r),degree(p)) ;
1152 break;
1153 case accent_noad:
1154 copy_sub_list(nucleus(r),nucleus(p)) ;
1155 copy_sub_list(subscr(r),subscr(p)) ;
1156 copy_sub_list(supscr(r),supscr(p)) ;
1157 copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ;
1158 copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ;
1159 copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ;
1160 break;
1161 case fence_noad:
1162 copy_sub_node(delimiter(r),delimiter(p)) ;
1163 break;
1164 case sub_box_node:
1165 case sub_mlist_node:
1166 copy_sub_list(math_list(r),math_list(p)) ;
1167 break;
1168 case fraction_noad:
1169 copy_sub_list(numerator(r),numerator(p)) ;
1170 copy_sub_list(denominator(r),denominator(p)) ;
1171 copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
1172 copy_sub_node(right_delimiter(r),right_delimiter(p)) ;
1173 break;
1174 case glue_spec_node:
1175 case dir_node:
1176 case local_par_node:
1177 case boundary_node:
1178 break;
1179 case whatsit_node:
1180 w = subtype(p) ;
1181 if (w >= backend_first_pdf_whatsit) {
1182 copy_node_wrapup_pdf(p,r);
1183 } else if (w >= backend_first_dvi_whatsit) {
1184 copy_node_wrapup_dvi(p,r);
1185 } else {
1186 copy_node_wrapup_core(p,r);
1188 break;
1190 return r;
1193 /* x */
1195 #define free_sub_list(source) if (source != null) flush_node_list(source);
1196 #define free_sub_node(source) if (source != null) flush_node(source);
1198 @ @c
1200 static void flush_node_wrapup_core(halfword p)
1202 switch (subtype(p)) {
1203 case open_node:
1204 case write_node:
1205 case close_node:
1206 case save_pos_node:
1207 break;
1208 case special_node:
1209 delete_token_ref(write_tokens(p));
1210 break;
1211 case late_lua_node:
1212 free_late_lua(p);
1213 break;
1214 case user_defined_node:
1215 switch (user_node_type(p)) {
1216 case 'a':
1217 delete_attribute_ref(user_node_value(p));
1218 break;
1219 case 'd':
1220 break;
1221 case 'n':
1222 flush_node_list(user_node_value(p));
1223 break;
1224 case 's':
1225 /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */
1226 break;
1227 case 't':
1228 delete_token_ref(user_node_value(p));
1229 break;
1230 default:
1232 const char *hlp[] = {
1233 "The type of the value in a user defined whatsit node should be one",
1234 "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
1235 "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
1236 "know how to free the node's value. A memory leak may result.",
1237 NULL
1239 tex_error("Unidentified user defined whatsit", hlp);
1241 break;
1243 break;
1247 void flush_node_wrapup_dvi(halfword p)
1251 void flush_node_wrapup_pdf(halfword p)
1253 switch(subtype(p)) {
1254 case pdf_save_node:
1255 case pdf_restore_node:
1256 case pdf_refobj_node:
1257 case pdf_end_link_node:
1258 case pdf_end_thread_node:
1259 break;
1260 case pdf_literal_node:
1261 free_pdf_literal(p);
1262 break;
1263 case pdf_colorstack_node:
1264 if (pdf_colorstack_cmd(p) <= colorstack_data)
1265 delete_token_ref(pdf_colorstack_data(p));
1266 break;
1267 case pdf_setmatrix_node:
1268 delete_token_ref(pdf_setmatrix_data(p));
1269 break;
1270 case pdf_annot_node:
1271 delete_token_ref(pdf_annot_data(p));
1272 break;
1273 case pdf_link_data_node:
1274 break;
1275 case pdf_start_link_node:
1276 if (pdf_link_attr(p) != null)
1277 delete_token_ref(pdf_link_attr(p));
1278 delete_action_ref(pdf_link_action(p));
1279 break;
1280 case pdf_dest_node:
1281 if (pdf_dest_named_id(p) > 0)
1282 delete_token_ref(pdf_dest_id(p));
1283 break;
1284 case pdf_action_node:
1285 if (pdf_action_type(p) == pdf_action_user) {
1286 delete_token_ref(pdf_action_tokens(p));
1287 } else {
1288 if (pdf_action_file(p) != null)
1289 delete_token_ref(pdf_action_file(p));
1290 if (pdf_action_type(p) == pdf_action_page)
1291 delete_token_ref(pdf_action_tokens(p));
1292 else if (pdf_action_named_id(p) > 0)
1293 delete_token_ref(pdf_action_id(p));
1295 break;
1296 case pdf_thread_data_node:
1297 break;
1298 case pdf_thread_node:
1299 case pdf_start_thread_node:
1300 if (pdf_thread_named_id(p) > 0)
1301 delete_token_ref(pdf_thread_id(p));
1302 if (pdf_thread_attr(p) != null)
1303 delete_token_ref(pdf_thread_attr(p));
1304 break;
1308 void flush_node(halfword p)
1310 halfword w;
1311 if (p == null) /* legal, but no-op */
1312 return;
1313 if (free_error(p))
1314 return;
1315 switch (type(p)) {
1316 case glyph_node:
1317 free_sub_list(lig_ptr(p));
1318 break;
1319 case glue_node:
1320 free_sub_list(leader_ptr(p));
1321 break;
1322 case hlist_node:
1323 case vlist_node:
1324 case unset_node:
1325 free_sub_list(list_ptr(p));
1326 break;
1327 case disc_node:
1328 /* watch the start at temp node hack */
1329 free_sub_list(vlink(pre_break(p)));
1330 free_sub_list(vlink(post_break(p)));
1331 free_sub_list(vlink(no_break(p)));
1332 break;
1333 case rule_node:
1334 case kern_node:
1335 case penalty_node:
1336 case math_node:
1337 break;
1338 case glue_spec_node:
1339 /* this allows free-ing of lua-allocated glue specs */
1340 //if (valid_node(p)) {
1341 // free_node(p, subtype(p));
1343 // return ;
1344 break ;
1345 case dir_node:
1346 case local_par_node:
1347 case boundary_node:
1348 break;
1349 case whatsit_node:
1350 w = subtype(p) ;
1351 if (w >= backend_first_pdf_whatsit) {
1352 flush_node_wrapup_pdf(p);
1353 } else if (w >= backend_first_dvi_whatsit) {
1354 flush_node_wrapup_dvi(p);
1355 } else {
1356 flush_node_wrapup_core(p);
1358 break;
1359 case ins_node:
1360 flush_node_list(ins_ptr(p));
1361 break;
1362 case margin_kern_node:
1363 flush_node(margin_char(p));
1364 break;
1365 case mark_node:
1366 delete_token_ref(mark_ptr(p));
1367 break;
1368 case adjust_node:
1369 flush_node_list(adjust_ptr(p));
1370 break;
1371 case style_node: /* nothing to do */
1372 break;
1373 case choice_node:
1374 free_sub_list(display_mlist(p));
1375 free_sub_list(text_mlist(p));
1376 free_sub_list(script_mlist(p));
1377 free_sub_list(script_script_mlist(p));
1378 break;
1379 case simple_noad:
1380 free_sub_list(nucleus(p));
1381 free_sub_list(subscr(p));
1382 free_sub_list(supscr(p));
1383 break;
1384 case radical_noad:
1385 free_sub_list(nucleus(p));
1386 free_sub_list(subscr(p));
1387 free_sub_list(supscr(p));
1388 free_sub_node(left_delimiter(p));
1389 free_sub_list(degree(p));
1390 break;
1391 case accent_noad:
1392 free_sub_list(nucleus(p));
1393 free_sub_list(subscr(p));
1394 free_sub_list(supscr(p));
1395 free_sub_list(top_accent_chr(p));
1396 free_sub_list(bot_accent_chr(p));
1397 free_sub_list(overlay_accent_chr(p));
1398 break;
1399 case fence_noad:
1400 free_sub_list(delimiter(p));
1401 break;
1402 case delim_node: /* nothing to do */
1403 case math_char_node:
1404 case math_text_char_node:
1405 break;
1406 case sub_box_node:
1407 case sub_mlist_node:
1408 free_sub_list(math_list(p));
1409 break;
1410 case fraction_noad:
1411 free_sub_list(numerator(p));
1412 free_sub_list(denominator(p));
1413 free_sub_node(left_delimiter(p));
1414 free_sub_node(right_delimiter(p));
1415 break;
1416 case pseudo_file_node:
1417 free_sub_list(pseudo_lines(p));
1418 break;
1419 case pseudo_line_node:
1420 case shape_node:
1421 free_node(p, subtype(p));
1422 return;
1423 break;
1424 case align_stack_node:
1425 case span_node:
1426 case movement_node:
1427 case if_node:
1428 case nesting_node:
1429 case unhyphenated_node:
1430 case hyphenated_node:
1431 case delta_node:
1432 case passive_node:
1433 case inserting_node:
1434 case split_up_node:
1435 case expr_node:
1436 case attribute_node:
1437 case attribute_list_node:
1438 case temp_node:
1439 break;
1440 default:
1441 formatted_error("nodes","flushing weird node type %d", type(p));
1442 return;
1444 if (nodetype_has_attributes(type(p))) {
1445 delete_attribute_ref(node_attr(p));
1446 lua_properties_reset(p);
1448 free_node(p, get_node_size(type(p), subtype(p)));
1449 return;
1452 @ @c
1453 void flush_node_list(halfword pp)
1454 { /* erase list of nodes starting at |p| */
1455 register halfword p = pp;
1456 if (p == null) /* legal, but no-op */
1457 return;
1458 if (free_error(p))
1459 return;
1460 lua_properties_push; /* saves stack and time */
1461 while (p != null) {
1462 register halfword q = vlink(p);
1463 flush_node(p);
1464 p = q;
1466 lua_properties_pop; /* saves stack and time */
1469 @ @c
1470 static void check_node_wrapup_core(halfword p)
1472 switch (subtype(p)) {
1473 /* frontend code */
1474 case special_node:
1475 check_token_ref(write_tokens(p));
1476 break;
1477 case user_defined_node:
1478 switch (user_node_type(p)) {
1479 case 'a':
1480 check_attribute_ref(user_node_value(p));
1481 break;
1482 case 't':
1483 check_token_ref(user_node_value(p));
1484 break;
1485 case 'n':
1486 dorangetest(p, user_node_value(p), var_mem_max);
1487 break;
1488 case 's':
1489 case 'd':
1490 break;
1491 default:
1492 confusion("unknown user node type");
1493 break;
1495 break;
1496 case open_node:
1497 case write_node:
1498 case close_node:
1499 case save_pos_node:
1500 break;
1504 void check_node_wrapup_dvi(halfword p)
1508 void check_node_wrapup_pdf(halfword p)
1510 switch (subtype(p)) {
1511 case pdf_literal_node:
1512 if (pdf_literal_type(p) == normal)
1513 check_token_ref(pdf_literal_data(p));
1514 break;
1515 case pdf_colorstack_node:
1516 if (pdf_colorstack_cmd(p) <= colorstack_data)
1517 check_token_ref(pdf_colorstack_data(p));
1518 break;
1519 case pdf_setmatrix_node:
1520 check_token_ref(pdf_setmatrix_data(p));
1521 break;
1522 case late_lua_node:
1523 if (late_lua_name(p) > 0)
1524 check_token_ref(late_lua_name(p));
1525 if (late_lua_type(p) == normal)
1526 check_token_ref(late_lua_data(p));
1527 break;
1528 case pdf_annot_node:
1529 check_token_ref(pdf_annot_data(p));
1530 break;
1531 case pdf_start_link_node:
1532 if (pdf_link_attr(p) != null)
1533 check_token_ref(pdf_link_attr(p));
1534 check_action_ref(pdf_link_action(p));
1535 break;
1536 case pdf_dest_node:
1537 if (pdf_dest_named_id(p) > 0)
1538 check_token_ref(pdf_dest_id(p));
1539 break;
1540 case pdf_thread_node:
1541 case pdf_start_thread_node:
1542 if (pdf_thread_named_id(p) > 0)
1543 check_token_ref(pdf_thread_id(p));
1544 if (pdf_thread_attr(p) != null)
1545 check_token_ref(pdf_thread_attr(p));
1546 break;
1547 case pdf_save_node:
1548 case pdf_restore_node:
1549 case pdf_refobj_node:
1550 case pdf_end_link_node:
1551 case pdf_end_thread_node:
1552 break;
1553 default:
1554 confusion("wrapup pdf nodes");
1555 break;
1559 void check_node(halfword p)
1561 halfword w ;
1562 switch (type(p)) {
1563 case glyph_node:
1564 dorangetest(p, lig_ptr(p), var_mem_max);
1565 break;
1566 case glue_node:
1567 dorangetest(p, leader_ptr(p), var_mem_max);
1568 break;
1569 case hlist_node:
1570 case vlist_node:
1571 case unset_node:
1572 case align_record_node:
1573 dorangetest(p, list_ptr(p), var_mem_max);
1574 break;
1575 case ins_node:
1576 dorangetest(p, ins_ptr(p), var_mem_max);
1577 break;
1578 case whatsit_node:
1579 w = subtype(p) ;
1580 if (w >= backend_first_pdf_whatsit) {
1581 check_node_wrapup_pdf(p);
1582 } else if (w >= backend_first_dvi_whatsit) {
1583 check_node_wrapup_dvi(p);
1584 } else {
1585 check_node_wrapup_core(p);
1587 break;
1588 case margin_kern_node:
1589 check_node(margin_char(p));
1590 break;
1591 case math_node:
1592 break;
1593 case disc_node:
1594 dorangetest(p, vlink(pre_break(p)), var_mem_max);
1595 dorangetest(p, vlink(post_break(p)), var_mem_max);
1596 dorangetest(p, vlink(no_break(p)), var_mem_max);
1597 break;
1598 case adjust_node:
1599 dorangetest(p, adjust_ptr(p), var_mem_max);
1600 break;
1601 case pseudo_file_node:
1602 dorangetest(p, pseudo_lines(p), var_mem_max);
1603 break;
1604 case pseudo_line_node:
1605 case shape_node:
1606 break;
1607 case choice_node:
1608 dorangetest(p, display_mlist(p), var_mem_max);
1609 dorangetest(p, text_mlist(p), var_mem_max);
1610 dorangetest(p, script_mlist(p), var_mem_max);
1611 dorangetest(p, script_script_mlist(p), var_mem_max);
1612 break;
1613 case fraction_noad:
1614 dorangetest(p, numerator(p), var_mem_max);
1615 dorangetest(p, denominator(p), var_mem_max);
1616 dorangetest(p, left_delimiter(p), var_mem_max);
1617 dorangetest(p, right_delimiter(p), var_mem_max);
1618 break;
1619 case simple_noad:
1620 dorangetest(p, nucleus(p), var_mem_max);
1621 dorangetest(p, subscr(p), var_mem_max);
1622 dorangetest(p, supscr(p), var_mem_max);
1623 break;
1624 case radical_noad:
1625 dorangetest(p, nucleus(p), var_mem_max);
1626 dorangetest(p, subscr(p), var_mem_max);
1627 dorangetest(p, supscr(p), var_mem_max);
1628 dorangetest(p, degree(p), var_mem_max);
1629 dorangetest(p, left_delimiter(p), var_mem_max);
1630 break;
1631 case accent_noad:
1632 dorangetest(p, nucleus(p), var_mem_max);
1633 dorangetest(p, subscr(p), var_mem_max);
1634 dorangetest(p, supscr(p), var_mem_max);
1635 dorangetest(p, top_accent_chr(p), var_mem_max);
1636 dorangetest(p, bot_accent_chr(p), var_mem_max);
1637 dorangetest(p, overlay_accent_chr(p), var_mem_max);
1638 break;
1639 case fence_noad:
1640 dorangetest(p, delimiter(p), var_mem_max);
1641 break;
1643 case rule_node:
1644 case kern_node:
1645 case penalty_node:
1646 case mark_node:
1647 case style_node:
1648 case attribute_list_node:
1649 case attribute_node:
1650 case glue_spec_node:
1651 case temp_node:
1652 case align_stack_node:
1653 case movement_node:
1654 case if_node:
1655 case nesting_node:
1656 case span_node:
1657 case unhyphenated_node:
1658 case hyphenated_node:
1659 case delta_node:
1660 case passive_node:
1661 case expr_node:
1662 case dir_node:
1663 case boundary_node:
1664 case local_par_node:
1665 break;
1666 default:
1667 fprintf(stdout, "check_node: type is %d\n", type(p));
1672 @ @c
1673 void fix_node_list(halfword head)
1675 halfword p, q;
1676 if (head == null)
1677 return;
1678 p = head;
1679 q = vlink(p);
1680 while (q != null) {
1681 alink(q) = p;
1682 p = q;
1683 q = vlink(p);
1687 @ @c
1688 halfword get_node(int s)
1690 register halfword r;
1692 if (s < MAX_CHAIN_SIZE) {
1693 r = free_chain[s];
1694 if (r != null) {
1695 free_chain[s] = vlink(r);
1696 #ifdef CHECK_NODE_USAGE
1697 varmem_sizes[r] = (char) s;
1698 #endif
1699 vlink(r) = null;
1700 var_used += s; /* maintain usage statistics */
1701 return r;
1703 /* this is the end of the 'inner loop' */
1704 return slow_get_node(s);
1705 } else {
1706 normal_error("nodes","there is a problem in getting a node, case 1");
1707 return null;
1711 @ @c
1712 void free_node(halfword p, int s)
1714 if (p <= my_prealloc) {
1715 formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p));
1716 return;
1718 #ifdef CHECK_NODE_USAGE
1719 varmem_sizes[p] = 0;
1720 #endif
1721 if (s < MAX_CHAIN_SIZE) {
1722 vlink(p) = free_chain[s];
1723 free_chain[s] = p;
1724 } else {
1725 /* todo ? it is perhaps possible to merge this node with an existing rover */
1726 node_size(p) = s;
1727 vlink(p) = rover;
1728 while (vlink(rover) != vlink(p)) {
1729 rover = vlink(rover);
1731 vlink(rover) = p;
1733 /* maintain statistics */
1734 var_used -= s;
1737 @ @c
1738 static void free_node_chain(halfword q, int s)
1740 register halfword p = q;
1741 while (vlink(p) != null) {
1742 #ifdef CHECK_NODE_USAGE
1743 varmem_sizes[p] = 0;
1744 #endif
1745 var_used -= s;
1746 p = vlink(p);
1748 var_used -= s;
1749 #ifdef CHECK_NODE_USAGE
1750 varmem_sizes[p] = 0;
1751 #endif
1752 vlink(p) = free_chain[s];
1753 free_chain[s] = q;
1756 @ @c
1757 void init_node_mem(int t)
1759 my_prealloc = var_mem_stat_max;
1761 /* message ?
1763 assert(whatsit_node_data[user_defined_node].id == user_defined_node);
1764 assert(node_data[passive_node].id == passive_node);
1767 varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t);
1768 if (varmem == NULL) {
1769 overflow("node memory size", (unsigned) var_mem_max);
1771 memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word));
1772 #ifdef CHECK_NODE_USAGE
1773 varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t);
1774 if (varmem_sizes == NULL) {
1775 overflow("node memory size", (unsigned) var_mem_max);
1777 memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t);
1778 #endif
1779 var_mem_max = t;
1780 rover = var_mem_stat_max + 1;
1781 vlink(rover) = rover;
1782 node_size(rover) = (t - rover);
1783 var_used = 0;
1784 /* initialize static glue specs */
1785 width(zero_glue) = 0;
1786 type(zero_glue) = glue_spec_node;
1787 vlink(zero_glue) = null;
1788 stretch(zero_glue) = 0;
1789 stretch_order(zero_glue) = normal;
1790 shrink(zero_glue) = 0;
1791 shrink_order(zero_glue) = normal;
1792 width(sfi_glue) = 0;
1793 type(sfi_glue) = glue_spec_node;
1794 vlink(sfi_glue) = null;
1795 stretch(sfi_glue) = 0;
1796 stretch_order(sfi_glue) = sfi;
1797 shrink(sfi_glue) = 0;
1798 shrink_order(sfi_glue) = normal;
1799 width(fil_glue) = 0;
1800 type(fil_glue) = glue_spec_node;
1801 vlink(fil_glue) = null;
1802 stretch(fil_glue) = unity;
1803 stretch_order(fil_glue) = fil;
1804 shrink(fil_glue) = 0;
1805 shrink_order(fil_glue) = normal;
1806 width(fill_glue) = 0;
1807 type(fill_glue) = glue_spec_node;
1808 vlink(fill_glue) = null;
1809 stretch(fill_glue) = unity;
1810 stretch_order(fill_glue) = fill;
1811 shrink(fill_glue) = 0;
1812 shrink_order(fill_glue) = normal;
1813 width(ss_glue) = 0;
1814 type(ss_glue) = glue_spec_node;
1815 vlink(ss_glue) = null;
1816 stretch(ss_glue) = unity;
1817 stretch_order(ss_glue) = fil;
1818 shrink(ss_glue) = unity;
1819 shrink_order(ss_glue) = fil;
1820 width(fil_neg_glue) = 0;
1821 type(fil_neg_glue) = glue_spec_node;
1822 vlink(fil_neg_glue) = null;
1823 stretch(fil_neg_glue) = -unity;
1824 stretch_order(fil_neg_glue) = fil;
1825 shrink(fil_neg_glue) = 0;
1826 shrink_order(fil_neg_glue) = normal;
1827 /* initialize node list heads */
1828 vinfo(page_ins_head) = 0;
1829 type(page_ins_head) = temp_node;
1830 vlink(page_ins_head) = null;
1831 alink(page_ins_head) = null;
1832 vinfo(contrib_head) = 0;
1833 type(contrib_head) = temp_node;
1834 vlink(contrib_head) = null;
1835 alink(contrib_head) = null;
1836 vinfo(page_head) = 0;
1837 type(page_head) = temp_node;
1838 vlink(page_head) = null;
1839 alink(page_head) = null;
1840 vinfo(temp_head) = 0;
1841 type(temp_head) = temp_node;
1842 vlink(temp_head) = null;
1843 alink(temp_head) = null;
1844 vinfo(hold_head) = 0;
1845 type(hold_head) = temp_node;
1846 vlink(hold_head) = null;
1847 alink(hold_head) = null;
1848 vinfo(adjust_head) = 0;
1849 type(adjust_head) = temp_node;
1850 vlink(adjust_head) = null;
1851 alink(adjust_head) = null;
1852 vinfo(pre_adjust_head) = 0;
1853 type(pre_adjust_head) = temp_node;
1854 vlink(pre_adjust_head) = null;
1855 alink(pre_adjust_head) = null;
1856 vinfo(active) = 0;
1857 type(active) = unhyphenated_node;
1858 vlink(active) = null;
1859 alink(active) = null;
1860 vinfo(align_head) = 0;
1861 type(align_head) = temp_node;
1862 vlink(align_head) = null;
1863 alink(align_head) = null;
1864 vinfo(end_span) = 0;
1865 type(end_span) = span_node;
1866 vlink(end_span) = null;
1867 alink(end_span) = null;
1868 type(begin_point) = glyph_node;
1869 subtype(begin_point) = 0;
1870 vlink(begin_point) = null;
1871 vinfo(begin_point + 1) = null;
1872 alink(begin_point) = null;
1873 font(begin_point) = 0;
1874 character(begin_point) = '.';
1875 vinfo(begin_point + 3) = 0;
1876 vlink(begin_point + 3) = 0;
1877 vinfo(begin_point + 4) = 0;
1878 vlink(begin_point + 4) = 0;
1879 type(end_point) = glyph_node;
1880 subtype(end_point) = 0;
1881 vlink(end_point) = null;
1882 vinfo(end_point + 1) = null;
1883 alink(end_point) = null;
1884 font(end_point) = 0;
1885 character(end_point) = '.';
1886 vinfo(end_point + 3) = 0;
1887 vlink(end_point + 3) = 0;
1888 vinfo(end_point + 4) = 0;
1889 vlink(end_point + 4) = 0;
1892 @ @c
1893 void dump_node_mem(void)
1895 dump_int(var_mem_max);
1896 dump_int(rover);
1897 dump_things(varmem[0], var_mem_max);
1898 #ifdef CHECK_NODE_USAGE
1899 dump_things(varmem_sizes[0], var_mem_max);
1900 #endif
1901 dump_things(free_chain[0], MAX_CHAIN_SIZE);
1902 dump_int(var_used);
1903 dump_int(my_prealloc);
1906 @ it makes sense to enlarge the varmem array immediately
1909 void undump_node_mem(void)
1911 int x;
1912 undump_int(x);
1913 undump_int(rover);
1914 var_mem_max = (x < 100000 ? 100000 : x);
1915 varmem = xmallocarray(memory_word, (unsigned) var_mem_max);
1916 undump_things(varmem[0], x);
1917 #ifdef CHECK_NODE_USAGE
1918 varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
1919 memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char));
1920 undump_things(varmem_sizes[0], x);
1921 #endif
1922 undump_things(free_chain[0], MAX_CHAIN_SIZE);
1923 undump_int(var_used);
1924 undump_int(my_prealloc);
1925 if (var_mem_max > x) {
1926 /* todo ? it is perhaps possible to merge the new node with an existing rover */
1927 vlink(x) = rover;
1928 node_size(x) = (var_mem_max - x);
1929 while (vlink(rover) != vlink(x)) {
1930 rover = vlink(rover);
1932 vlink(rover) = x;
1936 @ @c
1937 halfword slow_get_node(int s)
1939 register int t;
1941 RETRY:
1942 t = node_size(rover);
1943 if (vlink(rover) < var_mem_max && vlink(rover) != 0) {
1944 if (t > s) {
1945 /* allocating from the bottom helps decrease page faults */
1946 register halfword r = rover;
1947 rover += s;
1948 vlink(rover) = vlink(r);
1949 node_size(rover) = node_size(r) - s;
1950 if (vlink(rover) != r) { /* list is longer than one */
1951 halfword q = r;
1952 while (vlink(q) != r) {
1953 q = vlink(q);
1955 vlink(q) += s;
1956 } else {
1957 vlink(rover) += s;
1959 if (vlink(rover) < var_mem_max) {
1960 #ifdef CHECK_NODE_USAGE
1961 varmem_sizes[r] = (char) (s > 127 ? 127 : s);
1962 #endif
1963 vlink(r) = null;
1964 var_used += s; /* maintain usage statistics */
1965 return r; /* this is the only exit */
1966 } else {
1967 normal_error("nodes","there is a problem in getting a node, case 2");
1968 return null;
1970 } else {
1971 /* attempt to keep the free list small */
1972 int x;
1973 if (vlink(rover) != rover) {
1974 if (t < MAX_CHAIN_SIZE) {
1975 halfword l = vlink(rover);
1976 vlink(rover) = free_chain[t];
1977 free_chain[t] = rover;
1978 rover = l;
1979 while (vlink(l) != free_chain[t]) {
1980 l = vlink(l);
1982 vlink(l) = rover;
1983 goto RETRY;
1984 } else {
1985 halfword l = rover;
1986 while (vlink(rover) != l) {
1987 if (node_size(rover) > s) {
1988 goto RETRY;
1990 rover = vlink(rover);
1994 /* if we are still here, it was apparently impossible to get a match */
1995 x = (var_mem_max >> 2) + s;
1996 varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x));
1997 if (varmem == NULL) {
1998 overflow("node memory size", (unsigned) var_mem_max);
2000 memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word));
2001 #ifdef CHECK_NODE_USAGE
2002 varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x));
2003 if (varmem_sizes == NULL) {
2004 overflow("node memory size", (unsigned) var_mem_max);
2006 memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char));
2007 #endif
2008 /* todo ? it is perhaps possible to merge the new memory with an existing rover */
2009 vlink(var_mem_max) = rover;
2010 node_size(var_mem_max) = x;
2011 while (vlink(rover) != vlink(var_mem_max)) {
2012 rover = vlink(rover);
2014 vlink(rover) = var_mem_max;
2015 rover = var_mem_max;
2016 var_mem_max += x;
2017 goto RETRY;
2019 } else {
2020 normal_error("nodes","there is a problem in getting a node, case 3");
2021 return null;
2025 @ @c
2026 char *sprint_node_mem_usage(void)
2028 char *s;
2029 #ifdef CHECK_NODE_USAGE
2030 char *ss;
2031 int i;
2032 int b = 0;
2033 char msg[256];
2034 int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 };
2035 s = strdup("");
2036 for (i = (var_mem_max - 1); i > my_prealloc; i--) {
2037 if (varmem_sizes[i] > 0) {
2038 if (type(i) > last_normal_node + last_whatsit_node) {
2039 node_counts[last_normal_node + last_whatsit_node + 1]++;
2040 } else if (type(i) == whatsit_node) {
2041 node_counts[(subtype(i) + last_normal_node + 1)]++;
2042 } else {
2043 node_counts[type(i)]++;
2047 for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) {
2048 if (node_counts[i] > 0) {
2049 int j =
2050 (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0);
2051 snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i],
2052 get_node_name((i > last_normal_node ? whatsit_node : i), j));
2053 ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1));
2054 strcpy(ss, s);
2055 strcat(ss, msg);
2056 free(s);
2057 s = ss;
2058 b = 1;
2061 #else
2062 s = strdup("");
2063 #endif
2064 return s;
2067 @ @c
2068 halfword list_node_mem_usage(void)
2070 halfword q = null;
2071 #ifdef CHECK_NODE_USAGE
2072 halfword p = null;
2073 halfword i, j;
2074 char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
2075 memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max);
2076 for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) {
2077 if (saved_varmem_sizes[i] > 0) {
2078 j = copy_node(i);
2079 if (p == null) {
2080 q = j;
2081 } else {
2082 vlink(p) = j;
2084 p = j;
2087 free(saved_varmem_sizes);
2088 #endif
2089 return q;
2092 @ @c
2093 void print_node_mem_stats(void)
2095 int i, b;
2096 halfword j;
2097 char msg[256];
2098 char *s;
2099 int free_chain_counts[MAX_CHAIN_SIZE] = { 0 };
2100 snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc));
2101 tprint_nl(msg);
2102 s = sprint_node_mem_usage();
2103 tprint_nl(" ");
2104 tprint(s);
2105 free(s);
2106 tprint(" nodes");
2107 tprint_nl(" avail lists: ");
2108 b = 0;
2109 for (i = 1; i < MAX_CHAIN_SIZE; i++) {
2110 for (j = free_chain[i]; j != null; j = vlink(j))
2111 free_chain_counts[i]++;
2112 if (free_chain_counts[i] > 0) {
2113 snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]);
2114 tprint(msg);
2115 b = 1;
2118 /* newline, if needed */
2119 print_nlp();
2122 /* this belongs in the web but i couldn't find the correct syntactic place */
2124 halfword new_span_node(halfword n, int s, scaled w)
2126 halfword p = new_node(span_node, 0);
2127 span_link(p) = n;
2128 span_span(p) = s;
2129 width(p) = w;
2130 return p;
2133 @* Attribute stuff.
2136 static halfword new_attribute_node(unsigned int i, int v)
2138 register halfword r = get_node(attribute_node_size);
2139 type(r) = attribute_node;
2140 attribute_id(r) = (halfword) i;
2141 attribute_value(r) = v;
2142 /* not used but nicer in print */
2143 subtype(r) = 0;
2144 return r;
2147 @ @c
2148 halfword copy_attribute_list(halfword n)
2150 halfword q = get_node(attribute_node_size);
2151 register halfword p = q;
2152 type(p) = attribute_list_node;
2153 attr_list_ref(p) = 0;
2154 n = vlink(n);
2155 while (n != null) {
2156 register halfword r = get_node(attribute_node_size);
2157 /* the link will be fixed automatically in the next loop */
2158 (void) memcpy((void *) (varmem + r), (void *) (varmem + n),
2159 (sizeof(memory_word) * attribute_node_size));
2160 vlink(p) = r;
2161 p = r;
2162 n = vlink(n);
2164 return q;
2167 @ @c
2168 void update_attribute_cache(void)
2170 halfword p;
2171 register int i;
2172 attr_list_cache = get_node(attribute_node_size);
2173 type(attr_list_cache) = attribute_list_node;
2174 attr_list_ref(attr_list_cache) = 0;
2175 p = attr_list_cache;
2176 for (i = 0; i <= max_used_attr; i++) {
2177 register int v = attribute(i);
2178 if (v > UNUSED_ATTRIBUTE) {
2179 register halfword r = new_attribute_node((unsigned) i, v);
2180 vlink(p) = r;
2181 p = r;
2184 if (vlink(attr_list_cache) == null) {
2185 free_node(attr_list_cache, attribute_node_size);
2186 attr_list_cache = null;
2188 return;
2191 @ @c
2192 void build_attribute_list(halfword b)
2194 if (max_used_attr >= 0) {
2195 if (attr_list_cache == cache_disabled|| attr_list_cache == null) {
2196 update_attribute_cache();
2197 if (attr_list_cache == null)
2198 return;
2200 attr_list_ref(attr_list_cache)++;
2201 node_attr(b) = attr_list_cache;
2205 @ @c
2206 halfword current_attribute_list(void)
2208 if (max_used_attr >= 0) {
2209 if (attr_list_cache == cache_disabled) {
2210 update_attribute_cache();
2212 return attr_list_cache ;
2214 return null ;
2218 @ @c
2219 void reassign_attribute(halfword n, halfword new)
2221 halfword old;
2222 old = node_attr(n);
2223 if (new == null) {
2224 /* there is nothing to assign but we need to check for an old value */
2225 if (old != null)
2226 delete_attribute_ref(old); /* also nulls attr field of n */
2227 } else if (old == null) {
2228 /* nothing is assigned so we just do that now */
2229 assign_attribute_ref(n,new);
2230 } else if (old != new) {
2231 /* something is assigned so we need to clean up and assign then */
2232 delete_attribute_ref(old);
2233 assign_attribute_ref(n,new);
2235 /* else: same value so there is no need to assign and change the refcount */
2236 node_attr(n) = new ;
2239 @ @c
2240 void delete_attribute_ref(halfword b)
2242 if (b != null) {
2243 if (type(b) == attribute_list_node){
2244 attr_list_ref(b)--;
2245 if (attr_list_ref(b) == 0) {
2246 if (b == attr_list_cache)
2247 attr_list_cache = cache_disabled;
2248 free_node_chain(b, attribute_node_size);
2250 /* maintain sanity */
2251 if (attr_list_ref(b) < 0) {
2252 attr_list_ref(b) = 0;
2254 } else {
2255 normal_error("nodes","trying to delete an attribute reference of a non attribute node");
2260 void reset_node_properties(halfword b)
2262 if (b != null) {
2263 lua_properties_reset(b);
2267 @ |p| is an attr list head, or zero
2269 halfword do_set_attribute(halfword p, int i, int val)
2271 register halfword q;
2272 register int j = 0;
2273 if (p == null) { /* add a new head \& node */
2274 q = get_node(attribute_node_size);
2275 type(q) = attribute_list_node;
2276 attr_list_ref(q) = 1;
2277 p = new_attribute_node((unsigned) i, val);
2278 vlink(q) = p;
2279 return q;
2281 q = p;
2282 if (vlink(p) != null) {
2283 while (vlink(p) != null) {
2284 int t = attribute_id(vlink(p));
2285 if (t == i && attribute_value(vlink(p)) == val)
2286 return q; /* no need to do anything */
2287 if (t >= i)
2288 break;
2289 j++;
2290 p = vlink(p);
2293 p = q;
2294 while (j-- > 0)
2295 p = vlink(p);
2296 if (attribute_id(vlink(p)) == i) {
2297 attribute_value(vlink(p)) = val;
2298 } else { /* add a new node */
2299 halfword r = new_attribute_node((unsigned) i, val);
2300 vlink(r) = vlink(p);
2301 vlink(p) = r;
2303 return q;
2304 } else {
2305 normal_error("nodes","trying to set an attribute fails, case 1");
2306 return null ;
2310 @ @c
2311 void set_attribute(halfword n, int i, int val)
2313 register halfword p;
2314 register int j = 0;
2315 /* not all nodes can have an attribute list */
2316 if (!nodetype_has_attributes(type(n)))
2317 return;
2318 /* if we have no list, we create one and quit */
2319 p = node_attr(n);
2320 if (p == null) { /* add a new head \& node */
2321 p = get_node(attribute_node_size);
2322 type(p) = attribute_list_node;
2323 attr_list_ref(p) = 1;
2324 node_attr(n) = p;
2325 p = new_attribute_node((unsigned) i, val);
2326 vlink(node_attr(n)) = p;
2327 return;
2329 /* we check if we have this attribute already and quit if the value stays the same */
2330 if (vlink(p) != null) {
2331 while (vlink(p) != null) {
2332 int t = attribute_id(vlink(p));
2333 if (t == i && attribute_value(vlink(p)) == val)
2334 return;
2335 if (t >= i)
2336 break;
2337 j++;
2338 p = vlink(p);
2340 /* j has now the position (if found) .. we assume a sorted list ! */
2341 p = node_attr(n);
2343 if (attr_list_ref(p) == 0 ) {
2344 /* the list is invalid i.e. freed already */
2345 formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n);
2346 /* the still dangling list gets ref count 1 */
2347 attr_list_ref(p) = 1;
2348 } else if (attr_list_ref(p) == 1) {
2349 /* this can really happen HH-LS */
2350 if (p == attr_list_cache) {
2351 /* we can invalidate the cache setting */
2352 /* attr_list_cache = cache_disabled */
2353 /* or save the list, as done below */
2354 p = copy_attribute_list(p);
2355 node_attr(n) = p;
2356 /* the copied list gets ref count 1 */
2357 attr_list_ref(p) = 1;
2359 } else {
2360 /* the list is used multiple times so we make a copy */
2361 p = copy_attribute_list(p);
2362 /* we decrement the ref count or the original */
2363 delete_attribute_ref(node_attr(n));
2364 node_attr(n) = p;
2365 /* the copied list gets ref count 1 */
2366 attr_list_ref(p) = 1;
2370 /* we go to position j in the list */
2371 while (j-- > 0)
2372 p = vlink(p);
2373 /* if we have a hit we just set the value otherwise we add a new node */
2374 if (attribute_id(vlink(p)) == i) {
2375 attribute_value(vlink(p)) = val;
2376 } else { /* add a new node */
2377 halfword r = new_attribute_node((unsigned) i, val);
2378 vlink(r) = vlink(p);
2379 vlink(p) = r;
2381 } else {
2382 normal_error("nodes","trying to set an attribute fails, case 2");
2386 @ @c
2387 int unset_attribute(halfword n, int i, int val)
2389 register halfword p;
2390 register int t;
2391 register int j = 0;
2393 if (!nodetype_has_attributes(type(n)))
2394 return null;
2395 p = node_attr(n);
2396 if (p == null)
2397 return UNUSED_ATTRIBUTE;
2398 if (attr_list_ref(p) == 0) {
2399 formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n);
2400 return UNUSED_ATTRIBUTE;
2402 if (vlink(p) != null) {
2403 while (vlink(p) != null) {
2404 t = attribute_id(vlink(p));
2405 if (t > i)
2406 return UNUSED_ATTRIBUTE;
2407 if (t == i) {
2408 p = vlink(p);
2409 break;
2411 j++;
2412 p = vlink(p);
2414 if (attribute_id(p) != i)
2415 return UNUSED_ATTRIBUTE;
2416 /* if we are still here, the attribute exists */
2417 p = node_attr(n);
2418 if (attr_list_ref(p) > 1 || p == attr_list_cache) {
2419 halfword q = copy_attribute_list(p);
2420 if (attr_list_ref(p) > 1) {
2421 delete_attribute_ref(node_attr(n));
2423 attr_list_ref(q) = 1;
2424 node_attr(n) = q;
2426 p = vlink(node_attr(n));
2427 while (j-- > 0)
2428 p = vlink(p);
2429 t = attribute_value(p);
2430 if (val == UNUSED_ATTRIBUTE || t == val) {
2431 attribute_value(p) = UNUSED_ATTRIBUTE;
2433 return t;
2434 } else {
2435 normal_error("nodes","trying to unset an attribute fails");
2436 return null;
2440 @ @c
2441 int has_attribute(halfword n, int i, int val)
2443 register halfword p;
2444 if (!nodetype_has_attributes(type(n)))
2445 return UNUSED_ATTRIBUTE;
2446 p = node_attr(n);
2447 if (p == null || vlink(p) == null)
2448 return UNUSED_ATTRIBUTE;
2449 p = vlink(p);
2450 while (p != null) {
2451 if (attribute_id(p) == i) {
2452 int ret = attribute_value(p);
2453 if (val == UNUSED_ATTRIBUTE || val == ret)
2454 return ret;
2455 return UNUSED_ATTRIBUTE;
2456 } else if (attribute_id(p) > i) {
2457 return UNUSED_ATTRIBUTE;
2459 p = vlink(p);
2461 return UNUSED_ATTRIBUTE;
2464 @ @c
2465 void print_short_node_contents(halfword p)
2467 switch (type(p)) {
2468 case hlist_node:
2469 case vlist_node:
2470 case ins_node:
2471 case whatsit_node:
2472 case mark_node:
2473 case adjust_node:
2474 case unset_node:
2475 print_char('[');
2476 print_char(']');
2477 break;
2478 case rule_node:
2479 print_char('|');
2480 break;
2481 case glue_node:
2482 if (! glue_is_zero(p))
2483 print_char(' ');
2484 break;
2485 case math_node:
2486 print_char('$');
2487 break;
2488 case disc_node:
2489 short_display(vlink(pre_break(p)));
2490 short_display(vlink(post_break(p)));
2491 break;
2495 @ @c
2496 static void show_pdftex_whatsit_rule_spec(int p)
2498 tprint("(");
2499 print_rule_dimen(height(p));
2500 print_char('+');
2501 print_rule_dimen(depth(p));
2502 tprint(")x");
2503 print_rule_dimen(width(p));
2506 @ Each new type of node that appears in our data structure must be capable
2507 of being displayed, copied, destroyed, and so on. The routines that we
2508 need for write-oriented whatsits are somewhat like those for mark nodes;
2509 other extensions might, of course, involve more subtlety here.
2512 static void print_write_whatsit(const char *s, pointer p)
2514 tprint_esc(s);
2515 if (write_stream(p) < 16)
2516 print_int(write_stream(p));
2517 else if (write_stream(p) == 16)
2518 print_char('*');
2519 else
2520 print_char('-');
2523 @ @c
2524 static void show_node_wrapup_core(int p)
2526 switch (subtype(p)) {
2527 case open_node:
2528 print_write_whatsit("openout", p);
2529 print_char('=');
2530 print_file_name(open_name(p), open_area(p), open_ext(p));
2531 break;
2532 case write_node:
2533 print_write_whatsit("write", p);
2534 print_mark(write_tokens(p));
2535 break;
2536 case close_node:
2537 print_write_whatsit("closeout", p);
2538 break;
2539 case special_node:
2540 tprint_esc("special");
2541 print_mark(write_tokens(p));
2542 break;
2543 case late_lua_node:
2544 show_late_lua(p);
2545 break;
2546 case save_pos_node:
2547 tprint_esc("savepos");
2548 break;
2549 case user_defined_node:
2550 tprint_esc("whatsit");
2551 print_int(user_node_id(p));
2552 print_char('=');
2553 switch (user_node_type(p)) {
2554 case 'a':
2555 tprint("<>");
2556 break;
2557 case 'n':
2558 tprint("[");
2559 show_node_list(user_node_value(p));
2560 tprint("]");
2561 break;
2562 case 's':
2563 print_char('"');
2564 print(user_node_value(p));
2565 print_char('"');
2566 break;
2567 case 't':
2568 print_mark(user_node_value(p));
2569 break;
2570 default: /* only 'd' */
2571 print_int(user_node_value(p));
2572 break;
2574 break;
2578 void show_node_wrapup_dvi(int p)
2582 void show_node_wrapup_pdf(int p)
2584 switch (subtype(p)) {
2585 case pdf_literal_node:
2586 show_pdf_literal(p);
2587 break;
2588 case pdf_colorstack_node:
2589 tprint_esc("pdfcolorstack ");
2590 print_int(pdf_colorstack_stack(p));
2591 switch (pdf_colorstack_cmd(p)) {
2592 case colorstack_set:
2593 tprint(" set ");
2594 break;
2595 case colorstack_push:
2596 tprint(" push ");
2597 break;
2598 case colorstack_pop:
2599 tprint(" pop");
2600 break;
2601 case colorstack_current:
2602 tprint(" current");
2603 break;
2604 default:
2605 confusion("colorstack");
2606 break;
2608 if (pdf_colorstack_cmd(p) <= colorstack_data)
2609 print_mark(pdf_colorstack_data(p));
2610 break;
2611 case pdf_setmatrix_node:
2612 tprint_esc("pdfsetmatrix");
2613 print_mark(pdf_setmatrix_data(p));
2614 break;
2615 case pdf_save_node:
2616 tprint_esc("pdfsave");
2617 break;
2618 case pdf_restore_node:
2619 tprint_esc("pdfrestore");
2620 break;
2621 case pdf_refobj_node:
2622 tprint_esc("pdfrefobj");
2623 if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) {
2624 if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
2625 tprint(" attr");
2626 lua_rawgeti(Luas, LUA_REGISTRYINDEX,
2627 obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)));
2628 print_char(' ');
2629 tprint((const char *) lua_tostring(Luas, -1));
2630 lua_pop(Luas, 1);
2632 tprint(" stream");
2634 if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p)))
2635 tprint(" file");
2636 if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
2637 lua_rawgeti(Luas, LUA_REGISTRYINDEX,
2638 obj_obj_data(static_pdf, pdf_obj_objnum(p)));
2639 print_char(' ');
2640 tprint((const char *) lua_tostring(Luas, -1));
2641 lua_pop(Luas, 1);
2643 break;
2644 case pdf_annot_node:
2645 tprint_esc("pdfannot");
2646 show_pdftex_whatsit_rule_spec(p);
2647 print_mark(pdf_annot_data(p));
2648 break;
2649 case pdf_start_link_node:
2650 tprint_esc("pdfstartlink");
2651 show_pdftex_whatsit_rule_spec(p);
2652 if (pdf_link_attr(p) != null) {
2653 tprint(" attr");
2654 print_mark(pdf_link_attr(p));
2656 tprint(" action");
2657 if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) {
2658 tprint(" user");
2659 print_mark(pdf_action_tokens(pdf_link_action(p)));
2660 return;
2662 if (pdf_action_file(pdf_link_action(p)) != null) {
2663 tprint(" file");
2664 print_mark(pdf_action_file(pdf_link_action(p)));
2666 switch (pdf_action_type(pdf_link_action(p))) {
2667 case pdf_action_goto:
2668 if (pdf_action_named_id(pdf_link_action(p)) > 0) {
2669 tprint(" goto name");
2670 print_mark(pdf_action_id(pdf_link_action(p)));
2671 } else {
2672 tprint(" goto num");
2673 print_int(pdf_action_id(pdf_link_action(p)));
2675 break;
2676 case pdf_action_page:
2677 tprint(" page");
2678 print_int(pdf_action_id(pdf_link_action(p)));
2679 print_mark(pdf_action_tokens(pdf_link_action(p)));
2680 break;
2681 case pdf_action_thread:
2682 if (pdf_action_named_id(pdf_link_action(p)) > 0) {
2683 tprint(" thread name");
2684 print_mark(pdf_action_id(pdf_link_action(p)));
2685 } else {
2686 tprint(" thread num");
2687 print_int(pdf_action_id(pdf_link_action(p)));
2689 break;
2690 default:
2691 normal_error("pdf backend", "unknown action type for link");
2692 break;
2694 break;
2695 case pdf_end_link_node:
2696 tprint_esc("pdfendlink");
2697 break;
2698 case pdf_dest_node:
2699 tprint_esc("pdfdest");
2700 if (pdf_dest_named_id(p) > 0) {
2701 tprint(" name");
2702 print_mark(pdf_dest_id(p));
2703 } else {
2704 tprint(" num");
2705 print_int(pdf_dest_id(p));
2707 print_char(' ');
2708 switch (pdf_dest_type(p)) {
2709 case pdf_dest_xyz:
2710 tprint("xyz");
2711 if (pdf_dest_xyz_zoom(p) != null) {
2712 tprint(" zoom");
2713 print_int(pdf_dest_xyz_zoom(p));
2715 break;
2716 case pdf_dest_fitbh:
2717 tprint("fitbh");
2718 break;
2719 case pdf_dest_fitbv:
2720 tprint("fitbv");
2721 break;
2722 case pdf_dest_fitb:
2723 tprint("fitb");
2724 break;
2725 case pdf_dest_fith:
2726 tprint("fith");
2727 break;
2728 case pdf_dest_fitv:
2729 tprint("fitv");
2730 break;
2731 case pdf_dest_fitr:
2732 tprint("fitr");
2733 show_pdftex_whatsit_rule_spec(p);
2734 break;
2735 case pdf_dest_fit:
2736 tprint("fit");
2737 break;
2738 default:
2739 tprint("unknown!");
2740 break;
2742 break;
2743 case pdf_thread_node:
2744 case pdf_start_thread_node:
2745 if (subtype(p) == pdf_thread_node)
2746 tprint_esc("pdfthread");
2747 else
2748 tprint_esc("pdfstartthread");
2749 tprint("(");
2750 print_rule_dimen(height(p));
2751 print_char('+');
2752 print_rule_dimen(depth(p));
2753 tprint(")x");
2754 print_rule_dimen(width(p));
2755 if (pdf_thread_attr(p) != null) {
2756 tprint(" attr");
2757 print_mark(pdf_thread_attr(p));
2759 if (pdf_thread_named_id(p) > 0) {
2760 tprint(" name");
2761 print_mark(pdf_thread_id(p));
2762 } else {
2763 tprint(" num");
2764 print_int(pdf_thread_id(p));
2766 break;
2767 case pdf_end_thread_node:
2768 tprint_esc("pdfendthread");
2769 break;
2770 default:
2771 break;
2775 @ Now we are ready for |show_node_list| itself. This procedure has been
2776 written to be ``extra robust'' in the sense that it should not crash or get
2777 into a loop even if the data structures have been messed up by bugs in
2778 the rest of the program. You can safely call its parent routine
2779 |show_box(p)| for arbitrary values of |p| when you are debugging \TeX.
2780 However, in the presence of bad data, the procedure may
2781 fetch a |memory_word| whose variant is different from the way it was stored;
2782 for example, it might try to read |mem[p].hh| when |mem[p]|
2783 contains a scaled integer, if |p| is a pointer that has been
2784 clobbered or chosen at random.
2787 @ |str_room| need not be checked; see |show_box|
2789 @ Recursive calls on |show_node_list| therefore use the following pattern:
2791 #define node_list_display(A) do { \
2792 append_char('.'); \
2793 show_node_list(A); \
2794 flush_char(); \
2795 } while (0)
2797 /* prints a node list symbolically */
2799 void show_node_list(int p)
2801 int n = 0; /* the number of items already printed at this level */
2802 halfword w;
2803 real g; /* a glue ratio, as a floating point number */
2804 if ((int) cur_length > depth_threshold) {
2805 if (p > null)
2806 tprint(" []"); /* indicate that there's been some truncation */
2807 return;
2809 while (p != null) {
2810 print_ln();
2811 print_current_string(); /* display the nesting history */
2812 if (int_par(tracing_online_code) < -2)
2813 print_int(p);
2814 incr(n);
2815 if (n > breadth_max) { /* time to stop */
2816 tprint("etc.");
2817 return;
2819 /* Display node |p| */
2820 if (is_char_node(p)) {
2821 print_font_and_char(p);
2822 if (is_ligature(p)) {
2823 /* Display ligature |p|; */
2824 tprint(" (ligature ");
2825 if (is_leftboundary(p))
2826 print_char('|');
2827 font_in_short_display = font(p);
2828 short_display(lig_ptr(p));
2829 if (is_rightboundary(p))
2830 print_char('|');
2831 print_char(')');
2833 } else {
2834 switch (type(p)) {
2835 case hlist_node:
2836 case vlist_node:
2837 case unset_node:
2838 /* Display box |p|; */
2839 if (type(p) == hlist_node)
2840 tprint_esc("h");
2841 else if (type(p) == vlist_node)
2842 tprint_esc("v");
2843 else
2844 tprint_esc("unset");
2845 tprint("box(");
2846 print_scaled(height(p));
2847 print_char('+');
2848 print_scaled(depth(p));
2849 tprint(")x");
2850 print_scaled(width(p));
2851 if (type(p) == unset_node) {
2852 /* Display special fields of the unset node |p|; */
2853 if (span_count(p) != min_quarterword) {
2854 tprint(" (");
2855 print_int(span_count(p) + 1);
2856 tprint(" columns)");
2858 if (glue_stretch(p) != 0) {
2859 tprint(", stretch ");
2860 print_glue(glue_stretch(p), glue_order(p), NULL);
2862 if (glue_shrink(p) != 0) {
2863 tprint(", shrink ");
2864 print_glue(glue_shrink(p), glue_sign(p), NULL);
2866 } else {
2867 /* Display the value of |glue_set(p)| */
2868 /* The code will have to change in this place if |glue_ratio| is
2869 a structured type instead of an ordinary |real|. Note that this routine
2870 should avoid arithmetic errors even if the |glue_set| field holds an
2871 arbitrary random value. The following code assumes that a properly
2872 formed nonzero |real| number has absolute value $2^{20}$ or more when
2873 it is regarded as an integer; this precaution was adequate to prevent
2874 floating point underflow on the author's computer.
2877 g = (real) (glue_set(p));
2878 if ((g != 0.0) && (glue_sign(p) != normal)) {
2879 tprint(", glue set ");
2880 if (glue_sign(p) == shrinking)
2881 tprint("- ");
2882 if (g > 20000.0 || g < -20000.0) {
2883 if (g > 0.0)
2884 print_char('>');
2885 else
2886 tprint("< -");
2887 print_glue(20000 * unity, glue_order(p), NULL);
2888 } else {
2889 print_glue(round(unity * g), glue_order(p), NULL);
2893 if (shift_amount(p) != 0) {
2894 tprint(", shifted ");
2895 print_scaled(shift_amount(p));
2897 tprint(", direction ");
2898 print_dir(box_dir(p));
2900 node_list_display(list_ptr(p)); /* recursive call */
2901 break;
2902 case rule_node:
2903 /* Display rule |p|; */
2904 if (subtype(p) == normal_rule) {
2905 tprint_esc("rule(");
2906 } else if (subtype(p) == empty_rule) {
2907 tprint_esc("norule(");
2908 } else if (subtype(p) == user_rule) {
2909 tprint_esc("userrule(");
2910 } else if (subtype(p) == box_rule) {
2911 tprint_esc("box(");
2912 } else if (subtype(p) == image_rule) {
2913 tprint_esc("image(");
2915 print_rule_dimen(height(p));
2916 print_char('+');
2917 print_rule_dimen(depth(p));
2918 tprint(")x");
2919 print_rule_dimen(width(p));
2920 break;
2921 case ins_node:
2922 /* Display insertion |p|; */
2923 tprint_esc("insert");
2924 print_int(subtype(p));
2925 tprint(", natural size ");
2926 print_scaled(height(p));
2927 tprint("; split(");
2928 print_spec(split_top_ptr(p), NULL);
2929 print_char(',');
2930 print_scaled(depth(p));
2931 tprint("); float cost ");
2932 print_int(float_cost(p));
2933 node_list_display(ins_ptr(p)); /* recursive call */
2934 break;
2935 case dir_node:
2936 if (dir_dir(p) < 0) {
2937 tprint_esc("enddir");
2938 print_char(' ');
2939 print_dir(dir_dir(p) + dir_swap);
2940 } else {
2941 tprint_esc("begindir");
2942 print_char(' ');
2943 print_dir(dir_dir(p));
2945 break;
2946 case local_par_node:
2947 tprint_esc("localpar");
2948 append_char('.');
2949 print_ln();
2950 print_current_string();
2951 tprint_esc("localinterlinepenalty");
2952 print_char('=');
2953 print_int(local_pen_inter(p));
2954 print_ln();
2955 print_current_string();
2956 tprint_esc("localbrokenpenalty");
2957 print_char('=');
2958 print_int(local_pen_broken(p));
2959 print_ln();
2960 print_current_string();
2961 tprint_esc("localleftbox");
2962 if (local_box_left(p) == null) {
2963 tprint("=null");
2964 } else {
2965 append_char('.');
2966 show_node_list(local_box_left(p));
2967 decr(cur_length);
2969 print_ln();
2970 print_current_string();
2971 tprint_esc("localrightbox");
2972 if (local_box_right(p) == null) {
2973 tprint("=null");
2974 } else {
2975 append_char('.');
2976 show_node_list(local_box_right(p));
2977 decr(cur_length);
2979 decr(cur_length);
2980 break;
2981 case boundary_node:
2982 if (subtype(p)==0) {
2983 tprint_esc("noboundary");
2984 } else {
2985 switch (subtype(p)) {
2986 case 1:
2987 tprint_esc("boundary");
2988 break;
2989 case 2:
2990 tprint_esc("protrusionboundary");
2991 break;
2992 case 3:
2993 tprint_esc("wordboundary");
2994 break;
2995 default:
2996 tprint_esc("boundary");
2997 print_char(':');
2998 print_int(subtype(p));
2999 break;
3001 print_char('=');
3002 print_int(boundary_value(p));
3004 break;
3005 case whatsit_node:
3006 w = subtype(p) ;
3007 if (w >= backend_first_pdf_whatsit) {
3008 show_node_wrapup_pdf(p);
3009 } else if (w >= backend_first_dvi_whatsit) {
3010 show_node_wrapup_dvi(p);
3011 } else {
3012 show_node_wrapup_core(p);
3014 break;
3015 case glue_node:
3016 /* Display glue |p|; */
3017 if (subtype(p) >= a_leaders) {
3018 /* Display leaders |p|; */
3019 tprint_esc("");
3020 switch (subtype(p)) {
3021 case a_leaders:
3022 break;
3023 case c_leaders:
3024 print_char('c');
3025 break;
3026 case x_leaders:
3027 print_char('x');
3028 break;
3029 case g_leaders:
3030 print_char('g');
3031 break;
3032 default:
3033 normal_warning("nodes","weird glue leader subtype ignored");
3035 tprint("leaders ");
3036 print_spec(p, NULL);
3037 node_list_display(leader_ptr(p)); /* recursive call */
3038 } else {
3039 tprint_esc("glue");
3040 if (subtype(p) != normal) {
3041 print_char('(');
3042 if ((subtype(p) - 1) < thin_mu_skip_code) {
3043 print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1));
3044 } else if (subtype(p) < cond_math_glue) {
3045 print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1));
3046 } else if (subtype(p) == cond_math_glue) {
3047 tprint_esc("nonscript");
3048 } else {
3049 tprint_esc("mskip");
3051 print_char(')');
3053 if (subtype(p) != cond_math_glue) {
3054 print_char(' ');
3055 if (subtype(p) < cond_math_glue)
3056 print_spec(p, NULL);
3057 else
3058 print_spec(p, "mu");
3061 break;
3062 case margin_kern_node:
3063 tprint_esc("kern");
3064 print_scaled(width(p));
3065 if (subtype(p) == left_side)
3066 tprint(" (left margin)");
3067 else
3068 tprint(" (right margin)");
3069 break;
3070 case kern_node:
3071 /* Display kern |p|; */
3072 /* An ``explicit'' kern value is indicated implicitly by an explicit space. */
3073 if (subtype(p) != mu_glue) {
3074 tprint_esc("kern");
3075 if (subtype(p) != normal)
3076 print_char(' ');
3077 print_scaled(width(p));
3078 if (subtype(p) == accent_kern)
3079 tprint(" (for accent)");
3080 } else {
3081 tprint_esc("mkern");
3082 print_scaled(width(p));
3083 tprint("mu");
3085 break;
3086 case math_node:
3087 /* Display math node |p|; */
3088 tprint_esc("math");
3089 if (subtype(p) == before)
3090 tprint("on");
3091 else
3092 tprint("off");
3093 if (!glue_is_zero(p)) {
3094 tprint(", glued ");
3095 print_spec(p, NULL);
3096 } else if (surround(p) != 0) {
3097 tprint(", surrounded ");
3098 print_scaled(surround(p));
3100 break;
3101 case penalty_node:
3102 /* Display penalty |p|; */
3103 tprint_esc("penalty ");
3104 print_int(penalty(p));
3105 break;
3106 case disc_node:
3107 /* Display discretionary |p|; */
3108 /* The |post_break| list of a discretionary node is indicated by a prefixed
3109 `\.{\char'174}' instead of the `\..' before the |pre_break| list. */
3110 tprint_esc("discretionary");
3111 print_int(disc_penalty(p));
3112 print_char('|');
3113 if (vlink(no_break(p)) != null) {
3114 tprint(" replacing ");
3115 node_list_display(vlink(no_break(p)));
3117 node_list_display(vlink(pre_break(p))); /* recursive call */
3118 append_char('|');
3119 show_node_list(vlink(post_break(p)));
3120 flush_char(); /* recursive call */
3121 break;
3122 case mark_node:
3123 /* Display mark |p|; */
3124 tprint_esc("mark");
3125 if (mark_class(p) != 0) {
3126 print_char('s');
3127 print_int(mark_class(p));
3129 print_mark(mark_ptr(p));
3130 break;
3131 case adjust_node:
3132 /* Display adjustment |p|; */
3133 tprint_esc("vadjust");
3134 if (subtype(p) != 0)
3135 tprint(" pre ");
3136 node_list_display(adjust_ptr(p)); /* recursive call */
3137 break;
3138 case glue_spec_node:
3139 tprint("<glue_spec ");
3140 print_spec(p, NULL);
3141 tprint(">");
3142 break;
3143 default:
3144 show_math_node(p);
3145 break;
3148 p = vlink(p);
3152 @ This routine finds the 'base' width of a horizontal box, using the same logic
3153 that \TeX82 used for \.{\\predisplaywidth} */
3156 pointer actual_box_width(pointer r, scaled base_width)
3158 scaled d; /* increment to |v| */
3159 scaled w = -max_dimen; /* calculated |size| */
3160 scaled v = shift_amount(r) + base_width; /* |w| plus possible glue amount */
3161 pointer p = list_ptr(r); /* current node when calculating |pre_display_size| */
3162 while (p != null) {
3163 if (is_char_node(p)) {
3164 d = glyph_width(p);
3165 goto FOUND;
3167 switch (type(p)) {
3168 case hlist_node:
3169 case vlist_node:
3170 case rule_node:
3171 d = width(p);
3172 goto FOUND;
3173 break;
3174 case margin_kern_node:
3175 d = width(p);
3176 break;
3177 case kern_node:
3178 d = width(p);
3179 break;
3180 case math_node:
3181 /* begin mathskip code */
3182 if (glue_is_zero(p)) {
3183 d = surround(p);
3184 break;
3185 } else {
3186 /* fall through */
3188 /* end mathskip code */
3189 case glue_node:
3190 /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set|
3191 values, since such values are subject to system-dependent rounding.
3192 System-dependent numbers are not allowed to infiltrate parameters like
3193 |pre_display_size|, since \TeX82 is supposed to make the same decisions on all
3194 machines.
3196 d = width(p);
3197 if (glue_sign(r) == stretching) {
3198 if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0))
3199 v = max_dimen;
3200 } else if (glue_sign(r) == shrinking) {
3201 if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0))
3202 v = max_dimen;
3204 if (subtype(p) >= a_leaders)
3205 goto FOUND;
3206 break;
3207 default:
3208 d = 0;
3209 break;
3211 if (v < max_dimen)
3212 v = v + d;
3213 goto NOT_FOUND;
3214 FOUND:
3215 if (v < max_dimen) {
3216 v = v + d;
3217 w = v;
3218 } else {
3219 w = max_dimen;
3220 break;
3222 NOT_FOUND:
3223 p = vlink(p);
3225 return w;
3228 @ @c
3229 halfword tail_of_list(halfword p)
3231 halfword q = p;
3232 while (vlink(q) != null)
3233 q = vlink(q);
3234 return q;
3239 @ @c
3240 int var_used;
3242 @ Attribute lists need two extra globals to increase processing efficiency.
3243 |max_used_attr| limits the test loop that checks for set attributes, and
3244 |attr_list_cache| contains a pointer to an already created attribute list. It is
3245 set to the special value |cache_disabled| when the current value can no longer be
3246 trusted: after an assignment to an attribute register, and after a group has
3247 ended.
3250 int max_used_attr; /* maximum assigned attribute id */
3251 halfword attr_list_cache;
3253 @ From the computer's standpoint, \TeX's chief mission is to create
3254 horizontal and vertical lists. We shall now investigate how the elements
3255 of these lists are represented internally as nodes in the dynamic memory.
3257 A horizontal or vertical list is linked together by |link| fields in
3258 the first word of each node. Individual nodes represent boxes, glue,
3259 penalties, or special things like discretionary hyphens; because of this
3260 variety, some nodes are longer than others, and we must distinguish different
3261 kinds of nodes. We do this by putting a `|type|' field in the first word,
3262 together with the link and an optional `|subtype|'.
3264 @ Character nodes appear only in horizontal lists, never in vertical lists.
3266 An |hlist_node| stands for a box that was made from a horizontal list.
3267 Each |hlist_node| is seven words long, and contains the following fields
3268 (in addition to the mandatory |type| and |link|, which we shall not
3269 mention explicitly when discussing the other node types): The |height| and
3270 |width| and |depth| are scaled integers denoting the dimensions of the
3271 box. There is also a |shift_amount| field, a scaled integer indicating
3272 how much this box should be lowered (if it appears in a horizontal list),
3273 or how much it should be moved to the right (if it appears in a vertical
3274 list). There is a |list_ptr| field, which points to the beginning of the
3275 list from which this box was fabricated; if |list_ptr| is |null|, the box
3276 is empty. Finally, there are three fields that represent the setting of
3277 the glue: |glue_set(p)| is a word of type |glue_ratio| that represents
3278 the proportionality constant for glue setting; |glue_sign(p)| is
3279 |stretching| or |shrinking| or |normal| depending on whether or not the
3280 glue should stretch or shrink or remain rigid; and |glue_order(p)|
3281 specifies the order of infinity to which glue setting applies (|normal|,
3282 |sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used.
3284 @ The |new_null_box| function returns a pointer to an |hlist_node| in
3285 which all subfields have the values corresponding to `\.{\\hbox\{\}}'.
3286 The |subtype| field is set to |min_quarterword|, since that's the desired
3287 |span_count| value if this |hlist_node| is changed to an |unset_node|.
3290 halfword new_null_box(void)
3291 { /* creates a new box node */
3292 halfword p = new_node(hlist_node, min_quarterword);
3293 box_dir(p) = text_direction;
3294 return p;
3297 @ A |vlist_node| is like an |hlist_node| in all respects except that it
3298 contains a vertical list.
3300 @ A |rule_node| stands for a solid black rectangle; it has |width|,
3301 |depth|, and |height| fields just as in an |hlist_node|. However, if
3302 any of these dimensions is $-2^{30}$, the actual value will be determined
3303 by running the rule up to the boundary of the innermost enclosing box.
3304 This is called a ``running dimension.'' The |width| is never running in
3305 an hlist; the |height| and |depth| are never running in a~vlist.
3307 @ A new rule node is delivered by the |new_rule| function. It
3308 makes all the dimensions ``running,'' so you have to change the
3309 ones that are not allowed to run.
3312 halfword new_rule(int s)
3314 halfword p = new_node(rule_node,s);
3315 return p;
3318 @ Insertions are represented by |ins_node| records, where the |subtype|
3319 indicates the corresponding box number. For example, `\.{\\insert 250}'
3320 leads to an |ins_node| whose |subtype| is |250+min_quarterword|.
3321 The |height| field of an |ins_node| is slightly misnamed; it actually holds
3322 the natural height plus depth of the vertical list being inserted.
3323 The |depth| field holds the |split_max_depth| to be used in case this
3324 insertion is split, and the |split_top_ptr| points to the corresponding
3325 |split_top_skip|. The |float_cost| field holds the |floating_penalty| that
3326 will be used if this insertion floats to a subsequent page after a
3327 split insertion of the same class. There is one more field, the
3328 |ins_ptr|, which points to the beginning of the vlist for the insertion.
3330 @ A |mark_node| has a |mark_ptr| field that points to the reference count
3331 of a token list that contains the user's \.{\\mark} text.
3332 In addition there is a |mark_class| field that contains the mark class.
3334 @ An |adjust_node|, which occurs only in horizontal lists,
3335 specifies material that will be moved out into the surrounding
3336 vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}'
3337 operation. The |adjust_ptr| field points to the vlist containing this
3338 material.
3340 @ A |glyph_node|, which occurs only in horizontal lists, specifies a
3341 glyph in a particular font, along with its attribute list. Older
3342 versions of \TeX\ could use token memory for characters, because the
3343 font,char combination would fit in a single word (both values were
3344 required to be strictly less than $2^{16}$). In LuaTeX, room is
3345 needed for characters that are larger than that, as well as a pointer
3346 to a potential attribute list, and the two displacement values.
3348 In turn, that made the node so large that it made sense to merge
3349 ligature glyphs as well, as that requires only one extra pointer. A
3350 few extra classes of glyph nodes will be introduced later. The
3351 unification of all those types makes it easier to manipulate lists of
3352 glyphs. The subtype differentiates various glyph kinds.
3354 First, here is a function that returns a pointer to a glyph node for a given
3355 glyph in a given font. If that glyph doesn't exist, |null| is returned
3356 instead. Nodes of this subtype are directly created only for accents
3357 and their base (through |make_accent|), and math nucleus items (in the
3358 conversion from |mlist| to |hlist|).
3361 halfword new_glyph(int f, int c)
3363 halfword p = null; /* the new node */
3364 if ((f == 0) || (char_exists(f, c))) {
3365 p = new_glyph_node();
3366 set_to_glyph(p);
3367 font(p) = f;
3368 character(p) = c;
3370 return p;
3373 @ A subset of the glyphs nodes represent ligatures: characters
3374 fabricated from the interaction of two or more actual characters. The
3375 characters that generated the ligature have not been forgotten, since
3376 they are needed for diagnostic messages; the |lig_ptr| field points to
3377 a linked list of character nodes for all original characters that have
3378 been deleted. (This list might be empty if the characters that
3379 generated the ligature were retained in other nodes.)
3381 The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if
3382 the original source of the ligature included implicit left and/or
3383 right boundaries. These nodes are created by the C function |new_ligkern|.
3385 A third general type of glyphs could be called a character, as it
3386 only appears in lists that are not yet processed by the ligaturing and
3387 kerning steps of the program.
3389 |main_control| inserts these, and they are later converted to
3390 |subtype_normal| by |new_ligkern|.
3393 quarterword norm_min(int h)
3395 if (h <= 0)
3396 return 1;
3397 else if (h >= 255)
3398 return 255;
3399 else
3400 return (quarterword) h;
3403 halfword new_char(int f, int c)
3405 halfword p; /* the new node */
3406 p = new_glyph_node();
3407 set_to_character(p);
3408 font(p) = f;
3409 character(p) = c;
3410 lang_data(p) = make_lang_data(uc_hyph, cur_lang, left_hyphen_min, right_hyphen_min);
3411 return p;
3414 @ Left and right ghost glyph nodes are the result of \.{\\leftghost}
3415 and \.{\\rightghost}, respectively. They are going to be removed by
3416 |new_ligkern|, at the end of which they are no longer needed.
3418 @ Here are a few handy helpers used by the list output routines.
3421 scaled glyph_width(halfword p)
3423 scaled w = char_width(font(p), character(p));
3424 return w;
3427 scaled glyph_height(halfword p)
3429 scaled w = char_height(font(p), character(p)) + y_displace(p);
3430 if (w < 0)
3431 w = 0;
3432 return w;
3435 scaled glyph_depth(halfword p)
3437 scaled w = char_depth(font(p), character(p));
3438 if (y_displace(p) > 0)
3439 w = w - y_displace(p);
3440 if (w < 0)
3441 w = 0;
3442 return w;
3445 @ A |disc_node|, which occurs only in horizontal lists, specifies a
3446 ``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text
3447 that starts at |pre_break(p)| will precede the break, the text that starts at
3448 |post_break(p)| will follow the break, and text that appears in
3449 |no_break(p)| nodes will be ignored. For example, an ordinary
3450 discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with
3451 |pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|,
3452 and |no_break=null|.
3454 {TODO: Knuth said: All three of the discretionary texts must be lists
3455 that consist entirely of character, kern, box and rule nodes.}
3457 If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this
3458 break. Otherwise the |hyphen_penalty| will be charged. The texts will
3459 actually be substituted into the list by the line-breaking algorithm if it
3460 decides to make the break, and the discretionary node will disappear at
3461 that time; thus, the output routine sees only discretionaries that were
3462 not chosen.
3465 halfword new_disc(void)
3466 { /* creates an empty |disc_node| */
3467 halfword p = new_node(disc_node, 0);
3468 disc_penalty(p) = int_par(hyphen_penalty_code);
3469 return p;
3472 @ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
3473 |subtype| field in its first word says what `\\{whatsit}' it is, and
3474 implicitly determines the node size (which must be 2 or more) and the
3475 format of the remaining words. When a |whatsit_node| is encountered
3476 in a list, special actions are invoked; knowledgeable people who are
3477 careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
3478 things by adding code at the end of the program. For example, there
3479 might be a `\TeX nicolor' extension to specify different colors of ink,
3480 @^extensions to \TeX@>
3481 and the whatsit node might contain the desired parameters.
3483 The present implementation of \TeX\ treats the features associated with
3484 `\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
3485 illustrate how such routines might be coded. We shall defer further
3486 discussion of extensions until the end of this program.
3488 @ A |math_node|, which occurs only in horizontal lists, appears before and
3489 after mathematical formulas. The |subtype| field is |before| before the
3490 formula and |after| after it. There is a |surround| field, which represents
3491 the amount of surrounding space inserted by \.{\\mathsurround}.
3494 halfword new_math(scaled w, int s)
3496 halfword p = new_node(math_node, s);
3497 surround(p) = w;
3498 return p;
3501 @ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|,
3502 |rule_node|, |ins_node|, |mark_node|, |adjust_node|,
3503 |disc_node|, |whatsit_node|, and |math_node| are at the low end of the
3504 type codes, by permitting a break at glue in a list if and only if the
3505 |type| of the previous node is less than |math_node|. Furthermore, a
3506 node is discarded after a break if its type is |math_node| or~more.
3508 @ A |glue_node| represents glue in a list. However, it is really only
3509 a pointer to a separate glue specification, since \TeX\ makes use of the
3510 fact that many essentially identical nodes of glue are usually present.
3511 If |p| points to a |glue_node|, |glue_ptr(p)| points to
3512 another packet of words that specify the stretch and shrink components, etc.
3514 Glue nodes also serve to represent leaders; the |subtype| is used to
3515 distinguish between ordinary glue (which is called |normal|) and the three
3516 kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|).
3517 The |leader_ptr| field points to a rule node or to a box node containing the
3518 leaders; it is set to |null| in ordinary glue nodes.
3520 Many kinds of glue are computed from \TeX's ``skip'' parameters, and
3521 it is helpful to know which parameter has led to a particular glue node.
3522 Therefore the |subtype| is set to indicate the source of glue, whenever
3523 it originated as a parameter. We will be defining symbolic names for the
3524 parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|,
3525 etc.); it suffices for now to say that the |subtype| of parametric glue
3526 will be the same as the parameter number, plus~one.
3528 @ In math formulas there are two more possibilities for the |subtype| in a
3529 glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu}
3530 instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}'
3531 feature that cancels the glue node immediately following if it appears
3532 in a subscript.
3534 @ A glue specification has a halfword reference count in its first word,
3535 @^reference counts@>
3536 representing |null| plus the number of glue nodes that point to it (less one).
3537 Note that the reference count appears in the same position as
3538 the |link| field in list nodes; this is the field that is initialized
3539 to |null| when a node is allocated, and it is also the field that is flagged
3540 by |empty_flag| in empty nodes.
3542 Glue specifications also contain three |scaled| fields, for the |width|,
3543 |stretch|, and |shrink| dimensions. Finally, there are two one-byte
3544 fields called |stretch_order| and |shrink_order|; these contain the
3545 orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|)
3546 corresponding to the stretch and shrink values.
3548 @ Here is a function that returns a pointer to a copy of a glue spec.
3549 The reference count in the copy is |null|, because there is assumed
3550 to be exactly one reference to the new specification.
3553 halfword new_spec(halfword p)
3555 return copy_node(p == null ? zero_glue : p);
3558 @ And here's a function that creates a glue node for a given parameter
3559 identified by its code number; for example,
3560 |new_param_glue(line_skip_code)| returns a pointer to a glue node for the
3561 current \.{\\lineskip}.
3564 halfword new_param_glue(int n)
3566 halfword p = new_node(glue_node, n + 1);
3567 halfword q = glue_par(n);
3568 width(p) = width(q);
3569 stretch(p) = stretch(q);
3570 shrink(p) = shrink(q);
3571 stretch_order(p) = stretch_order(q);
3572 shrink_order(p) = shrink_order(q);
3573 return p;
3576 @ Glue nodes that are more or less anonymous are created by |new_glue|,
3577 whose argument points to a glue specification.
3580 halfword new_glue(halfword q)
3582 halfword p = new_node(glue_node, normal);
3583 width(p) = width(q);
3584 stretch(p) = stretch(q);
3585 shrink(p) = shrink(q);
3586 stretch_order(p) = stretch_order(q);
3587 shrink_order(p) = shrink_order(q);
3588 return p;
3591 @ Still another subroutine is needed: This one is sort of a combination
3592 of |new_param_glue| and |new_glue|. It creates a glue node for one of
3593 the current glue parameters, but it makes a fresh copy of the glue
3594 specification, since that specification will probably be subject to change,
3595 while the parameter will stay put.
3598 The global variable |temp_ptr| is set to the address of the new spec.
3602 halfword new_skip_param(int n)
3604 halfword p = new_node(glue_node, n + 1);
3605 halfword q = glue_par(n);
3606 width(p) = width(q);
3607 stretch(p) = stretch(q);
3608 shrink(p) = shrink(q);
3609 stretch_order(p) = stretch_order(q);
3610 shrink_order(p) = shrink_order(q);
3611 return p;
3614 @ A |kern_node| has a |width| field to specify a (normally negative)
3615 amount of spacing. This spacing correction appears in horizontal lists
3616 between letters like A and V when the font designer said that it looks
3617 better to move them closer together or further apart. A kern node can
3618 also appear in a vertical list, when its `|width|' denotes additional
3619 spacing in the vertical direction. The |subtype| is either |normal| (for
3620 kerns inserted from font information or math mode calculations) or |explicit|
3621 (for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern|
3622 (for kerns inserted from non-math accents) or |mu_glue| (for kerns
3623 inserted from \.{\\mkern} specifications in math formulas).
3625 @ The |new_kern| function creates a kern node having a given width.
3628 halfword new_kern(scaled w)
3630 halfword p = new_node(kern_node, normal);
3631 width(p) = w;
3632 return p;
3635 @ A |penalty_node| specifies the penalty associated with line or page
3636 breaking, in its |penalty| field. This field is a fullword integer, but
3637 the full range of integer values is not used: Any penalty |>=10000| is
3638 treated as infinity, and no break will be allowed for such high values.
3639 Similarly, any penalty |<=-10000| is treated as negative infinity, and a
3640 break will be forced.
3642 @ Anyone who has been reading the last few sections of the program will
3643 be able to guess what comes next.
3646 halfword new_penalty(int m)
3648 halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */
3649 penalty(p) = m;
3650 return p;
3653 @ You might think that we have introduced enough node types by now. Well,
3654 almost, but there is one more: An |unset_node| has nearly the same format
3655 as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign}
3656 or \.{\\valign} that are not yet in their final form, since the box
3657 dimensions are their ``natural'' sizes before any glue adjustment has been
3658 made. The |glue_set| word is not present; instead, we have a |glue_stretch|
3659 field, which contains the total stretch of order |glue_order| that is
3660 present in the hlist or vlist being boxed.
3661 Similarly, the |shift_amount| field is replaced by a |glue_shrink| field,
3662 containing the total shrink of order |glue_sign| that is present.
3663 The |subtype| field is called |span_count|; an unset box typically
3664 contains the data for |qo(span_count)+1| columns.
3665 Unset nodes will be changed to box nodes when alignment is completed.
3667 In fact, there are still more types coming. When we get to math formula
3668 processing we will see that a |style_node| has |type=14|; and a number
3669 of larger type codes will also be defined, for use in math mode only.
3671 Warning: If any changes are made to these data structure layouts, such as
3672 changing any of the node sizes or even reordering the words of nodes,
3673 the |copy_node_list| procedure and the memory initialization code
3674 below may have to be changed. Such potentially dangerous parts of the
3675 program are listed in the index under `data structure assumptions'.
3676 @!@^data structure assumptions@>
3677 However, other references to the nodes are made symbolically in terms of
3678 the \.{WEB} macro definitions above, so that format changes will leave
3679 \TeX's other algorithms intact.
3680 @^system dependencies@>
3682 @ This function creates a |local_paragraph| node
3686 halfword make_local_par_node(int mode)
3688 int callback_id;
3689 halfword q;
3690 halfword p = new_node(local_par_node,0);
3691 local_pen_inter(p) = local_inter_line_penalty;
3692 local_pen_broken(p) = local_broken_penalty;
3693 if (local_left_box != null) {
3694 q = copy_node_list(local_left_box);
3695 local_box_left(p) = q;
3696 local_box_left_width(p) = width(local_left_box);
3698 if (local_right_box != null) {
3699 q = copy_node_list(local_right_box);
3700 local_box_right(p) = q;
3701 local_box_right_width(p) = width(local_right_box);
3703 local_par_dir(p) = par_direction;
3704 /* callback with node passed */
3705 callback_id = callback_defined(insert_local_par_callback);
3706 if (callback_id > 0) {
3707 int sfix = lua_gettop(Luas);
3708 if (!get_callback(Luas, callback_id)) {
3709 lua_settop(Luas, sfix);
3710 return p;
3712 nodelist_to_lua(Luas, p);
3713 lua_push_local_par_mode(Luas,mode)
3714 if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */
3715 char errmsg[256]; /* temp hack ... we will have a formatted error */
3716 snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1));
3717 errmsg[255]='\0';
3718 lua_settop(Luas, sfix);
3719 normal_error("insert_local_par",errmsg); /* to be done */
3720 return p;
3722 lua_settop(Luas, sfix);
3724 /* done */
3725 return p;