beta-0.89.2
[luatex.git] / source / texk / web2c / luatexdir / tex / texnodes.w
blobde718139923060d261f13dfcdf639c09f5381af8
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", NULL
94 const char *node_fields_glue[] = {
95 "attr", "spec", "leader", 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", NULL
117 const char *node_fields_splitup[] = {
118 "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL
120 const char *node_fields_attribute[] = {
121 "number", "value", NULL
123 const char *node_fields_glue_spec[] = {
124 "width", "stretch", "shrink", "stretch_order", "shrink_order", "ref_count",
125 "writable", 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", "dvi_ptr", "dvi_h", 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",
241 "ref_count", NULL
243 const char *node_fields_whatsit_pdf_thread[] = {
244 "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
246 const char *node_fields_whatsit_pdf_start_thread[] = {
247 "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL
249 const char *node_fields_whatsit_pdf_end_thread[] = {
250 "attr", NULL
252 const char *node_fields_whatsit_pdf_colorstack[] = {
253 "attr", "stack", "cmd", "data", NULL
255 const char *node_fields_whatsit_pdf_setmatrix[] = {
256 "attr", "data", NULL
258 const char *node_fields_whatsit_pdf_save[] = {
259 "attr", NULL
261 const char *node_fields_whatsit_pdf_restore[] = {
262 "attr", NULL
265 /* subtypes */
267 const char *node_subtypes_glue[] = {
268 "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip",
269 "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip",
270 "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip", "thinmuskip",
271 "medmuskip", "thickmuskip", "mathskip", NULL
273 const char *node_subtypes_leader[] = { /* 100+ */
274 "leaders", "cleaders", "xleaders", "gleaders", NULL
276 const char *node_subtypes_fill[] = {
277 "stretch", "fi", "fil", "fill", "filll", NULL
279 const char *node_subtypes_penalty[] = {
280 "userpenalty", NULL
282 const char *node_subtypes_kern[] = {
283 "fontkern", "userkern", "accentkern", "italiccorrection", NULL
285 const char *node_subtypes_rule[] = {
286 "normal", "box", "image", "empty", "user", NULL
288 const char *node_subtypes_glyph[] = {
289 "character", "glyph", "ligature", "ghost", "left", "right", NULL
291 const char *node_subtypes_disc[] = {
292 "discretionary", "explicit", "automatic", "regular", "first", "second", NULL
294 const char *node_subtypes_marginkern[] = {
295 "left", "right", NULL
297 const char *node_subtypes_list[] = {
298 "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL
300 const char *node_subtypes_math[] = {
301 "beginmath", "endmath", NULL
303 const char *node_subtypes_noad[] = {
304 "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close",
305 "punct", "inner", "under", "over", "vcenter", NULL
307 const char *node_subtypes_radical[] = {
308 "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder",
309 "udelimiterover", NULL
311 const char *node_subtypes_accent[] = {
312 "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL,
314 const char *node_subtypes_fence[] = {
315 "unset", "left", "middle", "right", NULL
318 node_info node_data[] = { /* the last entry in a row is the etex number */
319 { hlist_node, box_node_size, node_fields_list, "hlist", 1 },
320 { vlist_node, box_node_size, node_fields_list, "vlist", 2 },
321 { rule_node, rule_node_size, node_fields_rule, "rule", 3 },
322 { ins_node, ins_node_size, node_fields_insert, "ins", 4 },
323 { mark_node, mark_node_size, node_fields_mark, "mark", 5 },
324 { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 },
325 { boundary_node, boundary_size, node_fields_boundary, "boundary", -1 },
326 { disc_node, disc_node_size, node_fields_disc, "disc", 8 },
327 { whatsit_node, -1, NULL, "whatsit", 9 },
328 { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 },
329 { dir_node, dir_node_size, node_fields_dir, "dir", -1 },
330 { math_node, math_node_size, node_fields_math, "math", 10 },
331 { glue_node, glue_node_size, node_fields_glue, "glue", 11 },
332 { kern_node, kern_node_size, node_fields_kern, "kern", 12 },
333 { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 },
334 { unset_node, box_node_size, node_fields_unset, "unset", 14 },
335 { style_node, style_node_size, node_fields_style, "style", 15 },
336 { choice_node, style_node_size, node_fields_choice, "choice", 15 },
337 { simple_noad, noad_size, node_fields_ord, "noad", 15 },
338 { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 },
339 { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 },
340 { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 },
341 { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 },
342 { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 },
343 { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 },
344 { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 },
345 { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 },
346 { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 },
347 { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 },
348 { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 },
349 { align_record_node, box_node_size, NULL, "align_record", -1 },
350 { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 },
351 { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 },
352 { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 },
353 { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 },
354 { expr_node, expr_node_size, NULL, "expr_stack", -1 },
355 { nesting_node, nesting_node_size, NULL, "nested_list", -1 },
356 { span_node, span_node_size, NULL, "span", -1 },
357 { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 },
358 { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 },
359 { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 },
360 { temp_node, temp_node_size, NULL, "temp", -1 },
361 { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 },
362 { movement_node, movement_node_size, NULL, "movement_stack", -1 },
363 { if_node, if_node_size, NULL, "if_stack", -1 },
364 { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 },
365 { hyphenated_node, active_node_size, NULL, "hyphenated", -1 },
366 { delta_node, delta_node_size, NULL, "delta", -1 },
367 { passive_node, passive_node_size, NULL, "passive", -1 },
368 { shape_node, variable_node_size, NULL, "shape", -1 },
369 { -1, -1, NULL, NULL, -1 },
372 #define last_normal_node shape_node
374 const char *node_subtypes_pdf_destination[] = {
375 "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL
377 const char *node_subtypes_pdf_literal[] = {
378 "origin", "page", "direct", NULL
381 node_info whatsit_node_data[] = {
382 { open_node, open_node_size, node_fields_whatsit_open, "open", -1 },
383 { write_node, write_node_size, node_fields_whatsit_write, "write", -1 },
384 { close_node, close_node_size, node_fields_whatsit_close, "close", -1 },
385 { special_node, special_node_size, node_fields_whatsit_special, "special", -1 },
386 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
387 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
388 { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 },
389 { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 },
390 { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 },
391 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
392 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
393 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
394 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
395 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
396 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
397 { fake_node, fake_node_size, NULL, fake_node_name, -1 },
398 /* here starts the dvi backend section, todo: a separate list */
399 /* nothing for dvi */
400 /* here starts the pdf backend section, todo: a separate list */
401 { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 },
402 { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 },
403 { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 },
404 { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 },
405 { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 },
406 { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 },
407 { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 },
408 { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 },
409 { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 },
410 { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 },
411 { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 },
412 { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 },
413 { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 },
414 { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 },
415 { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 },
416 { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 },
417 /* done */
418 { -1, -1, NULL, NULL, -1 },
421 #define last_whatsit_node pdf_restore_node
424 When we copy a node list, there are several possibilities: we do the same as a new node,
425 we copy the entry to the table in properties (a reference), we do a deep copy of a table
426 in the properties, we create a new table and give it the original one as a metatable.
427 After some experiments (that also included timing) with these scenarios I decided that a
428 deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable
429 variant were both ok, although the second ons is slower. The most important aspect to keep
430 in mind is that references to other nodes in properties no longer can be valid for that
431 copy. We could use two tables (one unique and one shared) or metatables but that only
432 complicates matters.
434 When defining a new node, we could already allocate a table but it is rather easy to do
435 that at the lua end e.g. using a metatable __index method. That way it is under macro
436 package control.
438 When deleting a node, we could keep the slot (e.g. setting it to false) but it could make
439 memory consumption raise unneeded when we have temporary large node lists and after that
440 only small lists.
442 So, in the end this is what we ended up with. For the record, I also experimented with the
443 following:
445 - copy attributes to the properties so that we have fast access at the lua end: in the end
446 the overhead is not compensated by speed and convenience, in fact, attributes are not
447 that slow when it comes to accessing them
449 - a bitset in the node but again the gain compared to attributes is neglectable and it also
450 demands a pretty string agreement over what bit represents what, and this is unlikely to
451 succeed in the tex community (I could use it for font handling, which is cross package,
452 but decided that it doesn't pay off
454 In case one wonders why properties make sense then, well, it is not so much speed that we
455 gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and
456 this mechanism makes sure that properties are cleaned up when a node is freed. Also, the
457 advantage of a more or less global properties table is that we stay at the lua end. An
458 alternative is to store a reference in the node itself but that is complicated by the fact
459 that the register has some limitations (no numeric keys) and we also don't want to mess with
460 it too much.
463 int lua_properties_level = 0 ; /* can be private */
464 int lua_properties_enabled = 0 ;
465 int lua_properties_use_metatable = 0 ;
468 We keep track of nesting so that we don't oveflow the stack, and, what is more
469 important, don't keep resolving the registry index.
472 #define lua_properties_push do { \
473 if (lua_properties_enabled) { \
474 lua_properties_level = lua_properties_level + 1 ; \
475 if (lua_properties_level == 1) { \
476 lua_rawgeti(Luas, LUA_REGISTRYINDEX, luaS_index(node_properties)); \
477 lua_gettable(Luas, LUA_REGISTRYINDEX); \
480 } while(0)
482 #define lua_properties_pop do { \
483 if (lua_properties_enabled) { \
484 if (lua_properties_level == 1) \
485 lua_pop(Luas,1); \
486 lua_properties_level = lua_properties_level - 1 ; \
488 } while(0)
490 /* No setting is needed: */
492 #define lua_properties_set(target) do { \
493 } while(0)
495 /* Resetting boils down to nilling. */
497 #define lua_properties_reset(target) do { \
498 if (lua_properties_enabled) { \
499 if (lua_properties_level == 0) { \
500 lua_rawgeti(Luas, LUA_REGISTRYINDEX, luaS_index(node_properties)); \
501 lua_gettable(Luas, LUA_REGISTRYINDEX); \
502 lua_pushnil(Luas); \
503 lua_rawseti(Luas,-2,target); \
504 lua_pop(Luas,1); \
505 } else { \
506 lua_pushnil(Luas); \
507 lua_rawseti(Luas,-2,target); \
510 } while(0)
513 For a moment I considered supporting all kind of data types but in practice
514 that makes no sense. So we stick to a cheap shallow copy with as option a
515 metatable. Btw, a deep copy would look like this:
517 static void copy_lua_table(lua_State* L, int index) {
518 lua_newtable(L);
519 lua_pushnil(L);
520 while(lua_next(L, index-1) != 0) {
521 lua_pushvalue(L, -2);
522 lua_insert(L, -2);
523 if (lua_type(L,-1)==LUA_TTABLE)
524 copy_lua_table(L,-1);
525 lua_settable(L, -4);
527 lua_pop(L,1);
530 #define lua_properties_copy(target, source) do { \
531 if (lua_properties_enabled) { \
532 lua_pushinteger(Luas,source); \
533 lua_rawget(Luas,-2); \
534 if (lua_type(Luas,-1)==LUA_TTABLE) { \
535 copy_lua_table(Luas,-1); \
536 lua_pushinteger(Luas,target); \
537 lua_insert(Luas,-2); \
538 lua_rawset(Luas,-3); \
539 } else { \
540 lua_pop(Luas,1); \
543 } while(0)
547 /* isn't there a faster way to metatable? */
549 #define lua_properties_copy(target,source) do { \
550 if (lua_properties_enabled) { \
551 if (lua_properties_level == 0) { \
552 lua_rawgeti(Luas, LUA_REGISTRYINDEX, luaS_index(node_properties)); \
553 lua_gettable(Luas, LUA_REGISTRYINDEX); \
554 lua_rawgeti(Luas,-1,source); \
555 if (lua_type(Luas,-1)==LUA_TTABLE) { \
556 if (lua_properties_use_metatable) { \
557 lua_newtable(Luas); \
558 lua_insert(Luas,-2); \
559 lua_setfield(Luas,-2,"__index"); \
560 lua_newtable(Luas); \
561 lua_insert(Luas,-2); \
562 lua_setmetatable(Luas,-2); \
564 lua_rawseti(Luas,-2,target); \
565 } else { \
566 lua_pop(Luas,1); \
568 lua_pop(Luas,1); \
569 } else { \
570 lua_rawgeti(Luas,-1,source); \
571 if (lua_type(Luas,-1)==LUA_TTABLE) { \
572 if (lua_properties_use_metatable) { \
573 lua_newtable(Luas); \
574 lua_insert(Luas,-2); \
575 lua_setfield(Luas,-2,"__index"); \
576 lua_newtable(Luas); \
577 lua_insert(Luas,-2); \
578 lua_setmetatable(Luas,-2); \
580 lua_rawseti(Luas,-2,target); \
581 } else { \
582 lua_pop(Luas,1); \
586 } while(0)
588 /* Here end the property handlers. */
590 @ @c
591 int valid_node(halfword p)
593 if (p > my_prealloc && p < var_mem_max) {
594 #ifdef CHECK_NODE_USAGE
595 if (varmem_sizes[p] > 0) {
596 return 1;
598 #else
599 return 1;
600 #endif
602 return 0;
605 @ @c
606 static int test_count = 1;
608 #define dorangetest(a,b,c) do { \
609 if (!(b>=0 && b<c)) { \
610 fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
611 (int)a, (int)b, (int)c, __LINE__,test_count); \
612 confusion("node range test failed"); \
613 } } while (0)
615 #define dotest(a,b,c) do { \
616 if (b!=c) { \
617 fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
618 (int)a, (int)b, (int)c, __LINE__,test_count); \
619 confusion("node test failed"); \
620 } } while (0)
622 #define check_action_ref(a) { dorangetest(p,a,var_mem_max); }
623 #define check_glue_ref(a) { dorangetest(p,a,var_mem_max); }
624 #define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); }
625 #define check_token_ref(a) { confusion("fuzzy token cleanup in node"); }
627 #ifdef CHECK_NODE_USAGE
629 static void check_static_node_mem(void)
631 dotest(zero_glue, width(zero_glue), 0);
632 dotest(zero_glue, type(zero_glue), glue_spec_node);
633 dotest(zero_glue, vlink(zero_glue), null);
634 dotest(zero_glue, stretch(zero_glue), 0);
635 dotest(zero_glue, stretch_order(zero_glue), normal);
636 dotest(zero_glue, shrink(zero_glue), 0);
637 dotest(zero_glue, shrink_order(zero_glue), normal);
639 dotest(sfi_glue, width(sfi_glue), 0);
640 dotest(sfi_glue, type(sfi_glue), glue_spec_node);
641 dotest(sfi_glue, vlink(sfi_glue), null);
642 dotest(sfi_glue, stretch(sfi_glue), 0);
643 dotest(sfi_glue, stretch_order(sfi_glue), sfi);
644 dotest(sfi_glue, shrink(sfi_glue), 0);
645 dotest(sfi_glue, shrink_order(sfi_glue), normal);
647 dotest(fil_glue, width(fil_glue), 0);
648 dotest(fil_glue, type(fil_glue), glue_spec_node);
649 dotest(fil_glue, vlink(fil_glue), null);
650 dotest(fil_glue, stretch(fil_glue), unity);
651 dotest(fil_glue, stretch_order(fil_glue), fil);
652 dotest(fil_glue, shrink(fil_glue), 0);
653 dotest(fil_glue, shrink_order(fil_glue), normal);
655 dotest(fill_glue, width(fill_glue), 0);
656 dotest(fill_glue, type(fill_glue), glue_spec_node);
657 dotest(fill_glue, vlink(fill_glue), null);
658 dotest(fill_glue, stretch(fill_glue), unity);
659 dotest(fill_glue, stretch_order(fill_glue), fill);
660 dotest(fill_glue, shrink(fill_glue), 0);
661 dotest(fill_glue, shrink_order(fill_glue), normal);
663 dotest(ss_glue, width(ss_glue), 0);
664 dotest(ss_glue, type(ss_glue), glue_spec_node);
665 dotest(ss_glue, vlink(ss_glue), null);
666 dotest(ss_glue, stretch(ss_glue), unity);
667 dotest(ss_glue, stretch_order(ss_glue), fil);
668 dotest(ss_glue, shrink(ss_glue), unity);
669 dotest(ss_glue, shrink_order(ss_glue), fil);
671 dotest(fil_neg_glue, width(fil_neg_glue), 0);
672 dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node);
673 dotest(fil_neg_glue, vlink(fil_neg_glue), null);
674 dotest(fil_neg_glue, stretch(fil_neg_glue), -unity);
675 dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil);
676 dotest(fil_neg_glue, shrink(fil_neg_glue), 0);
677 dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal);
680 static void node_mem_dump(halfword p)
682 halfword r;
683 for (r = my_prealloc + 1; r < var_mem_max; r++) {
684 if (vlink(r) == p) {
685 halfword s = r;
686 while (s > my_prealloc && varmem_sizes[s] == 0) {
687 s--;
689 if (s != null
690 && s != my_prealloc
691 && s != var_mem_max
692 && (r - s) < get_node_size(type(s), subtype(s))
693 && alink(s) != p) {
694 if (type(s) == disc_node) {
695 fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
696 get_node_name(type(s), subtype(s)), (int) s,
697 (int) vlink(s), (int) alink(s));
698 fprintf(stdout, "pre_break(%d,%d,%d), ",
699 (int) vlink_pre_break(s), (int) tlink(pre_break(s)),
700 (int) alink(pre_break(s)));
701 fprintf(stdout, "post_break(%d,%d,%d), ",
702 (int) vlink_post_break(s),
703 (int) tlink(post_break(s)),
704 (int) alink(post_break(s)));
705 fprintf(stdout, "no_break(%d,%d,%d)",
706 (int) vlink_no_break(s), (int) tlink(no_break(s)),
707 (int) alink(no_break(s)));
708 fprintf(stdout, "\n");
709 } else {
710 if (vlink(s) == p
711 || (type(s) == glyph_node && lig_ptr (s) == p)
712 || (type(s) == vlist_node && list_ptr(s) == p)
713 || (type(s) == hlist_node && list_ptr(s) == p)
714 || (type(s) == unset_node && list_ptr(s) == p)
715 || (type(s) == ins_node && ins_ptr (s) == p)
717 fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
718 get_node_name(type(s), subtype(s)), (int) s,
719 (int) vlink(s), (int) alink(s));
720 if (type(s) == glyph_node) {
721 fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s));
722 } else if (type(s) == vlist_node || type(s) == hlist_node) {
723 fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s));
725 fprintf(stdout, "\n");
726 } else {
727 if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) {
728 fprintf(stdout, " pointed to from %s node %d\n",
729 get_node_name(type(s), subtype(s)), (int) s);
738 #endif
740 static int free_error(halfword p)
742 if (p > my_prealloc && p < var_mem_max) {
743 #ifdef CHECK_NODE_USAGE
744 int i;
745 if (varmem_sizes[p] == 0) {
746 check_static_node_mem();
747 for (i = (my_prealloc + 1); i < var_mem_max; i++) {
748 if (varmem_sizes[i] > 0) {
749 check_node(i);
752 test_count++;
753 if (type(p) == glyph_node) {
754 formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p);
755 } else {
756 formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
758 node_mem_dump(p);
759 return 1;
761 #endif
762 } else {
763 formatted_error("nodes", "attempt to free an impossible node %d", (int) p);
764 return 1;
766 return 0;
769 @ @c
770 static int copy_error(halfword p)
772 if (p >= 0 && p < var_mem_max) {
773 #ifdef CHECK_NODE_USAGE
774 if (p > my_prealloc && varmem_sizes[p] == 0) {
775 if (type(p) == glyph_node) {
776 formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p);
777 } else {
778 formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
780 return 1;
782 #endif
783 } else {
784 formatted_error("nodes", "attempt to copy an impossible node %d", (int) p);
785 return 1;
787 return 0;
790 @ @c
791 halfword new_node(int i, int j)
793 int s = get_node_size(i, j);
794 halfword n = get_node(s);
796 It should be possible to do this memset at |free_node()|.
798 Both type() and subtype() will be set below, and vlink() is
799 set to null by |get_node()|, so we can do we clearing one
800 word less than |s|
802 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1)));
803 switch (i) {
804 case glyph_node:
805 init_lang_data(n);
806 break;
807 case hlist_node:
808 case vlist_node:
809 box_dir(n) = -1;
810 break;
811 case disc_node:
812 pre_break(n) = pre_break_head(n);
813 type(pre_break(n)) = nesting_node;
814 subtype(pre_break(n)) = pre_break_head(0);
815 post_break(n) = post_break_head(n);
816 type(post_break(n)) = nesting_node;
817 subtype(post_break(n)) = post_break_head(0);
818 no_break(n) = no_break_head(n);
819 type(no_break(n)) = nesting_node;
820 subtype(no_break(n)) = no_break_head(0);
821 break;
822 case rule_node:
823 depth(n) = null_flag;
824 height(n) = null_flag;
825 width(n) = null_flag;
826 rule_dir(n) = -1;
827 rule_index(n) = 0;
828 rule_transform(n) = 0;
829 break;
830 case whatsit_node:
831 if (j == open_node) {
832 open_name(n) = get_nullstr();
833 open_area(n) = open_name(n);
834 open_ext(n) = open_name(n);
836 break;
837 case unset_node:
838 width(n) = null_flag;
839 break;
840 case pseudo_line_node:
841 case shape_node:
842 /* this is a trick that makes |pseudo_files| slightly slower,
843 but the overall allocation faster then an explicit test
844 at the top of |new_node()|.
846 if (j>0) {
847 free_node(n, variable_node_size);
848 n = slow_get_node(j);
849 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1)));
851 break;
852 default:
853 break;
855 if (int_par(synctex_code)) {
856 /* handle synctex extension */
857 switch (i) {
858 case math_node:
859 synctex_tag_math(n) = cur_input.synctex_tag_field;
860 synctex_line_math(n) = line;
861 break;
862 case glue_node:
863 synctex_tag_glue(n) = cur_input.synctex_tag_field;
864 synctex_line_glue(n) = line;
865 break;
866 case kern_node:
867 if (j != 0) {
868 synctex_tag_kern(n) = cur_input.synctex_tag_field;
869 synctex_line_kern(n) = line;
871 break;
872 case hlist_node:
873 case vlist_node:
874 case unset_node:
875 synctex_tag_box(n) = cur_input.synctex_tag_field;
876 synctex_line_box(n) = line;
877 break;
878 case rule_node:
879 synctex_tag_rule(n) = cur_input.synctex_tag_field;
880 synctex_line_rule(n) = line;
881 break;
884 /* take care of attributes */
885 if (nodetype_has_attributes(i)) {
886 build_attribute_list(n);
887 /* lua_properties_set */
889 type(n) = (quarterword) i;
890 subtype(n) = (quarterword) j;
891 return n;
894 halfword raw_glyph_node(void)
896 register halfword n = get_node(glyph_node_size);
897 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
898 type(n) = glyph_node;
899 subtype(n) = 0;
900 return n;
903 halfword new_glyph_node(void)
905 register halfword n = get_node(glyph_node_size);
906 (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
907 type(n) = glyph_node;
908 subtype(n) = 0;
909 build_attribute_list(n);
910 /* lua_properties_set */
911 return n;
914 @ makes a duplicate of the node list that starts at |p| and returns a
915 pointer to the new list
918 halfword do_copy_node_list(halfword p, halfword end)
920 halfword q = null; /* previous position in new list */
921 halfword h = null; /* head of the list */
922 register halfword s ;
923 lua_properties_push; /* saves stack and time */
924 while (p != end) {
925 s = copy_node(p);
926 if (h == null) {
927 h = s;
928 } else {
929 couple_nodes(q, s);
931 q = s;
932 p = vlink(p);
934 lua_properties_pop; /* saves stack and time */
935 return h;
938 halfword copy_node_list(halfword p)
940 return do_copy_node_list(p, null);
943 #define copy_sub_list(target,source) do { \
944 if (source != null) { \
945 s = do_copy_node_list(source, null); \
946 target = s; \
947 } else { \
948 target = null; \
950 } while (0)
952 #define copy_sub_node(target,source) do { \
953 if (source != null) { \
954 s = copy_node(source); \
955 target = s ; \
956 } else { \
957 target = null; \
959 } while (0)
961 @ make a dupe of a single node
964 static void copy_node_wrapup_core(halfword p, halfword r)
966 halfword s ;
967 switch (subtype(p)) {
968 case write_node:
969 case special_node:
970 add_token_ref(write_tokens(p));
971 break;
972 case late_lua_node:
973 copy_late_lua(r, p);
974 break;
975 case user_defined_node:
976 switch (user_node_type(p)) {
977 case 'a':
978 add_node_attr_ref(user_node_value(p));
979 break;
980 case 'l':
981 copy_user_lua(r, p);
982 break;
983 case 'n':
984 s = copy_node_list(user_node_value(p));
985 user_node_value(r) = s;
986 break;
987 case 's':
988 /* |add_string_ref(user_node_value(p));| */
989 break;
990 case 't':
991 add_token_ref(user_node_value(p));
992 break;
994 break;
995 default:
996 break ;
1000 void copy_node_wrapup_dvi(halfword p, halfword r)
1004 void copy_node_wrapup_pdf(halfword p, halfword r)
1006 switch(subtype(p)) {
1007 case pdf_literal_node:
1008 copy_pdf_literal(r, p);
1009 break;
1010 case pdf_colorstack_node:
1011 if (pdf_colorstack_cmd(p) <= colorstack_data)
1012 add_token_ref(pdf_colorstack_data(p));
1013 break;
1014 case pdf_setmatrix_node:
1015 add_token_ref(pdf_setmatrix_data(p));
1016 break;
1017 case pdf_annot_node:
1018 add_token_ref(pdf_annot_data(p));
1019 break;
1020 case pdf_start_link_node:
1021 if (pdf_link_attr(r) != null)
1022 add_token_ref(pdf_link_attr(r));
1023 add_action_ref(pdf_link_action(r));
1024 break;
1025 case pdf_dest_node:
1026 if (pdf_dest_named_id(p) > 0)
1027 add_token_ref(pdf_dest_id(p));
1028 break;
1029 case pdf_thread_node:
1030 case pdf_start_thread_node:
1031 if (pdf_thread_named_id(p) > 0)
1032 add_token_ref(pdf_thread_id(p));
1033 if (pdf_thread_attr(p) != null)
1034 add_token_ref(pdf_thread_attr(p));
1035 break;
1036 default:
1037 break;
1041 halfword copy_node(const halfword p)
1043 halfword r; /* current node being fabricated for new list */
1044 halfword w ; /* whatsit subtype */
1045 register halfword s; /* a helper variable for copying into variable mem */
1046 register int i;
1047 if (copy_error(p)) {
1048 r = new_node(temp_node, 0);
1049 return r;
1051 i = get_node_size(type(p), subtype(p));
1052 r = get_node(i);
1054 (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i));
1056 if (int_par(synctex_code)) {
1057 /* handle synctex extension */
1058 switch (type(p)) {
1059 case math_node:
1060 synctex_tag_math(r) = cur_input.synctex_tag_field;
1061 synctex_line_math(r) = line;
1062 break;
1063 case kern_node:
1064 synctex_tag_kern(r) = cur_input.synctex_tag_field;
1065 synctex_line_kern(r) = line;
1066 break;
1069 if (nodetype_has_attributes(type(p))) {
1070 add_node_attr_ref(node_attr(p));
1071 alink(r) = null;
1072 lua_properties_copy(r,p);
1074 vlink(r) = null;
1076 switch (type(p)) {
1077 case glyph_node:
1078 copy_sub_list(lig_ptr(r),lig_ptr(p)) ;
1079 break;
1080 case glue_node:
1081 add_glue_ref(glue_ptr(p));
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 if (glue_ptr(p) != zero_glue) {
1120 add_glue_ref(glue_ptr(p));
1122 break;
1123 case ins_node:
1124 add_glue_ref(split_top_ptr(p));
1125 copy_sub_list(ins_ptr(r),ins_ptr(p)) ;
1126 break;
1127 case margin_kern_node:
1128 copy_sub_node(margin_char(r),margin_char(p));
1129 break;
1130 case mark_node:
1131 add_token_ref(mark_ptr(p));
1132 break;
1133 case adjust_node:
1134 copy_sub_list(adjust_ptr(r),adjust_ptr(p));
1135 break;
1136 case choice_node:
1137 copy_sub_list(display_mlist(r),display_mlist(p)) ;
1138 copy_sub_list(text_mlist(r),text_mlist(p)) ;
1139 copy_sub_list(script_mlist(r),script_mlist(p)) ;
1140 copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ;
1141 break;
1142 case simple_noad:
1143 copy_sub_list(nucleus(r),nucleus(p)) ;
1144 copy_sub_list(subscr(r),subscr(p)) ;
1145 copy_sub_list(supscr(r),supscr(p)) ;
1146 break;
1147 case radical_noad:
1148 copy_sub_list(nucleus(r),nucleus(p)) ;
1149 copy_sub_list(subscr(r),subscr(p)) ;
1150 copy_sub_list(supscr(r),supscr(p)) ;
1151 copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
1152 copy_sub_list(degree(r),degree(p)) ;
1153 break;
1154 case accent_noad:
1155 copy_sub_list(nucleus(r),nucleus(p)) ;
1156 copy_sub_list(subscr(r),subscr(p)) ;
1157 copy_sub_list(supscr(r),supscr(p)) ;
1158 copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ;
1159 copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ;
1160 copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ;
1161 break;
1162 case fence_noad:
1163 copy_sub_node(delimiter(r),delimiter(p)) ;
1164 break;
1165 case sub_box_node:
1166 case sub_mlist_node:
1167 copy_sub_list(math_list(r),math_list(p)) ;
1168 break;
1169 case fraction_noad:
1170 copy_sub_list(numerator(r),numerator(p)) ;
1171 copy_sub_list(denominator(r),denominator(p)) ;
1172 copy_sub_node(left_delimiter(r),left_delimiter(p)) ;
1173 copy_sub_node(right_delimiter(r),right_delimiter(p)) ;
1174 break;
1175 case glue_spec_node:
1176 glue_ref_count(r) = null;
1177 break;
1178 case dir_node:
1179 case local_par_node:
1180 case boundary_node:
1181 break;
1182 case whatsit_node:
1183 w = subtype(p) ;
1184 if (w >= backend_first_pdf_whatsit) {
1185 copy_node_wrapup_pdf(p,r);
1186 } else if (w >= backend_first_dvi_whatsit) {
1187 copy_node_wrapup_dvi(p,r);
1188 } else {
1189 copy_node_wrapup_core(p,r);
1191 break;
1193 return r;
1196 /* x */
1198 #define free_sub_list(source) if (source != null) flush_node_list(source);
1199 #define free_sub_node(source) if (source != null) flush_node(source);
1201 @ @c
1203 static void flush_node_wrapup_core(halfword p)
1205 switch (subtype(p)) {
1206 case open_node:
1207 case write_node:
1208 case close_node:
1209 case save_pos_node:
1210 break;
1211 case special_node:
1212 delete_token_ref(write_tokens(p));
1213 break;
1214 case late_lua_node:
1215 free_late_lua(p);
1216 break;
1217 case user_defined_node:
1218 switch (user_node_type(p)) {
1219 case 'a':
1220 delete_attribute_ref(user_node_value(p));
1221 break;
1222 case 'd':
1223 break;
1224 case 'n':
1225 flush_node_list(user_node_value(p));
1226 break;
1227 case 's':
1228 /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */
1229 break;
1230 case 't':
1231 delete_token_ref(user_node_value(p));
1232 break;
1233 default:
1235 const char *hlp[] = {
1236 "The type of the value in a user defined whatsit node should be one",
1237 "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
1238 "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
1239 "know how to free the node's value. A memory leak may result.",
1240 NULL
1242 tex_error("Unidentified user defined whatsit", hlp);
1244 break;
1246 break;
1250 void flush_node_wrapup_dvi(halfword p)
1254 void flush_node_wrapup_pdf(halfword p)
1256 switch(subtype(p)) {
1257 case pdf_save_node:
1258 case pdf_restore_node:
1259 case pdf_refobj_node:
1260 case pdf_end_link_node:
1261 case pdf_end_thread_node:
1262 break;
1263 case pdf_literal_node:
1264 free_pdf_literal(p);
1265 break;
1266 case pdf_colorstack_node:
1267 if (pdf_colorstack_cmd(p) <= colorstack_data)
1268 delete_token_ref(pdf_colorstack_data(p));
1269 break;
1270 case pdf_setmatrix_node:
1271 delete_token_ref(pdf_setmatrix_data(p));
1272 break;
1273 case pdf_annot_node:
1274 delete_token_ref(pdf_annot_data(p));
1275 break;
1276 case pdf_link_data_node:
1277 break;
1278 case pdf_start_link_node:
1279 if (pdf_link_attr(p) != null)
1280 delete_token_ref(pdf_link_attr(p));
1281 delete_action_ref(pdf_link_action(p));
1282 break;
1283 case pdf_dest_node:
1284 if (pdf_dest_named_id(p) > 0)
1285 delete_token_ref(pdf_dest_id(p));
1286 break;
1287 case pdf_action_node:
1288 if (pdf_action_type(p) == pdf_action_user) {
1289 delete_token_ref(pdf_action_tokens(p));
1290 } else {
1291 if (pdf_action_file(p) != null)
1292 delete_token_ref(pdf_action_file(p));
1293 if (pdf_action_type(p) == pdf_action_page)
1294 delete_token_ref(pdf_action_tokens(p));
1295 else if (pdf_action_named_id(p) > 0)
1296 delete_token_ref(pdf_action_id(p));
1298 break;
1299 case pdf_thread_data_node:
1300 break;
1301 case pdf_thread_node:
1302 case pdf_start_thread_node:
1303 if (pdf_thread_named_id(p) > 0)
1304 delete_token_ref(pdf_thread_id(p));
1305 if (pdf_thread_attr(p) != null)
1306 delete_token_ref(pdf_thread_attr(p));
1307 break;
1311 void flush_node(halfword p)
1313 halfword w;
1314 if (p == null) /* legal, but no-op */
1315 return;
1316 if (free_error(p))
1317 return;
1318 switch (type(p)) {
1319 case glyph_node:
1320 free_sub_list(lig_ptr(p));
1321 break;
1322 case glue_node:
1323 delete_glue_ref(glue_ptr(p));
1324 free_sub_list(leader_ptr(p));
1325 break;
1326 case hlist_node:
1327 case vlist_node:
1328 case unset_node:
1329 free_sub_list(list_ptr(p));
1330 break;
1331 case disc_node:
1332 /* watch the start at temp node hack */
1333 free_sub_list(vlink(pre_break(p)));
1334 free_sub_list(vlink(post_break(p)));
1335 free_sub_list(vlink(no_break(p)));
1336 break;
1337 case rule_node:
1338 case kern_node:
1339 case penalty_node:
1340 break;
1341 case math_node:
1342 /* begin mathskip code */
1343 if (glue_ptr(p) != zero_glue) {
1344 delete_glue_ref(glue_ptr(p));
1346 /* end mathskip code */
1347 break;
1348 case glue_spec_node:
1349 /* this allows free-ing of lua-allocated glue specs */
1350 if (valid_node(p)) {
1351 if (glue_ref_count(p)!=null) {
1352 decr(glue_ref_count(p));
1353 } else {
1354 free_node(p, get_node_size(type(p), subtype(p)));
1357 return ;
1358 break ;
1359 case dir_node:
1360 case local_par_node:
1361 case boundary_node:
1362 break;
1363 case whatsit_node:
1364 w = subtype(p) ;
1365 if (w >= backend_first_pdf_whatsit) {
1366 flush_node_wrapup_pdf(p);
1367 } else if (w >= backend_first_dvi_whatsit) {
1368 flush_node_wrapup_dvi(p);
1369 } else {
1370 flush_node_wrapup_core(p);
1372 break;
1373 case ins_node:
1374 flush_node_list(ins_ptr(p));
1375 delete_glue_ref(split_top_ptr(p));
1376 break;
1377 case margin_kern_node:
1378 flush_node(margin_char(p));
1379 break;
1380 case mark_node:
1381 delete_token_ref(mark_ptr(p));
1382 break;
1383 case adjust_node:
1384 flush_node_list(adjust_ptr(p));
1385 break;
1386 case style_node: /* nothing to do */
1387 break;
1388 case choice_node:
1389 free_sub_list(display_mlist(p));
1390 free_sub_list(text_mlist(p));
1391 free_sub_list(script_mlist(p));
1392 free_sub_list(script_script_mlist(p));
1393 break;
1394 case simple_noad:
1395 free_sub_list(nucleus(p));
1396 free_sub_list(subscr(p));
1397 free_sub_list(supscr(p));
1398 break;
1399 case radical_noad:
1400 free_sub_list(nucleus(p));
1401 free_sub_list(subscr(p));
1402 free_sub_list(supscr(p));
1403 free_sub_node(left_delimiter(p));
1404 free_sub_list(degree(p));
1405 break;
1406 case accent_noad:
1407 free_sub_list(nucleus(p));
1408 free_sub_list(subscr(p));
1409 free_sub_list(supscr(p));
1410 free_sub_list(top_accent_chr(p));
1411 free_sub_list(bot_accent_chr(p));
1412 free_sub_list(overlay_accent_chr(p));
1413 break;
1414 case fence_noad:
1415 free_sub_list(delimiter(p));
1416 break;
1417 case delim_node: /* nothing to do */
1418 case math_char_node:
1419 case math_text_char_node:
1420 break;
1421 case sub_box_node:
1422 case sub_mlist_node:
1423 free_sub_list(math_list(p));
1424 break;
1425 case fraction_noad:
1426 free_sub_list(numerator(p));
1427 free_sub_list(denominator(p));
1428 free_sub_node(left_delimiter(p));
1429 free_sub_node(right_delimiter(p));
1430 break;
1431 case pseudo_file_node:
1432 free_sub_list(pseudo_lines(p));
1433 break;
1434 case pseudo_line_node:
1435 case shape_node:
1436 free_node(p, subtype(p));
1437 return;
1438 break;
1439 case align_stack_node:
1440 case span_node:
1441 case movement_node:
1442 case if_node:
1443 case nesting_node:
1444 case unhyphenated_node:
1445 case hyphenated_node:
1446 case delta_node:
1447 case passive_node:
1448 case inserting_node:
1449 case split_up_node:
1450 case expr_node:
1451 case attribute_node:
1452 case attribute_list_node:
1453 case temp_node:
1454 break;
1455 default:
1456 formatted_error("nodes","flushing weird node type %d", type(p));
1457 return;
1459 if (nodetype_has_attributes(type(p))) {
1460 delete_attribute_ref(node_attr(p));
1461 lua_properties_reset(p);
1463 free_node(p, get_node_size(type(p), subtype(p)));
1464 return;
1467 @ @c
1468 void flush_node_list(halfword pp)
1469 { /* erase list of nodes starting at |p| */
1470 register halfword p = pp;
1471 if (p == null) /* legal, but no-op */
1472 return;
1473 if (free_error(p))
1474 return;
1475 lua_properties_push; /* saves stack and time */
1476 while (p != null) {
1477 register halfword q = vlink(p);
1478 flush_node(p);
1479 p = q;
1481 lua_properties_pop; /* saves stack and time */
1484 @ @c
1485 static void check_node_wrapup_core(halfword p)
1487 switch (subtype(p)) {
1488 /* frontend code */
1489 case special_node:
1490 check_token_ref(write_tokens(p));
1491 break;
1492 case user_defined_node:
1493 switch (user_node_type(p)) {
1494 case 'a':
1495 check_attribute_ref(user_node_value(p));
1496 break;
1497 case 't':
1498 check_token_ref(user_node_value(p));
1499 break;
1500 case 'n':
1501 dorangetest(p, user_node_value(p), var_mem_max);
1502 break;
1503 case 's':
1504 case 'd':
1505 break;
1506 default:
1507 confusion("unknown user node type");
1508 break;
1510 break;
1511 case open_node:
1512 case write_node:
1513 case close_node:
1514 case save_pos_node:
1515 break;
1519 void check_node_wrapup_dvi(halfword p)
1523 void check_node_wrapup_pdf(halfword p)
1525 switch (subtype(p)) {
1526 case pdf_literal_node:
1527 if (pdf_literal_type(p) == normal)
1528 check_token_ref(pdf_literal_data(p));
1529 break;
1530 case pdf_colorstack_node:
1531 if (pdf_colorstack_cmd(p) <= colorstack_data)
1532 check_token_ref(pdf_colorstack_data(p));
1533 break;
1534 case pdf_setmatrix_node:
1535 check_token_ref(pdf_setmatrix_data(p));
1536 break;
1537 case late_lua_node:
1538 if (late_lua_name(p) > 0)
1539 check_token_ref(late_lua_name(p));
1540 if (late_lua_type(p) == normal)
1541 check_token_ref(late_lua_data(p));
1542 break;
1543 case pdf_annot_node:
1544 check_token_ref(pdf_annot_data(p));
1545 break;
1546 case pdf_start_link_node:
1547 if (pdf_link_attr(p) != null)
1548 check_token_ref(pdf_link_attr(p));
1549 check_action_ref(pdf_link_action(p));
1550 break;
1551 case pdf_dest_node:
1552 if (pdf_dest_named_id(p) > 0)
1553 check_token_ref(pdf_dest_id(p));
1554 break;
1555 case pdf_thread_node:
1556 case pdf_start_thread_node:
1557 if (pdf_thread_named_id(p) > 0)
1558 check_token_ref(pdf_thread_id(p));
1559 if (pdf_thread_attr(p) != null)
1560 check_token_ref(pdf_thread_attr(p));
1561 break;
1562 case pdf_save_node:
1563 case pdf_restore_node:
1564 case pdf_refobj_node:
1565 case pdf_end_link_node:
1566 case pdf_end_thread_node:
1567 break;
1568 default:
1569 confusion("wrapup pdf nodes");
1570 break;
1574 void check_node(halfword p)
1576 halfword w ;
1577 switch (type(p)) {
1578 case glyph_node:
1579 dorangetest(p, lig_ptr(p), var_mem_max);
1580 break;
1581 case glue_node:
1582 check_glue_ref(glue_ptr(p));
1583 dorangetest(p, leader_ptr(p), var_mem_max);
1584 break;
1585 case hlist_node:
1586 case vlist_node:
1587 case unset_node:
1588 case align_record_node:
1589 dorangetest(p, list_ptr(p), var_mem_max);
1590 break;
1591 case ins_node:
1592 dorangetest(p, ins_ptr(p), var_mem_max);
1593 check_glue_ref(split_top_ptr(p));
1594 break;
1595 case whatsit_node:
1596 w = subtype(p) ;
1597 if (w >= backend_first_pdf_whatsit) {
1598 check_node_wrapup_pdf(p);
1599 } else if (w >= backend_first_dvi_whatsit) {
1600 check_node_wrapup_dvi(p);
1601 } else {
1602 check_node_wrapup_core(p);
1604 break;
1605 case margin_kern_node:
1606 check_node(margin_char(p));
1607 break;
1608 case math_node:
1609 /* begin mathskip code */
1610 if (glue_ptr(p) != zero_glue) {
1611 check_glue_ref(glue_ptr(p));
1613 /* end mathskip code */
1614 break;
1615 case disc_node:
1616 dorangetest(p, vlink(pre_break(p)), var_mem_max);
1617 dorangetest(p, vlink(post_break(p)), var_mem_max);
1618 dorangetest(p, vlink(no_break(p)), var_mem_max);
1619 break;
1620 case adjust_node:
1621 dorangetest(p, adjust_ptr(p), var_mem_max);
1622 break;
1623 case pseudo_file_node:
1624 dorangetest(p, pseudo_lines(p), var_mem_max);
1625 break;
1626 case pseudo_line_node:
1627 case shape_node:
1628 break;
1629 case choice_node:
1630 dorangetest(p, display_mlist(p), var_mem_max);
1631 dorangetest(p, text_mlist(p), var_mem_max);
1632 dorangetest(p, script_mlist(p), var_mem_max);
1633 dorangetest(p, script_script_mlist(p), var_mem_max);
1634 break;
1635 case fraction_noad:
1636 dorangetest(p, numerator(p), var_mem_max);
1637 dorangetest(p, denominator(p), var_mem_max);
1638 dorangetest(p, left_delimiter(p), var_mem_max);
1639 dorangetest(p, right_delimiter(p), var_mem_max);
1640 break;
1641 case simple_noad:
1642 dorangetest(p, nucleus(p), var_mem_max);
1643 dorangetest(p, subscr(p), var_mem_max);
1644 dorangetest(p, supscr(p), var_mem_max);
1645 break;
1646 case radical_noad:
1647 dorangetest(p, nucleus(p), var_mem_max);
1648 dorangetest(p, subscr(p), var_mem_max);
1649 dorangetest(p, supscr(p), var_mem_max);
1650 dorangetest(p, degree(p), var_mem_max);
1651 dorangetest(p, left_delimiter(p), var_mem_max);
1652 break;
1653 case accent_noad:
1654 dorangetest(p, nucleus(p), var_mem_max);
1655 dorangetest(p, subscr(p), var_mem_max);
1656 dorangetest(p, supscr(p), var_mem_max);
1657 dorangetest(p, top_accent_chr(p), var_mem_max);
1658 dorangetest(p, bot_accent_chr(p), var_mem_max);
1659 dorangetest(p, overlay_accent_chr(p), var_mem_max);
1660 break;
1661 case fence_noad:
1662 dorangetest(p, delimiter(p), var_mem_max);
1663 break;
1665 case rule_node:
1666 case kern_node:
1667 case penalty_node:
1668 case mark_node:
1669 case style_node:
1670 case attribute_list_node:
1671 case attribute_node:
1672 case glue_spec_node:
1673 case temp_node:
1674 case align_stack_node:
1675 case movement_node:
1676 case if_node:
1677 case nesting_node:
1678 case span_node:
1679 case unhyphenated_node:
1680 case hyphenated_node:
1681 case delta_node:
1682 case passive_node:
1683 case expr_node:
1684 case dir_node:
1685 case boundary_node:
1686 case local_par_node:
1687 break;
1688 default:
1689 fprintf(stdout, "check_node: type is %d\n", type(p));
1694 @ @c
1695 void fix_node_list(halfword head)
1697 halfword p, q;
1698 if (head == null)
1699 return;
1700 p = head;
1701 q = vlink(p);
1702 while (q != null) {
1703 alink(q) = p;
1704 p = q;
1705 q = vlink(p);
1709 @ @c
1710 halfword get_node(int s)
1712 register halfword r;
1714 if (s < MAX_CHAIN_SIZE) {
1715 r = free_chain[s];
1716 if (r != null) {
1717 free_chain[s] = vlink(r);
1718 #ifdef CHECK_NODE_USAGE
1719 varmem_sizes[r] = (char) s;
1720 #endif
1721 vlink(r) = null;
1722 var_used += s; /* maintain usage statistics */
1723 return r;
1725 /* this is the end of the 'inner loop' */
1726 return slow_get_node(s);
1727 } else {
1728 normal_error("nodes","there is a problem in getting a node, case 1");
1729 return null;
1733 @ @c
1734 void free_node(halfword p, int s)
1736 if (p <= my_prealloc) {
1737 formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p));
1738 return;
1740 #ifdef CHECK_NODE_USAGE
1741 varmem_sizes[p] = 0;
1742 #endif
1743 if (s < MAX_CHAIN_SIZE) {
1744 vlink(p) = free_chain[s];
1745 free_chain[s] = p;
1746 } else {
1747 /* todo ? it is perhaps possible to merge this node with an existing rover */
1748 node_size(p) = s;
1749 vlink(p) = rover;
1750 while (vlink(rover) != vlink(p)) {
1751 rover = vlink(rover);
1753 vlink(rover) = p;
1755 /* maintain statistics */
1756 var_used -= s;
1759 @ @c
1760 static void free_node_chain(halfword q, int s)
1762 register halfword p = q;
1763 while (vlink(p) != null) {
1764 #ifdef CHECK_NODE_USAGE
1765 varmem_sizes[p] = 0;
1766 #endif
1767 var_used -= s;
1768 p = vlink(p);
1770 var_used -= s;
1771 #ifdef CHECK_NODE_USAGE
1772 varmem_sizes[p] = 0;
1773 #endif
1774 vlink(p) = free_chain[s];
1775 free_chain[s] = q;
1778 @ @c
1779 void init_node_mem(int t)
1781 my_prealloc = var_mem_stat_max;
1783 /* message ?
1785 assert(whatsit_node_data[user_defined_node].id == user_defined_node);
1786 assert(node_data[passive_node].id == passive_node);
1789 varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t);
1790 if (varmem == NULL) {
1791 overflow("node memory size", (unsigned) var_mem_max);
1793 memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word));
1794 #ifdef CHECK_NODE_USAGE
1795 varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t);
1796 if (varmem_sizes == NULL) {
1797 overflow("node memory size", (unsigned) var_mem_max);
1799 memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t);
1800 #endif
1801 var_mem_max = t;
1802 rover = var_mem_stat_max + 1;
1803 vlink(rover) = rover;
1804 node_size(rover) = (t - rover);
1805 var_used = 0;
1806 /* initialize static glue specs */
1807 glue_ref_count(zero_glue) = null + 1;
1808 width(zero_glue) = 0;
1809 type(zero_glue) = glue_spec_node;
1810 vlink(zero_glue) = null;
1811 stretch(zero_glue) = 0;
1812 stretch_order(zero_glue) = normal;
1813 shrink(zero_glue) = 0;
1814 shrink_order(zero_glue) = normal;
1815 glue_ref_count(sfi_glue) = null + 1;
1816 width(sfi_glue) = 0;
1817 type(sfi_glue) = glue_spec_node;
1818 vlink(sfi_glue) = null;
1819 stretch(sfi_glue) = 0;
1820 stretch_order(sfi_glue) = sfi;
1821 shrink(sfi_glue) = 0;
1822 shrink_order(sfi_glue) = normal;
1823 glue_ref_count(fil_glue) = null + 1;
1824 width(fil_glue) = 0;
1825 type(fil_glue) = glue_spec_node;
1826 vlink(fil_glue) = null;
1827 stretch(fil_glue) = unity;
1828 stretch_order(fil_glue) = fil;
1829 shrink(fil_glue) = 0;
1830 shrink_order(fil_glue) = normal;
1831 glue_ref_count(fill_glue) = null + 1;
1832 width(fill_glue) = 0;
1833 type(fill_glue) = glue_spec_node;
1834 vlink(fill_glue) = null;
1835 stretch(fill_glue) = unity;
1836 stretch_order(fill_glue) = fill;
1837 shrink(fill_glue) = 0;
1838 shrink_order(fill_glue) = normal;
1839 glue_ref_count(ss_glue) = null + 1;
1840 width(ss_glue) = 0;
1841 type(ss_glue) = glue_spec_node;
1842 vlink(ss_glue) = null;
1843 stretch(ss_glue) = unity;
1844 stretch_order(ss_glue) = fil;
1845 shrink(ss_glue) = unity;
1846 shrink_order(ss_glue) = fil;
1847 glue_ref_count(fil_neg_glue) = null + 1;
1848 width(fil_neg_glue) = 0;
1849 type(fil_neg_glue) = glue_spec_node;
1850 vlink(fil_neg_glue) = null;
1851 stretch(fil_neg_glue) = -unity;
1852 stretch_order(fil_neg_glue) = fil;
1853 shrink(fil_neg_glue) = 0;
1854 shrink_order(fil_neg_glue) = normal;
1855 /* initialize node list heads */
1856 vinfo(page_ins_head) = 0;
1857 type(page_ins_head) = temp_node;
1858 vlink(page_ins_head) = null;
1859 alink(page_ins_head) = null;
1860 vinfo(contrib_head) = 0;
1861 type(contrib_head) = temp_node;
1862 vlink(contrib_head) = null;
1863 alink(contrib_head) = null;
1864 vinfo(page_head) = 0;
1865 type(page_head) = temp_node;
1866 vlink(page_head) = null;
1867 alink(page_head) = null;
1868 vinfo(temp_head) = 0;
1869 type(temp_head) = temp_node;
1870 vlink(temp_head) = null;
1871 alink(temp_head) = null;
1872 vinfo(hold_head) = 0;
1873 type(hold_head) = temp_node;
1874 vlink(hold_head) = null;
1875 alink(hold_head) = null;
1876 vinfo(adjust_head) = 0;
1877 type(adjust_head) = temp_node;
1878 vlink(adjust_head) = null;
1879 alink(adjust_head) = null;
1880 vinfo(pre_adjust_head) = 0;
1881 type(pre_adjust_head) = temp_node;
1882 vlink(pre_adjust_head) = null;
1883 alink(pre_adjust_head) = null;
1884 vinfo(active) = 0;
1885 type(active) = unhyphenated_node;
1886 vlink(active) = null;
1887 alink(active) = null;
1888 vinfo(align_head) = 0;
1889 type(align_head) = temp_node;
1890 vlink(align_head) = null;
1891 alink(align_head) = null;
1892 vinfo(end_span) = 0;
1893 type(end_span) = span_node;
1894 vlink(end_span) = null;
1895 alink(end_span) = null;
1896 type(begin_point) = glyph_node;
1897 subtype(begin_point) = 0;
1898 vlink(begin_point) = null;
1899 vinfo(begin_point + 1) = null;
1900 alink(begin_point) = null;
1901 font(begin_point) = 0;
1902 character(begin_point) = '.';
1903 vinfo(begin_point + 3) = 0;
1904 vlink(begin_point + 3) = 0;
1905 vinfo(begin_point + 4) = 0;
1906 vlink(begin_point + 4) = 0;
1907 type(end_point) = glyph_node;
1908 subtype(end_point) = 0;
1909 vlink(end_point) = null;
1910 vinfo(end_point + 1) = null;
1911 alink(end_point) = null;
1912 font(end_point) = 0;
1913 character(end_point) = '.';
1914 vinfo(end_point + 3) = 0;
1915 vlink(end_point + 3) = 0;
1916 vinfo(end_point + 4) = 0;
1917 vlink(end_point + 4) = 0;
1920 @ @c
1921 void dump_node_mem(void)
1923 dump_int(var_mem_max);
1924 dump_int(rover);
1925 dump_things(varmem[0], var_mem_max);
1926 #ifdef CHECK_NODE_USAGE
1927 dump_things(varmem_sizes[0], var_mem_max);
1928 #endif
1929 dump_things(free_chain[0], MAX_CHAIN_SIZE);
1930 dump_int(var_used);
1931 dump_int(my_prealloc);
1934 @ it makes sense to enlarge the varmem array immediately
1937 void undump_node_mem(void)
1939 int x;
1940 undump_int(x);
1941 undump_int(rover);
1942 var_mem_max = (x < 100000 ? 100000 : x);
1943 varmem = xmallocarray(memory_word, (unsigned) var_mem_max);
1944 undump_things(varmem[0], x);
1945 #ifdef CHECK_NODE_USAGE
1946 varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
1947 memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char));
1948 undump_things(varmem_sizes[0], x);
1949 #endif
1950 undump_things(free_chain[0], MAX_CHAIN_SIZE);
1951 undump_int(var_used);
1952 undump_int(my_prealloc);
1953 if (var_mem_max > x) {
1954 /* todo ? it is perhaps possible to merge the new node with an existing rover */
1955 vlink(x) = rover;
1956 node_size(x) = (var_mem_max - x);
1957 while (vlink(rover) != vlink(x)) {
1958 rover = vlink(rover);
1960 vlink(rover) = x;
1964 @ @c
1965 halfword slow_get_node(int s)
1967 register int t;
1969 RETRY:
1970 t = node_size(rover);
1971 if (vlink(rover) < var_mem_max && vlink(rover) != 0) {
1972 if (t > s) {
1973 /* allocating from the bottom helps decrease page faults */
1974 register halfword r = rover;
1975 rover += s;
1976 vlink(rover) = vlink(r);
1977 node_size(rover) = node_size(r) - s;
1978 if (vlink(rover) != r) { /* list is longer than one */
1979 halfword q = r;
1980 while (vlink(q) != r) {
1981 q = vlink(q);
1983 vlink(q) += s;
1984 } else {
1985 vlink(rover) += s;
1987 if (vlink(rover) < var_mem_max) {
1988 #ifdef CHECK_NODE_USAGE
1989 varmem_sizes[r] = (char) (s > 127 ? 127 : s);
1990 #endif
1991 vlink(r) = null;
1992 var_used += s; /* maintain usage statistics */
1993 return r; /* this is the only exit */
1994 } else {
1995 normal_error("nodes","there is a problem in getting a node, case 2");
1996 return null;
1998 } else {
1999 /* attempt to keep the free list small */
2000 int x;
2001 if (vlink(rover) != rover) {
2002 if (t < MAX_CHAIN_SIZE) {
2003 halfword l = vlink(rover);
2004 vlink(rover) = free_chain[t];
2005 free_chain[t] = rover;
2006 rover = l;
2007 while (vlink(l) != free_chain[t]) {
2008 l = vlink(l);
2010 vlink(l) = rover;
2011 goto RETRY;
2012 } else {
2013 halfword l = rover;
2014 while (vlink(rover) != l) {
2015 if (node_size(rover) > s) {
2016 goto RETRY;
2018 rover = vlink(rover);
2022 /* if we are still here, it was apparently impossible to get a match */
2023 x = (var_mem_max >> 2) + s;
2024 varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x));
2025 if (varmem == NULL) {
2026 overflow("node memory size", (unsigned) var_mem_max);
2028 memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word));
2029 #ifdef CHECK_NODE_USAGE
2030 varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x));
2031 if (varmem_sizes == NULL) {
2032 overflow("node memory size", (unsigned) var_mem_max);
2034 memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char));
2035 #endif
2036 /* todo ? it is perhaps possible to merge the new memory with an existing rover */
2037 vlink(var_mem_max) = rover;
2038 node_size(var_mem_max) = x;
2039 while (vlink(rover) != vlink(var_mem_max)) {
2040 rover = vlink(rover);
2042 vlink(rover) = var_mem_max;
2043 rover = var_mem_max;
2044 var_mem_max += x;
2045 goto RETRY;
2047 } else {
2048 normal_error("nodes","there is a problem in getting a node, case 3");
2049 return null;
2053 @ @c
2054 char *sprint_node_mem_usage(void)
2056 char *s;
2057 #ifdef CHECK_NODE_USAGE
2058 char *ss;
2059 int i;
2060 int b = 0;
2061 char msg[256];
2062 int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 };
2063 s = strdup("");
2064 for (i = (var_mem_max - 1); i > my_prealloc; i--) {
2065 if (varmem_sizes[i] > 0) {
2066 if (type(i) > last_normal_node + last_whatsit_node) {
2067 node_counts[last_normal_node + last_whatsit_node + 1]++;
2068 } else if (type(i) == whatsit_node) {
2069 node_counts[(subtype(i) + last_normal_node + 1)]++;
2070 } else {
2071 node_counts[type(i)]++;
2075 for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) {
2076 if (node_counts[i] > 0) {
2077 int j =
2078 (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0);
2079 snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i],
2080 get_node_name((i > last_normal_node ? whatsit_node : i), j));
2081 ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1));
2082 strcpy(ss, s);
2083 strcat(ss, msg);
2084 free(s);
2085 s = ss;
2086 b = 1;
2089 #else
2090 s = strdup("");
2091 #endif
2092 return s;
2095 @ @c
2096 halfword list_node_mem_usage(void)
2098 halfword q = null;
2099 #ifdef CHECK_NODE_USAGE
2100 halfword p = null;
2101 halfword i, j;
2102 char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
2103 memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max);
2104 for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) {
2105 if (saved_varmem_sizes[i] > 0) {
2106 j = copy_node(i);
2107 if (p == null) {
2108 q = j;
2109 } else {
2110 vlink(p) = j;
2112 p = j;
2115 free(saved_varmem_sizes);
2116 #endif
2117 return q;
2120 @ @c
2121 void print_node_mem_stats(void)
2123 int i, b;
2124 halfword j;
2125 char msg[256];
2126 char *s;
2127 int free_chain_counts[MAX_CHAIN_SIZE] = { 0 };
2128 snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc));
2129 tprint_nl(msg);
2130 s = sprint_node_mem_usage();
2131 tprint_nl(" ");
2132 tprint(s);
2133 free(s);
2134 tprint(" nodes");
2135 tprint_nl(" avail lists: ");
2136 b = 0;
2137 for (i = 1; i < MAX_CHAIN_SIZE; i++) {
2138 for (j = free_chain[i]; j != null; j = vlink(j))
2139 free_chain_counts[i]++;
2140 if (free_chain_counts[i] > 0) {
2141 snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]);
2142 tprint(msg);
2143 b = 1;
2146 /* newline, if needed */
2147 print_nlp();
2150 /* this belongs in the web but i couldn't find the correct syntactic place */
2152 halfword new_span_node(halfword n, int s, scaled w)
2154 halfword p = new_node(span_node, 0);
2155 span_link(p) = n;
2156 span_span(p) = s;
2157 width(p) = w;
2158 return p;
2161 @* Attribute stuff.
2164 static halfword new_attribute_node(unsigned int i, int v)
2166 register halfword r = get_node(attribute_node_size);
2167 type(r) = attribute_node;
2168 attribute_id(r) = (halfword) i;
2169 attribute_value(r) = v;
2170 /* not used but nicer in print */
2171 subtype(r) = 0;
2172 return r;
2175 @ @c
2176 halfword copy_attribute_list(halfword n)
2178 halfword q = get_node(attribute_node_size);
2179 register halfword p = q;
2180 type(p) = attribute_list_node;
2181 attr_list_ref(p) = 0;
2182 n = vlink(n);
2183 while (n != null) {
2184 register halfword r = get_node(attribute_node_size);
2185 /* the link will be fixed automatically in the next loop */
2186 (void) memcpy((void *) (varmem + r), (void *) (varmem + n),
2187 (sizeof(memory_word) * attribute_node_size));
2188 vlink(p) = r;
2189 p = r;
2190 n = vlink(n);
2192 return q;
2195 @ @c
2196 void update_attribute_cache(void)
2198 halfword p;
2199 register int i;
2200 attr_list_cache = get_node(attribute_node_size);
2201 type(attr_list_cache) = attribute_list_node;
2202 attr_list_ref(attr_list_cache) = 0;
2203 p = attr_list_cache;
2204 for (i = 0; i <= max_used_attr; i++) {
2205 register int v = attribute(i);
2206 if (v > UNUSED_ATTRIBUTE) {
2207 register halfword r = new_attribute_node((unsigned) i, v);
2208 vlink(p) = r;
2209 p = r;
2212 if (vlink(attr_list_cache) == null) {
2213 free_node(attr_list_cache, attribute_node_size);
2214 attr_list_cache = null;
2216 return;
2219 @ @c
2220 void build_attribute_list(halfword b)
2222 if (max_used_attr >= 0) {
2223 if (attr_list_cache == cache_disabled|| attr_list_cache == null) {
2224 update_attribute_cache();
2225 if (attr_list_cache == null)
2226 return;
2228 attr_list_ref(attr_list_cache)++;
2229 node_attr(b) = attr_list_cache;
2233 @ @c
2234 halfword current_attribute_list(void)
2236 if (max_used_attr >= 0) {
2237 if (attr_list_cache == cache_disabled) {
2238 update_attribute_cache();
2240 return attr_list_cache ;
2242 return null ;
2246 @ @c
2247 void reassign_attribute(halfword n, halfword new)
2249 halfword old;
2250 old = node_attr(n);
2251 if (new == null) {
2252 /* there is nothing to assign but we need to check for an old value */
2253 if (old != null)
2254 delete_attribute_ref(old); /* also nulls attr field of n */
2255 } else if (old == null) {
2256 /* nothing is assigned so we just do that now */
2257 assign_attribute_ref(n,new);
2258 } else if (old != new) {
2259 /* something is assigned so we need to clean up and assign then */
2260 delete_attribute_ref(old);
2261 assign_attribute_ref(n,new);
2263 /* else: same value so there is no need to assign and change the refcount */
2264 node_attr(n) = new ;
2267 @ @c
2268 void delete_attribute_ref(halfword b)
2270 if (b != null) {
2271 if (type(b) == attribute_list_node){
2272 attr_list_ref(b)--;
2273 if (attr_list_ref(b) == 0) {
2274 if (b == attr_list_cache)
2275 attr_list_cache = cache_disabled;
2276 free_node_chain(b, attribute_node_size);
2278 /* maintain sanity */
2279 if (attr_list_ref(b) < 0) {
2280 attr_list_ref(b) = 0;
2282 } else {
2283 normal_error("nodes","trying to delete an attribute reference of a non attribute node");
2288 void reset_node_properties(halfword b)
2290 if (b != null) {
2291 lua_properties_reset(b);
2295 @ |p| is an attr list head, or zero
2297 halfword do_set_attribute(halfword p, int i, int val)
2299 register halfword q;
2300 register int j = 0;
2301 if (p == null) { /* add a new head \& node */
2302 q = get_node(attribute_node_size);
2303 type(q) = attribute_list_node;
2304 attr_list_ref(q) = 1;
2305 p = new_attribute_node((unsigned) i, val);
2306 vlink(q) = p;
2307 return q;
2309 q = p;
2310 if (vlink(p) != null) {
2311 while (vlink(p) != null) {
2312 int t = attribute_id(vlink(p));
2313 if (t == i && attribute_value(vlink(p)) == val)
2314 return q; /* no need to do anything */
2315 if (t >= i)
2316 break;
2317 j++;
2318 p = vlink(p);
2321 p = q;
2322 while (j-- > 0)
2323 p = vlink(p);
2324 if (attribute_id(vlink(p)) == i) {
2325 attribute_value(vlink(p)) = val;
2326 } else { /* add a new node */
2327 halfword r = new_attribute_node((unsigned) i, val);
2328 vlink(r) = vlink(p);
2329 vlink(p) = r;
2331 return q;
2332 } else {
2333 normal_error("nodes","trying to set an attribute fails, case 1");
2334 return null ;
2338 @ @c
2339 void set_attribute(halfword n, int i, int val)
2341 register halfword p;
2342 register int j = 0;
2343 /* not all nodes can have an attribute list */
2344 if (!nodetype_has_attributes(type(n)))
2345 return;
2346 /* if we have no list, we create one and quit */
2347 p = node_attr(n);
2348 if (p == null) { /* add a new head \& node */
2349 p = get_node(attribute_node_size);
2350 type(p) = attribute_list_node;
2351 attr_list_ref(p) = 1;
2352 node_attr(n) = p;
2353 p = new_attribute_node((unsigned) i, val);
2354 vlink(node_attr(n)) = p;
2355 return;
2357 /* we check if we have this attribute already and quit if the value stays the same */
2358 if (vlink(p) != null) {
2359 while (vlink(p) != null) {
2360 int t = attribute_id(vlink(p));
2361 if (t == i && attribute_value(vlink(p)) == val)
2362 return;
2363 if (t >= i)
2364 break;
2365 j++;
2366 p = vlink(p);
2368 /* j has now the position (if found) .. we assume a sorted list ! */
2369 p = node_attr(n);
2371 if (attr_list_ref(p) == 0 ) {
2372 /* the list is invalid i.e. freed already */
2373 formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n);
2374 /* the still dangling list gets ref count 1 */
2375 attr_list_ref(p) = 1;
2376 } else if (attr_list_ref(p) == 1) {
2377 /* this can really happen HH-LS */
2378 if (p == attr_list_cache) {
2379 /* we can invalidate the cache setting */
2380 /* attr_list_cache = cache_disabled */
2381 /* or save the list, as done below */
2382 p = copy_attribute_list(p);
2383 node_attr(n) = p;
2384 /* the copied list gets ref count 1 */
2385 attr_list_ref(p) = 1;
2387 } else {
2388 /* the list is used multiple times so we make a copy */
2389 p = copy_attribute_list(p);
2390 /* we decrement the ref count or the original */
2391 delete_attribute_ref(node_attr(n));
2392 node_attr(n) = p;
2393 /* the copied list gets ref count 1 */
2394 attr_list_ref(p) = 1;
2398 /* we go to position j in the list */
2399 while (j-- > 0)
2400 p = vlink(p);
2401 /* if we have a hit we just set the value otherwise we add a new node */
2402 if (attribute_id(vlink(p)) == i) {
2403 attribute_value(vlink(p)) = val;
2404 } else { /* add a new node */
2405 halfword r = new_attribute_node((unsigned) i, val);
2406 vlink(r) = vlink(p);
2407 vlink(p) = r;
2409 } else {
2410 normal_error("nodes","trying to set an attribute fails, case 2");
2414 @ @c
2415 int unset_attribute(halfword n, int i, int val)
2417 register halfword p;
2418 register int t;
2419 register int j = 0;
2421 if (!nodetype_has_attributes(type(n)))
2422 return null;
2423 p = node_attr(n);
2424 if (p == null)
2425 return UNUSED_ATTRIBUTE;
2426 if (attr_list_ref(p) == 0) {
2427 formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n);
2428 return UNUSED_ATTRIBUTE;
2430 if (vlink(p) != null) {
2431 while (vlink(p) != null) {
2432 t = attribute_id(vlink(p));
2433 if (t > i)
2434 return UNUSED_ATTRIBUTE;
2435 if (t == i) {
2436 p = vlink(p);
2437 break;
2439 j++;
2440 p = vlink(p);
2442 if (attribute_id(p) != i)
2443 return UNUSED_ATTRIBUTE;
2444 /* if we are still here, the attribute exists */
2445 p = node_attr(n);
2446 if (attr_list_ref(p) > 1 || p == attr_list_cache) {
2447 halfword q = copy_attribute_list(p);
2448 if (attr_list_ref(p) > 1) {
2449 delete_attribute_ref(node_attr(n));
2451 attr_list_ref(q) = 1;
2452 node_attr(n) = q;
2454 p = vlink(node_attr(n));
2455 while (j-- > 0)
2456 p = vlink(p);
2457 t = attribute_value(p);
2458 if (val == UNUSED_ATTRIBUTE || t == val) {
2459 attribute_value(p) = UNUSED_ATTRIBUTE;
2461 return t;
2462 } else {
2463 normal_error("nodes","trying to unset an attribute fails");
2464 return null;
2468 @ @c
2469 int has_attribute(halfword n, int i, int val)
2471 register halfword p;
2472 if (!nodetype_has_attributes(type(n)))
2473 return UNUSED_ATTRIBUTE;
2474 p = node_attr(n);
2475 if (p == null || vlink(p) == null)
2476 return UNUSED_ATTRIBUTE;
2477 p = vlink(p);
2478 while (p != null) {
2479 if (attribute_id(p) == i) {
2480 int ret = attribute_value(p);
2481 if (val == UNUSED_ATTRIBUTE || val == ret)
2482 return ret;
2483 return UNUSED_ATTRIBUTE;
2484 } else if (attribute_id(p) > i) {
2485 return UNUSED_ATTRIBUTE;
2487 p = vlink(p);
2489 return UNUSED_ATTRIBUTE;
2492 @ @c
2493 void print_short_node_contents(halfword p)
2495 switch (type(p)) {
2496 case hlist_node:
2497 case vlist_node:
2498 case ins_node:
2499 case whatsit_node:
2500 case mark_node:
2501 case adjust_node:
2502 case unset_node:
2503 print_char('[');
2504 print_char(']');
2505 break;
2506 case rule_node:
2507 print_char('|');
2508 break;
2509 case glue_node:
2510 if (glue_ptr(p) != zero_glue)
2511 print_char(' ');
2512 break;
2513 case math_node:
2514 print_char('$');
2515 break;
2516 case disc_node:
2517 short_display(vlink(pre_break(p)));
2518 short_display(vlink(post_break(p)));
2519 break;
2523 @ @c
2524 static void show_pdftex_whatsit_rule_spec(int p)
2526 tprint("(");
2527 print_rule_dimen(height(p));
2528 print_char('+');
2529 print_rule_dimen(depth(p));
2530 tprint(")x");
2531 print_rule_dimen(width(p));
2534 @ Each new type of node that appears in our data structure must be capable
2535 of being displayed, copied, destroyed, and so on. The routines that we
2536 need for write-oriented whatsits are somewhat like those for mark nodes;
2537 other extensions might, of course, involve more subtlety here.
2540 static void print_write_whatsit(const char *s, pointer p)
2542 tprint_esc(s);
2543 if (write_stream(p) < 16)
2544 print_int(write_stream(p));
2545 else if (write_stream(p) == 16)
2546 print_char('*');
2547 else
2548 print_char('-');
2551 @ @c
2552 static void show_node_wrapup_core(int p)
2554 switch (subtype(p)) {
2555 case open_node:
2556 print_write_whatsit("openout", p);
2557 print_char('=');
2558 print_file_name(open_name(p), open_area(p), open_ext(p));
2559 break;
2560 case write_node:
2561 print_write_whatsit("write", p);
2562 print_mark(write_tokens(p));
2563 break;
2564 case close_node:
2565 print_write_whatsit("closeout", p);
2566 break;
2567 case special_node:
2568 tprint_esc("special");
2569 print_mark(write_tokens(p));
2570 break;
2571 case late_lua_node:
2572 show_late_lua(p);
2573 break;
2574 case save_pos_node:
2575 tprint_esc("savepos");
2576 break;
2577 case user_defined_node:
2578 tprint_esc("whatsit");
2579 print_int(user_node_id(p));
2580 print_char('=');
2581 switch (user_node_type(p)) {
2582 case 'a':
2583 tprint("<>");
2584 break;
2585 case 'n':
2586 tprint("[");
2587 show_node_list(user_node_value(p));
2588 tprint("]");
2589 break;
2590 case 's':
2591 print_char('"');
2592 print(user_node_value(p));
2593 print_char('"');
2594 break;
2595 case 't':
2596 print_mark(user_node_value(p));
2597 break;
2598 default: /* only 'd' */
2599 print_int(user_node_value(p));
2600 break;
2602 break;
2606 void show_node_wrapup_dvi(int p)
2610 void show_node_wrapup_pdf(int p)
2612 switch (subtype(p)) {
2613 case pdf_literal_node:
2614 show_pdf_literal(p);
2615 break;
2616 case pdf_colorstack_node:
2617 tprint_esc("pdfcolorstack ");
2618 print_int(pdf_colorstack_stack(p));
2619 switch (pdf_colorstack_cmd(p)) {
2620 case colorstack_set:
2621 tprint(" set ");
2622 break;
2623 case colorstack_push:
2624 tprint(" push ");
2625 break;
2626 case colorstack_pop:
2627 tprint(" pop");
2628 break;
2629 case colorstack_current:
2630 tprint(" current");
2631 break;
2632 default:
2633 confusion("colorstack");
2634 break;
2636 if (pdf_colorstack_cmd(p) <= colorstack_data)
2637 print_mark(pdf_colorstack_data(p));
2638 break;
2639 case pdf_setmatrix_node:
2640 tprint_esc("pdfsetmatrix");
2641 print_mark(pdf_setmatrix_data(p));
2642 break;
2643 case pdf_save_node:
2644 tprint_esc("pdfsave");
2645 break;
2646 case pdf_restore_node:
2647 tprint_esc("pdfrestore");
2648 break;
2649 case pdf_refobj_node:
2650 tprint_esc("pdfrefobj");
2651 if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) {
2652 if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
2653 tprint(" attr");
2654 lua_rawgeti(Luas, LUA_REGISTRYINDEX,
2655 obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)));
2656 print_char(' ');
2657 tprint((const char *) lua_tostring(Luas, -1));
2658 lua_pop(Luas, 1);
2660 tprint(" stream");
2662 if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p)))
2663 tprint(" file");
2664 if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) {
2665 lua_rawgeti(Luas, LUA_REGISTRYINDEX,
2666 obj_obj_data(static_pdf, pdf_obj_objnum(p)));
2667 print_char(' ');
2668 tprint((const char *) lua_tostring(Luas, -1));
2669 lua_pop(Luas, 1);
2671 break;
2672 case pdf_annot_node:
2673 tprint_esc("pdfannot");
2674 show_pdftex_whatsit_rule_spec(p);
2675 print_mark(pdf_annot_data(p));
2676 break;
2677 case pdf_start_link_node:
2678 tprint_esc("pdfstartlink");
2679 show_pdftex_whatsit_rule_spec(p);
2680 if (pdf_link_attr(p) != null) {
2681 tprint(" attr");
2682 print_mark(pdf_link_attr(p));
2684 tprint(" action");
2685 if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) {
2686 tprint(" user");
2687 print_mark(pdf_action_tokens(pdf_link_action(p)));
2688 return;
2690 if (pdf_action_file(pdf_link_action(p)) != null) {
2691 tprint(" file");
2692 print_mark(pdf_action_file(pdf_link_action(p)));
2694 switch (pdf_action_type(pdf_link_action(p))) {
2695 case pdf_action_goto:
2696 if (pdf_action_named_id(pdf_link_action(p)) > 0) {
2697 tprint(" goto name");
2698 print_mark(pdf_action_id(pdf_link_action(p)));
2699 } else {
2700 tprint(" goto num");
2701 print_int(pdf_action_id(pdf_link_action(p)));
2703 break;
2704 case pdf_action_page:
2705 tprint(" page");
2706 print_int(pdf_action_id(pdf_link_action(p)));
2707 print_mark(pdf_action_tokens(pdf_link_action(p)));
2708 break;
2709 case pdf_action_thread:
2710 if (pdf_action_named_id(pdf_link_action(p)) > 0) {
2711 tprint(" thread name");
2712 print_mark(pdf_action_id(pdf_link_action(p)));
2713 } else {
2714 tprint(" thread num");
2715 print_int(pdf_action_id(pdf_link_action(p)));
2717 break;
2718 default:
2719 normal_error("pdf backend", "unknown action type for link");
2720 break;
2722 break;
2723 case pdf_end_link_node:
2724 tprint_esc("pdfendlink");
2725 break;
2726 case pdf_dest_node:
2727 tprint_esc("pdfdest");
2728 if (pdf_dest_named_id(p) > 0) {
2729 tprint(" name");
2730 print_mark(pdf_dest_id(p));
2731 } else {
2732 tprint(" num");
2733 print_int(pdf_dest_id(p));
2735 print_char(' ');
2736 switch (pdf_dest_type(p)) {
2737 case pdf_dest_xyz:
2738 tprint("xyz");
2739 if (pdf_dest_xyz_zoom(p) != null) {
2740 tprint(" zoom");
2741 print_int(pdf_dest_xyz_zoom(p));
2743 break;
2744 case pdf_dest_fitbh:
2745 tprint("fitbh");
2746 break;
2747 case pdf_dest_fitbv:
2748 tprint("fitbv");
2749 break;
2750 case pdf_dest_fitb:
2751 tprint("fitb");
2752 break;
2753 case pdf_dest_fith:
2754 tprint("fith");
2755 break;
2756 case pdf_dest_fitv:
2757 tprint("fitv");
2758 break;
2759 case pdf_dest_fitr:
2760 tprint("fitr");
2761 show_pdftex_whatsit_rule_spec(p);
2762 break;
2763 case pdf_dest_fit:
2764 tprint("fit");
2765 break;
2766 default:
2767 tprint("unknown!");
2768 break;
2770 break;
2771 case pdf_thread_node:
2772 case pdf_start_thread_node:
2773 if (subtype(p) == pdf_thread_node)
2774 tprint_esc("pdfthread");
2775 else
2776 tprint_esc("pdfstartthread");
2777 tprint("(");
2778 print_rule_dimen(height(p));
2779 print_char('+');
2780 print_rule_dimen(depth(p));
2781 tprint(")x");
2782 print_rule_dimen(width(p));
2783 if (pdf_thread_attr(p) != null) {
2784 tprint(" attr");
2785 print_mark(pdf_thread_attr(p));
2787 if (pdf_thread_named_id(p) > 0) {
2788 tprint(" name");
2789 print_mark(pdf_thread_id(p));
2790 } else {
2791 tprint(" num");
2792 print_int(pdf_thread_id(p));
2794 break;
2795 case pdf_end_thread_node:
2796 tprint_esc("pdfendthread");
2797 break;
2798 default:
2799 break;
2803 @ Now we are ready for |show_node_list| itself. This procedure has been
2804 written to be ``extra robust'' in the sense that it should not crash or get
2805 into a loop even if the data structures have been messed up by bugs in
2806 the rest of the program. You can safely call its parent routine
2807 |show_box(p)| for arbitrary values of |p| when you are debugging \TeX.
2808 However, in the presence of bad data, the procedure may
2809 fetch a |memory_word| whose variant is different from the way it was stored;
2810 for example, it might try to read |mem[p].hh| when |mem[p]|
2811 contains a scaled integer, if |p| is a pointer that has been
2812 clobbered or chosen at random.
2815 @ |str_room| need not be checked; see |show_box|
2817 @ Recursive calls on |show_node_list| therefore use the following pattern:
2819 #define node_list_display(A) do { \
2820 append_char('.'); \
2821 show_node_list(A); \
2822 flush_char(); \
2823 } while (0)
2825 /* prints a node list symbolically */
2827 void show_node_list(int p)
2829 int n = 0; /* the number of items already printed at this level */
2830 halfword w;
2831 real g; /* a glue ratio, as a floating point number */
2832 if ((int) cur_length > depth_threshold) {
2833 if (p > null)
2834 tprint(" []"); /* indicate that there's been some truncation */
2835 return;
2837 while (p != null) {
2838 print_ln();
2839 print_current_string(); /* display the nesting history */
2840 if (int_par(tracing_online_code) < -2)
2841 print_int(p);
2842 incr(n);
2843 if (n > breadth_max) { /* time to stop */
2844 tprint("etc.");
2845 return;
2847 /* Display node |p| */
2848 if (is_char_node(p)) {
2849 print_font_and_char(p);
2850 if (is_ligature(p)) {
2851 /* Display ligature |p|; */
2852 tprint(" (ligature ");
2853 if (is_leftboundary(p))
2854 print_char('|');
2855 font_in_short_display = font(p);
2856 short_display(lig_ptr(p));
2857 if (is_rightboundary(p))
2858 print_char('|');
2859 print_char(')');
2861 } else {
2862 switch (type(p)) {
2863 case hlist_node:
2864 case vlist_node:
2865 case unset_node:
2866 /* Display box |p|; */
2867 if (type(p) == hlist_node)
2868 tprint_esc("h");
2869 else if (type(p) == vlist_node)
2870 tprint_esc("v");
2871 else
2872 tprint_esc("unset");
2873 tprint("box(");
2874 print_scaled(height(p));
2875 print_char('+');
2876 print_scaled(depth(p));
2877 tprint(")x");
2878 print_scaled(width(p));
2879 if (type(p) == unset_node) {
2880 /* Display special fields of the unset node |p|; */
2881 if (span_count(p) != min_quarterword) {
2882 tprint(" (");
2883 print_int(span_count(p) + 1);
2884 tprint(" columns)");
2886 if (glue_stretch(p) != 0) {
2887 tprint(", stretch ");
2888 print_glue(glue_stretch(p), glue_order(p), NULL);
2890 if (glue_shrink(p) != 0) {
2891 tprint(", shrink ");
2892 print_glue(glue_shrink(p), glue_sign(p), NULL);
2894 } else {
2895 /* Display the value of |glue_set(p)| */
2896 /* The code will have to change in this place if |glue_ratio| is
2897 a structured type instead of an ordinary |real|. Note that this routine
2898 should avoid arithmetic errors even if the |glue_set| field holds an
2899 arbitrary random value. The following code assumes that a properly
2900 formed nonzero |real| number has absolute value $2^{20}$ or more when
2901 it is regarded as an integer; this precaution was adequate to prevent
2902 floating point underflow on the author's computer.
2905 g = (real) (glue_set(p));
2906 if ((g != 0.0) && (glue_sign(p) != normal)) {
2907 tprint(", glue set ");
2908 if (glue_sign(p) == shrinking)
2909 tprint("- ");
2910 if (g > 20000.0 || g < -20000.0) {
2911 if (g > 0.0)
2912 print_char('>');
2913 else
2914 tprint("< -");
2915 print_glue(20000 * unity, glue_order(p), NULL);
2916 } else {
2917 print_glue(round(unity * g), glue_order(p), NULL);
2921 if (shift_amount(p) != 0) {
2922 tprint(", shifted ");
2923 print_scaled(shift_amount(p));
2925 tprint(", direction ");
2926 print_dir(box_dir(p));
2928 node_list_display(list_ptr(p)); /* recursive call */
2929 break;
2930 case rule_node:
2931 /* Display rule |p|; */
2932 if (subtype(p) == normal_rule) {
2933 tprint_esc("rule(");
2934 } else if (subtype(p) == empty_rule) {
2935 tprint_esc("norule(");
2936 } else if (subtype(p) == user_rule) {
2937 tprint_esc("userrule(");
2938 } else if (subtype(p) == box_rule) {
2939 tprint_esc("box(");
2940 } else if (subtype(p) == image_rule) {
2941 tprint_esc("image(");
2943 print_rule_dimen(height(p));
2944 print_char('+');
2945 print_rule_dimen(depth(p));
2946 tprint(")x");
2947 print_rule_dimen(width(p));
2948 break;
2949 case ins_node:
2950 /* Display insertion |p|; */
2951 tprint_esc("insert");
2952 print_int(subtype(p));
2953 tprint(", natural size ");
2954 print_scaled(height(p));
2955 tprint("; split(");
2956 print_spec(split_top_ptr(p), NULL);
2957 print_char(',');
2958 print_scaled(depth(p));
2959 tprint("); float cost ");
2960 print_int(float_cost(p));
2961 node_list_display(ins_ptr(p)); /* recursive call */
2962 break;
2963 case dir_node:
2964 if (dir_dir(p) < 0) {
2965 tprint_esc("enddir");
2966 print_char(' ');
2967 print_dir(dir_dir(p) + dir_swap);
2968 } else {
2969 tprint_esc("begindir");
2970 print_char(' ');
2971 print_dir(dir_dir(p));
2973 break;
2974 case local_par_node:
2975 tprint_esc("localpar");
2976 append_char('.');
2977 print_ln();
2978 print_current_string();
2979 tprint_esc("localinterlinepenalty");
2980 print_char('=');
2981 print_int(local_pen_inter(p));
2982 print_ln();
2983 print_current_string();
2984 tprint_esc("localbrokenpenalty");
2985 print_char('=');
2986 print_int(local_pen_broken(p));
2987 print_ln();
2988 print_current_string();
2989 tprint_esc("localleftbox");
2990 if (local_box_left(p) == null) {
2991 tprint("=null");
2992 } else {
2993 append_char('.');
2994 show_node_list(local_box_left(p));
2995 decr(cur_length);
2997 print_ln();
2998 print_current_string();
2999 tprint_esc("localrightbox");
3000 if (local_box_right(p) == null) {
3001 tprint("=null");
3002 } else {
3003 append_char('.');
3004 show_node_list(local_box_right(p));
3005 decr(cur_length);
3007 decr(cur_length);
3008 break;
3009 case boundary_node:
3010 if (subtype(p)==0) {
3011 tprint_esc("noboundary");
3012 } else{
3013 tprint_esc("boundary");
3014 print_char('=');
3015 print_int(subtype(p));
3016 print_char(':');
3017 print_int(boundary_value(p));
3019 break;
3020 case whatsit_node:
3021 w = subtype(p) ;
3022 if (w >= backend_first_pdf_whatsit) {
3023 show_node_wrapup_pdf(p);
3024 } else if (w >= backend_first_dvi_whatsit) {
3025 show_node_wrapup_dvi(p);
3026 } else {
3027 show_node_wrapup_core(p);
3029 break;
3030 case glue_node:
3031 /* Display glue |p|; */
3032 if (subtype(p) >= a_leaders) {
3033 /* Display leaders |p|; */
3034 tprint_esc("");
3035 switch (subtype(p)) {
3036 case a_leaders:
3037 break;
3038 case c_leaders:
3039 print_char('c');
3040 break;
3041 case x_leaders:
3042 print_char('x');
3043 break;
3044 case g_leaders:
3045 print_char('g');
3046 break;
3047 default:
3048 normal_warning("nodes","weird glue leader subtype ignored");
3050 tprint("leaders ");
3051 print_spec(glue_ptr(p), NULL);
3052 node_list_display(leader_ptr(p)); /* recursive call */
3053 } else {
3054 tprint_esc("glue");
3055 if (subtype(p) != normal) {
3056 print_char('(');
3057 if ((subtype(p) - 1) < thin_mu_skip_code) {
3058 print_cmd_chr(assign_glue_cmd,
3059 glue_base + (subtype(p) - 1));
3060 } else if (subtype(p) < cond_math_glue) {
3061 print_cmd_chr(assign_mu_glue_cmd,
3062 glue_base + (subtype(p) - 1));
3063 } else if (subtype(p) == cond_math_glue) {
3064 tprint_esc("nonscript");
3065 } else {
3066 tprint_esc("mskip");
3068 print_char(')');
3070 if (subtype(p) != cond_math_glue) {
3071 print_char(' ');
3072 if (subtype(p) < cond_math_glue)
3073 print_spec(glue_ptr(p), NULL);
3074 else
3075 print_spec(glue_ptr(p), "mu");
3078 break;
3079 case margin_kern_node:
3080 tprint_esc("kern");
3081 print_scaled(width(p));
3082 if (subtype(p) == left_side)
3083 tprint(" (left margin)");
3084 else
3085 tprint(" (right margin)");
3086 break;
3087 case kern_node:
3088 /* Display kern |p|; */
3089 /* An ``explicit'' kern value is indicated implicitly by an explicit space. */
3090 if (subtype(p) != mu_glue) {
3091 tprint_esc("kern");
3092 if (subtype(p) != normal)
3093 print_char(' ');
3094 print_scaled(width(p));
3095 if (subtype(p) == accent_kern)
3096 tprint(" (for accent)");
3097 } else {
3098 tprint_esc("mkern");
3099 print_scaled(width(p));
3100 tprint("mu");
3102 break;
3103 case math_node:
3104 /* Display math node |p|; */
3105 tprint_esc("math");
3106 if (subtype(p) == before)
3107 tprint("on");
3108 else
3109 tprint("off");
3110 if (width(p) != 0) {
3111 tprint(", surrounded ");
3112 print_scaled(width(p));
3114 break;
3115 case penalty_node:
3116 /* Display penalty |p|; */
3117 tprint_esc("penalty ");
3118 print_int(penalty(p));
3119 break;
3120 case disc_node:
3121 /* Display discretionary |p|; */
3122 /* The |post_break| list of a discretionary node is indicated by a prefixed
3123 `\.{\char'174}' instead of the `\..' before the |pre_break| list. */
3124 tprint_esc("discretionary");
3125 print_int(disc_penalty(p));
3126 print_char('|');
3127 if (vlink(no_break(p)) != null) {
3128 tprint(" replacing ");
3129 node_list_display(vlink(no_break(p)));
3131 node_list_display(vlink(pre_break(p))); /* recursive call */
3132 append_char('|');
3133 show_node_list(vlink(post_break(p)));
3134 flush_char(); /* recursive call */
3135 break;
3136 case mark_node:
3137 /* Display mark |p|; */
3138 tprint_esc("mark");
3139 if (mark_class(p) != 0) {
3140 print_char('s');
3141 print_int(mark_class(p));
3143 print_mark(mark_ptr(p));
3144 break;
3145 case adjust_node:
3146 /* Display adjustment |p|; */
3147 tprint_esc("vadjust");
3148 if (subtype(p) != 0)
3149 tprint(" pre ");
3150 node_list_display(adjust_ptr(p)); /* recursive call */
3151 break;
3152 case glue_spec_node:
3153 tprint("<glue_spec ");
3154 print_spec(p, NULL);
3155 tprint(">");
3156 break;
3157 default:
3158 show_math_node(p);
3159 break;
3162 p = vlink(p);
3166 @ This routine finds the 'base' width of a horizontal box, using the same logic
3167 that \TeX82 used for \.{\\predisplaywidth} */
3170 pointer actual_box_width(pointer r, scaled base_width)
3172 pointer q; /* glue specification when calculating |pre_display_size| */
3173 scaled d; /* increment to |v| */
3174 scaled w = -max_dimen; /* calculated |size| */
3175 scaled v = shift_amount(r) + base_width; /* |w| plus possible glue amount */
3176 pointer p = list_ptr(r); /* current node when calculating |pre_display_size| */
3177 while (p != null) {
3178 if (is_char_node(p)) {
3179 d = glyph_width(p);
3180 goto FOUND;
3182 switch (type(p)) {
3183 case hlist_node:
3184 case vlist_node:
3185 case rule_node:
3186 d = width(p);
3187 goto FOUND;
3188 break;
3189 case margin_kern_node:
3190 d = width(p);
3191 break;
3192 case kern_node:
3193 d = width(p);
3194 break;
3195 case math_node:
3196 /* begin mathskip code */
3197 if (glue_ptr(p) == zero_glue) {
3198 d = surround(p);
3199 break;
3200 } else {
3201 /* fall through */
3203 /* end mathskip code */
3204 case glue_node:
3205 /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set|
3206 values, since such values are subject to system-dependent rounding.
3207 System-dependent numbers are not allowed to infiltrate parameters like
3208 |pre_display_size|, since \TeX82 is supposed to make the same decisions on all
3209 machines.
3211 q = glue_ptr(p);
3212 d = width(q);
3213 if (glue_sign(r) == stretching) {
3214 if ((glue_order(r) == stretch_order(q))
3215 && (stretch(q) != 0))
3216 v = max_dimen;
3217 } else if (glue_sign(r) == shrinking) {
3218 if ((glue_order(r) == shrink_order(q))
3219 && (shrink(q) != 0))
3220 v = max_dimen;
3222 if (subtype(p) >= a_leaders)
3223 goto FOUND;
3224 break;
3225 default:
3226 d = 0;
3227 break;
3229 if (v < max_dimen)
3230 v = v + d;
3231 goto NOT_FOUND;
3232 FOUND:
3233 if (v < max_dimen) {
3234 v = v + d;
3235 w = v;
3236 } else {
3237 w = max_dimen;
3238 break;
3240 NOT_FOUND:
3241 p = vlink(p);
3243 return w;
3246 @ @c
3247 halfword tail_of_list(halfword p)
3249 halfword q = p;
3250 while (vlink(q) != null)
3251 q = vlink(q);
3252 return q;
3255 @ |delete_glue_ref| is called when a pointer to a glue
3256 specification is being withdrawn.
3259 void delete_glue_ref(halfword p)
3260 { /* |p| points to a glue specification */
3261 if (type(p) == glue_spec_node) {
3262 if (glue_ref_count(p) == null) {
3263 flush_node(p);
3264 } else {
3265 decr(glue_ref_count(p));
3267 } else {
3268 normal_error("nodes","invalid glue spec node");
3272 @ @c
3273 int var_used;
3274 halfword temp_ptr; /* a pointer variable for occasional emergency use */
3276 @ Attribute lists need two extra globals to increase processing efficiency.
3277 |max_used_attr| limits the test loop that checks for set attributes, and
3278 |attr_list_cache| contains a pointer to an already created attribute list. It is
3279 set to the special value |cache_disabled| when the current value can no longer be
3280 trusted: after an assignment to an attribute register, and after a group has
3281 ended.
3284 int max_used_attr; /* maximum assigned attribute id */
3285 halfword attr_list_cache;
3287 @ From the computer's standpoint, \TeX's chief mission is to create
3288 horizontal and vertical lists. We shall now investigate how the elements
3289 of these lists are represented internally as nodes in the dynamic memory.
3291 A horizontal or vertical list is linked together by |link| fields in
3292 the first word of each node. Individual nodes represent boxes, glue,
3293 penalties, or special things like discretionary hyphens; because of this
3294 variety, some nodes are longer than others, and we must distinguish different
3295 kinds of nodes. We do this by putting a `|type|' field in the first word,
3296 together with the link and an optional `|subtype|'.
3298 @ Character nodes appear only in horizontal lists, never in vertical lists.
3300 An |hlist_node| stands for a box that was made from a horizontal list.
3301 Each |hlist_node| is seven words long, and contains the following fields
3302 (in addition to the mandatory |type| and |link|, which we shall not
3303 mention explicitly when discussing the other node types): The |height| and
3304 |width| and |depth| are scaled integers denoting the dimensions of the
3305 box. There is also a |shift_amount| field, a scaled integer indicating
3306 how much this box should be lowered (if it appears in a horizontal list),
3307 or how much it should be moved to the right (if it appears in a vertical
3308 list). There is a |list_ptr| field, which points to the beginning of the
3309 list from which this box was fabricated; if |list_ptr| is |null|, the box
3310 is empty. Finally, there are three fields that represent the setting of
3311 the glue: |glue_set(p)| is a word of type |glue_ratio| that represents
3312 the proportionality constant for glue setting; |glue_sign(p)| is
3313 |stretching| or |shrinking| or |normal| depending on whether or not the
3314 glue should stretch or shrink or remain rigid; and |glue_order(p)|
3315 specifies the order of infinity to which glue setting applies (|normal|,
3316 |sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used.
3318 @ The |new_null_box| function returns a pointer to an |hlist_node| in
3319 which all subfields have the values corresponding to `\.{\\hbox\{\}}'.
3320 The |subtype| field is set to |min_quarterword|, since that's the desired
3321 |span_count| value if this |hlist_node| is changed to an |unset_node|.
3324 halfword new_null_box(void)
3325 { /* creates a new box node */
3326 halfword p = new_node(hlist_node, min_quarterword);
3327 box_dir(p) = text_direction;
3328 return p;
3331 @ A |vlist_node| is like an |hlist_node| in all respects except that it
3332 contains a vertical list.
3334 @ A |rule_node| stands for a solid black rectangle; it has |width|,
3335 |depth|, and |height| fields just as in an |hlist_node|. However, if
3336 any of these dimensions is $-2^{30}$, the actual value will be determined
3337 by running the rule up to the boundary of the innermost enclosing box.
3338 This is called a ``running dimension.'' The |width| is never running in
3339 an hlist; the |height| and |depth| are never running in a~vlist.
3341 @ A new rule node is delivered by the |new_rule| function. It
3342 makes all the dimensions ``running,'' so you have to change the
3343 ones that are not allowed to run.
3346 halfword new_rule(int s)
3348 halfword p = new_node(rule_node,s);
3349 return p;
3352 @ Insertions are represented by |ins_node| records, where the |subtype|
3353 indicates the corresponding box number. For example, `\.{\\insert 250}'
3354 leads to an |ins_node| whose |subtype| is |250+min_quarterword|.
3355 The |height| field of an |ins_node| is slightly misnamed; it actually holds
3356 the natural height plus depth of the vertical list being inserted.
3357 The |depth| field holds the |split_max_depth| to be used in case this
3358 insertion is split, and the |split_top_ptr| points to the corresponding
3359 |split_top_skip|. The |float_cost| field holds the |floating_penalty| that
3360 will be used if this insertion floats to a subsequent page after a
3361 split insertion of the same class. There is one more field, the
3362 |ins_ptr|, which points to the beginning of the vlist for the insertion.
3364 @ A |mark_node| has a |mark_ptr| field that points to the reference count
3365 of a token list that contains the user's \.{\\mark} text.
3366 In addition there is a |mark_class| field that contains the mark class.
3368 @ An |adjust_node|, which occurs only in horizontal lists,
3369 specifies material that will be moved out into the surrounding
3370 vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}'
3371 operation. The |adjust_ptr| field points to the vlist containing this
3372 material.
3374 @ A |glyph_node|, which occurs only in horizontal lists, specifies a
3375 glyph in a particular font, along with its attribute list. Older
3376 versions of \TeX\ could use token memory for characters, because the
3377 font,char combination would fit in a single word (both values were
3378 required to be strictly less than $2^{16}$). In LuaTeX, room is
3379 needed for characters that are larger than that, as well as a pointer
3380 to a potential attribute list, and the two displacement values.
3382 In turn, that made the node so large that it made sense to merge
3383 ligature glyphs as well, as that requires only one extra pointer. A
3384 few extra classes of glyph nodes will be introduced later. The
3385 unification of all those types makes it easier to manipulate lists of
3386 glyphs. The subtype differentiates various glyph kinds.
3388 First, here is a function that returns a pointer to a glyph node for a given
3389 glyph in a given font. If that glyph doesn't exist, |null| is returned
3390 instead. Nodes of this subtype are directly created only for accents
3391 and their base (through |make_accent|), and math nucleus items (in the
3392 conversion from |mlist| to |hlist|).
3395 halfword new_glyph(int f, int c)
3397 halfword p = null; /* the new node */
3398 if ((f == 0) || (char_exists(f, c))) {
3399 p = new_glyph_node();
3400 set_to_glyph(p);
3401 font(p) = f;
3402 character(p) = c;
3404 return p;
3407 @ A subset of the glyphs nodes represent ligatures: characters
3408 fabricated from the interaction of two or more actual characters. The
3409 characters that generated the ligature have not been forgotten, since
3410 they are needed for diagnostic messages; the |lig_ptr| field points to
3411 a linked list of character nodes for all original characters that have
3412 been deleted. (This list might be empty if the characters that
3413 generated the ligature were retained in other nodes.)
3415 The |subtype| field of these |glyph_node|s is 1, plus 2 and/or 1 if
3416 the original source of the ligature included implicit left and/or
3417 right boundaries. These nodes are created by the C function |new_ligkern|.
3419 A third general type of glyphs could be called a character, as it
3420 only appears in lists that are not yet processed by the ligaturing and
3421 kerning steps of the program.
3423 |main_control| inserts these, and they are later converted to
3424 |subtype_normal| by |new_ligkern|.
3427 quarterword norm_min(int h)
3429 if (h <= 0)
3430 return 1;
3431 else if (h >= 255)
3432 return 255;
3433 else
3434 return (quarterword) h;
3437 halfword new_char(int f, int c)
3439 halfword p; /* the new node */
3440 p = new_glyph_node();
3441 set_to_character(p);
3442 font(p) = f;
3443 character(p) = c;
3444 lang_data(p) = make_lang_data(uc_hyph, cur_lang, left_hyphen_min, right_hyphen_min);
3445 return p;
3448 @ Left and right ghost glyph nodes are the result of \.{\\leftghost}
3449 and \.{\\rightghost}, respectively. They are going to be removed by
3450 |new_ligkern|, at the end of which they are no longer needed.
3452 @ Here are a few handy helpers used by the list output routines.
3455 scaled glyph_width(halfword p)
3457 scaled w = char_width(font(p), character(p));
3458 return w;
3461 scaled glyph_height(halfword p)
3463 scaled w = char_height(font(p), character(p)) + y_displace(p);
3464 if (w < 0)
3465 w = 0;
3466 return w;
3469 scaled glyph_depth(halfword p)
3471 scaled w = char_depth(font(p), character(p));
3472 if (y_displace(p) > 0)
3473 w = w - y_displace(p);
3474 if (w < 0)
3475 w = 0;
3476 return w;
3479 @ A |disc_node|, which occurs only in horizontal lists, specifies a
3480 ``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text
3481 that starts at |pre_break(p)| will precede the break, the text that starts at
3482 |post_break(p)| will follow the break, and text that appears in
3483 |no_break(p)| nodes will be ignored. For example, an ordinary
3484 discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with
3485 |pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|,
3486 and |no_break=null|.
3488 {TODO: Knuth said: All three of the discretionary texts must be lists
3489 that consist entirely of character, kern, box and rule nodes.}
3491 If |subtype(p)=automatic_disc|, the |ex_hyphen_penalty| will be charged for this
3492 break. Otherwise the |hyphen_penalty| will be charged. The texts will
3493 actually be substituted into the list by the line-breaking algorithm if it
3494 decides to make the break, and the discretionary node will disappear at
3495 that time; thus, the output routine sees only discretionaries that were
3496 not chosen.
3499 halfword new_disc(void)
3500 { /* creates an empty |disc_node| */
3501 halfword p = new_node(disc_node, 0);
3502 disc_penalty(p) = int_par(hyphen_penalty_code);
3503 return p;
3506 @ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
3507 |subtype| field in its first word says what `\\{whatsit}' it is, and
3508 implicitly determines the node size (which must be 2 or more) and the
3509 format of the remaining words. When a |whatsit_node| is encountered
3510 in a list, special actions are invoked; knowledgeable people who are
3511 careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
3512 things by adding code at the end of the program. For example, there
3513 might be a `\TeX nicolor' extension to specify different colors of ink,
3514 @^extensions to \TeX@>
3515 and the whatsit node might contain the desired parameters.
3517 The present implementation of \TeX\ treats the features associated with
3518 `\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
3519 illustrate how such routines might be coded. We shall defer further
3520 discussion of extensions until the end of this program.
3522 @ A |math_node|, which occurs only in horizontal lists, appears before and
3523 after mathematical formulas. The |subtype| field is |before| before the
3524 formula and |after| after it. There is a |surround| field, which represents
3525 the amount of surrounding space inserted by \.{\\mathsurround}.
3528 halfword new_math(scaled w, int s)
3530 halfword p = new_node(math_node, s);
3531 surround(p) = w;
3532 return p;
3535 @ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|,
3536 |rule_node|, |ins_node|, |mark_node|, |adjust_node|,
3537 |disc_node|, |whatsit_node|, and |math_node| are at the low end of the
3538 type codes, by permitting a break at glue in a list if and only if the
3539 |type| of the previous node is less than |math_node|. Furthermore, a
3540 node is discarded after a break if its type is |math_node| or~more.
3542 @ A |glue_node| represents glue in a list. However, it is really only
3543 a pointer to a separate glue specification, since \TeX\ makes use of the
3544 fact that many essentially identical nodes of glue are usually present.
3545 If |p| points to a |glue_node|, |glue_ptr(p)| points to
3546 another packet of words that specify the stretch and shrink components, etc.
3548 Glue nodes also serve to represent leaders; the |subtype| is used to
3549 distinguish between ordinary glue (which is called |normal|) and the three
3550 kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|).
3551 The |leader_ptr| field points to a rule node or to a box node containing the
3552 leaders; it is set to |null| in ordinary glue nodes.
3554 Many kinds of glue are computed from \TeX's ``skip'' parameters, and
3555 it is helpful to know which parameter has led to a particular glue node.
3556 Therefore the |subtype| is set to indicate the source of glue, whenever
3557 it originated as a parameter. We will be defining symbolic names for the
3558 parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|,
3559 etc.); it suffices for now to say that the |subtype| of parametric glue
3560 will be the same as the parameter number, plus~one.
3562 @ In math formulas there are two more possibilities for the |subtype| in a
3563 glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu}
3564 instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}'
3565 feature that cancels the glue node immediately following if it appears
3566 in a subscript.
3568 @ A glue specification has a halfword reference count in its first word,
3569 @^reference counts@>
3570 representing |null| plus the number of glue nodes that point to it (less one).
3571 Note that the reference count appears in the same position as
3572 the |link| field in list nodes; this is the field that is initialized
3573 to |null| when a node is allocated, and it is also the field that is flagged
3574 by |empty_flag| in empty nodes.
3576 Glue specifications also contain three |scaled| fields, for the |width|,
3577 |stretch|, and |shrink| dimensions. Finally, there are two one-byte
3578 fields called |stretch_order| and |shrink_order|; these contain the
3579 orders of infinity (|normal|, |sfi|, |fil|, |fill|, or |filll|)
3580 corresponding to the stretch and shrink values.
3582 @ Here is a function that returns a pointer to a copy of a glue spec.
3583 The reference count in the copy is |null|, because there is assumed
3584 to be exactly one reference to the new specification.
3587 halfword new_spec(halfword p)
3588 { /* duplicates a glue specification */
3589 halfword q = copy_node(p);
3590 glue_ref_count(q) = null;
3591 return q;
3594 @ And here's a function that creates a glue node for a given parameter
3595 identified by its code number; for example,
3596 |new_param_glue(line_skip_code)| returns a pointer to a glue node for the
3597 current \.{\\lineskip}.
3600 halfword new_param_glue(int n)
3602 halfword p = new_node(glue_node, n + 1);
3603 halfword q = glue_par(n);
3604 glue_ptr(p) = q;
3605 incr(glue_ref_count(q));
3606 return p;
3609 @ Glue nodes that are more or less anonymous are created by |new_glue|,
3610 whose argument points to a glue specification.
3613 halfword new_glue(halfword q)
3615 halfword p = new_node(glue_node, normal);
3616 glue_ptr(p) = q;
3617 incr(glue_ref_count(q));
3618 return p;
3621 @ Still another subroutine is needed: This one is sort of a combination
3622 of |new_param_glue| and |new_glue|. It creates a glue node for one of
3623 the current glue parameters, but it makes a fresh copy of the glue
3624 specification, since that specification will probably be subject to change,
3625 while the parameter will stay put. The global variable |temp_ptr| is
3626 set to the address of the new spec.
3629 halfword new_skip_param(int n)
3631 halfword p; /* the new node */
3632 temp_ptr = new_spec(glue_par(n));
3633 p = new_glue(temp_ptr);
3634 glue_ref_count(temp_ptr) = null;
3635 subtype(p) = (quarterword) (n + 1);
3636 return p;
3639 @ A |kern_node| has a |width| field to specify a (normally negative)
3640 amount of spacing. This spacing correction appears in horizontal lists
3641 between letters like A and V when the font designer said that it looks
3642 better to move them closer together or further apart. A kern node can
3643 also appear in a vertical list, when its `|width|' denotes additional
3644 spacing in the vertical direction. The |subtype| is either |normal| (for
3645 kerns inserted from font information or math mode calculations) or |explicit|
3646 (for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern|
3647 (for kerns inserted from non-math accents) or |mu_glue| (for kerns
3648 inserted from \.{\\mkern} specifications in math formulas).
3650 @ The |new_kern| function creates a kern node having a given width.
3653 halfword new_kern(scaled w)
3655 halfword p = new_node(kern_node, normal);
3656 width(p) = w;
3657 return p;
3660 @ A |penalty_node| specifies the penalty associated with line or page
3661 breaking, in its |penalty| field. This field is a fullword integer, but
3662 the full range of integer values is not used: Any penalty |>=10000| is
3663 treated as infinity, and no break will be allowed for such high values.
3664 Similarly, any penalty |<=-10000| is treated as negative infinity, and a
3665 break will be forced.
3667 @ Anyone who has been reading the last few sections of the program will
3668 be able to guess what comes next.
3671 halfword new_penalty(int m)
3673 halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */
3674 penalty(p) = m;
3675 return p;
3678 @ You might think that we have introduced enough node types by now. Well,
3679 almost, but there is one more: An |unset_node| has nearly the same format
3680 as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign}
3681 or \.{\\valign} that are not yet in their final form, since the box
3682 dimensions are their ``natural'' sizes before any glue adjustment has been
3683 made. The |glue_set| word is not present; instead, we have a |glue_stretch|
3684 field, which contains the total stretch of order |glue_order| that is
3685 present in the hlist or vlist being boxed.
3686 Similarly, the |shift_amount| field is replaced by a |glue_shrink| field,
3687 containing the total shrink of order |glue_sign| that is present.
3688 The |subtype| field is called |span_count|; an unset box typically
3689 contains the data for |qo(span_count)+1| columns.
3690 Unset nodes will be changed to box nodes when alignment is completed.
3692 In fact, there are still more types coming. When we get to math formula
3693 processing we will see that a |style_node| has |type=14|; and a number
3694 of larger type codes will also be defined, for use in math mode only.
3696 Warning: If any changes are made to these data structure layouts, such as
3697 changing any of the node sizes or even reordering the words of nodes,
3698 the |copy_node_list| procedure and the memory initialization code
3699 below may have to be changed. Such potentially dangerous parts of the
3700 program are listed in the index under `data structure assumptions'.
3701 @!@^data structure assumptions@>
3702 However, other references to the nodes are made symbolically in terms of
3703 the \.{WEB} macro definitions above, so that format changes will leave
3704 \TeX's other algorithms intact.
3705 @^system dependencies@>
3707 @ This function creates a |local_paragraph| node
3711 halfword make_local_par_node(void)
3713 halfword q;
3714 halfword p = new_node(local_par_node,0);
3715 local_pen_inter(p) = local_inter_line_penalty;
3716 local_pen_broken(p) = local_broken_penalty;
3717 if (local_left_box != null) {
3718 q = copy_node_list(local_left_box);
3719 local_box_left(p) = q;
3720 local_box_left_width(p) = width(local_left_box);
3722 if (local_right_box != null) {
3723 q = copy_node_list(local_right_box);
3724 local_box_right(p) = q;
3725 local_box_right_width(p) = width(local_right_box);
3727 local_par_dir(p) = par_direction;
3728 return p;