w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

texnodes.c
Go to the documentation of this file.
1 /*
2 
3 Copyright 2006-2010 Taco Hoekwater <taco@luatex.org>
4 
5 This file is part of LuaTeX.
6 
7 LuaTeX is free software; you can redistribute it and/or modify it under the terms
8 of the GNU General Public License as published by the Free Software Foundation;
9 either version 2 of the License, or (at your option) any later version.
10 
11 LuaTeX is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License along with
16 LuaTeX; if not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 
20 #include "ptexlib.h"
21 #include "lua/luatex-api.h"
22 
23 /*tex
24 
25  This module started out using NDEBUG to trigger checking invalid node usage,
26  something that is needed because users can mess up nodes in Lua. At some
27  point that code was always enabled so it is now always on but still can be
28  recognized as additional code. And as the performance hit is close to zero so
29  disabling makes no sense, not even to make it configureable. There is a
30  little more memory used but that is neglectable compared to other memory
31  usage.
32 
33 */
34 
35 #define MAX_CHAIN_SIZE 13 /* why not a bit larger */
36 #define CHECK_NODE_USAGE 1 /* this triggers checking */
37 
38 memory_word *volatile varmem = NULL;
39 
40 #ifdef CHECK_NODE_USAGE
41  char *varmem_sizes = NULL;
42 #endif
43 
46 
48 
49 static int my_prealloc = 0;
50 
51 /*tex Used in font and lang: */
52 
54 
55 /*tex Defined below. */
56 
58 
59 #define fake_node 100
60 #define fake_node_size 2
61 #define fake_node_name "fake"
62 
63 #define variable_node_size 2
64 
65 /*tex
66 
67  The following definitions are used for keys at the \LUA\ end and
68  provide an efficient way to share hashed strings.
69 
70 */
71 
72 # define init_node_key(target,n,key) \
73  target[n].lua = luaS_##key##_index; \
74  target[n].name = luaS_##key##_ptr;
75 
76 # define init_field_key(target,n,key) \
77  target[n].lua = luaS_##key##_index; \
78  target[n].name = luaS_##key##_ptr;
79 
80 # define init_field_nop(target,n) \
81  target[n].lua = 0; \
82  target[n].name = NULL;
83 
84 /*tex The fields of nodes. */
85 
119 
120 /*tex The fields of whatsit nodes. */
121 
129 
145 
146 
147 /*tex The values of fields. */
148 
150  { 0, NULL, 0 },
151  { 1, NULL, 0 },
152  { 2, NULL, 0 },
153  { 3, NULL, 0 },
154  { -1, NULL, 0 },
155 };
156 
158  { 0, NULL, 0 },
159  { 1, NULL, 0 },
160  { 2, NULL, 0 },
161  { 3, NULL, 0 },
162  { 4, NULL, 0 },
163  { 5, NULL, 0 },
164  { 6, NULL, 0 },
165  { 7, NULL, 0 },
166  { -1, NULL, 0 },
167 };
168 
170  { 0, NULL, 0 },
171  { 1, NULL, 0 },
172  { 2, NULL, 0 },
173  { 3, NULL, 0 },
174  { -1, NULL, 0 },
175 };
176 
178  { 0, NULL, 0 },
179  { 1, NULL, 0 },
180  { 2, NULL, 0 },
181  { -1, NULL, 0 },
182 };
183 
185  { 0, NULL, 0 },
186  { 1, NULL, 0 },
187  { 2, NULL, 0 },
188  { 3, NULL, 0 },
189  { -1, NULL, 0 },
190 };
191 
193  { normal, NULL, 0 },
194  { sfi, NULL, 0 },
195  { fil, NULL, 0 },
196  { fill, NULL, 0 },
197  { filll, NULL, 0 },
198  { -1, NULL, 0 },
199 };
200 
202  { set_origin, NULL, 0 },
203  { direct_page, NULL, 0 },
204  { direct_always, NULL, 0 },
205  { direct_raw, NULL, 0 },
206  { direct_text, NULL, 0 },
207  { direct_font, NULL, 0 },
208  { scan_special, NULL, 0 },
209  { -1, NULL, 0 },
210 };
211 
213  { 0, NULL, 0 },
214  { 1, NULL, 0 },
215  { 2, NULL, 0 },
216  { -1, NULL, 0 },
217 };
218 
219 /*tex The subtypes of nodes (most have one). */
220 
222  { normal_dir, NULL, 0 },
223  { cancel_dir, NULL, 0 },
224  { -1, NULL, 0 },
225 };
226 
228  { user_skip_glue, NULL, 0 },
229  { line_skip_glue, NULL, 0 },
230  { baseline_skip_glue, NULL, 0 },
231  { par_skip_glue, NULL, 0 },
236  { left_skip_glue, NULL, 0 },
237  { right_skip_glue, NULL, 0 },
238  { top_skip_glue, NULL, 0 },
239  { split_top_skip_glue, NULL, 0 },
240  { tab_skip_glue, NULL, 0 },
241  { space_skip_glue, NULL, 0 },
242  { xspace_skip_glue, NULL, 0 },
243  { par_fill_skip_glue, NULL, 0 },
244  { math_skip_glue, NULL, 0 },
245  { thin_mu_skip_glue, NULL, 0 },
246  { med_mu_skip_glue, NULL, 0 },
247  { thick_mu_skip_glue, NULL, 0 },
248  /* math */
249  { cond_math_glue, NULL, 0 },
250  { mu_glue, NULL, 0 },
251  /* leaders */
252  { a_leaders, NULL, 0 },
253  { c_leaders, NULL, 0 },
254  { x_leaders, NULL, 0 },
255  { g_leaders, NULL, 0 },
256  { -1, NULL, 0 },
257 };
258 
259 /*tex
260 
261  Math glue and leaders have special numbers. At some point we might decide to
262  move them down so best don't use hard coded numbers!
263 
264 */
265 
267  { cond_math_glue, NULL, 0 },
268  { mu_glue, NULL, 0 },
269  { -1, NULL, 0 },
270 };
271 
273  { a_leaders, NULL, 0 },
274  { c_leaders, NULL, 0 },
275  { x_leaders, NULL, 0 },
276  { g_leaders, NULL, 0 },
277  { -1, NULL, 0 },
278 };
279 
281  { cancel_boundary, NULL, 0 },
282  { user_boundary, NULL, 0 },
283  { protrusion_boundary, NULL, 0 },
284  { word_boundary, NULL, 0 },
285  { -1, NULL, 0 },
286 };
287 
289  { user_penalty, NULL, 0 },
290  { linebreak_penalty, NULL, 0 },
291  { line_penalty, NULL, 0 },
292  { word_penalty, NULL, 0 },
293  { final_penalty, NULL, 0 },
294  { noad_penalty, NULL, 0 },
296  { after_display_penalty, NULL, 0 },
298  { -1, NULL, 0 },
299 };
300 
302  { font_kern, NULL, 0 },
303  { explicit_kern, NULL, 0 },
304  { accent_kern, NULL, 0 },
305  { italic_kern, NULL, 0 },
306  { -1, NULL, 0 },
307 };
308 
310  { normal_rule, NULL, 0 },
311  { box_rule, NULL, 0 },
312  { image_rule, NULL, 0 },
313  { empty_rule, NULL, 0 },
314  { user_rule, NULL, 0 },
315  { math_over_rule, NULL, 0 },
316  { math_under_rule, NULL, 0 },
317  { math_fraction_rule, NULL, 0 },
318  { math_radical_rule, NULL, 0 },
319  { outline_rule, NULL, 0 },
320  { -1, NULL, 0 },
321 };
322 
324  { glyph_unset, NULL, 0 },
325  { glyph_character, NULL, 0 },
326  { glyph_ligature, NULL, 0 },
327  { glyph_ghost, NULL, 0 },
328  { glyph_left, NULL, 0 },
329  { glyph_right, NULL, 0 },
330  { -1, NULL, 0 },
331 };
332 
334  { discretionary_disc, NULL, 0 },
335  { explicit_disc, NULL, 0 },
336  { automatic_disc, NULL, 0 },
337  { syllable_disc, NULL, 0 },
338  { init_disc, NULL, 0 },
339  { select_disc, NULL, 0 },
340  { -1, NULL, 0 },
341 };
342 
344  { left_side, NULL, 0 },
345  { right_side, NULL, 0 },
346  { -1, NULL, 0 },
347 };
348 
350  { unknown_list, NULL, 0 },
351  { line_list, NULL, 0 },
352  { hbox_list, NULL, 0 },
353  { indent_list, NULL, 0 },
354  { align_row_list, NULL, 0 },
355  { align_cell_list, NULL, 0 },
356  { equation_list, NULL, 0 },
357  { equation_number_list, NULL, 0 },
358  { math_list_list, NULL, 0 },
359  { math_char_list, NULL, 0 },
362  { math_h_delimiter_list, NULL, 0 },
363  { math_v_delimiter_list, NULL, 0 },
366  { math_numerator_list, NULL, 0 },
367  { math_denominator_list, NULL, 0 },
368  { math_limits_list, NULL, 0 },
369  { math_fraction_list, NULL, 0 },
370  { math_nucleus_list, NULL, 0 },
371  { math_sup_list, NULL, 0 },
372  { math_sub_list, NULL, 0 },
373  { math_degree_list, NULL, 0 },
374  { math_scripts_list, NULL, 0 },
375  { math_over_list, NULL, 0 },
376  { math_under_list, NULL, 0 },
377  { math_accent_list, NULL, 0 },
378  { math_radical_list, NULL, 0 },
379  { -1, NULL, 0 },
380 };
381 
383  { 0, NULL, 0 },
384  { 1, NULL, 0 },
385  { -1, NULL, 0 },
386 };
387 
389  { before, NULL, 0 },
390  { after, NULL, 0 },
391  { -1, NULL, 0 },
392 };
393 
395  { ord_noad_type, NULL, 0 },
396  { op_noad_type_normal, NULL, 0 },
397  { op_noad_type_limits, NULL, 0 },
399  { bin_noad_type, NULL, 0 },
400  { rel_noad_type, NULL, 0 },
401  { open_noad_type, NULL, 0 },
402  { close_noad_type, NULL, 0 },
403  { punct_noad_type, NULL, 0 },
404  { inner_noad_type, NULL, 0 },
405  { under_noad_type, NULL, 0 },
406  { over_noad_type, NULL, 0 },
407  { vcenter_noad_type, NULL, 0 },
408  { -1, NULL, 0 },
409 };
410 
412  { radical_noad_type, NULL, 0 },
413  { uradical_noad_type, NULL, 0 },
414  { uroot_noad_type, NULL, 0 },
419  { -1, NULL, 0 },
420 };
421 
423  { bothflexible_accent, NULL, 0 },
424  { fixedtop_accent, NULL, 0 },
425  { fixedbottom_accent, NULL, 0 },
426  { fixedboth_accent, NULL, 0 },
427  { -1, NULL, 0 },
428 };
429 
431  { unset_noad_side, NULL, 0 },
432  { left_noad_side, NULL, 0 },
433  { middle_noad_side, NULL, 0 },
434  { right_noad_side, NULL, 0 },
435  { no_noad_side, NULL, 0 },
436  { -1, NULL, 0 },
437 };
438 
439 /*tes This brings all together */
440 
450  { whatsit_node, -1, NULL, NULL, NULL, 9, 0 },
477  { expr_node, expr_node_size, NULL, NULL, NULL, -1, 0 },
479  { span_node, span_node_size, NULL, NULL, NULL, -1, 0 },
483  { temp_node, temp_node_size, NULL, NULL, NULL, -1, 0 },
486  { if_node, if_node_size, NULL, NULL, NULL, -1, 0 },
489  { delta_node, delta_node_size, NULL, NULL, NULL, -1, 0 },
491  { shape_node, variable_node_size, NULL, NULL, NULL, -1, 0 },
492  { -1, -1, NULL, NULL, NULL, -1, 0 }
493 };
494 
495 void l_set_node_data(void) {
529  init_node_key(node_data, inserting_node, page_insert)
530  init_node_key(node_data, split_up_node, split_insert)
531  init_node_key(node_data, expr_node, expr_stack)
532  init_node_key(node_data, nesting_node, nested_list)
539  init_node_key(node_data, movement_node, movement_stack)
546 
549 
570  init_node_key(node_subtypes_glue, thick_mu_skip_glue + 1, conditionalmathskip)
576 
577  init_node_key(node_subtypes_mathglue, 0, conditionalmathskip)
579 
581  init_node_key(node_subtypes_leader, 1, cleaders)
582  init_node_key(node_subtypes_leader, 2, xleaders)
583  init_node_key(node_subtypes_leader, 3, gleaders)
584 
589 
599 
603  init_node_key(node_subtypes_kern, italic_kern, italiccorrection)
604 
615 
618  init_node_key(node_subtypes_glyph, 2, ligature)
622 
629 
635 
665 
668 
671 
674 
688 
696 
701 
707 
712 
716 
722  init_field_key(node_fields_accent, 5, bot_accent);
723  init_field_key(node_fields_accent, 6, top_accent);
724  init_field_key(node_fields_accent, 7, overlay_accent);
725  init_field_key(node_fields_accent, 8, fraction);
727 
731 
735 
737 
741 
743  init_field_key(node_fields_choice, 1, display);
746  init_field_key(node_fields_choice, 4, scriptscript);
748 
755 
760 
767 
776 
787 
789  init_field_key(node_fields_glue, 1, leader);
796 
803 
810  init_field_key(node_fields_glyph, 6, uchyph);
811  init_field_key(node_fields_glyph, 7, components);
817  init_field_key(node_fields_glyph, 13, expansion_factor);
820 
828 
838 
841  init_field_key(node_fields_kern, 2, expansion_factor);
843 
855 
857  init_field_key(node_fields_local_par, 1, pen_inter);
858  init_field_key(node_fields_local_par, 2, pen_broken);
861  init_field_key(node_fields_local_par, 5, box_left_width);
862  init_field_key(node_fields_local_par, 6, box_right);
863  init_field_key(node_fields_local_par, 7, box_right_width);
865 
870 
872  init_field_key(node_fields_mark, 1, class);
875 
884 
889 
894 
900 
904 
914 
924 
931 
935 
939 
943 
956 
957 }
958 
960 
961  /*tex These are always there. The fake nodes are historical. */
962 
967  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
968  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
972  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
973  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
974  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
975  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
976  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
977  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
978  { fake_node, fake_node_size, NULL, NULL, NULL, -1, 0 },
979 
980  /*tex Here starts the \DVI\ backend section, todo: a separate list. */
981 
982  /*tex {\em There is nothing here.} */
983 
984  /*tex Here starts the \PDF\ backend section, todo: a separate list. */
985 
1003 
1004  /*tex That's it. */
1005 
1006  { -1, -1, NULL, NULL, NULL, -1, 0 },
1007 
1008 };
1009 
1018 
1022 
1029 
1036 
1039 
1043 
1049 
1054 
1072 
1081 
1089 
1094 
1098 
1103 
1111 
1119 
1125 
1136 
1139 
1143 
1146 
1151 
1155 
1158 
1161 
1165 
1174 
1183 
1192 
1193 }
1194 
1195 #define last_whatsit_node pdf_restore_node
1196 
1197 /*tex
1198 
1199  When we copy a node list, there are several possibilities: we do the same as
1200  a new node, we copy the entry to the table in properties (a reference), we do
1201  a deep copy of a table in the properties, we create a new table and give it
1202  the original one as a metatable. After some experiments (that also included
1203  timing) with these scenarios I decided that a deep copy made no sense, nor
1204  did nilling. In the end both the shallow copy and the metatable variant were
1205  both ok, although the second ons is slower. The most important aspect to keep
1206  in mind is that references to other nodes in properties no longer can be
1207  valid for that copy. We could use two tables (one unique and one shared) or
1208  metatables but that only complicates matters.
1209 
1210  When defining a new node, we could already allocate a table but it is rather
1211  easy to do that at the lua end e.g. using a metatable __index method. That
1212  way it is under macro package control.
1213 
1214  When deleting a node, we could keep the slot (e.g. setting it to false) but
1215  it could make memory consumption raise unneeded when we have temporary large
1216  node lists and after that only small lists.
1217 
1218  So, in the end this is what we ended up with. For the record, I also
1219  experimented with the following:
1220 
1221  \startitemize
1222 
1223  \startitem
1224  Copy attributes to the properties so that we have fast access at the
1225  lua end: in the end the overhead is not compensated by speed and
1226  convenience, in fact, attributes are not that slow when it comes to
1227  accessing them.
1228  \stopitem
1229 
1230  \startitem
1231  A bitset in the node but again the gain compared to attributes is
1232  neglectable and it also demands a pretty string agreement over what
1233  bit represents what, and this is unlikely to succeed in the tex
1234  community (I could use it for font handling, which is cross package,
1235  but decided that it doesn't pay off.
1236  \stopitem
1237 
1238  \stopitemize
1239 
1240  In case one wonders why properties make sense then, well, it is not so much
1241  speed that we gain, but more convenience: storing all kind of (temporary)
1242  data in attributes is no fun and this mechanism makes sure that properties
1243  are cleaned up when a node is freed. Also, the advantage of a more or less
1244  global properties table is that we stay at the lua end. An alternative is to
1245  store a reference in the node itself but that is complicated by the fact that
1246  the register has some limitations (no numeric keys) and we also don't want to
1247  mess with it too much.
1248 
1249 */
1250 
1254 
1255 /*tex
1256 
1257  We keep track of nesting so that we don't oveflow the stack, and, what is
1258  more important, don't keep resolving the registry index.
1259 
1260 */
1261 
1262 #define lua_properties_push do { \
1263  if (lua_properties_enabled) { \
1264  lua_properties_level = lua_properties_level + 1 ; \
1265  if (lua_properties_level == 1) { \
1266  lua_get_metatablelua_l(Luas,node_properties); \
1267  } \
1268  } \
1269 } while(0)
1270 
1271 #define lua_properties_pop do { \
1272  if (lua_properties_enabled) { \
1273  if (lua_properties_level == 1) \
1274  lua_pop(Luas,1); \
1275  lua_properties_level = lua_properties_level - 1 ; \
1276  } \
1277 } while(0)
1278 
1279 /*tex No setting is needed: */
1280 
1281 #define lua_properties_set(target) do { \
1282 } while(0)
1283 
1284 /*tex Resetting boils down to nilling. */
1285 
1286 #define lua_properties_reset(target) do { \
1287  if (lua_properties_enabled) { \
1288  if (lua_properties_level == 0) { \
1289  lua_get_metatablelua_l(Luas,node_properties); \
1290  lua_pushnil(Luas); \
1291  lua_rawseti(Luas,-2,target); \
1292  lua_pop(Luas,1); \
1293  } else { \
1294  lua_pushnil(Luas); \
1295  lua_rawseti(Luas,-2,target); \
1296  } \
1297  } \
1298 } while(0)
1299 
1300 /*tex
1301 
1302  For a moment I considered supporting all kind of data types but in practice
1303  that makes no sense. So we stick to a cheap shallow copy with as option a
1304  metatable. BTW, a deep copy would look like this:
1305 
1306  \starttyping
1307  static void copy_lua_table(lua_State* L, int index) {
1308  lua_newtable(L);
1309  lua_pushnil(L);
1310  while(lua_next(L, index-1) != 0) {
1311  lua_pushvalue(L, -2);
1312  lua_insert(L, -2);
1313  if (lua_type(L,-1)==LUA_TTABLE)
1314  copy_lua_table(L,-1);
1315  lua_settable(L, -4);
1316  }
1317  lua_pop(L,1);
1318  }
1319 
1320  #define lua_properties_copy(target, source) do { \
1321  if (lua_properties_enabled) { \
1322  lua_pushinteger(Luas,source); \
1323  lua_rawget(Luas,-2); \
1324  if (lua_type(Luas,-1)==LUA_TTABLE) { \
1325  copy_lua_table(Luas,-1); \
1326  lua_pushinteger(Luas,target); \
1327  lua_insert(Luas,-2); \
1328  lua_rawset(Luas,-3); \
1329  } else { \
1330  lua_pop(Luas,1); \
1331  } \
1332  } \
1333  } while(0)
1334  \stoptyping
1335 
1336 */
1337 
1338 /*tex Isn't there a faster way to metatable? */
1339 
1340 /*tex
1341 
1342  \starttyping
1343  #define lua_properties_copy(target,source) do { \
1344  if (lua_properties_enabled) { \
1345  if (lua_properties_level == 0) { \
1346  lua_get_metatablelua_l(Luas,node_properties); \
1347  lua_rawgeti(Luas,-1,source); \
1348  if (lua_type(Luas,-1)==LUA_TTABLE) { \
1349  if (lua_properties_use_metatable) { \
1350  lua_newtable(Luas); \
1351  lua_insert(Luas,-2); \
1352  lua_setfield(Luas,-2,"__index"); \
1353  lua_newtable(Luas); \
1354  lua_insert(Luas,-2); \
1355  lua_setmetatable(Luas,-2); \
1356  } \
1357  lua_rawseti(Luas,-2,target); \
1358  } else { \
1359  lua_pop(Luas,1); \
1360  } \
1361  lua_pop(Luas,1); \
1362  } else { \
1363  lua_rawgeti(Luas,-1,source); \
1364  if (lua_type(Luas,-1)==LUA_TTABLE) { \
1365  if (lua_properties_use_metatable) { \
1366  lua_newtable(Luas); \
1367  lua_insert(Luas,-2); \
1368  lua_setfield(Luas,-2,"__index"); \
1369  lua_newtable(Luas); \
1370  lua_insert(Luas,-2); \
1371  lua_setmetatable(Luas,-2); \
1372  } \
1373  lua_rawseti(Luas,-2,target); \
1374  } else { \
1375  lua_pop(Luas,1); \
1376  } \
1377  } \
1378  } \
1379  } while(0)
1380  \stoptyping
1381 
1382 */
1383 
1384 /*tex
1385 
1386  A simple testrun on many pages of dumb text shows 1% gain (of course it
1387  depends on how properties are used but some other tests confirm it).
1388 
1389 */
1390 
1391 #define lua_properties_copy(target,source) do { \
1392  if (lua_properties_enabled) { \
1393  if (lua_properties_level == 0) { \
1394  lua_get_metatablelua_l(Luas,node_properties); \
1395  lua_rawgeti(Luas,-1,source); \
1396  if (lua_type(Luas,-1)==LUA_TTABLE) { \
1397  if (lua_properties_use_metatable) { \
1398  lua_newtable(Luas); \
1399  lua_insert(Luas,-2); \
1400  lua_push_string_by_name(Luas,__index); \
1401  lua_insert(Luas,-2); \
1402  lua_rawset(Luas, -3); \
1403  lua_newtable(Luas); \
1404  lua_insert(Luas,-2); \
1405  lua_setmetatable(Luas,-2); \
1406  } \
1407  lua_rawseti(Luas,-2,target); \
1408  } else { \
1409  lua_pop(Luas,1); \
1410  } \
1411  lua_pop(Luas,1); \
1412  } else { \
1413  lua_rawgeti(Luas,-1,source); \
1414  if (lua_type(Luas,-1)==LUA_TTABLE) { \
1415  if (lua_properties_use_metatable) { \
1416  lua_newtable(Luas); \
1417  lua_insert(Luas,-2); \
1418  lua_push_string_by_name(Luas,__index); \
1419  lua_insert(Luas,-2); \
1420  lua_rawset(Luas, -3); \
1421  lua_newtable(Luas); \
1422  lua_insert(Luas,-2); \
1423  lua_setmetatable(Luas,-2); \
1424  } \
1425  lua_rawseti(Luas,-2,target); \
1426  } else { \
1427  lua_pop(Luas,1); \
1428  } \
1429  } \
1430  } \
1431 } while(0)
1432 
1433 /*tex Here end the property handlers. */
1434 
1436 {
1437  if (p > my_prealloc && p < var_mem_max) {
1438 #ifdef CHECK_NODE_USAGE
1439  if (varmem_sizes[p] > 0) {
1440  return 1;
1441  }
1442 #else
1443  return 1;
1444 #endif
1445  }
1446  return 0;
1447 }
1448 
1449 static int test_count = 1;
1450 
1451 #define dorangetest(a,b,c) do { \
1452  if (!(b>=0 && b<c)) { \
1453  fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
1454  (int)a, (int)b, (int)c, __LINE__,test_count); \
1455  confusion("node range test failed"); \
1456  } } while (0)
1457 
1458 #define dotest(a,b,c) do { \
1459  if (b!=c) { \
1460  fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
1461  (int)a, (int)b, (int)c, __LINE__,test_count); \
1462  confusion("node test failed"); \
1463  } } while (0)
1464 
1465 #define check_action_ref(a) { dorangetest(p,a,var_mem_max); }
1466 #define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); }
1467 
1468 /*tex hm, we can just pass |p| then. */
1469 
1470 #define check_token_ref(p) { \
1471  if (type(p) == whatsit_node) { \
1472  formatted_error("nodes","fuzzy token cleanup in whatsit node with type %s and subtype %i",node_data[type(p)].name,subtype(p)); \
1473  } else { \
1474  formatted_error("nodes","fuzzy token cleanup in node with type %s",node_data[type(p)].name); \
1475  } \
1476 }
1477 
1478 #ifdef CHECK_NODE_USAGE
1479 
1480 static void check_static_node_mem(void)
1481 {
1484  dotest(zero_glue, vlink(zero_glue), null);
1489 
1490  dotest(sfi_glue, width(sfi_glue), 0);
1492  dotest(sfi_glue, vlink(sfi_glue), null);
1497 
1498  dotest(fil_glue, width(fil_glue), 0);
1500  dotest(fil_glue, vlink(fil_glue), null);
1505 
1508  dotest(fill_glue, vlink(fill_glue), null);
1513 
1514  dotest(ss_glue, width(ss_glue), 0);
1516  dotest(ss_glue, vlink(ss_glue), null);
1521 
1529 }
1530 
1532 {
1533  halfword r;
1534  for (r = my_prealloc + 1; r < var_mem_max; r++) {
1535  if (vlink(r) == p) {
1536  halfword s = r;
1537  while (s > my_prealloc && varmem_sizes[s] == 0) {
1538  s--;
1539  }
1540  if (s != null
1541  && s != my_prealloc
1542  && s != var_mem_max
1543  && (r - s) < get_node_size(type(s), subtype(s))
1544  && alink(s) != p) {
1545  if (type(s) == disc_node) {
1546  fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
1547  get_node_name(type(s), subtype(s)), (int) s,
1548  (int) vlink(s), (int) alink(s));
1549  fprintf(stdout, "pre_break(%d,%d,%d), ",
1550  (int) vlink_pre_break(s), (int) tlink(pre_break(s)),
1551  (int) alink(pre_break(s)));
1552  fprintf(stdout, "post_break(%d,%d,%d), ",
1553  (int) vlink_post_break(s),
1554  (int) tlink(post_break(s)),
1555  (int) alink(post_break(s)));
1556  fprintf(stdout, "no_break(%d,%d,%d)",
1557  (int) vlink_no_break(s), (int) tlink(no_break(s)),
1558  (int) alink(no_break(s)));
1559  fprintf(stdout, "\n");
1560  } else {
1561  if (vlink(s) == p
1562  || (type(s) == glyph_node && lig_ptr (s) == p)
1563  || (type(s) == vlist_node && list_ptr(s) == p)
1564  || (type(s) == hlist_node && list_ptr(s) == p)
1565  || (type(s) == unset_node && list_ptr(s) == p)
1566  || (type(s) == ins_node && ins_ptr (s) == p)
1567  ) {
1568  fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ",
1569  get_node_name(type(s), subtype(s)), (int) s,
1570  (int) vlink(s), (int) alink(s));
1571  if (type(s) == glyph_node) {
1572  fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s));
1573  } else if (type(s) == vlist_node || type(s) == hlist_node) {
1574  fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s));
1575  }
1576  fprintf(stdout, "\n");
1577  } else {
1578  if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) {
1579  fprintf(stdout, " pointed to from %s node %d\n",
1580  get_node_name(type(s), subtype(s)), (int) s);
1581  }
1582  }
1583  }
1584  }
1585  }
1586  }
1587 }
1588 
1589 #endif
1590 
1591 static int free_error(halfword p)
1592 {
1593  if (p > my_prealloc && p < var_mem_max) {
1594 #ifdef CHECK_NODE_USAGE
1595  int i;
1596  if (varmem_sizes[p] == 0) {
1598  for (i = (my_prealloc + 1); i < var_mem_max; i++) {
1599  if (varmem_sizes[i] > 0) {
1600  check_node(i);
1601  }
1602  }
1603  test_count++;
1604  if (type(p) == glyph_node) {
1605  formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p);
1606  } else {
1607  formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
1608  }
1609  node_mem_dump(p);
1610  return 1;
1611  }
1612 #endif
1613  } else {
1614  formatted_error("nodes", "attempt to free an impossible node %d", (int) p);
1615  return 1;
1616  }
1617  return 0;
1618 }
1619 
1620 static int copy_error(halfword p)
1621 {
1622  if (p >= 0 && p < var_mem_max) {
1623 #ifdef CHECK_NODE_USAGE
1624  if (p > my_prealloc && varmem_sizes[p] == 0) {
1625  if (type(p) == glyph_node) {
1626  formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p);
1627  } else {
1628  formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p);
1629  }
1630  return 1;
1631  }
1632 #endif
1633  } else {
1634  formatted_error("nodes", "attempt to copy an impossible node %d", (int) p);
1635  return 1;
1636  }
1637  return 0;
1638 }
1639 
1640 /*tex
1641 
1642  Because of the 5-10\% overhead that \SYNTEX\ creates some options have been
1643  implemented controlled by |synctex_anyway_mode|.
1644 
1645  \startabulate
1646  \NC \type {1} \NC all but glyphs \NC \NR
1647  \NC \type {2} \NC also glyphs \NC \NR
1648  \NC \type {3} \NC glyphs and glue \NC \NR
1649  \NC \type {4} \NC only glyphs \NC \NR
1650  \stoptabulate
1651 
1652 */
1653 
1657 
1659 {
1661 };
1662 
1664 {
1665  return synctex_anyway_mode;
1666 };
1667 
1669 {
1670  synctex_no_files = f;
1671 };
1672 
1674 {
1675  return (int) synctex_no_files ;
1676 };
1677 
1679 {
1680  cur_input.synctex_tag_field = t;
1681 };
1682 
1684 {
1685  return (int) cur_input.synctex_tag_field;
1686 };
1687 
1689 {
1690  return (int) synctex_line_field;
1691 };
1692 
1693 static int forced_tag = 0;
1694 static int forced_line = 0;
1695 
1697 {
1698  forced_tag = t;
1699 };
1700 
1702 {
1703  forced_line = t;
1704 };
1705 
1707 {
1709 };
1710 
1711 /*tex |if_stack| is called a lot so maybe optimize that one. */
1712 
1714 {
1715  int s = get_node_size(i, j);
1716  halfword n = get_node(s);
1717  /*tex
1718 
1719  It should be possible to do this memset at |free_node()|. Both type() and
1720  subtype() will be set below, and vlink() is set to null by |get_node()|,
1721  so we can do we clearing one word less than |s|.
1722 
1723  */
1724  (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1)));
1725  switch (i) {
1726  case glyph_node:
1727  init_lang_data(n);
1728  break;
1729  case hlist_node:
1730  case vlist_node:
1731  box_dir(n) = -1;
1732  break;
1733  case disc_node:
1740  no_break(n) = no_break_head(n);
1743  break;
1744  case rule_node:
1745  depth(n) = null_flag;
1746  height(n) = null_flag;
1747  width(n) = null_flag;
1748  rule_dir(n) = -1;
1749  rule_index(n) = 0;
1750  rule_transform(n) = 0;
1751  break;
1752  case whatsit_node:
1753  if (j == open_node) {
1754  open_name(n) = get_nullstr();
1755  open_area(n) = open_name(n);
1756  open_ext(n) = open_name(n);
1757  }
1758  break;
1759  case unset_node:
1760  width(n) = null_flag;
1761  break;
1762  case pseudo_line_node:
1763  case shape_node:
1764  /*tex
1765 
1766  This is a trick that makes |pseudo_files| slightly slower, but
1767  the overall allocation faster then an explicit test at the top of
1768  |new_node()|.
1769 
1770  */
1771  if (j>0) {
1773  n = slow_get_node(j);
1774  (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1)));
1775  }
1776  break;
1777  case fraction_noad:
1778  fraction_fam(n) = -1;
1779  break;
1780  case simple_noad:
1781  noad_fam(n) = -1;
1782  break;
1783  default:
1784  break;
1785  }
1786  if (synctex_anyway_mode) {
1787  /*tex See table above. */
1788  switch (i) {
1789  case glyph_node:
1790  if (synctex_anyway_mode > 1) {
1791  synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
1793  }
1794  break;
1795  case glue_node:
1796  if (synctex_anyway_mode < 4) {
1797  synctex_tag_glue(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
1799  }
1800  break;
1801  case kern_node:
1802  if (synctex_anyway_mode < 3) {
1803  synctex_tag_kern(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
1805  }
1806  break;
1807  case hlist_node:
1808  case vlist_node:
1809  case unset_node:
1810  /*tex Rather useless: */
1811  if (synctex_anyway_mode < 3) {
1812  synctex_tag_box(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
1814  }
1815  break;
1816  case rule_node:
1817  if (synctex_anyway_mode < 3) {
1818  synctex_tag_rule(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
1820  }
1821  break;
1822  case math_node:
1823  /*tex Noads probably make more sense but let's not change that. */
1824  if (synctex_anyway_mode < 3) {
1825  synctex_tag_math(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
1827  }
1828  break;
1829  }
1830  } else if (synctex_par) {
1831  /*tex Handle the \SYNTEX\ extension. */
1832  switch (i) {
1833  case glue_node:
1834  synctex_tag_glue(n) = cur_input.synctex_tag_field;
1836  break;
1837  case kern_node:
1838  if (j != 0) {
1839  synctex_tag_kern(n) = cur_input.synctex_tag_field;
1841  }
1842  break;
1843  case hlist_node:
1844  case vlist_node:
1845  case unset_node:
1846  synctex_tag_box(n) = cur_input.synctex_tag_field;
1847  synctex_line_box(n) = line;
1848  break;
1849  case rule_node:
1850  synctex_tag_rule(n) = cur_input.synctex_tag_field;
1852  break;
1853  case math_node:
1854  synctex_tag_math(n) = cur_input.synctex_tag_field;
1856  break;
1857  }
1858  }
1859  /*tex Take care of attributes. */
1860  if (nodetype_has_attributes(i)) {
1862  /*tex No need for |lua_properties_set|. */
1863  }
1864  type(n) = (quarterword) i;
1865  subtype(n) = (quarterword) j;
1866  return n;
1867 }
1868 
1870 {
1871  register halfword n = get_node(glyph_node_size);
1872  (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
1873  if (synctex_anyway_mode > 1) {
1874  synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
1876  }
1877  type(n) = glyph_node;
1878  subtype(n) = 0;
1879  return n;
1880 }
1881 
1883 {
1884  register halfword n = get_node(glyph_node_size);
1885  (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1)));
1886  if (synctex_anyway_mode > 1) {
1887  synctex_tag_glyph(n) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
1889  }
1890  type(n) = glyph_node;
1891  subtype(n) = 0;
1893  /*tex No need for |lua_properties_set|. */
1894  return n;
1895 }
1896 
1897 /*tex
1898 
1899  This makes a duplicate of the node list that starts at |p| and returns a
1900  pointer to the new list.
1901 
1902 */
1903 
1905 {
1906  /*tex previous position in new list */
1907  halfword q = null;
1908  /*tex head of the list */
1909  halfword h = null;
1910  register halfword s ;
1911  /*tex saves stack and time */
1913  while (p != end) {
1914  s = copy_node(p);
1915  if (h == null) {
1916  h = s;
1917  } else {
1918  couple_nodes(q, s);
1919  }
1920  q = s;
1921  p = vlink(p);
1922  }
1923  /*tex saves stack and time */
1925  return h;
1926 }
1927 
1929 {
1930  return do_copy_node_list(p, null);
1931 }
1932 
1933 #define copy_sub_list(target,source) do { \
1934  if (source != null) { \
1935  s = do_copy_node_list(source, null); \
1936  target = s; \
1937  } else { \
1938  target = null; \
1939  } \
1940  } while (0)
1941 
1942 #define copy_sub_node(target,source) do { \
1943  if (source != null) { \
1944  s = copy_node(source); \
1945  target = s ; \
1946  } else { \
1947  target = null; \
1948  } \
1949 } while (0)
1950 
1951 /*tex Make a dupe of a single node. */
1952 
1954 {
1955  halfword s ;
1956  switch (subtype(p)) {
1957  case write_node:
1958  case special_node:
1960  break;
1961  case late_lua_node:
1962  copy_late_lua(r, p);
1963  break;
1964  case user_defined_node:
1965  switch (user_node_type(p)) {
1966  case 'a':
1968  break;
1969  case 'l':
1970  copy_user_lua(r, p);
1971  break;
1972  case 'n':
1974  user_node_value(r) = s;
1975  break;
1976  case 's':
1977  /* |add_string_ref(user_node_value(p));| */
1978  break;
1979  case 't':
1981  break;
1982  }
1983  break;
1984  default:
1985  break ;
1986  }
1987 }
1988 
1990 {
1991 }
1992 
1994 {
1995  switch(subtype(p)) {
1996  case pdf_literal_node:
1997  copy_pdf_literal(r, p);
1998  break;
1999  case pdf_colorstack_node:
2002  break;
2003  case pdf_setmatrix_node:
2005  break;
2006  case pdf_annot_node:
2008  break;
2009  case pdf_start_link_node:
2010  if (pdf_link_attr(r) != null)
2013  break;
2014  case pdf_dest_node:
2015  if (pdf_dest_named_id(p) > 0)
2017  break;
2018  case pdf_thread_node:
2019  case pdf_start_thread_node:
2020  if (pdf_thread_named_id(p) > 0)
2022  if (pdf_thread_attr(p) != null)
2024  break;
2025  default:
2026  break;
2027  }
2028 }
2029 
2031 {
2032  /*tex current node being fabricated for new list */
2033  halfword r;
2034  /*tex whatsit subtype */
2035  halfword w;
2036  /*tex type of node */
2037  halfword t;
2038  /*tex a helper variable for copying into variable mem */
2039  register halfword s;
2040  register int i;
2041  if (copy_error(p)) {
2042  r = new_node(temp_node, 0);
2043  return r;
2044  }
2045  t = type(p);
2046  i = get_node_size(t,subtype(p));
2047  r = get_node(i);
2048  (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i));
2049  /*tex A possible speedup:
2050 
2051  \starttyping
2052  if t == glue_spec) {
2053  return r;
2054  }
2055  \stoptyping
2056 
2057  */
2058  if (synctex_anyway_mode) {
2059  /*tex Not:
2060 
2061  \starttyping
2062  if (t == glyph_node) {
2063  if (synctex_anyway_mode > 1) {
2064  synctex_tag_glyph(r) = forced_tag ? forced_tag : cur_input.synctex_tag_field;
2065  synctex_line_glyph(r) = forced_line ? forced_line : synctex_line_field ? synctex_line_field : line;
2066  }
2067  }
2068  \stoptyping
2069  */
2070  } else if (synctex_par) {
2071  /*tex Handle synctex extension. */
2072  switch (t) {
2073  case math_node:
2074  synctex_tag_math(r) = cur_input.synctex_tag_field;
2076  break;
2077  case kern_node:
2078  synctex_tag_kern(r) = cur_input.synctex_tag_field;
2080  break;
2081  }
2082  }
2083  if (nodetype_has_attributes(t)) {
2085  alink(r) = null;
2087  }
2088  vlink(r) = null;
2089  switch (t) {
2090  case glyph_node:
2092  break;
2093  case glue_node:
2095  break;
2096  case hlist_node:
2097  case vlist_node:
2098  case unset_node:
2100  break;
2101  case disc_node:
2103  if (vlink_pre_break(p) != null) {
2105  alink(s) = pre_break(r);
2107  vlink_pre_break(r) = s;
2108  } else {
2109  assert(tlink(pre_break(r)) == null);
2110  }
2112  if (vlink_post_break(p) != null) {
2114  alink(s) = post_break(r);
2116  vlink_post_break(r) = s;
2117  } else {
2118  assert(tlink_post_break(r) == null);
2119  }
2120  no_break(r) = no_break_head(r);
2121  if (vlink(no_break(p)) != null) {
2123  alink(s) = no_break(r);
2125  vlink_no_break(r) = s;
2126  } else {
2127  assert(tlink_no_break(r) == null);
2128  }
2129  break;
2130  case math_node:
2131  break;
2132  case ins_node:
2134  break;
2135  case margin_kern_node:
2137  break;
2138  case mark_node:
2140  break;
2141  case adjust_node:
2143  break;
2144  case choice_node:
2149  break;
2150  case simple_noad:
2154  break;
2155  case radical_noad:
2161  break;
2162  case accent_noad:
2169  break;
2170  case fence_noad:
2172  break;
2173  case sub_box_node:
2174  case sub_mlist_node:
2176  break;
2177  case fraction_noad:
2182  break;
2183  case glue_spec_node:
2184  break;
2185  case dir_node:
2186  break;
2187  case local_par_node:
2190  case boundary_node:
2191  break;
2192  case whatsit_node:
2193  w = subtype(p) ;
2194  if (w >= backend_first_pdf_whatsit) {
2196  } else if (w >= backend_first_dvi_whatsit) {
2198  } else {
2200  }
2201  break;
2202  }
2203  return r;
2204 }
2205 
2206 #define free_sub_list(source) if (source != null) flush_node_list(source);
2207 #define free_sub_node(source) if (source != null) flush_node(source);
2208 
2210 {
2211  switch (subtype(p)) {
2212  case open_node:
2213  case write_node:
2214  case close_node:
2215  case save_pos_node:
2216  break;
2217  case special_node:
2219  break;
2220  case late_lua_node:
2221  free_late_lua(p);
2222  break;
2223  case user_defined_node:
2224  switch (user_node_type(p)) {
2225  case 'a':
2227  break;
2228  case 'd':
2229  break;
2230  case 'l':
2231  free_user_lua(p);
2232  break;
2233  case 'n':
2235  break;
2236  case 's':
2237  /*tex |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */
2238  break;
2239  case 't':
2241  break;
2242  default:
2243  {
2244  const char *hlp[] = {
2245  "The type of the value in a user defined whatsit node should be one",
2246  "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),",
2247  "or 't' (tokenlist). Yours has an unknown type, and therefore I don't",
2248  "know how to free the node's value. A memory leak may result.",
2249  NULL
2250  };
2251  tex_error("Unidentified user defined whatsit", hlp);
2252  }
2253  break;
2254  }
2255  break;
2256  }
2257 }
2258 
2260 {
2261 }
2262 
2264 {
2265  switch(subtype(p)) {
2266  case pdf_save_node:
2267  case pdf_restore_node:
2268  case pdf_link_state_node:
2269  case pdf_refobj_node:
2270  case pdf_end_link_node:
2271  case pdf_end_thread_node:
2272  break;
2273  case pdf_literal_node:
2275  break;
2276  case pdf_colorstack_node:
2279  break;
2280  case pdf_setmatrix_node:
2282  break;
2283  case pdf_annot_node:
2285  break;
2286  case pdf_link_data_node:
2287  break;
2288  case pdf_start_link_node:
2289  if (pdf_link_attr(p) != null)
2292  break;
2293  case pdf_dest_node:
2294  if (pdf_dest_named_id(p) > 0)
2296  break;
2297  case pdf_action_node:
2298  if (pdf_action_type(p) == pdf_action_user) {
2300  } else {
2301  if (pdf_action_file(p) != null)
2305  else if (pdf_action_named_id(p) > 0)
2307  }
2308  break;
2309  case pdf_thread_data_node:
2310  break;
2311  case pdf_thread_node:
2312  case pdf_start_thread_node:
2313  if (pdf_thread_named_id(p) > 0)
2315  if (pdf_thread_attr(p) != null)
2317  break;
2318  }
2319 }
2320 
2322 {
2323  halfword w;
2324  if (p == null){
2325  /*tex legal, but no-op. */
2326  return;
2327  }
2328  if (free_error(p))
2329  return;
2330  switch (type(p)) {
2331  case glyph_node:
2333  break;
2334  case glue_node:
2336  break;
2337  case hlist_node:
2338  case vlist_node:
2339  case unset_node:
2341  break;
2342  case disc_node:
2343  /*tex Watch the start at temp node hack! */
2347  break;
2348  case rule_node:
2349  case kern_node:
2350  case penalty_node:
2351  case math_node:
2352  break;
2353  case glue_spec_node:
2354  /*tex This allows free-ing of lua-allocated glue specs. */
2355  break ;
2356  case dir_node:
2357  break;
2358  case local_par_node:
2361  break;
2362  case boundary_node:
2363  break;
2364  case whatsit_node:
2365  w = subtype(p) ;
2366  if (w >= backend_first_pdf_whatsit) {
2368  } else if (w >= backend_first_dvi_whatsit) {
2370  } else {
2372  }
2373  break;
2374  case ins_node:
2376  break;
2377  case margin_kern_node:
2379  break;
2380  case mark_node:
2382  break;
2383  case adjust_node:
2385  break;
2386  case style_node:
2387  /*tex Nothing to do. */
2388  break;
2389  case choice_node:
2394  break;
2395  case simple_noad:
2399  break;
2400  case radical_noad:
2406  break;
2407  case accent_noad:
2414  break;
2415  case fence_noad:
2417  break;
2418  case delim_node:
2419  case math_char_node:
2420  case math_text_char_node:
2421  /*tex Nothing to do. */
2422  break;
2423  case sub_box_node:
2424  case sub_mlist_node:
2426  break;
2427  case fraction_noad:
2432  break;
2433  case pseudo_file_node:
2435  break;
2436  case pseudo_line_node:
2437  case shape_node:
2438  free_node(p, subtype(p));
2439  return;
2440  break;
2441  case align_stack_node:
2442  case span_node:
2443  case movement_node:
2444  case if_node:
2445  case nesting_node:
2446  case unhyphenated_node:
2447  case hyphenated_node:
2448  case delta_node:
2449  case passive_node:
2450  case inserting_node:
2451  case split_up_node:
2452  case expr_node:
2453  case attribute_node:
2454  case attribute_list_node:
2455  case temp_node:
2456  break;
2457  default:
2458  formatted_error("nodes","flushing weird node type %d", type(p));
2459  return;
2460  }
2461  if (nodetype_has_attributes(type(p))) {
2464  }
2466  return;
2467 }
2468 
2469 /*tex Erase the list of nodes starting at |pp|. */
2470 
2472 {
2473  register halfword p = pp;
2474  if (p == null) {
2475  /*tex Legal, but no-op. */
2476  return;
2477  }
2478  if (free_error(p))
2479  return;
2480  /*tex Saves stack and time. */
2482  while (p != null) {
2483  register halfword q = vlink(p);
2484  flush_node(p);
2485  p = q;
2486  }
2487  /*tex Saves stack and time. */
2489 }
2490 
2492 {
2493  switch (subtype(p)) {
2494  /*tex Frontend code. */
2495  case special_node:
2496  check_token_ref(p);
2497  break;
2498  case user_defined_node:
2499  switch (user_node_type(p)) {
2500  case 'a':
2502  break;
2503  case 't':
2504  check_token_ref(p);
2505  break;
2506  case 'n':
2508  break;
2509  case 's':
2510  case 'd':
2511  case 'l':
2512  break;
2513  default:
2514  confusion("unknown user node type");
2515  break;
2516  }
2517  break;
2518  case open_node:
2519  case write_node:
2520  case close_node:
2521  case save_pos_node:
2522  break;
2523  }
2524 }
2525 
2527 {
2528 }
2529 
2531 {
2532  switch (subtype(p)) {
2533  case pdf_literal_node:
2534  if (pdf_literal_type(p) == normal)
2535  check_token_ref(p);
2536  break;
2537  case pdf_colorstack_node:
2539  check_token_ref(p);
2540  break;
2541  case pdf_setmatrix_node:
2542  check_token_ref(p);
2543  break;
2544  case late_lua_node:
2545  if (late_lua_name(p) > 0)
2546  check_token_ref(p);
2547  if (late_lua_type(p) == normal)
2548  check_token_ref(p);
2549  break;
2550  case pdf_annot_node:
2551  check_token_ref(p);
2552  break;
2553  case pdf_start_link_node:
2554  if (pdf_link_attr(p) != null)
2555  check_token_ref(p);
2557  break;
2558  case pdf_dest_node:
2559  if (pdf_dest_named_id(p) > 0)
2560  check_token_ref(p);
2561  break;
2562  case pdf_thread_node:
2563  case pdf_start_thread_node:
2564  if (pdf_thread_named_id(p) > 0)
2565  check_token_ref(p);
2566  if (pdf_thread_attr(p) != null)
2567  check_token_ref(p);
2568  break;
2569  case pdf_save_node:
2570  case pdf_restore_node:
2571  case pdf_link_state_node:
2572  case pdf_refobj_node:
2573  case pdf_end_link_node:
2574  case pdf_end_thread_node:
2575  break;
2576  default:
2577  confusion("wrapup pdf nodes");
2578  break;
2579  }
2580 }
2581 
2583 {
2584  halfword w ;
2585  switch (type(p)) {
2586  case glyph_node:
2588  break;
2589  case glue_node:
2591  break;
2592  case hlist_node:
2593  case vlist_node:
2594  case unset_node:
2595  case align_record_node:
2597  break;
2598  case ins_node:
2600  break;
2601  case whatsit_node:
2602  w = subtype(p) ;
2603  if (w >= backend_first_pdf_whatsit) {
2605  } else if (w >= backend_first_dvi_whatsit) {
2607  } else {
2609  }
2610  break;
2611  case margin_kern_node:
2613  break;
2614  case math_node:
2615  break;
2616  case disc_node:
2620  break;
2621  case adjust_node:
2623  break;
2624  case pseudo_file_node:
2626  break;
2627  case pseudo_line_node:
2628  case shape_node:
2629  break;
2630  case choice_node:
2635  break;
2636  case fraction_noad:
2641  break;
2642  case simple_noad:
2646  break;
2647  case radical_noad:
2653  break;
2654  case accent_noad:
2661  break;
2662  case fence_noad:
2664  break;
2665  case local_par_node:
2668  break;
2669  /*tex
2670 
2671  There is no need for useless cases:
2672 
2673  \starttyping
2674  case rule_node:
2675  case kern_node:
2676  case penalty_node:
2677  case mark_node:
2678  case style_node:
2679  case attribute_list_node:
2680  case attribute_node:
2681  case glue_spec_node:
2682  case temp_node:
2683  case align_stack_node:
2684  case movement_node:
2685  case if_node:
2686  case nesting_node:
2687  case span_node:
2688  case unhyphenated_node:
2689  case hyphenated_node:
2690  case delta_node:
2691  case passive_node:
2692  case expr_node:
2693  case dir_node:
2694  case boundary_node:
2695  break;
2696  default:
2697  fprintf(stdout, "check_node: type is %d\n", type(p));
2698  \stoptyping
2699 
2700  */
2701  }
2702 }
2703 
2705 {
2706  halfword next, tail;
2707  if (head == null)
2708  return null;
2709  tail = head;
2710  next = vlink(head);
2711  while (next != null) {
2712  alink(next) = tail;
2713  tail = next;
2714  next = vlink(tail);
2715  }
2716  return tail;
2717 }
2718 
2720 {
2721  register halfword r;
2722 
2723  if (s < MAX_CHAIN_SIZE) {
2724  r = free_chain[s];
2725  if (r != null) {
2726  free_chain[s] = vlink(r);
2727 #ifdef CHECK_NODE_USAGE
2728  varmem_sizes[r] = (char) s;
2729 #endif
2730  vlink(r) = null;
2731  /*tex Maintain usage statistics. */
2732  var_used += s;
2733  return r;
2734  }
2735  /*tex This is the end of the \quote {inner loop}. */
2736  return slow_get_node(s);
2737  } else {
2738  normal_error("nodes","there is a problem in getting a node, case 1");
2739  return null;
2740  }
2741 }
2742 
2743 void free_node(halfword p, int s)
2744 {
2745  if (p <= my_prealloc) {
2746  formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p));
2747  return;
2748  }
2749 #ifdef CHECK_NODE_USAGE
2750  varmem_sizes[p] = 0;
2751 #endif
2752  if (s < MAX_CHAIN_SIZE) {
2753  vlink(p) = free_chain[s];
2754  free_chain[s] = p;
2755  } else {
2756  /*tex Todo: it is perhaps possible to merge this node with an existing rover? */
2757  node_size(p) = s;
2758  vlink(p) = rover;
2759  while (vlink(rover) != vlink(p)) {
2760  rover = vlink(rover);
2761  }
2762  vlink(rover) = p;
2763  }
2764  /*tex Maintain statistics. */
2765  var_used -= s;
2766 }
2767 
2768 static void free_node_chain(halfword q, int s)
2769 {
2770  register halfword p = q;
2771  while (vlink(p) != null) {
2772 #ifdef CHECK_NODE_USAGE
2773  varmem_sizes[p] = 0;
2774 #endif
2775  var_used -= s;
2776  p = vlink(p);
2777  }
2778  var_used -= s;
2779 #ifdef CHECK_NODE_USAGE
2780  varmem_sizes[p] = 0;
2781 #endif
2782  vlink(p) = free_chain[s];
2783  free_chain[s] = q;
2784 }
2785 
2786 /*tex
2787 
2788  At the start of the node memory area we reserve some special nodes, for
2789  instance frequently used glue specifications. We could as well just use
2790  new_glue here but for the moment we stick to the traditional approach.
2791 
2792 */
2793 
2794 #define initialize_glue(n,wi,st,sh,sto,sho) \
2795  vlink(n) = null; \
2796  type(n) = glue_spec_node; \
2797  width(n) = wi; \
2798  stretch(n) = st; \
2799  shrink(n) = sh; \
2800  stretch_order(n) = sto; \
2801  shrink_order(n) = sho;
2802 
2803 #define initialize_whatever(n,t) \
2804  vinfo(n) = 0; \
2805  type(n) = t; \
2806  vlink(n) = null; \
2807  alink(n) = null;
2808 
2809 #define initialize_point(n) \
2810  type(n) = glyph_node; \
2811  subtype(n) = 0; \
2812  vlink(n) = null; \
2813  vinfo(n + 1) = null; \
2814  alink(n) = null; \
2815  font(n) = 0; \
2816  character(n) = '.'; \
2817  vinfo(n + 3) = 0; \
2818  vlink(n + 3) = 0; \
2819  vinfo(n + 4) = 0; \
2820  vlink(n + 4) = 0;
2821 
2822 void init_node_mem(int t)
2823 {
2825 
2826  varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t);
2827  if (varmem == NULL) {
2828  overflow("node memory size", (unsigned) var_mem_max);
2829  }
2830  memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word));
2831 #ifdef CHECK_NODE_USAGE
2832  varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t);
2833  if (varmem_sizes == NULL) {
2834  overflow("node memory size", (unsigned) var_mem_max);
2835  }
2836  memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t);
2837 #endif
2838  var_mem_max = t;
2839  rover = var_mem_stat_max + 1;
2840  vlink(rover) = rover;
2841  node_size(rover) = (t - rover);
2842  var_used = 0;
2843 
2844  /*tex Initialize static glue specs. */
2845 
2846  initialize_glue(zero_glue,0,0,0,0,0);
2847  initialize_glue(sfi_glue,0,0,0,sfi,0);
2852 
2853  /*tex Initialize node list heads. */
2854 
2863 
2866 
2869 }
2870 
2871 void dump_node_mem(void)
2872 {
2874  dump_int(rover);
2876 #ifdef CHECK_NODE_USAGE
2878 #endif
2880  dump_int(var_used);
2882 }
2883 
2884 /*tex
2885 
2886  It makes sense to enlarge the varmem array immediately.
2887 
2888 */
2889 
2891 {
2892  int x;
2893  undump_int(x);
2894  undump_int(rover);
2895  var_mem_max = (x < 100000 ? 100000 : x);
2896  varmem = xmallocarray(memory_word, (unsigned) var_mem_max);
2897  undump_things(varmem[0], x);
2898 #ifdef CHECK_NODE_USAGE
2899  varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
2900  memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char));
2902 #endif
2906  if (var_mem_max > x) {
2907  /*tex Todo: is it perhaps possible to merge the new node with an existing rover? */
2908  vlink(x) = rover;
2909  node_size(x) = (var_mem_max - x);
2910  while (vlink(rover) != vlink(x)) {
2911  rover = vlink(rover);
2912  }
2913  vlink(rover) = x;
2914  }
2915 }
2916 
2918 {
2919  register int t;
2920 
2921  RETRY:
2922  t = node_size(rover);
2923  if (vlink(rover) < var_mem_max && vlink(rover) != 0) {
2924  if (t > s) {
2925  /*tex Allocating from the bottom helps decrease page faults. */
2926  register halfword r = rover;
2927  rover += s;
2928  vlink(rover) = vlink(r);
2929  node_size(rover) = node_size(r) - s;
2930  if (vlink(rover) != r) {
2931  /*tex The list is longer than one. */
2932  halfword q = r;
2933  while (vlink(q) != r) {
2934  q = vlink(q);
2935  }
2936  vlink(q) += s;
2937  } else {
2938  vlink(rover) += s;
2939  }
2940  if (vlink(rover) < var_mem_max) {
2941 #ifdef CHECK_NODE_USAGE
2942  varmem_sizes[r] = (char) (s > 127 ? 127 : s);
2943 #endif
2944  vlink(r) = null;
2945  /*tex Maintain usage statistics. */
2946  var_used += s;
2947  /*tex This is the only exit. */
2948  return r;
2949  } else {
2950  normal_error("nodes","there is a problem in getting a node, case 2");
2951  return null;
2952  }
2953  } else {
2954  /*tex Attempt to keep the free list small. */
2955  int x;
2956  if (vlink(rover) != rover) {
2957  if (t < MAX_CHAIN_SIZE) {
2958  halfword l = vlink(rover);
2959  vlink(rover) = free_chain[t];
2960  free_chain[t] = rover;
2961  rover = l;
2962  while (vlink(l) != free_chain[t]) {
2963  l = vlink(l);
2964  }
2965  vlink(l) = rover;
2966  goto RETRY;
2967  } else {
2968  halfword l = rover;
2969  while (vlink(rover) != l) {
2970  if (node_size(rover) > s) {
2971  goto RETRY;
2972  }
2973  rover = vlink(rover);
2974  }
2975  }
2976  }
2977  /*tex If we are still here, it was apparently impossible to get a match. */
2978  x = (var_mem_max >> 2) + s;
2979  varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x));
2980  if (varmem == NULL) {
2981  overflow("node memory size", (unsigned) var_mem_max);
2982  }
2983  memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word));
2984 #ifdef CHECK_NODE_USAGE
2985  varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x));
2986  if (varmem_sizes == NULL) {
2987  overflow("node memory size", (unsigned) var_mem_max);
2988  }
2989  memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char));
2990 #endif
2991  /*tex Todo: is it perhaps possible to merge the new memory with an existing rover? */
2992  vlink(var_mem_max) = rover;
2993  node_size(var_mem_max) = x;
2994  while (vlink(rover) != vlink(var_mem_max)) {
2995  rover = vlink(rover);
2996  }
2997  vlink(rover) = var_mem_max;
2998  rover = var_mem_max;
2999  var_mem_max += x;
3000  goto RETRY;
3001  }
3002  } else {
3003  normal_error("nodes","there is a problem in getting a node, case 3");
3004  return null;
3005  }
3006 }
3007 
3009 {
3010  char *s;
3011 #ifdef CHECK_NODE_USAGE
3012  char *ss;
3013  int i;
3014  int b = 0;
3015  char msg[256];
3016  int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 };
3017  s = strdup("");
3018  for (i = (var_mem_max - 1); i > my_prealloc; i--) {
3019  if (varmem_sizes[i] > 0) {
3021  node_counts[last_normal_node + last_whatsit_node + 1]++;
3022  } else if (type(i) == whatsit_node) {
3023  node_counts[(subtype(i) + last_normal_node + 1)]++;
3024  } else {
3025  node_counts[type(i)]++;
3026  }
3027  }
3028  }
3029  for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) {
3030  if (node_counts[i] > 0) {
3031  int j =
3032  (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0);
3033  snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i],
3035  ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1));
3036  strcpy(ss, s);
3037  strcat(ss, msg);
3038  free(s);
3039  s = ss;
3040  b = 1;
3041  }
3042  }
3043 #else
3044  s = strdup("");
3045 #endif
3046  return s;
3047 }
3048 
3050 {
3051  halfword q = null;
3052 #ifdef CHECK_NODE_USAGE
3053  halfword p = null;
3054  halfword i, j;
3055  char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max);
3056  memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max);
3057  for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) {
3058  if (saved_varmem_sizes[i] > 0) {
3059  j = copy_node(i);
3060  if (p == null) {
3061  q = j;
3062  } else {
3063  vlink(p) = j;
3064  }
3065  p = j;
3066  }
3067  }
3068  free(saved_varmem_sizes);
3069 #endif
3070  return q;
3071 }
3072 
3074 {
3075  int i, b;
3076  halfword j;
3077  char msg[256];
3078  char *s;
3079  int free_chain_counts[MAX_CHAIN_SIZE] = { 0 };
3080  snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc));
3081  tprint_nl(msg);
3083  tprint_nl(" ");
3084  tprint(s);
3085  free(s);
3086  tprint(" nodes");
3087  tprint_nl(" avail lists: ");
3088  b = 0;
3089  for (i = 1; i < MAX_CHAIN_SIZE; i++) {
3090  for (j = free_chain[i]; j != null; j = vlink(j))
3091  free_chain_counts[i]++;
3092  if (free_chain_counts[i] > 0) {
3093  snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]);
3094  tprint(msg);
3095  b = 1;
3096  }
3097  }
3098  /*tex A newline, if needed: */
3099  print_nlp();
3100 }
3101 
3103 {
3104  halfword p = new_node(span_node, 0);
3105  span_link(p) = n;
3106  span_span(p) = s;
3107  width(p) = w;
3108  return p;
3109 }
3110 
3111 /* Now comes some attribute stuff. */
3112 
3113 static halfword new_attribute_node(unsigned int i, int v)
3114 {
3115  register halfword r = get_node(attribute_node_size);
3116  type(r) = attribute_node;
3117  attribute_id(r) = (halfword) i;
3118  attribute_value(r) = v;
3119  /* not used but nicer in print */
3120  subtype(r) = 0;
3121  return r;
3122 }
3123 
3125 {
3127  register halfword p = q;
3129  attr_list_ref(p) = 0;
3130  n = vlink(n);
3131  while (n != null) {
3132  register halfword r = get_node(attribute_node_size);
3133  /*tex The link will be fixed automatically in the next loop. */
3134  (void) memcpy((void *) (varmem + r), (void *) (varmem + n), (sizeof(memory_word) * attribute_node_size));
3135  vlink(p) = r;
3136  p = r;
3137  n = vlink(n);
3138  }
3139  return q;
3140 }
3141 
3143 {
3144  halfword p;
3145  register int i;
3149  p = attr_list_cache;
3150  for (i = 0; i <= max_used_attr; i++) {
3151  register int v = attribute(i);
3152  if (v > UNUSED_ATTRIBUTE) {
3153  register halfword r = new_attribute_node((unsigned) i, v);
3154  vlink(p) = r;
3155  p = r;
3156  }
3157  }
3158  if (vlink(attr_list_cache) == null) {
3160  attr_list_cache = null;
3161  }
3162  return;
3163 }
3164 
3166 {
3167  if (max_used_attr >= 0) {
3168  if (attr_list_cache == cache_disabled|| attr_list_cache == null) {
3170  if (attr_list_cache == null)
3171  return;
3172  }
3175  }
3176 }
3177 
3179 {
3180  if (max_used_attr >= 0) {
3183  }
3184  return attr_list_cache ;
3185  }
3186  return null ;
3187 }
3188 
3190 {
3191  halfword old;
3192  old = node_attr(n);
3193  if (new == null) {
3194  /*tex There is nothing to assign but we need to check for an old value. */
3195  if (old != null) {
3196  /*tex This also nulls |attr| field of |n|. */
3197  delete_attribute_ref(old);
3198  }
3199  } else if (old == null) {
3200  /*tex Nothing is assigned so we just do that now. */
3201  assign_attribute_ref(n,new);
3202  } else if (old != new) {
3203  /*tex Something is assigned so we need to clean up and assign then. */
3204  delete_attribute_ref(old);
3205  assign_attribute_ref(n,new);
3206  }
3207  /*tex The same value so there is no need to assign and change the refcount. */
3208  node_attr(n) = new ;
3209 }
3210 
3212 {
3213  if (b != null) {
3214  if (type(b) == attribute_list_node){
3215  attr_list_ref(b)--;
3216  if (attr_list_ref(b) == 0) {
3217  if (b == attr_list_cache)
3220  }
3221  /*tex Maintain sanity. */
3222  if (attr_list_ref(b) < 0) {
3223  attr_list_ref(b) = 0;
3224  }
3225  } else {
3226  normal_error("nodes","trying to delete an attribute reference of a non attribute node");
3227  }
3228  }
3229 }
3230 
3232 {
3233  if (b != null) {
3235  }
3236 }
3237 
3238 /*tex Here |p| is an attr list head, or zero. */
3239 
3241 {
3242  register halfword q;
3243  register int j = 0;
3244  if (p == null) {
3245  /*tex Add a new head \& node. */
3248  attr_list_ref(q) = 1;
3249  p = new_attribute_node((unsigned) i, val);
3250  vlink(q) = p;
3251  return q;
3252  }
3253  q = p;
3254  if (vlink(p) != null) {
3255  while (vlink(p) != null) {
3256  int t = attribute_id(vlink(p));
3257  if (t == i && attribute_value(vlink(p)) == val) {
3258  /*tex There is no need to do anything. */
3259  return q;
3260  }
3261  if (t >= i)
3262  break;
3263  j++;
3264  p = vlink(p);
3265  }
3266  p = q;
3267  while (j-- > 0)
3268  p = vlink(p);
3269  if (attribute_id(vlink(p)) == i) {
3270  attribute_value(vlink(p)) = val;
3271  } else {
3272  /*tex Add a new node. */
3273  halfword r = new_attribute_node((unsigned) i, val);
3274  vlink(r) = vlink(p);
3275  vlink(p) = r;
3276  }
3277  return q;
3278  } else {
3279  normal_error("nodes","trying to set an attribute fails, case 1");
3280  return null ;
3281  }
3282 }
3283 
3284 void set_attribute(halfword n, int i, int val)
3285 {
3286  register halfword p;
3287  register int j = 0;
3288  /*tex Not all nodes can have an attribute list. */
3290  return;
3291  /*tex If we have no list, we create one and quit. */
3292  p = node_attr(n);
3293  if (p == null) { /* add a new head \& node */
3296  attr_list_ref(p) = 1;
3297  node_attr(n) = p;
3298  p = new_attribute_node((unsigned) i, val);
3299  vlink(node_attr(n)) = p;
3300  return;
3301  }
3302  /*tex We check if we have this attribute already and quit if the value stays the same. */
3303  if (vlink(p) != null) {
3304  while (vlink(p) != null) {
3305  int t = attribute_id(vlink(p));
3306  if (t == i && attribute_value(vlink(p)) == val)
3307  return;
3308  if (t >= i)
3309  break;
3310  j++;
3311  p = vlink(p);
3312  }
3313  /*tex If found |j| has now the position and we assume a sorted list ! */
3314  p = node_attr(n);
3315  if (attr_list_ref(p) == 0 ) {
3316  /*tex The list is invalid i.e. freed already. */
3317  formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n);
3318  /*tex The still dangling list gets ref count 1. */
3319  attr_list_ref(p) = 1;
3320  } else if (attr_list_ref(p) == 1) {
3321  /*tex This can really happen! */
3322  if (p == attr_list_cache) {
3323  /*tex
3324 
3325  We can invalidate the cache setting with |attr_list_cache =
3326  cache_disabled| or or save the list, as done below.
3327 
3328  */
3329  p = copy_attribute_list(p);
3330  node_attr(n) = p;
3331  /*tex The copied list gets ref count 1. */
3332  attr_list_ref(p) = 1;
3333  }
3334  } else {
3335  /*tex The list is used multiple times so we make a copy. */
3336  p = copy_attribute_list(p);
3337  /*tex We decrement the ref count or the original. */
3339  node_attr(n) = p;
3340  /*tex The copied list gets ref count 1. */
3341  attr_list_ref(p) = 1;
3342  }
3343  /*tex We go to position |j| in the list. */
3344  while (j-- > 0)
3345  p = vlink(p);
3346  /*tex If we have a hit we just set the value otherwise we add a new node. */
3347  if (attribute_id(vlink(p)) == i) {
3348  attribute_value(vlink(p)) = val;
3349  } else {
3350  /*tex Add a new node. */
3351  halfword r = new_attribute_node((unsigned) i, val);
3352  vlink(r) = vlink(p);
3353  vlink(p) = r;
3354  }
3355  } else {
3356  normal_error("nodes","trying to set an attribute fails, case 2");
3357  }
3358 }
3359 
3361 {
3362  register halfword p;
3363  register int t;
3364  register int j = 0;
3366  return null;
3367  p = node_attr(n);
3368  if (p == null)
3369  return UNUSED_ATTRIBUTE;
3370  if (attr_list_ref(p) == 0) {
3371  formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n);
3372  return UNUSED_ATTRIBUTE;
3373  }
3374  if (vlink(p) != null) {
3375  while (vlink(p) != null) {
3376  t = attribute_id(vlink(p));
3377  if (t > i)
3378  return UNUSED_ATTRIBUTE;
3379  if (t == i) {
3380  p = vlink(p);
3381  break;
3382  }
3383  j++;
3384  p = vlink(p);
3385  }
3386  if (attribute_id(p) != i)
3387  return UNUSED_ATTRIBUTE;
3388  /*tex If we are still here, the attribute exists. */
3389  p = node_attr(n);
3390  if (attr_list_ref(p) > 1 || p == attr_list_cache) {
3392  if (attr_list_ref(p) > 1) {
3394  }
3395  attr_list_ref(q) = 1;
3396  node_attr(n) = q;
3397  }
3398  p = vlink(node_attr(n));
3399  while (j-- > 0)
3400  p = vlink(p);
3401  t = attribute_value(p);
3402  if (val == UNUSED_ATTRIBUTE || t == val) {
3404  }
3405  return t;
3406  } else {
3407  normal_error("nodes","trying to unset an attribute fails");
3408  return null;
3409  }
3410 }
3411 
3412 int has_attribute(halfword n, int i, int val)
3413 {
3414  register halfword p;
3416  return UNUSED_ATTRIBUTE;
3417  p = node_attr(n);
3418  if (p == null || vlink(p) == null)
3419  return UNUSED_ATTRIBUTE;
3420  p = vlink(p);
3421  while (p != null) {
3422  if (attribute_id(p) == i) {
3423  int ret = attribute_value(p);
3424  if (val == UNUSED_ATTRIBUTE || val == ret)
3425  return ret;
3426  return UNUSED_ATTRIBUTE;
3427  } else if (attribute_id(p) > i) {
3428  return UNUSED_ATTRIBUTE;
3429  }
3430  p = vlink(p);
3431  }
3432  return UNUSED_ATTRIBUTE;
3433 }
3434 
3436 {
3437  switch (type(p)) {
3438  case hlist_node:
3439  case vlist_node:
3440  case ins_node:
3441  case whatsit_node:
3442  case mark_node:
3443  case adjust_node:
3444  case unset_node:
3445  print_char('[');
3446  print_char(']');
3447  break;
3448  case rule_node:
3449  print_char('|');
3450  break;
3451  case glue_node:
3452  if (! glue_is_zero(p))
3453  print_char(' ');
3454  break;
3455  case math_node:
3456  print_char('$');
3457  break;
3458  case disc_node:
3461  break;
3462  }
3463 }
3464 
3466 {
3467  tprint("(");
3469  print_char('+');
3471  tprint(")x");
3473 }
3474 
3475 /*tex
3476 
3477  Each new type of node that appears in our data structure must be capable of
3478  being displayed, copied, destroyed, and so on. The routines that we need for
3479  write-oriented whatsits are somewhat like those for mark nodes; other
3480  extensions might, of course, involve more subtlety here.
3481 
3482 */
3483 
3484 static void print_write_whatsit(const char *s, pointer p)
3485 {
3486  tprint_esc(s);
3487  if (write_stream(p) < 16)
3489  else if (write_stream(p) == 16)
3490  print_char('*');
3491  else
3492  print_char('-');
3493 }
3494 
3495 static void show_node_wrapup_core(int p)
3496 {
3497  switch (subtype(p)) {
3498  case open_node:
3499  print_write_whatsit("openout", p);
3500  print_char('=');
3502  break;
3503  case write_node:
3504  print_write_whatsit("write", p);
3506  break;
3507  case close_node:
3508  print_write_whatsit("closeout", p);
3509  break;
3510  case special_node:
3511  tprint_esc("special");
3513  break;
3514  case late_lua_node:
3515  show_late_lua(p);
3516  break;
3517  case save_pos_node:
3518  tprint_esc("savepos");
3519  break;
3520  case user_defined_node:
3521  tprint_esc("whatsit");
3523  print_char('=');
3524  switch (user_node_type(p)) {
3525  case 'a':
3526  tprint("<>");
3527  break;
3528  case 'n':
3529  tprint("[");
3531  tprint("]");
3532  break;
3533  case 's':
3534  print_char('"');
3536  print_char('"');
3537  break;
3538  case 't':
3540  break;
3541  default: /* only 'd' */
3543  break;
3544  }
3545  break;
3546  }
3547 }
3548 
3550 {
3551 }
3552 
3554 {
3555  switch (subtype(p)) {
3556  case pdf_literal_node:
3558  break;
3559  case pdf_colorstack_node:
3560  tprint_esc("pdfcolorstack ");
3562  switch (pdf_colorstack_cmd(p)) {
3563  case colorstack_set:
3564  tprint(" set ");
3565  break;
3566  case colorstack_push:
3567  tprint(" push ");
3568  break;
3569  case colorstack_pop:
3570  tprint(" pop");
3571  break;
3572  case colorstack_current:
3573  tprint(" current");
3574  break;
3575  default:
3576  confusion("colorstack");
3577  break;
3578  }
3581  break;
3582  case pdf_setmatrix_node:
3583  tprint_esc("pdfsetmatrix");
3585  break;
3586  case pdf_save_node:
3587  tprint_esc("pdfsave");
3588  break;
3589  case pdf_restore_node:
3590  tprint_esc("pdfrestore");
3591  break;
3592  case pdf_link_state_node:
3593  tprint_esc("pdflinkstate ");
3595  break;
3596  case pdf_refobj_node:
3597  tprint_esc("pdfrefobj");
3600  tprint(" attr");
3602  print_char(' ');
3603  tprint((const char *) lua_tostring(Luas, -1));
3604  lua_pop(Luas, 1);
3605  }
3606  tprint(" stream");
3607  }
3609  tprint(" file");
3612  print_char(' ');
3613  tprint((const char *) lua_tostring(Luas, -1));
3614  lua_pop(Luas, 1);
3615  }
3616  break;
3617  case pdf_annot_node:
3618  tprint_esc("pdfannot");
3621  break;
3622  case pdf_start_link_node:
3623  tprint_esc("pdfstartlink");
3625  if (pdf_link_attr(p) != null) {
3626  tprint(" attr");
3628  }
3629  tprint(" action");
3631  tprint(" user");
3633  return;
3634  }
3635  if (pdf_action_file(pdf_link_action(p)) != null) {
3636  tprint(" file");
3638  }
3639  switch (pdf_action_type(pdf_link_action(p))) {
3640  case pdf_action_goto:
3642  tprint(" goto name");
3644  } else {
3645  tprint(" goto num");
3647  }
3648  break;
3649  case pdf_action_page:
3650  tprint(" page");
3653  break;
3654  case pdf_action_thread:
3656  tprint(" thread name");
3658  } else {
3659  tprint(" thread num");
3661  }
3662  break;
3663  default:
3664  normal_error("pdf backend", "unknown action type for link");
3665  break;
3666  }
3667  break;
3668  case pdf_end_link_node:
3669  tprint_esc("pdfendlink");
3670  break;
3671  case pdf_dest_node:
3672  tprint_esc("pdfdest");
3673  if (pdf_dest_named_id(p) > 0) {
3674  tprint(" name");
3676  } else {
3677  tprint(" num");
3679  }
3680  print_char(' ');
3681  switch (pdf_dest_type(p)) {
3682  case pdf_dest_xyz:
3683  tprint("xyz");
3684  if (pdf_dest_xyz_zoom(p) != null) {
3685