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