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