another update to the manual (+ some cleanup in libs) (HH)
[luatex.git] / source / texk / web2c / luatexdir / tex / texnodes.w
blobd5d2d2364b620180478ea38652faac9b990b04d6
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_penalty[] = {
282 "userpenalty", NULL
284 const char *node_subtypes_kern[] = {
285 "fontkern", "userkern", "accentkern", "italiccorrection", NULL
287 const char *node_subtypes_rule[] = {
288 "normal", "box", "image", "empty", "user", NULL
290 const char *node_subtypes_glyph[] = {
291 "character", "glyph", "ligature", "ghost", "left", "right", NULL
293 const char *node_subtypes_disc[] = {
294 "discretionary", "explicit", "automatic", "regular", "first", "second", NULL
296 const char *node_subtypes_marginkern[] = {
297 "left", "right", NULL
299 const char *node_subtypes_list[] = {
300 "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL
302 const char *node_subtypes_adjust[] = {
303 "normal", "pre", NULL
305 const char *node_subtypes_math[] = {
306 "beginmath", "endmath", NULL
308 const char *node_subtypes_noad[] = {
309 "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close",
310 "punct", "inner", "under", "over", "vcenter", NULL
312 const char *node_subtypes_radical[] = {
313 "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder",
314 "udelimiterover", NULL
316 const char *node_subtypes_accent[] = {
317 "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL,
319 const char *node_subtypes_fence[] = {
320 "unset", "left", "middle", "right", NULL
323 node_info node_data[] = { /* the last entry in a row is the etex number */
324 { hlist_node, box_node_size, node_fields_list, "hlist", 1 },
325 { vlist_node, box_node_size, node_fields_list, "vlist", 2 },
326 { rule_node, rule_node_size, node_fields_rule, "rule", 3 },
327 { ins_node, ins_node_size, node_fields_insert, "ins", 4 },
328 { mark_node, mark_node_size, node_fields_mark, "mark", 5 },
329 { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 },
330 { boundary_node, boundary_size, node_fields_boundary, "boundary", -1 },
331 { disc_node, disc_node_size, node_fields_disc, "disc", 8 },
332 { whatsit_node, -1, NULL, "whatsit", 9 },
333 { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 },
334 { dir_node, dir_node_size, node_fields_dir, "dir", -1 },
335 { math_node, math_node_size, node_fields_math, "math", 10 },
336 { glue_node, glue_node_size, node_fields_glue, "glue", 11 },
337 { kern_node, kern_node_size, node_fields_kern, "kern", 12 },
338 { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 },
339 { unset_node, box_node_size, node_fields_unset, "unset", 14 },
340 { style_node, style_node_size, node_fields_style, "style", 15 },
341 { choice_node, style_node_size, node_fields_choice, "choice", 15 },
342 { simple_noad, noad_size, node_fields_ord, "noad", 15 },
343 { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 },
344 { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 },
345 { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 },
346 { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 },
347 { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 },
348 { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 },
349 { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 },
350 { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 },
351 { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 },
352 { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 },
353 { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 },
354 { align_record_node, box_node_size, NULL, "align_record", -1 },
355 { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 },
356 { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 },
357 { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 },
358 { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 },
359 { expr_node, expr_node_size, NULL, "expr_stack", -1 },
360 { nesting_node, nesting_node_size, NULL, "nested_list", -1 },
361 { span_node, span_node_size, NULL, "span", -1 },
362 { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 },
363 { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 },
364 { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 },
365 { temp_node, temp_node_size, NULL, "temp", -1 },
366 { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 },
367 { movement_node, movement_node_size, NULL, "movement_stack", -1 },
368 { if_node, if_node_size, NULL, "if_stack", -1 },
369 { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 },
370 { hyphenated_node, active_node_size, NULL, "hyphenated", -1 },
371 { delta_node, delta_node_size, NULL, "delta", -1 },
372 { passive_node, passive_node_size, NULL, "passive", -1 },
373 { shape_node, variable_node_size, NULL, "shape", -1 },
374 { -1, -1, NULL, NULL, -1 },
377 #define last_normal_node shape_node
379 const char *node_subtypes_pdf_destination[] = {
380 "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL
382 const char *node_subtypes_pdf_literal[] = {
383 "origin", "page", "direct", NULL
386 node_info whatsit_node_data[] = {
387 { open_node, open_node_size, node_fields_whatsit_open, "open", -1 },
388 { write_node, write_node_size, node_fields_whatsit_write, "write", -1 },
389 { close_node, close_node_size, node_fields_whatsit_close, "close", -1 },
390 { special_node, special_node_size, node_fields_whatsit_special, "special", -1 },
391 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
392 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
393 { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 },
394 { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 },
395 { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 },
396 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
397 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
398 { fake_node, fake_node_size, NULL, fake_node_name, -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 /* here starts the dvi backend section, todo: a separate list */
404 /* nothing for dvi */
405 /* here starts the pdf backend section, todo: a separate list */
406 { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 },
407 { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 },
408 { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 },
409 { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 },
410 { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 },
411 { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 },
412 { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 },
413 { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 },
414 { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 },
415 { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 },
416 { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 },
417 { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 },
418 { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 },
419 { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 },
420 { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 },
421 { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 },
422 /* done */
423 { -1, -1, NULL, NULL, -1 },
426 #define last_whatsit_node pdf_restore_node
429 When we copy a node list, there are several possibilities: we do the same as a new node,
430 we copy the entry to the table in properties (a reference), we do a deep copy of a table
431 in the properties, we create a new table and give it the original one as a metatable.
432 After some experiments (that also included timing) with these scenarios I decided that a
433 deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable
434 variant were both ok, although the second ons is slower. The most important aspect to keep
435 in mind is that references to other nodes in properties no longer can be valid for that
436 copy. We could use two tables (one unique and one shared) or metatables but that only
437 complicates matters.
439 When defining a new node, we could already allocate a table but it is rather easy to do
440 that at the lua end e.g. using a metatable __index method. That way it is under macro
441 package control.
443 When deleting a node, we could keep the slot (e.g. setting it to false) but it could make
444 memory consumption raise unneeded when we have temporary large node lists and after that
445 only small lists.
447 So, in the end this is what we ended up with. For the record, I also experimented with the
448 following:
450 - copy attributes to the properties so that we have fast access at the lua end: in the end
451 the overhead is not compensated by speed and convenience, in fact, attributes are not
452 that slow when it comes to accessing them
454 - a bitset in the node but again the gain compared to attributes is neglectable and it also
455 demands a pretty string agreement over what bit represents what, and this is unlikely to
456 succeed in the tex community (I could use it for font handling, which is cross package,
457 but decided that it doesn't pay off
459 In case one wonders why properties make sense then, well, it is not so much speed that we
460 gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and
461 this mechanism makes sure that properties are cleaned up when a node is freed. Also, the
462 advantage of a more or less global properties table is that we stay at the lua end. An
463 alternative is to store a reference in the node itself but that is complicated by the fact
464 that the register has some limitations (no numeric keys) and we also don't want to mess with
465 it too much.
468 int lua_properties_level = 0 ; /* can be private */
469 int lua_properties_enabled = 0 ;
470 int lua_properties_use_metatable = 0 ;
473 We keep track of nesting so that we don't oveflow the stack, and, what is more
474 important, don't keep resolving the registry index.
477 #define lua_properties_push do { \
478 if (lua_properties_enabled) { \
479 lua_properties_level = lua_properties_level + 1 ; \
480 if (lua_properties_level == 1) { \
481 lua_get_metatablelua_l(Luas,node_properties); \
484 } while(0)
486 #define lua_properties_pop do { \
487 if (lua_properties_enabled) { \
488 if (lua_properties_level == 1) \
489 lua_pop(Luas,1); \
490 lua_properties_level = lua_properties_level - 1 ; \
492 } while(0)
494 /* No setting is needed: */
496 #define lua_properties_set(target) do { \
497 } while(0)
499 /* Resetting boils down to nilling. */
501 #define lua_properties_reset(target) do { \
502 if (lua_properties_enabled) { \
503 if (lua_properties_level == 0) { \
504 lua_get_metatablelua_l(Luas,node_properties); \
505 lua_pushnil(Luas); \
506 lua_rawseti(Luas,-2,target); \
507 lua_pop(Luas,1); \
508 } else { \
509 lua_pushnil(Luas); \
510 lua_rawseti(Luas,-2,target); \
513 } while(0)
516 For a moment I considered supporting all kind of data types but in practice
517 that makes no sense. So we stick to a cheap shallow copy with as option a
518 metatable. Btw, a deep copy would look like this:
520 static void copy_lua_table(lua_State* L, int index) {
521 lua_newtable(L);
522 lua_pushnil(L);
523 while(lua_next(L, index-1) != 0) {
524 lua_pushvalue(L, -2);
525 lua_insert(L, -2);
526 if (lua_type(L,-1)==LUA_TTABLE)
527 copy_lua_table(L,-1);
528 lua_settable(L, -4);
530 lua_pop(L,1);
533 #define lua_properties_copy(target, source) do { \
534 if (lua_properties_enabled) { \
535 lua_pushinteger(Luas,source); \
536 lua_rawget(Luas,-2); \
537 if (lua_type(Luas,-1)==LUA_TTABLE) { \
538 copy_lua_table(Luas,-1); \
539 lua_pushinteger(Luas,target); \
540 lua_insert(Luas,-2); \
541 lua_rawset(Luas,-3); \
542 } else { \
543 lua_pop(Luas,1); \
546 } while(0)
550 /* isn't there a faster way to metatable? */
552 #define lua_properties_copy(target,source) do { \
553 if (lua_properties_enabled) { \
554 if (lua_properties_level == 0) { \
555 lua_get_metatablelua_l(Luas,node_properties); \
556 lua_rawgeti(Luas,-1,source); \
557 if (lua_type(Luas,-1)==LUA_TTABLE) { \
558 if (lua_properties_use_metatable) { \
559 lua_newtable(Luas); \
560 lua_insert(Luas,-2); \
561 lua_setfield(Luas,-2,"__index"); \
562 lua_newtable(Luas); \
563 lua_insert(Luas,-2); \
564 lua_setmetatable(Luas,-2); \
566 lua_rawseti(Luas,-2,target); \
567 } else { \
568 lua_pop(Luas,1); \
570 lua_pop(Luas,1); \
571 } else { \
572 lua_rawgeti(Luas,-1,source); \
573 if (lua_type(Luas,-1)==LUA_TTABLE) { \
574 if (lua_properties_use_metatable) { \
575 lua_newtable(Luas); \
576 lua_insert(Luas,-2); \
577 lua_setfield(Luas,-2,"__index"); \
578 lua_newtable(Luas); \
579 lua_insert(Luas,-2); \
580 lua_setmetatable(Luas,-2); \
582 lua_rawseti(Luas,-2,target); \
583 } else { \
584 lua_pop(Luas,1); \
588 } while(0)
590 /* Here end the property handlers. */
592 @ @c
593 int valid_node(halfword p)
595 if (p > my_prealloc && p < var_mem_max) {
596 #ifdef CHECK_NODE_USAGE
597 if (varmem_sizes[p] > 0) {
598 return 1;
600 #else
601 return 1;
602 #endif
604 return 0;
607 @ @c
608 static int test_count = 1;
610 #define dorangetest(a,b,c) do { \
611 if (!(b>=0 && b<c)) { \
612 fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
613 (int)a, (int)b, (int)c, __LINE__,test_count); \
614 confusion("node range test failed"); \
615 } } while (0)
617 #define dotest(a,b,c) do { \
618 if (b!=c) { \
619 fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
620 (int)a, (int)b, (int)c, __LINE__,test_count); \
621 confusion("node test failed"); \
622 } } while (0)
624 #define check_action_ref(a) { dorangetest(p,a,var_mem_max); }
625 #define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); }
626 #define check_token_ref(a) { confusion("fuzzy token cleanup in node"); }
628 #ifdef CHECK_NODE_USAGE
630 static void check_static_node_mem(void)
632 dotest(zero_glue, width(zero_glue), 0);
633 dotest(zero_glue, type(zero_glue), glue_spec_node);
634 dotest(zero_glue, vlink(zero_glue), null);
635 dotest(zero_glue, stretch(zero_glue), 0);
636 dotest(zero_glue, stretch_order(zero_glue), normal);
637 dotest(zero_glue, shrink(zero_glue), 0);
638 dotest(zero_glue, shrink_order(zero_glue), normal);
640 dotest(sfi_glue, width(sfi_glue), 0);
641 dotest(sfi_glue, type(sfi_glue), glue_spec_node);
642 dotest(sfi_glue, vlink(sfi_glue), null);
643 dotest(sfi_glue, stretch(sfi_glue), 0);
644 dotest(sfi_glue, stretch_order(sfi_glue), sfi);
645 dotest(sfi_glue, shrink(sfi_glue), 0);
646 dotest(sfi_glue, shrink_order(sfi_glue), normal);
648 dotest(fil_glue, width(fil_glue), 0);
649 dotest(fil_glue, type(fil_glue), glue_spec_node);
650 dotest(fil_glue, vlink(fil_glue), null);
651 dotest(fil_glue, stretch(fil_glue), unity);
652 dotest(fil_glue, stretch_order(fil_glue), fil);
653 dotest(fil_glue, shrink(fil_glue), 0);
654 dotest(fil_glue, shrink_order(fil_glue), normal);
656 dotest(fill_glue, width(fill_glue), 0);
657 dotest(fill_glue, type(fill_glue), glue_spec_node);
658 dotest(fill_glue, vlink(fill_glue), null);
659 dotest(fill_glue, stretch(fill_glue), unity);
660 dotest(fill_glue, stretch_order(fill_glue), fill);
661 dotest(fill_glue, shrink(fill_glue), 0);
662 dotest(fill_glue, shrink_order(fill_glue), normal);
664 dotest(ss_glue, width(ss_glue), 0);
665 dotest(ss_glue, type(ss_glue), glue_spec_node);
666 dotest(ss_glue, vlink(ss_glue), null);
667 dotest(ss_glue, stretch(ss_glue), unity);
668 dotest(ss_glue, stretch_order(ss_glue), fil);
669 dotest(ss_glue, shrink(ss_glue), unity);
670 dotest(ss_glue, shrink_order(ss_glue), fil);
672 dotest(fil_neg_glue, width(fil_neg_glue), 0);
673 dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node);
674 dotest(fil_neg_glue, vlink(fil_neg_glue), null);
675 dotest(fil_neg_glue, stretch(fil_neg_glue), -unity);
676 dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil);
677 dotest(fil_neg_glue, shrink(fil_neg_glue), 0);
678 dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal);
681 static void node_mem_dump(halfword p)
683 halfword r;
684 for (r = my_prealloc + 1; r < var_mem_max; r++) {
685 if (vlink(r) == p) {
686 halfword s = r;
687 while (s > my_prealloc && varmem_sizes[s] == 0) {
688 s--;
690 if (s != null
691 && s != my_prealloc
692 && s != var_mem_max
693 && (r - s) < get_node_size(type(s), subtype(s))
694 && alink(s) != p) {
695 if (type(s) == disc_node) {
696 fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
697 get_node_name(type(s), subtype(s)), (int) s,
698 (int) vlink(s), (int) alink(s));
699 fprintf(stdout, "pre_break(%d,%d,%d), ",
700 (int) vlink_pre_break(s), (int) tlink(pre_break(s)),
701 (int) alink(pre_break(s)));
702 fprintf(stdout, "post_break(%d,%d,%d), ",
703 (int) vlink_post_break(s),
704 (int) tlink(post_break(s)),
705 (int) alink(post_break(s)));
706 fprintf(stdout, "no_break(%d,%d,%d)",
707 (int) vlink_no_break(s), (int) tlink(no_break(s)),
708 (int) alink(no_break(s)));
709 fprintf(stdout, "\n");
710 } else {
711 if (vlink(s) == p
712 || (type(s) == glyph_node && lig_ptr (s) == p)
713 || (type(s) == vlist_node && list_ptr(s) == p)
714 || (type(s) == hlist_node && list_ptr(s) == p)
715 || (type(s) == unset_node && list_ptr(s) == p)
716 || (type(s) == ins_node && ins_ptr (s) == p)
718 fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
719 get_node_name(type(s), subtype(s)), (int) s,
720 (int) vlink(s), (int) alink(s));
721 if (type(s) == glyph_node) {
722 fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s));
723 } else if (type(s) == vlist_node || type(s) == hlist_node) {
724 fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s));
726 fprintf(stdout, "\n");
727 } else {
728 if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) {
729 fprintf(stdout, " pointed to from %s node %d\n",
730 get_node_name(type(s), subtype(s)), (int) s);
739 #endif
741 static int free_error(halfword p)
743 if (p > my_prealloc && p < var_mem_max) {
744 #ifdef CHECK_NODE_USAGE
745 int i;
746 if (varmem_sizes[p] == 0) {
747 check_static_node_mem();
748 for (i = (my_prealloc + 1); i < var_mem_max; i++) {
749 if (varmem_sizes[i] > 0) {
750 check_node(i);
753 test_count++;
754 if (type(p) == glyph_node) {
755 formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p);
756 } else {
757 formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
759 node_mem_dump(p);
760 return 1;
762 #endif
763 } else {
764 formatted_error("nodes", "attempt to free an impossible node %d", (int) p);
765 return 1;
767 return 0;
770 @ @c
771 static int copy_error(halfword p)
773 if (p >= 0 && p < var_mem_max) {
774 #ifdef CHECK_NODE_USAGE
775 if (p > my_prealloc && varmem_sizes[p] == 0) {
776 if (type(p) == glyph_node) {
777 formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p);
778 } else {
779 formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
781 return 1;
783 #endif
784 } else {
785 formatted_error("nodes", "attempt to copy an impossible node %d", (int) p);
786 return 1;
788 return 0;
791 @ @c
792 halfword new_node(int i, int j)
794 int s = get_node_size(i, j);
795 halfword n = get_node(s);
797 It should be possible to do this memset at |free_node()|.
799 Both type() and subtype() will be set below, and vlink() is
800 set to null by |get_node()|, so we can do we clearing one
801 word less than |s|
803 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1)));
804 switch (i) {
805 case glyph_node:
806 init_lang_data(n);
807 break;
808 case hlist_node:
809 case vlist_node:
810 box_dir(n) = -1;
811 break;
812 case disc_node:
813 pre_break(n) = pre_break_head(n);
814 type(pre_break(n)) = nesting_node;
815 subtype(pre_break(n)) = pre_break_head(0);
816 post_break(n) = post_break_head(n);
817 type(post_break(n)) = nesting_node;
818 subtype(post_break(n)) = post_break_head(0);
819 no_break(n) = no_break_head(n);
820 type(no_break(n)) = nesting_node;
821 subtype(no_break(n)) = no_break_head(0);
822 break;
823 case rule_node:
824 depth(n) = null_flag;
825 height(n) = null_flag;
826 width(n) = null_flag;
827 rule_dir(n) = -1;
828 rule_index(n) = 0;
829 rule_transform(n) = 0;
830 break;
831 case whatsit_node:
832 if (j == open_node) {
833 open_name(n) = get_nullstr();
834 open_area(n) = open_name(n);
835 open_ext(n) = open_name(n);
837 break;
838 case unset_node:
839 width(n) = null_flag;
840 break;
841 case pseudo_line_node:
842 case shape_node:
843 /* this is a trick that makes |pseudo_files| slightly slower,
844 but the overall allocation faster then an explicit test
845 at the top of |new_node()|.
847 if (j>0) {
848 free_node(n, variable_node_size);
849 n = slow_get_node(j);
850 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1)));
852 break;
853 default:
854 break;
856 if (int_par(synctex_code)) {
857 /* handle synctex extension */
858 switch (i) {
859 case math_node:
860 synctex_tag_math(n) = cur_input.synctex_tag_field;
861 synctex_line_math(n) = line;
862 break;
863 case glue_node:
864 synctex_tag_glue(n) = cur_input.synctex_tag_field;
865 synctex_line_glue(n) = line;
866 break;
867 case kern_node:
868 if (j != 0) {
869 synctex_tag_kern(n) = cur_input.synctex_tag_field;
870 synctex_line_kern(n) = line;
872 break;
873 case hlist_node:
874 case vlist_node:
875 case unset_node:
876 synctex_tag_box(n) = cur_input.synctex_tag_field;
877 synctex_line_box(n) = line;
878 break;
879 case rule_node:
880 synctex_tag_rule(n) = cur_input.synctex_tag_field;
881 synctex_line_rule(n) = line;
882 break;
885 /* take care of attributes */
886 if (nodetype_has_attributes(i)) {
887 build_attribute_list(n);
888 /* lua_properties_set */
890 type(n) = (quarterword) i;
891 subtype(n) = (quarterword) j;
892 return n;
895 halfword raw_glyph_node(void)
897 register halfword n = get_node(glyph_node_size);
898 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
899 type(n) = glyph_node;
900 subtype(n) = 0;
901 return n;
904 halfword new_glyph_node(void)
906 register halfword n = get_node(glyph_node_size);
907 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
908 type(n) = glyph_node;
909 subtype(n) = 0;
910 build_attribute_list(n);
911 /* lua_properties_set */
912 return n;
915 @ makes a duplicate of the node list that starts at |p| and returns a
916 pointer to the new list
919 halfword do_copy_node_list(halfword p, halfword end)
921 halfword q = null; /* previous position in new list */
922 halfword h = null; /* head of the list */
923 register halfword s ;
924 lua_properties_push; /* saves stack and time */
925 while (p != end) {
926 s = copy_node(p);
927 if (h == null) {
928 h = s;
929 } else {
930 couple_nodes(q, s);
932 q = s;
933 p = vlink(p);
935 lua_properties_pop; /* saves stack and time */
936 return h;
939 halfword copy_node_list(halfword p)
941 return do_copy_node_list(p, null);
944 #define copy_sub_list(target,source) do { \
945 if (source != null) { \
946 s = do_copy_node_list(source, null); \
947 target = s; \
948 } else { \
949 target = null; \
951 } while (0)
953 #define copy_sub_node(target,source) do { \
954 if (source != null) { \
955 s = copy_node(source); \
956 target = s ; \
957 } else { \
958 target = null; \
960 } while (0)
962 @ make a dupe of a single node
965 static void copy_node_wrapup_core(halfword p, halfword r)
967 halfword s ;
968 switch (subtype(p)) {
969 case write_node:
970 case special_node:
971 add_token_ref(write_tokens(p));
972 break;
973 case late_lua_node:
974 copy_late_lua(r, p);
975 break;
976 case user_defined_node:
977 switch (user_node_type(p)) {
978 case 'a':
979 add_node_attr_ref(user_node_value(p));
980 break;
981 case 'l':
982 copy_user_lua(r, p);
983 break;
984 case 'n':
985 s = copy_node_list(user_node_value(p));
986 user_node_value(r) = s;
987 break;
988 case 's':
989 /* |add_string_ref(user_node_value(p));| */
990 break;
991 case 't':
992 add_token_ref(user_node_value(p));
993 break;
995 break;
996 default:
997 break ;
1001 void copy_node_wrapup_dvi(halfword p, halfword r)
1005 void copy_node_wrapup_pdf(halfword p, halfword r)
1007 switch(subtype(p)) {
1008 case pdf_literal_node:
1009 copy_pdf_literal(r, p);
1010 break;
1011 case pdf_colorstack_node:
1012 if (pdf_colorstack_cmd(p) <= colorstack_data)
1013 add_token_ref(pdf_colorstack_data(p));
1014 break;
1015 case pdf_setmatrix_node:
1016 add_token_ref(pdf_setmatrix_data(p));
1017 break;
1018 case pdf_annot_node:
1019 add_token_ref(pdf_annot_data(p));
1020 break;
1021 case pdf_start_link_node:
1022 if (pdf_link_attr(r) != null)
1023 add_token_ref(pdf_link_attr(r));
1024 add_action_ref(pdf_link_action(r));
1025 break;
1026 case pdf_dest_node:
1027 if (pdf_dest_named_id(p) > 0)
1028 add_token_ref(pdf_dest_id(p));
1029 break;
1030 case pdf_thread_node:
1031 case pdf_start_thread_node:
1032 if (pdf_thread_named_id(p) > 0)
1033 add_token_ref(pdf_thread_id(p));
1034 if (pdf_thread_attr(p) != null)
1035 add_token_ref(pdf_thread_attr(p));
1036 break;
1037 default:
1038 break;
1042 halfword copy_node(const halfword p)
1044 halfword r; /* current node being fabricated for new list */
1045 halfword w ; /* whatsit subtype */
1046 register halfword s; /* a helper variable for copying into variable mem */
1047 register int i;
1048 if (copy_error(p)) {
1049 r = new_node(temp_node, 0);
1050 return r;
1052 i = get_node_size(type(p), subtype(p));
1053 r = get_node(i);
1055 (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i));
1057 if (int_par(synctex_code)) {
1058 /* handle synctex extension */
1059 switch (type(p)) {
1060 case math_node:
1061 synctex_tag_math(r) = cur_input.synctex_tag_field;
1062 synctex_line_math(r) = line;
1063 break;
1064 case kern_node:
1065 synctex_tag_kern(r) = cur_input.synctex_tag_field;
1066 synctex_line_kern(r) = line;
1067 break;
1070 if (nodetype_has_attributes(type(p))) {
1071 add_node_attr_ref(node_attr(p));
1072 alink(r) = null;
1073 lua_properties_copy(r,p);
1075 vlink(r) = null;
1077 switch (type(p)) {
1078 case glyph_node:
1079 copy_sub_list(lig_ptr(r),lig_ptr(p)) ;
1080 break;
1081 case glue_node:
1082 copy_sub_list(leader_ptr(r),leader_ptr(p)) ;
1083 break;
1084 case hlist_node:
1085 case vlist_node:
1086 case unset_node:
1087 copy_sub_list(list_ptr(r),list_ptr(p)) ;
1088 break;
1089 case disc_node:
1090 pre_break(r) = pre_break_head(r);
1091 if (vlink_pre_break(p) != null) {
1092 s = copy_node_list(vlink_pre_break(p));
1093 alink(s) = pre_break(r);
1094 tlink_pre_break(r) = tail_of_list(s);
1095 vlink_pre_break(r) = s;
1096 } else {
1097 assert(tlink(pre_break(r)) == null);
1099 post_break(r) = post_break_head(r);
1100 if (vlink_post_break(p) != null) {
1101 s = copy_node_list(vlink_post_break(p));
1102 alink(s) = post_break(r);
1103 tlink_post_break(r) = tail_of_list(s);
1104 vlink_post_break(r) = s;
1105 } else {
1106 assert(tlink_post_break(r) == null);
1108 no_break(r) = no_break_head(r);
1109 if (vlink(no_break(p)) != null) {
1110 s = copy_node_list(vlink_no_break(p));
1111 alink(s) = no_break(r);
1112 tlink_no_break(r) = tail_of_list(s);
1113 vlink_no_break(r) = s;
1114 } else {
1115 assert(tlink_no_break(r) == null);
1117 break;
1118 case math_node:
1119 break;
1120 case ins_node:
1121 copy_sub_list(ins_ptr(r),ins_ptr(p)) ;
1122 break;
1123 case margin_kern_node:
1124 copy_sub_node(margin_char(r),margin_char(p));
1125 break;
1126 case mark_node:
1127 add_token_ref(mark_ptr(p));
1128 break;
1129 case adjust_node:
1130 copy_sub_list(adjust_ptr(r),adjust_ptr(p));
1131 break;
1132 case choice_node:
1133 copy_sub_list(display_mlist(r),display_mlist(p)) ;
1134 copy_sub_list(text_mlist(r),text_mlist(p)) ;
1135 copy_sub_list(script_mlist(r),script_mlist(p)) ;
1136 copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ;
1137 break;
1138 case simple_noad:
1139 copy_sub_list(nucleus(r),nucleus(p)) ;
1140 copy_sub_list(subscr(r),subscr(p)) ;
1141 copy_sub_list(supscr(r),supscr(p)) ;
1142 break;
1143 case radical_noad:
1144 copy_sub_list(nucleus(r),nucleus(p)) ;
1145 copy_sub_list(subscr(r),subscr(p)) ;
1146 copy_sub_list(supscr(r),supscr(p)) ;
1147 copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
1148 copy_sub_list(degree(r),degree(p)) ;
1149 break;
1150 case accent_noad:
1151 copy_sub_list(nucleus(r),nucleus(p)) ;
1152 copy_sub_list(subscr(r),subscr(p)) ;
1153 copy_sub_list(supscr(r),supscr(p)) ;
1154 copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ;
1155 copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ;
1156 copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ;
1157 break;
1158 case fence_noad:
1159 copy_sub_node(delimiter(r),delimiter(p)) ;
1160 break;
1161 case sub_box_node:
1162 case sub_mlist_node:
1163 copy_sub_list(math_list(r),math_list(p)) ;
1164 break;
1165 case fraction_noad:
1166 copy_sub_list(numerator(r),numerator(p)) ;
1167 copy_sub_list(denominator(r),denominator(p)) ;
1168 copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
1169 copy_sub_node(right_delimiter(r),right_delimiter(p)) ;
1170 break;
1171 case glue_spec_node:
1172 case dir_node:
1173 case local_par_node:
1174 case boundary_node:
1175 break;
1176 case whatsit_node:
1177 w = subtype(p) ;
1178 if (w >= backend_first_pdf_whatsit) {
1179 copy_node_wrapup_pdf(p,r);
1180 } else if (w >= backend_first_dvi_whatsit) {
1181 copy_node_wrapup_dvi(p,r);
1182 } else {
1183 copy_node_wrapup_core(p,r);
1185 break;
1187 return r;
1190 /* x */
1192 #define free_sub_list(source) if (source != null) flush_node_list(source);
1193 #define free_sub_node(source) if (source != null) flush_node(source);
1195 @ @c
1197 static void flush_node_wrapup_core(halfword p)
1199 switch (subtype(p)) {
1200 case open_node:
1201 case write_node:
1202 case close_node:
1203 case save_pos_node:
1204 break;
1205 case special_node:
1206 delete_token_ref(write_tokens(p));
1207 break;
1208 case late_lua_node:
1209 free_late_lua(p);
1210 break;
1211 case user_defined_node:
1212 switch (user_node_type(p)) {
1213 case 'a':
1214 delete_attribute_ref(user_node_value(p));
1215 break;
1216 case 'd':
1217 break;
1218 case 'n':
1219 flush_node_list(user_node_value(p));
1220 break;
1221 case 's':
1222 /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */
1223 break;
1224 case 't':
1225 delete_token_ref(user_node_value(p));
1226 break;
1227 default:
1229 const char *hlp[] = {
1230 "The type of the value in a user defined whatsit node should be one",
1231 "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
1232 "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
1233 "know how to free the node's value. A memory leak may result.",
1234 NULL
1236 tex_error("Unidentified user defined whatsit", hlp);
1238 break;
1240 break;
1244 void flush_node_wrapup_dvi(halfword p)
1248 void flush_node_wrapup_pdf(halfword p)
1250 switch(subtype(p)) {
1251 case pdf_save_node:
1252 case pdf_restore_node:
1253 case pdf_refobj_node:
1254 case pdf_end_link_node:
1255 case pdf_end_thread_node:
1256 break;
1257 case pdf_literal_node:
1258 free_pdf_literal(p);
1259 break;
1260 case pdf_colorstack_node:
1261 if (pdf_colorstack_cmd(p) <= colorstack_data)
1262 delete_token_ref(pdf_colorstack_data(p));
1263 break;
1264 case pdf_setmatrix_node:
1265 delete_token_ref(pdf_setmatrix_data(p));
1266 break;
1267 case pdf_annot_node:
1268 delete_token_ref(pdf_annot_data(p));
1269 break;
1270 case pdf_link_data_node:
1271 break;
1272 case pdf_start_link_node:
1273 if (pdf_link_attr(p) != null)
1274 delete_token_ref(pdf_link_attr(p));
1275 delete_action_ref(pdf_link_action(p));
1276 break;
1277 case pdf_dest_node:
1278 if (pdf_dest_named_id(p) > 0)
1279 delete_token_ref(pdf_dest_id(p));
1280 break;
1281 case pdf_action_node:
1282 if (pdf_action_type(p) == pdf_action_user) {
1283 delete_token_ref(pdf_action_tokens(p));
1284 } else {
1285 if (pdf_action_file(p) != null)
1286 delete_token_ref(pdf_action_file(p));
1287 if (pdf_action_type(p) == pdf_action_page)
1288 delete_token_ref(pdf_action_tokens(p));
1289 else if (pdf_action_named_id(p) > 0)
1290 delete_token_ref(pdf_action_id(p));
1292 break;
1293 case pdf_thread_data_node:
1294 break;
1295 case pdf_thread_node:
1296 case pdf_start_thread_node:
1297 if (pdf_thread_named_id(p) > 0)
1298 delete_token_ref(pdf_thread_id(p));
1299 if (pdf_thread_attr(p) != null)
1300 delete_token_ref(pdf_thread_attr(p));
1301 break;
1305 void flush_node(halfword p)
1307 halfword w;
1308 if (p == null) /* legal, but no-op */
1309 return;
1310 if (free_error(p))
1311 return;
1312 switch (type(p)) {
1313 case glyph_node:
1314 free_sub_list(lig_ptr(p));
1315 break;
1316 case glue_node:
1317 free_sub_list(leader_ptr(p));
1318 break;
1319 case hlist_node:
1320 case vlist_node:
1321 case unset_node:
1322 free_sub_list(list_ptr(p));
1323 break;
1324 case disc_node:
1325 /* watch the start at temp node hack */
1326 free_sub_list(vlink(pre_break(p)));
1327 free_sub_list(vlink(post_break(p)));
1328 free_sub_list(vlink(no_break(p)));
1329 break;
1330 case rule_node:
1331 case kern_node:
1332 case penalty_node:
1333 case math_node:
1334 break;
1335 case glue_spec_node:
1336 /* this allows free-ing of lua-allocated glue specs */
1337 //if (valid_node(p)) {
1338 // free_node(p, subtype(p));
1340 // return ;
1341 break ;
1342 case dir_node:
1343 case local_par_node:
1344 case boundary_node:
1345 break;
1346 case whatsit_node:
1347 w = subtype(p) ;
1348 if (w >= backend_first_pdf_whatsit) {
1349 flush_node_wrapup_pdf(p);
1350 } else if (w >= backend_first_dvi_whatsit) {
1351 flush_node_wrapup_dvi(p);
1352 } else {
1353 flush_node_wrapup_core(p);
1355 break;
1356 case ins_node:
1357 flush_node_list(ins_ptr(p));
1358 break;
1359 case margin_kern_node:
1360 flush_node(margin_char(p));
1361 break;
1362 case mark_node:
1363 delete_token_ref(mark_ptr(p));
1364 break;
1365 case adjust_node:
1366 flush_node_list(adjust_ptr(p));
1367 break;
1368 case style_node: /* nothing to do */
1369 break;
1370 case choice_node:
1371 free_sub_list(display_mlist(p));
1372 free_sub_list(text_mlist(p));
1373 free_sub_list(script_mlist(p));
1374 free_sub_list(script_script_mlist(p));
1375 break;
1376 case simple_noad:
1377 free_sub_list(nucleus(p));
1378 free_sub_list(subscr(p));
1379 free_sub_list(supscr(p));
1380 break;
1381 case radical_noad:
1382 free_sub_list(nucleus(p));
1383 free_sub_list(subscr(p));
1384 free_sub_list(supscr(p));
1385 free_sub_node(left_delimiter(p));
1386 free_sub_list(degree(p));
1387 break;
1388 case accent_noad:
1389 free_sub_list(nucleus(p));
1390 free_sub_list(subscr(p));
1391 free_sub_list(supscr(p));
1392 free_sub_list(top_accent_chr(p));
1393 free_sub_list(bot_accent_chr(p));
1394 free_sub_list(overlay_accent_chr(p));
1395 break;
1396 case fence_noad:
1397 free_sub_list(delimiter(p));
1398 break;
1399 case delim_node: /* nothing to do */
1400 case math_char_node:
1401 case math_text_char_node:
1402 break;
1403 case sub_box_node:
1404 case sub_mlist_node:
1405 free_sub_list(math_list(p));
1406 break;
1407 case fraction_noad:
1408 free_sub_list(numerator(p));
1409 free_sub_list(denominator(p));
1410 free_sub_node(left_delimiter(p));
1411 free_sub_node(right_delimiter(p));
1412 break;
1413 case pseudo_file_node:
1414 free_sub_list(pseudo_lines(p));
1415 break;
1416 case pseudo_line_node:
1417 case shape_node:
1418 free_node(p, subtype(p));
1419 return;
1420 break;
1421 case align_stack_node:
1422 case span_node:
1423 case movement_node:
1424 case if_node:
1425 case nesting_node:
1426 case unhyphenated_node:
1427 case hyphenated_node:
1428 case delta_node:
1429 case passive_node:
1430 case inserting_node:
1431 case split_up_node:
1432 case expr_node:
1433 case attribute_node:
1434 case attribute_list_node:
1435 case temp_node:
1436 break;
1437 default:
1438 formatted_error("nodes","flushing weird node type %d", type(p));
1439 return;
1441 if (nodetype_has_attributes(type(p))) {
1442 delete_attribute_ref(node_attr(p));
1443 lua_properties_reset(p);
1445 free_node(p, get_node_size(type(p), subtype(p)));
1446 return;
1449 @ @c
1450 void flush_node_list(halfword pp)
1451 { /* erase list of nodes starting at |p| */
1452 register halfword p = pp;
1453 if (p == null) /* legal, but no-op */
1454 return;
1455 if (free_error(p))
1456 return;
1457 lua_properties_push; /* saves stack and time */
1458 while (p != null) {
1459 register halfword q = vlink(p);
1460 flush_node(p);
1461 p = q;
1463 lua_properties_pop; /* saves stack and time */
1466 @ @c
1467 static void check_node_wrapup_core(halfword p)
1469 switch (subtype(p)) {
1470 /* frontend code */
1471 case special_node:
1472 check_token_ref(write_tokens(p));
1473 break;
1474 case user_defined_node:
1475 switch (user_node_type(p)) {
1476 case 'a':
1477 check_attribute_ref(user_node_value(p));
1478 break;
1479 case 't':
1480 check_token_ref(user_node_value(p));
1481 break;
1482 case 'n':
1483 dorangetest(p, user_node_value(p), var_mem_max);
1484 break;
1485 case 's':
1486 case 'd':
1487 break;
1488 default:
1489 confusion("unknown user node type");
1490 break;
1492 break;
1493 case open_node:
1494 case write_node:
1495 case close_node:
1496 case save_pos_node:
1497 break;
1501 void check_node_wrapup_dvi(halfword p)
1505 void check_node_wrapup_pdf(halfword p)
1507 switch (subtype(p)) {
1508 case pdf_literal_node:
1509 if (pdf_literal_type(p) == normal)
1510 check_token_ref(pdf_literal_data(p));
1511 break;
1512 case pdf_colorstack_node:
1513 if (pdf_colorstack_cmd(p) <= colorstack_data)
1514 check_token_ref(pdf_colorstack_data(p));
1515 break;
1516 case pdf_setmatrix_node:
1517 check_token_ref(pdf_setmatrix_data(p));
1518 break;
1519 case late_lua_node:
1520 if (late_lua_name(p) > 0)
1521 check_token_ref(late_lua_name(p));
1522 if (late_lua_type(p) == normal)
1523 check_token_ref(late_lua_data(p));
1524 break;
1525 case pdf_annot_node:
1526 check_token_ref(pdf_annot_data(p));
1527 break;
1528 case pdf_start_link_node:
1529 if (pdf_link_attr(p) != null)
1530 check_token_ref(pdf_link_attr(p));
1531 check_action_ref(pdf_link_action(p));
1532 break;
1533 case pdf_dest_node:
1534 if (pdf_dest_named_id(p) > 0)
1535 check_token_ref(pdf_dest_id(p));
1536 break;
1537 case pdf_thread_node:
1538 case pdf_start_thread_node:
1539 if (pdf_thread_named_id(p) > 0)
1540 check_token_ref(pdf_thread_id(p));
1541 if (pdf_thread_attr(p) != null)
1542 check_token_ref(pdf_thread_attr(p));
1543 break;
1544 case pdf_save_node:
1545 case pdf_restore_node:
1546 case pdf_refobj_node:
1547 case pdf_end_link_node:
1548 case pdf_end_thread_node:
1549 break;
1550 default:
1551 confusion("wrapup pdf nodes");
1552 break;
1556 void check_node(halfword p)
1558 halfword w ;
1559 switch (type(p)) {
1560 case glyph_node:
1561 dorangetest(p, lig_ptr(p), var_mem_max);
1562 break;
1563 case glue_node:
1564 dorangetest(p, leader_ptr(p), var_mem_max);
1565 break;
1566 case hlist_node:
1567 case vlist_node:
1568 case unset_node:
1569 case align_record_node:
1570 dorangetest(p, list_ptr(p), var_mem_max);
1571 break;
1572 case ins_node:
1573 dorangetest(p, ins_ptr(p), var_mem_max);
1574 break;
1575 case whatsit_node:
1576 w = subtype(p) ;
1577 if (w >= backend_first_pdf_whatsit) {
1578 check_node_wrapup_pdf(p);
1579 } else if (w >= backend_first_dvi_whatsit) {
1580 check_node_wrapup_dvi(p);
1581 } else {
1582 check_node_wrapup_core(p);
1584 break;
1585 case margin_kern_node:
1586 check_node(margin_char(p));
1587 break;
1588 case math_node:
1589 break;
1590 case disc_node:
1591 dorangetest(p, vlink(pre_break(p)), var_mem_max);
1592 dorangetest(p, vlink(post_break(p)), var_mem_max);
1593 dorangetest(p, vlink(no_break(p)), var_mem_max);
1594 break;
1595 case adjust_node:
1596 dorangetest(p, adjust_ptr(p), var_mem_max);
1597 break;
1598 case pseudo_file_node:
1599 dorangetest(p, pseudo_lines(p), var_mem_max);
1600 break;
1601 case pseudo_line_node:
1602 case shape_node:
1603 break;
1604 case choice_node:
1605 dorangetest(p, display_mlist(p), var_mem_max);
1606 dorangetest(p, text_mlist(p), var_mem_max);
1607 dorangetest(p, script_mlist(p), var_mem_max);
1608 dorangetest(p, script_script_mlist(p), var_mem_max);
1609 break;
1610 case fraction_noad:
1611 dorangetest(p, numerator(p), var_mem_max);
1612 dorangetest(p, denominator(p), var_mem_max);
1613 dorangetest(p, left_delimiter(p), var_mem_max);
1614 dorangetest(p, right_delimiter(p), var_mem_max);
1615 break;
1616 case simple_noad:
1617 dorangetest(p, nucleus(p), var_mem_max);
1618 dorangetest(p, subscr(p), var_mem_max);
1619 dorangetest(p, supscr(p), var_mem_max);
1620 break;
1621 case radical_noad:
1622 dorangetest(p, nucleus(p), var_mem_max);
1623 dorangetest(p, subscr(p), var_mem_max);
1624 dorangetest(p, supscr(p), var_mem_max);
1625 dorangetest(p, degree(p), var_mem_max);
1626 dorangetest(p, left_delimiter(p), var_mem_max);
1627 break;
1628 case accent_noad:
1629 dorangetest(p, nucleus(p), var_mem_max);
1630 dorangetest(p, subscr(p), var_mem_max);
1631 dorangetest(p, supscr(p), var_mem_max);
1632 dorangetest(p, top_accent_chr(p), var_mem_max);
1633 dorangetest(p, bot_accent_chr(p), var_mem_max);
1634 dorangetest(p, overlay_accent_chr(p), var_mem_max);
1635 break;
1636 case fence_noad:
1637 dorangetest(p, delimiter(p), var_mem_max);
1638 break;
1640 case rule_node:
1641 case kern_node:
1642 case penalty_node:
1643 case mark_node:
1644 case style_node:
1645 case attribute_list_node:
1646 case attribute_node:
1647 case glue_spec_node:
1648 case temp_node:
1649 case align_stack_node:
1650 case movement_node:
1651 case if_node:
1652 case nesting_node:
1653 case span_node:
1654 case unhyphenated_node:
1655 case hyphenated_node:
1656 case delta_node:
1657 case passive_node:
1658 case expr_node:
1659 case dir_node:
1660 case boundary_node:
1661 case local_par_node:
1662 break;
1663 default:
1664 fprintf(stdout, "check_node: type is %d\n", type(p));
1669 @ @c
1670 void fix_node_list(halfword head)
1672 halfword p, q;
1673 if (head == null)
1674 return;
1675 p = head;
1676 q = vlink(p);
1677 while (q != null) {
1678 alink(q) = p;
1679 p = q;
1680 q = vlink(p);
1684 @ @c
1685 halfword get_node(int s)
1687 register halfword r;
1689 if (s < MAX_CHAIN_SIZE) {
1690 r = free_chain[s];
1691 if (r != null) {
1692 free_chain[s] = vlink(r);
1693 #ifdef CHECK_NODE_USAGE
1694 varmem_sizes[r] = (char) s;
1695 #endif
1696 vlink(r) = null;
1697 var_used += s; /* maintain usage statistics */
1698 return r;
1700 /* this is the end of the 'inner loop' */
1701 return slow_get_node(s);
1702 } else {
1703 normal_error("nodes","there is a problem in getting a node, case 1");
1704 return null;
1708 @ @c
1709 void free_node(halfword p, int s)
1711 if (p <= my_prealloc) {
1712 formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p));
1713 return;
1715 #ifdef CHECK_NODE_USAGE
1716 varmem_sizes[p] = 0;
1717 #endif
1718 if (s < MAX_CHAIN_SIZE) {
1719 vlink(p) = free_chain[s];
1720 free_chain[s] = p;
1721 } else {
1722 /* todo ? it is perhaps possible to merge this node with an existing rover */
1723 node_size(p) = s;
1724 vlink(p) = rover;
1725 while (vlink(rover) != vlink(p)) {
1726 rover = vlink(rover);
1728 vlink(rover) = p;
1730 /* maintain statistics */
1731 var_used -= s;
1734 @ @c
1735 static void free_node_chain(halfword q, int s)
1737 register halfword p = q;
1738 while (vlink(p) != null) {
1739 #ifdef CHECK_NODE_USAGE
1740 varmem_sizes[p] = 0;
1741 #endif
1742 var_used -= s;
1743 p = vlink(p);
1745 var_used -= s;
1746 #ifdef CHECK_NODE_USAGE
1747 varmem_sizes[p] = 0;
1748 #endif
1749 vlink(p) = free_chain[s];
1750 free_chain[s] = q;
1753 @ @c
1754 void init_node_mem(int t)
1756 my_prealloc = var_mem_stat_max;
1758 /* message ?
1760 assert(whatsit_node_data[user_defined_node].id == user_defined_node);
1761 assert(node_data[passive_node].id == passive_node);
1764 varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t);
1765 if (varmem == NULL) {
1766 overflow("node memory size", (unsigned) var_mem_max);
1768 memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word));
1769 #ifdef CHECK_NODE_USAGE
1770 varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t);
1771 if (varmem_sizes == NULL) {
1772 overflow("node memory size", (unsigned) var_mem_max);
1774 memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t);
1775 #endif
1776 var_mem_max = t;
1777 rover = var_mem_stat_max + 1;
1778 vlink(rover) = rover;
1779 node_size(rover) = (t - rover);
1780 var_used = 0;
1781 /* initialize static glue specs */
1782 width(zero_glue) = 0;
1783 type(zero_glue) = glue_spec_node;
1784 vlink(zero_glue) = null;
1785 stretch(zero_glue) = 0;
1786 stretch_order(zero_glue) = normal;
1787 shrink(zero_glue) = 0;
1788 shrink_order(zero_glue) = normal;
1789 width(sfi_glue) = 0;
1790 type(sfi_glue) = glue_spec_node;
1791 vlink(sfi_glue) = null;
1792 stretch(sfi_glue) = 0;
1793 stretch_order(sfi_glue) = sfi;
1794 shrink(sfi_glue) = 0;
1795 shrink_order(sfi_glue) = normal;
1796 width(fil_glue) = 0;
1797 type(fil_glue) = glue_spec_node;
1798 vlink(fil_glue) = null;
1799 stretch(fil_glue) = unity;
1800 stretch_order(fil_glue) = fil;
1801 shrink(fil_glue) = 0;
1802 shrink_order(fil_glue) = normal;
1803 width(fill_glue) = 0;
1804 type(fill_glue) = glue_spec_node;
1805 vlink(fill_glue) = null;
1806 stretch(fill_glue) = unity;
1807 stretch_order(fill_glue) = fill;
1808 shrink(fill_glue) = 0;
1809 shrink_order(fill_glue) = normal;
1810 width(ss_glue) = 0;
1811 type(ss_glue) = glue_spec_node;
1812 vlink(ss_glue) = null;
1813 stretch(ss_glue) = unity;
1814 stretch_order(ss_glue) = fil;
1815 shrink(ss_glue) = unity;
1816 shrink_order(ss_glue) = fil;
1817 width(fil_neg_glue) = 0;
1818 type(fil_neg_glue) = glue_spec_node;
1819 vlink(fil_neg_glue) = null;
1820 stretch(fil_neg_glue) = -unity;
1821 stretch_order(fil_neg_glue) = fil;
1822 shrink(fil_neg_glue) = 0;
1823 shrink_order(fil_neg_glue) = normal;
1824 /* initialize node list heads */
1825 vinfo(page_ins_head) = 0;
1826 type(page_ins_head) = temp_node;
1827 vlink(page_ins_head) = null;
1828 alink(page_ins_head) = null;
1829 vinfo(contrib_head) = 0;
1830 type(contrib_head) = temp_node;
1831 vlink(contrib_head) = null;
1832 alink(contrib_head) = null;
1833 vinfo(page_head) = 0;
1834 type(page_head) = temp_node;
1835 vlink(page_head) = null;
1836 alink(page_head) = null;
1837 vinfo(temp_head) = 0;
1838 type(temp_head) = temp_node;
1839 vlink(temp_head) = null;
1840 alink(temp_head) = null;
1841 vinfo(hold_head) = 0;
1842 type(hold_head) = temp_node;
1843 vlink(hold_head) = null;
1844 alink(hold_head) = null;
1845 vinfo(adjust_head) = 0;
1846 type(adjust_head) = temp_node;
1847 vlink(adjust_head) = null;
1848 alink(adjust_head) = null;
1849 vinfo(pre_adjust_head) = 0;
1850 type(pre_adjust_head) = temp_node;
1851 vlink(pre_adjust_head) = null;
1852 alink(pre_adjust_head) = null;
1853 vinfo(active) = 0;
1854 type(active) = unhyphenated_node;
1855 vlink(active) = null;
1856 alink(active) = null;
1857 vinfo(align_head) = 0;
1858 type(align_head) = temp_node;
1859 vlink(align_head) = null;
1860 alink(align_head) = null;
1861 vinfo(end_span) = 0;
1862 type(end_span) = span_node;
1863 vlink(end_span) = null;
1864 alink(end_span) = null;
1865 type(begin_point) = glyph_node;
1866 subtype(begin_point) = 0;
1867 vlink(begin_point) = null;
1868 vinfo(begin_point + 1) = null;
1869 alink(begin_point) = null;
1870 font(begin_point) = 0;
1871 character(begin_point) = '.';
1872 vinfo(begin_point + 3) = 0;
1873 vlink(begin_point + 3) = 0;
1874 vinfo(begin_point + 4) = 0;
1875 vlink(begin_point + 4) = 0;
1876 type(end_point) = glyph_node;
1877 subtype(end_point) = 0;
1878 vlink(end_point) = null;
1879 vinfo(end_point + 1) = null;
1880 alink(end_point) = null;
1881 font(end_point) = 0;
1882 character(end_point) = '.';
1883 vinfo(end_point + 3) = 0;
1884 vlink(end_point + 3) = 0;
1885 vinfo(end_point + 4) = 0;
1886 vlink(end_point + 4) = 0;
1889 @ @c
1890 void dump_node_mem(void)
1892 dump_int(var_mem_max);
1893 dump_int(rover);
1894 dump_things(varmem[0], var_mem_max);
1895 #ifdef CHECK_NODE_USAGE
1896 dump_things(varmem_sizes[0], var_mem_max);
1897 #endif
1898 dump_things(free_chain[0], MAX_CHAIN_SIZE);
1899 dump_int(var_used);
1900 dump_int(my_prealloc);
1903 @ it makes sense to enlarge the varmem array immediately
1906 void undump_node_mem(void)
1908 int x;
1909 undump_int(x);
1910 undump_int(rover);
1911 var_mem_max = (x < 100000 ? 100000 : x);
1912 varmem = xmallocarray(memory_word, (unsigned) var_mem_max);
1913 undump_things(varmem[0], x);
1914 #ifdef CHECK_NODE_USAGE
1915 varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
1916 memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char));
1917 undump_things(varmem_sizes[0], x);
1918 #endif
1919 undump_things(free_chain[0], MAX_CHAIN_SIZE);
1920 undump_int(var_used);
1921 undump_int(my_prealloc);
1922 if (var_mem_max > x) {
1923 /* todo ? it is perhaps possible to merge the new node with an existing rover */
1924 vlink(x) = rover;
1925 node_size(x) = (var_mem_max - x);
1926 while (vlink(rover) != vlink(x)) {
1927 rover = vlink(rover);
1929 vlink(rover) = x;
1933 @ @c
1934 halfword slow_get_node(int s)
1936 register int t;
1938 RETRY:
1939 t = node_size(rover);
1940 if (vlink(rover) < var_mem_max && vlink(rover) != 0) {
1941 if (t > s) {
1942 /* allocating from the bottom helps decrease page faults */
1943 register halfword r = rover;
1944 rover += s;
1945 vlink(rover) = vlink(r);
1946 node_size(rover) = node_size(r) - s;
1947 if (vlink(rover) != r) { /* list is longer than one */
1948 halfword q = r;
1949 while (vlink(q) != r) {
1950 q = vlink(q);
1952 vlink(q) += s;
1953 } else {
1954 vlink(rover) += s;
1956 if (vlink(rover) < var_mem_max) {
1957 #ifdef CHECK_NODE_USAGE
1958 varmem_sizes[r] = (char) (s > 127 ? 127 : s);
1959 #endif
1960 vlink(r) = null;
1961 var_used += s; /* maintain usage statistics */
1962 return r; /* this is the only exit */
1963 } else {
1964 normal_error("nodes","there is a problem in getting a node, case 2");
1965 return null;
1967 } else {
1968 /* attempt to keep the free list small */
1969 int x;
1970 if (vlink(rover) != rover) {
1971 if (t < MAX_CHAIN_SIZE) {
1972 halfword l = vlink(rover);
1973 vlink(rover) = free_chain[t];
1974 free_chain[t] = rover;
1975 rover = l;
1976 while (vlink(l) != free_chain[t]) {
1977 l = vlink(l);
1979 vlink(l) = rover;
1980 goto RETRY;
1981 } else {
1982 halfword l = rover;
1983 while (vlink(rover) != l) {
1984 if (node_size(rover) > s) {
1985 goto RETRY;
1987 rover = vlink(rover);
1991 /* if we are still here, it was apparently impossible to get a match */
1992 x = (var_mem_max >> 2) + s;
1993 varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x));
1994 if (varmem == NULL) {
1995 overflow("node memory size", (unsigned) var_mem_max);
1997 memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word));
1998 #ifdef CHECK_NODE_USAGE
1999 varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x));
2000 if (varmem_sizes == NULL) {
2001 overflow("node memory size", (unsigned) var_mem_max);
2003 memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char));
2004 #endif
2005 /* todo ? it is perhaps possible to merge the new memory with an existing rover */
2006 vlink(var_mem_max) = rover;
2007 node_size(var_mem_max) = x;
2008 while (vlink(rover) != vlink(var_mem_max)) {
2009 rover = vlink(rover);
2011 vlink(rover) = var_mem_max;
2012 rover = var_mem_max;
2013 var_mem_max += x;
2014 goto RETRY;
2016 } else {
2017 normal_error("nodes","there is a problem in getting a node, case 3");
2018 return null;
2022 @ @c
2023 char *sprint_node_mem_usage(void)
2025 char *s;
2026 #ifdef CHECK_NODE_USAGE
2027 char *ss;
2028 int i;
2029 int b = 0;
2030 char msg[256];
2031 int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 };
2032 s = strdup("");
2033 for (i = (var_mem_max - 1); i > my_prealloc; i--) {
2034 if (varmem_sizes[i] > 0) {
2035 if (type(i) > last_normal_node + last_whatsit_node) {
2036 node_counts[last_normal_node + last_whatsit_node + 1]++;
2037 } else if (type(i) == whatsit_node) {
2038 node_counts[(subtype(i) + last_normal_node + 1)]++;
2039 } else {
2040 node_counts[type(i)]++;
2044 for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) {
2045 if (node_counts[i] > 0) {
2046 int j =
2047 (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0);
2048 snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i],
2049 get_node_name((i > last_normal_node ? whatsit_node : i), j));
2050 ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1));
2051 strcpy(ss, s);
2052 strcat(ss, msg);
2053 free(s);
2054 s = ss;
2055 b = 1;
2058 #else
2059 s = strdup("");
2060 #endif
2061 return s;
2064 @ @c
2065 halfword list_node_mem_usage(void)
2067 halfword q = null;
2068 #ifdef CHECK_NODE_USAGE
2069 halfword p = null;
2070 halfword i, j;
2071 char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
2072 memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max);
2073 for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) {
2074 if (saved_varmem_sizes[i] > 0) {
2075 j = copy_node(i);
2076 if (p == null) {
2077 q = j;
2078 } else {
2079 vlink(p) = j;
2081 p = j;
2084 free(saved_varmem_sizes);
2085 #endif
2086 return q;
2089 @ @c
2090 void print_node_mem_stats(void)
2092 int i, b;
2093 halfword j;
2094 char msg[256];
2095 char *s;
2096 int free_chain_counts[MAX_CHAIN_SIZE] = { 0 };
2097 snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc));
2098 tprint_nl(msg);
2099 s = sprint_node_mem_usage();
2100 tprint_nl(" ");
2101 tprint(s);
2102 free(s);
2103 tprint(" nodes");
2104 tprint_nl(" avail lists: ");
2105 b = 0;
2106 for (i = 1; i < MAX_CHAIN_SIZE; i++) {
2107 for (j = free_chain[i]; j != null; j = vlink(j))
2108 free_chain_counts[i]++;
2109 if (free_chain_counts[i] > 0) {
2110 snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]);
2111 tprint(msg);
2112 b = 1;
2115 /* newline, if needed */
2116 print_nlp();
2119 /* this belongs in the web but i couldn't find the correct syntactic place */
2121 halfword new_span_node(halfword n, int s, scaled w)
2123 halfword p = new_node(span_node, 0);
2124 span_link(p) = n;
2125 span_span(p) = s;
2126 width(p) = w;
2127 return p;
2130 @* Attribute stuff.
2133 static halfword new_attribute_node(unsigned int i, int v)
2135 register halfword r = get_node(attribute_node_size);
2136 type(r) = attribute_node;
2137 attribute_id(r) = (halfword) i;
2138 attribute_value(r) = v;
2139 /* not used but nicer in print */
2140 subtype(r) = 0;
2141 return r;
2144 @ @c
2145 halfword copy_attribute_list(halfword n)
2147 halfword q = get_node(attribute_node_size);
2148 register halfword p = q;
2149 type(p) = attribute_list_node;
2150 attr_list_ref(p) = 0;
2151 n = vlink(n);
2152 while (n != null) {
2153 register halfword r = get_node(attribute_node_size);
2154 /* the link will be fixed automatically in the next loop */
2155 (void) memcpy((void *) (varmem + r), (void *) (varmem + n),
2156 (sizeof(memory_word) * attribute_node_size));
2157 vlink(p) = r;
2158 p = r;
2159 n = vlink(n);
2161 return q;
2164 @ @c
2165 void update_attribute_cache(void)
2167 halfword p;
2168 register int i;
2169 attr_list_cache = get_node(attribute_node_size);
2170 type(attr_list_cache) = attribute_list_node;
2171 attr_list_ref(attr_list_cache) = 0;
2172 p = attr_list_cache;
2173 for (i = 0; i <= max_used_attr; i++) {
2174 register int v = attribute(i);
2175 if (v > UNUSED_ATTRIBUTE) {
2176 register halfword r = new_attribute_node((unsigned) i, v);
2177 vlink(p) = r;
2178 p = r;
2181 if (vlink(attr_list_cache) == null) {
2182 free_node(attr_list_cache, attribute_node_size);
2183 attr_list_cache = null;
2185 return;
2188 @ @c
2189 void build_attribute_list(halfword b)
2191 if (max_used_attr >= 0) {
2192 if (attr_list_cache == cache_disabled|| attr_list_cache == null) {
2193 update_attribute_cache();
2194 if (attr_list_cache == null)
2195 return;
2197 attr_list_ref(attr_list_cache)++;
2198 node_attr(b) = attr_list_cache;
2202 @ @c
2203 halfword current_attribute_list(void)
2205 if (max_used_attr >= 0) {
2206 if (attr_list_cache == cache_disabled) {
2207 update_attribute_cache();
2209 return attr_list_cache ;
2211 return null ;
2215 @ @c
2216 void reassign_attribute(halfword n, halfword new)
2218 halfword old;
2219 old = node_attr(n);
2220 if (new == null) {
2221 /* there is nothing to assign but we need to check for an old value */
2222 if (old != null)
2223 delete_attribute_ref(old); /* also nulls attr field of n */
2224 } else if (old == null) {
2225 /* nothing is assigned so we just do that now */
2226 assign_attribute_ref(n,new);
2227 } else if (old != new) {
2228 /* something is assigned so we need to clean up and assign then */
2229 delete_attribute_ref(old);
2230 assign_attribute_ref(n,new);
2232 /* else: same value so there is no need to assign and change the refcount */
2233 node_attr(n) = new ;
2236 @ @c
2237 void delete_attribute_ref(halfword b)
2239 if (b != null) {
2240 if (type(b) == attribute_list_node){
2241 attr_list_ref(b)--;
2242 if (attr_list_ref(b) == 0) {
2243 if (b == attr_list_cache)
2244 attr_list_cache = cache_disabled;
2245 free_node_chain(b, attribute_node_size);
2247 /* maintain sanity */
2248 if (attr_list_ref(b) < 0) {
2249 attr_list_ref(b) = 0;
2251 } else {
2252 normal_error("nodes","trying to delete an attribute reference of a non attribute node");
2257 void reset_node_properties(halfword b)
2259 if (b != null) {
2260 lua_properties_reset(b);
2264 @ |p| is an attr list head, or zero
2266 halfword do_set_attribute(halfword p, int i, int val)
2268 register halfword q;
2269 register int j = 0;
2270 if (p == null) { /* add a new head \& node */
2271 q = get_node(attribute_node_size);
2272 type(q) = attribute_list_node;
2273 attr_list_ref(q) = 1;
2274 p = new_attribute_node((unsigned) i, val);
2275 vlink(q) = p;
2276 return q;
2278 q = p;
2279 if (vlink(p) != null) {
2280 while (vlink(p) != null) {
2281 int t = attribute_id(vlink(p));
2282 if (t == i && attribute_value(vlink(p)) == val)
2283 return q; /* no need to do anything */
2284 if (t >= i)
2285 break;
2286 j++;
2287 p = vlink(p);
2290 p = q;
2291 while (j-- > 0)
2292 p = vlink(p);
2293 if (attribute_id(vlink(p)) == i) {
2294 attribute_value(vlink(p)) = val;
2295 } else { /* add a new node */
2296 halfword r = new_attribute_node((unsigned) i, val);
2297 vlink(r) = vlink(p);
2298 vlink(p) = r;
2300 return q;
2301 } else {
2302 normal_error("nodes","trying to set an attribute fails, case 1");
2303 return null ;
2307 @ @c
2308 void set_attribute(halfword n, int i, int val)
2310 register halfword p;
2311 register int j = 0;
2312 /* not all nodes can have an attribute list */
2313 if (!nodetype_has_attributes(type(n)))
2314 return;
2315 /* if we have no list, we create one and quit */
2316 p = node_attr(n);
2317 if (p == null) { /* add a new head \& node */
2318 p = get_node(attribute_node_size);
2319 type(p) = attribute_list_node;
2320 attr_list_ref(p) = 1;
2321 node_attr(n) = p;
2322 p = new_attribute_node((unsigned) i, val);
2323 vlink(node_attr(n)) = p;
2324 return;
2326 /* we check if we have this attribute already and quit if the value stays the same */
2327 if (vlink(p) != null) {
2328 while (vlink(p) != null) {
2329 int t = attribute_id(vlink(p));
2330 if (t == i && attribute_value(vlink(p)) == val)
2331 return;
2332 if (t >= i)
2333 break;
2334 j++;
2335 p = vlink(p);
2337 /* j has now the position (if found) .. we assume a sorted list ! */
2338 p = node_attr(n);
2340 if (attr_list_ref(p) == 0 ) {
2341 /* the list is invalid i.e. freed already */
2342 formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n);
2343 /* the still dangling list gets ref count 1 */
2344 attr_list_ref(p) = 1;
2345 } else if (attr_list_ref(p) == 1) {
2346 /* this can really happen HH-LS */
2347 if (p == attr_list_cache) {
2348 /* we can invalidate the cache setting */
2349 /* attr_list_cache = cache_disabled */
2350 /* or save the list, as done below */
2351 p = copy_attribute_list(p);
2352 node_attr(n) = p;
2353 /* the copied list gets ref count 1 */
2354 attr_list_ref(p) = 1;
2356 } else {
2357 /* the list is used multiple times so we make a copy */
2358 p = copy_attribute_list(p);
2359 /* we decrement the ref count or the original */
2360 delete_attribute_ref(node_attr(n));
2361 node_attr(n) = p;
2362 /* the copied list gets ref count 1 */
2363 attr_list_ref(p) = 1;
2367 /* we go to position j in the list */
2368 while (j-- > 0)
2369 p = vlink(p);
2370 /* if we have a hit we just set the value otherwise we add a new node */
2371 if (attribute_id(vlink(p)) == i) {
2372 attribute_value(vlink(p)) = val;
2373 } else { /* add a new node */
2374 halfword r = new_attribute_node((unsigned) i, val);
2375 vlink(r) = vlink(p);
2376 vlink(p) = r;
2378 } else {
2379 normal_error("nodes","trying to set an attribute fails, case 2");
2383 @ @c
2384 int unset_attribute(halfword n, int i, int val)
2386 register halfword p;
2387 register int t;
2388 register int j = 0;
2390 if (!nodetype_has_attributes(type(n)))
2391 return null;
2392 p = node_attr(n);
2393 if (p == null)
2394 return UNUSED_ATTRIBUTE;
2395 if (attr_list_ref(p) == 0) {
2396 formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n);
2397 return UNUSED_ATTRIBUTE;
2399 if (vlink(p) != null) {
2400 while (vlink(p) != null) {
2401 t = attribute_id(vlink(p));
2402 if (t > i)
2403 return UNUSED_ATTRIBUTE;
2404 if (t == i) {
2405 p = vlink(p);
2406 break;
2408 j++;
2409 p = vlink(p);
2411 if (attribute_id(p) != i)
2412 return UNUSED_ATTRIBUTE;
2413 /* if we are still here, the attribute exists */
2414 p = node_attr(n);
2415 if (attr_list_ref(p) > 1 || p == attr_list_cache) {
2416 halfword q = copy_attribute_list(p);
2417 if (attr_list_ref(p) > 1) {
2418 delete_attribute_ref(node_attr(n));
2420 attr_list_ref(q) = 1;
2421 node_attr(n) = q;
2423 p = vlink(node_attr(n));
2424 while (j-- > 0)
2425 p = vlink(p);
2426 t = attribute_value(p);
2427 if (val == UNUSED_ATTRIBUTE || t == val) {
2428 attribute_value(p) = UNUSED_ATTRIBUTE;
2430 return t;
2431 } else {
2432 normal_error("nodes","trying to unset an attribute fails");
2433 return null;
2437 @ @c
2438 int has_attribute(halfword n, int i, int val)
2440 register halfword p;
2441 if (!nodetype_has_attributes(type(n)))
2442 return UNUSED_ATTRIBUTE;
2443 p = node_attr(n);
2444 if (p == null || vlink(p) == null)
2445 return UNUSED_ATTRIBUTE;
2446 p = vlink(p);
2447 while (p != null) {
2448 if (attribute_id(p) == i) {
2449 int ret = attribute_value(p);
2450 if (val == UNUSED_ATTRIBUTE || val == ret)
2451 return ret;
2452 return UNUSED_ATTRIBUTE;
2453 } else if (attribute_id(p) > i) {
2454 return UNUSED_ATTRIBUTE;
2456 p = vlink(p);
2458 return UNUSED_ATTRIBUTE;
2461 @ @c
2462 void print_short_node_contents(halfword p)
2464 switch (type(p)) {
2465 case hlist_node:
2466 case vlist_node:
2467 case ins_node:
2468 case whatsit_node:
2469 case mark_node:
2470 case adjust_node:
2471 case unset_node:
2472 print_char('[');
2473 print_char(']');
2474 break;
2475 case rule_node:
2476 print_char('|');
2477 break;
2478 case glue_node:
2479 if (! glue_is_zero(p))
2480 print_char(' ');
2481 break;
2482 case math_node:
2483 print_char('$');
2484 break;
2485 case disc_node:
2486 short_display(vlink(pre_break(p)));
2487 short_display(vlink(post_break(p)));
2488 break;
2492 @ @c
2493 static void show_pdftex_whatsit_rule_spec(int p)
2495 tprint("(");
2496 print_rule_dimen(height(p));
2497 print_char('+');
2498 print_rule_dimen(depth(p));
2499 tprint(")x");
2500 print_rule_dimen(width(p));
2503 @ Each new type of node that appears in our data structure must be capable
2504 of being displayed, copied, destroyed, and so on. The routines that we
2505 need for write-oriented whatsits are somewhat like those for mark nodes;
2506 other extensions might, of course, involve more subtlety here.
2509 static void print_write_whatsit(const char *s, pointer p)
2511 tprint_esc(s);
2512 if (write_stream(p) < 16)
2513 print_int(write_stream(p));
2514 else if (write_stream(p) == 16)
2515 print_char('*');
2516 else
2517 print_char('-');
2520 @ @c
2521 static void show_node_wrapup_core(int p)
2523 switch (subtype(p)) {
2524 case open_node:
2525 print_write_whatsit("openout", p);
2526 print_char('=');
2527 print_file_name(open_name(p), open_area(p), open_ext(p));
2528 break;
2529 case write_node:
2530 print_write_whatsit("write", p);
2531 print_mark(write_tokens(p));
2532 break;
2533 case close_node:
2534 print_write_whatsit("closeout", p);
2535 break;
2536 case special_node:
2537 tprint_esc("special");
2538 print_mark(write_tokens(p));
2539 break;
2540 case late_lua_node:
2541 show_late_lua(p);
2542 break;
2543 case save_pos_node:
2544 tprint_esc("savepos");
2545 break;
2546 case user_defined_node:
2547 tprint_esc("whatsit");
2548 print_int(user_node_id(p));
2549 print_char('=');
2550 switch (user_node_type(p)) {
2551 case 'a':
2552 tprint("<>");
2553 break;
2554 case 'n':
2555 tprint("[");
2556 show_node_list(user_node_value(p));
2557 tprint("]");
2558 break;
2559 case 's':
2560 print_char('"');
2561 print(user_node_value(p));
2562 print_char('"');
2563 break;
2564 case 't':
2565 print_mark(user_node_value(p));
2566 break;
2567 default: /* only 'd' */
2568 print_int(user_node_value(p));
2569 break;
2571 break;
2575 void show_node_wrapup_dvi(int p)
2579 void show_node_wrapup_pdf(int p)
2581 switch (subtype(p)) {
2582 case pdf_literal_node:
2583 show_pdf_literal(p);
2584 break;
2585 case pdf_colorstack_node:
2586 tprint_esc("pdfcolorstack ");
2587 print_int(pdf_colorstack_stack(p));
2588 switch (pdf_colorstack_cmd(p)) {
2589 case colorstack_set:
2590 tprint(" set ");
2591 break;
2592 case colorstack_push:
2593 tprint(" push ");
2594 break;
2595 case colorstack_pop:
2596 tprint(" pop");
2597 break;
2598 case colorstack_current:
2599 tprint(" current");
2600 break;
2601 default:
2602 confusion("colorstack");
2603 break;
2605 if (pdf_colorstack_cmd(p) <= colorstack_data)
2606 print_mark(pdf_colorstack_data(p));
2607 break;
2608 case pdf_setmatrix_node:
2609 tprint_esc("pdfsetmatrix");
2610 print_mark(pdf_setmatrix_data(p));
2611 break;
2612 case pdf_save_node:
2613 tprint_esc("pdfsave");
2614 break;
2615 case pdf_restore_node:
2616 tprint_esc("pdfrestore");
2617 break;
2618 case pdf_refobj_node:
2619 tprint_esc("pdfrefobj");
2620 if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) {
2621 if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
2622 tprint(" attr");
2623 lua_rawgeti(Luas, LUA_REGISTRYINDEX,
2624 obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)));
2625 print_char(' ');
2626 tprint((const char *) lua_tostring(Luas, -1));
2627 lua_pop(Luas, 1);
2629 tprint(" stream");
2631 if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p)))
2632 tprint(" file");
2633 if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
2634 lua_rawgeti(Luas, LUA_REGISTRYINDEX,
2635 obj_obj_data(static_pdf, pdf_obj_objnum(p)));
2636 print_char(' ');
2637 tprint((const char *) lua_tostring(Luas, -1));
2638 lua_pop(Luas, 1);
2640 break;
2641 case pdf_annot_node:
2642 tprint_esc("pdfannot");
2643 show_pdftex_whatsit_rule_spec(p);
2644 print_mark(pdf_annot_data(p));
2645 break;
2646 case pdf_start_link_node:
2647 tprint_esc("pdfstartlink");
2648 show_pdftex_whatsit_rule_spec(p);
2649 if (pdf_link_attr(p) != null) {
2650 tprint(" attr");
2651 print_mark(pdf_link_attr(p));
2653 tprint(" action");
2654 if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) {
2655 tprint(" user");
2656 print_mark(pdf_action_tokens(pdf_link_action(p)));
2657 return;
2659 if (pdf_action_file(pdf_link_action(p)) != null) {
2660 tprint(" file");
2661 print_mark(pdf_action_file(pdf_link_action(p)));
2663 switch (pdf_action_type(pdf_link_action(p))) {
2664 case pdf_action_goto:
2665 if (pdf_action_named_id(pdf_link_action(p)) > 0) {
2666 tprint(" goto name");
2667 print_mark(pdf_action_id(pdf_link_action(p)));
2668 } else {
2669 tprint(" goto num");
2670 print_int(pdf_action_id(pdf_link_action(p)));
2672 break;
2673 case pdf_action_page:
2674 tprint(" page");
2675 print_int(pdf_action_id(pdf_link_action(p)));
2676 print_mark(pdf_action_tokens(pdf_link_action(p)));
2677 break;
2678 case pdf_action_thread:
2679 if (pdf_action_named_id(pdf_link_action(p)) > 0) {
2680 tprint(" thread name");
2681 print_mark(pdf_action_id(pdf_link_action(p)));
2682 } else {
2683 tprint(" thread num");
2684 print_int(pdf_action_id(pdf_link_action(p)));
2686 break;
2687 default:
2688 normal_error("pdf backend", "unknown action type for link");
2689 break;
2691 break;
2692 case pdf_end_link_node:
2693 tprint_esc("pdfendlink");
2694 break;
2695 case pdf_dest_node:
2696 tprint_esc("pdfdest");
2697 if (pdf_dest_named_id(p) > 0) {
2698 tprint(" name");
2699 print_mark(pdf_dest_id(p));
2700 } else {
2701 tprint(" num");
2702 print_int(pdf_dest_id(p));
2704 print_char(' ');
2705 switch (pdf_dest_type(p)) {
2706 case pdf_dest_xyz:
2707 tprint("xyz");
2708 if (pdf_dest_xyz_zoom(p) != null) {
2709 tprint(" zoom");
2710 print_int(pdf_dest_xyz_zoom(p));
2712 break;
2713 case pdf_dest_fitbh:
2714 tprint("fitbh");
2715 break;
2716 case pdf_dest_fitbv:
2717 tprint("fitbv");
2718 break;
2719 case pdf_dest_fitb:
2720 tprint("fitb");
2721 break;
2722 case pdf_dest_fith:
2723 tprint("fith");
2724 break;
2725 case pdf_dest_fitv:
2726 tprint("fitv");
2727 break;
2728 case pdf_dest_fitr:
2729 tprint("fitr");
2730 show_pdftex_whatsit_rule_spec(p);
2731 break;
2732 case pdf_dest_fit:
2733 tprint("fit");
2734 break;
2735 default:
2736 tprint("unknown!");
2737 break;
2739 break;
2740 case pdf_thread_node:
2741 case pdf_start_thread_node:
2742 if (subtype(p) == pdf_thread_node)
2743 tprint_esc("pdfthread");
2744 else
2745 tprint_esc("pdfstartthread");
2746 tprint("(");
2747 print_rule_dimen(height(p));
2748 print_char('+');
2749 print_rule_dimen(depth(p));
2750 tprint(")x");
2751 print_rule_dimen(width(p));
2752 if (pdf_thread_attr(p) != null) {
2753 tprint(" attr");
2754 print_mark(pdf_thread_attr(p));
2756 if (pdf_thread_named_id(p) > 0) {
2757 tprint(" name");
2758 print_mark(pdf_thread_id(p));
2759 } else {
2760 tprint(" num");
2761 print_int(pdf_thread_id(p));
2763 break;
2764 case pdf_end_thread_node:
2765 tprint_esc("pdfendthread");
2766 break;
2767 default:
2768 break;
2772 @ Now we are ready for |show_node_list| itself. This procedure has been
2773 written to be ``extra robust'' in the sense that it should not crash or get
2774 into a loop even if the data structures have been messed up by bugs in
2775 the rest of the program. You can safely call its parent routine
2776 |show_box(p)| for arbitrary values of |p| when you are debugging \TeX.
2777 However, in the presence of bad data, the procedure may
2778 fetch a |memory_word| whose variant is different from the way it was stored;
2779 for example, it might try to read |mem[p].hh| when |mem[p]|
2780 contains a scaled integer, if |p| is a pointer that has been
2781 clobbered or chosen at random.
2784 @ |str_room| need not be checked; see |show_box|
2786 @ Recursive calls on |show_node_list| therefore use the following pattern:
2788 #define node_list_display(A) do { \
2789 append_char('.'); \
2790 show_node_list(A); \
2791 flush_char(); \
2792 } while (0)
2794 /* prints a node list symbolically */
2796 void show_node_list(int p)
2798 int n = 0; /* the number of items already printed at this level */
2799 halfword w;
2800 real g; /* a glue ratio, as a floating point number */
2801 if ((int) cur_length > depth_threshold) {
2802 if (p > null)
2803 tprint(" []"); /* indicate that there's been some truncation */
2804 return;
2806 while (p != null) {
2807 print_ln();
2808 print_current_string(); /* display the nesting history */
2809 if (int_par(tracing_online_code) < -2)
2810 print_int(p);
2811 incr(n);
2812 if (n > breadth_max) { /* time to stop */
2813 tprint("etc.");
2814 return;
2816 /* Display node |p| */
2817 if (is_char_node(p)) {
2818 print_font_and_char(p);
2819 if (is_ligature(p)) {
2820 /* Display ligature |p|; */
2821 tprint(" (ligature ");
2822 if (is_leftboundary(p))
2823 print_char('|');
2824 font_in_short_display = font(p);
2825 short_display(lig_ptr(p));
2826 if (is_rightboundary(p))
2827 print_char('|');
2828 print_char(')');
2830 } else {
2831 switch (type(p)) {
2832 case hlist_node:
2833 case vlist_node:
2834 case unset_node:
2835 /* Display box |p|; */
2836 if (type(p) == hlist_node)
2837 tprint_esc("h");
2838 else if (type(p) == vlist_node)
2839 tprint_esc("v");
2840 else
2841 tprint_esc("unset");
2842 tprint("box(");
2843 print_scaled(height(p));
2844 print_char('+');
2845 print_scaled(depth(p));
2846 tprint(")x");
2847 print_scaled(width(p));
2848 if (type(p) == unset_node) {
2849 /* Display special fields of the unset node |p|; */
2850 if (span_count(p) != min_quarterword) {
2851 tprint(" (");
2852 print_int(span_count(p) + 1);
2853 tprint(" columns)");
2855 if (glue_stretch(p) != 0) {
2856 tprint(", stretch ");
2857 print_glue(glue_stretch(p), glue_order(p), NULL);
2859 if (glue_shrink(p) != 0) {
2860 tprint(", shrink ");
2861 print_glue(glue_shrink(p), glue_sign(p), NULL);
2863 } else {
2864 /* Display the value of |glue_set(p)| */
2865 /* The code will have to change in this place if |glue_ratio| is
2866 a structured type instead of an ordinary |real|. Note that this routine
2867 should avoid arithmetic errors even if the |glue_set| field holds an
2868 arbitrary random value. The following code assumes that a properly
2869 formed nonzero |real| number has absolute value $2^{20}$ or more when
2870 it is regarded as an integer; this precaution was adequate to prevent
2871 floating point underflow on the author's computer.
2874 g = (real) (glue_set(p));
2875 if ((g != 0.0) && (glue_sign(p) != normal)) {
2876 tprint(", glue set ");
2877 if (glue_sign(p) == shrinking)
2878 tprint("- ");
2879 if (g > 20000.0 || g < -20000.0) {
2880 if (g > 0.0)
2881 print_char('>');
2882 else
2883 tprint("< -");
2884 print_glue(20000 * unity, glue_order(p), NULL);
2885 } else {
2886 print_glue(round(unity * g), glue_order(p), NULL);
2890 if (shift_amount(p) != 0) {
2891 tprint(", shifted ");
2892 print_scaled(shift_amount(p));
2894 tprint(", direction ");
2895 print_dir(box_dir(p));
2897 node_list_display(list_ptr(p)); /* recursive call */
2898 break;
2899 case rule_node:
2900 /* Display rule |p|; */
2901 if (subtype(p) == normal_rule) {
2902 tprint_esc("rule(");
2903 } else if (subtype(p) == empty_rule) {
2904 tprint_esc("norule(");
2905 } else if (subtype(p) == user_rule) {
2906 tprint_esc("userrule(");
2907 } else if (subtype(p) == box_rule) {
2908 tprint_esc("box(");
2909 } else if (subtype(p) == image_rule) {
2910 tprint_esc("image(");
2912 print_rule_dimen(height(p));
2913 print_char('+');
2914 print_rule_dimen(depth(p));
2915 tprint(")x");
2916 print_rule_dimen(width(p));
2917 break;
2918 case ins_node:
2919 /* Display insertion |p|; */
2920 tprint_esc("insert");
2921 print_int(subtype(p));
2922 tprint(", natural size ");
2923 print_scaled(height(p));
2924 tprint("; split(");
2925 print_spec(split_top_ptr(p), NULL);
2926 print_char(',');
2927 print_scaled(depth(p));
2928 tprint("); float cost ");
2929 print_int(float_cost(p));
2930 node_list_display(ins_ptr(p)); /* recursive call */
2931 break;
2932 case dir_node:
2933 if (dir_dir(p) < 0) {
2934 tprint_esc("enddir");
2935 print_char(' ');
2936 print_dir(dir_dir(p) + dir_swap);
2937 } else {
2938 tprint_esc("begindir");
2939 print_char(' ');
2940 print_dir(dir_dir(p));
2942 break;
2943 case local_par_node:
2944 tprint_esc("localpar");
2945 append_char('.');
2946 print_ln();
2947 print_current_string();
2948 tprint_esc("localinterlinepenalty");
2949 print_char('=');
2950 print_int(local_pen_inter(p));
2951 print_ln();
2952 print_current_string();
2953 tprint_esc("localbrokenpenalty");
2954 print_char('=');
2955 print_int(local_pen_broken(p));
2956 print_ln();
2957 print_current_string();
2958 tprint_esc("localleftbox");
2959 if (local_box_left(p) == null) {
2960 tprint("=null");
2961 } else {
2962 append_char('.');
2963 show_node_list(local_box_left(p));
2964 decr(cur_length);
2966 print_ln();
2967 print_current_string();
2968 tprint_esc("localrightbox");
2969 if (local_box_right(p) == null) {
2970 tprint("=null");
2971 } else {
2972 append_char('.');
2973 show_node_list(local_box_right(p));
2974 decr(cur_length);
2976 decr(cur_length);
2977 break;
2978 case boundary_node:
2979 if (subtype(p)==0) {
2980 tprint_esc("noboundary");
2981 } else{
2982 tprint_esc("boundary");
2983 print_char('=');
2984 print_int(subtype(p));
2985 print_char(':');
2986 print_int(boundary_value(p));
2988 break;
2989 case whatsit_node:
2990 w = subtype(p) ;
2991 if (w >= backend_first_pdf_whatsit) {
2992 show_node_wrapup_pdf(p);
2993 } else if (w >= backend_first_dvi_whatsit) {
2994 show_node_wrapup_dvi(p);
2995 } else {
2996 show_node_wrapup_core(p);
2998 break;
2999 case glue_node:
3000 /* Display glue |p|; */
3001 if (subtype(p) >= a_leaders) {
3002 /* Display leaders |p|; */
3003 tprint_esc("");
3004 switch (subtype(p)) {
3005 case a_leaders:
3006 break;
3007 case c_leaders:
3008 print_char('c');
3009 break;
3010 case x_leaders:
3011 print_char('x');
3012 break;
3013 case g_leaders:
3014 print_char('g');
3015 break;
3016 default:
3017 normal_warning("nodes","weird glue leader subtype ignored");
3019 tprint("leaders ");
3020 print_spec(p, NULL);
3021 node_list_display(leader_ptr(p)); /* recursive call */
3022 } else {
3023 tprint_esc("glue");
3024 if (subtype(p) != normal) {
3025 print_char('(');
3026 if ((subtype(p) - 1) < thin_mu_skip_code) {
3027 print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1));
3028 } else if (subtype(p) < cond_math_glue) {
3029 print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1));
3030 } else if (subtype(p) == cond_math_glue) {
3031 tprint_esc("nonscript");
3032 } else {
3033 tprint_esc("mskip");
3035 print_char(')');
3037 if (subtype(p) != cond_math_glue) {
3038 print_char(' ');
3039 if (subtype(p) < cond_math_glue)
3040 print_spec(p, NULL);
3041 else
3042 print_spec(p, "mu");
3045 break;
3046 case margin_kern_node:
3047 tprint_esc("kern");
3048 print_scaled(width(p));
3049 if (subtype(p) == left_side)
3050 tprint(" (left margin)");
3051 else
3052 tprint(" (right margin)");
3053 break;
3054 case kern_node:
3055 /* Display kern |p|; */
3056 /* An ``explicit'' kern value is indicated implicitly by an explicit space. */
3057 if (subtype(p) != mu_glue) {
3058 tprint_esc("kern");
3059 if (subtype(p) != normal)
3060 print_char(' ');
3061 print_scaled(width(p));
3062 if (subtype(p) == accent_kern)
3063 tprint(" (for accent)");
3064 } else {
3065 tprint_esc("mkern");
3066 print_scaled(width(p));
3067 tprint("mu");
3069 break;
3070 case math_node:
3071 /* Display math node |p|; */
3072 tprint_esc("math");
3073 if (subtype(p) == before)
3074 tprint("on");
3075 else
3076 tprint("off");
3077 if (!glue_is_zero(p)) {
3078 tprint(", glued ");
3079 print_spec(p, NULL);
3080 } else if (surround(p) != 0) {
3081 tprint(", surrounded ");
3082 print_scaled(surround(p));
3084 break;
3085 case penalty_node:
3086 /* Display penalty |p|; */
3087 tprint_esc("penalty ");
3088 print_int(penalty(p));
3089 break;
3090 case disc_node:
3091 /* Display discretionary |p|; */
3092 /* The |post_break| list of a discretionary node is indicated by a prefixed
3093 `\.{\char'174}' instead of the `\..' before the |pre_break| list. */
3094 tprint_esc("discretionary");
3095 print_int(disc_penalty(p));
3096 print_char('|');
3097 if (vlink(no_break(p)) != null) {
3098 tprint(" replacing ");
3099 node_list_display(vlink(no_break(p)));
3101 node_list_display(vlink(pre_break(p))); /* recursive call */
3102 append_char('|');
3103 show_node_list(vlink(post_break(p)));
3104 flush_char(); /* recursive call */
3105 break;
3106 case mark_node:
3107 /* Display mark |p|; */
3108 tprint_esc("mark");
3109 if (mark_class(p) != 0) {
3110 print_char('s');
3111 print_int(mark_class(p));
3113 print_mark(mark_ptr(p));
3114 break;
3115 case adjust_node:
3116 /* Display adjustment |p|; */
3117 tprint_esc("vadjust");
3118 if (subtype(p) != 0)
3119 tprint(" pre ");
3120 node_list_display(adjust_ptr(p)); /* recursive call */
3121 break;
3122 case glue_spec_node:
3123 tprint("<glue_spec ");
3124 print_spec(p, NULL);
3125 tprint(">");
3126 break;
3127 default:
3128 show_math_node(p);
3129 break;
3132 p = vlink(p);
3136 @ This routine finds the 'base' width of a horizontal box, using the same logic
3137 that \TeX82 used for \.{\\predisplaywidth} */
3140 pointer actual_box_width(pointer r, scaled base_width)
3142 scaled d; /* increment to |v| */
3143 scaled w = -max_dimen; /* calculated |size| */
3144 scaled v = shift_amount(r) + base_width; /* |w| plus possible glue amount */
3145 pointer p = list_ptr(r); /* current node when calculating |pre_display_size| */
3146 while (p != null) {
3147 if (is_char_node(p)) {
3148 d = glyph_width(p);
3149 goto FOUND;
3151 switch (type(p)) {
3152 case hlist_node:
3153 case vlist_node:
3154 case rule_node:
3155 d = width(p);
3156 goto FOUND;
3157 break;
3158 case margin_kern_node:
3159 d = width(p);
3160 break;
3161 case kern_node:
3162 d = width(p);
3163 break;
3164 case math_node:
3165 /* begin mathskip code */
3166 if (glue_is_zero(p)) {
3167 d = surround(p);
3168 break;
3169 } else {
3170 /* fall through */
3172 /* end mathskip code */
3173 case glue_node:
3174 /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set|
3175 values, since such values are subject to system-dependent rounding.
3176 System-dependent numbers are not allowed to infiltrate parameters like
3177 |pre_display_size|, since \TeX82 is supposed to make the same decisions on all
3178 machines.
3180 d = width(p);
3181 if (glue_sign(r) == stretching) {
3182 if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0))
3183 v = max_dimen;
3184 } else if (glue_sign(r) == shrinking) {
3185 if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0))
3186 v = max_dimen;
3188 if (subtype(p) >= a_leaders)
3189 goto FOUND;
3190 break;
3191 default:
3192 d = 0;
3193 break;
3195 if (v < max_dimen)
3196 v = v + d;
3197 goto NOT_FOUND;
3198 FOUND:
3199 if (v < max_dimen) {
3200 v = v + d;
3201 w = v;
3202 } else {
3203 w = max_dimen;
3204 break;
3206 NOT_FOUND:
3207 p = vlink(p);
3209 return w;
3212 @ @c
3213 halfword tail_of_list(halfword p)
3215 halfword q = p;
3216 while (vlink(q) != null)
3217 q = vlink(q);
3218 return q;
3223 @ @c
3224 int var_used;
3226 @ Attribute lists need two extra globals to increase processing efficiency.
3227 |max_used_attr| limits the test loop that checks for set attributes, and
3228 |attr_list_cache| contains a pointer to an already created attribute list. It is
3229 set to the special value |cache_disabled| when the current value can no longer be
3230 trusted: after an assignment to an attribute register, and after a group has
3231 ended.
3234 int max_used_attr; /* maximum assigned attribute id */
3235 halfword attr_list_cache;
3237 @ From the computer's standpoint, \TeX's chief mission is to create
3238 horizontal and vertical lists. We shall now investigate how the elements
3239 of these lists are represented internally as nodes in the dynamic memory.
3241 A horizontal or vertical list is linked together by |link| fields in
3242 the first word of each node. Individual nodes represent boxes, glue,
3243 penalties, or special things like discretionary hyphens; because of this
3244 variety, some nodes are longer than others, and we must distinguish different
3245 kinds of nodes. We do this by putting a `|type|' field in the first word,
3246 together with the link and an optional `|subtype|'.
3248 @ Character nodes appear only in horizontal lists, never in vertical lists.
3250 An |hlist_node| stands for a box that was made from a horizontal list.
3251 Each |hlist_node| is seven words long, and contains the following fields
3252 (in addition to the mandatory |type| and |link|, which we shall not
3253 mention explicitly when discussing the other node types): The |height| and
3254 |width| and |depth| are scaled integers denoting the dimensions of the
3255 box. There is also a |shift_amount| field, a scaled integer indicating
3256 how much this box should be lowered (if it appears in a horizontal list),
3257 or how much it should be moved to the right (if it appears in a vertical
3258 list). There is a |list_ptr| field, which points to the beginning of the
3259 list from which this box was fabricated; if |list_ptr| is |null|, the box
3260 is empty. Finally, there are three fields that represent the setting of
3261 the glue: |glue_set(p)| is a word of type |glue_ratio| that represents
3262 the proportionality constant for glue setting; |glue_sign(p)| is
3263 |stretching| or |shrinking| or |normal| depending on whether or not the
3264 glue should stretch or shrink or remain rigid; and |glue_order(p)|
3265 specifies the order of infinity to which glue setting applies (|normal|,
3266 |sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used.
3268 @ The |new_null_box| function returns a pointer to an |hlist_node| in
3269 which all subfields have the values corresponding to `\.{\\hbox\{\}}'.
3270 The |subtype| field is set to |min_quarterword|, since that's the desired
3271 |span_count| value if this |hlist_node| is changed to an |unset_node|.
3274 halfword new_null_box(void)
3275 { /* creates a new box node */
3276 halfword p = new_node(hlist_node, min_quarterword);
3277 box_dir(p) = text_direction;
3278 return p;
3281 @ A |vlist_node| is like an |hlist_node| in all respects except that it
3282 contains a vertical list.
3284 @ A |rule_node| stands for a solid black rectangle; it has |width|,
3285 |depth|, and |height| fields just as in an |hlist_node|. However, if
3286 any of these dimensions is $-2^{30}$, the actual value will be determined
3287 by running the rule up to the boundary of the innermost enclosing box.
3288 This is called a ``running dimension.'' The |width| is never running in
3289 an hlist; the |height| and |depth| are never running in a~vlist.
3291 @ A new rule node is delivered by the |new_rule| function. It
3292 makes all the dimensions ``running,'' so you have to change the
3293 ones that are not allowed to run.
3296 halfword new_rule(int s)
3298 halfword p = new_node(rule_node,s);
3299 return p;
3302 @ Insertions are represented by |ins_node| records, where the |subtype|
3303 indicates the corresponding box number. For example, `\.{\\insert 250}'
3304 leads to an |ins_node| whose |subtype| is |250+min_quarterword|.
3305 The |height| field of an |ins_node| is slightly misnamed; it actually holds
3306 the natural height plus depth of the vertical list being inserted.
3307 The |depth| field holds the |split_max_depth| to be used in case this
3308 insertion is split, and the |split_top_ptr| points to the corresponding
3309 |split_top_skip|. The |float_cost| field holds the |floating_penalty| that
3310 will be used if this insertion floats to a subsequent page after a
3311 split insertion of the same class. There is one more field, the
3312 |ins_ptr|, which points to the beginning of the vlist for the insertion.
3314 @ A |mark_node| has a |mark_ptr| field that points to the reference count
3315 of a token list that contains the user's \.{\\mark} text.
3316 In addition there is a |mark_class| field that contains the mark class.
3318 @ An |adjust_node|, which occurs only in horizontal lists,
3319 specifies material that will be moved out into the surrounding
3320 vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}'
3321 operation. The |adjust_ptr| field points to the vlist containing this
3322 material.
3324 @ A |glyph_node|, which occurs only in horizontal lists, specifies a
3325 glyph in a particular font, along with its attribute list. Older
3326 versions of \TeX\ could use token memory for characters, because the
3327 font,char combination would fit in a single word (both values were
3328 required to be strictly less than $2^{16}$). In LuaTeX, room is
3329 needed for characters that are larger than that, as well as a pointer
3330 to a potential attribute list, and the two displacement values.
3332 In turn, that made the node so large that it made sense to merge
3333 ligature glyphs as well, as that requires only one extra pointer. A
3334 few extra classes of glyph nodes will be introduced later. The
3335 unification of all those types makes it easier to manipulate lists of
3336 glyphs. The subtype differentiates various glyph kinds.
3338 First, here is a function that returns a pointer to a glyph node for a given
3339 glyph in a given font. If that glyph doesn't exist, |null| is returned
3340 instead. Nodes of this subtype are directly created only for accents
3341 and their base (through |make_accent|), and math nucleus items (in the
3342 conversion from |mlist| to |hlist|).
3345 halfword new_glyph(int f, int c)
3347 halfword p = null; /* the new node */
3348 if ((f == 0) || (char_exists(f, c))) {
3349 p = new_glyph_node();
3350 set_to_glyph(p);
3351 font(p) = f;
3352 character(p) = c;
3354 return p;
3357 @ A subset of the glyphs nodes represent ligatures: characters
3358 fabricated from the interaction of two or more actual characters. The
3359 characters that generated the ligature have not been forgotten, since
3360 they are needed for diagnostic messages; the |lig_ptr| field points to
3361 a linked list of character nodes for all original characters that have
3362 been deleted. (This list might be empty if the characters that
3363 generated the ligature were retained in other nodes.)
3365 The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if
3366 the original source of the ligature included implicit left and/or
3367 right boundaries. These nodes are created by the C function |new_ligkern|.
3369 A third general type of glyphs could be called a character, as it
3370 only appears in lists that are not yet processed by the ligaturing and
3371 kerning steps of the program.
3373 |main_control| inserts these, and they are later converted to
3374 |subtype_normal| by |new_ligkern|.
3377 quarterword norm_min(int h)
3379 if (h <= 0)
3380 return 1;
3381 else if (h >= 255)
3382 return 255;
3383 else
3384 return (quarterword) h;
3387 halfword new_char(int f, int c)
3389 halfword p; /* the new node */
3390 p = new_glyph_node();
3391 set_to_character(p);
3392 font(p) = f;
3393 character(p) = c;
3394 lang_data(p) = make_lang_data(uc_hyph, cur_lang, left_hyphen_min, right_hyphen_min);
3395 return p;
3398 @ Left and right ghost glyph nodes are the result of \.{\\leftghost}
3399 and \.{\\rightghost}, respectively. They are going to be removed by
3400 |new_ligkern|, at the end of which they are no longer needed.
3402 @ Here are a few handy helpers used by the list output routines.
3405 scaled glyph_width(halfword p)
3407 scaled w = char_width(font(p), character(p));
3408 return w;
3411 scaled glyph_height(halfword p)
3413 scaled w = char_height(font(p), character(p)) + y_displace(p);
3414 if (w < 0)
3415 w = 0;
3416 return w;
3419 scaled glyph_depth(halfword p)
3421 scaled w = char_depth(font(p), character(p));
3422 if (y_displace(p) > 0)
3423 w = w - y_displace(p);
3424 if (w < 0)
3425 w = 0;
3426 return w;
3429 @ A |disc_node|, which occurs only in horizontal lists, specifies a
3430 ``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text
3431 that starts at |pre_break(p)| will precede the break, the text that starts at
3432 |post_break(p)| will follow the break, and text that appears in
3433 |no_break(p)| nodes will be ignored. For example, an ordinary
3434 discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with
3435 |pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|,
3436 and |no_break=null|.
3438 {TODO: Knuth said: All three of the discretionary texts must be lists
3439 that consist entirely of character, kern, box and rule nodes.}
3441 If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this
3442 break. Otherwise the |hyphen_penalty| will be charged. The texts will
3443 actually be substituted into the list by the line-breaking algorithm if it
3444 decides to make the break, and the discretionary node will disappear at
3445 that time; thus, the output routine sees only discretionaries that were
3446 not chosen.
3449 halfword new_disc(void)
3450 { /* creates an empty |disc_node| */
3451 halfword p = new_node(disc_node, 0);
3452 disc_penalty(p) = int_par(hyphen_penalty_code);
3453 return p;
3456 @ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
3457 |subtype| field in its first word says what `\\{whatsit}' it is, and
3458 implicitly determines the node size (which must be 2 or more) and the
3459 format of the remaining words. When a |whatsit_node| is encountered
3460 in a list, special actions are invoked; knowledgeable people who are
3461 careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
3462 things by adding code at the end of the program. For example, there
3463 might be a `\TeX nicolor' extension to specify different colors of ink,
3464 @^extensions to \TeX@>
3465 and the whatsit node might contain the desired parameters.
3467 The present implementation of \TeX\ treats the features associated with
3468 `\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
3469 illustrate how such routines might be coded. We shall defer further
3470 discussion of extensions until the end of this program.
3472 @ A |math_node|, which occurs only in horizontal lists, appears before and
3473 after mathematical formulas. The |subtype| field is |before| before the
3474 formula and |after| after it. There is a |surround| field, which represents
3475 the amount of surrounding space inserted by \.{\\mathsurround}.
3478 halfword new_math(scaled w, int s)
3480 halfword p = new_node(math_node, s);
3481 surround(p) = w;
3482 return p;
3485 @ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|,
3486 |rule_node|, |ins_node|, |mark_node|, |adjust_node|,
3487 |disc_node|, |whatsit_node|, and |math_node| are at the low end of the
3488 type codes, by permitting a break at glue in a list if and only if the
3489 |type| of the previous node is less than |math_node|. Furthermore, a
3490 node is discarded after a break if its type is |math_node| or~more.
3492 @ A |glue_node| represents glue in a list. However, it is really only
3493 a pointer to a separate glue specification, since \TeX\ makes use of the
3494 fact that many essentially identical nodes of glue are usually present.
3495 If |p| points to a |glue_node|, |glue_ptr(p)| points to
3496 another packet of words that specify the stretch and shrink components, etc.
3498 Glue nodes also serve to represent leaders; the |subtype| is used to
3499 distinguish between ordinary glue (which is called |normal|) and the three
3500 kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|).
3501 The |leader_ptr| field points to a rule node or to a box node containing the
3502 leaders; it is set to |null| in ordinary glue nodes.
3504 Many kinds of glue are computed from \TeX's ``skip'' parameters, and
3505 it is helpful to know which parameter has led to a particular glue node.
3506 Therefore the |subtype| is set to indicate the source of glue, whenever
3507 it originated as a parameter. We will be defining symbolic names for the
3508 parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|,
3509 etc.); it suffices for now to say that the |subtype| of parametric glue
3510 will be the same as the parameter number, plus~one.
3512 @ In math formulas there are two more possibilities for the |subtype| in a
3513 glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu}
3514 instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}'
3515 feature that cancels the glue node immediately following if it appears
3516 in a subscript.
3518 @ A glue specification has a halfword reference count in its first word,
3519 @^reference counts@>
3520 representing |null| plus the number of glue nodes that point to it (less one).
3521 Note that the reference count appears in the same position as
3522 the |link| field in list nodes; this is the field that is initialized
3523 to |null| when a node is allocated, and it is also the field that is flagged
3524 by |empty_flag| in empty nodes.
3526 Glue specifications also contain three |scaled| fields, for the |width|,
3527 |stretch|, and |shrink| dimensions. Finally, there are two one-byte
3528 fields called |stretch_order| and |shrink_order|; these contain the
3529 orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|)
3530 corresponding to the stretch and shrink values.
3532 @ Here is a function that returns a pointer to a copy of a glue spec.
3533 The reference count in the copy is |null|, because there is assumed
3534 to be exactly one reference to the new specification.
3537 halfword new_spec(halfword p)
3539 return copy_node(p == null ? zero_glue : p);
3542 @ And here's a function that creates a glue node for a given parameter
3543 identified by its code number; for example,
3544 |new_param_glue(line_skip_code)| returns a pointer to a glue node for the
3545 current \.{\\lineskip}.
3548 halfword new_param_glue(int n)
3550 halfword p = new_node(glue_node, n + 1);
3551 halfword q = glue_par(n);
3552 width(p) = width(q);
3553 stretch(p) = stretch(q);
3554 shrink(p) = shrink(q);
3555 stretch_order(p) = stretch_order(q);
3556 shrink_order(p) = shrink_order(q);
3557 return p;
3560 @ Glue nodes that are more or less anonymous are created by |new_glue|,
3561 whose argument points to a glue specification.
3564 halfword new_glue(halfword q)
3566 halfword p = new_node(glue_node, normal);
3567 width(p) = width(q);
3568 stretch(p) = stretch(q);
3569 shrink(p) = shrink(q);
3570 stretch_order(p) = stretch_order(q);
3571 shrink_order(p) = shrink_order(q);
3572 return p;
3575 @ Still another subroutine is needed: This one is sort of a combination
3576 of |new_param_glue| and |new_glue|. It creates a glue node for one of
3577 the current glue parameters, but it makes a fresh copy of the glue
3578 specification, since that specification will probably be subject to change,
3579 while the parameter will stay put.
3582 The global variable |temp_ptr| is set to the address of the new spec.
3586 halfword new_skip_param(int n)
3588 halfword p = new_node(glue_node, n + 1);
3589 halfword q = glue_par(n);
3590 width(p) = width(q);
3591 stretch(p) = stretch(q);
3592 shrink(p) = shrink(q);
3593 stretch_order(p) = stretch_order(q);
3594 shrink_order(p) = shrink_order(q);
3595 return p;
3598 @ A |kern_node| has a |width| field to specify a (normally negative)
3599 amount of spacing. This spacing correction appears in horizontal lists
3600 between letters like A and V when the font designer said that it looks
3601 better to move them closer together or further apart. A kern node can
3602 also appear in a vertical list, when its `|width|' denotes additional
3603 spacing in the vertical direction. The |subtype| is either |normal| (for
3604 kerns inserted from font information or math mode calculations) or |explicit|
3605 (for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern|
3606 (for kerns inserted from non-math accents) or |mu_glue| (for kerns
3607 inserted from \.{\\mkern} specifications in math formulas).
3609 @ The |new_kern| function creates a kern node having a given width.
3612 halfword new_kern(scaled w)
3614 halfword p = new_node(kern_node, normal);
3615 width(p) = w;
3616 return p;
3619 @ A |penalty_node| specifies the penalty associated with line or page
3620 breaking, in its |penalty| field. This field is a fullword integer, but
3621 the full range of integer values is not used: Any penalty |>=10000| is
3622 treated as infinity, and no break will be allowed for such high values.
3623 Similarly, any penalty |<=-10000| is treated as negative infinity, and a
3624 break will be forced.
3626 @ Anyone who has been reading the last few sections of the program will
3627 be able to guess what comes next.
3630 halfword new_penalty(int m)
3632 halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */
3633 penalty(p) = m;
3634 return p;
3637 @ You might think that we have introduced enough node types by now. Well,
3638 almost, but there is one more: An |unset_node| has nearly the same format
3639 as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign}
3640 or \.{\\valign} that are not yet in their final form, since the box
3641 dimensions are their ``natural'' sizes before any glue adjustment has been
3642 made. The |glue_set| word is not present; instead, we have a |glue_stretch|
3643 field, which contains the total stretch of order |glue_order| that is
3644 present in the hlist or vlist being boxed.
3645 Similarly, the |shift_amount| field is replaced by a |glue_shrink| field,
3646 containing the total shrink of order |glue_sign| that is present.
3647 The |subtype| field is called |span_count|; an unset box typically
3648 contains the data for |qo(span_count)+1| columns.
3649 Unset nodes will be changed to box nodes when alignment is completed.
3651 In fact, there are still more types coming. When we get to math formula
3652 processing we will see that a |style_node| has |type=14|; and a number
3653 of larger type codes will also be defined, for use in math mode only.
3655 Warning: If any changes are made to these data structure layouts, such as
3656 changing any of the node sizes or even reordering the words of nodes,
3657 the |copy_node_list| procedure and the memory initialization code
3658 below may have to be changed. Such potentially dangerous parts of the
3659 program are listed in the index under `data structure assumptions'.
3660 @!@^data structure assumptions@>
3661 However, other references to the nodes are made symbolically in terms of
3662 the \.{WEB} macro definitions above, so that format changes will leave
3663 \TeX's other algorithms intact.
3664 @^system dependencies@>
3666 @ This function creates a |local_paragraph| node
3670 halfword make_local_par_node(int mode)
3672 int callback_id;
3673 halfword q;
3674 halfword p = new_node(local_par_node,0);
3675 local_pen_inter(p) = local_inter_line_penalty;
3676 local_pen_broken(p) = local_broken_penalty;
3677 if (local_left_box != null) {
3678 q = copy_node_list(local_left_box);
3679 local_box_left(p) = q;
3680 local_box_left_width(p) = width(local_left_box);
3682 if (local_right_box != null) {
3683 q = copy_node_list(local_right_box);
3684 local_box_right(p) = q;
3685 local_box_right_width(p) = width(local_right_box);
3687 local_par_dir(p) = par_direction;
3688 /* callback with node passed */
3689 callback_id = callback_defined(insert_local_par_callback);
3690 if (callback_id > 0) {
3691 int sfix = lua_gettop(Luas);
3692 if (!get_callback(Luas, callback_id)) {
3693 lua_settop(Luas, sfix);
3694 return p;
3696 nodelist_to_lua(Luas, p);
3697 lua_push_local_par_mode(Luas,mode)
3698 if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */
3699 char errmsg[256]; /* temp hack ... we will have a formatted error */
3700 snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1));
3701 errmsg[255]='\0';
3702 lua_settop(Luas, sfix);
3703 normal_error("insert_local_par",errmsg); /* to be done */
3704 return p;
3706 lua_settop(Luas, sfix);
3708 /* done */
3709 return p;